File size: 4,753 Bytes
5dfdf10
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# from PIL import Image
from board_to_fen.predict import get_fen_from_image_path
import chess as c
import chess.engine as ce

import easyocr
from langchain.tools import tool

import os
import numpy as np
import cv2
from typing import Dict

os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3"

BASE_DIR = os.path.dirname(os.path.abspath(__file__))

STOCKFISH_EXECUTABLE_PATH = os.path.join(
    BASE_DIR,
    '../../external/chess-modules/stockfish/stockfish-ubuntu-x86-64-avx2'
)

STOCKFISH_EXECUTABLE_PATH = os.path.normpath(STOCKFISH_EXECUTABLE_PATH)


@tool
def grab_board_view(chess_img_path: str) -> Dict:
    """
    Identify the chess board view from its related filepath (.png, .jpg or .jpeg)

    Parameters
    ----------
    chess_img_path: str
        File path to Chess Board Image. It must point to a 2D board image

    Returns:
        Dict: {"board_view", board_view}  # Where board_view in (True, False, "Unknown")

    Example:
        >>> grab_board_view("chess_board.png")
        {"board_view", True}
    """
    cropped_square_path = "temp-square.png"
    # Read Chess Board Img and Crop the bottom-left square
    img_bgr = cv2.imread(chess_img_path)
    img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)

    img_np = np.array(img_rgb)
    img_np_shape = img_np.shape
    img_rows, img_cols = img_np_shape[:2]
    img_cropped = img_np[img_rows // 8 * 7 :, : img_cols // 8 * 1, :]
    img_cropped = cv2.cvtColor(img_cropped, cv2.COLOR_BGR2RGB)
    cv2.imwrite(cropped_square_path, img_cropped)

    # Wrap text from image path
    reader = easyocr.Reader(["en"], gpu=False)
    results = reader.readtext(image=cropped_square_path)
    results.reverse()

    # Grab text
    left_bottom_cell = ""
    for _, text_temp, _ in results:
        left_bottom_cell += text_temp

    # Identify black view
    if left_bottom_cell in ("h8", "8h", "h", "8"):
        black_view = True
    elif left_bottom_cell in ("a1", "1a", "a", "1"):
        black_view = False
    else:
        black_view = "Unknown"

    # delete temp image
    if os.path.isfile(cropped_square_path):
        os.remove(cropped_square_path)

    return {"black_view": black_view}


@tool
def extract_fen_position(path_to_chess_img: str, black_view: bool = True) -> str:
    """
    Get the FEN position from chess 2D board.

    Parameters
    ----------
    path_to_chess_img : str
        file path to chess board, must have png, jpg, jpeg formats

    black_view: bool
        Set to True iff the board is viewed from Black's perspective.

    Returns:
        str: Predicted Chess position in FEN notation

    Example:
        >>> extract_fen_position("chess_board.png")
        'r1bqkbnr/pppp1ppp/2n5/4p3/1b2P3/2N2N2/PPPP1PPP/R1BQKB1R w KQkq - 2 4'
    """
    fen = get_fen_from_image_path(path_to_chess_img, black_view=black_view)
    return fen


@tool
def predict_next_best_move(
    fen_position: str, path_to_stockfish: str = STOCKFISH_EXECUTABLE_PATH
) -> str:
    """
    Leverage Stockfish 17.1 to predict the next best move.

    Parameters
    ----------
    fen_position : str
        FEN notation for the Chess Image. Must have the following syntax
        syntax: [Piece Placement] [Active Color] [Castling Availability] [En Passant Target Square] [Halfmove Clock] [Fullmove Number]


    Returns:
        str: Predicted next best move in FEN notation

    Example:
        >>> predict_next_best_move('r1bqkbnr/pppp1ppp/2n5/4p3/1b2P3/2N2N2/PPPP1PPP/R1BQKB1R w KQkq - 2 4')
        'O-O'
    """
    # Init Chess Board
    board = c.Board(fen_position)

    # Get Stockfish model
    chess_engine = ce.SimpleEngine.popen_uci(path_to_stockfish)

    # Predict next best move
    stockfish_player = chess_engine.play(board=board, limit=ce.Limit(time=10))
    best_move_uci = stockfish_player.move
    best_move = board.san(
        move=best_move_uci
    )  # Convert from Universal Chess interface to Standard Algebraic Notation
    return best_move


if __name__ == "__main__":
    #result = grab_board_view(chess_img_path="/home/santiagoal/.cache/huggingface/hub/datasets--gaia-benchmark--GAIA/snapshots/897f2dfbb5c952b5c3c1509e648381f9c7b70316/2023/validation/cca530fc-4052-43b2-b130-b30968d8aa44.png")
    #print(result)
    #print("\n", "=" * 30, "\n")
    #chess_img_path = input("Please type the path to Chess Board img: ")
    #fen = extract_fen_position(path_to_chess_img=chess_img_path)
    #usr_res = input(
    #    f"\nThe predicted FEN is {fen}\nif you want to correct it, please type the correction, otherwise type Enter: "
    #)
    usr_res = input("Pass FEN: ")
    fen = usr_res if len(usr_res) > 1 else fen
    best_move = predict_next_best_move(fen_position=fen)
    print(f"The next best move is: {best_move}")

# TODO: Address warnings