- Blind mode tutorial
lichess.org
Donate

How I Made MY OWN Chess Engine?

ChessChess botChess engine
SmileyMate - my new chess engine

Hello everyone! This blog is suitable for those who want to try to make their own chess engine, and just for those who want to listen to how it is done) I warn you right away - 1) excuse me for the mistakes! I used a translator) 2) I am a BEGINNER programmer, I wrote most of the code using ChatGPT LoL. I'll try to explain the main pieces of code in my own words) Well, as you probably already understood, this is not recommended for experienced programmers)
Well, that's it, I wish you a pleasant viewing!

1. The main thing is to start

Once you start - that's it, you won't be able to tear yourself away from the task) The same thing happened to me. Moreover, I was so motivated that when I didn't know something, but it had to be implemented, I turned to ChatGPT lol. I don't know how, but such a wonderful name as "SmileyMate" came to my mind right after the start of the project. Well, let's move on to step 2.

2. Programming

I know that all engines are written in c++ etc. but I just feel more comfortable writing in python. It probably won't be powerful even after 1000 updates and improvements, but... This is just a hobby project for now. So, here's my code:

#!/usr/bin/env python3
import sys
import chess
import random

piece_values = {
    chess.PAWN: 1,
    chess.KNIGHT: 3,
    chess.BISHOP: 3,
    chess.ROOK: 5,
    chess.QUEEN: 9,
    chess.KING: 0
}

center_squares = [chess.D4, chess.E4, chess.D5, chess.E5]

def evaluate_board(board):
    
    if board.is_checkmate():

        return 10000 if board.turn == chess.BLACK else -10000
    if board.is_stalemate():
        return 0

    score = 0
    for piece_type in piece_values:
        score += len(board.pieces(piece_type, chess.WHITE)) * piece_values[piece_type]
        score -= len(board.pieces(piece_type, chess.BLACK)) * piece_values[piece_type]
    for square in center_squares:
        piece = board.piece_at(square)
        if piece:
            score += 0.2 if piece.color == chess.WHITE else -0.2
    return score

def minimax(board, depth):
    if depth == 0 or board.is_game_over():
        return evaluate_board(board)

    legal_moves = list(board.legal_moves)
    if board.turn == chess.WHITE:
        max_eval = -float('inf')
        for move in legal_moves:
            board.push(move)
            eval = minimax(board, depth - 1)
            board.pop()
            if eval > max_eval:
                max_eval = eval
        return max_eval
    else:
        min_eval = float('inf')
        for move in legal_moves:
            board.push(move)
            eval = minimax(board, depth - 1)
            board.pop()
            if eval < min_eval:
                min_eval = eval
        return min_eval

def choose_move(board):
    
    if board.fullmove_number == 1:
        return random.choice(list(board.legal_moves))

    best_score = -float('inf') if board.turn == chess.WHITE else float('inf')
    best_moves = []

    for move in board.legal_moves:
        board.push(move)
        score = minimax(board, 2)  
        board.pop()

        if board.turn == chess.WHITE:
            if score > best_score:
                best_score = score
                best_moves = [move]
            elif score == best_score:
                best_moves.append(move)
        else:
            if score < best_score:
                best_score = score
                best_moves = [move]
            elif score == best_score:
                best_moves.append(move)

    return random.choice(best_moves) if best_moves else None

def main():
    board = chess.Board()
    while True:
        line = sys.stdin.readline()
        if not line:
            break
        line = line.strip()

        if line == "uci":
            print("id name SmileyMate version 1.0.3")
            print("id author Classic")
            print("uciok")
        elif line == "isready":
            print("readyok")
        elif line.startswith("ucinewgame"):
            board.reset()
        elif line.startswith("position"):
            parts = line.split(" ")
            if "startpos" in parts:
                board.reset()
                if "moves" in parts:
                    moves_index = parts.index("moves")
                    moves = parts[moves_index + 1:]
                    for mv in moves:
                        board.push_uci(mv)
            elif "fen" in parts:
                fen_index = parts.index("fen")
                fen_str = " ".join(parts[fen_index + 1:fen_index + 7])
                board.set_fen(fen_str)
                if "moves" in parts:
                    moves_index = parts.index("moves")
                    moves = parts[moves_index + 1:]
                    for mv in moves:
                        board.push_uci(mv)
        elif line.startswith("go"):
            move = choose_move(board)
            if move is not None:
                print("bestmove", move.uci())
            else:
                print("bestmove 0000")
        elif line == "quit":
            break

        sys.stdout.flush()

if __name__ == "__main__":
    main()

As you can see, the engine must support UCI. If you have windows and you use BotLi (like me) or Lichess-bot, then the engine just needs to be compiled into .exe from .py. It's not difficult at all, here's a little guide on how to do it (for beginners):
In CMD: 1) pip install pyinstaller
2) cd and_your_foloder
3) pyinstaller --onefile your_engine.py
After a while, you will have a "dist" folder in your folder, which contains itself.the exe file. All that remains is to go into the bot's config and specify the path to your engine :)

I repeat, chatgpt wrote most of the code for my engine. Currently, the bot with the nickname @NewChessEngine-ai is constantly playing using my engine (this is my engine, and by the way, it plays 24/7), and you can challenge it right now! And yes, do not make a conclusion from the first move, so as not to waste time - my engine in the code makes the first move randomly. I'm sure you're surprised how the engine plays so well in 133 lines of code) Me, too, btw.

I hope I managed to motivate you to make your own engine, and convince you that it's not difficult at all. Let's hold a contest on the forum: who has a better engine (just don't copy stockfish, lol), it will be interesting to see your code options. If you liked the blog, please like it, it's probably the only thing that can motivate me to blog further (besides being active on the forum)! Thanks for reading about this moment, see you in the next blogs! ;)

P.S. Now the bot is @C0kval , and it use my other engine - Okval