//* 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; }