int game_load(int slot) { int retval; char temp[80]; board_t *board; if (ch_userdir()) { DBG_ERROR("Failed to enter user directory"); return 1; } comm_send("force\n"); snprintf(temp, sizeof(temp), "save%i.pgn", slot); retval = pgn_parse_file(temp); if (retval) { DBG_ERROR("Failed to parse PGN file '%s'", temp); return 1; } board = history->last->board; ui->update(board, NULL); if (config->player[board->turn] == PLAYER_ENGINE) comm_send("go\n"); else if (config->player[OPPONENT(board->turn)] == PLAYER_ENGINE) { if (board->turn == WHITE) comm_send("white\n"); else comm_send("black\n"); } return retval; }
bitboard getRawMoves(const position * const pos, const int player, const int piece, const int type, const int sq) { bitboard rawMoves = 0; bitboard attackRange = 0; bitboard allPieces = 0; bitboard opponentSquares = 0; assert(sq >= 0 && sq <= 63); allPieces = pos->pieces[BLACK] | pos->pieces[WHITE]; if(CAPTURE == type) { attackRange = getAttackRange(piece, type, sq, allPieces, pos->epSquare); opponentSquares = pos->pieces[OPPONENT(player)]; if(pos->epSquare != -1) { opponentSquares |= _mask[pos->epSquare]; } /* captures */ rawMoves = attackRange & opponentSquares; } else if(NORMAL == type) { attackRange = getAttackRange(piece, type, sq, allPieces, pos->epSquare); /* normal moves, no violence ;) */ rawMoves = attackRange & ~allPieces; } else { assert(0); /* don't know type of move */ } return rawMoves; }
XOBoard negaMaxThink(XOBoard::Player player, XOBoard board) { /* TODO: Fa o miscare si returneaza tabla dupa aceasta miscare. Aceasta * functie de AI trebuie sa respecte acest format pentru ca este data in * constructorul jocului, dar puteti apela aici o functie scrisa de voi. */ int sol = -10, x, y, alpha = -10, beta =10; for (int i = 0; i < 3; ++ i) for (int j = 0; j < 3; ++ j) if (board.is_empty(i,j)) { board.put(player,i,j); int k = -negaMaxMin(OPPONENT(player), board, -beta, -alpha); std::cerr<<i<<" "<<j<<" "<<k<<std::endl; board.erase(i,j); if (k > alpha) { alpha = k; x = i; y = j; } } /* TODO: Stergeti linia de mai jos dupa ce rezolvati. */ board.put(player, x,y); return board; }
static int square_attacked(board_t *b, int square, int side) { board_t board = *b; move_t move; int i; board.turn = side; /* We add a piece because we only want to take capture moves into consideration. */ board.square[square] = KING + OPPONENT(side); move.destination = square; for (i = 0; i < 64; i++) { if (COLOUR(board.square[i]) == side) { move.source = i; if ((PIECE(board.square[i]) == PAWN) && ((square < 8) || (square >= 56))) move.promotion_piece = QUEEN + side; else move.promotion_piece = NONE; if (move_is_semi_valid(&board, &move)) return 1; } } return 0; }
int getMoves(const position * const pos, const int player, const int piece, const int type, move store[]) { bitboard rawMoves = 0; int sq; int numMoves = 0; int opponent; bitboard pieces; opponent = OPPONENT(player); /* opponent in check = player won. should be checked somewhere before this */ assert(!inCheck(pos, opponent, pos->kingSquare[opponent])); pieces = getPieceBitboard(pos, piece); sq = 0; if(IS_KING(piece)) { /* we already know the location of the king */ sq = pos->kingSquare[player]; pieces = pieces >> sq; assert(pieces != 0); /* we should not move the king off the board */ } while(pieces != 0) { if(LSB_SET(pieces)) { rawMoves = getRawMoves(pos, player, piece, type, sq); numMoves += getPieceMoves(pos, player, type, rawMoves, sq, &store[numMoves]); } pieces = pieces >> 1; sq++; } return numMoves; }
int Eval::megaval(char **map, Pos& pos, PLAYER player) { // 1 : win -> 5 aligné ou 10 de score // 2 : bloquer alignement 5 de l'adversaire ou empecher de manger si score adverse == 8 // 3 : faire un 4 libre // 4 : empecher 4 libre // 4 : bloquer un 3 libre // 5 : manger deux pièces // 6 : empêcher l'autre de manger // 7 : poids des aligements return (_eval__alignment(map, pos, player, false) + (1.5 * _eval__alignment(map, pos, static_cast<PLAYER>(OPPONENT(player)), true)) + _eval__eat(map, pos, player) + (1.5 * _eval__eat(map, pos, static_cast<PLAYER>(OPPONENT(player)))) + _eval__block(map, pos, player) + _eval__win(map, pos, player)); }
void OXGame::undo() { _player = OPPONENT(_player); _moveCount--; int x, y; x = _move[_moveCount].x; y = _move[_moveCount].y; _cell[ x ][ y ] = EMPTY; }
/** Un exemplu de functie de gandire care foloseste rezultatele furnizate de * o abordare negaMax exhaustiva. */ int negaMaxMin(XOBoard::Player player, XOBoard& board, int alpha, int beta) { int sol = -10; if( board.is_full()) { return board.get_score(player)-board.get_score(OPPONENT(player)); } for (int i = 0; i < 3; ++ i) for (int j = 0; j < 3; ++ j) if (board.is_empty(i,j)) { board.put(player,i,j); int k = -negaMaxMin(OPPONENT(player), board, -beta, -alpha); board.erase(i,j); if (k >= beta) return beta; if (k > alpha) alpha = k; } return alpha; }
BOOL tsumego_setup_if_closed(ThreadInfo *info, const Board *board, TreeBlock *bl) { const TreeHandle *s = info->s; Stone win_state = S_EMPTY; // Check if the newly created node b is an end state, if so, no need to call dcnn. Stone curr_player = OPPONENT(board->_next_player); if (curr_player == s->params.defender) { // Check if in the given region, whether a player has two eyes. // Check at least one group of player lives. BOOL curr_lives = OneGroupLives(board, curr_player, &s->params.ld_region); if (curr_lives) win_state = curr_player; } if (s->params.defender == S_BLACK && board->_w_cap >= 4) win_state = S_WHITE; else if (s->params.defender == S_WHITE && board->_b_cap >= 4) win_state = S_BLACK; // If it is a terminal state, then we can proceed. // Change the prove/dis_prove number, if one side lives. if (win_state != S_EMPTY) { /* if (win_state == S_WHITE) { char buf[100]; Coord last_move = bl->parent->data.moves[bl->parent_offset]; printf("%s Lives! Final move: %s\n", curr_player == S_BLACK ? "B" : "W", get_move_str(last_move, curr_player, buf)); ShowBoard(board, SHOW_ALL); printf("\n"); } */ int b, w; if (win_state == S_BLACK) { b = 0; w = INIT_PROVE_NUM; } else { w = 0; b = INIT_PROVE_NUM; } // Update the statistics in the parent node. ProveNumber *pn = &bl->parent->cnn_data.ps[bl->parent_offset]; __atomic_store_n(&pn->w, w, __ATOMIC_RELAXED); __atomic_store_n(&pn->b, b, __ATOMIC_RELAXED); bl->n = 0; bl->terminal_status = win_state; // bl->player = mmove.player; return TRUE; } return FALSE; }
void threaded_run_tsumego_bp(ThreadInfo *info, float black_moku, Stone next_player, int end_ply, BOOL board_on_child, BlockOffset child_offset, TreeBlock *b) { // In tsumego, there is no playout policy, and we just evaluate the curent board situation and backprop. TreeHandle *s = info->s; TreePool *p = &s->p; TreeBlock *curr = b; Stone player = next_player; // If the board is pointing to the child, we need to switch the current player. if (board_on_child) player = OPPONENT(player); // Backpropagation. while (curr->parent != NULL) { update_pn(info, board_on_child, player, curr); curr = curr->parent; player = OPPONENT(player); } if (s->params.use_online_model) { // Update the online model. player = board_on_child ? OPPONENT(next_player) : next_player; update_online_model(info, player, b); } }
static int in_check(board_t *board, int turn) { int i; for (i = 0; i < 64; i++) if (board->square[i] == KING + turn) break; if (i == 64) { DBG_ERROR("board is missing a king"); exit(1); } return square_attacked(board, i, OPPONENT(turn)); }
bool OXGame::move(int x, int y) { if (_finished) return 1; _cell[x][y] = _player; _move[_moveCount].x = x; _move[_moveCount].y = y; _moveCount++; if (is5inRow(x, y)) { _winner = _player; _finished = true; } if (_moveCount == _boardSize * _boardSize) _finished = true; _player = OPPONENT(_player); return _finished; }
static void storeMoveIfLegal(const position* const pos, move* m, const int player, move store[], int *numMoves) { position newPosition; int status; int opponent; status = playMove(pos, &newPosition, m, player); if(status != ILLEGAL) { /* move is legal */ /* check that the move does not put players king in check, which would be illegal */ if(!inCheck(&newPosition, player, newPosition.kingSquare[player])) { /* calculate evaluation */ opponent = OPPONENT(player); if(inCheck(&newPosition, opponent, newPosition.kingSquare[opponent])) { m->flags |= CHECK; m->eval += EVAL_CHECK; } store[(*numMoves)++] = *m; } } }
int make_move(board_t *board, move_t *move) { /* Assume that move is valid. */ if ((PIECE(board->square[move->source]) == PAWN) && ((PIECE(board->square[move->destination]) == NONE)) && ((move->source % 8) != (move->destination % 8))) { /* En-passant move. */ int ep = move->destination - 8 * (move->destination > move->source ? 1 : -1); board->captured[board->square[ep]]++; board->square[ep] = NONE; } /* Update captured pieces. */ if (board->square[move->destination] != NONE) board->captured[board->square[move->destination]]++; if ((PIECE(board->square[move->source]) == KING) && (move->destination - move->source == 2)) { /* Kingside castle. */ board->square[move->destination - 1] = board->square[move->destination + 1]; board->square[move->destination + 1] = NONE; } if ((PIECE(board->square[move->source]) == KING) && (move->source - move->destination == 2)) { /* Queenside castle. */ board->square[move->destination + 1] = board->square[move->destination - 2]; board->square[move->destination - 2] = NONE; } if ((PIECE(board->square[move->source]) == PAWN) && (move->destination < 8 || move->destination >= 56)) /* Promotion. */ board->square[move->destination] = move->promotion_piece; else board->square[move->destination] = board->square[move->source]; board->square[move->source] = NONE; board->turn = OPPONENT(board->turn); return 0; }
/* Types of moves: 1) King normal move 2) King captures 3) King castles 4) */ int playMove(const position * const pos, position *newPosition, const move * const m, const int player) { int opponent; opponent = OPPONENT(player); /* assert consistency of position */ assertPosition(pos); /* opponent cannot be check, this should be handled elsewhere */ assert(!inCheck(pos, opponent, pos->kingSquare[opponent])); /* init new position to old position */ *newPosition = *pos; /* opponent has to play in the new position */ newPosition->toPlay = opponent; movePiece(newPosition, m, player); if(IS_CAPTURE(m)) { /* clear bitmask for captured piece */ clearCapturedPiece(newPosition, m); } /* ep is disabled after any move played */ DISABLE_EP(newPosition); /* assert consistency of new position */ assertPosition(newPosition); return 0; }
// =============================================== Back propagation. void threaded_run_bp(ThreadInfo *info, float black_moku, Stone next_player, int end_ply, BOOL board_on_child, BlockOffset child_offset, TreeBlock *b) { TreeHandle *s = info->s; TreePool *p = &s->p; // Dynkomi is adjusted if the win rate is too high. float komi = s->common_params->komi + s->common_variants->dynkomi; float black_count_playout = sigmoid(black_moku - komi); float black_count; // If there is network that predicts moku, then we should combine the results. if (b->has_score && s->params.use_cnn_final_score && end_ply >= s->params.min_ply_to_use_cnn_final_score) { // Final score = final_mixture_ratio * win_rate_prediction + (1.0 - final_mixture_ratio) * playout_result. float cnn_final_playout = sigmoid(b->score - komi); black_count = s->params.final_mixture_ratio * cnn_final_playout + (1 - s->params.final_mixture_ratio) * black_count_playout; } else { black_count = black_count_playout; } // Rave moves encoded in the board. int rave_moves[BOUND_COORD]; if (s->params.use_rave) memset(rave_moves, 0, sizeof(rave_moves)); TreeBlock *curr; BlockOffset curr_offset; if (board_on_child) { curr = b; curr_offset = child_offset; } else { curr = b->parent; curr_offset = b->parent_offset; } // Backprop from b. while (curr != NULL) { Stat* stat = &curr->data.stats[curr_offset]; // Add total first, otherwise the winning rate might go over 1. __sync_add_and_fetch(&stat->total, 1); inc_atomic_float(&stat->black_win, black_count); // stat->total += 1; // stat->black_win += black_count; // __sync_add_and_fetch(&stat->black_win, black_count); // Then update rave, if rave mode is open if (s->params.use_rave) { // Update rave moves. rave_moves[curr->data.moves[curr_offset]] = 1; // Loop over existing moves. for (int i = 0; i < curr->n; ++i) { Coord m = curr->data.moves[i]; if (rave_moves[m]) { Stat *rave_stat = &curr->data.rave_stats[i]; __sync_add_and_fetch(&rave_stat->total, 1); inc_atomic_float(&rave_stat->black_win, black_count); // __sync_add_and_fetch(&rave_stat->black_win, black_count); } } } curr_offset = curr->parent_offset; curr = curr->parent; } if (s->params.use_online_model) { // Update the online model. Stone player = (board_on_child ? OPPONENT(next_player) : next_player); update_online_model(info, player, b); } }
inline void calc_piece_value(struct t_board *board, struct t_chess_eval *eval) { t_chess_color color, opponent; t_chess_square square; t_chess_piece piece; t_chess_piece piece_type; t_bitboard b; t_bitboard attack_squares; t_bitboard moves; int move_count; struct t_pawn_hash_record *pawn_record = eval->pawn_evaluation; for (color = WHITE; color <= BLACK; color++) { t_chess_value middlegame = 0; t_chess_value endgame = 0; opponent = OPPONENT(color); //========================================================= //-- Rooks first //========================================================= piece = PIECEINDEX(color, ROOK); eval->attacklist[piece] = 0; b = board->piecelist[piece]; //-- Remove Rook and Queens (so we can easily evaluate rams) t_bitboard _all_pieces = board->all_pieces ^ board->pieces[color][QUEEN] ^ b; t_bitboard _not_occupied = ~(board->occupied[color] & _all_pieces); //-- Rooks on the 7th if ((b & rank_mask[color][6]) && (board->pieces[opponent][KING] & rank_mask[color][7])) { middlegame += MG_ROOK_ON_7TH; endgame += MG_ROOK_ON_7TH; } //-- Rooks on Open file if (b & pawn_record->open_file) { middlegame += popcount(b & pawn_record->open_file) * pawn_record->pawn_count[color] * MG_ROOK_ON_OPEN_FILE; } //-- Rooks on Semi-Open file if (b & pawn_record->semi_open_file[color]) { middlegame += popcount(b & pawn_record->semi_open_file[color]) * pawn_record->pawn_count[color] * MG_ROOK_ON_SEMI_OPEN_FILE; } //-- Loop around for all pieces while (b) { //-- Find the square it resides square = bitscan_reset(&b); //-- Generate moves moves = rook_magic_moves[square][((rook_magic[square].mask & _all_pieces) * rook_magic[square].magic) >> 52]; eval->attacks[color][ROOK] |= moves; moves &= _not_occupied; //-- Mobility (along ranks) move_count = popcount(moves & square_rank_mask[square]); middlegame += horizontal_rook_mobility[MIDDLEGAME][move_count]; endgame += horizontal_rook_mobility[ENDGAME][move_count]; //-- Mobility (along files) move_count = popcount(moves & square_column_mask[square]); middlegame += vertical_rook_mobility[MIDDLEGAME][move_count]; endgame += vertical_rook_mobility[ENDGAME][move_count]; //-- King safety if (attack_squares = (moves & eval->king_zone[opponent])) { eval->king_attack_count[opponent]++; eval->king_attack_pressure[opponent] += popcount(attack_squares) * 40; } assert(eval->king_zone[opponent] != 0); // piece-square tables middlegame += piece_square_table[piece][MIDDLEGAME][square]; endgame += piece_square_table[piece][ENDGAME][square]; } //========================================================= //-- Queens //========================================================= piece = PIECEINDEX(color, QUEEN); eval->attacklist[piece] = 0; b = board->piecelist[piece]; _all_pieces ^= board->pieces[color][BISHOP]; _not_occupied = ~(board->occupied[color] & _all_pieces); while (b) { //-- Find the square it resides square = bitscan_reset(&b); //-- Rook-like Moves t_bitboard rook_moves = rook_magic_moves[square][((rook_magic[square].mask & _all_pieces) * rook_magic[square].magic) >> 52]; eval->attacklist[piece] |= rook_moves; rook_moves &= _not_occupied; //-- Bishop-like moves t_bitboard bishop_moves = bishop_magic_moves[square][((bishop_magic[square].mask & _all_pieces) * bishop_magic[square].magic) >> 55]; eval->attacklist[piece] |= bishop_moves; bishop_moves &= _not_occupied; //-- Mobility move_count = popcount((rook_moves & square_column_mask[square]) | bishop_moves); middlegame += move_count; //-- King safety if (attack_squares = ((rook_moves | bishop_moves) & eval->king_zone[opponent])) { eval->king_attack_count[opponent]++; eval->king_attack_pressure[opponent] += 80 * popcount(attack_squares); } //-- piece-square tables middlegame += piece_square_table[piece][MIDDLEGAME][square]; endgame += piece_square_table[piece][ENDGAME][square]; } //-- Interaction of double pawns & major pieces if (pawn_record->double_pawns[color]) { int double_pawn_count = popcount(pawn_record->double_pawns[color]); int major_piece_count = popcount(board->pieces[color][ROOK] | board->pieces[color][QUEEN]); switch (major_piece_count) { case 0: break; case 1: middlegame += (double_pawn_count * 12) - pawn_record->semi_open_double_pawns[color] * 30; endgame += (double_pawn_count * 12) - pawn_record->semi_open_double_pawns[color] * 25; break; case 2: middlegame += (double_pawn_count * 24) - pawn_record->semi_open_double_pawns[color] * 35; endgame += (double_pawn_count * 24) - pawn_record->semi_open_double_pawns[color] * 30; break; case 3: middlegame += (double_pawn_count * 30) - pawn_record->semi_open_double_pawns[color] * 40; endgame += (double_pawn_count * 30) - pawn_record->semi_open_double_pawns[color] * 35; break; case 4: middlegame += (double_pawn_count * 30) - pawn_record->semi_open_double_pawns[color] * 40; endgame += (double_pawn_count * 30) - pawn_record->semi_open_double_pawns[color] * 35; break; case 5: middlegame += (double_pawn_count * 30) - pawn_record->semi_open_double_pawns[color] * 40; endgame += (double_pawn_count * 30) - pawn_record->semi_open_double_pawns[color] * 35; break; } } //========================================================= //-- Bishops //========================================================= piece = PIECEINDEX(color, BISHOP); eval->attacklist[piece] = 0; b = board->piecelist[piece]; //-- Bishop pair bonus if (b & (b - 1)) { middlegame += MG_BISHOP_PAIR; endgame += EG_BISHOP_PAIR; } //-- Remove Own Pieces (leave pawns) _all_pieces = board->all_pieces ^ board->pieces[color][KNIGHT] ^ board->pieces[color][QUEEN]; _not_occupied = ~(board->occupied[color] & _all_pieces); while (b) { //-- Find the square it resides square = bitscan_reset(&b); //-- Generate moves moves = bishop_magic_moves[square][((bishop_magic[square].mask & _all_pieces) * bishop_magic[square].magic) >> 55]; eval->attacklist[piece] |= moves; moves &= _not_occupied; //-- Mobility move_count = popcount(moves); middlegame += bishop_mobility[MIDDLEGAME][move_count]; endgame += bishop_mobility[ENDGAME][move_count]; //-- Trapped //middlegame -= trapped_bishop[MIDDLEGAME][move_count]; //endgame -= trapped_bishop[ENDGAME][move_count]; //-- King safety if (attack_squares = (moves & eval->king_zone[opponent])) { eval->king_attack_count[opponent]++; eval->king_attack_pressure[opponent] += 20 * popcount(attack_squares); } // piece-square tables middlegame += piece_square_table[piece][MIDDLEGAME][square]; endgame += piece_square_table[piece][ENDGAME][square]; } //========================================================= //-- Knights //========================================================= piece = PIECEINDEX(color, KNIGHT); eval->attacklist[piece] = 0; b = board->piecelist[piece]; _not_occupied = ~board->occupied[color] & ~eval->attacks[opponent][PAWN]; //-- Outposts t_bitboard knight_outpost = b & pawn_record->potential_outpost[color]; while (knight_outpost) { square = bitscan_reset(&knight_outpost); t_chess_color square_color = SQUARECOLOR(square); //-- Can it be taken by a minor piece? if ((board->pieces[opponent][KNIGHT] == 0) && ((board->pieces[opponent][BISHOP] & color_square_mask[square_color]) == 0)) { middlegame += 25 - square_distance(square, board->king_square[opponent]); endgame += 10; } else { middlegame += 15 - square_distance(square, board->king_square[opponent]); endgame += 8; } } while (b) { //-- Find the square it resides square = bitscan_reset(&b); //-- Opponents King Tropism middlegame -= square_distance(square, board->king_square[opponent]) * 2; //-- Generate moves moves = knight_mask[square]; eval->attacklist[piece] |= moves; //-- Connected to another knight if (moves & board->piecelist[piece]) { middlegame += MG_CONNECTED_KNIGHTS; endgame += EG_CONNECTED_KNIGHTS; } //-- King safety if (attack_squares = (moves & eval->king_zone[opponent])) { eval->king_attack_count[opponent]++; eval->king_attack_pressure[opponent] += 20 * popcount(attack_squares); } //-- Mobility (not including any squares attacked by enemy pawns) moves &= _not_occupied; move_count = popcount(moves); middlegame += knight_mobility[MIDDLEGAME][move_count]; endgame += knight_mobility[ENDGAME][move_count]; // piece-square tables middlegame += piece_square_table[piece][MIDDLEGAME][square]; endgame += piece_square_table[piece][ENDGAME][square]; } //========================================================= //-- King Attacks //========================================================= piece = PIECEINDEX(color, KING); eval->attacklist[piece] = king_mask[board->king_square[color]]; //========================================================= //-- Blocked Central Pawns //========================================================= if (b = (central_kq_pawns[color] & board->pieces[color][PAWN])){ b = ((b << 8) >> (color * 16)) & board->all_pieces; middlegame += -10 * popcount(b); } //-- Add to board scores eval->middlegame += middlegame * (1 - color * 2); eval->endgame += endgame * (1 - color * 2); //-- Create combined attacks eval->attacks[color][BLANK] = eval->attacks[color][PAWN] | eval->attacks[color][ROOK] | eval->attacks[color][BISHOP] | eval->attacks[color][KNIGHT] | eval->attacks[color][QUEEN] | eval->attacks[color][KING]; }
t_chess_value alphabeta(struct t_board *board, int ply, int depth, t_chess_value alpha, t_chess_value beta, BOOL early_cutoff, struct t_move_record *exclude_move) { //-- Should we call qsearch? if (depth <= 0 && !board->in_check) return qsearch_plus(board, ply, depth, alpha, beta); //-- Increment the nodes nodes++; //-- see if we need to update stats */ if ((nodes & message_update_mask) == 0) uci_check_status(board, ply); //-- Local Principle Variation variable struct t_pv_data *pv = &(board->pv_data[ply]); //-- Has the maximum depth been reached if (ply >= MAXPLY || uci.stop) { pv->best_line_length = ply; assert(pv->eval->static_score >= -CHECKMATE && pv->eval->static_score <= CHECKMATE); return pv->eval->static_score; } /* check to see if this is a repeated position or draw by 50 moves */ if (repetition_draw(board)) { pv->best_line_length = ply; return 0; } //-- Mate Distance Pruning if (CHECKMATE - ply <= alpha) { return alpha; } else if (-CHECKMATE + ply >= beta) { return beta; } else if ((!board->in_check) && (-CHECKMATE + ply + 2 >= beta)) { return beta; } //-- Declare local variables struct t_pv_data *next_pv = pv->next_pv; struct t_pv_data *previous_pv = pv->previous_pv; t_chess_value best_score = -CHECKMATE; t_chess_value a = alpha; t_chess_value b = beta; //-- Determine what type of node it is if (beta > alpha + 1) pv->node_type = node_pv; else if (previous_pv->node_type == node_pv) pv->node_type = node_lite_all; else if (previous_pv->node_type == node_cut) pv->node_type = node_all; else pv->node_type = node_cut; //-- Probe Hash struct t_move_record *hash_move = NULL; struct t_hash_record *hash_record = probe(board->hash); //-- Has there been a match? if (hash_record != NULL) { //-- Get the score from the hash table t_chess_value hash_score = get_hash_score(hash_record, ply); //-- Could it make a cut-off? if (early_cutoff && hash_record->depth >= depth) { //-- Score in hash table is at least as good as beta if (hash_record->bound != HASH_UPPER && hash_score >= beta) { hash_record->age = hash_age; assert(hash_score >= -CHECKMATE && hash_score <= CHECKMATE); return hash_score; } //-- Score is worse than alpha if (hash_record->bound != HASH_LOWER && hash_score <= alpha) { hash_record->age = hash_age; assert(hash_score >= -CHECKMATE && hash_score <= CHECKMATE); return hash_score; } //-- Score is more accurate if (hash_record->bound == HASH_EXACT) { hash_record->age = hash_age; pv->best_line_length = ply; update_best_line_from_hash(board, ply); assert(hash_score >= -CHECKMATE && hash_score <= CHECKMATE); return hash_score; } } //-- Store the hash move for further use! hash_move = hash_record->move; //-- Use the hash score to refine the node type if (hash_record->bound != HASH_UPPER && hash_score >= beta) pv->node_type = node_super_cut; else if (hash_record->bound != HASH_LOWER && hash_score <= alpha) pv->node_type = node_super_all; else if (hash_record->bound == HASH_EXACT && pv->node_type == node_all) pv->node_type = node_lite_all; } //-- Beta pruning if (early_cutoff && depth <= 4 && pv->node_type != node_pv && beta < MAX_CHECKMATE && beta > -MAX_CHECKMATE && !board->in_check) { int pessimistic_score = pv->eval->static_score - depth * 50 - 100; if (pessimistic_score >= beta) return pessimistic_score; } //-- Razoring. t_chess_value e; if (early_cutoff && (depth <= 4) && pv->node_type != node_pv && !board->in_check){ t_chess_value razor_margin = depth * 50 + 50; if (pv->eval->static_score + razor_margin <= alpha){ t_chess_value razor_alpha = alpha - razor_margin; e = qsearch_plus(board, ply, depth, razor_alpha, razor_alpha + 1); if (e <= razor_alpha) return e; } } //-- Null Move t_undo undo[1]; pv->mate_threat = 0; pv->null_refutation = NULL; pv->extension = FALSE; if (early_cutoff && can_do_null_move(board, pv, ply, alpha, beta)) { //-- Calculate Reduction //int r = (800 + 70 * depth) / 256 + min(3, (pv->eval->static_score - beta) / 128); int r = min(4, 2 + (25 * depth) / 128 + (pv->eval->static_score - beta) / 128); //int r = 3; //-- Make the changes on the board make_null_move(board, undo); //-- Store the move in the PV data pv->current_move = NULL; //-- Clear the Killer +2 if (ply + 2 <= MAXPLY){ board->pv_data[ply + 2].killer1 = NULL; board->pv_data[ply + 2].killer2 = NULL; } //-- Evaluate the new board position evaluate(board, next_pv->eval); //-- Find the new score e = -alphabeta(board, ply + 1, depth - r - 1 , -beta, -beta + 1, TRUE, NULL); //-- undo the null move unmake_null_move(board, undo); //-- is it good enough for a cut-off? if (e >= beta) { if (e > MAX_CHECKMATE) e = beta; poke(board->hash, e, ply, depth, HASH_LOWER, NULL); return e; } //-- Is there a Mate Threat after a super-reduced move - if so then exit? if (e < -MAX_CHECKMATE){ if (pv->previous_pv->reduction > 1) return alpha; pv->mate_threat = e; } //-- Record the move which refuted the NULL move if (ply < MAXPLY) pv->null_refutation = board->pv_data[ply + 1].current_move; } //-- Internal Iterative Deepening! if (hash_move == NULL && !uci.stop){ //-- PV Nodes - we *really* need a good move if (pv->node_type == node_pv && depth > 2) { //-- Search with reduced depth e = alphabeta(board, ply, depth - 2, alpha, beta, FALSE, NULL); //-- If still no move then search with -INFINITY bound if (e <= alpha) e = alphabeta(board, ply, depth - 2, -CHESS_INFINITY, beta, FALSE, NULL); //-- Probe the hash hash_record = probe(board->hash); //-- Set the hash move if (hash_record != NULL) hash_move = hash_record->move; } //-- Fail high nodes //else if ((pv->node_type == node_cut || pv->node_type == node_super_cut) && (depth >= 7) && alpha > -MAX_CHECKMATE && beta < MAX_CHECKMATE){ // //-- Search with reduced depth // e = alphabeta(board, ply, depth / 2, alpha, beta); // //-- If still no move then search with -INFINITY bound // if (e <= alpha) // e = alphabeta(board, ply, depth / 2, -CHESS_INFINITY, beta); // //-- Probe the hash // hash_record = probe(board->hash); // //-- Set the hash move // if (hash_record != NULL) // hash_move = hash_record->move; //} } //-- Generate All Moves struct t_move_list moves[1]; moves->hash_move = hash_move; if (board->in_check) { generate_evade_check(board, moves); // Are we in checkmate? if (moves->count == 0) { pv->best_line_length = ply; e = -CHECKMATE + ply; return e; } order_evade_check(board, moves, ply); } else { generate_moves(board, moves); order_moves(board, moves, ply); } //-- Enhanced Transposition Cutoff? t_chess_color to_move = board->to_move; if (early_cutoff && (depth > 4) && pv->node_type != node_pv && beta < MAX_CHECKMATE && alpha > -MAX_CHECKMATE && !uci.stop) { BOOL fail_low; while (simple_make_next_move(board, moves, undo)) { //-- Calculate Reduction Conservatively i.e. assume minimum reduction if (board->in_check) pv->reduction = 0; else if (PIECETYPE(moves->current_move->piece) == PAWN && COLOR_RANK(to_move, moves->current_move->to_square) >= 6) pv->reduction = 0; else pv->reduction = 1; //-- Simple version of alpha_beta for tips of search e = -alphabeta_tip(board, ply + 1, depth - pv->reduction, -beta, &fail_low); //-- Take the move back unmake_move(board, undo); //-- Is it good enough for a cutoff? if (e >= beta) { poke(board->hash, e, ply, depth, HASH_LOWER, moves->current_move); assert(e >= -CHECKMATE && e <= CHECKMATE); return e; } //-- Is it going to enhance the move ordering? if (fail_low) { moves->value[moves->imove] += MOVE_ORDER_ETC; assert(moves->move[moves->imove] == moves->current_move); } } //-- Reset the real move count for the "proper" search moves->imove = moves->count; } ////-- No Hash Move for Cut Nodes with score below beta and no good capture and no ETC //if (hash_move == NULL && (any_fail_low == FALSE) && ((pv->node_type == node_cut) || (pv->node_type == node_super_all)) && (depth >= 3) && (beta > pv->eval->static_score) && no_good_captures(board, moves)){ // //-- Search with reduced depth // e = alphabeta(board, ply, depth - 3, alpha, beta); // //-- If still no move then search with -INFINITY bound // if (e <= alpha) // e = alphabeta(board, ply, depth - 3, -CHESS_INFINITY, beta); // //-- Probe the hash // hash_record = probe(board->hash); // //-- Set the hash move // if (hash_record != NULL) // hash_move = hash_record->move; //} //-- Create the list of "bad" captures struct t_move_list bad_moves[1]; bad_moves->count = 0; bad_moves->imove = 0; //-- Reset the move count (must be after IID) pv->legal_moves_played = 0; //-- Variables used to calculate the reduction t_chess_color opponent = OPPONENT(to_move); BOOL in_check = board->in_check; struct t_move_record *last_move = NULL; if (ply > 1) last_move = board->pv_data[ply - 2].current_move; //-- Play moves while (!uci.stop && make_next_move(board, moves, bad_moves, undo)) { //-- Increment the "legal_moves_played" counter pv->legal_moves_played++; pv->current_move = moves->current_move; //========================================// // Futility Pruning //========================================// if (uci.options.futility_pruning && is_futile(pv, next_pv, depth, a, b)){ unmake_move(board, undo); continue; } //-- Clear the Killer +2 if (ply + 2 <= MAXPLY){ board->pv_data[ply + 2].killer1 = NULL; board->pv_data[ply + 2].killer2 = NULL; } //-- Evaluate the new board position evaluate(board, next_pv->eval); ////========================================// //// See if Extension is Necessary ////========================================// //pv->extension = FALSE; //if (pv->mate_threat){ // e = -alphabeta(board, ply + 1, depth - 1, -CHECKMATE, pv->mate_threat + 2); // pv->extension = (e > pv->mate_threat); // if (e <= pv->mate_threat && e <= a) // { // unmake_move(board, undo); // continue; // } //} //========================================// // Calculate reduction //========================================// struct t_move_record *current_move = pv->current_move; //-- In Check? if (board->in_check){ if (see_safe(board, current_move->to_square, 0)) pv->reduction = 0; else if (ply > 3 && board->pv_data[ply - 1].in_check && board->pv_data[ply - 3].in_check) pv->reduction = 0; else pv->reduction = 1; } //-- Pawn push to 7th else if (PIECETYPE(current_move->piece) == PAWN && COLOR_RANK(to_move, current_move->to_square) >= 6){ //-- Pawn Promotion if (current_move->promote_to){ if (pv->legal_moves_played == 1 || PIECETYPE(current_move->promote_to) == QUEEN){ //--Extend if it's a safe pawn promotion or first move if ((pv->current_move->captured && moves->current_move_see_positive) || see_safe(board, current_move->to_square, 0)) pv->reduction = 0; else pv->reduction = 1; } // Reduce Heavily if not a queen promotion else pv->reduction = 5; } //-- Push to the 7th else if (pv->legal_moves_played == 1 || (pv->current_move->captured && moves->current_move_see_positive) || see_safe(board, current_move->to_square, 0)) pv->reduction = 0; else pv->reduction = 1; } //-- First Move? else if (pv->legal_moves_played == 1) pv->reduction = 1; ////-- Under Threat of Mate //else if (pv->mate_threat) // pv->reduction = 1; //-- Good Capture? else if (pv->current_move->captured && moves->current_move_see_positive){ pv->reduction = 1; } //-- Is this getting out of check? else if (in_check){ if (pv->current_move == pv->check_killer1) pv->reduction = 1; else if (PIECETYPE(current_move->piece) == KING){ if (CAN_CASTLE(to_move, board->castling)) pv->reduction = 4; else pv->reduction = 1; } else if (current_move->captured) /* must be a bad capture */ pv->reduction = 2; else if (see_safe(board, current_move->to_square, 0)) pv->reduction = 1; else pv->reduction = 2; } //-- Don't reduce Killers! else if (pv->current_move == pv->killer1){ pv->reduction = 1; } //-- Does it move a threatened piece? else if (pv->null_refutation != NULL && pv->null_refutation->to_square == pv->current_move->from_square){ if (see_safe(board, current_move->to_square, 0)) pv->reduction = 1; else pv->reduction = 3; } //-- Candidate for serious reductions else{ switch (pv->node_type) { case node_cut: pv->reduction = 3; if (pv->current_move->captured) pv->reduction += 1; break; case node_super_cut: pv->reduction = 4; if (pv->current_move->captured) pv->reduction += 1; break; case node_pv: if (pv->legal_moves_played > 2) pv->reduction = 2; else pv->reduction = 1; break; case node_lite_all: if (pv->legal_moves_played > 2) pv->reduction = 2; else pv->reduction = 1; if (pv->current_move->captured) pv->reduction += 1; break; case node_super_all: if (current_move->captured){ if (pv->legal_moves_played < 4) pv->reduction = 3; else pv->reduction = 4; } else if (!see_safe(board, current_move->to_square, 0)){ if (pv->legal_moves_played < 4) pv->reduction = 4; else if (pv->legal_moves_played < 12) pv->reduction = 5; else pv->reduction = 6; } else if (pv->legal_moves_played < 4) pv->reduction = 2; else if (pv->legal_moves_played < 12) pv->reduction = 3; else pv->reduction = 4; break; case node_all: if (current_move->captured){ if (pv->legal_moves_played < 4) pv->reduction = 3; else pv->reduction = 4; } else if (!see_safe(board, current_move->to_square, 0)){ if (pv->legal_moves_played < 4) pv->reduction = 4; else pv->reduction = 5; } else if (pv->legal_moves_played < 4) pv->reduction = 2; else if (pv->legal_moves_played < 18) pv->reduction = 3; else pv->reduction = 4; break; } } //-- Search the next ply at reduced depth e = -alphabeta(board, ply + 1, depth - pv->reduction, -b, -a, TRUE, NULL); //-- Fail high on a super-reduced move? if (e > a && pv->reduction > 1) { pv->reduction = 1; //-- Search again using the full width e = -alphabeta(board, ply + 1, depth - 1, -beta, -a, TRUE, NULL); } //-- Is a research required? else if (alpha + 1 != beta && e > a && a + 1 == b) e = -alphabeta(board, ply + 1, depth - pv->reduction, -beta, -a, TRUE, NULL); unmake_move(board, undo); //-- Is it good enough to cut-off? if (e >= beta) { if (board->in_check) update_check_killers(pv, depth); else update_killers(pv, depth); //-- Record the cutoff cutoffs++; if (pv->legal_moves_played == 1) first_move_cutoffs++; //-- Store in the hash table poke(board->hash, e, ply, depth, HASH_LOWER, pv->current_move); return e; } //-- Is it the best so far? if (e > best_score) { best_score = e; //-- Does it improve upon alpha (i.e. is it part of the PV)? if (e > a) { a = e; //-- Update the Principle Variation update_best_line(board, ply); } } // Reset the zero width window b = a + 1; //-- Was this a fail low at a node which should have failed high? //if (pv->node_type == ) } //-- Is it a draw if (pv->legal_moves_played == 0) { pv->best_line_length = ply; return 0; } //-- Update Hash if (best_score > alpha) poke(board->hash, best_score, ply, depth, HASH_EXACT, pv->best_line[ply]); else poke(board->hash, best_score, ply, depth, HASH_UPPER, NULL); // Return Best Score found assert(best_score >= -CHECKMATE && best_score <= CHECKMATE); return best_score; }
bool is_middle_ladder(Board *b, Coord coord, group_t laddered, Stone lcolor) { /* TODO: Remove the redundant parameters. */ assert(group_at(b, laddered)->liberties == 1); Coord last_lib = get_nlibs_of_group(b, laddered, 1, NULL); assert(last_lib == coord); assert(group_at(b, laddered)->color == lcolor); /* If we can move into empty space or do not have enough space * to escape, this is obviously not a ladder. */ if (immediate_liberty_count(b, coord) != 2) { /* if (DEBUGL(5)) fprintf(stderr, "no ladder, wrong free space\n"); */ return false; } /* A fair chance for a ladder. Group in atari, with some but limited * space to escape. Time for the expensive stuff - set up a temporary * board and start selective 2-liberty search. */ Board *bset = (Board *)malloc(BOARD_MAX_SIZE * 2 * sizeof(Board)); struct move_queue ccq = { .moves = 0 }; if (can_countercapture(b, lcolor, laddered, lcolor, &ccq, 0)) { /* We could escape by countercapturing a group. * Investigate. */ assert(ccq.moves > 0); for (unsigned int i = 0; i < ccq.moves; i++) { Board b2; CopyBoard(&b2, b); bool is_ladder = middle_ladder_walk(&b2, bset, laddered, ccq.move[i], lcolor); // board_done_noalloc(&b2); if (!is_ladder) { free(bset); return false; } } } Board b2; CopyBoard(&b2, b); Coord last_lib2 = get_nlibs_of_group(&b2, laddered, 1, NULL); bool is_ladder = middle_ladder_walk(&b2, bset, laddered, last_lib2, lcolor); // board_done_noalloc(&b2); free(bset); return is_ladder; } bool wouldbe_ladder(Board *b, group_t group, Coord escapelib, Coord chaselib, Stone lcolor) { assert(b->_groups[group].liberties == 2); assert(b->_groups[group].color == lcolor); /* if (DEBUGL(6)) fprintf(stderr, "would-be ladder check - does %s %s play out chasing move %s?\n", stone2str(lcolor), coord2sstr(escapelib, b), coord2sstr(chaselib, b)); */ if (!NEIGHBOR8(escapelib, chaselib)) { /* if (DEBUGL(5)) fprintf(stderr, "cannot determine ladder for remote simulated stone\n"); */ return false; } if (neighbor_count_at(b, chaselib, lcolor) != 1 || immediate_liberty_count(b, chaselib) != 2) { /* if (DEBUGL(5)) fprintf(stderr, "overly trivial for a ladder\n"); */ return false; } bool is_ladder = false; Board *bset = (Board *)malloc(BOARD_MAX_SIZE * 2 * sizeof(Board)); Board b2; CopyBoard(&b2, b); GroupId4 ids; if (TryPlay(&b2, X(chaselib), Y(chaselib), OPPONENT(lcolor), &ids)) { Play(&b2, &ids); Coord last_lib2 = get_nlibs_of_group(&b2, group, 1, NULL); is_ladder = middle_ladder_walk(&b2, bset, group, last_lib2, lcolor); } // board_done_noalloc(&b2); free(bset); return is_ladder; }
// ---------------------------------------------------------------------------- void AICarbon::undo() { int x, y, k; UCHAR p; int xp, yp; assert(check()); moveCount--; xp = remMove[moveCount].x; yp = remMove[moveCount].y; upperLeftCand = remULCand[moveCount]; lowerRightCand = remLRCand[moveCount]; OXCell* c = remCell[moveCount]; c->update1(0); c->update1(1); c->update1(2); c->update1(3); c->update4(); nSt[0][c->status4[0]]++; nSt[1][c->status4[1]]++; c->piece = EMPTY; // zamiana graczy who = OPPONENT(who); opp = OPPONENT(opp); // aktualizowanie mieszania table.undo(xp, yp, who); // modyfikowanie <pat> for (k = 0; k < 4; k++) { x = xp; y = yp; for (p = 16; p != 0; p <<= 1) { x -= DX[k]; y -= DY[k]; cell[x][y].pattern[k][who] ^= p; if (cell[x][y].piece == EMPTY)// && (cell[x][y].adj1 || cell[x][y].adj2)) { cell[x][y].update1(k); nSt[0][cell[x][y].status4[0]]--; nSt[1][cell[x][y].status4[1]]--; cell[x][y].update4(); nSt[0][cell[x][y].status4[0]]++; nSt[1][cell[x][y].status4[1]]++; } } x = xp; y = yp; for (p = 8; p != 0; p >>= 1) { x += DX[k]; y += DY[k]; cell[x][y].pattern[k][who] ^= p; if (cell[x][y].piece == EMPTY)// && (cell[x][y].adj1 || cell[x][y].adj2)) { cell[x][y].update1(k); nSt[0][cell[x][y].status4[0]]--; nSt[1][cell[x][y].status4[1]]--; cell[x][y].update4(); nSt[0][cell[x][y].status4[0]]++; nSt[1][cell[x][y].status4[1]]++; } } } // usuwanie kandydatow cell[xp - 1][yp - 1].adj1--; cell[xp ][yp - 1].adj1--; cell[xp + 1][yp - 1].adj1--; cell[xp - 1][yp ].adj1--; cell[xp + 1][yp ].adj1--; cell[xp - 1][yp + 1].adj1--; cell[xp ][yp + 1].adj1--; cell[xp + 1][yp + 1].adj1--; cell[xp - 2][yp - 2].adj2--; cell[xp ][yp - 2].adj2--; cell[xp + 2][yp - 2].adj2--; cell[xp - 2][yp ].adj2--; cell[xp + 2][yp ].adj2--; cell[xp - 2][yp + 2].adj2--; cell[xp ][yp + 2].adj2--; cell[xp + 2][yp + 2].adj2--; assert(check()); }
/** Puteti folosi functia negaMax pentru a implementa un AI pe baza de negaMAX. * * Functia primeste ca parametri: * * player = Jucatorul care trebuie sa mute in continuare (identitatea * calculatorului care gandeste cu aceasta functie). * Valorile posibile sunt { XOBoard::PlayerX, XOBoard::PlayerO } * * board = Tabla pe care o vede jucatorul care trebuie sa mute in continuare. * * alpha = Inseamna ca player a gasit deja o cale prin care pot sa termin * jocul cu un scor cel putin egal cu alpha. * * beta = Inseamna ca OPPONENT(player) a gasit o cale prin care sa-l forteze pe * player sa termine jocul cu un scor cel mult egal cu beta (cu alte * cuvinte daca player gaseste o modalitate sa castige mai mult de beta, * cel mai probabil analizeaza un scenariu nerealist in care a presupus * ca OPPONENT(player) a fost prost la un moment dat si a facut o * greseala. */ std::pair<int, XOBoard> negaMax(XOBoard::Player player, XOBoard board, int alpha, int beta) { /* Daca s-a terminat jocul, scorul este cel raportat. */ if (board.game_over()) { int myScore = board.get_score(player) - board.get_score(OPPONENT(player)); return std::pair<int, XOBoard>(myScore, board); } /* Generam lista de expansiuni ale tablei (toate mutarile viitoare). */ std::vector<XOBoard> expansions; for (unsigned int i = 0; i < 3; ++i) { for (unsigned int j = 0; j < 3; ++j) { if (board.get(i, j) == '_') { board.put(player, i, j); expansions.push_back(board); board.erase(i, j); } } } /* Verificam care este mutarea cea mai inteleapta. */ XOBoard nextMove; for (unsigned int i = 0; i < expansions.size(); ++i) { /* Fiindca urmatorul nivel de negaMax este privit din partea oponentului, * cand apelez functia trebuie sa neg pe alfa si sa i-l servesc drept * beta pentru ca asta inseamna ca il "avertizez" ca nu sunt fraier si ca * deja stiu un mod prin care el nu poate sa faca mai mult decat -alpha. * * Pe de alta parte, desi eu il limitez pe el superior, din punct de vedere * inferior nu am nici un motiv sa-l limitez, asa ca ii voi servi un alpha * egal cu -INF (nu stiu cat de prost poate el sa joace, n-am cum sa-mi dau * seama). */ /* Acum ne gandim cum s-ar descurca el in situatia asta. */ std::pair<int, XOBoard> outcome = negaMax( OPPONENT(player), expansions[i], -INF, -alpha); /* Vedem ce miscare a reusit sa scoata el in conditiile date. */ int myScore = -outcome.first; /* Analizam jocul din perspectiva taierii alfa-beta. */ if (myScore > beta) { /* Inseamna ca asta e un scenariu in care el ar fi facut o greseala. Noi * stim ca el nu e prost, asa ca din moment ce a gasit deja mai sus in * arbore o modalitate prin care sa ma faca sa termin jocul cu cel mult * beta, n-o sa joace in asa fel incat sa ma puna pe mine in situatia * asta de acum. Aplicam deci, taierea beta. */ return std::pair<int, XOBoard>(beta, nextMove); } else if (myScore > alpha) { /* Inseamna ca tocmai am gasit o miscare prin care eu sa castig la sigur * mai mult decat stiam inainte ca pot sa castig (daca vreti, un fel de * plan "la sigur" mai bun). */ alpha = myScore; nextMove = expansions[i]; } } /* Raportam mutarea aleasa ca fiind cea mai buna. */ return std::pair<int, XOBoard>(alpha, nextMove); }
int inCheck(const position * const pos, const int player, const int targetSquare) { bitboard attacks = 0; int opponent = OPPONENT(player); int sq = 0; bitboard knights = 0; bitboard allPieces = 0; bitboard rooks = 0; bitboard bishops = 0; bitboard pawns = 0; int rankState; int fileState; int rank; int file; bitboard rot_a1h8; bitboard rot_h1a8; int state; bitboard target; /* should be used only for kings or empty squares */ assert(((targetSquare == pos->kingSquare[player])) || ((_mask[targetSquare] & (pos->pieces[WHITE] | pos->pieces[BLACK])) == 0)); if(targetSquare == pos->kingSquare[player]) { target = pos->king[player]; } else { target = _mask[targetSquare]; } /* opponent king attacks */ attacks |= _king[pos->kingSquare[opponent]]; /* incremental checking... */ if(attacks & target) { return 1; } /* opponent knight attacks */ knights = pos->knights[opponent]; sq = 0; while(knights != 0) { if(LSB_SET(knights)) { attacks |= _knight[sq]; /* incremental checking... */ if(attacks & target) { return 1; } } knights = knights >> 1; sq++; } allPieces = pos->pieces[WHITE] | pos->pieces[BLACK]; /* rook and queen attacks */ rooks = pos->rooks[opponent] | pos->queens[opponent]; sq = 0; while(rooks != 0) { if(LSB_SET(rooks)) { rank = sq / 8; rankState = RANK_STATE(allPieces, rank); attacks |= _horz[sq][rankState]; file = sq % 8; fileState = FILE_STATE(rotate(allPieces), file); attacks |= _vert[sq][fileState]; /* incremental checking... */ if(attacks & target) { return 1; } } rooks = rooks >> 1; sq++; } /* bishop and queen attacks */ bishops = pos->bishops[opponent] | pos->queens[opponent]; sq = 0; attacks = 0; // remove while(bishops != 0) { if(LSB_SET(bishops)) { rot_a1h8 = rotate_a1h8(allPieces); state = A1H8_STATE(rot_a1h8, sq); attacks |= _a1h8[sq][state]; rot_h1a8 = rotate_h1a8(allPieces); state = H1A8_STATE(rot_h1a8, sq); attacks |= _h1a8[sq][state]; /* incremental checking... */ if(attacks & target) { return 1; } } bishops = bishops >> 1; sq++; } /* pawn attacks */ pawns = pos->pawns[opponent]; sq = 0; while(pawns != 0) { if(LSB_SET(pawns)) { if(WHITE == opponent) { attacks |= _wpawn_attack[sq]; } else { attacks |= _bpawn_attack[sq]; } /* incremental checking... */ if(attacks & target) { return 1; } } pawns = pawns >> 1; sq++; } return 0; }
static void update_online_model(ThreadInfo *info, Stone player, TreeBlock *b) { TreeHandle *s = info->s; TreePool *p = &s->p; if (b == NULL) error("update_online_model: input b cannot be NULL!"); pthread_mutex_lock(&s->mutex_online_model); // Backprop from b. // Note that for any new tree block, its first opp_preds will be update here. (So a policy will never // avoid a nonleaf child just because its opp_preds is "empty"). for (;b != p->root; b = b->parent, player = OPPONENT(player)) { TreeBlock * parent = b->parent; BlockOffset parent_offset = b->parent_offset; const Stat* stat = &b->parent->data.stats[parent_offset]; if (b->extra == NULL) continue; // Compute online prediction. PRINT_DEBUG("In update_online_model, compute online prediction..\n"); float pred = s->model_bias; for (int i = 0; i < BOARD_SIZE * BOARD_SIZE; i ++) { pred += s->model_weights[i] * b->extra[i]; } pred = sigmoid(pred); // For the parent, it is the win rate from opponent point of view. b->parent->data.opp_preds[parent_offset] = pred; PRINT_DEBUG("In update_online_model, finish computing online prediction..\n"); BOOL update_model = FALSE; float weight = 0.0; float target = -1.0; if (s->params.life_and_death_mode) { // if the current b/w number has zero, then it is an end state and we should update the model, otherwise pass. const ProveNumber *pn = &b->parent->cnn_data.ps[parent_offset]; if (pn == NULL) error("update_online_model: pn cannot be NULL!"); PRINT_DEBUG("In update_online_model with life_and_death_mode is on. Before we set target..\n"); if ((pn->b == 0 && player == S_BLACK) || (pn->w == 0 && player == S_WHITE)) target = 1.0; if ((pn->b == 0 && player == S_WHITE) || (pn->w == 0 && player == S_BLACK)) target = 0.0; if (target > 0) update_model = TRUE; weight = 10.0; } else { // Normal mode. // Get actual win rate. float win_rate = ((float)stat->black_win) / stat->total; if (player == S_WHITE) win_rate = 1 - win_rate; update_model = stat->total > 30; target = win_rate; weight = min(stat->total, 1000); } // One must make sure that if update_model is TRUE, target must be meaningful. PRINT_DEBUG("In update_online_model, accumulate the error ...\n"); float err = target - pred; if (target >= 0) { // Mean average. s->model_acc_err += fabs(err); s->model_count_err ++; } // Gradient update. if (target >= 0 && update_model) { PRINT_DEBUG("In update_online_model, update the model ..."); // Update the weights as well. // y = f(w . x + b) // objective: min ||y - f(w.x+b)||^2, // Note that: err = y - f(w.x+b), f' = f * (1 - f) // grad w_i = - err * f(1-f) * x_i // grad b = - err * f(1-f) float alpha = err * pred * (1 - pred) * weight * s->params.online_model_alpha; for (int i = 0; i < BOARD_SIZE * BOARD_SIZE; ++i) { // Add negative gradient. s->model_weights[i] += alpha * b->extra[i]; } s->model_bias += alpha; } } pthread_mutex_unlock(&s->mutex_online_model); }
// ---------------------------------------------------------------------------- // xp, yp in <4, boardSize + 4) void AICarbon::_move(int xp, int yp) { nSearched++; int x, y, k; UCHAR p; assert(check()); nSt[0][cell[xp][yp].status4[0]]--; nSt[1][cell[xp][yp].status4[1]]--; cell[xp][yp].piece = who; remCell[moveCount] = &cell[xp][yp]; remMove[moveCount] = OXPoint(xp, yp); remULCand[moveCount] = upperLeftCand; remLRCand[moveCount] = lowerRightCand; moveCount++; if(xp - 2 < upperLeftCand.x) upperLeftCand.x = __max(xp - 2, 4); if(yp - 2 < upperLeftCand.y) upperLeftCand.y = __max(yp - 2, 4); if(xp + 2 > lowerRightCand.x) lowerRightCand.x = __min(xp + 2, boardWidth + 3); if(yp + 2 > lowerRightCand.y) lowerRightCand.y = __min(yp + 2, boardHeight + 3); // modyfikowanie <pat> i <points> for (k = 0; k < 4; k++) { x = xp; y = yp; for (p = 16; p != 0; p <<= 1) { x -= DX[k]; y -= DY[k]; cell[x][y].pattern[k][who] |= p; if (cell[x][y].piece == EMPTY)// && (cell[x][y].adj1 || cell[x][y].adj2)) { cell[x][y].update1(k); nSt[0][cell[x][y].status4[0]]--; nSt[1][cell[x][y].status4[1]]--; cell[x][y].update4(); nSt[0][cell[x][y].status4[0]]++; nSt[1][cell[x][y].status4[1]]++; } } x = xp; y = yp; for (p = 8; p != 0; p >>= 1) { x += DX[k]; y += DY[k]; cell[x][y].pattern[k][who] |= p; if (cell[x][y].piece == EMPTY)// && (cell[x][y].adj1 || cell[x][y].adj2)) { cell[x][y].update1(k); nSt[0][cell[x][y].status4[0]]--; nSt[1][cell[x][y].status4[1]]--; cell[x][y].update4(); nSt[0][cell[x][y].status4[0]]++; nSt[1][cell[x][y].status4[1]]++; } } } // dodawanie kandydatow cell[xp - 1][yp - 1].adj1++; cell[xp ][yp - 1].adj1++; cell[xp + 1][yp - 1].adj1++; cell[xp - 1][yp ].adj1++; cell[xp + 1][yp ].adj1++; cell[xp - 1][yp + 1].adj1++; cell[xp ][yp + 1].adj1++; cell[xp + 1][yp + 1].adj1++; cell[xp - 2][yp - 2].adj2++; cell[xp ][yp - 2].adj2++; cell[xp + 2][yp - 2].adj2++; cell[xp - 2][yp ].adj2++; cell[xp + 2][yp ].adj2++; cell[xp - 2][yp + 2].adj2++; cell[xp ][yp + 2].adj2++; cell[xp + 2][yp + 2].adj2++; // aktualizowanie mieszania table.move(xp, yp, who); // zamiana graczy who = OPPONENT(who); opp = OPPONENT(opp); assert(check()); }
/* Checks whether a move is valid, except for whether this puts own king in check. */ static int move_is_semi_valid(board_t *board, move_t *move) { /* Bounds check. */ if ((move->source > 63) || (move->source < 0)) return 0; if ((move->destination > 63) || (move->destination < 0)) return 0; /* Test for moving to same square. */ if (move->source == move->destination) return 0; /* Test for empty source square. */ if (board->square[move->source] == NONE) return 0; /* Test for moving opponent's piece. */ if (COLOUR(board->square[move->source]) != board->turn) return 0; /* Check that a promotion piece is specified for promotion moves. */ if ((PIECE(board->square[move->source]) == PAWN) && ((move->destination < 8) || (move->destination >= 56))) { switch (PIECE(move->promotion_piece)) { case KNIGHT: case ROOK: case BISHOP: case QUEEN: break; default: return 0; } if (COLOUR(move->promotion_piece) != board->turn) return 0; } else if (move->promotion_piece != NONE) /* Promotion piece specified for non-promotion move. */ return 0; switch (PIECE(board->square[move->source])) { case KNIGHT: if ((HOR != 1) && (HOR != 2)) return 0; if ((HOR == 1) && (VERT != 2)) return 0; if ((HOR == 2) && (VERT != 1)) return 0; if (board->square[move->destination] == NONE) break; if (COLOUR(board->square[move->destination]) == COLOUR(board->square[move->source])) return 0; break; case BISHOP: if (HOR != VERT) return 0; if (!ray_ok(board, move)) return 0; break; case ROOK: if ((HOR != 0) && (VERT != 0)) return 0; if (!ray_ok(board, move)) return 0; break; case QUEEN: if ((HOR != 0) && (VERT != 0) && (HOR != VERT)) return 0; if (!ray_ok(board, move)) return 0; break; case PAWN: /* Catch moves in wrong direction. */ if ((move->destination > move->source) && (COLOUR(board->square[move->source]) == BLACK)) return 0; if ((move->destination < move->source) && (COLOUR(board->square[move->source]) == WHITE)) return 0; if (HOR > 1) return 0; if (HOR == 0) { /* Regular or double push. */ if (VERT > 2) return 0; if (VERT == 2) if (!(((move->source >= 8) && (move->source <= 15)) || ((move->source >= 48) && (move->source <= 55)))) return 0; /* Use ray checking code with added requirement that destination square is empty. */ if (!ray_ok(board, move) || (board->square[move->destination] != NONE)) return 0; } else { if (VERT != 1) return 0; if (!ray_ok(board, move)) return 0; /* En-passant move. */ if (board->square[move->destination] == NONE) { if ((COLOUR(board->square[move->source]) == WHITE) && !((move->source >= 32) && (move->source < 40))) return 0; if ((COLOUR(board->square[move->source]) == BLACK) && !((move->source >= 24) && (move->source < 32))) return 0; if (board->square[move->destination + (COLOUR(board->square[move->source]) == WHITE ? -8 : 8)] != PAWN + OPPONENT(COLOUR(board->square[move->source]))) return 0; } } break; case KING: if (HOR > 2) return 0; else if (HOR == 2) { int white = COLOUR(board->square[move->source]) == WHITE; int i, step = (move->destination > move->source ? 1 : -1); int rook = (step == 1 ? (white ? 7 : 63) : (white ? 0 : 56)); /* Castling. */ if (VERT != 0) return 0; if (move->source != (white ? 4 : 60)) return 0; if (board->square[rook] != ROOK + COLOUR(board->square[move->source])) return 0; i = move->source + step; while (i != rook) { if (board->square[i] != NONE) return 0; i += step; } /* Check whether any of the squares the king moves over is under ** attack. Note that destination square is checked later. */ if (square_attacked(board, move->source, (white ? BLACK : WHITE))) return 0; if (square_attacked(board, move->source + step, (white ? BLACK : WHITE))) return 0; } else { if (VERT > 1) return 0; if (!ray_ok(board, move)) return 0; } } return 1; }
int main(int argc, char *argv[]) { if (argc < 5) { printf("Usage: %s server_hostname server_port opponent_level" "(1=dumb, 5, 7, 8)own_level(1=dumb, 5, 7, 8)\n", argv[0]); exit(0); } //Datele pentru conexiune int sockfd, portno; struct sockaddr_in serv_addr; struct hostent *server; //Initializari & creare socket portno = atoi(argv[2]); sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0){ error("ERROR opening socket"); } server = gethostbyname(argv[1]); if (server == NULL) { fprintf(stderr,"ERROR, no such host\n"); exit(0); } bzero((char *) &serv_addr, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; bcopy((char *)server->h_addr, (char *)&serv_addr.sin_addr.s_addr, server->h_length); serv_addr.sin_port = htons(portno); //Conectare la server if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0) { error("ERROR connecting"); } //Trimitem primul mesaj - dificultatea adversarului char buffer[256]; bzero(buffer,256); buffer[0] = atoi(argv[3]); char message_size = 1; // Trimitem 1 octet sendMessage(sockfd, &message_size, 1); sendMessage(sockfd, buffer, 1); // primesc raspuns cu culoarea Player player; bzero(buffer,256); char read_message_size; readMessage(sockfd, &read_message_size, 1); readMessage(sockfd, buffer, read_message_size); if (buffer[0] == 0) { /* sunt jucatorul alb */ player = White; printf("Sunt alb!\n"); } else if (buffer[0] == 1) { /* sunt jucatorul negru */ player = Black; printf("Sunt negru!\n"); }else{ // mesaj invalid; eroare! } BGBoard board; Moves next_move; int dice1, dice2, n; while (true) { bzero(buffer,256); readMessage(sockfd, &read_message_size, 1); readMessage(sockfd, buffer, read_message_size); if (read_message_size == 1) { // Jocul s-a terminat; if (buffer[0] == 'W') { printf("WE WON\n"); } else { printf("WE LOST\n"); } break; } /* se efectueaza mutarea adversarului */ for (int i = 0; i < read_message_size-2; i+=2) { board.move(OPPONENT(player),buffer[i],buffer[i+1]); } /* se extrage zarul din mesajul primit */ dice1 = buffer[read_message_size-2]; dice2 = buffer[read_message_size-1]; /* se calculeaza mutarea optima pentru mine */ next_move = think(board,player,dice1,dice2); n = (int)next_move.size(); /* daca nu am gasit nicio mutare valida, trimit mesaj de '0' bytes */ if (!n) { sendMessage(sockfd, (char*) &n, 1); continue; } /* altfel se efectueaza mutarea */ bzero(buffer,256); for (int i = 0; i < n; i+=2) { buffer[i] = (char)next_move[i]; buffer[i+1] = (char)next_move[i+1]; board.move(player,next_move[i],next_move[i+1]); } /* se trimite la server un mesaj cu mutarea pe care vreau sa o fac */ sendMessage(sockfd, (char*) &n, 1); sendMessage(sockfd, buffer, n); printf("Message sent!\n"); } close(sockfd); return 0; }
void configure_piece_moves(int *i) { int delta, d; t_chess_square s, target; t_chess_color color, opponent; t_chess_piece p, move_piece, capture; struct t_move_record *move = &xmove_list[*i]; for (color = WHITE; color <= BLACK; color++) { opponent = OPPONENT(color); for (s = A1; s <= H8; s++) { for (p = KNIGHT; p <= KING; p++) { if (PIECETYPE(p) != PAWN) { move_piece = p + (color * 8); if (slider[p]) { d = 0; for (delta = direction[p][d]; (delta = direction[p][d]) != 0; d++) { target = x88_SQUARE(s) + delta; while (x88_ONBOARD(target)) { move->captured = BLANK; move->from_square = s; move->to_square = x88_TO_64(target); move->move_type = MOVE_PIECE_MOVE; move->piece = move_piece; move->promote_to = BLANK; move_directory[move->from_square][move->to_square][move->piece] = move; (*i)++; move++; for (capture = KNIGHT; capture <= QUEEN; capture++) { move->captured = capture + (opponent * 8); move->from_square = s; move->to_square = x88_TO_64(target); move->move_type = MOVE_PIECExPIECE; move->piece = move_piece; move->promote_to = BLANK; (*i)++; move++; } if ((x88_TO_64(target) >= A2) && (x88_TO_64(target) <= H7)) { move->captured = PAWN + (opponent * 8); move->from_square = s; move->to_square = x88_TO_64(target); move->move_type = MOVE_PIECExPAWN; move->piece = move_piece; move->promote_to = BLANK; (*i)++; move++; } target += delta; } } } else { d = 0; for (delta = direction[p][d]; (delta = direction[p][d]) != 0; d++) { target = x88_SQUARE(s) + delta; if (x88_ONBOARD(target)) { move->captured = BLANK; move->from_square = s; move->to_square = x88_TO_64(target); if (PIECETYPE(move_piece) == KING) move->move_type = MOVE_KING_MOVE; else move->move_type = MOVE_PIECE_MOVE; move->piece = move_piece; move->promote_to = BLANK; move_directory[move->from_square][move->to_square][move->piece] = move; (*i)++; move++; for (capture = KNIGHT; capture <= QUEEN; capture++) { move->captured = capture + (opponent * 8); move->from_square = s; move->to_square = x88_TO_64(target); if (PIECETYPE(move_piece) == KING) move->move_type = MOVE_KINGxPIECE; else move->move_type = MOVE_PIECExPIECE; move->piece = move_piece; move->promote_to = BLANK; (*i)++; move++; } if ((x88_TO_64(target) >= A2) && (x88_TO_64(target) <= H7)) { move->captured = PAWN + (opponent * 8); move->from_square = s; move->to_square = x88_TO_64(target); if (PIECETYPE(move_piece) == KING) move->move_type = MOVE_KINGxPAWN; else move->move_type = MOVE_PIECExPAWN; move->piece = move_piece; move->promote_to = BLANK; (*i)++; move++; } } } } } } } } }
static bool middle_ladder_walk(Board *b, Board *bset, group_t laddered, Coord nextmove, Stone lcolor) { assert(group_at(b, laddered)->liberties == 1); /* First, escape. */ /* if (DEBUGL(6)) fprintf(stderr, " ladder escape %s\n", coord2sstr(nextmove, b)); */ GroupId4 ids; if (!TryPlay2(b, nextmove, &ids)) error("The play should never be wrong!"); Play(b, &ids); // laddered = group_at(b, laddered); /* if (DEBUGL(8)) { board_print(b, stderr); fprintf(stderr, "%s c %d\n", coord2sstr(laddered, b), board_group_info(b, laddered).libs); } */ int laddered_libs = b->_groups[laddered].liberties; if (laddered_libs == 1) { /* if (DEBUGL(6)) fprintf(stderr, "* we can capture now\n"); */ return true; } if (laddered_libs > 2) { /* if (DEBUGL(6)) fprintf(stderr, "* we are free now\n"); */ return false; } FOR4(nextmove, _, c) { if (board_at(b, c) == OPPONENT(lcolor) && group_at(b, c)->liberties == 1) { /* We can capture one of the ladder stones * anytime later. */ /* XXX: If we were very lucky, capturing * this stone will not help us escape. * That should be pretty rate. */ /* if (DEBUGL(6)) fprintf(stderr, "* can capture chaser\n"); */ return false; } } ENDFOR4 /* Now, consider alternatives. */ int liblist[2], libs = 0; Coord tmp_libs[2]; get_nlibs_of_group(b, laddered, 2, tmp_libs); for (int i = 0; i < 2; i++) { Coord ataristone = tmp_libs[i]; Coord escape = tmp_libs[1 - i]; if (immediate_liberty_count(b, escape) > 2 + NEIGHBOR4(ataristone, escape)) { /* Too much free space, ignore. */ continue; } liblist[libs++] = i; } /* Try out the alternatives. */ bool is_ladder = false; for (int i = 0; !is_ladder && i < libs; i++) { Board *b2 = b; if (i != libs - 1) { b2 = bset++; CopyBoard(b2, b); } Coord libs_b2[2]; get_nlibs_of_group(b2, laddered, 2, libs_b2); Coord ataristone = libs_b2[liblist[i]]; // Coord escape = board_group_info(b2, laddered).lib[1 - liblist[i]]; struct move m = { ataristone, OPPONENT(lcolor) }; bool play_successful = TryPlay2(b2, ataristone, &ids); if (play_successful) Play(b2, &ids); /* If we just played self-atari, abandon ship. */ /* XXX: If we were very lucky, capturing this stone will * not help us escape. That should be pretty rate. */ /* if (DEBUGL(6)) fprintf(stderr, "(%d=%d) ladder atari %s (%d libs)\n", i, res, coord2sstr(ataristone, b2), board_group_info(b2, group_at(b2, ataristone)).libs); */ if (play_successful && group_at(b2, ataristone)->liberties > 1) { Coord last_lib = get_nlibs_of_group(b2, laddered, 1, NULL); is_ladder = middle_ladder_walk(b2, bset, laddered, last_lib, lcolor); } /* Why we need to do deallocation? if (i != libs - 1) { board_done_noalloc(b2); } */ } /* if (DEBUGL(6)) fprintf(stderr, "propagating %d\n", is_ladder); */ return is_ladder; }
const sMinMaxResult MinMax<T>::eval(IBoard &board, const Rules &rules, const sMinMaxState &minMaxState, const IEval &evalFunction) { if (minMaxState.depth == 0 || rules.gameEnded(board, minMaxState.lastStroke, minMaxState.captures[0], minMaxState.captures[1]) != common::eCell::E_CELL) return (((sMinMaxResult) { minMaxState.lastStroke , evalFunction.eval(board, rules, minMaxState) })); uint8_t nCaptures(0); uint16_t captures(0); sMinMaxResult result; result.value = T::initialValue; uint8_t alpha(minMaxState.alphaBeta[0]); uint8_t beta(minMaxState.alphaBeta[1]); // Use iterator instead uint8_t size = board.getSize(); for (uint8_t i = 0; i < size; ++i) { for (uint8_t j = 0; j < size; ++j) { if (board.getHitBoard({i, j}) == false || !rules.isValid(board, {i, j}, minMaxState.currentPlayer)) continue ; board.setCell({i, j}, minMaxState.currentPlayer); nCaptures = rules.applyCapture(board, {i, j}, captures); const auto &next = T::search( board, rules , { (uint8_t)(minMaxState.depth - 1), !minMaxState.maximizing, OPPONENT(minMaxState.currentPlayer) , {i, j} , { (uint8_t)(minMaxState.currentPlayer == common::eCell::P1 ? minMaxState.captures[0] + nCaptures : minMaxState.captures[0]) , (uint8_t)(minMaxState.currentPlayer == common::eCell::P1 ? minMaxState.captures[1] : minMaxState.captures[1] + nCaptures) } , {alpha, beta} } , evalFunction); rules.undoCapture(board, {i, j}, captures, OPPONENT(minMaxState.currentPlayer)); board.setCell({i, j}, common::eCell::NONE); if (T::compareValues(next.value, result.value)) { result.coord = {i, j}; result.value = next.value; } if (T::alphaBetaComp(result.value, alpha, beta)) return (result); T::setAlphaBeta(result.value, alpha, beta); } } return (result); };
void init_move_directory() { int i, j; t_chess_square f, t; t_chess_piece p; t_chess_color color, opponent; static char s[64]; struct t_move_record *move; uchar not_mask; // initialize the global move lists for (f = A1; f <= H8; f++) { for (t = A1; t <= H8; t++) { for (p = BLANK; p <= BLACKKING; p++) { move_directory[f][t][p] = NULL; } } } configure_castling(); i = 4; configure_pawn_push(&i); configure_pawn_capture(&i); configure_piece_moves(&i); init_directory_castling_delta(); //-- Fill in the data for (i = 0, move = &xmove_list[0]; i < GLOBAL_MOVE_COUNT; i++, move++) { move->index = i; move->history = 0; move->refutation = NULL; move->from_to_bitboard = SQUARE64(move->from_square) | SQUARE64(move->to_square); move->capture_mask = 0; if (move->captured && (move->move_type != MOVE_PxP_EP)) move->capture_mask = SQUARE64(move->to_square); color = COLOR(move->piece); opponent = OPPONENT(color); move->hash_delta = white_to_move_hash; move->pawn_hash_delta = white_to_move_hash; if (move->captured) { assert(move->piece >= 0 && move->piece < 15); move->mvvlva = see_piece_value[move->captured] * 100 + (see_piece_value[QUEEN] - see_piece_value[move->piece]); } else move->mvvlva = 0; assert(move->piece >= 0 && move->piece < 16); assert(move->from_square >= 0 && move->from_square < 64); assert(move->to_square >= 0 && move->to_square < 64); switch (move->move_type) { case MOVE_CASTLE: j = move->index; assert( move->index >= 0 && move->index < 4); move->hash_delta ^= hash_value[move->piece][move->from_square] ^ hash_value[move->piece][move->to_square]; move->hash_delta ^= hash_value[castle[j].rook_piece][castle[j].rook_from] ^ hash_value[castle[j].rook_piece][castle[j].rook_to]; move->pawn_hash_delta ^= hash_value[move->piece][move->from_square] ^ hash_value[move->piece][move->to_square]; break; case MOVE_PAWN_PUSH1: move->hash_delta ^= hash_value[move->piece][move->from_square] ^ hash_value[move->piece][move->to_square]; move->pawn_hash_delta ^= hash_value[move->piece][move->from_square] ^ hash_value[move->piece][move->to_square]; break; case MOVE_PAWN_PUSH2: move->hash_delta ^= hash_value[move->piece][move->from_square] ^ hash_value[move->piece][move->to_square]; move->pawn_hash_delta ^= hash_value[move->piece][move->from_square] ^ hash_value[move->piece][move->to_square]; break; case MOVE_PxPAWN: assert(PIECEINDEX(opponent, PAWN) >= 0 && PIECEINDEX(opponent, PAWN) < 64); move->hash_delta ^= hash_value[move->piece][move->from_square] ^ hash_value[move->piece][move->to_square] ^ hash_value[PIECEINDEX(opponent, PAWN)][move->to_square]; move->pawn_hash_delta ^= hash_value[move->piece][move->from_square] ^ hash_value[move->piece][move->to_square] ^ hash_value[PIECEINDEX(opponent, PAWN)][move->to_square]; break; case MOVE_PxPIECE: assert(move->captured >= 0 && move->captured < 16); move->hash_delta ^= hash_value[move->piece][move->from_square] ^ hash_value[move->piece][move->to_square] ^ hash_value[move->captured][move->to_square]; move->pawn_hash_delta ^= hash_value[move->piece][move->from_square] ^ hash_value[move->piece][move->to_square]; break; case MOVE_PxP_EP: move->hash_delta ^= hash_value[move->piece][move->from_square] ^ hash_value[move->piece][move->to_square] ^ hash_value[PIECEINDEX(opponent, PAWN)][(move->to_square - 8) + 16 * color]; move->pawn_hash_delta ^= hash_value[move->piece][move->from_square] ^ hash_value[move->piece][move->to_square] ^ hash_value[PIECEINDEX(opponent, PAWN)][(move->to_square - 8) + 16 * color]; break; case MOVE_PROMOTION: assert(move->promote_to >= 0 && move->promote_to < 16); move->hash_delta ^= hash_value[move->piece][move->from_square] ^ hash_value[move->promote_to][move->to_square]; move->pawn_hash_delta ^= hash_value[move->piece][move->from_square]; break; case MOVE_CAPTUREPROMOTE: assert(move->captured >= 0 && move->captured < 16); assert(move->promote_to >= 0 && move->promote_to < 16); move->hash_delta ^= hash_value[move->piece][move->from_square] ^ hash_value[move->promote_to][move->to_square] ^ hash_value[move->captured][move->to_square]; move->pawn_hash_delta ^= hash_value[move->piece][move->from_square]; break; case MOVE_PIECE_MOVE: move->hash_delta ^= hash_value[move->piece][move->from_square] ^ hash_value[move->piece][move->to_square]; break; case MOVE_PIECExPIECE: move->hash_delta ^= hash_value[move->piece][move->from_square] ^ hash_value[move->piece][move->to_square] ^ hash_value[move->captured][move->to_square]; break; case MOVE_PIECExPAWN: move->hash_delta ^= hash_value[move->piece][move->from_square] ^ hash_value[move->piece][move->to_square] ^ hash_value[move->captured][move->to_square]; move->pawn_hash_delta ^= hash_value[move->captured][move->to_square]; break; case MOVE_KING_MOVE: move->hash_delta ^= hash_value[move->piece][move->from_square] ^ hash_value[move->piece][move->to_square]; move->pawn_hash_delta ^= hash_value[move->piece][move->from_square] ^ hash_value[move->piece][move->to_square]; break; case MOVE_KINGxPIECE: move->hash_delta ^= hash_value[move->piece][move->from_square] ^ hash_value[move->piece][move->to_square] ^ hash_value[move->captured][move->to_square]; move->pawn_hash_delta ^= hash_value[move->piece][move->from_square] ^ hash_value[move->piece][move->to_square]; break; case MOVE_KINGxPAWN: move->hash_delta ^= hash_value[move->piece][move->from_square] ^ hash_value[move->piece][move->to_square] ^ hash_value[move->captured][move->to_square]; move->pawn_hash_delta ^= hash_value[move->captured][move->to_square]; move->pawn_hash_delta ^= hash_value[move->piece][move->from_square] ^ hash_value[move->piece][move->to_square]; break; } } }