/** * * Execute the best play * * */ zet Agent::play(board& b,bool player){ this->playing_color = player; if (lapse(get_generator())){ std::vector<zet> s = enumerate_moves(b,player); int rn_n = std::uniform_int_distribution<int>(0,s.size()-1)(get_generator()); FILE_LOG(logDEBUG)<<" * lapsing - returning random move" <<std::endl; return s[rn_n]; } std::vector<zet> s = solve(b,player); FILE_LOG(logDEBUG)<<"Playing for player "<<((player==BLACK)?"BLACK":"WHITE")<< " there are "<< s.size() <<" moves" << std::endl; FILE_LOG(logDEBUG)<<" board is :" <<b<<std::endl; if(s.empty()){ FILE_LOG(logERROR)<<" board with no moves "<<b <<std::endl; } assert(!s.empty()); zet r; //std::random_shuffle(s.begin(),s.end()); if (player == BLACK || is_negamax()){ r=*std::max_element(s.begin(),s.end(),[](const zet& z1, const zet& z2 ){ return z1.val < z2.val;}); } else{ r=*std::min_element(s.begin(),s.end(),[](const zet& z1, const zet& z2 ){ return z1.val < z2.val;}); } FILE_LOG(logDEBUG)<<((player==BLACK)?"BLACK":"WHITE")<<" playes move "<<r.zet_id<<" with value:"<<r.val <<std::endl; return r; }
static int negamax_algo(struct chessboard *c, int color, int depth, int alpha, int beta) { struct move_list *l; struct raw_move m; int n; int val, best_val = INT_MIN; if (!depth) return !color ? calculate_board_heuristic(c) : -calculate_board_heuristic(c); l = allocate_move_list(); for (int i = 0; i < 16; i++) { n = enumerate_moves(c, (color << 4) | i, l); expanded_moves += n; for (int j = 0; j < n; j++) { get_move(l, j, &m.sx, &m.sy, &m.dx, &m.dy); execute_raw_move(c, &m); evaluated_moves++; val = -negamax_algo(c, !color, depth - 1, -beta, -alpha); unwind_raw_move(c, &m); best_val = max(best_val, val); alpha = max(alpha, val); if (alpha >= beta) break; } } free_move_list(l); return best_val; }
/* Returns sx|sy|dx|dy in an integer byte-by-byte from least to most * significant, indicating which move should be made next. * * We seperate the initial iteration of negamax out like this to track the * actual move associated with the best score. Doing so during the * deeper iterations is a waste of time. */ unsigned int calculate_move(struct chessboard *c, int color, int depth) { struct move_list *l; int n, fbsx = -1, fbsy = -1, fbdx = -1, fbdy = -1; int val, best_val = INT_MIN, alpha = INT_MIN, beta = INT_MAX; struct raw_move m; struct timespec *t; expanded_moves = 0; evaluated_moves = 0; t = start_timer(); l = allocate_move_list(); for (int i = 0; i < 16; i++) { n = enumerate_moves(c, (color << 4) | i, l); expanded_moves += n; for (int j = 0; j < n; j++) { get_move(l, j, &m.sx, &m.sy, &m.dx, &m.dy); execute_raw_move(c, &m); evaluated_moves++; val = -negamax_algo(c, !color, depth - 1, -beta, -alpha); alpha = max(alpha, val); if (val > best_val) { best_val = val; fbsx = m.sx; fbsy = m.sy; fbdx = m.dx; fbdy = m.dy; } debug("Move %d/%d for piece %d (%d,%d) => (%d,%d) has heuristic value %d\n", j + 1, n, (color << 4) | i, m.sx, m.sy, m.dx, m.dy, val); unwind_raw_move(c, &m); } } free_move_list(l); expanded_moves += n; debug("Evaluated %luM/%luM expanded moves in %lu seconds\n", evaluated_moves / 1000000, expanded_moves / 1000000, get_timer_and_free(t) / 1000); return (fbsx) | (fbsy << 8) | (fbdx << 16) | (fbdy << 24); }