def process_tree(node: ChessSearchNode, board: chess.Board): if node.move_index is None: fen = board.fen() move_san = '' else: move = chess.Move.from_uci(self.config.labels[node.move_index]) move_san = board.san(move) board.push(move) fen = board.fen() tree = { 'fen': fen, 'prev_move': move_san, 'static_value': node.static_value, 'rollup_value': node.rollup_value, 'move_probability': node.move_probability, } if node.children_created and node.depth < max_depth: children = [ process_tree(child, board) for move_index, child in node.children.items() ] children = sorted(children, key=lambda child: child['move_probability'], reverse=True) tree['children'] = children if node.depth > 0: board.pop() return tree
def get_root(fen, moves): board = Board(fen) root = Node(fen) if moves is not None: for move in moves: fen = board.fen().split(' ') root.previous.append(' '.join(fen[:2])) board.push(Move.from_uci(move)) root.position = board.fen() return root
def dfs(state, num_searches, isplayer): comp = max if isplayer else min board = Board(state) if (board.is_checkmate()): return 0.0 if isplayer else 1.0 if (board.is_stalemate()): return 0.5 if (board.can_claim_draw()): return 0.5 rv = 0.0 if isplayer else 1.0 moves = list(board.legal_moves) # if number of nodes to search exceeds the search limit provided # score all nodes if (len(moves) > num_searches): for move in moves: board.push(move) rv = comp(rv, score(board.fen())) board.pop() return rv search_move = [False for _ in range(len(moves))] search_count = 0 i = 0 while i < len(moves): inc = min(60, len(moves) - i) # gives 50% chance for all nodes to be searched # TODO make an adjustable probability parameter tp = random.randrange(1 << inc) while tp > 0: if (tp & 1): search_move[i] = True search_count += 1 tp >>= 1 i += inc score_count = len(moves) - search_count nodes_per_search = 0 if (search_count != 0): nodes_per_search = (num_searches - score_count) // search_count vals = [] for i in range(len(moves)): move = moves[i] if search_move[i]: board.push(move) rv = comp(rv, dfs(board.fen(), nodes_per_search, not isplayer)) board.pop() else: board.push(move) rv = comp(rv, score(move)) board.pop() return rv
def repertoire(id=1): if request.args.get('practice'): print(practice(id)) board = Board() form = MoveForm() if request.method == 'GET': position = Position.query.filter_by(id=id).first() moves = Move.query.join(User.moves).filter(User.id == current_user.id, Move.source_position_id == position.id).all() return render_template('tools/repertoire.html', fen=position.fen + ' 0 1', moves=moves, form=form, id=id) if request.method == 'POST': fen = form.append_fen.data san = form.append_san.data quality_id = form.quality.data board = Board(fen) board.push_san(san) source_position = Position.query.filter_by(fen=' '.join(fen.split()[:-2])).first() end_position_fen = ' '.join(board.fen().split()[:-2]) end_position = Position.query.filter_by(fen=end_position_fen).first() if end_position is None: end_position = Position(fen=end_position_fen) db.session.add(end_position) move = Move.query.filter_by(source_position_id=source_position.id, destination_position_id=end_position.id).first() if move is None: move = Move(source_position_id=source_position.id, destination_position_id=end_position.id, san=san) um = UserMove(quality_id=quality_id) um.move = move current_user.moves.append(um) db.session.commit() moves = Move.query.join(User.moves).filter(User.id == current_user.id, Move.source_position_id == source_position.id).all() return render_template('tools/repertoire.html', fen=source_position.fen + ' 0 1', moves=moves, form=form, id=id)
def evaluation_from_board(board: chess.Board, evaluation_depth: int = 22) -> Dict: board_fen = board.fen() stockfish = Stockfish("/opt/homebrew/Cellar/stockfish/13/bin/stockfish") stockfish.set_depth(evaluation_depth) # set engine depth stockfish.set_fen_position(board_fen) return stockfish.get_evaluation()
def evaluate(self, board: chess.Board) -> int: """ Evaluate board via. piece position/value tables in addition to material differences Args: board (chess.Board): board state to evaluate Returns: (int) """ val = 0 for square in chess.SQUARES: piece = board.piece_type_at(square) color = board.color_at(square) if piece: # Get base piece value, add position based value from # piece value table piece_value = self.piece_values[PIECE_NAMES[piece]].value piece_value += get_piece_value_from_table( PIECE_NAMES[piece], color, square, self.value_tables) if not color: piece_value *= -1 val += piece_value self.evaluations[board.fen()] = val return val
def next_move(count): count = int(count) // 2 fen = request.form['fen'] if count <= 5: move = opening_book_next_move(fen, 'performance.bin') if move is not None: return move # use the c version one b = Board(fen) if is_endgame(b): move = query_end_game_move(b.fen()) if move != None: return move if b.turn: side = '0' # white else: side = '1' result = s.run(['./ai/c_modules/main', fen, side], stdout=s.PIPE) move = result.stdout.decode('utf-8').split("\n")[-2] # board = Board(fen) # move = min_f_root(board, -10001, 10001, 4)[1] # black is the ai, so call min_f return move
def find_random_child(self) -> Node: """ Get a random move """ random_move = random.choice(list(Board(self.fen).legal_moves)) new_board = Board(fen=self.fen) new_board.push(random_move) return Node(fen=new_board.fen())
def command(depth: int, board: chess.Board, msg: str): """ Accept UCI commands and respond. The board state is also updated. """ msg = msg.strip() tokens = msg.split(" ") while "" in tokens: tokens.remove("") if msg == "quit": sys.exit() if msg == "uci": print("id name Andoma") # Andrew/Roma -> And/oma print("id author Andrew Healey & Roma Parramore") print("uciok") return if msg == "isready": print("readyok") return if msg == "ucinewgame": return if msg.startswith("position"): if len(tokens) < 2: return # Set starting position if tokens[1] == "startpos": board.reset() moves_start = 2 elif tokens[1] == "fen": fen = " ".join(tokens[2:8]) board.set_fen(fen) moves_start = 8 else: return # Apply moves if len(tokens) <= moves_start or tokens[moves_start] != "moves": return for move in tokens[(moves_start + 1):]: board.push_uci(move) if msg == "d": # Non-standard command, but supported by Stockfish and helps debugging print(board) print(board.fen()) if msg[0:2] == "go": _move = next_move(depth, board) print(f"bestmove {_move}") return
def encode_state(self, board: chess.Board): ''' Encode the board to check for repetitions :param board: chess.Board ''' encoding = board.fen() self._encoded_state_counter[encoding] = self._encoded_state_counter.get(encoding, 0) + 1 if self._encoded_state_counter[encoding] > self._max_repetitions: self._max_repetitions = self._encoded_state_counter[encoding]
def make_ai_move(board: chess.Board): board_copy = chess.Board(board.fen(), chess960=True) move = ai.make_move( board_copy, 10.0 ) # Change this float if you want to test your AI under time pressure if isinstance(move, str): move = chess.Move.from_uci(move) return move
def iterate(self, n_iters, s: chess.Board): s_fen = s.fen() self.nodes_parameters[s_fen] = np.array((1, 0)) for i in range(n_iters): # v, n = self.search(s) # self.nodes_parameters[s_fen][0] += n # self.nodes_parameters[s_fen][1] += v self.search_iter(s_fen) if True or i % 5 == 0: print(f'Iteration {i+1} [{"=" * (i//5)}>{" " * ((n_iters-i-1)//5)}]')
def handle_state_change(self, event): board = Board(fen=self.initial_fen) for move in event["moves"].split(): board.push(Move.from_uci(move)) self.node = Node(fen=board.fen()) self.my_turn = not self.my_turn print(f"My turn? {self.my_turn}") if self.my_turn: self.make_move()
def children(fen: str) -> FrozenSet[Node]: """ Get children of a given game state """ board = Board(fen=fen) next_moves = [] for move in board.legal_moves: new_board = Board(fen=fen) new_board.push(move) next_moves.append(Node(fen=new_board.fen())) return frozenset(next_moves)
def alphabeta(board: chess.Board, depth: int, alpha: chess.Board, beta: chess.Board, player: chess.Color): if depth == 0: return eval(board, alpha, beta, player) if player == chess.WHITE: for move in board.generate_legal_moves(): new_board = board.copy() new_board.push(move) alpha = alphabeta(new_board, depth-1, alpha, beta, chess.BLACK) if model.predict_mlp(utils.bitify(beta.fen()), utils.bitify(alpha.fen()))[0] == 1: break return alpha else: for move in board.generate_legal_moves(): new_board = board.copy() new_board.push(move) beta = alphabeta(new_board, depth-1, alpha, beta, chess.WHITE) if model.predict_mlp(utils.bitify(beta.fen()), utils.bitify(alpha.fen()))[0] == 1: break return beta
def add_if_known(board: chess.Board, game_legal_moves: LegalMovesT, game_states: List[np.ndarray]) -> None: """If board state hasn't already been observed: Adds state to set and list of observed states. Also adds legal moves and state corresponding to the Board.""" board_fen = board.fen() if board_fen not in observed_states_set: observed_states_set.add(board_fen) observed_states.append(board_fen) state_legal_moves = get_state_legal_moves(board=board) game_legal_moves.append(state_legal_moves) add_board_state_to_list(board=board, in_list=game_states)
def get_move(old_node: Node, new_node: Node) -> str: """ Get the UCI of a move given an old and new state """ old_board = Board(fen=old_node.fen) for move in old_board.legal_moves: temp_board = Board(fen=old_node.fen) temp_board.push(move) if temp_board.fen() == new_node.fen: return move.uci() else: raise RuntimeError( "Trying to make illegal move! " f"{old_node.fen} -> {new_node.fen}" )
def board_to_numpy(self, board: chess.Board): fen = board.fen().split(' ')[0].split('/') output = np.empty(shape=(8, 8), dtype=str) for i, row in enumerate(fen): j = 0 for square in row: if square.isnumeric(): for _ in range(j, int(square) + j): output[i][j] = '.' j += 1 continue output[i][j] = square j += 1 return output
class Kidpawn(): def __init__(self, board_fen: str = None): if board_fen: self.b = Board(board_fen) else: # Not sure why this is needed. self.b = Board() def svg(self): return self.b._repr_svg_() def fen(self): """Returns board state as a FEN string """ return self.b.fen() def move(self, move: str) -> [bool, str]: """Returns true if the move succeeded """ try: self.b.push_uci(move) return True, f"you moved: {move}" except ValueError as e: msg = str(e) if msg.startswith("illegal uci"): return False, f"illegal move: {move}. Try something like d2d4 or h7h8q." else: return False, f"other error {msg}" except Exception as e: traceback.print_exc() return False, f"unknown error {e}" def bot_move(self): """Computer makes a move herself, and updates the board """ m = lookahead1_move(self.b) if m: return self.move(m.uci()) else: return False, "No valid moves" def game_over_msg(self) -> str: """If the game is over, returns a string why. If game is not over, return blank """ if self.b.is_game_over(): return f"Game over: {self.b.result()}" else: return None
def write_ascii(self, board: chess.Board): """ Loops through a game board and prints it out as ascii. Useful for debugging. For example, the starting board would print out: ---|-------------------------------- 8 | r | n | b | q | k | b | n | r | ---|-------------------------------- 7 | p | p | p | p | p | p | p | p | ---|-------------------------------- 6 | | | | | | | | | ---|-------------------------------- 5 | | | | | | | | | ---|-------------------------------- 4 | | | | | | | | | ---|-------------------------------- 3 | | | | | | | | | ---|-------------------------------- 2 | P | P | P | P | P | P | P | P | ---|-------------------------------- 1 | R | N | B | Q | K | B | N | R | ---|-------------------------------- | a | b | c | d | e | f | g | h | """ fen = board.fen() rows = fen.split(' ')[0].split('/') output = '---|%s\n' % ('-' * 32) row_separator = '\n' + output row_nums = reversed(range(1, 9)) for row_num, row in zip(row_nums, rows): output += ' %s | ' % row_num for piece in row: if piece.isdigit(): output += ' | ' * int(piece) else: output += piece + ' | ' output += row_separator output += ' | ' for i in ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']: output += str(i) + ' | ' self.set_header('Content-Type', 'text/plain; charset=utf-8') self.finish(output)
def write_json(self, board: chess.Board): """ Writes all of the board info in json """ best_move = self.get_best_move(board) output = OrderedDict([ ('fen', board.fen()), ('fullmoveNumber', board.fullmove_number), ('result', board.result()), ('isGameOver', board.is_game_over()), ('isCheckmate', board.is_checkmate()), ('isStalemate', board.is_stalemate()), ('isInsufficientMaterial', board.is_insufficient_material()), ('isSeventyfiveMoves', board.is_seventyfive_moves()), ('isFivefoldRepetition', board.is_fivefold_repetition()), ('white', OrderedDict([ ('hasKingsideCastlingRights', board.has_kingside_castling_rights(chess.WHITE)), ('hasQueensideCastlingRights', board.has_queenside_castling_rights(chess.WHITE)), ])), ('black', OrderedDict([ ('hasKingsideCastlingRights', board.has_kingside_castling_rights(chess.BLACK)), ('hasQueensideCastlingRights', board.has_queenside_castling_rights(chess.BLACK)), ])), ('turn', OrderedDict([ ('color', 'white' if board.turn is chess.WHITE else 'black'), ('isInCheck', board.is_check()), ('bestMove', best_move), ('legalMoves', [move.uci() for move in board.legal_moves]), ('canClaimDraw', board.can_claim_draw()), ('canClaimFiftyMoves', board.can_claim_fifty_moves()), ('canClaimThreefoldRepetition', board.can_claim_threefold_repetition()), ])), ]) self.finish(output)
def get_board_values(self, board: chess.Board, white_multiplier: int): """ :param board: chess board :param white_multiplier: 1 if AI is white else -1 :return: """ # using Hans Berliner's system fen = board.fen() total = 0 for square in fen: if square in self.piece_value: total += self.piece_value[square] total *= white_multiplier return round(total, 2)
def _search(cls, board: chess.Board, value_func: Callable[[chess.Board], float], max_depth: int, memory: Optional[Dict[str, float]], curr_depth: int = 0, alpha: float = -float("inf"), beta: float = float("inf")) -> Tuple[float, chess.Move, Optional[Dict[str, float]]]: assert max_depth >= 1, f"max_depth should be positive integer, {max_depth} is passed" if curr_depth == max_depth: if memory is None: return value_func(board), None, None else: board_fen = board.fen() if board_fen in memory: return memory[board_fen], None, memory else: value = value_func(board) memory[board_fen] = value return value, None, memory # maximize if board.turn == chess.WHITE: best_valuation = -float("inf") best_move = None for move in board.legal_moves: board.push(move) valuation, _, memory = cls._search(board, value_func, max_depth, memory, curr_depth+1, best_valuation, beta) board.pop() if valuation > best_valuation: best_valuation = valuation best_move = move if best_valuation > beta: return beta, best_move, memory # minimize elif board.turn == chess.BLACK: best_valuation = float("inf") best_move = None for move in board.legal_moves: board.push(move) valuation, _, memory = cls._search(board, value_func, max_depth, memory, curr_depth+1, alpha, best_valuation) board.pop() if valuation < best_valuation: best_valuation = valuation best_move = move if best_valuation < alpha: return alpha, best_move, memory return best_valuation, best_move, memory
def log_board(board: Board, unicode_pieces=True): """ Logs the fen string and board representation """ log(Color.VIOLET, board.fen()) w_color = Color.WHITE b_color = Color.DARK_GREY sq_color = Color.BLACK board_str = "\n " + str(board).replace("\n", "\n ") board_str = re.sub("[a-z]", lambda p: b_color + p[0] + sq_color, board_str) board_str = re.sub("[A-Z]", lambda p: w_color + p[0] + sq_color, board_str) if unicode_pieces: piece_map = { "k": "\u2654", "q": "\u2655", "r": "\u2656", "b": "\u2657", "n": "\u2658", "p": "\u2659", } for p, code in piece_map.items(): board_str = board_str.replace(p, code).replace(p.capitalize(), code) log(sq_color, board_str + "\n")
def nn_select_best_moves(self, board: chess.Board): """Select best moves in board.""" good_moves = True if self.use_nn: hash = chess.polyglot.zobrist_hash(board) if hash not in self.nn_tb: good_moves = list() for move in board.legal_moves: if self.nn.check_move(board.fen(), move.uci()): good_moves.append(move) self.nn_tb[hash] = good_moves good_moves = self.nn_tb[hash] if not good_moves: good_moves = list(board.legal_moves) self.nn_tb[hash] = good_moves if int(sys.getsizeof(self.nn_tb) / 1024 / 1024) >= self.hashlimit / 2: del self.nn_tb[list(self.nn_tb.keys())[0]] self.hashfull += 1 return good_moves else: return list(board.legal_moves)
def check(self: PuzzleChecker) -> None: unchecked_puzzles = self.list_unchecked_puzzles() log.info(f"{len(unchecked_puzzles)} still need to be checked") with open(PUZZLE_CHECKED_PATH, "a") as output: for i, (puzzle_id, puzzle_info) in enumerate(unchecked_puzzles): print(f"\r{i} puzzles processed, {self.tl():.2f}s", end="") b = Board(fen=puzzle_info.fen) res: Set[Error] = set() for i, move in enumerate(puzzle_info.moves): if i % 2 and nb_piece( b ) <= 7: # 0, 2, 4... are moves made by the opponent, we don't check them res = res.union( self.req(b.fen(), move, puzzle_info.expected_winning)) b.push_uci(move) if bool(res): # Not empty log.error( f"puzzle {puzzle_id} contains some errors: {res}") output.write(puzzle_id + " " + " ".join(map(lambda x: x.name, res)) + "\n") time.sleep(0.55) #rate-limited otherwise
def get_move(state): #assume both players play the best moves #states where you play, take the maximum calculated as rv #states where opponent plays, take the min calculated as rv #limit number of searches artificially for now #choose better numbers later print('started search over state', state) search_limit = 1000 board = Board(state) mx = 0 rv = None vals = [] for move in board.legal_moves: board.push(move) val = dfs(board.fen(), search_limit, False) if (val > mx): rv = move mx = val board.pop() vals.append((str(move), val)) print(vals) print('found move', rv) return rv
def game2permove(d): result, moves = d data = [] result = result.strip() moves = sum( [m.strip().split() for m in re.split(r'[0-9]+\. ', moves) if m], []) # value of state from player's perspective # 1 from winner's perspective, -1 from loser's perspective, 0 if tie value = 1 if result == '1-0' else -1 if result == '0-1' else 0 board = Board() for move_str in moves: move = board.parse_san(move_str) assert idx2move(board, *move2idx(move)) == move data.append((board.fen(), value, move_str)) # Move to next data point board.push(move) value *= -1 return data
def evaluate(self, board: chess.Board) -> int: """ Evaluate boardstate via. material difference on board Args: board (chess.Board): board state to evaluate Returns: (int) """ val = 0 for square in chess.SQUARES: # For each piece on board, get value of piece on board. piece = board.piece_type_at(square) color = board.color_at(square) if piece: piece_value = self.piece_values[PIECE_NAMES[piece]].value if not color: # BLACK encoded as False piece_value *= -1 val += piece_value self.evaluations[board.fen()] = val # Return difference in piece values between white & black return val
class DialogEnterPosition(QDialog): def __init__(self, board=None, parent=None): super(DialogEnterPosition, self).__init__(parent) #self.resize(600, 400) self.setWindowTitle(self.trUtf8("Enter Position")) self.displayBoard = DisplayBoard(self.deep_copy_board_pos(board),self) # create a copy of the current board if(board): # create a deepcopy of current board self.current = self.deep_copy_board_pos(board) else: self.current = Board() self.cbWhiteShort = QCheckBox(self.trUtf8("White O-O")) self.cbWhiteLong = QCheckBox(self.trUtf8("White O-O-O")) self.cbBlackShort = QCheckBox(self.trUtf8("Black O-O")) self.cbBlackLong = QCheckBox(self.trUtf8("Black O-O-O")) grpBox_castle = QGroupBox(self.trUtf8("Castling Rights")) vbox_castle = QVBoxLayout() vbox_castle.addWidget(self.cbWhiteShort) vbox_castle.addWidget(self.cbWhiteLong) vbox_castle.addWidget(self.cbBlackShort) vbox_castle.addWidget(self.cbBlackLong) vbox_castle.addStretch(1) grpBox_castle.setLayout(vbox_castle) self.rbWhite = QRadioButton(self.trUtf8("White To Move")) self.rbBlack = QRadioButton(self.trUtf8("Black To Move")) grpBox_turn = QGroupBox(self.trUtf8("Turn")) vbox_radio = QVBoxLayout() vbox_radio.addWidget(self.rbWhite) vbox_radio.addWidget(self.rbBlack) vbox_radio.addStretch(1) grpBox_turn.setLayout(vbox_radio) self.buttonInit = QPushButton(self.trUtf8("Initial Position")) self.buttonClear = QPushButton(self.trUtf8("Clear Board")) self.buttonCurrent = QPushButton(self.trUtf8("Current Position")) vbox_config = QVBoxLayout() vbox_config.addWidget(grpBox_castle) vbox_config.addWidget(grpBox_turn) vbox_config.addStretch(1) vbox_config.addWidget(self.buttonInit) vbox_config.addWidget(self.buttonClear) vbox_config.addWidget(self.buttonCurrent) hbox = QHBoxLayout() hbox.addWidget(self.displayBoard) hbox.addLayout(vbox_config) vbox = QVBoxLayout() vbox.addLayout(hbox) self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok| QDialogButtonBox.Cancel) vbox.addWidget(self.buttonBox) self.setLayout(vbox) self.connect(self.buttonBox, SIGNAL("accepted()"), self, SLOT("accept()")) self.connect(self.buttonBox, SIGNAL("rejected()"), self, SLOT("reject()")) self.cbWhiteShort.toggled.connect(self.set_castling_rights) self.cbWhiteLong.toggled.connect(self.set_castling_rights) self.cbBlackShort.toggled.connect(self.set_castling_rights) self.cbBlackLong.toggled.connect(self.set_castling_rights) self.rbWhite.toggle() self.rbWhite.toggled.connect(self.set_turn) self.rbBlack.toggled.connect(self.set_turn) self.buttonInit.clicked.connect(self.initial_position) self.buttonClear.clicked.connect(self.clear_board) self.buttonCurrent.clicked.connect(self.set_current) # reset who's current turn it is and the current # castling rights of the position self.set_castling_rights() self.set_turn() def set_castling_rights(self): self.displayBoard.board.castling_rights = 0 if(self.cbWhiteShort.isChecked()): self.displayBoard.board.castling_rights = self.displayBoard.board.castling_rights | chess.BB_H1 if(self.cbWhiteLong.isChecked()): self.displayBoard.board.castling_rights = self.displayBoard.board.castling_rights | chess.BB_A1 if(self.cbBlackShort.isChecked()): self.displayBoard.board.castling_rights = self.displayBoard.board.castling_rights | chess.BB_H8 if(self.cbBlackLong.isChecked()): self.displayBoard.board.castling_rights = self.displayBoard.board.castling_rights | chess.BB_A8 if(self.displayBoard.board.status() == chess.STATUS_VALID): self.enable_ok_button() else: self.disable_ok_button() def set_turn(self): if(self.rbWhite.isChecked()): self.displayBoard.board.turn = WHITE else: self.displayBoard.board.turn = BLACK if(self.displayBoard.board.status() == 0): self.enable_ok_button() else: self.disable_ok_button() def clear_board(self): self.displayBoard.board.clear() self.set_castling_rights() self.set_turn() self.update() def initial_position(self): self.displayBoard.board.reset() self.set_castling_rights() self.set_turn() self.update() def set_current(self): fen = self.current.fen() board = Board(fen) self.displayBoard.board = board self.set_castling_rights() self.set_turn() self.update() # creates a deep copy of the given # board into a clean new board, resetting # all move histories, castling rights, etc. def deep_copy_board_pos(self,board): fresh = Board() for i in range(0,8): for j in range(0,8): piece = board.piece_at(j*8+i) if(piece): sym = piece.symbol() fresh.set_piece_at(j*8+i,Piece.from_symbol(sym)) else: fresh.remove_piece_at(j*8+i) return fresh def enable_ok_button(self): self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(True) self.update() def disable_ok_button(self): self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(False) self.update()
class DialogEnterPosition(QDialog): def __init__(self, board=None, parent=None): super(DialogEnterPosition, self).__init__(parent) # self.resize(600, 400) self.setWindowTitle(self.trUtf8("Enter Position")) self.displayBoard = DisplayBoard(self.deep_copy_board_pos(board), self) # create a copy of the current board if board: # create a deepcopy of current board self.current = self.deep_copy_board_pos(board) else: self.current = Board() self.cbWhiteShort = QCheckBox(self.trUtf8("White O-O")) self.cbWhiteLong = QCheckBox(self.trUtf8("White O-O-O")) self.cbBlackShort = QCheckBox(self.trUtf8("Black O-O")) self.cbBlackLong = QCheckBox(self.trUtf8("Black O-O-O")) grpBox_castle = QGroupBox(self.trUtf8("Castling Rights")) vbox_castle = QVBoxLayout() vbox_castle.addWidget(self.cbWhiteShort) vbox_castle.addWidget(self.cbWhiteLong) vbox_castle.addWidget(self.cbBlackShort) vbox_castle.addWidget(self.cbBlackLong) vbox_castle.addStretch(1) grpBox_castle.setLayout(vbox_castle) self.rbWhite = QRadioButton(self.trUtf8("White To Move")) self.rbBlack = QRadioButton(self.trUtf8("Black To Move")) grpBox_turn = QGroupBox(self.trUtf8("Turn")) vbox_radio = QVBoxLayout() vbox_radio.addWidget(self.rbWhite) vbox_radio.addWidget(self.rbBlack) vbox_radio.addStretch(1) grpBox_turn.setLayout(vbox_radio) self.buttonInit = QPushButton(self.trUtf8("Initial Position")) self.buttonClear = QPushButton(self.trUtf8("Clear Board")) self.buttonCurrent = QPushButton(self.trUtf8("Current Position")) vbox_config = QVBoxLayout() vbox_config.addWidget(grpBox_castle) vbox_config.addWidget(grpBox_turn) vbox_config.addStretch(1) vbox_config.addWidget(self.buttonInit) vbox_config.addWidget(self.buttonClear) vbox_config.addWidget(self.buttonCurrent) hbox = QHBoxLayout() hbox.addWidget(self.displayBoard) hbox.addLayout(vbox_config) vbox = QVBoxLayout() vbox.addLayout(hbox) self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) vbox.addWidget(self.buttonBox) self.setLayout(vbox) self.connect(self.buttonBox, SIGNAL("accepted()"), self, SLOT("accept()")) self.connect(self.buttonBox, SIGNAL("rejected()"), self, SLOT("reject()")) self.cbWhiteShort.toggled.connect(self.set_castling_rights) self.cbWhiteLong.toggled.connect(self.set_castling_rights) self.cbBlackShort.toggled.connect(self.set_castling_rights) self.cbBlackLong.toggled.connect(self.set_castling_rights) self.rbWhite.toggle() self.rbWhite.toggled.connect(self.set_turn) self.rbBlack.toggled.connect(self.set_turn) self.buttonInit.clicked.connect(self.initial_position) self.buttonClear.clicked.connect(self.clear_board) self.buttonCurrent.clicked.connect(self.set_current) # reset who's current turn it is and the current # castling rights of the position self.set_castling_rights() self.set_turn() def set_castling_rights(self): self.displayBoard.board.castling_rights = 0 if self.cbWhiteShort.isChecked(): self.displayBoard.board.castling_rights = self.displayBoard.board.castling_rights | chess.BB_H1 if self.cbWhiteLong.isChecked(): self.displayBoard.board.castling_rights = self.displayBoard.board.castling_rights | chess.BB_A1 if self.cbBlackShort.isChecked(): self.displayBoard.board.castling_rights = self.displayBoard.board.castling_rights | chess.BB_H8 if self.cbBlackLong.isChecked(): self.displayBoard.board.castling_rights = self.displayBoard.board.castling_rights | chess.BB_A8 if self.displayBoard.board.status() == chess.STATUS_VALID: self.enable_ok_button() else: self.disable_ok_button() def set_turn(self): if self.rbWhite.isChecked(): self.displayBoard.board.turn = WHITE else: self.displayBoard.board.turn = BLACK if self.displayBoard.board.status() == 0: self.enable_ok_button() else: self.disable_ok_button() def clear_board(self): self.displayBoard.board.clear() self.set_castling_rights() self.set_turn() self.update() def initial_position(self): self.displayBoard.board.reset() self.set_castling_rights() self.set_turn() self.update() def set_current(self): fen = self.current.fen() board = Board(fen) self.displayBoard.board = board self.set_castling_rights() self.set_turn() self.update() # creates a deep copy of the given # board into a clean new board, resetting # all move histories, castling rights, etc. def deep_copy_board_pos(self, board): fresh = Board() for i in range(0, 8): for j in range(0, 8): piece = board.piece_at(j * 8 + i) if piece: sym = piece.symbol() fresh.set_piece_at(j * 8 + i, Piece.from_symbol(sym)) else: fresh.remove_piece_at(j * 8 + i) return fresh def enable_ok_button(self): self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(True) self.update() def disable_ok_button(self): self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(False) self.update()
class PyCachEngine: def __init__(self, path, db_path, options=dict()): self.board = Board() self.db = UnQLite(db_path) self.engine = Popen(path, universal_newlines=True, stdin=PIPE, stdout=PIPE) self._put('uci') self._ready() for option, val in options.items(): self._set_option(option, val) self.num_games = 1 while True: self.board.reset() self.learn(200) def __del__(self): self.db.close() self.engine.kill() def _put(self, line): if not self.engine.stdin: raise BrokenPipeError() self.engine.stdin.write(line + '\n') self.engine.stdin.flush() def _read(self): if not self.engine.stdout: raise BrokenPipeError() return self.engine.stdout.readline().strip() def _ready(self): self._put('isready') while self._read() != 'readyok': continue def _bestmove(self): while True: line = self._read() if 'depth' in line: depth = int(line.split()[2]) if 'bestmove' in line: move = line.split()[1] return (move, depth) def _set_option(self, option, value): self._put(f'setoption option {option} value {value}') def _store(self, new_fen, move, depth): with self.db.transaction(): if new_fen in self.db: _move, _depth = eval(self.db[new_fen].decode('utf-8')) print(_move, _depth) if int(_depth) >= depth: return self.db[new_fen] = (move, depth) self.db.commit() def learn(self, movetime): fen = self.board.fen() new_fen = ' '.join(fen.split()[:-2]) self._put(f'position fen {fen}') self._put(f'go movetime {movetime}') move, depth = self._bestmove() self.board.push_uci(move) self._store(new_fen, move, depth) system('clear') # print(fen) print(self.board) print() print('new_fen:', new_fen) print('depth:', depth) print('move:', move) print('db_size:', len(self.db)) print('num_games:', self.num_games) if not self.board.is_game_over(): self.learn(movetime) else: result = self.board.outcome().result() self.num_games += 1 print(result)