def parse_position(pgn_position: pgn.ChildNode, is_mainline: bool) -> SerializablePosition: nonlocal current_index nonlocal all_positions index = current_index current_index += 1 next_pos_index = None if pgn_position.variations: next_pos_index = parse_position(pgn_position.variations[0], True).index variations_indexes = [] if len(pgn_position.variations) > 1: variations_indexes = [ parse_position(p, False).index for i, p in enumerate(pgn_position.variations) if i > 0 ] position = SerializablePosition( index=index, next_position_index=next_pos_index, variations_indexes=variations_indexes, nags=list(pgn_position.nags), fen=pgn_position.board().fen(), comment=pgn_position.comment, commentBefore=pgn_position.starting_comment, san=pgn_position.san(), is_mainline=is_mainline, move=Move(from_square=square_name(pgn_position.move.from_square), to=square_name(pgn_position.move.to_square), promotion=pgn_position.move.promotion)) all_positions.insert(0, position) return position
def getNextState(self, board, player, action): # Copy board to new board b = board.copy() origin = np.where(self.VALIDS_INDEX == action)[0][0] destination = np.where(self.VALIDS_INDEX == action)[1][0] origin_name = chess.square_name(origin) destination_name = chess.square_name(destination) move = "".join([origin_name, destination_name]) # print(move) is_promotion = (b.piece_at(origin).piece_type == chess.PAWN and chess.square_rank(destination) in [0, 7]) if is_promotion: move = str(move + 'q') move = chess.Move.from_uci(move) # print(move) # print(list(board.legal_moves)) b.push(move) # print(b.mirror(), -player) return b.mirror(), -player
def update_hashcode_zobriest(piece, board, h, hashTurn, piece_hash, move): """ Update hashcode of a board with Zobriest Hashing Need to call this function before using board.push(move). :param board: :param h: :param move: :return: """ to_col = board.color_at(move.to_square) to_col = get_color_code(to_col) to_piece = board.piece_type_at(move.to_square) from_uci = chess.square_name(move.from_square) x1 = d[from_uci[0]] y1 = d[from_uci[1]] to_uci = chess.square_name(move.to_square) x2 = d[to_uci[0]] y2 = d[to_uci[1]] indice_color = 0 if board.turn else 1 #True = White h = h ^ piece_hash[(piece - 1) + 6 * indice_color][x1][y1] h = h ^ piece_hash[(piece - 1) + 6 * indice_color][x2][y2] h = h ^ hashTurn if (to_col == 1): h = h ^ piece_hash[(to_piece - 1)][x2][y2] elif (to_col == 2): h = h ^ piece_hash[(to_piece - 1) + 6][x2][y2] return h
def update_hashcode(piece, board, h, hashTable, hashTurn, move): """ Update hashcode of a board. Need to call this function before using board.push(move). :param board: :param h: :param move: :return: """ col = board.color_at(move.to_square) col = get_color_code(col) from_uci = chess.square_name(move.from_square) x1 = d[from_uci[0]] y1 = d[from_uci[1]] to_uci = chess.square_name(move.to_square) x2 = d[to_uci[0]] y2 = d[to_uci[1]] move_color = get_color_code(board.turn) if col != None: h = h ^ hashTable[col][x2][y2][piece - 1] h = h ^ hashTable[move_color][x2][y2][piece - 1] h = h ^ hashTable[move_color][x1][y1][piece - 1] h = h ^ hashTurn return h
def action_to_uci(self, action): origin = np.where(self.VALIDS_INDEX == action)[0][0] destination = np.where(self.VALIDS_INDEX == action)[1][0] origin_name = chess.square_name(origin) destination_name = chess.square_name(destination) move = "".join([origin_name, destination_name]) return move
def get_move_list(board): move_list = {} for move in list(board.legal_moves): start_square = chess.square_name(move.from_square) if start_square in move_list.keys(): move_list[start_square].append(chess.square_name(move.to_square)) else: move_list[start_square] = [chess.square_name(move.to_square)] return move_list
def knight_moves(): out = [] for i in range(64): start = chess.square_name(i) # Up if chess.square_rank(i) < 6: if chess.square_file(i) != 7: out.append(start + chess.square_name(i + 17)) if chess.square_file(i) != 0: out.append(start + chess.square_name(i + 15)) # Right if chess.square_file(i) < 6: if chess.square_rank(i) != 7: out.append(start + chess.square_name(i + 10)) if chess.square_rank(i) != 0: out.append(start + chess.square_name(i - 6)) # Down if chess.square_rank(i) > 1: if chess.square_file(i) != 7: out.append(start + chess.square_name(i - 15)) if chess.square_file(i) != 0: out.append(start + chess.square_name(i - 17)) # Left if chess.square_file(i) > 1: if chess.square_rank(i) != 7: out.append(start + chess.square_name(i + 6)) if chess.square_rank(i) != 0: out.append(start + chess.square_name(i - 10)) return out
def make_move(move_str, board, driver): move = util.parse_move(move_str, board) start_sq = chess.square_name(move.from_square) end_sq = chess.square_name(move.to_square) start_el = util.get_square_el(start_sq, driver) util.sleep_random(5, 10) util.click_el(start_el, driver) util.sleep_random(0.1, 1.5) end_el = util.get_square_el(end_sq, driver) util.click_el(end_el, driver) board.push(move)
def get_pieces(board): white_pieces = {} black_pieces = {} for piece in PIECES: ps = board.pieces(piece, chess.WHITE) squares = [chess.square_name(s) for s in ps] white_pieces[piece] = squares ps = board.pieces(piece, chess.BLACK) squares = [chess.square_name(s) for s in ps] black_pieces[piece] = squares return white_pieces, black_pieces
def add_turn_to_file(board: b.ChessBoard, file_name: str = 'game_results.txt'): """Adds a new turn to a file that has already been initialised. The `seek()` function is used to update two characters in the board. :param board: Current state of the board :param file_name: Name of the file to be saved :raises IOError: If the file has not been initialised """ if not os.path.exists(f'results/{file_name}'): raise IOError('The file specified does not exist') current_move = board.board.peek() from_square = chess.square_name(current_move.from_square) to_square = chess.square_name(current_move.to_square) current_piece_from = board.get_piece_at(square=from_square) current_piece_to = board.get_piece_at(square=to_square) with open(file=f'results/{file_name}', mode='r+', encoding='utf-8') as f: current_line = f.readline() while 'Current state of the board:' not in current_line: current_line = f.readline() # jump two 2 newline characters # we have reached the line containing the board initial_cursor = f.seek(f.tell() + 2) # get "from" position that needs to be updated squares_one_line = board.squares_one_line from_pos = squares_one_line.index(from_square) # seek "from" position f.seek(initial_cursor + from_pos) # replace "from" square if current_piece_from: f.write(str(current_piece_from)) else: f.write('.') # get "to" position to_pos = squares_one_line.index(to_square) # seek "to" position f.seek(initial_cursor + to_pos) # replace "to" square if current_piece_to: f.write(str(current_piece_to)) else: f.write('.')
def post(): """ Sets chess play in the Core """ data = request.get_json() required_validation = ErrorUtil.isRequired('move', data['move']) if not required_validation['valid']: return required_validation['response'] session_key = BoardSessions.getBoardSession(request.referrer) core = ChessCore.getBoard(session_key) try: parsed_move = core.board.parse_san(data['move']) except ValueError: return Lib.resInvalidJson('invalid Move') castling = core.getCastlingMove(parsed_move, color=core.getTurn()) # removes castling rights move = core.movePiece(data['move'], session_key=session_key) res = {'status': 1, 'data': chess.square_name(move.to_square)} if castling['castling']: res['castling'] = True res['castlingMove'] = castling['castlingMove'] return Lib.resJson(res)
def populate_move_from(self): self.comboBoxMoveFrom.clear() a = str(self.board.legal_moves) b = a[a.index('(') + 1:a.index(')')].split(', ') l = [] if self.checkBoxUciSan.checkState(): for move in b: move_piece = move[0] if len(move) > 2 else 'P' #move_piece = chess.PIECE_NAMES[chess.PIECE_SYMBOLS.index(move_piece.lower())] if move_piece not in l: l.append(move_piece) self.comboBoxMoveFrom.addItem(move_piece) else: for move in self.board.legal_moves: move_from = chess.square_name(move.from_square).upper() if move_from not in l: l.append(move_from) self.comboBoxMoveFrom.addItem(move_from) if len(l) > 0: self.comboBoxMoveFrom.setCurrentIndex(0) self.populate_move_to()
def getBoardDetails(self): boardData = { 'black': [], 'white': [] } legal_moves = self.getLegalMoves() legal_moves_starter = [m.uci()[0:2] for m in legal_moves] legal_moves_ender = [m.uci()[2:4] for m in legal_moves] for i in range(64): pieceType = self.__board.piece_type_at(i) pieceColor = self.__board.color_at(i) piecePosition = chess.square_name(i) if pieceColor != None: if pieceColor: pieceClass = 'black' else: pieceClass = 'white' possibleVictims = [p for p in self.__board.attacks(i) if not self.__board.color_at(p) and chess.square_name(p) in legal_moves_ender ] possibleAttacker = [a for a in self.__board.attackers(not self.AI_color,i) if chess.square_name(a) in legal_moves_starter ] boardData[pieceClass].append({ 'type': pieceTypes[pieceType-1], 'position': piecePosition, 'attacks': possibleVictims, 'attackers': possibleAttacker }) return boardData
def getStringArray(aBoard): boardArray = [] for key in aBoard.piece_map(): boardArray.append(aBoard.piece_map()[key].symbol() + "@" + chess.square_name(key)) return boardArray
def get_all_moves(): """return all possible moves in the baord and map those to squares""" num_moves = 0 moves = [] # for x in range(8): for x, f in enumerate(chess.FILE_NAMES): # for y in range(8): for y, r in enumerate(chess.RANK_NAMES): # print(x, y) # knight moves kmoves = [(2, 1), (1, 2), (-1, 2), (-2, 1), (-2, -1), (-1, -2), (1, -2), (2, -1)] for mv in kmoves: if 7 >= x + mv[0] >= 0 and 7 >= y + mv[1] >= 0: sq = chess.square(x + mv[0], y + mv[1]) moves.append("{}{}{}".format(f, r, chess.square_name(sq))) for i in range(8): # hv moves if i != x: sq = chess.square(i, y) moves.append("{}{}{}".format(f, r, chess.square_name(sq))) if i != y: sq = chess.square(x, i) moves.append("{}{}{}".format(f, r, chess.square_name(sq))) # diag moves --> flip x, ys directions = list( map(list, [ zip(range(x + 1, 8), range(y + 1, 8)), zip(range(x + 1, 8), range(y - 1, -1, -1)), zip(range(x - 1, -1, -1), range(y + 1, 8)), zip(range(x - 1, -1, -1), range(y - 1, -1, -1)), ])) diag_moves = [] for d in directions: diag_moves.extend(d) for i, mv in enumerate(diag_moves): sq = chess.square(*mv) if sq == "{}{}".format(f, r): continue diag_moves[i] = "{}{}{}".format(f, r, chess.square_name(sq)) moves.extend(diag_moves) moves_idx = {mv: i for i, mv in enumerate(moves)} return moves_idx
def calculate_min_max_tree(state, env, player, depth, mode): evaluations = [] legal_moves = [state.san(x) for x in env.legal_moves] evals = [] #Move Ordering if player == 'white': opp = False else: opp = True #checkmate moves = [x for x in legal_moves if '#' in x] #check moves2 = [x for x in legal_moves if '+' in x] for m in moves2: moves.append(m) #capture for i in [5, 4, 3, 2, 1]: #1=pawn #find the oppenent piece squares = state.pieces(i, opp) square_list = [chess.square_name(square) for square in squares] #for square in squares: # square_list.append(square) #chess.square_name(21) #see if we are attacking it for j in square_list: capt = [x for x in legal_moves if 'x' + str(j) in x] for k in capt: moves.append(k) #moves = legal_moves myset = set(moves) legal_moves = list(myset) if len(legal_moves) == 0: legal_moves = [state.san(x) for x in env.legal_moves] for move in legal_moves: state_new = copy.deepcopy(state) env_new = copy.deepcopy(env) action = state_new.push_san(move) state_new, reward, done, _ = env_new.step(action) #best_move = Search(depth=5, player ='white', env=env, state=state) best_eval = AlphaSearch(depth=depth, player='white', env=env_new, state=state_new, alpha=-10000, beta=10000, evaluations=evaluations, max_depth=depth, mode=mode) if player == 'black': best_eval = -best_eval evals.append(best_eval) print(evals, legal_moves) best_move = legal_moves[evals.index(max(evals))] return best_move
def post_options(board, tweet_id): order = [chess.QUEEN, chess.KING, chess.ROOK, chess.BISHOP, chess.KNIGHT, chess.PAWN] moves = {chess.piece_name(p).title(): [] for p in order} piece_map = board.piece_map() for m in list(board.legal_moves): piece = chess.piece_name(piece_map[m.from_square].piece_type).title() moves[piece].append(m) poll_ids = [] for p in moves: if len(moves[p]) <= 0: continue op = [] for m in moves[p]: f = chess.square_name(m.from_square).upper() t = chess.square_name(m.to_square).upper() if m.promotion: prom = chess.piece_name(m.promotion).title() op.append(move_msg_prom.format(p, f, t, prom)) continue op.append(move_msg.format(p, f, t)) # Make sure we don't leave a poll with only 1 options (tweeter doesn't accept this) if len(op) % 4 == 1: op.append(filler_text) op = [op[i:i + 4] for i in range(0, len(op), 4)] head_tweet_id = post_tweet(p + " Moves:", reply_id=tweet_id, entries=op.pop(0)) if not head_tweet_id: poll_ids.append(tweet_id) panic_clean_tweets(poll_ids) poll_ids.append(head_tweet_id) for poll_ops in op: head_tweet_id = post_tweet(p + " cont...", reply_id=head_tweet_id, entries=poll_ops) if not head_tweet_id: poll_ids.append(tweet_id) panic_clean_tweets(poll_ids) poll_ids.append(head_tweet_id) return poll_ids
def queen_moves(): out = [] for i in range(64): start = chess.square_name(i) # Left -> Right rank_start = (i // 8) * 8 for j in range(rank_start, rank_start + 8): if i == j: continue end = chess.square_name(j) out.append(start + end) # Bottom -> Top file_start = i % 8 for j in range(file_start, 64, 8): if i == j: continue end = chess.square_name(j) out.append(start + end) # Diagonal 1 if i != 7 and i != 56: if chess.square_rank(i) == 7 or chess.square_file(i) == 7: align = i - 9 else: align = i + 9 diagonal = list(chess.SquareSet.ray(i, align)) for j in diagonal: if i == j: continue end = chess.square_name(j) out.append(start + end) # Diagonal 2 if i != 0 and i != 63: if chess.square_rank(i) == 7 or chess.square_file(i) == 0: align = i - 7 else: align = i + 7 diagonal = list(chess.SquareSet.ray(i, align)) for j in diagonal: if i == j: continue end = chess.square_name(j) out.append(start + end) return out
def get_piece_positions(self, is_white: bool): board = self.get_board() piece_map = board.piece_map() pos_list = list() colour_to_look_for = chess.WHITE if is_white else chess.BLACK for pos, piece in piece_map.items(): if piece.color == colour_to_look_for: pos_list.append(chess.square_name(pos)) return pos_list
def get_current_positions(board, color): positions = [] for i in range(64): piece = board.piece_at(i) if piece is None: continue if piece.color != (color == 'white'): continue positions.append((chess.square_name(i), piece_code(piece.piece_type))) return positions
def populate_move_to(self): self.comboBoxMoveTo.clear() a = str(self.board.legal_moves) b = a[a.index('(') + 1:a.index(')')].split(', ') if len(self.comboBoxMoveFrom.currentText()) and self.checkBoxUciSan.checkState(): for move in b: if len(move) <= 2 and (self.comboBoxMoveFrom.currentText().startswith('P')): self.comboBoxMoveTo.addItem(move) elif move.startswith(self.comboBoxMoveFrom.currentText()[0]): self.comboBoxMoveTo.addItem(move[1::]) else: for move in self.board.legal_moves: move_from = chess.square_name(move.from_square).upper() move_to = chess.square_name(move.to_square).upper() if move_from == self.comboBoxMoveFrom.currentText(): self.comboBoxMoveTo.addItem(move_to)
def pawn_promos(color): out = [] if color == chess.WHITE: a = 48 b = 56 else: a = 8 b = 16 for i in range(a, b): if color == chess.WHITE: left = max(56, i + 7) right = min(63, i + 9) + 1 else: left = max(0, i - 9) right = min(7, i - 7) + 1 for j in range(left, right): for p in ["q", "r", "b", "n"]: out.append(chess.square_name(i) + chess.square_name(j) + p) return out
def get_attack_from_pos_svg(self, is_white: bool, attack_from_square: str): positions_to_attack_from = self.get_piece_positions(is_white) if attack_from_square not in positions_to_attack_from: raise Exception("Square: " + attack_from_square + " is not one of possible squares to attack from.") board = self.get_board() squares_to_move_to = set() squares_to_move_to_names = list() for move in board.legal_moves: if chess.square_name(move.from_square) == attack_from_square: squares_to_move_to.add(move.to_square) squares_to_move_to_names.append( chess.square_name(move.to_square)) colour = chess.WHITE if is_white else chess.BLACK board_svg = str( chess.svg.board(board, orientation=colour, size=GameManager.SIZE_OF_SVG, squares=squares_to_move_to)) return (board_svg, positions_to_attack_from, squares_to_move_to_names)
def detect_initial_changes(self): self.current_state = self.image.snap() for rank in range(2, 4): for file in range(0, 8): coordinates = self.scan_region[rank, file] region_changes = 0 for index in range(self.scan_region_area): x, y = coordinates[index] pixel = self.current_state.pixel(x - self.board_x, y - self.board_y) if BoardImage.is_close(pixel, self.light_square_color) or \ BoardImage.is_close(pixel, self.dark_square_color): pass else: region_changes += 1 print("-> ", (rank, file), region_changes) if region_changes >= self.region_threshold: if self.is_animating: if self.current_state.pixels == self.animating_state.pixels: self.is_animating = False self.set_animating_state(None) else: self.set_animating_state(self.current_state) return None else: self.is_animating = True self.set_animating_state(self.current_state) return None square_name = chess.square_name( Chessboard.get_square(rank, file, self.board.color)) if square_name in ("a3", "c3", "h3", "f3"): # It might be a pawn move x, y = self.square_centers[rank - 1, file] pixel = self.current_state.pixel( x - self.board_x, y - self.board_y) if BoardImage.is_close(pixel, self.light_square_color) or \ BoardImage.is_close(pixel, self.dark_square_color): # Pawn move move = square_name # Did this just to remind myself else: # Knight move move = "N" + square_name elif square_name in Chessboard.LEGAL_FIRST_MOVES: move = square_name # Did this just to remind myself else: self.interface.log( "Move detected was not a legal move.", level="error") raise RuntimeError( "Error: Move detected was not a legal move.") self.interface.log(f"Opponent move: {move}") uci_string = Chessboard.LEGAL_FIRST_MOVES_UCI[ Chessboard.LEGAL_FIRST_MOVES.index(move)] return chess.Move.from_uci(uci_string)
def helper_adv(G, piece, square_name, node_name): board = chess.Board(None) piece = chess.Piece.from_symbol(piece) square = chess.parse_square(square_name) board.set_piece_at(square, piece) attacks = board.attacks(square) for sq in attacks: square_name = chess.square_name(sq) for node in G.nodes(): if node.endswith(square_name): G.add_edge(node, node_name)
def get_aggregate(pgn_filename, player_name): full_aggregates = {'white': [], 'black': []} with open(pgn_filename) as pgn_file: while True: game = chess.pgn.read_game(pgn_file) if game is None: break print(game.headers['Date']) color = 'white' if player_name in game.headers['White'] else 'black' color_aggregates = full_aggregates[color] turns_mod = 0 if color == 'white' else 1 board = game.board() for i, move in enumerate(list(game.mainline_moves())): board.push(move) if i % 2 != turns_mod: continue move_index = i // 2 # Either this index is a new maximum game length for existing # aggregates (so it'll be appended to the list), or it's already # in the list, but it cannot be further than the list. assert move_index <= len(color_aggregates) if move_index == len(color_aggregates): color_aggregates.append({}) aggregate = color_aggregates[move_index] square = chess.square_name(move.from_square) piece = piece_code(board.piece_type_at(move.to_square)) direction = get_direction_from_move(move) datapoints = [(square, piece, direction)] # Taking the rook into account for castling if piece == 'K' and get_file_diff(move) > 1: datapoints.append(CASTLING_ROOK_DATAPOINTS[(color, direction)]) for square, piece, direction in datapoints: aggregate.setdefault(square, {}).setdefault(direction, {}).setdefault( piece, 0) aggregate[square][direction][piece] += 1 # Positional aggregates positions = get_current_positions(board, color) for square, piece in positions: aggregate.setdefault(square, {}).setdefault('_', {}).setdefault( piece, 0) aggregate[square]['_'][piece] += 1 return full_aggregates
def shalloweval(self, piececolor): if (self.draw): return 0 eval = 0 is_endgame = self.e.is_endgame(self.getfen()) valfinder = SquareValue() #loop through each square of the board #If a piece exists, add it's value to the eval outcome = self.board.outcome(claim_draw=False) if (outcome): if (outcome.winner == piececolor): #bot wins eval = 20000 elif (outcome.winner == (not piececolor)): #opponent wins eval = -20000 else: eval = 0 else: i = 0 while (i < 64): piece = self.getpiece(i) if (piece): value = valfinder.getpiecevalue(i, piececolor, piece, is_endgame) eval += value if ((piece.piece_type > 1) and piece.piece_type < 6): if (piece.color != self.board.turn): attackers = list( self.board.attackers( (not (piece.color)), chess.parse_square(chess.square_name(i)))) #defenders = list(self.board.attackers((piece.color), chess.parse_square(chess.square_name(i)))) j = 0 while (j < len(attackers)): #if len(defenders) == 0: # eval -= value # break att = abs( valfinder.getpiecevalue( attackers[j], piececolor, self.board.piece_at(attackers[j]), is_endgame)) #att = self.getpiece(attackers[j]).piece_type if (abs(value) > att): #if piece.piece_type >= att: if (abs(value) - abs(att) >= 100): eval -= value break j += 1 i = i + 1 return eval
def findColorOfSquares(board): whiteAttackersPerSquare={} blackAttackersPerSquare={} for square in chess.SQUARES: whiteAttackersPerSquare[square]=(len(board.attackers(chess.WHITE, square))) blackAttackersPerSquare[square]=(len(board.attackers(chess.BLACK, square))) highlightSquareComment=[] squareColorWhiteWinning='G' squareColorBlackWinning='R' for square in chess.SQUARES: if whiteAttackersPerSquare[square] > blackAttackersPerSquare[square]: if blackAttackersPerSquare[square]!=0: highlightSquareComment.append(squareColorWhiteWinning+chess.square_name(square)) elif whiteAttackersPerSquare[square] < blackAttackersPerSquare[square]: if whiteAttackersPerSquare[square] !=0: highlightSquareComment.append(squareColorBlackWinning+chess.square_name(square)) else: if whiteAttackersPerSquare[square] != 0: highlightSquareComment.append('Y' + chess.square_name(square)) return highlightSquareComment
def canvasTouch(self, e): # get id of canvas item closest to click point # if that item is tagged with 'piece', get the next item # under that, i.e. the square sqId = self.find_closest(e.x, e.y, 0, 'piece')[0] # The 2nd tag of a square is always its name, i.e. 'e4' sqName = self.gettags(sqId)[1] # A move is already in progress, so this is the second touch if self.MiP: # is this the same square clicked the last time? # if so, we'll unhighlight everything and return isSameSq = sqId == self.MiP[0][0] # unhighlight the from square self.itemconfigure(self.MiP[0][0], width=0) # unhighlight each to square for m in self.MiP: # if this finishes one of the legal moves, make it! if self.gettags(m[1])[1] == sqName: self.makeHumanMove(m[2]) self.itemconfigure(m[1], width=0) self.MiP = [] return # To make it here, this is first touch. # Iterate all the legal moves in the position, # and highlight the potential landing squares board = self.boardPane.curNode.board() for move in board.legal_moves: if chess.square_name(move.from_square) == sqName: fSqId = self.find_withtag(sqName)[0] self.itemconfigure(fSqId, outline=self.settings['hlSqColor'], width=4) tSqId = self.find_withtag(chess.square_name(move.to_square))[0] self.itemconfigure(tSqId, outline=self.settings['hlSqColor'], width=4) self.MiP.append((fSqId, tSqId, move))
def _update_initial_positions(self, move, side) -> None: """Updates the square to initial position dict.""" to = chess.square_name(move.to_square) from_ = chess.square_name(move.from_square) # save the captured piece's initial position captured_initial_position = self._initial_positions.get(to, None) # update the dictionary self._initial_positions[to] = self._initial_positions[from_] del (self._initial_positions[from_]) # if taking, then should remove piece from dict, which already happens # check if castling to update rook if self._board.is_castling(move): rank = '1' if side == WHITE else '8' if self._board.is_kingside_castling(move): # rook must be on file h because hasn't moved old_rook_pos = 'h' + rank new_rook_pos = 'f' + rank self._initial_positions[new_rook_pos] = old_rook_pos del (self._initial_positions[old_rook_pos]) else: # queenside castling # rook must be on file a because hasn't moved old_rook_pos = 'a' + rank new_rook_pos = 'd' + rank self._initial_positions[new_rook_pos] = old_rook_pos del (self._initial_positions[old_rook_pos]) # check if en_passant if self._board.is_en_passant(move): en_passant_square = self._get_en_passant_square() del self._initial_positions[en_passant_square] return captured_initial_position