def check_game_over( self, message: Dict[str, str], bot_handler: Any, new_board: chess.Board ) -> bool: """Checks if a game is over due to - checkmate, - stalemate, - insufficient material, - 50 moves without a capture or pawn move, or - 3-fold repetition. Replies to the bot handler if it is game over. Parameters: - message: The Zulip Bots message object. - bot_handler: The Zulip Bots bot handler object. - new_board: The board object. Returns: True if it is game over, false if it's not. """ # This assumes that the players will claim a draw after 3-fold # repetition or 50 moves go by without a capture or pawn move. # According to the official rules, the game is only guaranteed to # be over if it's *5*-fold or *75* moves, but if either player # wants the game to be a draw, after 3 or 75 it a draw. For now, # just assume that the players would want the draw. if new_board.is_game_over(True): game_over_output = '' if new_board.is_checkmate(): game_over_output = make_loss_response( new_board, 'was checkmated' ) elif new_board.is_stalemate(): game_over_output = make_draw_response('stalemate') elif new_board.is_insufficient_material(): game_over_output = make_draw_response( 'insufficient material' ) elif new_board.can_claim_fifty_moves(): game_over_output = make_draw_response( '50 moves without a capture or pawn move' ) elif new_board.can_claim_threefold_repetition(): game_over_output = make_draw_response('3-fold repetition') bot_handler.send_reply( message, game_over_output ) return True return False
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_game_over_reason(board: chess.Board, claim_draw: bool = False, both_agreed: bool = False) -> str: if claim_draw and both_agreed: return "Both opponents agreed to a draw" reasons = [ "Checkmate", "Stalemate", "Insufficient material", "Draw by the seventyfive-move rule", "Draw by the fivefold repetition rule", ] checks = [ board.is_checkmate(), board.is_stalemate(), board.is_insufficient_material(), board.is_seventyfive_moves(), board.is_fivefold_repetition(), ] if claim_draw: reasons = [ *reasons, *[ "Draw by the fifty-move rule", "Draw by the threefold repetition rule" ], ] checks = [ *checks, *[ board.can_claim_fifty_moves(), board.can_claim_threefold_repetition() ], ] possible = [reason for reason, check in zip(reasons, checks) if check] if len(possible) > 0: return possible[0] else: raise RuntimeError("No possible game over reason")
def get_best_move(board: chess.Board) -> chess.Move: my_color = board.turn global_max_score = -sys.maxsize best_moves = [] # attacked_pieces = get_attacked_pieces(board, my_color) # print(attacked_pieces) for my_candidate_move in list(board.legal_moves): board.push(my_candidate_move) bonus = 0 if board.is_checkmate(): return my_candidate_move if board.is_stalemate(): bonus = +500 if board.can_claim_threefold_repetition(): current_score = __get_board_total_score(board, my_color) if current_score > 0: bonus = +1 else: bonus = -1 is_my_candidate_move_attacked = __is_attacked(board, my_candidate_move.to_square) candidate_min_score = sys.maxsize for opponent_candidate_move in list(board.legal_moves): board.push(opponent_candidate_move) if board.is_checkmate(): current_score = -9999 else: current_score = __get_board_total_score(board, my_color) + bonus if is_my_candidate_move_attacked: current_score = current_score + 1 candidate_min_score = min(current_score, candidate_min_score) board.pop() if candidate_min_score > global_max_score: global_max_score = candidate_min_score best_moves = [my_candidate_move] elif candidate_min_score == global_max_score: best_moves.append(my_candidate_move) board.pop() best_move = random.choice(best_moves) # Always promote to queen. if best_move.uci()[-1].isalpha(): best_move.promotion = chess.QUEEN return best_move