/// 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]; } } }
/// generate<LEGAL> generates all the legal moves in the given position public static int generate_legal(Position pos, ExtMove[] mlist, int mPos) { int end, cur = mPos; Bitboard pinned = pos.pinned_pieces(pos.side_to_move()); Square ksq = pos.king_square(pos.side_to_move()); end = pos.checkers() != 0 ? generate_evasions(pos, mlist, mPos) : generate(pos, mlist, mPos, GenTypeS.NON_EVASIONS); while (cur != end) { if ((pinned != 0 || Types.from_sq(mlist[cur].move) == ksq || Types.type_of_move(mlist[cur].move) == MoveTypeS.ENPASSANT) && !pos.legal(mlist[cur].move, pinned)) { mlist[cur].move = mlist[--end].move; } else { ++cur; } } return(end); }
/// 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); }