def create_move_encoding(self, move): assert isinstance(move, chess.Move) index = 0 move_key = (chess.square_rank(move.to_square) - chess.square_rank(move.from_square), chess.square_file(move.to_square) - chess.square_file(move.from_square), 0 if move.promotion is None else move.promotion) init_vector = [0 for x in range(64 * 73)] if move is None or move == chess.Move.null(): return init_vector if self.board.piece_at(move.from_square).piece_type == chess.KNIGHT \ or move.promotion is not None and move.promotion != chess.QUEEN: index = 56 + self.special_move_indices[move_key] else: king_steps = chess.square_distance(move.from_square, move.to_square) unit_vector = (move_key[0], move_key[1]) if king_steps != 0: unit_vector = (move_key[0] / king_steps, move_key[1] / king_steps) if unit_vector != (0, 0): index = 7 * self.unit_moves[unit_vector] + king_steps - 1 print(index) init_vector[move.from_square * 73 + index] = 1 return init_vector
def paint_pieces(self, painter, board, flip, piece_size): last_move = self.game.get_last_move() for s in chess.SQUARES: x = piece_size * ((7 - chess.square_file(s)) if flip else chess.square_file(s)) y = piece_size * (chess.square_rank(s) if flip else (7 - chess.square_rank(s))) p = board.piece_at(s) if p: if s == self.from_square and self.mouseMovePos: x = self.mouseMovePos.x() - self.offset_x y = self.mouseMovePos.y() - self.offset_y # center images if show_ascii: sym = p.unicode_symbol() painter.drawText(x, y, piece_size, piece_size, Qt.AlignCenter, sym) else: piece_index = PIECE_IMAGE_INDEX[p.piece_type] + (0 if p.color else 6) img = QImage.scaled(self.piece_map[piece_index], piece_size, piece_size, Qt.KeepAspectRatio) offset_x = (piece_size-img.width())/2 offset_y = (piece_size-img.height())/2 painter.drawImage(x+offset_x, y+offset_y, img) if last_move and (last_move.from_square == s or last_move.to_square == s): painter.drawRect(x, y, piece_size, piece_size)
def generate_rook_move(self, move): print('Creating castling move') a_side = chess.square_file(move.to_square) < chess.square_file( move.from_square) print('a_side: {}'.format(a_side)) #TODO: Simplify this using the last digit of the move # if board.turn == chess.BLACK: #black's turn next # #Create white rook move # if a_side: # rook_move = 'a1-d1' # else: # rook_move = 'h1-f1' # else: # #Create black rook move # if a_side: # rook_move = 'a8-d8' # else: # rook_move = 'h8-f8' rank = str(move)[-1] #'8' or '1' if a_side: rook_move = 'a{}-d{}'.format(rank, rank) else: rook_move = 'h{}-f{}'.format(rank, rank) print('Rook move: {}'.format(rook_move)) return rook_move
def fill_piece(iplanes, ix, bb, b, flip_rank, flip_file): """ Compute piece placement and attack plane for a given piece type """ if AUX_INP: abb = 0 squares = chess.SquareSet(bb) for sq in squares: abb = abb | b.attacks_mask(sq) f = chess.square_file(sq) r = chess.square_rank(sq) if flip_rank: r = RANK_U - r if flip_file: f = FILE_U - f iplanes[r, f, ix + 12] = 1.0 squares = chess.SquareSet(abb) for sq in squares: f = chess.square_file(sq) r = chess.square_rank(sq) if flip_rank: r = RANK_U - r if flip_file: f = FILE_U - f iplanes[r, f, ix] = 1.0 else: squares = chess.SquareSet(bb) for sq in squares: f = chess.square_file(sq) r = chess.square_rank(sq) if flip_rank: r = RANK_U - r if flip_file: f = FILE_U - f iplanes[r, f, ix] = 1.0
def fill_planes(iplanes, b): """ Compute input planes for ResNet training """ #fill planes flip_rank = (b.turn == chess.BLACK) flip_file = (chess.square_file(b.king(b.turn)) < 4) fill_planes_(iplanes, b, b.turn, flip_rank, flip_file) #enpassant, castling, fifty and on-board mask if b.ep_square: f = chess.square_file(b.ep_square) r = chess.square_rank(b.ep_square) if flip_rank: r = RANK_U - r if flip_file: f = FILE_U - f iplanes[r, f, CHANNELS - 8] = 1.0 if b.has_queenside_castling_rights(b.turn): iplanes[:, :, CHANNELS - (6 if flip_file else 7)] = 1.0 if b.has_kingside_castling_rights(b.turn): iplanes[:, :, CHANNELS - (7 if flip_file else 6)] = 1.0 if b.has_queenside_castling_rights(not b.turn): iplanes[:, :, CHANNELS - (4 if flip_file else 5)] = 1.0 if b.has_kingside_castling_rights(not b.turn): iplanes[:, :, CHANNELS - (5 if flip_file else 4)] = 1.0 iplanes[:, :, CHANNELS - 3] = b.fullmove_number / 200.0 iplanes[:, :, CHANNELS - 2] = b.halfmove_clock / 100.0 iplanes[:, :, CHANNELS - 1] = 1.0
def dovetail_mate(puzzle: Puzzle) -> bool: node = puzzle.game.end() board = node.board() king = board.king(not puzzle.pov) assert king is not None assert isinstance(node, ChildNode) if square_file(king) in [0, 7] or square_rank(king) in [0, 7]: return False queen_square = node.move.to_square if (util.moved_piece_type(node) != QUEEN or square_file(queen_square) == square_file(king) or square_rank(queen_square) == square_rank(king) or square_distance(queen_square, king) > 1): return False for square in [ s for s in SquareSet(chess.BB_ALL) if square_distance(s, king) == 1 ]: if square == queen_square: continue attackers = list(board.attackers(puzzle.pov, square)) if attackers == [queen_square]: if board.piece_at(square): return False elif attackers: return False return True
def box_corner(self, available_squares, square): if chess.square_file(square) == 0 or chess.square_file(square) == 7: if (available_squares[1] and available_squares[2]) in self.board.attacks(self.board.king(self.winner())): print('Box mate') elif chess.square_rank(square) == 0 or chess.square_rank(square) == 7: if (available_squares[0] and available_squares[1]) in self.board.attacks(self.board.king(self.winner())): print('Box mate')
def get_model_num_from_move(self, move): directions = [-1, 1, -7, 7, -8, 8, -9, 9] knight_moves = [17, 15, 10, 6, -17, -15, -10, -6] diff = move.to_square - move.from_square # Underpromotion if move.promotion != 5 and move.promotion != None: directions = [7, 8, 9] for d in directions: if abs(diff) == d: direction = d _type = 3 * directions.index(direction) + move.promotion - 2 + 64 # Knight move elif diff in knight_moves and square_distance(move.to_square, move.from_square) == 2: _type = 56 + knight_moves.index(diff) else: direction = 0 for d in reversed(directions): if np.sign(diff) == np.sign(d) and diff % d == 0: direction = d break if square_rank(move.to_square) == square_rank(move.from_square): direction = np.sign(diff) * 1 elif square_file(move.to_square) == square_file(move.from_square): direction = np.sign(diff) * 8 distance = abs(diff / direction) _type = 7 * directions.index(direction) + (distance - 1) move_num = move.from_square * (7 * 8 + 8 + 9) + _type return int(move_num)
def get_mobility(board, pieces, num): x = [] pieces = list(pieces) for i in range(num): if len(pieces) - 1 < i: x.append([0, 0, 0, 0]) continue mir = 8 mar = 0 mif = 8 maf = 0 index = pieces[i] ora = chess.square_rank(index) ofa = chess.square_file(index) for attack in board.attacks(index): sr = chess.square_rank(attack) sf = chess.square_file(attack) if sr < mir: mir = sr if sr > mar: mar = sr if sf < mif: mif = sf if sf > maf: maf = sf x.append([ (mir - ora) / 7., (mar - ora) / 7., (mif - ofa) / 7., (maf - ofa) / 7. ]) return x
def ladder_corner(self, available_squares, square): # 8 plausible ways to get ladder mated on a corner ladder = False if chess.square_file(square) == 0 or chess.square_file(square) == 7: for i in available_squares[:2]: for attacker in self.board.attackers(self.winner(), i): if (square != i and (str(self.board.piece_at(attacker)).upper() == 'Q' or str(self.board.piece_at(attacker)).upper() == 'R') and (chess.square_file(attacker) == 1 or chess.square_file(attacker) == 6)): ladder = True elif chess.square_rank(square) == 0 or chess.square_rank(square) == 7: for i in available_squares[1:]: for attacker in self.board.attackers(self.winner(), i): if (square != i and (str(self.board.piece_at(attacker)).upper() == 'Q' or str(self.board.piece_at(attacker)).upper() == 'R') and (chess.square_rank(attacker) == 1 or chess.square_rank(attacker) == 6)): ladder = True # Prevents from printing it out multiple times if ladder: print('Ladder mate')
def exposed_king(puzzle: Puzzle) -> bool: if puzzle.pov: pov = puzzle.pov board = puzzle.mainline[0].board() else: pov = not puzzle.pov board = puzzle.mainline[0].board().mirror() king = board.king(not pov) assert king is not None if chess.square_rank(king) < 5: return False squares = SquareSet.from_square(king - 8) if chess.square_file(king) > 0: squares.add(king - 1) squares.add(king - 9) if chess.square_file(king) < 7: squares.add(king + 1) squares.add(king - 7) for square in squares: if board.piece_at(square) == Piece(PAWN, not pov): return False for node in puzzle.mainline[1::2][1:-1]: if node.board().is_check(): return True return False
def is_short_diagonal(from_sq, to_sq): ''' return True if diagonal is short ''' if (((chess.square_rank(to_sq) <= 3) and (chess.square_file(to_sq) <= 3)) or ((chess.square_rank(to_sq) > 3) and (chess.square_file(to_sq) > 3))): # This means that King is in lower-left quadrant or # in upper-right quadrant # In this quadrants NW_SE_diagonals are shortest if nw_se_diagonal(from_sq, to_sq): return True elif sw_ne_diagonal(from_sq, to_sq): return False else: # pragma: no cover raise KeyError else: # Other two quadrants. And diagonals are vise-versa. if nw_se_diagonal(from_sq, to_sq): return False elif sw_ne_diagonal(from_sq, to_sq): return True else: # pragma: no cover raise KeyError
def BoardEncode(self): """Converts a board to numpy array representation (8,8,21) same as Alphazero with history_length = 1 (only one board)""" array = np.zeros((8, 8, 26), dtype=int) for square, piece in self.board.piece_map().items(): rank, file = chess.square_rank(square), chess.square_file(square) piece_type, color = piece.piece_type, piece.color # The first six planes encode the pieces of the active player, # the following six those of the active player's opponent. Since # this class always stores boards oriented towards the white player, # White is considered to be the active player here. offset = 0 if color == chess.WHITE else 6 offset1 = 6 if color == chess.WHITE else 18 # Chess enumerates piece types beginning with one, which we have # to account for idx = piece_type - 1 # We use now a for loop to save the squares attacked by the piece we just found for i in list(self.board.attacks(square)): array[chess.square_rank(i),chess.square_file(i),idx+offset1] = 1 array[rank, file, idx + offset] = 1 # Repetition counters array[:, :, 24] = self.board.is_repetition(2) array[:, :, 25] = self.board.is_repetition(3) #return array #def observation(self, board: chess.Board) -> np.array: #Converts chess.Board observations instance to numpy arrays. #self._history.push(board) #history = self._history.view(orientation=board.turn) history = array meta = np.zeros( shape=(8 ,8, 7), dtype=int ) # Active player color meta[:, :, 0] = int(self.board.turn) # Total move count meta[:, :, 1] = self.board.fullmove_number # Active player castling rights meta[:, :, 2] = self.board.has_kingside_castling_rights(self.board.turn) meta[:, :, 3] = self.board.has_queenside_castling_rights(self.board.turn) # Opponent player castling rights meta[:, :, 4] = self.board.has_kingside_castling_rights(not self.board.turn) meta[:, :, 5] = self.board.has_queenside_castling_rights(not self.board.turn) # No-progress counter meta[:, :, 6] = self.board.halfmove_clock observation = np.concatenate([history, meta], axis=-1) return np.transpose(observation, (2, 0, 1))
def dir_and_dist(square1, square2): rank1 = chess.square_rank(square1) rank2 = chess.square_rank(square2) file1 = chess.square_file(square1) file2 = chess.square_file(square2) horiz = 'W' if file1 > file2 else 'E' vert = 'S' if rank1 > rank2 else 'N' filedist = abs(file1 - file2) rankdist = abs(rank1 - rank2) diag = filedist == rankdist straight = filedist == 0 or rankdist == 0 knight = (filedist == 1 and rankdist == 2) or (filedist == 2 and rankdist == 1) far = 'F' if rankdist == 2 else 'S' if diag: dist = filedist return {'dir': '{}{}'.format(vert, horiz), 'dist': dist} elif straight: queen_dir = horiz if filedist != 0 else vert dist = filedist if filedist != 0 else rankdist return {'dir': '{}'.format(queen_dir), 'dist': dist} elif knight: # The distance should not be needed when handling a knight move, -1 is an indicator return {'dir': '{}{}{}'.format(vert, horiz, far), 'dist': -1}
def click(self, x, y): global focus, turn x = int(x / 50) y = int(y / 50) # moving the piece that is in my team if focus != None and focus.p.color == turn: # wheter the move was legal or not if focus.Move(x * 50, y * 50, s) == True: # moving a turn turn = not turn focus = None return # unselecting when move is illigal but staying in function to see if player selected something else focus = None # on which soldier did the player click and is it on his team for square in chess.SQUARES: f = board.piece_at(square) if f != None and chess.square_file( square ) == x and 7 - chess.square_rank(square) == y and f.color == turn: focus = soldier(chess.square_file(square), 7 - chess.square_rank(square), f) break
def en_passant(puzzle: Puzzle) -> bool: for node in puzzle.mainline[1::2]: if (util.moved_piece_type(node) == PAWN and square_file( node.move.from_square) != square_file(node.move.to_square) and not node.parent.board().piece_at(node.move.to_square)): return True return False
def moveMatch(candidateMove): if move[1] == 0: match = board.is_castling(candidateMove) elif move[1] == 1: match = board.is_capture(candidateMove) and chess.square_rank( candidateMove.from_square) < chess.square_rank( candidateMove.to_square) # forward capture (y-axis) elif move[1] == 2: match = board.is_capture(candidateMove) and chess.square_rank( candidateMove.from_square) > chess.square_rank( candidateMove.to_square) # backwards capture elif move[1] == 3: match = board.is_capture(candidateMove) and chess.square_rank( candidateMove.from_square) == chess.square_rank( candidateMove.to_square) # horizontal capture elif move[1] == 4: match = board.is_into_check(candidateMove) elif move[1] == 5: match = chess.square_rank( candidateMove.from_square) < chess.square_rank( candidateMove.to_square) # forward candidateMove elif move[1] == 6: match = chess.square_rank( candidateMove.from_square) > chess.square_rank( candidateMove.to_square) elif move[1] == 7: match = chess.square_file( candidateMove.from_square) > chess.square_file( candidateMove.to_square) elif move[1] == 8: match = chess.square_file( candidateMove.from_square) < chess.square_file( candidateMove.to_square) return match
def back_rank_mate(puzzle: Puzzle) -> bool: node = puzzle.game.end() board = node.board() king = board.king(not puzzle.pov) assert king is not None assert isinstance(node, ChildNode) back_rank = 7 if puzzle.pov else 0 if board.is_checkmate() and square_rank(king) == back_rank: squares = SquareSet.from_square(king + (-8 if puzzle.pov else 8)) if puzzle.pov: if chess.square_file(king) < 7: squares.add(king - 7) if chess.square_file(king) > 0: squares.add(king - 9) else: if chess.square_file(king) < 7: squares.add(king + 9) if chess.square_file(king) > 0: squares.add(king + 7) for square in squares: piece = board.piece_at(square) if piece is None or piece.color == puzzle.pov or board.attackers( puzzle.pov, square): return False return any( square_rank(checker) == back_rank for checker in board.checkers()) return False
def ladder(self, available_squares, square): # If all the remaining squares are attacked by a queen or a rook ladder = False checkmater_file = 0 checkmater_rank = 0 attacker_file = 0 attacker_rank = 0 for i in available_squares[1:4]: for attacker in self.board.attackers(self.winner(), i): if square != i and (str(self.board.piece_at(attacker)).upper() == 'Q' or str(self.board.piece_at(attacker)).upper() == 'R'): # For some reason, it would print 'Ladder mate' 3 times. This just bypasses it. attacker_file = chess.square_file(attacker) attacker_rank = chess.square_rank(attacker) ladder = True if (available_squares[0] and available_squares[4]) in self.board.attacks(square): checkmater_file = chess.square_file(square) checkmater_rank = chess.square_rank(square) ladder = True else: ladder = False if (((checkmater_file == 0 and attacker_file == 1) or (checkmater_file == 7 and attacker_file == 6)) or ((checkmater_rank == 0 and attacker_rank == 1) or (checkmater_rank == 7 and attacker_rank == 6))): if ladder: print('Ladder mate')
def move_to_tensor(move): return [ chess.square_file(move.from_square) / 8, chess.square_rank(move.from_square) / 8, chess.square_file(move.to_square) / 8, chess.square_rank(move.to_square) / 8 ]
def remove_closest(self, pieces_squares, square, piece_type): #skip if the list of possible squares is empty if not pieces_squares: return min = 100 pr = 0 sr = chess.square_file(square) if piece_type is chess.PAWN: #print("pawn") new_ps = [] for ps in pieces_squares: if chess.square_file(ps) is sr: new_ps.append(ps) if new_ps: pieces_squares = new_ps for ps in pieces_squares: dist = chess.square_distance(ps, square) if dist < min: pr = ps min = dist self.board.remove_piece_at(pr) #print(pr) return
def adjust_move_tensor(self, move_tensor): def move_tensor_diff(tensor_1, tensor_2): """ Compute the difference between two move tensors. """ source_diff = scipy.spatial.distance.euclidean( tensor_1[:2], tensor_2[:2]) target_diff = scipy.spatial.distance.euclidean( tensor_1[2:], tensor_2[2:]) return source_diff**2 + target_diff tensor_1 = [x * 8 for x in move_tensor[0][:-1]] legal_moves = list(self.legal_moves) min_diff = 139 # Maximum diff between two moves. for i, legal_move in enumerate(legal_moves): tensor_2 = [ chess.square_file(legal_move.from_square), chess.square_rank(legal_move.from_square), chess.square_file(legal_move.to_square), chess.square_rank(legal_move.to_square) ] diff = move_tensor_diff(tensor_1, tensor_2) if diff < min_diff: min_diff = diff move_i = i return (legal_moves[move_i], min_diff / 139)
def encode_knight_move(move): def encode_delta(delta): return 0 if delta >= 0 else 1 file_delta = chess.square_file(move.to_square) - chess.square_file(move.from_square) rank_delta = chess.square_rank(move.to_square) - chess.square_rank(move.from_square) move_type = 0 if abs(rank_delta) > abs(file_delta) else 4 # Provides 8 unique encodings without 8 ifs return move_type + 2 * encode_delta(file_delta) + encode_delta(rank_delta)
def draw_pieces(screen, state): if state.PLAYING_SIDE is chess.WHITE: for i in range(len(chess.SQUARES)): square = chess.SQUARES[len(chess.SQUARES) - 1 - i] x = chess.square_file(square) y = chess.square_rank(square) piece = state.board.piece_at(i) if piece is not None: bpiece = pieces[piece.symbol()] else: bpiece = None if bpiece != None: screen.blit( IMAGES[bpiece], pg.Rect(WIDTH - SQUARE_SIZE - (x * SQUARE_SIZE), y * SQUARE_SIZE, SQUARE_SIZE, SQUARE_SIZE)) else: for i in range(len(chess.SQUARES)): square = chess.SQUARES[i] x = chess.square_file(square) y = chess.square_rank(square) piece = state.board.piece_at(i) if piece is not None: bpiece = pieces[piece.symbol()] else: bpiece = None if bpiece != None: screen.blit( IMAGES[bpiece], pg.Rect(WIDTH - SQUARE_SIZE - (x * SQUARE_SIZE), y * SQUARE_SIZE, SQUARE_SIZE, SQUARE_SIZE))
def deflection(puzzle: Puzzle) -> bool: for node in puzzle.mainline[1::2][1:]: captured_piece = node.parent.board().piece_at(node.move.to_square) if captured_piece or node.move.promotion: capturing_piece = util.moved_piece_type(node) if captured_piece and util.king_values[ captured_piece. piece_type] > util.king_values[capturing_piece]: continue square = node.move.to_square prev_op_move = node.parent.move assert (prev_op_move) grandpa = node.parent.parent assert isinstance(grandpa, ChildNode) prev_player_move = grandpa.move prev_player_capture = grandpa.parent.board().piece_at( prev_player_move.to_square) if ((not prev_player_capture or util.values[prev_player_capture.piece_type] < util.moved_piece_type(grandpa)) and square != prev_op_move.to_square and square != prev_player_move.to_square and (prev_op_move.to_square == prev_player_move.to_square or grandpa.board().is_check()) and (square in grandpa.board().attacks(prev_op_move.from_square) or node.move.promotion and square_file(node.move.to_square) == square_file(prev_op_move.from_square) and node.move.from_square in grandpa.board().attacks( prev_op_move.from_square)) and (not square in node.parent.board().attacks( prev_op_move.to_square))): return True return False
def is_passed(board, pawn, color): enemy_pawns = board.pieces(chess.PAWN, not color) opposing_squares = squares_ahead(pawn, color) if chess.square_file(pawn) != 0: opposing_squares |= squares_ahead(pawn - 1, color) if chess.square_file(pawn) != 7: opposing_squares |= squares_ahead(pawn + 1, color) return len(enemy_pawns & opposing_squares) == 0
def move_to_cat(board, move): pieceFile = chess.square_file(move.from_square) pieceType = board.piece_type_at(move.from_square) if pieceType == 3: piece = 0 elif pieceType == 2: piece = 1 elif pieceType == 4: piece = 2 if pieceType == 1 and pieceFile < 2: piece = 3 if pieceType == 1 and 1 < pieceFile < 4: piece = 4 if pieceType == 1 and 3 < pieceFile < 6: piece = 5 if pieceType == 1 and 5 < pieceFile < 8: piece = 6 elif pieceType == 6: piece = 7 elif pieceType == 5: piece = 8 pieces = [0, 0, 0, 0, 0, 0, 0, 0, 0] pieces[piece] = 1 moves = [0, 0, 0, 0, 0, 0, 0, 0, 0] def checking(board, move): board.push(move) check = board.is_check() board.pop() return check if board.is_castling(move): moves[0] = 1 elif board.is_capture(move) and chess.square_rank( move.from_square) < chess.square_rank(move.to_square): moves[1] = 1 elif board.is_capture(move) and chess.square_rank( move.from_square) > chess.square_rank(move.to_square): moves[2] = 1 elif board.is_capture(move) and chess.square_rank( move.from_square) == chess.square_rank(move.to_square): moves[3] = 1 if checking(board, move): moves[4] = 1 if chess.square_rank(move.from_square) < chess.square_rank( move.to_square): # forward move: moves[5] = 1 if chess.square_rank(move.from_square) > chess.square_rank(move.to_square): moves[6] = 1 if chess.square_file(move.from_square) > chess.square_file(move.to_square): moves[7] = 1 if chess.square_file(move.from_square) < chess.square_file(move.to_square): moves[8] = 1 return pieces + moves
def unpack(move: chess.Move) -> Tuple[int, int, int, int]: """Converts chess.Move instances into move coordinates.""" from_rank = chess.square_rank(move.from_square) from_file = chess.square_file(move.from_square) to_rank = chess.square_rank(move.to_square) to_file = chess.square_file(move.to_square) return from_rank, from_file, to_rank, to_file
def get_kings_distance_euclidean(board): king_white_square = board.king(True) king_black_square = board.king(False) king_white_file = chess.square_file(king_white_square) king_white_rank = chess.square_rank(king_white_square) king_black_file = chess.square_file(king_black_square) king_black_rank = chess.square_rank(king_black_square) x = abs(king_white_file - king_black_file) y = abs(king_white_rank - king_black_rank) return math.sqrt(x**2 + y**2)
def grecos(self, available_squares, square): if self.is_blocked(available_squares[1]): if chess.square_file(square) == 0 or chess.square_file(square) == 7: for attacker in self.board.attackers(self.winner(), available_squares[0]): if str(self.board.piece_at(attacker)) == 'B': print("Greco's mate") elif chess.square_rank(square) == 0 or chess.square_rank(square) == 7: for attacker in self.board.attackers(self.winner(), available_squares[2]): if str(self.board.piece_at(attacker)) == 'B': print("Greco's mate")
def hash_ep_square(self, board: chess.Board) -> int: # Hash in the en passant file. if board.ep_square: # But only if there's actually a pawn ready to capture it. Legality # of the potential capture is irrelevant. if board.turn == chess.WHITE: ep_mask = chess.shift_down(chess.BB_SQUARES[board.ep_square]) else: ep_mask = chess.shift_up(chess.BB_SQUARES[board.ep_square]) ep_mask = chess.shift_left(ep_mask) | chess.shift_right(ep_mask) if ep_mask & board.pawns & board.occupied_co[board.turn]: return self.array[772 + chess.square_file(board.ep_square)] return 0
def board(board=None, squares=None, flipped=False, coordinates=True, lastmove=None, check=None, arrows=(), size=None, style=None): """ Renders a board with pieces and/or selected squares as an SVG image. :param board: A :class:`chess.BaseBoard` for a chessboard with pieces or ``None`` (the default) for a chessboard without pieces. :param squares: A :class:`chess.SquareSet` with selected squares. :param flipped: Pass ``True`` to flip the board. :param coordinates: Pass ``False`` to disable coordinates in the margin. :param lastmove: A :class:`chess.Move` to be highlighted. :param check: A square to be marked as check. :param arrows: A list of :class:`~chess.svg.Arrow` objects like ``[chess.svg.Arrow(chess.E2, chess.E4)]`` or a list of tuples like ``[(chess.E2, chess.E4)]``. An arrow from a square pointing to the same square is drawn as a circle, like ``[(chess.E2, chess.E2)]``. :param size: The size of the image in pixels (e.g., ``400`` for a 400 by 400 board) or ``None`` (the default) for no size limit. :param style: A CSS stylesheet to include in the SVG image. >>> import chess >>> import chess.svg >>> >>> from IPython.display import SVG >>> >>> board = chess.Board("8/8/8/8/4N3/8/8/8 w - - 0 1") >>> squares = board.attacks(chess.E4) >>> SVG(chess.svg.board(board=board, squares=squares)) # doctest: +SKIP .. image:: ../docs/Ne4.svg """ margin = 20 if coordinates else 0 svg = _svg(8 * SQUARE_SIZE + 2 * margin, size) if style: ET.SubElement(svg, "style").text = style defs = ET.SubElement(svg, "defs") if board: for color in chess.COLORS: for piece_type in chess.PIECE_TYPES: if board.pieces_mask(piece_type, color): defs.append(ET.fromstring(PIECES[chess.Piece(piece_type, color).symbol()])) if squares: defs.append(ET.fromstring(XX)) if check is not None: defs.append(ET.fromstring(CHECK_GRADIENT)) for square, bb in enumerate(chess.BB_SQUARES): file_index = chess.square_file(square) rank_index = chess.square_rank(square) x = (file_index if not flipped else 7 - file_index) * SQUARE_SIZE + margin y = (7 - rank_index if not flipped else rank_index) * SQUARE_SIZE + margin cls = ["square", "light" if chess.BB_LIGHT_SQUARES & bb else "dark"] if lastmove and square in [lastmove.from_square, lastmove.to_square]: cls.append("lastmove") fill_color = DEFAULT_COLORS[" ".join(cls)] cls.append(chess.SQUARE_NAMES[square]) ET.SubElement(svg, "rect", { "x": str(x), "y": str(y), "width": str(SQUARE_SIZE), "height": str(SQUARE_SIZE), "class": " ".join(cls), "stroke": "none", "fill": fill_color, }) if square == check: ET.SubElement(svg, "rect", { "x": str(x), "y": str(y), "width": str(SQUARE_SIZE), "height": str(SQUARE_SIZE), "class": "check", "fill": "url(#check_gradient)", }) # Render pieces. if board is not None: piece = board.piece_at(square) if piece: ET.SubElement(svg, "use", { "xlink:href": "#%s-%s" % (chess.COLOR_NAMES[piece.color], chess.PIECE_NAMES[piece.piece_type]), "transform": "translate(%d, %d)" % (x, y), }) # Render selected squares. if squares is not None and squares & bb: ET.SubElement(svg, "use", { "xlink:href": "#xx", "x": str(x), "y": str(y), }) if coordinates: for file_index, file_name in enumerate(chess.FILE_NAMES): x = (file_index if not flipped else 7 - file_index) * SQUARE_SIZE + margin svg.append(_text(file_name, x, 0, SQUARE_SIZE, margin)) svg.append(_text(file_name, x, margin + 8 * SQUARE_SIZE, SQUARE_SIZE, margin)) for rank_index, rank_name in enumerate(chess.RANK_NAMES): y = (7 - rank_index if not flipped else rank_index) * SQUARE_SIZE + margin svg.append(_text(rank_name, 0, y, margin, SQUARE_SIZE)) svg.append(_text(rank_name, margin + 8 * SQUARE_SIZE, y, margin, SQUARE_SIZE)) for tail, head in arrows: tail_file = chess.square_file(tail) tail_rank = chess.square_rank(tail) head_file = chess.square_file(head) head_rank = chess.square_rank(head) xtail = margin + (tail_file + 0.5 if not flipped else 7.5 - tail_file) * SQUARE_SIZE ytail = margin + (7.5 - tail_rank if not flipped else tail_rank + 0.5) * SQUARE_SIZE xhead = margin + (head_file + 0.5 if not flipped else 7.5 - head_file) * SQUARE_SIZE yhead = margin + (7.5 - head_rank if not flipped else head_rank + 0.5) * SQUARE_SIZE if (head_file, head_rank) == (tail_file, tail_rank): ET.SubElement(svg, "circle", { "cx": str(xhead), "cy": str(yhead), "r": str(SQUARE_SIZE * 0.9 / 2), "stroke-width": str(SQUARE_SIZE * 0.1), "stroke": "#888", "fill": "none", "opacity": "0.5", }) else: marker_size = 0.75 * SQUARE_SIZE marker_margin = 0.1 * SQUARE_SIZE dx, dy = xhead - xtail, yhead - ytail hypot = math.hypot(dx, dy) shaft_x = xhead - dx * (marker_size + marker_margin) / hypot shaft_y = yhead - dy * (marker_size + marker_margin) / hypot xtip = xhead - dx * marker_margin / hypot ytip = yhead - dy * marker_margin / hypot ET.SubElement(svg, "line", { "x1": str(xtail), "y1": str(ytail), "x2": str(shaft_x), "y2": str(shaft_y), "stroke": "#888", "stroke-width": str(SQUARE_SIZE * 0.2), "opacity": "0.5", "stroke-linecap": "butt", "class": "arrow", }) marker = [] marker.append((xtip, ytip)) marker.append((shaft_x + dy * 0.5 * marker_size / hypot, shaft_y - dx * 0.5 * marker_size / hypot)) marker.append((shaft_x - dy * 0.5 * marker_size / hypot, shaft_y + dx * 0.5 * marker_size / hypot)) ET.SubElement(svg, "polygon", { "points": " ".join(str(x) + "," + str(y) for x, y in marker), "fill": "#888", "opacity": "0.5", "class": "arrow", }) return ET.tostring(svg).decode("utf-8")