chess endgame engine in Python doesn't work perfectly

114 views Asked by At

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.

0

There are 0 answers