I used new GPT-4o to write python script for strong chess engine.
After few days messaging with GPT, I got really strong engine.
I know nothing about the programming, I only do what GPT explained me.
I tried to transform python script to C++ language with help of GPT but not successfully, because I remembered when LKaufman wrote C++ is much faster than previous language of Komodo. I tried to make UCI form of this engine, too unsuccessfully.
But I am sure if I continue with GPT-4o maybe I got strong UCI engine which can compete with those existed engines.
GPT-4o made a chess engine
Moderators: hgm, chrisw, Rebel
-
- Posts: 21
- Joined: Sun Apr 07, 2024 3:44 pm
- Full name: Darko Markovic
-
- Posts: 12108
- Joined: Thu Mar 09, 2006 12:57 am
- Location: Birmingham UK
- Full name: Graham Laight
Re: GPT-4o made a chess engine
Are you willing to share the prompt you used to generate the code - or did it take multiple prompts?
If it took multiple prompts, would it be feasible to combine these into a single prompt?
New type of source code to start adding to GitHub: LLM prompts.
If it took multiple prompts, would it be feasible to combine these into a single prompt?
New type of source code to start adding to GitHub: LLM prompts.
Want to attract exceptional people? Be exceptional.
-
- Posts: 1933
- Joined: Tue Apr 19, 2016 6:08 am
- Location: U.S.A
- Full name: Andrew Grant
Re: GPT-4o made a chess engine
I would highly suspect the end result is not actually a chess engine.
When you can't win an argument, you censor it.
When you can't win an election, you remove your opponents.
Just because you've been doing something for a long time, does not mean you are any good at it.
When you can't win an election, you remove your opponents.
Just because you've been doing something for a long time, does not mean you are any good at it.
-
- Posts: 3021
- Joined: Wed Mar 10, 2010 10:18 pm
- Location: Hamburg, Germany
- Full name: Srdja Matovic
Re: GPT-4o made a chess engine
ATM I would be surprised if it plays even legal moves, previous attempts for using GPT for move generator code were blurry*, maybe later.
*using existing Python chess libraries is considered cheating
***edit***
Would be interesting, if it grasped the concept of AlphaBeta pruning or another game tree search though.
--
Srdja
*using existing Python chess libraries is considered cheating
***edit***
Would be interesting, if it grasped the concept of AlphaBeta pruning or another game tree search though.
--
Srdja
-
- Posts: 21
- Joined: Sun Apr 07, 2024 3:44 pm
- Full name: Darko Markovic
Re: GPT-4o made a chess engine
You're funnyAndrewGrant wrote: ↑Sat Jun 08, 2024 5:49 pm I would highly suspect the end result is not actually a chess engine.
Take a look the script (scroll down):
https://chatgpt.com/share/a7230082-e288 ... 289fbc7fb4
-
- Posts: 21
- Joined: Sun Apr 07, 2024 3:44 pm
- Full name: Darko Markovic
Re: GPT-4o made a chess engine
This is the output from an engine in that position, depth 5.
Engine evaluated all moves and played a move with highest eval:
(You can compare with eval from chinese base based on Stockfish)
Engine evaluated all moves and played a move with highest eval:
(You can compare with eval from chinese base based on Stockfish)
Last edited by darmar on Sat Jun 08, 2024 11:35 pm, edited 1 time in total.
-
- Posts: 3021
- Joined: Wed Mar 10, 2010 10:18 pm
- Location: Hamburg, Germany
- Full name: Srdja Matovic
Re: GPT-4o made a chess engine
Nice, thanks for sharingdarmar wrote: ↑Sat Jun 08, 2024 11:08 pm [...]
Take a look the script (scroll down):
https://chatgpt.com/share/a7230082-e288 ... 289fbc7fb4
--
Srdja
-
- Posts: 12108
- Joined: Thu Mar 09, 2006 12:57 am
- Location: Birmingham UK
- Full name: Graham Laight
Re: GPT-4o made a chess engine
Personally, if I wrote a chess engine, I would use a library: the part I'd want to do myself would be the fun bit: evaluating the position.
If I were running an LLM computer chess competition (engines where the source code is an LLM prompt, the invigilator runs the prompt against a public LL M, compiles the output, then plays these compiled executables against each other), I'd be inclined to have two categories: one that allows libraries and one that doesn't.
As I said in the "Gemini" thread, this would be a good thing to do because it would make computer chess tournaments fun again - like they used to be in the 20th century.
Want to attract exceptional people? Be exceptional.
-
- Posts: 3021
- Joined: Wed Mar 10, 2010 10:18 pm
- Location: Hamburg, Germany
- Full name: Srdja Matovic
Re: GPT-4o made a chess engine
+1 nice tournament!towforce wrote: ↑Sat Jun 08, 2024 11:40 pm [...]
If I were running an LLM computer chess competition (engines where the source code is an LLM prompt, the invigilator runs the prompt against a public LL M, compiles the output, then plays these compiled executables against each other), I'd be inclined to have two categories: one that allows libraries and one that doesn't.
[...]
--
Srdja
-
- Posts: 3021
- Joined: Wed Mar 10, 2010 10:18 pm
- Location: Hamburg, Germany
- Full name: Srdja Matovic
Re: GPT-4o made a chess engine
If it is okay, I would like to archive the source code here in this TC thread, it is really a step*, feel free to report my post to moderators if not ok...darmar wrote: ↑Sat Jun 08, 2024 11:08 pm [...]
Take a look the script (scroll down):
https://chatgpt.com/share/a7230082-e288 ... 289fbc7fb4
Code: Select all
import chess
import chess.pgn
import chess.polyglot
import os
# Piece values with slight positional bonuses
PIECE_VALUES = {
chess.PAWN: 100,
chess.KNIGHT: 320,
chess.BISHOP: 330,
chess.ROOK: 500,
chess.QUEEN: 900,
chess.KING: 20000
}
# Central squares and other positional factors
CENTER_SQUARES = [chess.E4, chess.D4, chess.E5, chess.D5]
KING_SAFETY_PENALTY = 50
PAWN_STRUCTURE_BONUS = 20
MOBILITY_BONUS = 5
def evaluate_board(board):
if board.is_checkmate():
return -PIECE_VALUES[chess.KING] if board.turn == chess.WHITE else PIECE_VALUES[chess.KING]
if board.is_stalemate() or board.is_insufficient_material():
return 0
value = 0
for piece_type in PIECE_VALUES:
value += len(board.pieces(piece_type, chess.WHITE)) * PIECE_VALUES[piece_type]
value -= len(board.pieces(piece_type, chess.BLACK)) * PIECE_VALUES[piece_type]
value += evaluate_pawn_structure(board)
value += evaluate_king_safety(board)
value += evaluate_control_of_center(board)
value += evaluate_piece_mobility(board)
return value
def evaluate_pawn_structure(board):
score = 0
for color in [chess.WHITE, chess.BLACK]:
pawns = board.pieces(chess.PAWN, color)
for pawn in pawns:
# Isolated pawns
if not any(board.piece_at(chess.square(pawn % 8, r)) == chess.PAWN and board.color_at(chess.square(pawn % 8, r)) == color for r in range(8) if r != chess.square_rank(pawn)):
score -= PAWN_STRUCTURE_BONUS if color == chess.WHITE else -PAWN_STRUCTURE_BONUS
# Doubled pawns
if len([p for p in pawns if chess.square_file(p) == chess.square_file(pawn)]) > 1:
score -= PAWN_STRUCTURE_BONUS // 2 if color == chess.WHITE else -PAWN_STRUCTURE_BONUS // 2
# Backward pawns
if is_backward_pawn(board, pawn, color):
score -= PAWN_STRUCTURE_BONUS // 3 if color == chess.WHITE else -PAWN_STRUCTURE_BONUS // 3
return score
def is_backward_pawn(board, pawn_square, color):
file = chess.square_file(pawn_square)
rank = chess.square_rank(pawn_square)
direction = 1 if color == chess.WHITE else -1
backward = True
for adj_file in [file - 1, file + 1]:
if 0 <= adj_file < 8:
adj_square = chess.square(adj_file, rank)
if board.piece_at(adj_square) == chess.PAWN and board.color_at(adj_square) == color:
backward = False
break
if backward:
for forward_rank in range(rank + direction, 8 if color == chess.WHITE else -1, direction):
forward_square = chess.square(file, forward_rank)
if board.piece_at(forward_square) == chess.PAWN and board.color_at(forward_square) == color:
backward = False
break
if (adj_file := file - 1) >= 0:
adj_square = chess.square(adj_file, forward_rank)
if board.piece_at(adj_square) == chess.PAWN and board.color_at(adj_square) != color:
backward = False
break
if (adj_file := file + 1) < 8:
adj_square = chess.square(adj_file, forward_rank)
if board.piece_at(adj_square) == chess.PAWN and board.color_at(adj_square) != color:
backward = False
break
return backward
def evaluate_king_safety(board):
score = 0
for color in [chess.WHITE, chess.BLACK]:
king_square = board.king(color)
king_rank = chess.square_rank(king_square)
king_file = chess.square_file(king_square)
pawns_around_king = 0
for rank in range(max(0, king_rank - 1), min(7, king_rank + 1) + 1):
for file in range(max(0, king_file - 1), min(7, king_file + 1) + 1):
if board.piece_at(chess.square(file, rank)) == chess.PAWN and board.color_at(chess.square(file, rank)) == color:
pawns_around_king += 1
score -= (3 - pawns_around_king) * KING_SAFETY_PENALTY if color == chess.WHITE else -(3 - pawns_around_king) * KING_SAFETY_PENALTY
return score
def evaluate_control_of_center(board):
score = 0
for square in CENTER_SQUARES:
if board.piece_at(square):
piece = board.piece_at(square)
if piece.color == chess.WHITE:
score += PIECE_VALUES[piece.piece_type] * 0.1
else:
score -= PIECE_VALUES[piece.piece_type] * 0.1
return score
def evaluate_piece_mobility(board):
score = 0
for color in [chess.WHITE, chess.BLACK]:
for square in board.piece_map():
piece = board.piece_at(square)
if piece.color == color:
if color == chess.WHITE:
score += len(board.attacks(square)) * MOBILITY_BONUS
else:
score -= len(board.attacks(square)) * MOBILITY_BONUS
return score
KILLER_MOVES = {}
HISTORY_HEURISTIC = {}
def order_moves(board, depth):
captures = []
non_captures = []
for move in board.legal_moves:
if board.is_capture(move):
captures.append(move)
else:
non_captures.append(move)
captures.sort(key=lambda move: PIECE_VALUES[board.piece_at(move.to_square).piece_type] if board.piece_at(move.to_square) else 0, reverse=True)
# Killer move heuristic
non_captures.sort(key=lambda move: (move in KILLER_MOVES.get(depth, []), HISTORY_HEURISTIC.get(move, 0)), reverse=True)
return captures + non_captures
def register_killer_move(move, depth):
if depth not in KILLER_MOVES:
KILLER_MOVES[depth] = [move]
elif move not in KILLER_MOVES[depth]:
KILLER_MOVES[depth].append(move)
if len(KILLER_MOVES[depth]) > 2:
KILLER_MOVES[depth].pop(0)
def register_history_heuristic(move, depth):
if move in HISTORY_HEURISTIC:
HISTORY_HEURISTIC[move] += depth * depth
else:
HISTORY_HEURISTIC[move] = depth * depth
TRANSPOSITION_TABLE = {}
def alpha_beta_search(board, depth, alpha, beta):
if depth == 0:
return quiescence_search(board, alpha, beta)
board_fen = board.fen()
if board_fen in TRANSPOSITION_TABLE and TRANSPOSITION_TABLE[board_fen][0] >= depth:
return TRANSPOSITION_TABLE[board_fen][1]
best_score = -float('inf')
for move in order_moves(board, depth):
board.push(move)
score = -alpha_beta_search(board, depth - 1, -beta, -alpha)
board.pop()
if score >= beta:
register_killer_move(move, depth)
TRANSPOSITION_TABLE[board_fen] = (depth, score)
return score
if score > alpha:
alpha = score
best_score = score
register_history_heuristic(move, depth)
TRANSPOSITION_TABLE[board_fen] = (depth, best_score)
return best_score
def null_move_pruning(board, depth, alpha, beta):
R = 2 # Reduction value for null move pruning
if depth > R and not board.is_check():
board.push(chess.Move.null())
score = -alpha_beta_search(board, depth - 1 - R, -beta, -beta + 1)
board.pop()
if score >= beta:
return beta
return None
def quiescence_search(board, alpha, beta):
stand_pat = evaluate_board(board)
if stand_pat >= beta:
return beta
if alpha < stand_pat:
alpha = stand_pat
for move in board.legal_moves:
if board.is_capture(move):
board.push(move)
score = -quiescence_search(board, -beta, -alpha)
board.pop()
if score >= beta:
return beta
if score > alpha:
alpha = score
return alpha
def search(board, depth):
alpha = -float('inf')
beta = float('inf')
best_move = None
best_score = -float('inf')
for move in order_moves(board, depth):
board.push(move)
score = -alpha_beta_search(board, depth - 1, -beta, -alpha)
board.pop()
if score > best_score:
best_score = score
best_move = move
# Print the search progress
print(f"Depth: {depth}, Move: {move}, Eval: {score}")
return best_move, best_score
def play_as_white():
board = chess.Board()
depth = 3
game = chess.pgn.Game()
game.headers["White"] = "Engine"
game.headers["Black"] = "User"
node = game
with chess.polyglot.open_reader("book.bin") as reader:
while not board.is_game_over():
print(board)
if board.turn == chess.WHITE:
move = None
try:
entry = reader.get(board)
move = entry.move
except:
pass
if not move:
move, score = search(board, depth)
board.push(move)
print(f"Engine move: {move}")
else:
user_move = input("Enter your move in UCI format (e.g., e2e4): ")
try:
move = chess.Move.from_uci(user_move)
if move in board.legal_moves:
board.push(move)
else:
print("Illegal move, try again.")
continue
except:
print("Invalid move format, try again.")
continue
node = node.add_variation(move)
print("Game over")
print(board.result())
# Save game to PGN file
with open("games.pgn", "a") as pgn_file:
print(game, file=pgn_file, end="\n\n")
def play_as_black():
board = chess.Board()
depth = 3
game = chess.pgn.Game()
game.headers["White"] = "User"
game.headers["Black"] = "Engine"
node = game
with chess.polyglot.open_reader("book.bin") as reader:
while not board.is_game_over():
print(board)
if board.turn == chess.WHITE:
user_move = input("Enter your move in UCI format (e.g., e2e4): ")
try:
move = chess.Move.from_uci(user_move)
if move in board.legal_moves:
board.push(move)
else:
print("Illegal move, try again.")
continue
except:
print("Invalid move format, try again.")
continue
else:
move = None
try:
entry = reader.get(board)
move = entry.move
except:
pass
if not move:
move, score = search(board, depth)
board.push(move)
print(f"Engine move: {move}")
node = node.add_variation(move)
print("Game over")
print(board.result())
# Save game to PGN file
with open("games.pgn", "a") as pgn_file:
print(game, file=pgn_file, end="\n\n")
# Uncomment the desired function to play as white or black
# play_as_white()
play_as_black()
forum3/viewtopic.php?f=2&t=81097&start=20#p939245
--
Srdja