flask_app.py:
from flask import Flask, request, jsonify, render_template
import chess
import json
from bot_brains import BotNN

app = Flask(__name__)

bot = BotNN()
game_moves = []  # List to store moves for training

@app.route("/")
def home():
    return render_template("index.html")

@app.route("/bot_move", methods=["POST"])
def bot_move():
    fen = request.json["fen"]
    board = chess.Board(fen)
    move = bot.choose_move(board)
    game_moves.append(move)
    return jsonify({"move": move.uci()})

@app.route("/save_game", methods=["POST"])
def save_game():
    data = request.get_json()
    with open("player.json", "w") as f:
        json.dump({"fen": data["fen"]}, f)
    return jsonify({"status": "success"})

@app.route("/end_game", methods=["POST"])
def end_game():
    """
    End the game and train the bot based on the result.
    """
    result = request.json["result"]  # 1 for win, -1 for loss
    bot.train(game_moves, result)
    game_moves.clear()  # Reset moves after training
    return jsonify({"status": "game ended, bot trained"})

if __name__ == "__main__":
    app.run(debug=True)


bot_brains.py:
import chess
import numpy as np

class BotNN:
    def __init__(self):
        self.weights, self.biases = self._init_brain()
        self.learning_rate = 0.01  # Learning rate for weight updates
        self.discount_factor = 0.9  # Factor for considering future rewards
        self.history = []  # To store moves for training

    def _init_brain(self):
        input_size = 768  # 8x8 board with 12 piece types
        hidden_size = 128
        output_size = 64  # Number of possible moves

        weights = {
            'input_to_hidden': np.random.randn(input_size, hidden_size),
            'hidden_to_output': np.random.randn(hidden_size, output_size)
        }
        biases = {
            'hidden': np.random.randn(hidden_size),
            'output': np.random.randn(output_size)
        }

        return weights, biases

    def sigmoid(self, x):
        return 1 / (1 + np.exp(-x))

    def sigmoid_derivative(self, x):
        return x * (1 - x)

    def forward(self, board):
        board_input = self.board_to_input(board)
        hidden_layer = self.sigmoid(np.dot(board_input, self.weights['input_to_hidden']) + self.biases['hidden'])
        output_layer = self.sigmoid(np.dot(hidden_layer, self.weights['hidden_to_output']) + self.biases['output'])
        return output_layer, hidden_layer, board_input

    def board_to_input(self, board):
        input_vector = np.zeros(768)
        for i, square in enumerate(board.board_fen()):
            if square == 'r':
                input_vector[i] = 1
            # Handle other pieces (for simplicity, extend this part)
        return input_vector

    def choose_move(self, board):
        output, _, _ = self.forward(board)
        best_move_idx = np.argmax(output)
        return chess.Move.from_uci(str(best_move_idx))

    def train(self, game_moves, result):
        """
        Train the bot after the game has ended.
        - `game_moves` is the list of moves made during the game.
        - `result` is the outcome of the game (1 for win, -1 for loss, 0 for draw).
        """
        for move in game_moves:
            output, hidden_layer, board_input = self.forward(move)
            # Calculate reward (win: +1, loss: -1, draw: 0)
            reward = result  # We use the result to calculate the reward

            # Update weights based on reward and discount factor
            output_gradient = self.sigmoid_derivative(output) * reward
            hidden_gradient = self.sigmoid_derivative(hidden_layer) * np.dot(self.weights['hidden_to_output'], output_gradient)

            # Update the weights using the gradients
            self.weights['hidden_to_output'] += self.learning_rate * np.outer(hidden_layer, output_gradient)
            self.weights['input_to_hidden'] += self.learning_rate * np.outer(board_input, hidden_gradient)

            # Optionally, could store the game for more complex training with self-play or backpropagation.

    def reset_history(self):
        self.history.clear()

    def add_move_to_history(self, move):
        self.history.append(move)

index.html:
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Chess Game with AI</title>
  <script src="https://unpkg.com/@chriso/chessboardjs@1.0.0/dist/chessboard-1.0.0.min.js"></script>
  <link rel="stylesheet" href="https://unpkg.com/@chriso/chessboardjs@1.0.0/dist/chessboard-1.0.0.min.css" />
  <style>
    #board { width: 400px; height: 400px; margin: 20px; }
  </style>
</head>
<body>
  <h1>Chess with AI Bot</h1>
  <div id="board"></div>
  <button onclick="startNewGame()">Start New Game</button>

  <!-- Buttons to end the game with win/loss -->
  <button onclick="endGame(1)">End Game (Win)</button>
  <button onclick="endGame(-1)">End Game (Loss)</button>

  <script>
    let board;
    let game = new Chess();

    // Initialize Chessboard and attach to the div with ID 'board'
    function initBoard() {
      board = Chessboard('board', {
        draggable: true,
        dropOffBoard: 'trash',
        sparePieces: true,
        onDrop: onMove,
        onMouseoutSquare: squareMouseout,
        onMouseoverSquare: squareMouseover
      });
      board.start();
    }

    // Handle piece movement on the board
    function onMove (source, target) {
      const move = game.move({
        from: source,
        to: target,
        promotion: 'q'
      });

      renderMoveHistory(game.history());
      makeBotMove();
      saveGameState();
    }

    // Render the move history
    function renderMoveHistory(moves) {
      let historyElement = document.querySelector('.move-history');
      historyElement.innerHTML = moves.join(', ');
    }

    // Make the bot's move
    function makeBotMove() {
      fetch('/bot_move', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ fen: game.fen() })
      })
      .then(response => response.json())
      .then(data => {
        const move = game.move(data.move);
        renderMoveHistory(game.history());
        board.position(game.fen());
        saveGameState();
      });
    }

    // Save the game state
    function saveGameState() {
      fetch('/save_game', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ fen: game.fen() })
      });
    }

    // Start a new game
    function startNewGame() {
      game.reset();
      board.start();
      saveGameState();
    }

    // End the game (Win/Loss)
    function endGame(result) {
      fetch('/end_game', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ result: result })
      })
      .then(response => response.json())
      .then(data => {
        console.log("Game ended, training complete");
      });
    }

    // Call initBoard when the page is fully loaded
    window.onload = function() {
      initBoard();
    };
  </script>
</body>
</html>