public void score_evasions() { // Try good captures ordered by MVV/LVA, then non-captures if destination square // is not under attack, ordered by history value, then bad-captures and quiet // moves with a negative SEE. This last group is ordered by the SEE value. Move m; int see; for (int it = 0; it != end; ++it) { m = moves[it].move; if ((see = pos.see_sign(m)) < ValueS.VALUE_ZERO) { moves[it].value = see - HistoryStats.Max; // At the bottom } else if (pos.capture(m)) { moves[it].value = Position.PieceValue[PhaseS.MG][pos.piece_on(Types.to_sq(m))] - Types.type_of_piece(pos.moved_piece(m)) + HistoryStats.Max; } else { moves[it].value = history[pos.moved_piece(m)][Types.to_sq(m)]; } } }
/// move_to_uci() converts a move to a string in coordinate notation /// (g1f3, a7a8q, etc.). The only special case is castling moves, where we print /// in the e1g1 notation in normal chess mode, and in e1h1 notation in chess960 /// mode. Internally castling moves are always encoded as "king captures rook". public static string move_to_uci(Move m, bool chess960) { Square from = Types.from_sq(m); Square to = Types.to_sq(m); if (m == MoveS.MOVE_NONE) { return("(none)"); } if (m == MoveS.MOVE_NULL) { return("0000"); } if (Types.type_of_move(m) == MoveTypeS.CASTLING && !chess960) { to = Types.make_square((to > from ? FileS.FILE_G : FileS.FILE_C), Types.rank_of(from)); } string move = Types.square_to_string(from) + Types.square_to_string(to); if (Types.type_of_move(m) == MoveTypeS.PROMOTION) { move += PieceToChar[ColorS.BLACK][Types.promotion_type(m)]; // Lower case } return(move); }
/// score() assign a numerical value to each move in a move list. The moves with /// highest values will be picked first. public void 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 the // badCaptures[] array, but instead of doing it now we delay until the move // has been picked up in pick_move_from_list(). This way we save some SEE // calls in case we get a cutoff. Move m; for (int it = 0; it != end; ++it) { m = moves[it].move; moves[it].value = Position.PieceValue[PhaseS.MG][pos.piece_on(Types.to_sq(m))] - Types.type_of_piece(pos.moved_piece(m)); if (Types.type_of_move(m) == MoveTypeS.ENPASSANT) { moves[it].value += Position.PieceValue[PhaseS.MG][PieceTypeS.PAWN]; } else if (Types.type_of_move(m) == MoveTypeS.PROMOTION) { moves[it].value += Position.PieceValue[PhaseS.MG][Types.promotion_type(m)] - Position.PieceValue[PhaseS.MG][PieceTypeS.PAWN]; } } }
public void score_quiets() { Move m; for (int it = 0; it != end; ++it) { m = moves[it].move; moves[it].value = history[pos.moved_piece(m)][Types.to_sq(m)]; } }
/// move_to_san() takes a position and a legal Move as input and returns its /// short algebraic notation representation. public static string move_to_san(Position pos, Move m) { if (m == MoveS.MOVE_NONE) { return("(none)"); } if (m == MoveS.MOVE_NULL) { return("(null)"); } Debug.Assert((new MoveList(pos, GenTypeS.LEGAL).contains(m))); Bitboard others, b; string san = ""; Color us = pos.side_to_move(); Square from = Types.from_sq(m); Square to = Types.to_sq(m); Piece pc = pos.piece_on(from); PieceType pt = Types.type_of_piece(pc); if (Types.type_of_move(m) == MoveTypeS.CASTLING) { san = to > from ? "O-O" : "O-O-O"; } else { if (pt != PieceTypeS.PAWN) { san = "" + PieceToChar[ColorS.WHITE][pt]; // Upper case // A disambiguation occurs if we have more then one piece of type 'pt' // that can reach 'to' with a legal move. others = b = (pos.attacks_from_piece_square(pc, to) & pos.pieces_color_piecetype(us, pt)) ^ BitBoard.SquareBB[from]; while (b != 0) { Square s = BitBoard.pop_lsb(ref b); if (!pos.legal(Types.make_move(s, to), pos.pinned_pieces(us))) { others ^= BitBoard.SquareBB[s]; } } if (0 == others) { /* Disambiguation is not needed */ } else if (0 == (others & BitBoard.file_bb_square(from))) { san += Types.file_to_char(Types.file_of(from)); } else if (0 == (others & BitBoard.rank_bb_square(from))) { san += Types.rank_to_char(Types.rank_of(from)); } else { san += Types.square_to_string(from); } } else if (pos.capture(m)) { san = "" + Types.file_to_char(Types.file_of(from)); } if (pos.capture(m)) { san += 'x'; } san += Types.square_to_string(to); if (Types.type_of_move(m) == MoveTypeS.PROMOTION) { san += "=" + PieceToChar[ColorS.WHITE][Types.promotion_type(m)]; } } if (pos.gives_check(m, new CheckInfo(pos))) { StateInfo st = new StateInfo(); pos.do_move(m, st); san += (new MoveList(pos, GenTypeS.LEGAL)).size() > 0 ? "+" : "#"; pos.undo_move(m); } return(san); }
/// probe() tries to find a book move for the given position. If no move is /// found returns MOVE_NONE. If pickBest is true returns always the highest /// rated move, otherwise randomly chooses one, based on the move score. public Move probe(Position pos, string fName, bool pickBest) { if (fName == null || (fileName != fName && !open(fName))) { return(MoveS.MOVE_NONE); } PolyglotBook.Entry e = new PolyglotBook.Entry(); UInt16 best = 0; uint sum = 0; Move move = MoveS.MOVE_NONE; Key key = polyglot_key(pos); stream.Seek(find_first(key) * SIZE_OF_BOOKENTRY, SeekOrigin.Begin); while (read(ref e) && e.key == key) { best = Math.Max(best, e.count); sum += e.count; // Choose book move according to its score. If a move has a very // high score it has higher probability to be choosen than a move // with lower score. Note that first entry is always chosen. if ((sum != 0 && ((((uint)RKiss.rand64()) % sum) < e.count)) || (pickBest && e.count == best)) { move = (e.move); } } if (move == 0) { return(MoveS.MOVE_NONE); } // A PolyGlot book move is encoded as follows: // // bit 0- 5: destination square (from 0 to 63) // bit 6-11: origin square (from 0 to 63) // bit 12-14: promotion piece (from KNIGHT == 1 to QUEEN == 4) // // Castling moves follow "king captures rook" representation. So in case book // move is a promotion we have to convert to our representation, in all the // other cases we can directly compare with a Move after having masked out // the special Move's flags (bit 14-15) that are not supported by PolyGlot. int pt = (move >> 12) & 7; if (pt != 0) { move = Types.make(Types.from_sq(move), Types.to_sq(move), MoveTypeS.PROMOTION, (pt + 1)); } // Add 'special move' flags and verify it is legal for (MoveList ml = new MoveList(pos, GenTypeS.LEGAL); ml.mlist[ml.cur].move != MoveS.MOVE_NONE; ++ml) { if (move == (ml.move() ^ Types.type_of_move(ml.move()))) { return(ml.move()); } } return(MoveS.MOVE_NONE); }
/// next_move() is the most important method of the MovePicker class. It returns /// a new pseudo legal move every time it is called, until there are no more moves /// left. It picks the move with the biggest value from a list of generated moves /// taking care not to return the ttMove if it has already been searched. public Move next_move_false() { Move move; while (true) { while (cur == end) { generate_next_stage(); } switch (stage) { case StagesS.MAIN_SEARCH: case StagesS.EVASION: case StagesS.QSEARCH_0: case StagesS.QSEARCH_1: case StagesS.PROBCUT: ++cur; return(ttMove); case StagesS.CAPTURES_S1: move = moves[pick_best(moves, cur++, end)].move; if (move != ttMove) { if (pos.see_sign(move) >= ValueS.VALUE_ZERO) { return(move); } // Losing capture, move it to the tail of the array moves[endBadCaptures--].move = move; } break; case StagesS.KILLERS_S1: move = moves[cur++].move; if (move != MoveS.MOVE_NONE && move != ttMove && pos.pseudo_legal(move) && !pos.capture(move)) { return(move); } break; case StagesS.QUIETS_1_S1: case StagesS.QUIETS_2_S1: move = moves[cur++].move; if (move != ttMove && move != moves[Types.MAX_MOVES].move && move != moves[Types.MAX_MOVES + 1].move && move != moves[Types.MAX_MOVES + 2].move && move != moves[Types.MAX_MOVES + 3].move && move != moves[Types.MAX_MOVES + 4].move && move != moves[Types.MAX_MOVES + 5].move) { return(move); } break; case StagesS.BAD_CAPTURES_S1: return(moves[cur--].move); case StagesS.EVASIONS_S2: case StagesS.CAPTURES_S3: case StagesS.CAPTURES_S4: move = moves[pick_best(moves, cur++, end)].move; if (move != ttMove) { return(move); } break; case StagesS.CAPTURES_S5: move = moves[pick_best(moves, cur++, end)].move; if (move != ttMove && pos.see(move) > captureThreshold) { return(move); } break; case StagesS.CAPTURES_S6: move = moves[pick_best(moves, cur++, end)].move; if (Types.to_sq(move) == recaptureSquare) { return(move); } break; case StagesS.QUIET_CHECKS_S3: move = moves[cur++].move; if (move != ttMove) { return(move); } break; case StagesS.STOP: return(MoveS.MOVE_NONE); default: Debug.Assert(false); break; } } }