std::string to_usi_string(Move m) { std::stringstream ss; if (!is_ok(m)) { ss <<((m == MOVE_RESIGN) ? "resign" : (m == MOVE_WIN ) ? "win" : (m == MOVE_NULL ) ? "null" : (m == MOVE_NONE ) ? "none" : ""); } else if (is_drop(m)) { ss << Piece(move_from(m)); ss << '*'; ss << move_to(m); } else { ss << move_from(m); ss << move_to(m); if (is_promote(m)) ss << '+'; } return ss.str(); }
std::string move_to_string(const Move m){ Square to = move_to(m); int type = move_type(m); if(type >= MovePN && type <= MovePQ) return square_to_string(move_from(m)) + square_to_string(to) + piece_to_char(Piece(type+Black)); return square_to_string(move_from(m)) + square_to_string(to); }
std::string pretty(Move m) { if (is_drop(m)) return (pretty(move_to(m)) + pretty2(Piece(move_from(m))) + (pretty_jp ? "打" : "*")); else return pretty(move_from(m)) + pretty(move_to(m)) + (is_promote(m) ? (pretty_jp ? "成" : "+") : ""); }
void MovePicker::score_evasions() { // Try good captures ordered by MVV/LVA, then non-captures if // destination square is not under attack, ordered by history // value, and at the end bad-captures and non-captures with a // negative SEE. This last group is ordered by the SEE score. Move m; int seeScore; // Skip if we don't have at least two moves to order if (lastMove < moves + 2) return; for (MoveStack* cur = moves; cur != lastMove; cur++) { m = cur->move; if ((seeScore = pos.see_sign(m)) < 0) cur->score = seeScore - History::MaxValue; // Be sure we are at the bottom else if (pos.is_capture(m)) #if defined(NANOHA) cur->score = piece_value_midgame(pos.piece_on(move_to(m))) - type_of(move_piece(m)) + History::MaxValue; #else cur->score = piece_value_midgame(pos.piece_on(move_to(m))) - type_of(pos.piece_on(move_from(m))) + History::MaxValue; #endif else #if defined(NANOHA) { Piece piece = is_promotion(m) ? Piece(move_piece(m) | PROMOTED) : move_piece(m); cur->score = H.value(piece, move_to(m)); } #else cur->score = H.value(pos.piece_on(move_from(m)), move_to(m)); #endif }
static int ambiguity(int move, const board_t * board) { int from, to, piece; list_t list[1]; int i, n, m; // init from = move_from(move); to = move_to(move); piece = move_piece(move,board); gen_legal_moves(list,board); // no ambiguity? n = 0; for (i = 0; i < list_size(list); i++) { m = list_move(list,i); if (move_piece(m,board) == piece && move_to(m) == to) { n++; } } if (n == 1) return AMBIGUITY_NONE; // file ambiguity? n = 0; for (i = 0; i < list_size(list); i++) { m = list_move(list,i); if (move_piece(m,board) == piece && move_to(m) == to) { if (square_file(move_from(m)) == square_file(from)) n++; } } if (n == 1) return AMBIGUITY_FILE; // rank ambiguity? n = 0; for (i = 0; i < list_size(list); i++) { m = list_move(list,i); if (move_piece(m,board) == piece && move_to(m) == to) { if (square_rank(move_from(m)) == square_rank(from)) n++; } } if (n == 1) return AMBIGUITY_RANK; // square ambiguity return AMBIGUITY_SQUARE; }
void MovePicker::score<CAPTURES>() { // Position::see()を用いると遅い。単に取る駒の価値順に調べたほうがパフォーマンス的にもいい。 // 歩が成る指し手もあるのでこれはある程度優先されないといけない。 // CAPTURE系である以上、打つ指し手は除外されている。 for (auto& m : *this) { // CAPTURES_PRO_PLUSで生成しているので歩の成る指し手が混じる。これは金と歩の価値の差の点数とする。 // 移動させる駒の駒種。駒取りなので移動元は盤上であることは保証されている。 auto pt = type_of(pos.piece_on(move_from(m))); // bool pawn_promo = is_promote(m) && pt == PAWN; // MVV-LVAに、歩の成りに加点する形にしておく。 // → 歩の成りは加点しないほうがよさげ? m.value =// (pawn_promo ? (Value)(Eval::ProDiffPieceValue[PAWN]) : VALUE_ZERO) + (Value)Eval::CapturePieceValue[pos.piece_on(to_sq(m))] - LVA(pt); // 盤の上のほうの段にあるほど価値があるので下の方の段に対して小さなペナルティを課す。 // (基本的には取る駒の価値が大きいほど優先であるから..) // m.value -= Value(1 * relative_rank(pos.side_to_move(), rank_of(move_to(m)))); // → 将棋ではあまりよくないアイデア。 } }
void MovePicker::score_captures() { // Winning and equal captures in the main search are ordered by MVV/LVA. // Suprisingly, this appears to perform slightly better than SEE based // move ordering. The reason is probably that in a position with a winning // capture, capturing a more valuable (but sufficiently defended) piece // first usually doesn't hurt. The opponent will have to recapture, and // the hanging piece will still be hanging (except in the unusual cases // where it is possible to recapture with the hanging piece). Exchanging // big pieces before capturing a hanging piece probably helps to reduce // the subtree size. for(int i = 0; i < numOfMoves; i++) { int seeValue = pos->see(moves[i].move); if(seeValue >= 0) { if(move_promotion(moves[i].move)) moves[i].score = QueenValueMidgame; else moves[i].score = int(pos->midgame_value_of_piece_on(move_to(moves[i].move))) - int(pos->type_of_piece_on(move_from(moves[i].move))); } else moves[i].score = seeValue; } }
void calc_difference(const Position &pos, Move last_move, SearchStack *ss) { const Square from = move_from(last_move); const PieceType type = move_piece_type(last_move); if (type == kKing) { if (pos.side_to_move() == kBlack) calc_difference_king_move_no_capture<kWhite>(pos, ss); else calc_difference_king_move_no_capture<kBlack>(pos, ss); } else { if (from >= kBoardSquare) { calc_no_capture_difference(pos, ss); } else { const PieceType capture = move_capture(last_move); if (capture == kPieceNone) calc_no_capture_difference(pos, ss); else calc_difference_capture(pos, ss); } } }
void MovePicker::score_captures() { // Winning and equal captures in the main search are ordered by MVV/LVA. // Suprisingly, this appears to perform slightly better than SEE based // move ordering. The reason is probably that in a position with a winning // capture, capturing a more valuable (but sufficiently defended) piece // first usually doesn't hurt. The opponent will have to recapture, and // the hanging piece will still be hanging (except in the unusual cases // where it is possible to recapture with the hanging piece). Exchanging // big pieces before capturing a hanging piece probably helps to reduce // the subtree size. // In main search we want to push captures with negative SEE values to // badCaptures[] array, but instead of doing it now we delay till when // the move has been picked up in pick_move_from_list(), this way we save // some SEE calls in case we get a cutoff (idea from Pablo Vazquez). Move m; // Use MVV/LVA ordering for (MoveStack* cur = moves; cur != lastMove; cur++) { m = cur->move; cur->score = piece_value_midgame(pos.piece_on(move_to(m))) - type_of(pos.piece_on(move_from(m))); if (is_promotion(m)) #if defined(NANOHA) cur->score += piece_value_midgame(Piece(move_piece(m) | PROMOTED)); #else cur->score += piece_value_midgame(Piece(promotion_piece_type(m))); #endif } }
void sorting_captures(Move *movelist, int n) { int sorting_values[n]; for(int i = 0; i < n; i += 1) { Move i_move = movelist[i]; int figure = board[move_from(i_move)]; int broken = move_broken(i_move); sorting_values[i] = mvv_lva[figure][broken]; } for(int i = 1; i < n; i += 1) { int j = i; while(sorting_values[j] > sorting_values[j - 1] && j > 0) { int tmp = sorting_values[j]; sorting_values[j] = sorting_values[j - 1]; sorting_values[j - 1] = tmp; Move tmp2 = movelist[j]; movelist[j] = movelist[j - 1]; movelist[j - 1] = tmp2; j -= 1; } } }
int PVS(int alpha, int beta, int depth) { if(is_draw_by_repetition_or_50_moves()) return DRAW; if(depth == 0) return quiescence(alpha, beta); Move movelist[256]; int n = generate_moves(movelist); if(n == 0) { if(!in_check(turn_to_move)) return DRAW; return LOSING + ply - begin_ply; } sorting_moves(movelist, n); int bool_search_pv = 1; Move bestmove = 0; for(int i = 0; i < n; i += 1) { Move i_move = movelist[i]; make_move(i_move); int score; if(bool_search_pv) { score = -PVS(-beta, -alpha, depth - 1); } else { score = -ZWS(-alpha, depth - 1, 1); if(score > alpha) score = -PVS(-beta, -alpha, depth - 1); } unmake_move(i_move); if(score >= beta) { hash_save_entry(depth, beta, i_move, MORE_THAN_BETA); if(!move_broken(i_move)) { history[board[move_from(i_move)]][move_to(i_move)] = depth * depth; } return beta; } if(score > alpha) { bestmove = i_move; alpha = score; } bool_search_pv = 0; } if(bestmove != 0) hash_save_entry(depth, alpha, bestmove, BETWEEN_ALPHA_AND_BETA); else hash_save_entry(depth, alpha, 0, LESS_THAN_ALPHA); return alpha; }
void MovePicker::score_noncaptures() { for(int i = 0; i < numOfMoves; i++) { Move m = moves[i].move; if(m == killer1) moves[i].score = HistoryMax + 2; else if(m == killer2) moves[i].score = HistoryMax + 1; else moves[i].score = H.move_ordering_score(pos->piece_on(move_from(m)), m); } }
void sorting_moves(Move *movelist, int n) { int sorting_values[n]; Entry *entry = hash_get_entry(); Move hash_move = 0; if(entry != NULL) hash_move = entry->bestmove; for(int i = 0; i < n; i += 1) { Move i_move = movelist[i]; if(i_move == hash_move) { sorting_values[i] = 10000000; } else if(move_broken(i_move)) { int figure = board[move_from(i_move)]; int broken = move_broken(i_move); sorting_values[i] = mvv_lva[figure][broken]; } else { sorting_values[i] = history[board[move_from(i_move)]][move_to(i_move)]; } } for(int i = 1; i < n; i += 1) { int j = i; while(sorting_values[j] > sorting_values[j - 1] && j > 0) { int tmp = sorting_values[j]; sorting_values[j] = sorting_values[j - 1]; sorting_values[j - 1] = tmp; Move tmp2 = movelist[j]; movelist[j] = movelist[j - 1]; movelist[j - 1] = tmp2; j -= 1; } } }
void MovePicker::score_evasions() { for(int i = 0; i < numOfMoves; i++) { Move m = moves[i].move; if(m == ttMove) moves[i].score = 2*HistoryMax; else if(!pos->square_is_empty(move_to(m))) { int seeScore = pos->see(m); moves[i].score = (seeScore >= 0)? seeScore + HistoryMax : seeScore; } else moves[i].score = H.move_ordering_score(pos->piece_on(move_from(m)), m); } }
void unmake_move(Move move) { turn_to_move = not_turn_to_move; ply -= 1; int direction_of_pawns = turn_to_move == WHITE? -10: 10; int from = move_from(move); int to = move_to(move); int figure = board[move_to(move)]; int broken = move_broken(move); int turn = move_turn(move); if(turn) board[from] = create_figure(turn_to_move, PAWN); else board[from] = figure; if(get_value(figure) == PAWN && to == ply->en_passant) { board[to] = EMPTY; board[to - direction_of_pawns] = create_figure(not_turn_to_move, PAWN); } else board[to] = broken; if(get_value(figure) == KING) { int horizontal; if(turn_to_move == WHITE) { place_of_white_king = from; horizontal = 90; } else { place_of_black_king = from; horizontal = 20; } if(from - to == 2) { board[horizontal + 1] = board[horizontal + 4]; board[horizontal + 4] = EMPTY; } else if(to - from == 2) { board[horizontal + 8] = board[horizontal + 6]; board[horizontal + 6] = EMPTY; } } }
void MovePicker::score_noncaptures() { Move m; for (MoveStack* cur = moves; cur != lastMove; cur++) { m = cur->move; #if defined(NANOHA) assert(m != MOVE_NULL); Piece piece = is_promotion(m) ? Piece(move_piece(m) | PROMOTED) : move_piece(m); cur->score = H.value(piece, move_to(m)); #else Square from = move_from(m); cur->score = H.value(pos.piece_on(from), move_to(m)); #endif } }
void move_to_str(Move move, char *string) { int from = move_from(move); int to = move_to(move); int turn = move_turn(move); string[0] = (from % 10) - 1 + 'a'; string[1] = 9 - (from / 10) + '1'; string[2] = (to % 10) - 1 + 'a'; string[3] = 9 - (to / 10) + '1'; switch(get_value(turn)) { case QUEEN : string[4] = 'q'; break; case ROOK : string[4] = 'r'; break; case BISHOP: string[4] = 'b'; break; case KNIGHT: string[4] = 'n'; break; default: string[4] = '\0'; } string[5] = '\0'; }
void fun(){ int i; Matrix a,b; Vector v; assign(&a,&e); for(i=0;i<nodenum;i++){ move_from(&b,vertex[i].x, vertex[i].y); multiply(&a,&b); rotate(&b,vertex[i].degree); multiply(&a,&b); move_back(&b,vertex[i].x, vertex[i].y); multiply(&a,&b); assign(&mat[i],&a); } for(i =0;i < 3;i++){ printf("%.2lf %.2lf %.2lf\n", a[i][0], a[i][1], a[i][2]); } v[0] = a[2][1]*a[1][0] + a[2][0]*(1-a[1][1]); v[0]/= ((1-a[1][1])*(1-a[0][0]) - a[0][1] * a[1][0]); v[1] = a[0][1] * v[0] + a[2][1]*a[1][0] ; v[1] /= (1 - a[1][1]); v[2] = 1; vertex[0].x = v[0]; vertex[0].y = v[1]; for(i =0;i < nodenum-1;i++){ V_mul_M(&v, &mat[i]); vertex[i+1].x = v[0]; vertex[i+1].y = v[1]; v[0] = vertex[0].x; v[1] = vertex[0].y; v[2] = 1; } for(i=0;i<nodenum;i++){ printf("%.2lf %.2lf\n", vertex[i].x, vertex[i].y); } }
void make_move(Move move) { U64 hash = ply->hash; int direction_of_pawns = turn_to_move == WHITE? -10: 10; int from = move_from(move); int figure = board[from]; int to = move_to(move); int turn = move_turn(move); int broken = move_broken(move); board[from] = EMPTY; hash ^= zobrist_piecesquare[figure][from]; if(turn) { board[to] = turn; hash ^= zobrist_piecesquare[turn][to]; } else { board[to] = figure; hash ^= zobrist_piecesquare[figure][to]; } if(get_value(figure) == PAWN && to == ply->en_passant) { int tmp = to - direction_of_pawns; board[tmp] = EMPTY; hash ^= zobrist_piecesquare[create_figure(not_turn_to_move, PAWN)][tmp]; } else if(broken) { hash ^= zobrist_piecesquare[broken][to]; } if(ply->en_passant) { hash ^= zobrist_en_passant[ply->en_passant]; } ply += 1; if(get_value(figure) == PAWN && to - from == direction_of_pawns * 2) { ply->en_passant = from + direction_of_pawns; hash ^= zobrist_en_passant[ply->en_passant]; } else { ply->en_passant = 0; } ply->castlings = (ply - 1)->castlings; if(get_value(figure) == KING) { int horizontal; if(turn_to_move == WHITE) { place_of_white_king = to; make_white_castlings_is_incorrect(); horizontal = 90; } else { place_of_black_king = to; make_black_castlings_is_incorrect(); horizontal = 20; } if(from - to == 2) { board[horizontal + 4] = board[horizontal + 1]; board[horizontal + 1] = EMPTY; } else if(to - from == 2) { board[horizontal + 6] = board[horizontal + 8]; board[horizontal + 8] = EMPTY; } } if(board[98] != create_figure(WHITE, ROOK)) make_K_castling_is_incorrect(); if(board[91] != create_figure(WHITE, ROOK)) make_Q_castling_is_incorrect(); if(board[28] != create_figure(BLACK, ROOK)) make_k_castling_is_incorrect(); if(board[21] != create_figure(BLACK, ROOK)) make_q_castling_is_incorrect(); if(broken || get_value(figure) == PAWN) ply->number_of_insignificant_plies = 0; else ply->number_of_insignificant_plies = (ply - 1)->number_of_insignificant_plies + 1; turn_to_move = not_turn_to_move; hash ^= zobrist_color; hash ^= zobrist_castlings[(ply - 1)->castlings]; hash ^= zobrist_castlings[ply->castlings]; ply->hash = hash; }
static int print_pv(app_t *app, int depth) { int count = 0; piece_t p; u8 from; move_t mv; assert(app); assert(app->game.board); #define VIA_PROBE #ifdef VIA_PROBE /* get the current position */ mv = probe_hash_move(&app->hash, app->game.board->key); while ((mv != 0) && (count < depth)) { if (find_move(app, mv)) { from = move_from(mv); p = app->game.board->pos.squares[from]; if (do_move(app->game.board, &app->game.undo, mv)) { app->search.pv[count++] = mv; print_algebraic(p, mv); printf(" "); } else { break; } } else { break; } mv = probe_hash_move(&app->hash, app->game.board->key); } #else while (app->search.pv[count]) { mv = app->search.pv[count++]; if (find_move(app, mv)) { from = move_from(mv); p = app->board->pos.squares[from]; if (do_move(app->board, &app->ul, mv)) { print_algebraic(p, mv); printf(" "); } else { break; } } else { break; } } #endif while (app->game.board->ply) { undo_move(app->game.board, &app->game.undo); } return count; }
void Thread::move_good(const Move b_move, const Move h_move[256], const int h_size, const int ply, const int depth){ Piece p; Square from, to; if(!is_tactical(b_move) && b_move != MoveNull){ if(killer[ply][0] != b_move) { killer[ply][1] = killer[ply][0]; killer[ply][0] = b_move; } p = pos.get_piece(move_from(b_move)); to = move_to(b_move); from = move_from(b_move); #ifndef NDEBUG if(p>13 || to > 63 || from > 63){ std::cerr<<"Wrong good move!"<<std::endl; pos.print(); std::cerr<<"move:"<<move_to_string(b_move)<<std::endl; exit(1); } #endif history[p][from][to] += depth*depth; if (history[p][from][to] >= HistoryMax) { for (Piece p = WP; p <= BK; p++) { for(Square sq_f=A1; sq_f<=H8; sq_f++){ for(Square sq_t=A1; sq_t<=H8; sq_t++){ history[p][sq_f][sq_t] /= 2; } } } } his_tot[p][from][to]++; his_hit[p][from][to]++; if (his_tot[p][from][to] >= HistoryMax) { his_tot[p][from][to] = (his_tot[p][from][to] + 1) / 2; his_hit[p][from][to] = (his_hit[p][from][to] + 1) / 2; } } for(int i=0; i<h_size; i++){ if(is_tactical(h_move[i])) continue; p = pos.get_piece(move_from(h_move[i])); to = move_to(h_move[i]); from = move_from(h_move[i]); #ifndef NDEBUG if(p>13 || to > 63 || from > 63){ std::cerr<<"Wrong good move!"<<std::endl; pos.print(); std::cerr<<"move:"<<move_to_string(h_move[i])<<std::endl; exit(1); } #endif his_tot[p][from][to]++; if (his_tot[p][from][to] >= HistoryMax) { his_tot[p][from][to] = (his_tot[p][from][to] + 1) / 2; his_hit[p][from][to] = (his_hit[p][from][to] + 1) / 2; } } }
int Thread::eval_history_move(const Move move) const { Piece p = pos.get_piece(move_from(move)); Square to = move_to(move); Square from = move_from(move); return (his_hit[p][from][to] * 16384) / his_tot[p][from][to]; }
Move move_from_san(Position &pos, const std::string &movestr) { assert(pos.is_ok()); MovePicker mp = MovePicker(pos, false, MOVE_NONE, MOVE_NONE, MOVE_NONE, MOVE_NONE, OnePly); // Castling moves if(movestr == "O-O-O") { Move m; while((m = mp.get_next_move()) != MOVE_NONE) if(move_is_long_castle(m) && pos.move_is_legal(m)) return m; return MOVE_NONE; } else if(movestr == "O-O") { Move m; while((m = mp.get_next_move()) != MOVE_NONE) if(move_is_short_castle(m) && pos.move_is_legal(m)) return m; return MOVE_NONE; } // Normal moves const char *cstr = movestr.c_str(); const char *c; char *cc; char str[10]; int i; // Initialize str[] by making a copy of movestr with the characters // 'x', '=', '+' and '#' removed. cc = str; for(i=0, c=cstr; i<10 && *c!='\0' && *c!='\n' && *c!=' '; i++, c++) if(!strchr("x=+#", *c)) { *cc = strchr("nrq", *c)? toupper(*c) : *c; cc++; } *cc = '\0'; int left = 0, right = strlen(str) - 1; PieceType pt = NO_PIECE_TYPE, promotion; Square to; File fromFile = FILE_NONE; Rank fromRank = RANK_NONE; // Promotion? if(strchr("BNRQ", str[right])) { promotion = piece_type_from_char(str[right]); right--; } else promotion = NO_PIECE_TYPE; // Find the moving piece: if(left < right) { if(strchr("BNRQK", str[left])) { pt = piece_type_from_char(str[left]); left++; } else pt = PAWN; } // Find the to square: if(left < right) { if(str[right] < '1' || str[right] > '8' || str[right-1] < 'a' || str[right-1] > 'h') return MOVE_NONE; to = make_square(file_from_char(str[right-1]), rank_from_char(str[right])); right -= 2; } else return MOVE_NONE; // Find the file and/or rank of the from square: if(left <= right) { if(strchr("abcdefgh", str[left])) { fromFile = file_from_char(str[left]); left++; } if(strchr("12345678", str[left])) fromRank = rank_from_char(str[left]); } // Look for a matching move: Move m, move = MOVE_NONE; int matches = 0; while((m = mp.get_next_move()) != MOVE_NONE) { bool match = true; if(pos.type_of_piece_on(move_from(m)) != pt) match = false; else if(move_to(m) != to) match = false; else if(move_promotion(m) != promotion) match = false; else if(fromFile != FILE_NONE && fromFile != square_file(move_from(m))) match = false; else if(fromRank != RANK_NONE && fromRank != square_rank(move_from(m))) match = false; if(match) { move = m; matches++; } } if(matches == 1) return move; else return MOVE_NONE; }
const std::string move_to_san(Position &pos, Move m) { std::string str; assert(pos.is_ok()); assert(move_is_ok(m)); Square from, to; Piece pc; from = move_from(m); to = move_to(m); pc = pos.piece_on(move_from(m)); if(m == MOVE_NONE) { str = "(none)"; return str; } else if(m == MOVE_NULL) { str = "(null)"; return str; } else if(move_is_long_castle(m) || (int(to - from) == -2 && type_of_piece(pc) == KING)) str = "O-O-O"; else if(move_is_short_castle(m) || (int(to - from) == 2 && type_of_piece(pc) == KING)) str = "O-O"; else { str = ""; if(type_of_piece(pc) == PAWN) { if(pos.move_is_capture(m)) str += file_to_char(square_file(move_from(m))); } else { str += piece_type_to_char(type_of_piece(pc), true); Ambiguity amb = move_ambiguity(pos, m); switch(amb) { case AMBIGUITY_NONE: break; case AMBIGUITY_FILE: str += file_to_char(square_file(from)); break; case AMBIGUITY_RANK: str += rank_to_char(square_rank(from)); break; case AMBIGUITY_BOTH: str += square_to_string(from); break; default: assert(false); } } if(pos.move_is_capture(m)) str += "x"; str += square_to_string(move_to(m)); if(move_promotion(m)) { str += "="; str += piece_type_to_char(move_promotion(m), true); } } // Is the move check? We don't use pos.move_is_check(m) here, because // Position::move_is_check doesn't detect all checks (not castling moves, // promotions and en passant captures). UndoInfo u; pos.do_move(m, u); if(pos.is_check()) str += pos.is_mate()? "#" : "+"; pos.undo_move(m, u); return str; }
void move_do(board_t * board, int move) { int me, opp; int from, to; int piece, pos, capture; int old_flags, new_flags; int sq, ep_square; int pawn; ASSERT(board_is_ok(board)); ASSERT(move_is_ok(move)); ASSERT(move_is_pseudo(move,board)); // init me = board->turn; opp = colour_opp(me); from = move_from(move); to = move_to(move); piece = board->square[from]; ASSERT(colour_equal(piece,me)); pos = board->pos[from]; ASSERT(pos>=0); // update turn board->turn = opp; board->key ^= random_64(RandomTurn); // update castling rights old_flags = board_flags(board); if (piece_is_king(piece)) { board->castle[me][SideH] = SquareNone; board->castle[me][SideA] = SquareNone; } if (board->castle[me][SideH] == from) board->castle[me][SideH] = SquareNone; if (board->castle[me][SideA] == from) board->castle[me][SideA] = SquareNone; if (board->castle[opp][SideH] == to) board->castle[opp][SideH] = SquareNone; if (board->castle[opp][SideA] == to) board->castle[opp][SideA] = SquareNone; new_flags = board_flags(board); board->key ^= hash_castle_key(new_flags^old_flags); // HACK // update en-passant square ep_square = sq = board->ep_square; if (sq != SquareNone) { board->key ^= random_64(RandomEnPassant+square_file(sq)); board->ep_square = SquareNone; } if (piece_is_pawn(piece) && abs(to-from) == 32) { pawn = piece_make_pawn(opp); if (board->square[to-1] == pawn || board->square[to+1] == pawn) { board->ep_square = sq = (from + to) / 2; board->key ^= random_64(RandomEnPassant+square_file(sq)); } } // update ply number (captures are handled later) board->ply_nb++; if (piece_is_pawn(piece)) board->ply_nb = 0; // conversion // update move number if (me == Black) board->move_nb++; // castle if (colour_equal(board->square[to],me)) { int rank; int king_from, king_to; int rook_from, rook_to; int rook; rank = colour_is_white(me) ? Rank1 : Rank8; king_from = from; rook_from = to; if (to > from) { // h side king_to = square_make(FileG,rank); rook_to = square_make(FileF,rank); } else { // a side king_to = square_make(FileC,rank); rook_to = square_make(FileD,rank); } // remove the rook pos = board->pos[rook_from]; ASSERT(pos>=0); rook = Rook64 | me; // HACK square_clear(board,rook_from,rook); // move the king square_move(board,king_from,king_to,piece); // put the rook back square_set(board,rook_to,rook,pos); ASSERT(board->key==hash_key(board)); return; } // remove the captured piece if (piece_is_pawn(piece) && to == ep_square) { // en-passant capture sq = square_ep_dual(to); capture = board->square[sq]; ASSERT(capture==piece_make_pawn(opp)); square_clear(board,sq,capture); board->ply_nb = 0; // conversion } else { capture = board->square[to]; if (capture != Empty) { // normal capture ASSERT(colour_equal(capture,opp)); ASSERT(!piece_is_king(capture)); square_clear(board,to,capture); board->ply_nb = 0; // conversion } } // move the piece if (move_is_promote(move)) { // promote square_clear(board,from,piece); piece = move_promote_hack(move) | me; // HACK square_set(board,to,piece,pos); } else { // normal move square_move(board,from,to,piece); } ASSERT(board->key==hash_key(board)); }
bool move_is_legal(const Position& pos, const Move m, Bitboard pinned) { assert(pos.is_ok()); assert(move_is_ok(m)); assert(pinned == pos.pinned_pieces(pos.side_to_move())); Color us = pos.side_to_move(); Color them = opposite_color(us); Square from = move_from(m); Square to = move_to(m); Piece pc = pos.piece_on(from); // Use a slower but simpler function for uncommon cases if (move_is_ep(m) || move_is_castle(m)) return move_is_legal(pos, m); // If the from square is not occupied by a piece belonging to the side to // move, the move is obviously not legal. if (color_of_piece(pc) != us) return false; // The destination square cannot be occupied by a friendly piece if (pos.color_of_piece_on(to) == us) return false; // Handle the special case of a pawn move if (type_of_piece(pc) == PAWN) { // Move direction must be compatible with pawn color int direction = to - from; if ((us == WHITE) != (direction > 0)) return false; // A pawn move is a promotion iff the destination square is // on the 8/1th rank. if (( (square_rank(to) == RANK_8 && us == WHITE) ||(square_rank(to) == RANK_1 && us != WHITE)) != bool(move_is_promotion(m))) return false; // Proceed according to the square delta between the origin and // destination squares. switch (direction) { case DELTA_NW: case DELTA_NE: case DELTA_SW: case DELTA_SE: // Capture. The destination square must be occupied by an enemy // piece (en passant captures was handled earlier). if (pos.color_of_piece_on(to) != them) return false; break; case DELTA_N: case DELTA_S: // Pawn push. The destination square must be empty. if (!pos.square_is_empty(to)) return false; break; case DELTA_NN: // Double white pawn push. The destination square must be on the fourth // rank, and both the destination square and the square between the // source and destination squares must be empty. if ( square_rank(to) != RANK_4 || !pos.square_is_empty(to) || !pos.square_is_empty(from + DELTA_N)) return false; break; case DELTA_SS: // Double black pawn push. The destination square must be on the fifth // rank, and both the destination square and the square between the // source and destination squares must be empty. if ( square_rank(to) != RANK_5 || !pos.square_is_empty(to) || !pos.square_is_empty(from + DELTA_S)) return false; break; default: return false; } // The move is pseudo-legal, check if it is also legal return pos.is_check() ? pos.pl_move_is_evasion(m, pinned) : pos.pl_move_is_legal(m, pinned); } // Luckly we can handle all the other pieces in one go return bit_is_set(pos.attacks_from(pc, from), to) && (pos.is_check() ? pos.pl_move_is_evasion(m, pinned) : pos.pl_move_is_legal(m, pinned)) && !move_is_promotion(m); }
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; }
std::string pretty(Move m, Piece movedPieceType) { if (is_drop(m)) return (pretty(move_to(m)) + pretty2(movedPieceType) + (pretty_jp ? "打" : "*")); else return pretty(move_to(m)) + pretty2(movedPieceType) + (is_promote(m) ? (pretty_jp ? "成" : "+") : "") + "["+ pretty(move_from(m))+"]"; }
bool move_to_san(int move, const board_t * board, char string[], int size) { int from, to, piece; char tmp_string[256]; ASSERT(move_is_ok(move)); ASSERT(board_is_ok(board)); ASSERT(string!=NULL); ASSERT(size>=8); ASSERT(move_is_legal(move,board)); if (size < 8) return false; // init from = move_from(move); to = move_to(move); string[0] = '\0'; // castle if (move_is_castle(move,board)) { if (to > from) { strcat(string,"O-O"); } else { strcat(string,"O-O-O"); } goto check; } // from piece = board->square[from]; if (piece_is_pawn(piece)) { // pawn if (move_is_capture(move,board)) { sprintf(tmp_string,"%c",file_to_char(square_file(from))); strcat(string,tmp_string); } } else { // piece sprintf(tmp_string,"%c",toupper(piece_to_char(piece))); strcat(string,tmp_string); // ambiguity switch (ambiguity(move,board)) { case AMBIGUITY_NONE: break; case AMBIGUITY_FILE: sprintf(tmp_string,"%c",file_to_char(square_file(from))); strcat(string,tmp_string); break; case AMBIGUITY_RANK: sprintf(tmp_string,"%c",rank_to_char(square_rank(from))); strcat(string,tmp_string); break; case AMBIGUITY_SQUARE: if (!square_to_string(from,tmp_string,256)) return false; strcat(string,tmp_string); break; default: ASSERT(false); break; } } // capture if (move_is_capture(move,board)) strcat(string,"x"); // to if (!square_to_string(to,tmp_string,256)) return false; strcat(string,tmp_string); // promote if (move_is_promote(move)) { sprintf(tmp_string,"=%c",toupper(piece_to_char(move_promote(move,board)))); strcat(string,tmp_string); } // check check: if (move_is_mate(move,board)) { strcat(string,"#"); } else if (move_is_check(move,board)) { strcat(string,"+"); } return true; }