I have the following backtracking solution to chess endgames with D=3 figures on the board.
I should have obtained white king to move on Kc2, but I'm getting Qh3h8.
I have printed the print(board.legal_moves) and I'm obtaining
<LegalMoveGenerator at 0x1d4739daed0 (Kb3, Ka3)> which doesn't print Qh3h8 at all.
I gives at first move Kb3 and Ka3 while there is black k which can move to kb3 and ka3,
not white Kb3 nor Ka3.
It is white (K) turn to move, but only black (k) can move to ka3 and kb3. The correct move is Kc2 anyway
There must be some bug in the code, can someone kindly help to me ?
I cannot find it by myself.
The code:
import chess
import time
import threading
# Globální proměnná pro sledování počtu prohledaných pozic
positions_count = 0
def update_positions_count(last_time_printed):
global positions_count
while not board.is_game_over():
if time.time() - last_time_printed > 1:
print(f"\rProhledaných pozic: {positions_count}", end='')
last_time_printed = time.time()
def evaluate_board(board, depth):
global positions_count
positions_count += 1
if board.is_checkmate():
return 10000 - depth
if board.is_stalemate() or board.can_claim_draw():
return 0
return None
# ... negamax, find_best_move ...
# Negamax algoritmus
def negamax(board, depth, alpha, beta, color):
evaluated = evaluate_board(board, depth)
if evaluated is not None:
return color * evaluated
if depth == 0 or board.is_game_over():
return 0
max_eval = float('-inf')
print(board.legal_moves)
for move in board.legal_moves:
board.push(move)
eval = -negamax(board, depth - 1, -beta, -alpha, -color)
board.pop()
max_eval = max(max_eval, eval)
alpha = max(alpha, eval)
if alpha >= beta:
break
return max_eval
# def find_best_move(board, depth):
# best_move = None
# best_value = float('-inf')
# alpha = float('-inf')
# beta = float('inf')
# color = 1 if board.turn else -1
# for move in board.legal_moves:
# print(move)
# if str(move) == "Kc2":
# print("HERE")
# board.push(move)
# board_value = -negamax(board, depth - 1, -beta, -alpha, -color)
# board.pop()
# if board_value > best_value:
# best_value = board_value
# best_move = move
# return best_move
def find_best_move(board, depth):
best_move = None
best_value = float('-inf')
alpha = float('-inf')
beta = float('inf')
color = 1 if board.turn else -1
print(f"Na tahu je: {'bílý' if board.turn else 'černý'}")
for move in board.legal_moves:
print(move.uci()) # Vypíše tahy ve formátu UCI
if move.uci() == "c1c2": # Příklad pro tah bílého krále
print("HERE")
board.push(move)
board_value = -negamax(board, depth - 1, -beta, -alpha, -color)
board.pop()
if board_value > best_value:
best_value = board_value
best_move = move
return best_move
# Hlavní část kódu
start_position = "8/8/8/8/8/7Q/k7/2K5 w - - 0 1"
board = chess.Board(start_position)
depth = 7 # Můžete zvážit snížení hloubky pro rychlejší výsledky
last_time_printed = time.time()
positions_count_thread = threading.Thread(target=update_positions_count, args=(last_time_printed,), daemon=True)
positions_count_thread.start()
print(board)
print()
while not board.is_game_over():
best_move = find_best_move(board, depth)
if best_move is not None:
board.push(best_move)
print("\n", board) # Vytiskne šachovnici po provedení nejlepšího tahu
else:
print("Žádný další legální tah není možný.")
break
print("\nKonec hry")
THE OUTPUT
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . Q
k . . . . . . .
. . K . . . . .
Na tahu je: bílý
h3h8
<LegalMoveGenerator at 0x1d4739daed0 (Kb3, Ka3)>
<LegalMoveGenerator at 0x1d4739da050 (Qg8+, Qf8, Qe8, Qd8, Qc8, Qb8+, Qa8, Qh7, Qg7, Qh6, Qf6, Qh5, Qe5, Qh4, Qd4, Qh3+, Qc3+, Qh2, Qb2+, Qh1, Qa1, Kd2, Kd1, Kb1)>
<LegalMoveGenerator at 0x1d4739da690 (Kb4, Ka4, Kc3, Ka3)>
<LegalMoveGenerator at 0x1d4739dac50 (Qh8, Qf8+, Qe8, Qd8, Qc8, Qb8+, Qa8, Qh7, Qg7, Qf7, Qg6, Qe6, Qg5, Qd5, Qg4+, Qc4+, Qg3, Qb3+, Qg2, Qa2, Qg1, Kd2, Kc2, Kb2, Kd1, Kb1)>
<LegalMoveGenerator at 0x1d4739dbcd0 (Kc5, Kb5, Ka5, Kc4, Ka4, Kb3, Ka3)>
<LegalMoveGenerator at 0x1d4739dae50 (Qg8, Qf8+, Qe8, Qd8, Qc8+, Qb8, Qa8, Qh7, Qg7, Qh6, Qf6, Qh5+, Qe5+, Qh4, Qd4+, Qh3, Qc3+, Qh2, Qb2, Qh1, Qa1, Kd2, Kc2, Kb2, Kd1, Kb1)>
<LegalMoveGenerator at 0x1d4739da890 (Qg8, Qf8, Qe8+, Qd8, Qc8, Qb8+, Qa8, Qh7, Qg7, Qh6, Qf6, Qh5+, Qe5+, Qh4, Qd4, Qh3, Qc3, Qh2, Qb2+, Qh1, Qa1, Kd2, Kc2, Kb2, Kd1, Kb1)>
<LegalMoveGenerator at 0x1d4739daf90 (Qg8, Qf8, Qe8, Qd8+, Qc8, Qb8, Qa8+, Qh7, Qg7, Qh6, Qf6, Qh5+, Qe5+, Qh4, Qd4, Qh3, Qc3+, Qh2, Q
EDIT: modification of the code and new oputputs of it, but I'm still note getting an optimal result
Why ka6 moves to ka7 instead of ph4 ?
Isn't ph4 more optimal for black?
The oputput is here and the code what has achieved below:
Output:
Počáteční pozice:
. . . . . . . K
. . . . . . . .
k . P . . . . .
. . . . . . . p
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
Prohledaných pozic: 43661880 Čas: 0d 02h 19m 22sPo tahu:
. . . . . . . .
. . . . . . K .
k . P . . . . .
. . . . . . . p
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
Prohledaných pozic: 165219395 Čas: 0d 08h 41m 57sPo tahu:
. . . . . . . .
k . . . . . K .
. . P . . . . .
. . . . . . . p
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
Prohledaných pozic: 275368567 Čas: 0d 14h 35m 14s
Code:
import chess
import time
import threading
positions_count = 0
stop_thread = False
def count_pieces(board):
"""Spočítá a vrátí počet figurek na šachovnici."""
count = 0
for square in chess.SQUARES:
if board.piece_at(square):
count += 1
return count
def update_positions_count_and_time():
global positions_count, start_time
while not stop_thread:
elapsed_time = time.time() - start_time
formatted_time = format_time(elapsed_time)
print(f"\rProhledaných pozic: {positions_count} Čas: {formatted_time}", end='')
time.sleep(1)
def format_time(seconds):
"""Převede sekundy na formát %dd %hh %mm %ss."""
days, seconds = divmod(seconds, 86400)
hours, seconds = divmod(seconds, 3600)
minutes, seconds = divmod(seconds, 60)
return f"{int(days)}d {int(hours):02d}h {int(minutes):02d}m {int(seconds):02d}s"
def evaluate_board(board, depth):
global positions_count
positions_count += 1
if board.is_checkmate():
return 10000 - depth
if board.is_stalemate() or board.can_claim_draw():
return 0
return None
def evaluate_material(board):
"""Vrátí celkovou hodnotu materiálu na šachovnici."""
piece_values = {chess.PAWN: 1, chess.KNIGHT: 3, chess.BISHOP: 3, chess.ROOK: 5, chess.QUEEN: 9, chess.KING: 0}
total_value = 0
for square in chess.SQUARES:
piece = board.piece_at(square)
if piece:
value = piece_values[piece.piece_type]
if piece.color == chess.WHITE:
total_value += value
else:
total_value -= value
return total_value
def negamax(board, depth, alpha, beta, color, move_history):
evaluated = evaluate_board(board, depth)
if evaluated is not None:
return color * evaluated
if depth == 0 or board.is_game_over():
return color * evaluate_material(board)
max_eval = float('-inf')
for move in board.legal_moves:
board.push(move)
move_history.append(board.fen())
eval = -negamax(board, depth - 1, -beta, -alpha, -color, move_history)
board.pop()
move_history.pop()
if eval > max_eval:
max_eval = eval
alpha = max(alpha, eval)
if alpha >= beta:
break
return max_eval
def find_best_move(board, depth):
best_move = None
best_value = float('-inf')
alpha = float('-inf')
beta = float('inf')
color = 1 if board.turn else -1
move_history = [board.fen()]
for move in board.legal_moves:
board.push(move)
move_history.append(board.fen())
board_value = -negamax(board, depth - 1, -beta, -alpha, -color, move_history)
board.pop()
move_history.pop()
if board_value > best_value:
best_value = board_value
best_move = move
return best_move, move_history
# ... (Předchozí definice funkcí zůstávají stejné) ...
def find_and_execute_best_move(board, depth):
"""Najde a provede nejlepší tah na šachovnici."""
best_move, move_history = find_best_move(board, depth)
if best_move:
board.push(best_move)
print("Po tahu:")
print(board)
print()
return best_move
start_position = "7K/8/k1P5/7p/8/8/8/8 w - - 0 1"
board = chess.Board(start_position)
depth = 13
start_time = time.time()
print("Počáteční pozice:")
print(board)
print()
positions_count_thread = threading.Thread(target=update_positions_count_and_time, daemon=True)
positions_count_thread.start()
while not board.is_game_over():
find_and_execute_best_move(board, depth)
if board.is_checkmate() or board.is_stalemate() or board.can_claim_draw():
break
stop_thread = True
print("Konec hry")
if board.is_checkmate():
print("Mat!")
elif board.is_stalemate() or board.can_claim_draw():
print("Remíza!")
stop_thread = True
if depth == 0 or board.is_game_over():will pollute your alpha/beta scores with nonsense. You need to be checking for game termination inside your move loop, before assigning bogus scores toalpha/max_eval.