The Python code I created using Python and Tensorflow does not work as I want

50 Views Asked by At

I wrote two codes, one of which generates random moves, evaluates them with Stockfish, and then trains them to create a model. Other code for using this model and playing with it.

import chess
import chess.engine
import numpy as np
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Dropout
from sklearn.model_selection import train_test_split
import tensorflow as tf
from tqdm import tqdm
import random

STOCKFISH_PATH = "/Users/berkegulacar/Downloads/stockfish"
ENGINE_TIME_LIMIT = 1.0
NUM_GAMES = 500

def evaluate_board(engine, board):
    with engine.analysis(board, limit=chess.engine.Limit(time=ENGINE_TIME_LIMIT)) as analysis:
        for info in analysis:
            if info.get("score") is not None:
                return info.get("score").white().score(mate_score=10000)
    return

def board_to_input(board):
    piece_to_index = {
        'P': 0, 'N': 1, 'B': 2, 'R': 3, 'Q': 4, 'K': 5,
        'p': 6, 'n': 7, 'b': 8, 'r': 9, 'q': 10, 'k': 11
    }
    board_planes = np.zeros((64, 12), dtype=np.float32)
    for square in chess.SQUARES:
        piece = board.piece_at(square)
        if piece:
            index = piece_to_index[piece.symbol()]
            board_planes[square, index] = 1
    board_planes = board_planes.reshape((8, 8, 12))
    extra_features = np.zeros((8, 8, 5), dtype=np.float32)
    extra_features[:, :, 0] = int(board.has_kingside_castling_rights(chess.WHITE))
    extra_features[:, :, 1] = int(board.has_queenside_castling_rights(chess.WHITE))
    extra_features[:, :, 2] = int(board.has_kingside_castling_rights(chess.BLACK))
    extra_features[:, :, 3] = int(board.has_queenside_castling_rights(chess.BLACK))
    if board.ep_square:
        ep_square = np.unravel_index(board.ep_square, (8, 8))
        extra_features[ep_square[0], ep_square[1], 4] = 1
    turn_channel = np.full((8, 8, 1), int(board.turn == chess.BLACK), dtype=np.float32)
    board_input = np.concatenate((board_planes, extra_features, turn_channel), axis=-1)
    return board_input

def play_game(engine):
    board = chess.Board()
    game_data = []
    while not board.is_game_over(claim_draw=True):
        move = random.choice(list(board.legal_moves))
        board.push(move)
        score = evaluate_board(engine, board)
        game_data.append((board_to_input(board), score))
    return game_data

def play_and_collect_data(num_games, engine_path):
    engine = chess.engine.SimpleEngine.popen_uci(engine_path)
    games_data = []
    for _ in tqdm(range(num_games), desc="Playing games and collecting data"):
        games_data.extend(play_game(engine))
    engine.quit()
    return games_data

def preprocess_data(games_data):
    X, y = zip(*games_data)
    X = np.array(X, dtype=np.float32)
    y = np.array(y, dtype=np.float32) / 10000
    return train_test_split(X, y, test_size=0.1, random_state=42)

def create_model(input_shape):
    model = Sequential([
        Dense(512, activation='relu', input_shape=input_shape),
        Dropout(0.2),
        Dense(512, activation='relu'),
        Dropout(0.2),
        Dense(1, activation='tanh')
    ])
    model.compile(optimizer='adam', loss='mean_squared_error')
    return model

def train_model(model, X_train, y_train, X_test, y_test):
    model.fit(X_train, y_train, validation_data=(X_test, y_test), epochs=50)
    return model

if __name__ == '__main__':
    games_data = play_and_collect_data(NUM_GAMES, STOCKFISH_PATH)
    X_train, X_test, y_train, y_test = preprocess_data(games_data)
    model = create_model(X_train[0].shape)
    model = train_model(model, X_train, y_train, X_test, y_test)
    model.save('model.h5')

**And code for playing with model: **

import chess
import chess.engine
import numpy as np
import tensorflow as tf

MODEL_PATH = '/Users/berkegulacar/Downloads/model.h5'
STOCKFISH_PATH = '/Users/berkegulacar/Downloads/stockfish'

model = tf.keras.models.load_model(MODEL_PATH)
engine = chess.engine.SimpleEngine.popen_uci(STOCKFISH_PATH)

def board_to_input(board):
    piece_to_index = {
        'P': 0, 'N': 1, 'B': 2, 'R': 3, 'Q': 4, 'K': 5,
        'p': 6, 'n': 7, 'b': 8, 'r': 9, 'q': 10, 'k': 11
    }
    board_planes = np.zeros((64, 12), dtype=np.float32)
    for square in chess.SQUARES:
        piece = board.piece_at(square)
        if piece:
            index = piece_to_index[piece.symbol()]
            board_planes[square, index] = 1
    board_planes = board_planes.reshape((8, 8, 12))
    extra_features = np.zeros((8, 8, 5), dtype=np.float32)
    extra_features[:, :, 0] = int(board.has_kingside_castling_rights(chess.WHITE))
    extra_features[:, :, 1] = int(board.has_queenside_castling_rights(chess.WHITE))
    extra_features[:, :, 2] = int(board.has_kingside_castling_rights(chess.BLACK))
    extra_features[:, :, 3] = int(board.has_queenside_castling_rights(chess.BLACK))
    if board.ep_square:
        ep_square = np.unravel_index(board.ep_square, (8, 8))
        extra_features[ep_square[0], ep_square[1], 4] = 1
    turn_channel = np.full((8, 8, 1), int(board.turn == chess.BLACK), dtype=np.float32)
    board_input = np.concatenate((board_planes, extra_features, turn_channel), axis=-1)
    return board_input

def select_best_move(model, board, engine):
    legal_moves = list(board.legal_moves)
    best_move = None
    best_move_score = -np.inf

    for move in legal_moves:
        board.push(move)
        processed_board = board_to_input(board).reshape(1, 8, 8, 18)
        board_score = model.predict(processed_board)
        board.pop()
        current_move_score = board_score.flatten()[0]
        
        if current_move_score > best_move_score:
            best_move_score = current_move_score
            best_move = move

    if best_move is None:
        result = engine.play(board, chess.engine.Limit(time=0.1))
        best_move = result.move
        print("Stockfish's move: {best_move}")
    else:
        print(f"Model's move: {best_move}")

    return best_move

def play_game(model, engine):
    board = chess.Board()
    while not board.is_game_over(claim_draw=True):
        print(board)
        user_move = input("Your move (e.g., e2e4): ")
        try:
            move = chess.Move.from_uci(user_move)
            if move in board.legal_moves:
                board.push(move)
            else:
                print("Invalid move, try again.")
                continue
        except ValueError:
            print("Invalid move format, try again.")
            continue
        if board.is_game_over(claim_draw=True):
            break
        best_move = select_best_move(model, board, engine)
        board.push(best_move)
    print("Game result:", board.result())

if __name__ == '__main__':
    play_game(model, engine)
    engine.quit()

The problem is that I think there is a scoring error in the trained model, so no matter what I play, it always repeats the same and illogical g8h6 followed by h8g8 and g8h8 and is checkmated. I need help for this. I'm waiting for your help.

I tried fen to numeric and one-hot encoding but the result is the same.

0

There are 0 best solutions below