//* P L A Y I N G F U N C T I O N S
int8_t valid_pos (int8_t POS) {
int8_t I;
// on the board?
I = POS >> 1;
if (POS < 0 || POS > 77 || I == 4 || I == 14 || I == 24 || I == 34) {
return -1;
}
// square vacant?
for (I = 1; I <= 12; I++) {
if ((I <= COMP_NUM && POS == COMP_POS (I)) || (I <= OPP_NUM && POS == OPP_POS (I))) {
return 1;
}
}
return 0;
}
int8_t valid_move (int8_t M, int8_t P, Player *COMPp, Player *OPPp) {
int8_t CPOS, I, T;
assert(1 <= P && P <= 12);
if (CKTYPE(COMP__TYPE (P)) != CROWN) {
if (COMP__PLAYERNO == 1) { // player 1
if ((M & 4) == 0) {
return -1; // backward move
}
} else { // else player 2
if ((M & 4) != 0) {
return -1; // backward move
}
}
}
CPOS = COMP__POS (P);
if (valid_pos (CPOS + MOVE (M))) {
return -1;
}
if ((M & 1) == 0) {
return 0; // ordinary move
}
if (OPP_NUM == 0) {
return -1;
}
I = CPOS + MOVE (M - 1); // else take-move
for (T = 1; T <= OPP__NUM; T++) {
if (I == OPP__POS (T)) {
return T; // piece to take?
}
}
return -1;
}
// THIS MAY BE THE BUG! COMP_NUM etc define in terms of COMP, *not* COMPp
int8_t pending_move (Player *COMPp, Player *OPPp) {
int8_t P, M, PMCOMPNUM; // might be able to reuse COMP_NUM ?
PMCOMPNUM = COMP__NUM;
if (!PMCOMPNUM) {
return -1; // no pieces left!!
}
assert(1 <= PMCOMPNUM && PMCOMPNUM <= 12);
for (P = 1; P <= PMCOMPNUM; P++) {
for (M = 1; M <= 7; M += 2) {
if (valid_move (M, P, COMPp, OPPp) > 0) {
return P; // return piece doing take
}
}
}
// ordinary moves
for (P = 1; P <= PMCOMPNUM; P++) {
for (M = 0; M <= 6; M += 2) {
if (!valid_move (M, P, COMPp, OPPp)) {
return 0; // move ok
}
}
}
return -1; // no moves
}
static inline int8_t CROWNING (int8_t POS, int8_t PLAYER) { // assumes valid pos
return ((PLAYER == 1 && POS < 7) || (PLAYER == 2 && POS > 70));
}
static inline INTEGER MODI (INTEGER N) {
return (N < 0) ? -N : N;
}
static inline int8_t MOD8 (int8_t N) {
return (N < 0) ? -N : N;
}
INTEGER value_of_side (Player *COMPp, Player *OPPp) {
// bases have already been adjusted
INTEGER V1, V2, V3, V4, V5, V6, V7;
int8_t P, PP, M, MM, MV, ADV1, VM, PLAYER;
INTEGER *CTYPEp;
int8_t *CPOSp;
#define CPOS (*CPOSp)
#define CTYPE (*CTYPEp)
PLAYER = COMP__PLAYERNO;
// back control
V2 = 0;
if (!OPP__NUMC) { // OPP has no crowns yet
if (PLAYER == 1) { // player 1
for (P = 1; P <= 5; P++) {
if (COMP__POS (P) == BACK1 (P))
V2 += back_wt;
}
} else { // player 2
for (P = 1; P <= 5; P++) {
if (COMP__POS (P) == BACK2 (P))
V2 += back_wt;
}
}
}
V1 = V3 = V4 = V5 = V6 = V7 = 0;
for (P = 1; P <= COMP__NUM; P++) {
CPOSp = &COMP__POS (P);
CTYPEp = &COMP__TYPE (P);
ADV1 = 0;
// add up piece values
V1 += CKTYPE(CTYPE);
for (M = 0; M <= 7; M++) {
VM = valid_move (M, P, COMPp, OPPp);
MV = MOVE (M);
// centre control
if (CPOS == CENTSQ (M)) {
V4 += cent_wt;
if (CKTYPE(CTYPE) == CROWN)
V4 += cent_wt + 1;
}
if (VM >= 0) { // mobility
V3 += mob_wt;
if (VM > 0)
V3 += mob_wt;
if (CKTYPE(CTYPE) != CROWN) {
// advancement 1
if ((ADV1 == 0) && CROWNING (CPOS + MV, PLAYER)) {
V5 += adv1_wt;
ADV1 = 1;
}
// advancement 2
if (VM == 0) { // ignore jumps to crown
CPOS += MV;
for (MM = 0; MM <= 6; MM += 2) {
if (CROWNING (CPOS + MOVE (MM), PLAYER)
&& valid_move (MM, P, COMPp, OPPp) >= 0) {
V6 += adv2_wt;
break;
}
}
CPOS -= MV;
}
}
// cramp
if ((VM == 0) && CKTYPE(CTYPE) == CROWN && OPP__NUM > 0) {
CPOS += MV;
for (PP = 1; PP <= OPP__NUM; PP++) {
for (MM = 1; MM <= 7; MM += 2) {
if (valid_move (MM, PP, OPPp, COMPp) >= 0) {
V5 -= cramp_wt;
break;
}
}
}
CPOS -= MV;
}
}
}
}
return V1 + V2 + V3 + V4 + V5 + V6 + V7;
#undef FRAME
#undef CPOS
#undef CTYPE
}
INTEGER value_of_pos (Player *COMPp, Player *OPPp) {
INTEGER VALUE;
if (pending_move (COMPp, OPPp) >= 0) {
VALUE = value_of_side (COMPp, OPPp) - value_of_side (OPPp, COMPp) - ply_number;
} else {
VALUE = -INFTY + ply_number; // no mobility!!
}
return (COMP__PLAYERNO == ME) ? VALUE : -VALUE;
}
#ifdef OLDSTYLE
void make_move (int8_t MV, int8_t P, int8_t *Tp, Player *COMPp, Player *OPPp) {
#define T (*Tp)
INTEGER *CTYPEp, *OTYPEp;
int8_t *CPOSp, *OPOSp, *ONUMp, *ONUMCp;
#define CPOS (*CPOSp)
#define CTYPE (*CTYPEp)
#define OPOS (*OPOSp)
#define OTYPE (*OTYPEp)
#define ONUM (*ONUMp)
#define ONUMC (*ONUMCp)
CPOSp = &COMP__POS (P);
CTYPEp = &COMP__TYPE (P);
CPOS = CPOS + MV;
if (T) { // a take
OPOSp = &OPP__POS (T);
OTYPEp = &OPP__TYPE (T);
ONUMp = &OPP__NUM;
ONUMCp = &OPP__NUMC;
SP += 1;
PSTACK (SP) = OPOS; // save position of taken piece
TSTACK (SP) = CKTYPE(OTYPE); // save piece type
NSTACK (SP) = T; // save piece number
OPOS = OPP__POS (ONUM); // remove piece from board
ONUM -= 1; // reduce piece count for opp
if (CKTYPE(OTYPE) == CROWN) ONUMC -= 1;
OTYPE = CKTYPE(OPP__TYPE (ONUM + 1)); // (really OPP_TYPE(ONUM), but value before ONUM was decremented above) Could substitute ONUM--?
}
if (CKTYPE(CTYPE) != CROWN && CROWNING (CPOS, COMP__PLAYERNO)) {
CTYPE = CKTYPE(CROWN); // crown piece
COMP__NUMC += 1; // increase crown count
T = 0; // no more takes
}
#undef T
#ifdef PIZERO
DEBUG_EVERYTHING(__LINE__);
#endif
}
#else
void make_move (int8_t MV, int8_t P, int8_t *Tp, Player *COMPp, Player *OPPp) {
#define T (*Tp)
// This is fairly major surgery, considering that it was working before.
// might need to revert. geting a gcc register spill error
#ifdef PIZERO
DEBUG_EVERYTHING(__LINE__);
#endif
COMP__POS (P) += MV;
if (T) { // a take
SP += 1;
PSTACK (SP) = OPP__POS (T); // save position of taken piece
TSTACK (SP) = CKTYPE(OPP__TYPE (T)); // save piece type
NSTACK (SP) = T; // save piece number
OPP__POS (T) = OPP__POS (OPP__NUM); // remove piece from board
OPP__NUM -= 1; // reduce piece count for opp
if (CKTYPE(OPP__TYPE (T)) == CROWN) OPP__NUMC -= 1;
OPP__TYPE (T) = CKTYPE(OPP__TYPE (OPP__NUM + 1)); // (really OPP_TYPE(ONUM), but value before ONUM was decremented above) Could substitute ONUM--?
}
if (CKTYPE(COMP__TYPE (P)) != CROWN && CROWNING (COMP__POS (P), COMP__PLAYERNO)) {
COMP__TYPE (P) = CKTYPE(CROWN); // crown piece
COMP__NUMC += 1; // increase crown count
T = 0; // no more takes
}
#undef T
#ifdef PIZERO
DEBUG_EVERYTHING(__LINE__);
#endif
}
#endif
void Display_board (int8_t ignore);
// This is the expensive recursion. Minimise local variables as much as possible.
INTEGER try_possible_moves (int8_t PLY, INTEGER DEPTH, Player *COMPp, Player *OPPp) {
static INTEGER VALUE; // ONLY VARIABLES THAT DON'T NEED TO SURVIVE RECURSION!!!
INTEGER OLDTYPE;
int8_t M, MM, MMM, MMF, TAKES, OLDPOS, APT, P, T, TT;
INTEGER *MAXPLYp, *MINPLYp;
#define MAXPLY (*MAXPLYp)
#define MINPLY (*MINPLYp)
#define PURSUIT(PLY,M) (PLY == 1 ? 1 : (~M) & 1)
NODES += 1;
APT = pending_move (COMPp, OPPp);
if ((DEPTH >= search_limit && APT <= 0) || PLY > MAX_RECURSIVE_DEPTH) {
ply_number = PLY - 1;
return value_of_pos (COMPp, OPPp);
}
MINPLYp = &MIN (PLY);
MINPLY = INFTY;
MAXPLYp = &MAX (PLY);
MAXPLY = -INFTY;
if (APT >= 0) { // COMP able to move
if (APT > 0) { // take priority
P = APT;
APT = 2;
MMM = 1;
MMF = 7;
if (PLY == 1)
search_limit = 2;
} else {
P = 1;
APT = 2;
MMM = 0;
MMF = 6;
}
for (P = P; P <= COMP__NUM; P++) {
OLDPOS = COMP__POS (P); // STACK THESE TO PRESERVE ACROSS RECURSION
OLDTYPE = CKTYPE(COMP__TYPE (P));
for (M = MMM; M <= MMF; M += APT) {
TAKES = 0;
T = valid_move (M, P, COMPp, OPPp);
if (T >= 0) { // valid move
MM = M;
TT = T;
do {
//another_take:
if (TT > 0) TAKES += 1;
// Hack! to flash "THINKING" on screen. Somewhat erratic.
Wait_Recal (); Flash++; Reset0Ref (); Intensity_7F (); Print_Str_d (0, -120, " THINKING...\x80");
#ifdef PIZERO
Dim=DIM;Display_board(0);Dim=0;
#endif
make_move (MOVE (MM), P, &TT, COMPp, OPPp); // try this move
if (TT > 0) { // another take
for (MM = 1; MM <= 7; MM += 2) {
TT = valid_move (MM, P, COMPp, OPPp);
if (TT > 0) break; // goto another_take;
}
}
} while (TT > 0);
VALUE = try_possible_moves (PLY + 1, DEPTH + PURSUIT (PLY, M), OPPp, COMPp);
COMP__POS (P) = OLDPOS;
COMP__TYPE (P) = CKTYPE(OLDTYPE);
OPP__NUM += TAKES;
while (TAKES > 0) {
TT = NSTACK (SP);
OPP__POS (TT) = PSTACK (SP);
OPP__TYPE (TT) = CKTYPE(TSTACK (SP));
TAKES -= 1;
SP -= 1;
}
if (COMP__PLAYERNO == ME) {
if (VALUE > MAXPLY) {
MAXPLY = VALUE;
if (PLY == 1) {
best_move = M;
best_take = T;
best_piece = P;
}
}
if (PLY != 1 && MAXPLY >= MIN (PLY - 1)) {
return MAXPLY;
}
} else {
if (VALUE < MINPLY)
MINPLY = VALUE;
if (MINPLY <= MAX (PLY - 1)) {
return MINPLY;
}
}
} // if (T>=0)
} // for M
} // for P
} // if APT >= 0
return (COMP__PLAYERNO == ME) ? MAXPLY : MINPLY;
}