text: from typing import Any, Tuple, List
  from environment.game_state import GameState
  import math
  from numba import jit
  import random
  from ai_controller.common.numerical import exp_normalise
  
  
  # @jit(nopython = True)
  def minimax(
    player_idx : int,
    state : GameState,
    depth : int,
    is_max : bool,
    output_tree_data : bool = False
  ) -> Tuple[float, float, Any]:
    """
    Perform minimax on a tree node
  
    :return: 3-Tuple `(score, likelihood, move)`
    """
    # TODO: alpha-beta pruning
  
    if state.is_terminal is True:
      assert state.score is not None
      if state.score[player_idx] == 0:
        return (0, 1, None)
      elif state.score[player_idx] == -1:
        return (-10000, 1, None)
      else:
        return (10000, 1, None)
  
    eval : float = state.heuristic_eval(player_idx)
  
    if depth == 0:
      return (eval, 1, None)
  
    moves = list(state.get_possible_moves())
    random.shuffle(moves)
    score_likelihood_move : List[Tuple[float, float, Any]] = list(map(
      lambda move : (
        *minimax(
          player_idx,
          state.clone().play_move(move),
          depth - 1,
          not is_max,
          output_tree_data=False
        )[0:2],
        move
      ), moves
    ))
  
    if is_max:
      exp_norm = exp_normalise([elem[0] for elem in score_likelihood_move])
    else:
      exp_norm = exp_normalise([-elem[0] for elem in score_likelihood_move])
    
    score_move_likelihood = [(*sd, e) for (sd, e) in zip(score_likelihood_move, exp_norm)]  
  
    if output_tree_data is True:
      data : str = ""
      for (score, _, move, likelihood) in score_move_likelihood:
        data += f"{score:.2f} {move} {likelihood:.2f}\n"
  
      with open(f"output/minimax/ply-{state.ply}.txt", "w") as f:
        f.write(data)
  
    if is_max:
      child = max(score_move_likelihood, key = lambda elem : elem[0])
    else:
      child = min(score_move_likelihood, key = lambda elem : elem[0])
  
    score, _, move, likelihood = child
    return (
      score,
      likelihood,
      move
    )
  