# Importing files from this project
import ResNet
import MCTS
import argparse
import Files
from keras.optimizers import SGD


# import Config and Gamelogic, from the game you want to train/play

parser = argparse.ArgumentParser(description='Command line for AZ!')
parser.add_argument("--game", default= "TicTacToe",
                    choices= ["TicTacToe", "FourInARow"], required= False, help= "Choose one of the games from the list")
parser.add_argument("--numSearch", type = int,  default = 100, help = "This is number of searches preformed by MCTS")
parser.add_argument("--opponent", type = str, default= "4r_7", help = "Choose the agent you want to play against")

args = parser.parse_args()
typeOfGame = args.game

if typeOfGame == "TicTacToe":
    print("Skal spille TicTacToe")
    from TicTacToe import Config
    from TicTacToe import Gamelogic
elif typeOfGame == "FourInARow":
    print("Skal spille FourInARow")
    from FourInARow import Config
    from FourInARow import Gamelogic


from loss import softmax_cross_entropy_with_logits, softmax


#Importing other libraries
import numpy as np

# Creating and returning a tree with properties specified from the input
def get_tree(config, agent, game, dirichlet_noise=True):
    tree = MCTS.MCTS(game, game.get_board(), agent, config)
    return tree

# Generating data by self-play
def generate_data(game, agent, config, num_sim=100, games=1):
    tree = get_tree(config, agent, game)

    x = []
    y_policy = []
    y_value = []

    for _ in range(games):

        game.__init__()
        history = []
        policy_targets = []
        player_moved_list = []
        positions = []

        while not game.is_final():
            tree.reset_search()
            tree.root.board_state = game.get_board()
            tree.search_series(num_sim)
            temp_move = tree.get_temperature_move(tree.root)
            history.append(temp_move)
            policy_targets.append(np.array(tree.get_posterior_probabilities()))
            if typeOfGame == "FourInARow":
                (tree.get_prior_probabilities(game.get_board().reshape(1, 6, 7, 2)))
            if typeOfGame == "TicTacToe":
                (tree.get_prior_probabilities(game.get_board().reshape(1,3,3,2)))
            player_moved_list.append(game.get_turn())
            positions.append(np.array(game.get_board()))

            game.execute_move(temp_move)
            print("________________")
            game.print_board()
            print("________________")

        game_outcome = game.get_outcome()
        if game_outcome == [1, -1]:
            print("X vant")
        elif game_outcome == [-1, 1]:
            print ("O vant")
        else:
            print("Uavgjort")
        
        value_targets = [game_outcome[x] for x in player_moved_list]

        x = x + positions
        y_policy = y_policy + policy_targets
        y_value = y_value + value_targets


    return np.array(x), np.array(y_policy), np.array(y_value)


# Training AlphaZero by generating data from self-play and fitting the network
def train(game, config, num_filters, num_res_blocks, num_sim=125, epochs=50, games_each_epoch=10,
          batch_size=32, num_train_epochs=10):
    h, w, d = config.board_dims[1:]
    agent = ResNet.ResNet.build(h, w, d, num_filters, config.policy_output_dim, num_res_blocks=num_res_blocks)
    agent.compile(loss=[softmax_cross_entropy_with_logits, 'mean_squared_error'],
                  optimizer=SGD(lr=0.001, momentum=0.9))
    agent.summary()

    for epoch in range(epochs):
        x, y_pol, y_val = generate_data(game, agent, config, num_sim=num_sim, games=games_each_epoch)
        print("Epoch")
        print(x.shape)
        raw = agent.predict(x)
        for num in range(len(x)):
            print("targets-predictions")
            print(y_pol[num], y_val[num])
            print(softmax(y_pol[num], raw[0][num]), raw[1][num])

        agent.fit(x=x, y=[y_pol, y_val], batch_size=min(batch_size, len(x)), epochs=num_train_epochs, callbacks=[])
        agent.save_weights("Models/"+Config.name+"/"+str(epoch)+".h5")
    return agent

# Returns the best legal move based on the predictions from
# The neural network
def choose_best_legal_move(legal_moves, y_pred):
    best_move = np.argmax(y_pred)
    print("Best move", best_move)
    if (y_pred[best_move] == 0):
        return None
    if best_move in legal_moves:
        return best_move
    else:
        y_pred[best_move] = 0
        return choose_best_legal_move(legal_moves, y_pred)


if __name__ == '__main__':
    if typeOfGame == "FourInARow":
        train(Gamelogic.FourInARow(), Config, 128, 7)
    elif typeOfGame == "TicTacToe":
        train(Gamelogic.TicTacToe(), Config, 128, 4)

# Importing files from this project
import ResNet
import MCTS
import argparse
import Files
from keras.optimizers import SGD


# import Config and Gamelogic, from the game you want to train/play

parser = argparse.ArgumentParser(description='Command line for AZ!')
parser.add_argument("--game", default= "TicTacToe",
                    choices= ["TicTacToe", "FourInARow"], required= False, help= "Choose one of the games from the list")
parser.add_argument("--numSearch", type = int,  default = 100, help = "This is number of searches preformed by MCTS")
parser.add_argument("--opponent", type = str, default= "4r_7", help = "Choose the agent you want to play against")

args = parser.parse_args()
typeOfGame = args.game

if typeOfGame == "TicTacToe":
    print("Skal spille TicTacToe")
    from TicTacToe import Config
    from TicTacToe import Gamelogic
elif typeOfGame == "FourInARow":
    print("Skal spille FourInARow")
    from FourInARow import Config
    from FourInARow import Gamelogic


from loss import softmax_cross_entropy_with_logits, softmax


#Importing other libraries
import numpy as np

# Creating and returning a tree with properties specified from the input
def get_tree(config, agent, game, dirichlet_noise=True):
    tree = MCTS.MCTS(game, game.get_board(), agent, config)
    return tree

# Generating data by self-play
def generate_data(game, agent, config, num_sim=100, games=1):
    tree = get_tree(config, agent, game)

    x = []
    y_policy = []
    y_value = []

    for _ in range(games):

        game.__init__()
        history = []
        policy_targets = []
        player_moved_list = []
        positions = []

        while not game.is_final():
            tree.reset_search()
            tree.root.board_state = game.get_board()
            tree.search_series(num_sim)
            temp_move = tree.get_temperature_move(tree.root)
            history.append(temp_move)
            policy_targets.append(np.array(tree.get_posterior_probabilities()))
            if typeOfGame == "FourInARow":
                (tree.get_prior_probabilities(game.get_board().reshape(1, 6, 7, 2)))
            if typeOfGame == "TicTacToe":
                (tree.get_prior_probabilities(game.get_board().reshape(1,3,3,2)))
            player_moved_list.append(game.get_turn())
            positions.append(np.array(game.get_board()))

            game.execute_move(temp_move)
            print("________________")
            game.print_board()
            print("________________")

        game_outcome = game.get_outcome()
        if game_outcome == [1, -1]:
            print("X vant")
        elif game_outcome == [-1, 1]:
            print ("O vant")
        else:
            print("Uavgjort")
        
        value_targets = [game_outcome[x] for x in player_moved_list]

        x = x + positions
        y_policy = y_policy + policy_targets
        y_value = y_value + value_targets


    return np.array(x), np.array(y_policy), np.array(y_value)


# Training AlphaZero by generating data from self-play and fitting the network
def train(game, config, num_filters, num_res_blocks, num_sim=125, epochs=50, games_each_epoch=10,
          batch_size=32, num_train_epochs=10):
    h, w, d = config.board_dims[1:]
    agent = ResNet.ResNet.build(h, w, d, num_filters, config.policy_output_dim, num_res_blocks=num_res_blocks)
    agent.compile(loss=[softmax_cross_entropy_with_logits, 'mean_squared_error'],
                  optimizer=SGD(lr=0.001, momentum=0.9))
    agent.summary()

    for epoch in range(epochs):
        x, y_pol, y_val = generate_data(game, agent, config, num_sim=num_sim, games=games_each_epoch)
        print("Epoch")
        print(x.shape)
        raw = agent.predict(x)
        for num in range(len(x)):
            print("targets-predictions")
            print(y_pol[num], y_val[num])
            print(softmax(y_pol[num], raw[0][num]), raw[1][num])

        agent.fit(x=x, y=[y_pol, y_val], batch_size=min(batch_size, len(x)), epochs=num_train_epochs, callbacks=[])
        agent.save_weights("Models/"+Config.name+"/"+str(epoch)+".h5")
    return agent

# Returns the best legal move based on the predictions from
# The neural network
def choose_best_legal_move(legal_moves, y_pred):
    best_move = np.argmax(y_pred)
    print("Best move", best_move)
    if (y_pred[best_move] == 0):
        return None
    if best_move in legal_moves:
        return best_move
    else:
        y_pred[best_move] = 0
        return choose_best_legal_move(legal_moves, y_pred)


if __name__ == '__main__':
    if typeOfGame == "FourInARow":
        train(Gamelogic.FourInARow(), Config, 128, 7)
    elif typeOfGame == "TicTacToe":
        train(Gamelogic.TicTacToe(), Config, 128, 4)

# Importing files from this project # import Config and Gamelogic, from the game you want to train/play #Importing other libraries # Creating and returning a tree with properties specified from the input # Generating data by self-play # Training AlphaZero by generating data from self-play and fitting the network # Returns the best legal move based on the predictions from # The neural network