/* ディスクの設置をする関数 */ int place_disk(const int side, const XY sq) { int n = 0, i; // パスのとき if (sq.x == PASSMOVE.x || sq.y == PASSMOVE.y) return 0; assert(is_legal_move(side, sq)); for (i = 0; i < 8; i++) { const XY dir = directions[i]; if (!can_flip(side, sq, dir))// この方向にはひっくり返せないとき continue; int x = sq.x + dir.x; int y = sq.y + dir.y; // ある方向のディスクをひっくり返す while (board[x][y] == -side) { board[x][y] = side; n++; x += dir.x; y += dir.y; } } board[sq.x][sq.y] = side; // 最後に端をひっくり返す assert(n > 0); return n; }
// moves must contain at least b->len elements index_t gen_moves(const board_t *b, stone_t color, index_t *moves, bool ko_rule) { index_t cnt = 0; for (index_t i = 0; i < b->empty_ptr; i++) { if (is_legal_move(b, b->list[i], color, ko_rule)) { moves[cnt++] = b->list[i]; } } return cnt; }
int main(int argc, char **argv) { srand((unsigned) time(NULL)); int board[8][8];//グローバル変数ではなく、いちいち渡すことに const int random_side = (argc >= 2) ? atoi(argv[1]) : 0;//MANの先攻後攻の決定 init_board(board); int turn; int pass=0; for (turn = 1;; turn *= -1) {//先手の時はturn=1,後手の時はturn=-1 print_board(board); Pair legal_moves[60];//合法手を全て収納するための配列 const int nmoves = generate_all_legal_moves(turn, legal_moves,board);//空きマス0能時にnamoves=-1を返す ←一回目のここで既に何かミスってる if (nmoves == -1) break; // no empty square→ゲーム終了 if (nmoves == 0) { pass++; printf("turn = %d, move = Pass\n", turn);//パスの処理 if(pass>=2){ break; } continue; }else{//passの初期化 pass=0; } Pair move; if (turn == random_side) {//人間側の操作のとき。com同士ならこれはおこらない move = legal_moves[rand()%nmoves]; //ランダム選択 assert(is_legal_move(turn,move,board)); } else {//COM側の操作 move = goodmove(turn,board);//turnは少なくとも必要 assert(is_legal_move(turn,move,board));//念のため。最適解がちゃんとlegalmoveか確認 } place_disk(turn, move,board);//moveをそもそも受け取れていなかった printf("turn = %d, move = %c%c\n", turn, 'a' + move.x, '1' + move.y); }//breakするまでfor文は繰り返す judge(random,board);//先攻か後攻かを返す return 0; }
// iterater interface to gen_moves // set ptr to b->list in first function call // set ptr to NULL in the end index_t gen_moves_next(board_t *b, stone_t color, index_t &offset, bool ko_rule) { index_t* ptr = b->list+offset; while (ptr < b->list + b->empty_ptr) { if (is_legal_move(b, *ptr, color, ko_rule)) { offset++; return *ptr; } ptr++; offset++; } return -1; }
int main() { initialize(); int tests = 1000; int passed = 0; fast_srandom(time(0)); for (int i = 0; i < tests; i++) { board_t *b = new board_t; int size = fast_random(max_size - 4) + 5; empty_board(b, size); int steps = 0; bool failed = false; while (true) { stone_t color = steps % 2 == 0 ? STONE_BLACK : STONE_WHITE; index_t pos = gen_move(b, color); if (pos < 0) break; if (!is_legal_move(b, pos, color)) { failed = true; break; } if (pos >= 0) { put_stone(b, pos, color); if (!check_board(b)) { failed = true; break; } } steps++; } delete b; if (failed) { printf("[F]"); } else { printf("[%d]", steps); passed++; } } printf("\n"); printf("Passed %d out of %d random tests\n", passed, tests); return passed == tests ? 0 : 1; }
/* 合法手を作る関数 */ int generate_moves(const int side, XY *moves) { int nmoves = 0; int x, y; for (x = 0; x < 8; x++) { for (y = 0; y < 8; y++) { XY tmp = {x, y}; // 検査用 if (!is_legal_move(side, tmp)) // 合法手でないなら次へ continue; assert(nmoves < 60); moves[nmoves++] = tmp; // 合法手の代入 } } return nmoves; }
/* Perform checks on a move's legality and return TRUE if the move is made. */ int make_move (int player, int start_pos, int end_pos) { if (valid_start_pos (player, start_pos) == FALSE) { return FALSE; } if (valid_end_pos (end_pos) == FALSE) { return FALSE; } if (is_legal_move (player, start_pos, end_pos) == FALSE) { return FALSE; } move_piece (start_pos, end_pos); if (player_in_check (opponent_player (player)) == TRUE) { printf ("Move places opponent in check!\n"); } if (player_has_moves (opponent_player (player)) == FALSE) { checkmate = TRUE; } return TRUE; }
int enum_moves(move_storage ** list_address) { move_storage * list; void * new_address; int x1, y1, x2, y2; register int legal_moves; if (list_address == NULL) return (legal_moves = 0); list = *(list_address); if (list == NULL) list = (move_storage *)malloc(0); legal_moves = 0; for (y1 = 0; y1 < BOARD_SIZE; y1++) for (x1 = 0; x1 < BOARD_SIZE; x1++) for (y2 = 0; y2 < BOARD_SIZE; y2++) for (x2 = 0; x2 < BOARD_SIZE; x2++) if (is_legal_move(x1, y1, x2, y2)) { new_address = realloc( list, ++legal_moves * sizeof(move_storage) ); assert(new_address != NULL); list = (move_storage *)new_address; list[legal_moves - 1].origin.file = x1; list[legal_moves - 1].origin.rank = y1; list[legal_moves - 1].target.file = x2; list[legal_moves - 1].target.rank = y2; } assert(list != NULL); assert(legal_moves >= 0); *(list_address) = list; return (legal_moves); }
/* 人間の入力を管理する関数 */ void man_player(const int side, XY *move) { XY moves[MOVENUM]; char buf[1000]; // 手がなければパス if (generate_moves(side, moves) == 0) { printf("Pass!\n"); printf("Press Enter!\n"); fgets(buf, sizeof(buf), stdin); *move = PASSMOVE; return ; } while (1) { // 手の入力 do { printf("Where? "); fgets(buf, sizeof(buf), stdin); } while(strlen(buf) < 1 || buf[0] < 'a' || buf[0] > 'h' || buf[1] < '1' || buf[1] > '8'); move->x = buf[0] - 'a'; move->y = buf[1] - '1'; // 合法手かどうかの判定 if (is_legal_move(side, *move)) break; else printf("Illeagal Move\n\n"); } }
// Unified alpha-beta and quiescence search int abq(board *b, int alpha, int beta, int ply, int centiply_extension, bool allow_extensions, bool side_to_move_in_check) { if (search_terminate_requested) return 0; // Check for search termination int alpha_orig = alpha; // For use in later TT storage // Retrieve the value from the transposition table, if appropriate evaluation stored; tt_get(b, &stored); if (!e_eq(stored, no_eval) && stored.depth >= ply && use_ttable) { if (stored.type == qexact || stored.type == exact) return stored.score; if (stored.type == qlowerbound || stored.type == lowerbound) alpha = max(alpha, stored.score); else if (stored.type == qupperbound || stored.type == upperbound) beta = min(beta, stored.score); if (alpha >= beta) return stored.score; } // Futility pruning: enter quiescence early if the node is futile if (use_futility_pruning && !side_to_move_in_check && ply == 1) { if (relative_evaluation(b) + frontier_futility_margin < alpha) ply = 0; } else if (use_futility_pruning && !side_to_move_in_check && ply == 2) { if (relative_evaluation(b) + prefrontier_futility_margin < alpha) ply = 0; } bool quiescence = (ply <= 0); // Generate all possible moves for the quiscence search or normal search, and compute the // static evaluation if applicable. move *moves = NULL; int num_available_moves = 0; if (quiescence) moves = board_moves(b, &num_available_moves, true); // Generate only captures else moves = board_moves(b, &num_available_moves, false); // Generate all moves if (quiescence && !use_qsearch) { free(moves); return relative_evaluation(b); // If qsearch is turned off } // Abort if the quiescence search is too deep (currently 45 plies) if (ply < -quiesce_ply_cutoff) { sstats.qnode_aborts++; free(moves); return relative_evaluation(b); } int quiescence_stand_pat; // Allow the quiescence search to generate cutoffs if (quiescence) { quiescence_stand_pat = relative_evaluation(b); alpha = max(alpha, quiescence_stand_pat); if (alpha >= beta) { free(moves); return quiescence_stand_pat; } } else if (!e_eq(stored, no_eval) && use_tt_move_hueristic) { assert(is_legal_move(b, stored.best)); // TODO // For non-quiescence search, use the TT entry as a hueristic moves[num_available_moves] = stored.best; num_available_moves++; } // Update search stats if (quiescence) sstats.qnodes_searched++; else sstats.nodes_searched++; // Search hueristic: sort exchanges using MVV-LVA if (quiescence && mvvlva) nlopt_qsort_r(moves, num_available_moves, sizeof(move), b, &capture_move_comparator); // Search extensions bool no_more_extensions = false; // Extend the search if we are in check //coord king_loc = b->black_to_move ? b->black_king : b->white_king; bool currently_in_check = side_to_move_in_check; //in_check(b, king_loc.col, king_loc.row, b->black_to_move); if (check_extend && currently_in_check && ply <= check_extend_threshold && !quiescence && allow_extensions) { // only extend in shallow non-quiescence situations centiply_extension += check_extension_centiply; no_more_extensions = true; } // Process any extensions if (allow_extensions && centiply_extension >= 100) { centiply_extension -= 100; ply += 1; } else if (allow_extensions && centiply_extension <= -100) { centiply_extension += 100; ply -= 1; } if (no_more_extensions) allow_extensions = false; // Only allow one check extension move best_move_yet = no_move; int best_score_yet = NEG_INFINITY; int num_moves_actually_examined = 0; // We might end up in checkmate //for (int iterations = 0; iterations < 2; iterations++) { // ABDADA iterations for (int i = num_available_moves - 1; i >= 0; i--) { // Iterate backwards to match MVV-LVA sort order /*int claimed_node_id = -1; if (i != num_available_moves - 1 && iterations == 1) { // Skip redundant young brothers on the first pass if (!tt_try_to_claim_node(b, &claimed_node_id)) continue; // Skip the node if it is already being searched } else tt_always_claim_node(b, &claimed_node_id);*/ apply(b, moves[i]); bool we_moved_into_check; // Choose the more efficient version if possible // If we were already in check, we need to do the expensive search if (side_to_move_in_check) { coord king_loc = b->black_to_move ? b->white_king : b->black_king; // for side that just moved we_moved_into_check = in_check(b, king_loc.col, king_loc.row, !(b->black_to_move)); } else we_moved_into_check = puts_in_check(b, moves[i], !b->black_to_move); // Never move into check if (we_moved_into_check) { unapply(b, moves[i]); //tt_unclaim_node(claimed_node_id); continue; } bool opponent_in_check = puts_in_check(b, moves[i], b->black_to_move); /*coord opp_king_loc = b->black_to_move ? b->black_king : b->white_king; bool opponent_in_check = in_check(b, opp_king_loc.col, opp_king_loc.row, (b->black_to_move));*/ int score = -abq(b, -beta, -alpha, ply - 1, centiply_extension, allow_extensions, opponent_in_check); num_moves_actually_examined++; unapply(b, moves[i]); if (score > best_score_yet) { best_score_yet = score; best_move_yet = moves[i]; } alpha = max(alpha, best_score_yet); if (alpha >= beta) { //tt_unclaim_node(claimed_node_id); break; } //tt_unclaim_node(claimed_node_id); } //} free(moves); // We are done with the array // We have no available moves (or captures) that don't leave us in check // This means checkmate or stalemate in normal search // It might mean no captures are available in quiescence search if (num_moves_actually_examined == 0) { if (quiescence) return quiescence_stand_pat; // TODO: qsearch doesn't understand stalemate or checkmate // This seems paradoxical, but the +1 is necessary so we pick some move in case of checkmate if (currently_in_check) return NEG_INFINITY + 1; // checkmate else return 0; // stalemate } if (quiescence && best_score_yet < quiescence_stand_pat) return quiescence_stand_pat; // TODO experimental stand pat if (search_terminate_requested) return 0; // Search termination preempts tt_put // Record the selected move in the transposition table evaltype type; if (best_score_yet <= alpha_orig) type = (quiescence) ? qupperbound : upperbound; else if (best_score_yet >= beta) type = (quiescence) ? qlowerbound : lowerbound; else type = (quiescence) ? qexact : exact; evaluation eval = {.best = best_move_yet, .score = best_score_yet, .type = type, .depth = ply}; tt_put(b, eval); return best_score_yet; }