I'm trying to create a code for perfectly optimal chess endgame. By that I mean that the loosing player tries to delay the checkmate as long as possible while the winning tries to checkmate the opponent as soon as possible. This code for chess endgame is my currently best one
import chess
def simplify_fen_string(fen):
parts = fen.split(' ')
simplified_fen = ' '.join(parts[:4]) # Zachováváme pouze informace o pozici
return simplified_fen
def evaluate_position(board):
#print(f"Position: {board.fen()}")
if board.is_checkmate():
### print(f"Position: {board.fen()}, return -1000")
return -1000 # Mat protihráči
elif board.is_stalemate() or board.is_insufficient_material() or board.can_claim_draw():
### print(f"Position: {board.fen()}, return 0")
return 0 # Remíza
else:
#print(f"Position: {board.fen()}, return None")
return None # Hra pokračuje
def create_AR_entry(result, children, last_move):
return {"result": result, "children": children, "last_move": last_move, "best_child": None}
def update_best_case(best_case):
if best_case == 0:
return best_case
if best_case > 0:
return best_case - 1
else:
return best_case + 1
def update_AR_for_mate_in_k(board, AR, simplified_initial_fen, max_k=1000):
evaluated_list = []
#print(f"")
for k in range(1, max_k + 1):
print(f"K = {k}")
changed = False
for _t in range(2): # Zajistíme, že pro každé k proběhne aktualizace dvakrát
print(f"_t = {_t}")
for fen in list(AR.keys()):
#print(f"Fen = {fen}, looking for {simplified_initial_fen}, same = {fen == simplified_initial_fen}")
board.set_fen(fen)
if AR[fen]['result'] is not None:
if fen == simplified_initial_fen:
print(f"Finally we found a mate! {AR[fen]['result']}")
return
continue # Pokud již máme hodnocení, přeskočíme
# Získáme výchozí hodnoty pro nejlepší a nejhorší scénář
best_case = float("-inf")
#worst_case = float("inf")
nones_present = False
best_child = None
for move in board.legal_moves:
#print(f"Move = {move}")
board.push(move)
next_fen = simplify_fen_string(board.fen())
#AR[fen]['children'].append(next_fen)
if next_fen not in AR:
AR[next_fen] = create_AR_entry(evaluate_position(board), None, move)
evaluated_list.append(next_fen)
if ((len(evaluated_list)) % 100000 == 0):
print(f"Evaluated: {len(evaluated_list)}")
board.pop()
#for child in AR[fen]['children']:
next_eval = AR[next_fen]['result']
if next_eval is not None:
if (-next_eval > best_case):
best_case = max(best_case, -next_eval)
best_child = next_fen
#worst_case = min(worst_case, -next_eval)
else:
nones_present = True
if nones_present:
if best_case > 0:
AR[fen]['result'] = update_best_case(best_case)
AR[fen]['best_child'] = best_child
changed = True
else:
# Aktualizace hodnocení podle nejlepšího a nejhoršího scénáře
#if worst_case == -1000:
# Pokud všechny tahy vedou k matu, hráč na tahu může být matován v k tazích
# AR[fen] = -1000 + k
# changed = True
#elif best_case <= 0:
# Pokud nejlepší scénář není lepší než remíza, znamená to remízu nebo prohru
# AR[fen] = max(best_case, 0) # Zabráníme nastavení hodnoty méně než 0, pokud je remíza možná
# changed = True
#elif best_case == 1000:
# Pokud existuje alespoň jeden tah, který vede k matu protihráče, hráč na tahu může vynutit mat v k tazích
# AR[fen] = 1000 - k
# changed = True
AR[fen]['result'] = update_best_case(best_case)
AR[fen]['best_child'] = best_child
changed = True
### print(f"Position = {fen}, results = {best_case} {nones_present} => {AR[fen]['result']}")
if (fen == "8/8/3R4/8/8/5K2/8/4k3 b - -" or fen == "8/8/3R4/8/8/5K2/8/5k2 w - -"):
print("^^^^^^^^")
# remove here
#break
#if not changed:
#break # Pokud nedošlo k žádné změně, ukončíme smyčku
#if not changed:
#break # Ukončíme hlavní smyčku, pokud nedošlo ke změně v poslední iteraci
def print_draw_positions(AR):
"""
Vytiskne všechny remízové pozice (hodnota 0) zaznamenané v slovníku AR.
"""
print("Remízové pozice:")
for fen, value in AR.items():
if True or (value > 990 and value < 1000):
print(f"FEN>: {fen}, Hodnota: {value}","\n",chess.Board(fen),"<\n")
def find_path_to_end(AR, fen):
if AR[fen]['result'] is None:
print(f"Unfortunately, there is no path that is known to be the best")
fen_i = fen
print(chess.Board(fen_i),"\n<")
path = fen
while AR[fen_i]['best_child'] is not None:
fen_i = AR[fen_i]['best_child']
print(chess.Board(fen_i),"\n<")
path = path + ", " + fen_i
print(f"Path is: {path}")
def main():
initial_fen = "1k6/5P2/2K5/8/8/8/8/8 w - - 0 1"
initial_fen_original = "8/8/8/8/3Q4/5K2/8/4k3 w - - 0 1"
initial_fen_mate_in_one_aka_one_ply = "3r1k2/5r1p/5Q1K/2p3p1/1p4P1/8/8/8 w - - 2 56"
initial_fen_mate_in_two_aka_three_plies = "r5k1/2r3p1/pb6/1p2P1N1/3PbB1P/3pP3/PP1K1P2/3R2R1 b - - 4 28"
initial_fen_mated_in_two_plies = "r5k1/2r3p1/p7/bp2P1N1/3PbB1P/3pP3/PP1K1P2/3R2R1 w - - 5 29"
mate_in_two_aka_three_plies_simple = "8/8/8/8/3R4/5K2/8/4k3 w - - 0 1"
mated_in_one_aka_two_plies_simple = "8/8/3R4/8/8/5K2/8/4k3 b - - 1 1"
mate_in_one_aka_one_ply_simple = "8/8/3R4/8/8/5K2/8/5k2 w - - 2 2"
initial_fen = mate_in_two_aka_three_plies_simple
initial_fen = "1k6/5P2/2K5/8/8/8/8/8 w - - 0 1"
initial_fen = "1k6/8/2K5/8/8/8/8/8 w - - 0 1"
initial_fen = "8/8/8/8/8/7N/1k5K/6B1 w - - 0 1"
initial_fen = "7K/8/k1P5/7p/8/8/8/8 w - - 0 1"
simplified_fen = simplify_fen_string(initial_fen)
board = chess.Board(initial_fen)
AR = {simplified_fen: {"result": None, "last_move": None, "children": None, "best_child": None}} # Inicializace AR s počáteční pozicí
update_AR_for_mate_in_k(board, AR, simplified_fen, max_k=58) # Aktualizace AR
#print_draw_positions(AR)
print(f"AR for initial fen is = {AR[simplified_fen]}")
find_path_to_end(AR, simplified_fen)
main()
However,for initial fen = "8/8/8/4k3/2K4R/8/8/8 w - - 0 1" it doesn't give the optimal result like this one: https://lichess.org/analysis/8/8/8/4k3/2K4R/8/8/8_w_-_-_0_1?color=white
Rather, it gives 27 plies like this while lichess.com link above gives 1000-977==23 plies which I suppose is the correct number. Finding the bug will be highly appreciated.