Move AIPlayer::alpha_beta(GameBoard* game, int lookahead, int alpha, int beta, int player) { vector<Move> all_moves = get_all_moves(game, player); //cout << "Moves are: " << endl; //cout << m.x +1 << ", " << m.y + 1 << " Score: " << m.score << endl; if (all_moves.empty()) return Move(-1, -1, maximizing_player(player) * 35); if (lookahead == 0) { //return heuristic value of previous player if (!is_maximizing(player)) { Move temp = *(std::max_element(all_moves.begin(), all_moves.end(), [](Move m1, Move m2){return m1.score < m2.score;})); //cout << "return: "<< temp.x +1 << ", " << temp.y + 1 << " Score: " << temp.score << endl; return temp; } else { Move temp = *(min_element(all_moves.begin(), all_moves.end(), [](Move m1, Move m2){return m1.score < m2.score;})); //cout << "return: "<< temp.x +1 << ", " << temp.y + 1 << " Score: " << temp.score << endl; return temp; } } Move return_move; if (is_maximizing(player)) { for (auto m : all_moves) { GameBoard temp_board(game); temp_board.make_move(m.x, m.y, player); Move temp_move = alpha_beta(&temp_board, lookahead - 1, alpha, beta, other_player(player)); if (temp_move.score > alpha) { return_move = m; alpha = temp_move.score; } if (alpha >= beta) { break; } } return return_move; } else { for (auto m : all_moves) { GameBoard temp_board(game); temp_board.make_move(m.x, m.y, player); Move temp_move = alpha_beta(&temp_board, lookahead - 1, alpha, beta, other_player(player)); if (temp_move.score < beta) { beta = temp_move.score; return_move = m; } if (alpha >= beta) { break; } } return return_move; } }
int search_a_goodmove(int color, MoveType * bestMove,int alpha,int beta,int dep) { //alpha-beta根节点函数 int i,best=-INF; int bestmove=-1; memset(history,0,sizeof(history)); int stacklen=createmove(color,dep); if(stacklen==0){ return 0; } for(i=0;i<stacklen;i++){ movestack[dep][i].score=history[movestack[dep][i].x[0]][movestack[dep][i].y[0]][movestack[dep][i].x[1]][movestack[dep][i].y[1]][movestack[dep][i].x[2]][movestack[dep][i].y[2]]; } qsort(movestack[dep],stacklen,sizeof(movestack[dep][0]),cmp); for(i=0;i<stacklen;i++){ movechess(movestack[dep][i]); best=-alpha_beta(color^3,-beta,-alpha,dep+1); unmovechess(movestack[dep][i]); if(best>alpha){ alpha=best; *bestMove=movestack[dep][i]; bestmove=i; } if(alpha>=beta){ bestmove=i; break; } } if(bestmove!=-1){ history[movestack[dep][bestmove].x[0]][movestack[dep][bestmove].y[0]][movestack[dep][bestmove].x[1]][movestack[dep][bestmove].y[1]][movestack[dep][bestmove].x[2]][movestack[dep][bestmove].y[2]]+=2<<(MAX_DEEP-dep); } return 1; }
int alpha_beta(int color,int alpha,int beta,int depth){ int i,best=-INF; int bestmove=-1; if(depth==MAX_DEEP){ return evaluate(color); } int stacklen=createmove(color,depth); if(stacklen==0){ return evaluate(color); } for(i=0;i<stacklen;i++){ movestack[depth][i].score=history[movestack[depth][i].x[0]][movestack[depth][i].y[0]][movestack[depth][i].x[1]][movestack[depth][i].y[1]][movestack[depth][i].x[2]][movestack[depth][i].y[2]]; } qsort(movestack[depth],stacklen,sizeof(movestack[depth][0]),cmp); for(i=0;i<stacklen;i++){ movechess(movestack[depth][i]); best=-alpha_beta(color^3,-beta,-alpha,depth+1); unmovechess(movestack[depth][i]); if(best>alpha){ alpha=best; bestmove=i; } if(alpha>=beta){ bestmove=i; break; } } if(bestmove!=-1){ history[movestack[depth][bestmove].x[0]][movestack[depth][bestmove].y[0]][movestack[depth][bestmove].x[1]][movestack[depth][bestmove].y[1]][movestack[depth][bestmove].x[2]][movestack[depth][bestmove].y[2]]+=2<<(MAX_DEEP-depth); } return alpha; }
ChessAI::ChessAI(Chess* chess, int level) :m_level(level), m_fromPos(-1), m_toPos(-1) { m_board = new Chess(*chess); //Chess* board = new Chess(*chess); //m_boards.push_back(board); int alpha = -10000; int beta = 10000; c = 0; /* for(int i = 0; i < m_level; i++) { Chess* board = new Chess(); m_boards.push_back(board); }*/ //DWORD dw = GetTickCount();//timeGetTime(); time(NULL); t1 = 0; t2 = 0; t3 = 0; t4 = 0; clock_t cc = clock(); alpha_beta(0, alpha, beta); CCLog("calulator times: %d", c); cc = clock() - cc; CCLog("all time: %f", (float)cc/1000); if(t1!=0) CCLog("t1: %f", (float)t1/1000); if(t2!=0) CCLog("t2: %f", (float)t2/1000); if(t3!=0) CCLog("t3: %f", (float)t3/1000); if(t4!=0) CCLog("t4: %f", (float)t4/1000); }
/* --------------------------------------------------------------------------- void iterative_deepening(CHESS_STATE *cs, int max_depth) Purpose: An iterative deepening function The idea is to repeat search at each depth from 1 to n. At start of each search > 1, the best result from the previous search is performed first. This improves the performance of alpha beta. Returns: 3 structures are returned by reference as output parameters: 1. ITERATIVE_DEEPENING_INFO structure 2. Legal Move Count 3. Legal Moves --------------------------------------------------------------------------- */ void iterative_deepening(CHESS_STATE *cs, int time_limit, int *id_count, ITERATIVE_DEEPENING_INFO *id_info, int debug_mode) { int depth; depth=0; if (debug_mode==1) { printf("\nDepth\tValue\tCount\tPlay\tTime\tRate\n"); printf("-----\t-----\t-----\t----\t----\t----\n"); } while (depth < MAX_ITERATIVE_DEEPENING_DEPTH) { depth++; id_info[depth].depth = depth; // start stopwatch clock_t start, end; start = clock(); id_info[depth].value = alpha_beta(cs, 0, depth, -999999, 999999, id_info); end = clock(); double elapsed; elapsed = ((double)(end-start)) / CLOCKS_PER_SEC; int rate; rate = (elapsed==0) ? 0 : (float)id_info[depth].moves_evaluated / elapsed; // display best values if (debug_mode==1) printf("%d\t%d\t%d\t%s\t%.2f\t%d\n",id_info[depth].depth,id_info[depth].value, id_info[depth].moves_evaluated, id_info[depth].best_move, elapsed, rate); // check time limit elapsed if (time_limit==0 || id_info[1].legal_move_count * elapsed > (double)time_limit) { *id_count = depth; break; } } // print legal moves if (debug_mode==1) { printf("\nLegal move count: %d\nMoves: ", id_info[1].legal_move_count); int i; for (i=0; i<id_info[1].legal_move_count; i++) printf("%s ", id_info[1].legal_moves[i]); printf("\n\n"); } }
/** * The search is controlled by the think() function. This function initializes * all variables needed for search, sets up thread pools, and then performs a * search starting at the root node. A root node search is fundamentally * different from searches at other depths because we know for sure that we * should not perform a qsearch or a null-move search. */ void think(app_t *app) { int i, val; node_t root; assert(app); assert(app->game.board); init_node(&root); root.flags |= NODE_ROOT; /* reset our node count board->ply */ app->game.board->ply = 0; app->search.nodes = 0; app->hash.generations++; /* clear the stop bit */ app->search.flags &= ~SEARCH_STOPPED; /* clear search heuristics */ memset(app->search.pv, 0, SEARCH_MAXDEPTH * sizeof(move_t)); memset(app->game.board->killers, 0, 2 * SEARCH_MAXDEPTH * sizeof(int)); memset(app->game.board->history, 0, 2 * 6 * 64 * sizeof(int)); /* set the start time */ get_current_tick(&app->search.start); /* iterative deepening */ for (i = 1; i < app->search.depth; i++) { root.depth = i; val = alpha_beta(app, &root, -MATE, MATE, i); if (app->mode == IUCI) { print_uci_info(app, i, val); } } }
/** * Use brute force to evaluate every possible move, and find the best one. * Once the best move is found, play it. */ void AIPlayer::play() { int x = 0, y = 0, cur_max = 0, best_x = 0, best_y = 0, temp = 0, rand_n = 0; if (diff_ == 1){ Move best_move = alpha_beta(game_, lookahead_, std::numeric_limits<int>::min(), std::numeric_limits<int>::max(), player_); best_x = best_move.x; best_y = best_move.y; } else{ for (x = 0; x < game_->get_size(); x++) { for (y = 0; y < game_->get_size(); y++) { temp = rate_move(x, y, game_, player_); // if this move is better, choose it if (temp > cur_max) { cur_max = temp; best_x = x; best_y = y; } // otherwise flip a coin to make a decision else if (temp == cur_max) { rand_n = rand() % 2; if (rand_n == 1) { best_x = x; best_y = y; } } } } } //cout << "Making Move: " << best_x + 1 << ", " << best_y + 1 << endl; game_->make_move(best_x, best_y, player_); }
int alpha_beta(app_t *app, cnodeptr_t parent, int alpha, int beta, int depth) { int palpha = alpha; int i, score = -MATE, highest = -MATE; node_t node; move_t cutoff = 0; piece_t p; init_node(&node); assert(app); app->search.nodes++; node.depth = depth; /* max depth */ if (app->game.board->ply > (SEARCH_MAXDEPTH - 1)) { /* return evaluate(app->board); */ return quiescent(app, parent, alpha, beta); } /* recursive base */ if (depth == 0) { return evaluate(app->game.board); } /* draws */ if (repetitions(app) || (app->game.board->half >= 100)) { return 0; } /* if we are checked, set the nodes checked flag */ if (check(app->game.board, app->game.board->side)) { node.flags |= NODE_CHECK; /* extend our search by 1 depth if we are in check */ /* NOTES: we may want to NOT extend our search here if the parent is in check, because the means we already extended once */ depth++; } /* TODO: - NULL moves - Late-move reduction - Tactical extensions (pins & forks -> depth++) */ /* probe our table */ if (probe_hash(&app->hash, app->game.board, &cutoff, &score, depth, alpha, beta) == TRUE) { app->hash.cut++; return score; } /* generate moves */ generate_moves(app->game.board, &node.ml, &node.ml); /* reset score */ score = -MATE; /* try to match our hash hit move */ if (cutoff != 0) { for (i = 0; i < node.ml.count; i++) { if (node.ml.moves[i] == cutoff) { node.ml.scores[i] = 20000; break; } } } /* search negamax */ for (i = 0; i < node.ml.count; i++) { /* get the next move ordered */ next_move(i, &node.ml); if (!(do_move(app->game.board, &app->game.undo, node.ml.moves[i]))) continue; score = -alpha_beta(app, &node, -beta, -alpha, depth - 1); node.made++; undo_move(app->game.board, &app->game.undo); /* score whatever is best so far */ if (score > highest) { node.best = node.ml.moves[i]; highest = score; /* update alpha */ if (score > alpha) { if (score >= beta) { /* non-captures causing beta cutoffs (killers) */ if (!is_capture(node.ml.moves[i])) { app->game.board->killers[1][app->game.board->ply] = app->game.board->killers[0][app->game.board->ply]; app->game.board->killers[0][app->game.board->ply] = node.ml.moves[i]; } /* store this beta in our transposition table */ store_hash(&app->hash, app->game.board, node.best, beta, HASH_BETA, depth); return beta; } /* update alpha */ alpha = score; /* update our history */ if (!is_capture(node.best)) { p = app->game.board->pos.squares[move_from(node.best)]; app->game.board->history[piece_color(p)][piece_type(p)][move_to(node.best)] += depth; } } } } /* check for checkmate or stalemate */ if (!node.made) { if (node.flags & NODE_CHECK) { return -MATE + app->game.board->ply; } else { return 0; } } if (alpha != palpha) { /* store this as an exact, since we beat alpha */ store_hash(&app->hash, app->game.board, node.best, highest, HASH_EXACT, depth); } else { /* store the current alpha */ store_hash(&app->hash, app->game.board, node.best, alpha, HASH_ALPHA, depth); } return alpha; }
/* Find the confidence score and dump the confidence output into the file */ static void confidence_utt(char *uttid, FILE * _confmatchsegfp) { seg_hyp_line_t s_hypline; char line[16384]; char dagfile[16384]; const char *fmt; const char *latdir; const char *latext; E_INFO("Processing %s\n", uttid); if (fgets(line, sizeof(line), _confmatchsegfp) == NULL) E_FATAL("Fail to read a line in the matchsegfp for uttid %s\n", uttid); /* Read the hypseg */ if (read_s3hypseg_line(line, &s_hypline, lmset->cur_lm, dict) == HYPSEG_FAILURE) E_FATAL("Fail to parse matchseg in utt ID %s\n", uttid); E_INFO("Matchseg file name %s\n", s_hypline.seq); if (strcmp(uttid, s_hypline.seq)) E_FATAL("Uttids in control file and matchseg file mismatches\n"); /* Read the lattice */ latdir = cmd_ln_str_r(config, "-inlatdir"); latext = cmd_ln_str_r(config, "-latext"); if (latdir) sprintf(dagfile, "%s/%s.%s", latdir, uttid, latext); else sprintf(dagfile, "%s.%s", uttid, latext); E_INFO("Reading DAG file: %s\n", dagfile); if (confidence_word_posterior(dagfile, &s_hypline, uttid, lmset->cur_lm, dict, fpen) == CONFIDENCE_FAILURE) { E_INFO("Fail to compute word posterior probability \n"); } #if 0 if (ca_dag_load_lattice (dagfile, &word_lattice, lmset->cur_lm, dict, fpen) == CONFIDENCE_FAILURE) E_FATAL("Unable to load dag %s for uttid %s\n", dagfile, uttid); /* Compute Alpha-beta */ if (alpha_beta(&word_lattice, lmset->cur_lm, dict) == CONFIDENCE_FAILURE) E_FATAL("Unable to compute alpha beta score for uttid %s\n", uttid); /* Compute Posterior WORD probability */ if (pwp(&s_hypline, &word_lattice) == CONFIDENCE_FAILURE) E_FATAL("Unable to compute pwp for uttid %s\n", uttid); #endif /* Compute LM type */ if (compute_lmtype(&s_hypline, lmset->cur_lm, dict) == CONFIDENCE_FAILURE) E_FATAL("Fail to compute lm type\n"); /* combined LM type */ if (compute_combined_lmtype(&s_hypline) == CONFIDENCE_FAILURE) E_FATAL("Fail to compute lm type\n"); /* Dump pwp line */ fmt = cmd_ln_str_r(config, "-confoutputfmt"); if (!strcmp(fmt, "scores")) { dump_line(stdout, &s_hypline, dict); dump_line(outconfmatchsegfp, &s_hypline, dict); } else { glist_t hyp; srch_hyp_t *s; conf_srch_hyp_t *h; hyp = NULL; for (h = (conf_srch_hyp_t *) s_hypline.wordlist; h; h = h->next) { s = &(h->sh); hyp = glist_add_ptr(hyp, (void *) s); } matchseg_write(stdout, hyp, uttid, NULL, lmset->cur_lm, dict, 0, NULL, 0); matchseg_write(outconfmatchsegfp, hyp, uttid, NULL, lmset->cur_lm, dict, 0, NULL, 0); } #if 0 /* Delete lattice, delete hypsegline */ if (ca_dag_free_lattice(&word_lattice) == CONFIDENCE_FAILURE) { E_WARN("Fail to free lattice.\n"); return CONFIDENCE_FAILURE; } #endif if (free_seg_hyp_line(&s_hypline) != HYPSEG_SUCCESS) E_FATAL("Fail to free the segment hypothesis line structure. \n"); }
Move AIPlayer_fork::alpha_beta(GameBoard* game, int lookahead, int alpha, int beta, int player) { concurrent_vector<Move> all_moves = concurrent_get_all_moves(game, player); //cout << "Moves are: " << endl; //cout << m.x +1 << ", " << m.y + 1 << " Score: " << m.score << endl; if (all_moves.empty()) return Move(-1, -1, maximizing_player(player) * 35); if (lookahead == 0) { //return heuristic value of previous player if (!is_maximizing(player)) { Move temp = *(std::max_element(all_moves.begin(), all_moves.end(), [](Move m1, Move m2) { return m1.score < m2.score; })); //cout << "return: "<< temp.x +1 << ", " << temp.y + 1 << " Score: " << temp.score << endl; return temp; } else { Move temp = *(std::min_element(all_moves.begin(), all_moves.end(), [](Move m1, Move m2) { return m1.score < m2.score; })); //cout << "return: "<< temp.x +1 << ", " << temp.y + 1 << " Score: " << temp.score << endl; return temp; } } Move return_move; if (is_maximizing(player)) { task_group g; for (unsigned int i = 0; i < all_moves.size(); i++) { g.run([=, &all_moves, &return_move, &alpha, & beta] { GameBoard temp_board(game); temp_board.make_move(all_moves[i].x, all_moves[i].y, player); Move temp_move; temp_move = alpha_beta(&temp_board, lookahead - 1, alpha, beta, other_player(player)); if (temp_move.score > alpha) { return_move = all_moves[i]; alpha = temp_move.score; } if (alpha >= beta) { //g.cancel(); } }); } g.wait(); return return_move; } else { task_group g; for (unsigned int i = 0; i < all_moves.size(); i++) { g.run([=, &all_moves, &return_move, &alpha, &beta] { GameBoard temp_board(game); temp_board.make_move(all_moves[i].x, all_moves[i].y, player); Move temp_move; temp_move = alpha_beta(&temp_board, lookahead - 1, alpha, beta, other_player(player)); if (temp_move.score < beta) { beta = temp_move.score; return_move = all_moves[i]; } if (alpha >= beta) { //g.cancel(); } }); } g.wait(); return return_move; } }
/** * Alph-Beta Pruning *@param depth how deep to search the tree *@param alpha the minimax value for alpha *@param beta the minimax value for beta *@return x y coordinates for move */ int AlphaBetaAI::alpha_beta(AlphaBetaNode* node, int depth, int maxPlayer, int myScore, int otherScore, int pointsRemaining) { node->myScore = myScore; node->otherScore = otherScore; node->generateBoard(heapBoard); if (depth <= 0 || pointsRemaining == 0) // or is a terminal node? { //printf("leaf node!\n"); int eval = evaluate(heapBoard, myScore, otherScore, maxPlayer); return eval; } vector<int> legalMoves(node->getMoves()); //std::vector<int> moves; //generateLegalMoves(_board, legalMoves); //vector of all possible legal moves to make while (legalMoves.size()) //for each child node of max and min player { //printf("legal moves: %d\n", legalMoves.size()); int myPoints = myScore; int otherPoints = otherScore; int pointsLeft = pointsRemaining; bool scored = makeNextMove(heapBoard, legalMoves[legalMoves.size()-1], maxPlayer, myPoints, otherPoints, pointsLeft); AlphaBetaNode* kid = find_root(heapBoard, node, pointsLeft); node->hasKid(kid); kid->hasParent(node); //printf("getting eval %d\n", kid); float eval = alpha_beta(kid, 0, scored?maxPlayer:!maxPlayer, myPoints, otherPoints, pointsLeft); /*if (scored) { eval += eval+1; }*/ //printf("got! eval %d\n", kid); if (maxPlayer) { if (eval > node->alpha) { node->alpha = eval; } //printf("%d %d\n", node->alpha, node->beta); if (node->beta <= node->alpha) { //printf("max-cut\n"); unmakeLastMove(heapBoard, legalMoves[legalMoves.size()-1]); break; //beta cut-off } } else { if (eval < node->beta) { node->beta = eval; } //printf("\t\t\t%d %d\n", node->alpha, node->beta); if (node->beta <= node->alpha) { //printf("min-cut\n"); unmakeLastMove(heapBoard, legalMoves[legalMoves.size()-1]); break; //alpha cut-off } } //while (moves.size()) { unmakeLastMove(heapBoard, legalMoves[legalMoves.size()-1]); // moves.pop_back(); //} legalMoves.pop_back(); break; }//end while loop of all legalMoves //printf("about to return thingy\n"); if (maxPlayer) { return node->alpha; } return node->beta; }
static int alpha_beta(int field[][MAX_FIELD_ROWS], int x, int y, int depth, int alpha, int beta, int maximizing_player) { int done = FALSE; int their_longest = get_longest_line(x, y, game.settings.their_botid, field); int your_longest = get_longest_line(x, y, game.settings.your_botid, field); int last_player; int chldx; int chldy; int v; if (depth <= 0 || their_longest >= WIN_LENGTH || your_longest >= WIN_LENGTH) { if (maximizing_player) last_player = 2; else last_player = 1; if (their_longest >= WIN_LENGTH) return evaluate(game.settings.your_botid, game.settings.their_botid, x, y, last_player, field) + (ALPHABETA_LEVEL - depth); else if (your_longest >= WIN_LENGTH) return evaluate(game.settings.your_botid, game.settings.your_botid, x, y, last_player, field) - (ALPHABETA_LEVEL - depth); else return evaluate(game.settings.your_botid, 0, x, y, last_player, field) - (ALPHABETA_LEVEL - depth); } else if (board_full(game.round + ALPHABETA_LEVEL - depth)) return 0; if (maximizing_player) { v = INT_MIN; for (chldx = 0; chldx < game.settings.field_columns; ++chldx) { for (chldy = 0; chldy < game.settings.field_rows; ++chldy) { if (can_be_placed(chldx, chldy, field)) { field[chldx][chldy] = game.settings.your_botid; v = max(v, alpha_beta(field, chldx, chldy, depth - 1, alpha, beta, FALSE)); alpha = max(alpha, v); field[chldx][chldy] = 0; if (beta <= alpha) { done = TRUE; break; } } } if (done) break; } return v; } else { v = INT_MAX; for (chldx = 0; chldx < game.settings.field_columns; ++chldx) { for (chldy = 0; chldy < game.settings.field_rows; ++chldy) { if (can_be_placed(chldx, chldy, field)) { field[chldx][chldy] = game.settings.their_botid; v = min(v, alpha_beta(field, chldx, chldy, depth - 1, alpha, beta, TRUE)); beta = min(beta, v); field[chldx][chldy] = 0; if (beta <= alpha) { done = TRUE; break; } } } if (done) break; } return v; } }
int calc_best_column(void) { struct Player me; struct Player them; int col_to_drop = game.settings.field_columns / 2; int ordering[MAX_FIELD_COLUMNS]; int col; int i; int j; int maxAB = INT_MIN; int resAB; int x; int y; me.id = game.settings.your_botid; them.id = game.settings.their_botid; me.attacks = ATTACK_POINTS_DEFAULT; them.attacks = ATTACK_POINTS_DEFAULT; get_attack_point_counts(&me, &them, game.field); fprintf(stderr, "Your shared odd count: %d\n", me.attacks.shared_odd_count); fprintf(stderr, "Their shared odd count: %d\n", them.attacks.shared_odd_count); fprintf(stderr, "Your shared even count: %d\n", me.attacks.shared_even_count); fprintf(stderr, "Their shared even count: %d\n", them.attacks.shared_even_count); fprintf(stderr, "Your unshared odd count: %d\n", me.attacks.unshared_odd_count); fprintf(stderr, "Their unshared odd count: %d\n", them.attacks.unshared_odd_count); fprintf(stderr, "Your unshared even count: %d\n", me.attacks.unshared_even_count); fprintf(stderr, "Their unshared even count: %d\n", them.attacks.unshared_even_count); if (has_advantage(me, them, FALSE)) fprintf(stderr, "Your advantage!\n"); else if (has_advantage(them, me, FALSE)) fprintf(stderr, "Their advantage...\n"); i = game.settings.field_columns / 2; ordering[0] = i; for (j = 1; j < game.settings.field_columns; ++j) { if (j % 2 == 0) ordering[j] = i + j; else ordering[j] = i - j; i = ordering[j]; } for (x = 0; x < game.settings.field_columns; ++x) { for (y = 0; y < game.settings.field_rows; ++y) { col = ordering[x]; if (can_be_placed(col, y, game.field)) { game.field[col][y] = game.settings.your_botid; if (game.time_remaining > TIMEBANK_LOW && game.round > 11) resAB = alpha_beta(game.field, col, y, ALPHABETA_LEVEL, INT_MIN, INT_MAX, FALSE); else resAB = alpha_beta(game.field, col, y, ALPHABETA_LEVEL - 1, INT_MIN, INT_MAX, FALSE); game.field[col][y] = 0; fprintf(stderr, "(%d, %d): %d\n", col, y, resAB); if (resAB > maxAB) { maxAB = resAB; col_to_drop = col; } } } } return col_to_drop; }
/* -------------------------------------------------------------------------------------- int alpha_beta(CHESS_STATE *node, int depth, int alpha, int beta) purpose: alpha-beta pruning evaluation of minimax algorithm the algorithm always returns an absolute number regardless of player's color. + = good - = bad beta represents previous player's best choice. Doesn't want it if alpha would worsen it. ---------------------------------------------------------------------------------------*/ int alpha_beta(CHESS_STATE *node, int current_depth, int fixed_depth_horizon, int alpha, int beta, ITERATIVE_DEEPENING_INFO *id_info) { // leaf node test (include quiescence search) // note that we extend the current depth to go 1 PAST // the fixed depth horizon, to allow for check validation // at the leaf node. The quiescence search will automatically // cater for check validation if (current_depth>fixed_depth_horizon && !node->flg_is_noisy) { id_info[current_depth-1].moves_evaluated++; return node->board_value; } // otherwise, get opponents moves generate_moves(node); current_depth++; CHESS_STATE *p; CHESS_STATE *q; for (p = node->child_head; p != NULL; p = q) { q = p->next; if (node->flg_is_white_move) { // maximiser if (alpha>=beta) { break; // cutoff } int ab = alpha_beta(p, current_depth, fixed_depth_horizon, alpha, beta, id_info); if (ab > alpha) { alpha=ab; int d; if (current_depth==1) id_info[fixed_depth_horizon].best_move = get_move(p,0); } } else { // minimiser if (alpha>=beta) { break; // cutoff } int ab = alpha_beta(p, current_depth, fixed_depth_horizon, alpha, beta, id_info); if (ab < beta)// { beta=ab; int d; if (current_depth==1) id_info[fixed_depth_horizon].best_move = get_move(p,0); } } } // legal moves (only required for depth 1) if (current_depth==1) { CHESS_STATE *child; int i=0; for (child=node->child_head; child!=NULL; child=child->next) { if (child->flg_is_illegal==0) { id_info[1].legal_moves[i] = get_move(child,0); i++; } } id_info[1].legal_move_count=i; } delete_nodes(node); if (node->flg_is_white_move) { // maximier return alpha; } else { return beta; } }
int ChessAI::alpha_beta(int depth, int& alpha, int& beta) { if(depth == m_level) { c++; //m_boards[depth].nextTurn(); //int value = m_boards[depth]->getBoardValue(); m_board->nextTurn(); int value = m_board->getBoardValue(); m_board->nextTurn(); return value; } vector<pair<int,int> > allMovable; allMovable.clear(); m_board->getAllMovable(allMovable); //m_boards[depth]->getAllMovable(allMovable); //if(m_boards[depth]->isTurnA()) if(m_board->isTurnA()) { //CCLog("A depth:%d", depth); for(size_t i = 0; i < allMovable.size(); i++) { clock_t c3 = clock(); //*m_boards[depth+1] = *m_boards[depth]; c3 = clock() - c3; t3+=c3; clock_t c4 = clock(); pair<int,int> move = allMovable[i]; //m_boards[depth+1]->move(move.first, move.second); //m_boards[depth+1]->check(); m_board->move(move.first, move.second); m_board->check(); vector<int> deads = m_board->getDeadChessmen(); c4 = clock() - c4; t4+=c4; //if(depth == 0 && m_boards[depth+1]->checkFinish()) if(depth == 0 && m_board->checkFinish()) { m_fromPos = move.first; m_toPos = move.second; return 3000; } //m_boards[depth+1]->nextTurn(); m_board->nextTurn(); int a = alpha; int b = beta; int value = alpha_beta(depth+1, alpha, beta); alpha = a; beta = b; m_board->move(move.second, move.first); m_board->revive(deads, false); m_board->nextTurn(); //CCLog("A move:%d->%d, value:%d", move.first,move.second, value); if(value > alpha) { //CCLog("A biggerbiggerbigger alpha: %d", value); //CCLog("A better move:%d->%d", move.first,move.second); alpha = value; if(depth == 0) { m_fromPos = move.first; m_toPos = move.second; } } if(alpha >= beta || value == 3000) { //CCLog("alpha>=beta, %d > %d", alpha, beta); break; } } //CCLog("A return alpha: %d", alpha); return alpha; } else { //CCLog("B depth:%d", depth); for(size_t i = 0; i < allMovable.size(); i++) { clock_t c3 = clock(); //*m_boards[depth+1] = *m_boards[depth]; c3 = clock() - c3; t3+=c3; clock_t c4 = clock(); pair<int,int> move = allMovable[i]; //m_boards[depth+1]->move(move.first, move.second); //m_boards[depth+1]->check(); m_board->move(move.first, move.second); m_board->check(); vector<int> deads = m_board->getDeadChessmen(); c4 = clock() - c4; t4+=c4; //if(depth == 0 && m_boards[depth+1]->checkFinish()) if(depth == 0 && m_board->checkFinish()) { m_fromPos = move.first; m_toPos = move.second; return -3000; } //m_boards[depth+1]->nextTurn(); m_board->nextTurn(); int a = alpha; int b = beta; int value = alpha_beta(depth+1, alpha, beta); alpha = a; beta = b; m_board->move(move.second, move.first); m_board->revive(deads, true); m_board->nextTurn(); //CCLog("B move:%d->%d, value:%d", move.first,move.second, value); if(value < beta) { //CCLog("B smallersmallersmaller beta: %d", value); //CCLog("B better move:%d->%d", move.first,move.second); beta = value; if(depth == 0) { m_fromPos = move.first; m_toPos = move.second; } } if(alpha >= beta || value == -3000) { //CCLog("alpha>=beta, %d > %d", alpha, beta); break; } } //CCLog("B return beta: %d", beta); return beta; } }
/* Alpha Beta Pruning - Minimax Search Algorithm */ move_t alpha_beta(board_t *b, int32_t alpha, int32_t beta, uint32_t ply) { uint8_t i, type; move_list_t *list; move_t m, best; /* Query ECO tree */ if(atoi(config->name) >= 50) if(query_eco(&m)) return m; /* Query transposition table */ type = query_transposition(b->hash, alpha, beta, ply, &m); switch(type) { case TYPE_ALPHA: /* Chooses the best alpha between the old and the one from the table */ alpha = MAX(alpha, m.eval); /* If we have a Beta cutoff, returns */ if(alpha >= beta) return m; break; case TYPE_BETA: /* Chooses the best beta between the old and the one from the table */ beta = MIN(beta, m.eval); /* If we have a Beta cutoff, returns */ if(alpha >= beta) return m; break; case TYPE_EXACT: /* If the table had an exact evaluation, returns it */ return m; case TYPE_INVALID: /* If the transposition is invalid, just ignore and keep going*/ break; } /* If it's a leaf node, evaluate it properly */ if(ply == 0) { m.eval = heuristic(b, onmove); return m; } /* Default type of the value to be inserted in the Transposition table */ type = TYPE_ALPHA; /* Initialize the best possible move as blank */ SET_BLANK_MOVE(best); /* Get the possible next moves */ list = gen_move_list(b, FALSE); /* For each possible next move... */ for(i = 0; i < list->size; i++) { /* Let's see the board after that move... */ move(b, list->move[i]); /* Did we reach any end game condition? */ switch(end(b)) { case CHECK_MATE: m.eval = (onmove == b->onmove) ? -MAX_HEU : MAX_HEU; break; case STALE_MATE: case REPETITION: case FIFTY_MOVES: case TWO_KINGS: m.eval = -MAX_HEU; break; case NO_MATE: default: /* If not, keep searching down in the search tree */ m = alpha_beta(b, -beta, -alpha, ply - 1); m.eval = -m.eval; break; } /* Restores the previous board (before the possible move) */ unmove(b); /* Beta cutoff */ if(m.eval >= beta) { best = list->move[i]; best.eval = m.eval; type = TYPE_BETA; break; /* Alpha cutoff */ } else if(m.eval > alpha) { best = list->move[i]; alpha = best.eval = m.eval; type = TYPE_EXACT; /* Best possible move until now */ } else if(i == 0 || m.eval > best.eval) { best = list->move[i]; best.eval = m.eval; } /* If our time's up, return immediately */ if(get_timeout()) { clear_move_list(list); return best; /*break;*/ } } /* Update the Transposition table */ add_transposition(b->hash, type, ply, best); /* Clear temporary information and return */ clear_move_list(list); return best; }
/* Search thread main function */ void *search_loop(void *arg) { uint8_t ply; move_t mv, mv_tmp; SET_BLANK_MOVE(mv); SET_BLANK_MOVE(mv_tmp); pthread_mutex_lock(&mutex); /* Initializations */ precompute_moves(); precompute_distances(); init_zobrist_keys(); init_history(); init_transposition_table(); config_alarm(config->max_seconds); max_depth = config->max_depth; /* Setup board */ board = set_board("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"); if(board == NULL) quit("Error: Could not setup new board!\n"); /* ECO = encyclopedia of chess openings */ if(atoi(config->name) >= 50) if(!load_eco(board)) quit("Error: Could not load Encyclopedia of Chess Openings!\n"); pthread_mutex_unlock(&mutex); /* Keep alive until the status changes to QUIT */ while(status != QUIT) { switch(status) { case NOP: /* NOP: just wait for a change in status */ pthread_mutex_lock(&mutex); while(status == NOP) pthread_cond_wait(&cond, &mutex); pthread_mutex_unlock(&mutex); break; case FORCE: /* FORCE: wait for a change in status but don't start searches */ pthread_mutex_lock(&mutex); while(status == FORCE) pthread_cond_wait(&cond, &mutex); pthread_mutex_unlock(&mutex); break; case SEARCH: /* Iterative Deepening Search */ /* Save the on-move color */ onmove = board->onmove; /* Sets as blank the move to be played*/ SET_BLANK_MOVE(mv); /* Starts counting the time */ start_alarm(); /* For each depth, search with alpha-beta minimax */ for(ply = 2; ply <= max_depth; ply += 2) { mv_tmp = alpha_beta(board, -MAX_HEU, MAX_HEU, ply); /* Did we run out of time? If so, stops deepening iterations */ if(get_timeout()) break; mv = mv_tmp; } /* Stops counting the time, if it hasn't already reached limit */ stop_alarm(); /* If the move is still blank, use the partial move found */ if(IS_BLANK_MOVE(mv)) mv = mv_tmp; /* Perform the move found in the search */ move(board, mv); /* Returns to the NOP status */ set_status(NOP); break; case PONDER: /* Reserved for future use */ set_status(NOP); break; default: quit("Error: Invalid search status!\n"); } } /* Clean up memory */ clear_eco(); clear_transposition_table(); clear_history(); clear_board(board); /* Exit Search Thread */ return NULL; }