(game, win_score, maxdepth=50, tt=None, depth=0)
return best_value
Solves a game using a depth-first search: the game is explored until
    endgames are reached.

    The endgames are evaluated to see if there are victories or defeats.
    Then, a situation in which every move leads to a defeat is labelled
    as a (certain) defeat, and a situation in which one move leads to a
    (certain) defeat of the opponent is labelled as a (certain) victory.
    Situations are evaluated until the initial condition receives a label
    (victory or defeat). Draws are also possible.

    This algorithm can be faster but less informative than ``solve_with_iterative_deepening``,
    as it does not provide 'optimal' strategies (like shortest path to
    the victory). It returns simply 1, 0, or -1 to indicate certain
    victory, draw, or defeat of the first player.

    Parameters
    -----------

    game:
      An Game instance, initialized and ready to be played.

    win_score:
      Score above which a score means a win.

    maxdepth:
      Maximal recursion depth allowed.

    tt:
      An optional transposition table to speed up computations.


    depth:
      Index of the current depth (don't touch that).

    Returns
    --------

    result
      Either 1 (certain victory of the first player) or -1
      (certain defeat) or 0 (either draw, or the search was not
      deep enough)
Solves a game using a depth-first search: the game is explored until
    endgames are reached.
Solves a game using a depth - first search : the game is explored until endgames are reached .
def solve_with_depth_first_search(game, win_score, maxdepth=50, tt=None, depth=0):
    """
    Solves a game using a depth-first search: the game is explored until
    endgames are reached.

    The endgames are evaluated to see if there are victories or defeats.
    Then, a situation in which every move leads to a defeat is labelled
    as a (certain) defeat, and a situation in which one move leads to a
    (certain) defeat of the opponent is labelled as a (certain) victory.
    Situations are evaluated until the initial condition receives a label
    (victory or defeat). Draws are also possible.

    This algorithm can be faster but less informative than ``solve_with_iterative_deepening``,
    as it does not provide 'optimal' strategies (like shortest path to
    the victory). It returns simply 1, 0, or -1 to indicate certain
    victory, draw, or defeat of the first player.

    Parameters
    -----------

    game:
      An Game instance, initialized and ready to be played.

    win_score:
      Score above which a score means a win.

    maxdepth:
      Maximal recursion depth allowed.

    tt:
      An optional transposition table to speed up computations.


    depth:
      Index of the current depth (don't touch that).

    Returns
    --------

    result
      Either 1 (certain victory of the first player) or -1
      (certain defeat) or 0 (either draw, or the search was not
      deep enough)

    """

    # Is there a transposition table and is this game in it ?
    lookup = None if (tt is None) else tt.lookup(game)
    if lookup is not None:
        return lookup["value"]

    if depth == maxdepth:
        raise "Max recursion depth reached :("

    if game.is_over():
        score = game.scoring()
        value = 1 if (score >= win_score) else (-1 if -score >= win_score else 0)
        if tt is not None:
            tt.store(game=game, value=value, move=None)
        return value

    possible_moves = game.possible_moves()

    state = game
    unmake_move = hasattr(state, "unmake_move")

    best_value, best_move = -1, None

    for move in possible_moves:

        if not unmake_move:
            game = state.copy()  # re-initialize move

        game.make_move(move)
        game.switch_player()

        move_value = -solve_with_depth_first_search(
            game, win_score, maxdepth, tt, depth + 1
        )

        if unmake_move:
            game.switch_player()
            game.unmake_move(move)

        if move_value == 1:
            if tt is not None:
                tt.store(game=state, value=1, move=move)
            return move_value

        if move_value == 0 and best_value == -1:
            # Is forcing a draw possible ?
            best_value = 0
            best_move = move
    if tt is not None:
        tt.store(game=state, value=best_value, move=best_move)

    return best_value
def solve_with_depth_first_search ( game , win_score , maxdepth = 50 , tt = None , depth = 0 ) : # Is there a transposition table and is this game in it ? lookup = None if ( tt is None ) else tt . lookup ( game ) if lookup is not None : return lookup [ "value" ] if depth == maxdepth : raise "Max recursion depth reached :(" if game . is_over ( ) : score = game . scoring ( ) value = 1 if ( score >= win_score ) else ( - 1 if - score >= win_score else 0 ) if tt is not None : tt . store ( game = game , value = value , move = None ) return value possible_moves = game . possible_moves ( ) state = game unmake_move = hasattr ( state , "unmake_move" ) best_value , best_move = - 1 , None for move in possible_moves : if not unmake_move : game = state . copy ( ) # re-initialize move game . make_move ( move ) game . switch_player ( ) move_value = - solve_with_depth_first_search ( game , win_score , maxdepth , tt , depth + 1 ) if unmake_move : game . switch_player ( ) game . unmake_move ( move ) if move_value == 1 : if tt is not None : tt . store ( game = state , value = 1 , move = move ) return move_value if move_value == 0 and best_value == - 1 : # Is forcing a draw possible ? best_value = 0 best_move = move if tt is not None : tt . store ( game = state , value = best_value , move = best_move ) return best_value