"""
HW03 - Adversarial search
"""

def get_step_flag():
  step = [2,2]
  
  while (len(step) != 1):
    step = input('Enter 0 to display only the final results - 1 for single-stepping option: ').split()
    step = [int(x) for x in step]

  if (step[0]):
    print()
    print('>> get_step_flag function takes input to decide Single-stepping option')
    print()
  return step[0]

def get_algorithm(singleStepFlag):
  if (singleStepFlag) :
    print()
    print('>> Program function takes input to decide if Straight Minimaxing or w/ Alpha-beta Pruning')
    print(">> With alphabeta , we don't explicitly use the -inf,inf intial values, but that approach is implicitly implemented" )
    print()

  algorithm = []
  
  while (len(algorithm) != 1 or (algorithm[0] != 0 and algorithm[0] != 1)):
    algorithm = input('Enter 0 for Straight-Minimaxing Program - 1 for Minimax with Alphabeta Pruning: ').split()
    algorithm = [int(x) for x in algorithm]

  return algorithm[0]
   
def choose_input(singleStepFlag):
  if (singleStepFlag) :
    print()
    print('>> choose_input function takes input to Solve for the Assignment Trees OR user-inputted')
    print()

  chooseInput = [2, 2]
  
  while (len(chooseInput) != 1 or (chooseInput[0] != 0 and chooseInput[0] != 1)):
    chooseInput = input('Enter 0 for solving the Assignment-Given tree(s) - 1 for Keyboard Input: ').split()
    chooseInput = [int(x) for x in chooseInput]
  
  return chooseInput[0]

def minimax(leaves, singleStepFlag):
  L = min(leaves[0],leaves[1],leaves[2])
  M = min(leaves[3],leaves[4],leaves[5])
  R = min(leaves[6],leaves[7],leaves[8])
  if (singleStepFlag) :
    print("\n >>> 1a )  Take the sequence of values for leaves, and for L , M, R take min() of the the patches of 3 nodes left, middle and right , according to the tree structure" )

  Minimizer = [L, M, R]
  sortedIndex = sorted(range(len(Minimizer)), key=lambda k: Minimizer[k], reverse = True)
  Minimizer_dict = {0: "L"  ,1: "M"  , 2: "R" }
  if (singleStepFlag) :
    print("\n >>> 2a )  Sort the Minimizer level Nodes' values from Highest to Lowest value" )

  max = Minimizer_dict[sortedIndex[0]]
  if (singleStepFlag) :
    print("\n >>>  3a )   After sorting, the Final Output as required by assignment instructions is the L or M or R depedning on the sorting" )
    
  print("The move of the max player is :", max )

def alphabeta(leaves, singleStepFlag):

  if (singleStepFlag) :
    print("\n >>> 1b ) Take the sequence of values corrosponding to Tree leaves (terminal Nodes)  " )
    
  Minimizer_dict = {0: "L"  ,1: "M"  , 2: "R" }
  A = leaves[0] 
  B = leaves[1] 
  C = leaves[2]
  D = leaves[3]
  E = leaves[4]
  F = leaves[5] 
  G = leaves[6] 
  H = leaves[7]
  I = leaves[8]  

  pruned = []  
  if (singleStepFlag):
    print("\n >>> 2b ) Take min() of the the first 3 nodes, L = min (the 3-patch on the left), and initially assign D value to M node (as temporary ceiling) and G value to R node (as temporary ceiling)" )
  L = min(A,B,C)
  M_ceil = D
  R_ceil = G

  if L >= M_ceil:
    if (singleStepFlag) :
      print("\n >>> 3b )  Because  L >= M_ceil, append node E and F to pruned list -- in alphab. order " )
    pruned.append('E')
    pruned.append('F')
    
  elif L < M_ceil:
    if M_ceil > E:
      M_ceil = E
    if (singleStepFlag) :
        print("\n >>> 3b )  In case M_ceil is bigger that E node value, replace it on the minimizer level, M_ceil = E  " )
    
    if L >= M_ceil:
      if (singleStepFlag) :
         print("\n >>> 4b )  after the new value for M_ceil, we check to see it is indeed smaller than L, and thus prune F " )
      pruned.append('F')
    
    elif  M_ceil > F:
      M_ceil = F  
      if (singleStepFlag) :  
        print("\n >>> 4b )  No pruning Happens at M patch, i.e. middle  " )
    
    
  if L >= R_ceil or M_ceil >= R_ceil : #Now third , R node 
    if (singleStepFlag) :
      print("\n >>> 5b )  Because  L >= R_ceil, or M_ceil >= R_ceil, Prune H , I nodes -- in alphab. order " )
    pruned.append('H')
    pruned.append('I')
  elif H < R_ceil:
    R_ceil = H
    if (singleStepFlag) :
      print("\n >>> 5b )  In case R_ceil is bigger that H node value, replace it on the minimizer level, R_ceil = H  " )
    
  elif L >= R_ceil or M_ceil >= R_ceil :
    if (singleStepFlag) :  
      print("\n >>> 6b )  after the new value for R_ceil, we check to see it is indeed smaller than L or M_ceil, and thus prune I  " )
    pruned.append('I')
  elif I < R_ceil:
    R_ceil = I
    if (singleStepFlag) :  
      print("\n >>> 4b )  No pruning Happens at R patch, i.e. right node  " )    
    
  MaxinmizerLevel = [L ,M_ceil, R_ceil]
  sortedIndex = sorted(range(len(MaxinmizerLevel)), key=lambda k: MaxinmizerLevel[k], reverse = True)
  if (singleStepFlag) :  
    print("\n >>> 7b ) Sort minimizer level nodes, and choose max  " ) 
  max = Minimizer_dict[sortedIndex[0]]

  print("The move of the max player is :", max )
  print("The Pruned Nodes are : ", end='')
  [print(x, end=' ') for x in pruned]
      
def main():
  # get the nine input values
  leaves = []
  singleStepFlag = get_step_flag()
  
  algorithm = get_algorithm(singleStepFlag)
  get_input = choose_input(singleStepFlag)  


  if (get_input) == 0 and (not algorithm):
      print("\n- Minimax For tree with values of [5 ,3 , 1 , 2, 5 , 4 ,1, 3 , 3] corrosponding to A, B, C, D, E, F, G, H, I in respective order.")
      minimax([5 ,3 , 1 , 2, 5 , 4 ,1, 3 , 3], singleStepFlag)

  if (not get_input) and (algorithm):
    print('\n-------------------------------------------------------------------------------------------------')
    print("\n- Minimax with Alphabeta Pruning For tree with values of [5 ,3 , 1 , 2, 5 , 4 ,1, 3 , 3] corrosponding to A, B, C, D, E, F, G, H, I in respective order.")
    alphabeta([5 ,3 , 1 , 2, 5 , 4 ,1, 3 , 3], singleStepFlag)

    print('\n\n-------------------------------------------------------------------------------------------------')
    print("\n- Minimax with Alphabeta Pruning For tree with values of [5, 2, 2, 5, 1, 3, 2, 4, 2] corrosponding to A, B, C, D, E, F, G, H, I in respective order.")
    alphabeta([5, 2, 2, 5, 1, 3, 2, 4, 2], singleStepFlag)

    print('\n\n-------------------------------------------------------------------------------------------------')
    print("\n- Minimax with Alphabeta Pruning For tree with values of [1 ,3 ,4 ,1 ,4 ,1 ,3 ,5 ,3] corrosponding to A, B, C, D, E, F, G, H, I in respective order.")
    alphabeta([1 ,3 ,4 ,1 ,4 ,1 ,3 ,5 ,3], singleStepFlag)

  if get_input:  
      while (len(leaves)!= 9):
        leaves = input('Enter Integer Values For A, B, C, D, E, F, G, H, I, separated by blank spaces: \n').split()
        leaves = [int(x) for x in leaves]
      
      if not algorithm:
        minimax(leaves, singleStepFlag)
      elif algorithm:
        alphabeta(leaves, singleStepFlag)

if __name__ == '__main__':
  main()

f54ab5decf74c416d3ca02cee574bad665035d91
<mask token>


def get_step_flag():
    step = [2, 2]
    while len(step) != 1:
        step = input(
            'Enter 0 to display only the final results - 1 for single-stepping option: '
            ).split()
        step = [int(x) for x in step]
    if step[0]:
        print()
        print(
            '>> get_step_flag function takes input to decide Single-stepping option'
            )
        print()
    return step[0]


<mask token>


def main():
    leaves = []
    singleStepFlag = get_step_flag()
    algorithm = get_algorithm(singleStepFlag)
    get_input = choose_input(singleStepFlag)
    if get_input == 0 and not algorithm:
        print(
            """
- Minimax For tree with values of [5 ,3 , 1 , 2, 5 , 4 ,1, 3 , 3] corrosponding to A, B, C, D, E, F, G, H, I in respective order."""
            )
        minimax([5, 3, 1, 2, 5, 4, 1, 3, 3], singleStepFlag)
    if not get_input and algorithm:
        print(
            """
-------------------------------------------------------------------------------------------------"""
            )
        print(
            """
- Minimax with Alphabeta Pruning For tree with values of [5 ,3 , 1 , 2, 5 , 4 ,1, 3 , 3] corrosponding to A, B, C, D, E, F, G, H, I in respective order."""
            )
        alphabeta([5, 3, 1, 2, 5, 4, 1, 3, 3], singleStepFlag)
        print(
            """

-------------------------------------------------------------------------------------------------"""
            )
        print(
            """
- Minimax with Alphabeta Pruning For tree with values of [5, 2, 2, 5, 1, 3, 2, 4, 2] corrosponding to A, B, C, D, E, F, G, H, I in respective order."""
            )
        alphabeta([5, 2, 2, 5, 1, 3, 2, 4, 2], singleStepFlag)
        print(
            """

-------------------------------------------------------------------------------------------------"""
            )
        print(
            """
- Minimax with Alphabeta Pruning For tree with values of [1 ,3 ,4 ,1 ,4 ,1 ,3 ,5 ,3] corrosponding to A, B, C, D, E, F, G, H, I in respective order."""
            )
        alphabeta([1, 3, 4, 1, 4, 1, 3, 5, 3], singleStepFlag)
    if get_input:
        while len(leaves) != 9:
            leaves = input(
                """Enter Integer Values For A, B, C, D, E, F, G, H, I, separated by blank spaces: 
"""
                ).split()
            leaves = [int(x) for x in leaves]
        if not algorithm:
            minimax(leaves, singleStepFlag)
        elif algorithm:
            alphabeta(leaves, singleStepFlag)


<mask token>

<mask token>


def get_step_flag():
    step = [2, 2]
    while len(step) != 1:
        step = input(
            'Enter 0 to display only the final results - 1 for single-stepping option: '
            ).split()
        step = [int(x) for x in step]
    if step[0]:
        print()
        print(
            '>> get_step_flag function takes input to decide Single-stepping option'
            )
        print()
    return step[0]


def get_algorithm(singleStepFlag):
    if singleStepFlag:
        print()
        print(
            '>> Program function takes input to decide if Straight Minimaxing or w/ Alpha-beta Pruning'
            )
        print(
            ">> With alphabeta , we don't explicitly use the -inf,inf intial values, but that approach is implicitly implemented"
            )
        print()
    algorithm = []
    while len(algorithm) != 1 or algorithm[0] != 0 and algorithm[0] != 1:
        algorithm = input(
            'Enter 0 for Straight-Minimaxing Program - 1 for Minimax with Alphabeta Pruning: '
            ).split()
        algorithm = [int(x) for x in algorithm]
    return algorithm[0]


def choose_input(singleStepFlag):
    if singleStepFlag:
        print()
        print(
            '>> choose_input function takes input to Solve for the Assignment Trees OR user-inputted'
            )
        print()
    chooseInput = [2, 2]
    while len(chooseInput) != 1 or chooseInput[0] != 0 and chooseInput[0] != 1:
        chooseInput = input(
            'Enter 0 for solving the Assignment-Given tree(s) - 1 for Keyboard Input: '
            ).split()
        chooseInput = [int(x) for x in chooseInput]
    return chooseInput[0]


def minimax(leaves, singleStepFlag):
    L = min(leaves[0], leaves[1], leaves[2])
    M = min(leaves[3], leaves[4], leaves[5])
    R = min(leaves[6], leaves[7], leaves[8])
    if singleStepFlag:
        print(
            """
 >>> 1a )  Take the sequence of values for leaves, and for L , M, R take min() of the the patches of 3 nodes left, middle and right , according to the tree structure"""
            )
    Minimizer = [L, M, R]
    sortedIndex = sorted(range(len(Minimizer)), key=lambda k: Minimizer[k],
        reverse=True)
    Minimizer_dict = {(0): 'L', (1): 'M', (2): 'R'}
    if singleStepFlag:
        print(
            "\n >>> 2a )  Sort the Minimizer level Nodes' values from Highest to Lowest value"
            )
    max = Minimizer_dict[sortedIndex[0]]
    if singleStepFlag:
        print(
            """
 >>>  3a )   After sorting, the Final Output as required by assignment instructions is the L or M or R depedning on the sorting"""
            )
    print('The move of the max player is :', max)


<mask token>


def main():
    leaves = []
    singleStepFlag = get_step_flag()
    algorithm = get_algorithm(singleStepFlag)
    get_input = choose_input(singleStepFlag)
    if get_input == 0 and not algorithm:
        print(
            """
- Minimax For tree with values of [5 ,3 , 1 , 2, 5 , 4 ,1, 3 , 3] corrosponding to A, B, C, D, E, F, G, H, I in respective order."""
            )
        minimax([5, 3, 1, 2, 5, 4, 1, 3, 3], singleStepFlag)
    if not get_input and algorithm:
        print(
            """
-------------------------------------------------------------------------------------------------"""
            )
        print(
            """
- Minimax with Alphabeta Pruning For tree with values of [5 ,3 , 1 , 2, 5 , 4 ,1, 3 , 3] corrosponding to A, B, C, D, E, F, G, H, I in respective order."""
            )
        alphabeta([5, 3, 1, 2, 5, 4, 1, 3, 3], singleStepFlag)
        print(
            """

-------------------------------------------------------------------------------------------------"""
            )
        print(
            """
- Minimax with Alphabeta Pruning For tree with values of [5, 2, 2, 5, 1, 3, 2, 4, 2] corrosponding to A, B, C, D, E, F, G, H, I in respective order."""
            )
        alphabeta([5, 2, 2, 5, 1, 3, 2, 4, 2], singleStepFlag)
        print(
            """

-------------------------------------------------------------------------------------------------"""
            )
        print(
            """
- Minimax with Alphabeta Pruning For tree with values of [1 ,3 ,4 ,1 ,4 ,1 ,3 ,5 ,3] corrosponding to A, B, C, D, E, F, G, H, I in respective order."""
            )
        alphabeta([1, 3, 4, 1, 4, 1, 3, 5, 3], singleStepFlag)
    if get_input:
        while len(leaves) != 9:
            leaves = input(
                """Enter Integer Values For A, B, C, D, E, F, G, H, I, separated by blank spaces: 
"""
                ).split()
            leaves = [int(x) for x in leaves]
        if not algorithm:
            minimax(leaves, singleStepFlag)
        elif algorithm:
            alphabeta(leaves, singleStepFlag)


<mask token>

<mask token>


def get_step_flag():
    step = [2, 2]
    while len(step) != 1:
        step = input(
            'Enter 0 to display only the final results - 1 for single-stepping option: '
            ).split()
        step = [int(x) for x in step]
    if step[0]:
        print()
        print(
            '>> get_step_flag function takes input to decide Single-stepping option'
            )
        print()
    return step[0]


def get_algorithm(singleStepFlag):
    if singleStepFlag:
        print()
        print(
            '>> Program function takes input to decide if Straight Minimaxing or w/ Alpha-beta Pruning'
            )
        print(
            ">> With alphabeta , we don't explicitly use the -inf,inf intial values, but that approach is implicitly implemented"
            )
        print()
    algorithm = []
    while len(algorithm) != 1 or algorithm[0] != 0 and algorithm[0] != 1:
        algorithm = input(
            'Enter 0 for Straight-Minimaxing Program - 1 for Minimax with Alphabeta Pruning: '
            ).split()
        algorithm = [int(x) for x in algorithm]
    return algorithm[0]


def choose_input(singleStepFlag):
    if singleStepFlag:
        print()
        print(
            '>> choose_input function takes input to Solve for the Assignment Trees OR user-inputted'
            )
        print()
    chooseInput = [2, 2]
    while len(chooseInput) != 1 or chooseInput[0] != 0 and chooseInput[0] != 1:
        chooseInput = input(
            'Enter 0 for solving the Assignment-Given tree(s) - 1 for Keyboard Input: '
            ).split()
        chooseInput = [int(x) for x in chooseInput]
    return chooseInput[0]


def minimax(leaves, singleStepFlag):
    L = min(leaves[0], leaves[1], leaves[2])
    M = min(leaves[3], leaves[4], leaves[5])
    R = min(leaves[6], leaves[7], leaves[8])
    if singleStepFlag:
        print(
            """
 >>> 1a )  Take the sequence of values for leaves, and for L , M, R take min() of the the patches of 3 nodes left, middle and right , according to the tree structure"""
            )
    Minimizer = [L, M, R]
    sortedIndex = sorted(range(len(Minimizer)), key=lambda k: Minimizer[k],
        reverse=True)
    Minimizer_dict = {(0): 'L', (1): 'M', (2): 'R'}
    if singleStepFlag:
        print(
            "\n >>> 2a )  Sort the Minimizer level Nodes' values from Highest to Lowest value"
            )
    max = Minimizer_dict[sortedIndex[0]]
    if singleStepFlag:
        print(
            """
 >>>  3a )   After sorting, the Final Output as required by assignment instructions is the L or M or R depedning on the sorting"""
            )
    print('The move of the max player is :', max)


def alphabeta(leaves, singleStepFlag):
    if singleStepFlag:
        print(
            """
 >>> 1b ) Take the sequence of values corrosponding to Tree leaves (terminal Nodes)  """
            )
    Minimizer_dict = {(0): 'L', (1): 'M', (2): 'R'}
    A = leaves[0]
    B = leaves[1]
    C = leaves[2]
    D = leaves[3]
    E = leaves[4]
    F = leaves[5]
    G = leaves[6]
    H = leaves[7]
    I = leaves[8]
    pruned = []
    if singleStepFlag:
        print(
            """
 >>> 2b ) Take min() of the the first 3 nodes, L = min (the 3-patch on the left), and initially assign D value to M node (as temporary ceiling) and G value to R node (as temporary ceiling)"""
            )
    L = min(A, B, C)
    M_ceil = D
    R_ceil = G
    if L >= M_ceil:
        if singleStepFlag:
            print(
                """
 >>> 3b )  Because  L >= M_ceil, append node E and F to pruned list -- in alphab. order """
                )
        pruned.append('E')
        pruned.append('F')
    elif L < M_ceil:
        if M_ceil > E:
            M_ceil = E
        if singleStepFlag:
            print(
                """
 >>> 3b )  In case M_ceil is bigger that E node value, replace it on the minimizer level, M_ceil = E  """
                )
        if L >= M_ceil:
            if singleStepFlag:
                print(
                    """
 >>> 4b )  after the new value for M_ceil, we check to see it is indeed smaller than L, and thus prune F """
                    )
            pruned.append('F')
        elif M_ceil > F:
            M_ceil = F
            if singleStepFlag:
                print(
                    '\n >>> 4b )  No pruning Happens at M patch, i.e. middle  '
                    )
    if L >= R_ceil or M_ceil >= R_ceil:
        if singleStepFlag:
            print(
                """
 >>> 5b )  Because  L >= R_ceil, or M_ceil >= R_ceil, Prune H , I nodes -- in alphab. order """
                )
        pruned.append('H')
        pruned.append('I')
    elif H < R_ceil:
        R_ceil = H
        if singleStepFlag:
            print(
                """
 >>> 5b )  In case R_ceil is bigger that H node value, replace it on the minimizer level, R_ceil = H  """
                )
    elif L >= R_ceil or M_ceil >= R_ceil:
        if singleStepFlag:
            print(
                """
 >>> 6b )  after the new value for R_ceil, we check to see it is indeed smaller than L or M_ceil, and thus prune I  """
                )
        pruned.append('I')
    elif I < R_ceil:
        R_ceil = I
        if singleStepFlag:
            print(
                '\n >>> 4b )  No pruning Happens at R patch, i.e. right node  '
                )
    MaxinmizerLevel = [L, M_ceil, R_ceil]
    sortedIndex = sorted(range(len(MaxinmizerLevel)), key=lambda k:
        MaxinmizerLevel[k], reverse=True)
    if singleStepFlag:
        print('\n >>> 7b ) Sort minimizer level nodes, and choose max  ')
    max = Minimizer_dict[sortedIndex[0]]
    print('The move of the max player is :', max)
    print('The Pruned Nodes are : ', end='')
    [print(x, end=' ') for x in pruned]


def main():
    leaves = []
    singleStepFlag = get_step_flag()
    algorithm = get_algorithm(singleStepFlag)
    get_input = choose_input(singleStepFlag)
    if get_input == 0 and not algorithm:
        print(
            """
- Minimax For tree with values of [5 ,3 , 1 , 2, 5 , 4 ,1, 3 , 3] corrosponding to A, B, C, D, E, F, G, H, I in respective order."""
            )
        minimax([5, 3, 1, 2, 5, 4, 1, 3, 3], singleStepFlag)
    if not get_input and algorithm:
        print(
            """
-------------------------------------------------------------------------------------------------"""
            )
        print(
            """
- Minimax with Alphabeta Pruning For tree with values of [5 ,3 , 1 , 2, 5 , 4 ,1, 3 , 3] corrosponding to A, B, C, D, E, F, G, H, I in respective order."""
            )
        alphabeta([5, 3, 1, 2, 5, 4, 1, 3, 3], singleStepFlag)
        print(
            """

-------------------------------------------------------------------------------------------------"""
            )
        print(
            """
- Minimax with Alphabeta Pruning For tree with values of [5, 2, 2, 5, 1, 3, 2, 4, 2] corrosponding to A, B, C, D, E, F, G, H, I in respective order."""
            )
        alphabeta([5, 2, 2, 5, 1, 3, 2, 4, 2], singleStepFlag)
        print(
            """

-------------------------------------------------------------------------------------------------"""
            )
        print(
            """
- Minimax with Alphabeta Pruning For tree with values of [1 ,3 ,4 ,1 ,4 ,1 ,3 ,5 ,3] corrosponding to A, B, C, D, E, F, G, H, I in respective order."""
            )
        alphabeta([1, 3, 4, 1, 4, 1, 3, 5, 3], singleStepFlag)
    if get_input:
        while len(leaves) != 9:
            leaves = input(
                """Enter Integer Values For A, B, C, D, E, F, G, H, I, separated by blank spaces: 
"""
                ).split()
            leaves = [int(x) for x in leaves]
        if not algorithm:
            minimax(leaves, singleStepFlag)
        elif algorithm:
            alphabeta(leaves, singleStepFlag)


<mask token>

<mask token>


def get_step_flag():
    step = [2, 2]
    while len(step) != 1:
        step = input(
            'Enter 0 to display only the final results - 1 for single-stepping option: '
            ).split()
        step = [int(x) for x in step]
    if step[0]:
        print()
        print(
            '>> get_step_flag function takes input to decide Single-stepping option'
            )
        print()
    return step[0]


def get_algorithm(singleStepFlag):
    if singleStepFlag:
        print()
        print(
            '>> Program function takes input to decide if Straight Minimaxing or w/ Alpha-beta Pruning'
            )
        print(
            ">> With alphabeta , we don't explicitly use the -inf,inf intial values, but that approach is implicitly implemented"
            )
        print()
    algorithm = []
    while len(algorithm) != 1 or algorithm[0] != 0 and algorithm[0] != 1:
        algorithm = input(
            'Enter 0 for Straight-Minimaxing Program - 1 for Minimax with Alphabeta Pruning: '
            ).split()
        algorithm = [int(x) for x in algorithm]
    return algorithm[0]


def choose_input(singleStepFlag):
    if singleStepFlag:
        print()
        print(
            '>> choose_input function takes input to Solve for the Assignment Trees OR user-inputted'
            )
        print()
    chooseInput = [2, 2]
    while len(chooseInput) != 1 or chooseInput[0] != 0 and chooseInput[0] != 1:
        chooseInput = input(
            'Enter 0 for solving the Assignment-Given tree(s) - 1 for Keyboard Input: '
            ).split()
        chooseInput = [int(x) for x in chooseInput]
    return chooseInput[0]


def minimax(leaves, singleStepFlag):
    L = min(leaves[0], leaves[1], leaves[2])
    M = min(leaves[3], leaves[4], leaves[5])
    R = min(leaves[6], leaves[7], leaves[8])
    if singleStepFlag:
        print(
            """
 >>> 1a )  Take the sequence of values for leaves, and for L , M, R take min() of the the patches of 3 nodes left, middle and right , according to the tree structure"""
            )
    Minimizer = [L, M, R]
    sortedIndex = sorted(range(len(Minimizer)), key=lambda k: Minimizer[k],
        reverse=True)
    Minimizer_dict = {(0): 'L', (1): 'M', (2): 'R'}
    if singleStepFlag:
        print(
            "\n >>> 2a )  Sort the Minimizer level Nodes' values from Highest to Lowest value"
            )
    max = Minimizer_dict[sortedIndex[0]]
    if singleStepFlag:
        print(
            """
 >>>  3a )   After sorting, the Final Output as required by assignment instructions is the L or M or R depedning on the sorting"""
            )
    print('The move of the max player is :', max)


def alphabeta(leaves, singleStepFlag):
    if singleStepFlag:
        print(
            """
 >>> 1b ) Take the sequence of values corrosponding to Tree leaves (terminal Nodes)  """
            )
    Minimizer_dict = {(0): 'L', (1): 'M', (2): 'R'}
    A = leaves[0]
    B = leaves[1]
    C = leaves[2]
    D = leaves[3]
    E = leaves[4]
    F = leaves[5]
    G = leaves[6]
    H = leaves[7]
    I = leaves[8]
    pruned = []
    if singleStepFlag:
        print(
            """
 >>> 2b ) Take min() of the the first 3 nodes, L = min (the 3-patch on the left), and initially assign D value to M node (as temporary ceiling) and G value to R node (as temporary ceiling)"""
            )
    L = min(A, B, C)
    M_ceil = D
    R_ceil = G
    if L >= M_ceil:
        if singleStepFlag:
            print(
                """
 >>> 3b )  Because  L >= M_ceil, append node E and F to pruned list -- in alphab. order """
                )
        pruned.append('E')
        pruned.append('F')
    elif L < M_ceil:
        if M_ceil > E:
            M_ceil = E
        if singleStepFlag:
            print(
                """
 >>> 3b )  In case M_ceil is bigger that E node value, replace it on the minimizer level, M_ceil = E  """
                )
        if L >= M_ceil:
            if singleStepFlag:
                print(
                    """
 >>> 4b )  after the new value for M_ceil, we check to see it is indeed smaller than L, and thus prune F """
                    )
            pruned.append('F')
        elif M_ceil > F:
            M_ceil = F
            if singleStepFlag:
                print(
                    '\n >>> 4b )  No pruning Happens at M patch, i.e. middle  '
                    )
    if L >= R_ceil or M_ceil >= R_ceil:
        if singleStepFlag:
            print(
                """
 >>> 5b )  Because  L >= R_ceil, or M_ceil >= R_ceil, Prune H , I nodes -- in alphab. order """
                )
        pruned.append('H')
        pruned.append('I')
    elif H < R_ceil:
        R_ceil = H
        if singleStepFlag:
            print(
                """
 >>> 5b )  In case R_ceil is bigger that H node value, replace it on the minimizer level, R_ceil = H  """
                )
    elif L >= R_ceil or M_ceil >= R_ceil:
        if singleStepFlag:
            print(
                """
 >>> 6b )  after the new value for R_ceil, we check to see it is indeed smaller than L or M_ceil, and thus prune I  """
                )
        pruned.append('I')
    elif I < R_ceil:
        R_ceil = I
        if singleStepFlag:
            print(
                '\n >>> 4b )  No pruning Happens at R patch, i.e. right node  '
                )
    MaxinmizerLevel = [L, M_ceil, R_ceil]
    sortedIndex = sorted(range(len(MaxinmizerLevel)), key=lambda k:
        MaxinmizerLevel[k], reverse=True)
    if singleStepFlag:
        print('\n >>> 7b ) Sort minimizer level nodes, and choose max  ')
    max = Minimizer_dict[sortedIndex[0]]
    print('The move of the max player is :', max)
    print('The Pruned Nodes are : ', end='')
    [print(x, end=' ') for x in pruned]


def main():
    leaves = []
    singleStepFlag = get_step_flag()
    algorithm = get_algorithm(singleStepFlag)
    get_input = choose_input(singleStepFlag)
    if get_input == 0 and not algorithm:
        print(
            """
- Minimax For tree with values of [5 ,3 , 1 , 2, 5 , 4 ,1, 3 , 3] corrosponding to A, B, C, D, E, F, G, H, I in respective order."""
            )
        minimax([5, 3, 1, 2, 5, 4, 1, 3, 3], singleStepFlag)
    if not get_input and algorithm:
        print(
            """
-------------------------------------------------------------------------------------------------"""
            )
        print(
            """
- Minimax with Alphabeta Pruning For tree with values of [5 ,3 , 1 , 2, 5 , 4 ,1, 3 , 3] corrosponding to A, B, C, D, E, F, G, H, I in respective order."""
            )
        alphabeta([5, 3, 1, 2, 5, 4, 1, 3, 3], singleStepFlag)
        print(
            """

-------------------------------------------------------------------------------------------------"""
            )
        print(
            """
- Minimax with Alphabeta Pruning For tree with values of [5, 2, 2, 5, 1, 3, 2, 4, 2] corrosponding to A, B, C, D, E, F, G, H, I in respective order."""
            )
        alphabeta([5, 2, 2, 5, 1, 3, 2, 4, 2], singleStepFlag)
        print(
            """

-------------------------------------------------------------------------------------------------"""
            )
        print(
            """
- Minimax with Alphabeta Pruning For tree with values of [1 ,3 ,4 ,1 ,4 ,1 ,3 ,5 ,3] corrosponding to A, B, C, D, E, F, G, H, I in respective order."""
            )
        alphabeta([1, 3, 4, 1, 4, 1, 3, 5, 3], singleStepFlag)
    if get_input:
        while len(leaves) != 9:
            leaves = input(
                """Enter Integer Values For A, B, C, D, E, F, G, H, I, separated by blank spaces: 
"""
                ).split()
            leaves = [int(x) for x in leaves]
        if not algorithm:
            minimax(leaves, singleStepFlag)
        elif algorithm:
            alphabeta(leaves, singleStepFlag)


if __name__ == '__main__':
    main()

"""
HW03 - Adversarial search
"""

def get_step_flag():
  step = [2,2]
  
  while (len(step) != 1):
    step = input('Enter 0 to display only the final results - 1 for single-stepping option: ').split()
    step = [int(x) for x in step]

  if (step[0]):
    print()
    print('>> get_step_flag function takes input to decide Single-stepping option')
    print()
  return step[0]

def get_algorithm(singleStepFlag):
  if (singleStepFlag) :
    print()
    print('>> Program function takes input to decide if Straight Minimaxing or w/ Alpha-beta Pruning')
    print(">> With alphabeta , we don't explicitly use the -inf,inf intial values, but that approach is implicitly implemented" )
    print()

  algorithm = []
  
  while (len(algorithm) != 1 or (algorithm[0] != 0 and algorithm[0] != 1)):
    algorithm = input('Enter 0 for Straight-Minimaxing Program - 1 for Minimax with Alphabeta Pruning: ').split()
    algorithm = [int(x) for x in algorithm]

  return algorithm[0]
   
def choose_input(singleStepFlag):
  if (singleStepFlag) :
    print()
    print('>> choose_input function takes input to Solve for the Assignment Trees OR user-inputted')
    print()

  chooseInput = [2, 2]
  
  while (len(chooseInput) != 1 or (chooseInput[0] != 0 and chooseInput[0] != 1)):
    chooseInput = input('Enter 0 for solving the Assignment-Given tree(s) - 1 for Keyboard Input: ').split()
    chooseInput = [int(x) for x in chooseInput]
  
  return chooseInput[0]

def minimax(leaves, singleStepFlag):
  L = min(leaves[0],leaves[1],leaves[2])
  M = min(leaves[3],leaves[4],leaves[5])
  R = min(leaves[6],leaves[7],leaves[8])
  if (singleStepFlag) :
    print("\n >>> 1a )  Take the sequence of values for leaves, and for L , M, R take min() of the the patches of 3 nodes left, middle and right , according to the tree structure" )

  Minimizer = [L, M, R]
  sortedIndex = sorted(range(len(Minimizer)), key=lambda k: Minimizer[k], reverse = True)
  Minimizer_dict = {0: "L"  ,1: "M"  , 2: "R" }
  if (singleStepFlag) :
    print("\n >>> 2a )  Sort the Minimizer level Nodes' values from Highest to Lowest value" )

  max = Minimizer_dict[sortedIndex[0]]
  if (singleStepFlag) :
    print("\n >>>  3a )   After sorting, the Final Output as required by assignment instructions is the L or M or R depedning on the sorting" )
    
  print("The move of the max player is :", max )

def alphabeta(leaves, singleStepFlag):

  if (singleStepFlag) :
    print("\n >>> 1b ) Take the sequence of values corrosponding to Tree leaves (terminal Nodes)  " )
    
  Minimizer_dict = {0: "L"  ,1: "M"  , 2: "R" }
  A = leaves[0] 
  B = leaves[1] 
  C = leaves[2]
  D = leaves[3]
  E = leaves[4]
  F = leaves[5] 
  G = leaves[6] 
  H = leaves[7]
  I = leaves[8]  

  pruned = []  
  if (singleStepFlag):
    print("\n >>> 2b ) Take min() of the the first 3 nodes, L = min (the 3-patch on the left), and initially assign D value to M node (as temporary ceiling) and G value to R node (as temporary ceiling)" )
  L = min(A,B,C)
  M_ceil = D
  R_ceil = G

  if L >= M_ceil:
    if (singleStepFlag) :
      print("\n >>> 3b )  Because  L >= M_ceil, append node E and F to pruned list -- in alphab. order " )
    pruned.append('E')
    pruned.append('F')
    
  elif L < M_ceil:
    if M_ceil > E:
      M_ceil = E
    if (singleStepFlag) :
        print("\n >>> 3b )  In case M_ceil is bigger that E node value, replace it on the minimizer level, M_ceil = E  " )
    
    if L >= M_ceil:
      if (singleStepFlag) :
         print("\n >>> 4b )  after the new value for M_ceil, we check to see it is indeed smaller than L, and thus prune F " )
      pruned.append('F')
    
    elif  M_ceil > F:
      M_ceil = F  
      if (singleStepFlag) :  
        print("\n >>> 4b )  No pruning Happens at M patch, i.e. middle  " )
    
    
  if L >= R_ceil or M_ceil >= R_ceil : #Now third , R node 
    if (singleStepFlag) :
      print("\n >>> 5b )  Because  L >= R_ceil, or M_ceil >= R_ceil, Prune H , I nodes -- in alphab. order " )
    pruned.append('H')
    pruned.append('I')
  elif H < R_ceil:
    R_ceil = H
    if (singleStepFlag) :
      print("\n >>> 5b )  In case R_ceil is bigger that H node value, replace it on the minimizer level, R_ceil = H  " )
    
  elif L >= R_ceil or M_ceil >= R_ceil :
    if (singleStepFlag) :  
      print("\n >>> 6b )  after the new value for R_ceil, we check to see it is indeed smaller than L or M_ceil, and thus prune I  " )
    pruned.append('I')
  elif I < R_ceil:
    R_ceil = I
    if (singleStepFlag) :  
      print("\n >>> 4b )  No pruning Happens at R patch, i.e. right node  " )    
    
  MaxinmizerLevel = [L ,M_ceil, R_ceil]
  sortedIndex = sorted(range(len(MaxinmizerLevel)), key=lambda k: MaxinmizerLevel[k], reverse = True)
  if (singleStepFlag) :  
    print("\n >>> 7b ) Sort minimizer level nodes, and choose max  " ) 
  max = Minimizer_dict[sortedIndex[0]]

  print("The move of the max player is :", max )
  print("The Pruned Nodes are : ", end='')
  [print(x, end=' ') for x in pruned]
      
def main():
  # get the nine input values
  leaves = []
  singleStepFlag = get_step_flag()
  
  algorithm = get_algorithm(singleStepFlag)
  get_input = choose_input(singleStepFlag)  


  if (get_input) == 0 and (not algorithm):
      print("\n- Minimax For tree with values of [5 ,3 , 1 , 2, 5 , 4 ,1, 3 , 3] corrosponding to A, B, C, D, E, F, G, H, I in respective order.")
      minimax([5 ,3 , 1 , 2, 5 , 4 ,1, 3 , 3], singleStepFlag)

  if (not get_input) and (algorithm):
    print('\n-------------------------------------------------------------------------------------------------')
    print("\n- Minimax with Alphabeta Pruning For tree with values of [5 ,3 , 1 , 2, 5 , 4 ,1, 3 , 3] corrosponding to A, B, C, D, E, F, G, H, I in respective order.")
    alphabeta([5 ,3 , 1 , 2, 5 , 4 ,1, 3 , 3], singleStepFlag)

    print('\n\n-------------------------------------------------------------------------------------------------')
    print("\n- Minimax with Alphabeta Pruning For tree with values of [5, 2, 2, 5, 1, 3, 2, 4, 2] corrosponding to A, B, C, D, E, F, G, H, I in respective order.")
    alphabeta([5, 2, 2, 5, 1, 3, 2, 4, 2], singleStepFlag)

    print('\n\n-------------------------------------------------------------------------------------------------')
    print("\n- Minimax with Alphabeta Pruning For tree with values of [1 ,3 ,4 ,1 ,4 ,1 ,3 ,5 ,3] corrosponding to A, B, C, D, E, F, G, H, I in respective order.")
    alphabeta([1 ,3 ,4 ,1 ,4 ,1 ,3 ,5 ,3], singleStepFlag)

  if get_input:  
      while (len(leaves)!= 9):
        leaves = input('Enter Integer Values For A, B, C, D, E, F, G, H, I, separated by blank spaces: \n').split()
        leaves = [int(x) for x in leaves]
      
      if not algorithm:
        minimax(leaves, singleStepFlag)
      elif algorithm:
        alphabeta(leaves, singleStepFlag)

if __name__ == '__main__':
  main()
