int get_shallow_move(int *board, int side) { /* Return the move that gives the best position immediately after */ int i; int move = 0; int score; int flips[20]; int old_board[100]; int best_score = MIN_SCORE; // backup the existing board copy_board(board, old_board); for (i=11; i<89; ++i) { if (legal_move(board,i,side,flips) == 1) { make_move(board,i,side,flips); score = evaluate_board(board, side, 0); if (score > best_score) { best_score = score; move = i; } // reset the board before each iteration copy_board(old_board, board); } } return move; }
int have_legal_moves (othello_bd *bd) { for (int i = 0 ; i < X_SIZE ; ++i) { for (int j = 0 ; j < Y_SIZE ; ++j) { if (legal_move(bd,i,j)) { return true; } } } return false; }
/* Generate a move. */ static void generate_move(int *i, int *j, int color) { int moves[MAX_BOARD * MAX_BOARD]; int num_moves = 0; int move; int ai, aj; int k; memset(moves, 0, sizeof(moves)); for (ai = 0; ai < board_size; ai++) for (aj = 0; aj < board_size; aj++) { /* Consider moving at (ai, aj) if it is legal and not suicide. */ if (legal_move(ai, aj, color) && !suicide(ai, aj, color)) { /* Further require the move not to be suicide for the opponent... */ if (!suicide(ai, aj, OTHER_COLOR(color))) moves[num_moves++] = POS(ai, aj); else { /* ...however, if the move captures at least one stone, * consider it anyway. */ for (k = 0; k < 4; k++) { int bi = ai + deltai[k]; int bj = aj + deltaj[k]; if (on_board(bi, bj) && get_board(bi, bj) == OTHER_COLOR(color)) { moves[num_moves++] = POS(ai, aj); break; } } } } } /* Choose one of the considered moves randomly with uniform * distribution. (Strictly speaking the moves with smaller 1D * coordinates tend to have a very slightly higher probability to be * chosen, but for all practical purposes we get a uniform * distribution.) */ if (num_moves > 0) { move = moves[xor_randn(num_moves)]; *i = I(move); *j = J(move); } else { /* But pass if no move was considered. */ *i = -1; *j = -1; } }
static int gtp_play(char *s) { int i, j; int color = EMPTY; if (!gtp_decode_move(s, &color, &i, &j)) return gtp_failure("invalid color or coordinate"); if (!legal_move(i, j, color)) return gtp_failure("illegal move"); play_move(i, j, color); return gtp_success(""); }
int get_random_move(int *board, int side) { /* Randomly pick a move! * Mostly used for simulating other AI algorithms */ int move; int counter = 0; int possible_moves[64]; int flips[20]; for(int i=11; i<89; ++i) { if (legal_move(board,i,side,flips)) { possible_moves[counter] = i; counter++; } } // Using modulus with rand is not ideal, but it's good enough // Problem is that it can give skewed results move = possible_moves[(rand() % counter)]; return move; }
int minimize(int *board, int side, int unplayed, int ply) { /* Search to depth ply with mutual recursion * Side should be the opponent of currently playing side * Return the lowest score found */ int old_board[100]; int flips[20]; int score = MAX_SCORE; if (ply == 0 || test_end(board, unplayed) == 1) return evaluate_board(board, side, unplayed); copy_board(board, old_board); for (int i=11; i<89; ++i) { if (legal_move(board, i, side, flips) == 0) continue; make_move(board, i, side, flips); score = MIN(score, maximize(board, -side, 0, ply-1)); copy_board(old_board, board); } return score; }
int ab_minimize(int *board, int side, int unplayed, int ply,int a, int b) { /* Search to depth ply with mutual recursion * Return minimum score possible as opponent of current player * Cut off with a and b when possible */ int old_board[100]; int flips[20]; if (ply == 0 || test_end(board, unplayed) == 1) return evaluate_board(board, side, unplayed); copy_board(board, old_board); for (int i=11; i<89; ++i) { if (legal_move(board, i, side, flips) == 0) continue; make_move(board, i, side, flips); b = MIN(b, ab_maximize(board, -side, 0, ply-1, a, b)); copy_board(old_board, board); if (b <= a) break; } return b; }
int ab_maximize(int *board, int side, int unplayed, int ply, int a,int b) { /* Search to depth ply with mutual recursion * Return maximum score possible for current side * Cuts off search using a and b parameters if possible */ int old_board[100]; int flips[20]; if (ply == 0 || test_end(board, unplayed) == 1) return evaluate_board(board, side, unplayed); copy_board(board, old_board); for (int i=11; i<89; ++i) { if (legal_move(board, i, side, flips) == 0) continue; make_move(board, i, side, flips); a = MAX(a, ab_minimize(board, -side, 0, ply-1, a, b)); copy_board(old_board, board); if (b <= a) // that's a cutoff, no point further pursuing this position break; } return a; }
// main difference between get_move and maximize is that this one // returns a move, not a score int get_minimax_move(int *board, int side, int unplayed, int ply) { /* Similar to maximize(), but return move instead of score */ int score; int best_move = 0; int flips[20]; int old_board[100]; int best_score = MIN_SCORE-2; copy_board(board, old_board); for (int i=11; i<89; ++i) { if (legal_move(board, i, side, flips) == 0) continue; make_move(board, i, side, flips); score = minimize(board, -side, unplayed, ply); copy_board(old_board, board); if (score > best_score) { best_score = score; best_move = i; } } return best_move; }
int get_alphabeta_move(int *board, int side, int unplayed, int ply) { /* Return best move using alphabeta search to ply depth * Similar to ab_maximize, but finds move instead of score */ int score; int best_move = 0; int flips[20]; int old_board[100]; int best_score = MIN_SCORE-1; copy_board(board, old_board); for (int i=11; i<89; ++i) { if (legal_move(board, i, side, flips) == 0) continue; make_move(board, i, side, flips); score = ab_minimize(board, -side, unplayed, ply, best_score,MAX_SCORE+1); copy_board(old_board, board); if (score > best_score) { best_score = score; best_move = i; } } return best_move; }
// Blasphemous! maximize instead of maximise?! int maximize(int *board, int side, int unplayed, int ply) { /* The max portion of minimax * Return maximum score available from position * Used with currently playing side's turn * Search with mutual recursion to depth ply */ int old_board[100]; int flips[20]; int score = MIN_SCORE; if (ply == 0 || test_end(board, unplayed) == 1) // either reached end of search or game is over, so return return evaluate_board(board, side, unplayed); // backup the current playing board copy_board(board, old_board); for (int i=11; i<89; ++i) { if (legal_move(board, i, side, flips) == 0) continue; make_move(board, i, side, flips); score = MAX(score, minimize(board, -side, 0, ply-1)); // reset the board after computing the score copy_board(old_board, board); } return score; }
/*- * Add a randomly chosen tile to a given vertex. This requires more checking * as we must make sure the new tile conforms to the vertex rules at every * vertex it touches. */ static void add_random_tile(fringe_node_c * vertex, ModeInfo * mi) { fringe_node_c *right, *left, *far; int i, j, n, n_hits, n_good; unsigned side, fc, no_good, s; vertex_type_c vtypes[MAX_COMPL]; rule_match_c hits[MAX_TILES_PER_VERTEX * N_VERTEX_RULES]; tiling_c *tp = &tilings[MI_SCREEN(mi)]; if (MI_NPIXELS(mi) > 2) { tp->thick_color = NRAND(MI_NPIXELS(mi)); /* Insure good contrast */ tp->thin_color = (NRAND(2 * MI_NPIXELS(mi) / 3) + tp->thick_color + MI_NPIXELS(mi) / 6) % MI_NPIXELS(mi); } else { unsigned long temp = tp->thick_color; tp->thick_color = tp->thin_color; tp->thin_color = temp; } n_hits = match_rules(vertex, hits, False); side = NRAND(2) ? S_LEFT : S_RIGHT; n = find_completions(vertex, hits, n_hits, side, vtypes /*, False */ ); /* One answer would mean a forced tile. */ if (n <= 0) { tp->done = True; if (MI_IS_VERBOSE(mi)) { (void) fprintf(stderr, "Weirdness in add_random_tile()\n"); (void) fprintf(stderr, "n = %d\n", n); } } no_good = 0; n_good = n; for (i = 0; i < n; i++) { fc = fringe_changes(mi, vertex, side, vtypes[i], &right, &far, &left); if (fc & FC_BAG) { tp->done = True; if (MI_IS_VERBOSE(mi)) { (void) fprintf(stderr, "Weirdness in add_random_tile()\n"); (void) fprintf(stderr, "fc = %d, FC_BAG = %d\n", fc, FC_BAG); } } if (right) { s = (((fc & FC_CUT_FAR) && (fc & FC_CUT_LEFT)) ? S_RIGHT : S_LEFT); if (!legal_move(right, s, VT_RIGHT(vtypes[i]))) { no_good |= (1 << i); n_good--; continue; } } if (left) { s = (((fc & FC_CUT_FAR) && (fc & FC_CUT_RIGHT)) ? S_LEFT : S_RIGHT); if (!legal_move(left, s, VT_LEFT(vtypes[i]))) { no_good |= (1 << i); n_good--; continue; } } if (far) { s = ((fc & FC_CUT_LEFT) ? S_RIGHT : S_LEFT); if (!legal_move(far, s, VT_FAR(vtypes[i]))) { no_good |= (1 << i); n_good--; } } } if (n_good <= 0) { tp->done = True; if (MI_IS_VERBOSE(mi)) { (void) fprintf(stderr, "Weirdness in add_random_tile()\n"); (void) fprintf(stderr, "n_good = %d\n", n_good); } } n = NRAND(n_good); for (i = j = 0; i <= n; i++, j++) while (no_good & (1 << j)) j++; if (!add_tile(mi, vertex, side, vtypes[j - 1])) { tp->done = True; if (MI_IS_VERBOSE(mi)) { (void) fprintf(stderr, "Weirdness in add_random_tile()\n"); } free_penrose(tp); } }