# Path: conf/ConfigWrapper.py
# class ConfigWrapper:
#     """
#     Used for loading and accessing values from a json config file.
#     """
#     def __init__(self, d: Dict):
#         self.d = d
# 
#     @staticmethod
#     def new(filename: str):
#         with open(filename) as confFile:
#             return ConfigWrapper(json.load(confFile))
# 
#     def __getitem__(self, key: str):
#         """
#         allows for accessing like, conf["index items like this"]
#         """
#         try:
#             head, tail = key.split(' ', 1)
#             return self.__getattr__(head)[tail]
#         except ValueError:
#             return self.__getattr__(key)
# 
#     def __getattr__(self, key: str):
#         """
#         allows for accessing like, conf.index.like.this
#         """
#         r = self.d.get(key)
#         if isinstance(r, dict):
#             return ConfigWrapper(r)
#         return r
# 
#     def asdict(self) -> Dict:
#         return self.d
# 
#     def __repr__(self):
#         return "ConfigWrapper({})".format(self.d)
# 
# Path: modules/game/AnalysedGame.py
# class GameAnalysedGame(NamedTuple('GameAnalysedGame', [
#         ('analysedGame', AnalysedGame),
#         ('game', Game)
#     ])):
#     """
#     Merger of Game and Analysed Game for a merged tensor
#     """
#     def length(self):
#         return self.analysedGame.length()
# 
#     def tensor(self):
#         try:
#             gt = self.game.boardTensorsByPlayerId(self.analysedGame.playerId, safe = False)
#             at = self.analysedGame.tensor()
#             return [
#                 [_1 + _2 for _1, _2 in zip(gt[0], at)],
#                 gt[1]
#             ]
#         except (TypeError, AttributeError):
#             return None


from default_imports import *
from conf.ConfigWrapper import ConfigWrapper
from random import shuffle
from math import ceil
from modules.game.AnalysedGame import GameAnalysedGame
from keras.models import load_model, Model
from keras.layers import Dropout, Embedding, Reshape, Dense, LSTM, Input, concatenate, Conv1D, Flatten
from keras.optimizers import Adam
from keras.engine.training import Model
from numpy import ndarray
import numpy as np
import logging
import os

GamePrediction = NewType('GamePrediction', int)
MovePrediction = NewType('MovePrediction', int)

WeightedMovePrediction = NewType('WeightedMovePrediction', int)
WeightedGamePrediction = NewType('WeightedGamePrediction', int)

class AnalysedGamePrediction(NamedTuple('AnalysedGamePrediction', [
        ('game', GamePrediction),
        ('lstmMoves', List[MovePrediction]),
        ('isolatedMoves', List[MovePrediction])
    ])):
    @staticmethod
    def fromTensor(tensor: ndarray, length: int):
        return AnalysedGamePrediction(
            game = int(100*tensor[0][0]),
            lstmMoves = [int(100*i) for i in tensor[1][0][:length]],
            isolatedMoves = [int(100*i) for i in tensor[2][0][:length]])

    def weightedMovePredictions(self) -> List[WeightedMovePrediction]:
        return [int(0.5*(l + i)) for l, i in zip(self.lstmMoves, self.isolatedMoves)]
    
    def weightedGamePrediction(self) -> WeightedGamePrediction:
        moveActivations = sorted(self.weightedMovePredictions(), reverse=True)
        moveActivationsLen = len(moveActivations)

        nanToZero = lambda x: 0 if np.isnan(x) else x

        highest = nanToZero(np.average([i for i in moveActivations if i > 80]))
        topX = np.average(moveActivations[:ceil(0.3*moveActivationsLen)])

# Path: conf/ConfigWrapper.py
# class ConfigWrapper:
#     """
#     Used for loading and accessing values from a json config file.
#     """
#     def __init__(self, d: Dict):
#         self.d = d
# 
#     @staticmethod
#     def new(filename: str):
#         with open(filename) as confFile:
#             return ConfigWrapper(json.load(confFile))
# 
#     def __getitem__(self, key: str):
#         """
#         allows for accessing like, conf["index items like this"]
#         """
#         try:
#             head, tail = key.split(' ', 1)
#             return self.__getattr__(head)[tail]
#         except ValueError:
#             return self.__getattr__(key)
# 
#     def __getattr__(self, key: str):
#         """
#         allows for accessing like, conf.index.like.this
#         """
#         r = self.d.get(key)
#         if isinstance(r, dict):
#             return ConfigWrapper(r)
#         return r
# 
#     def asdict(self) -> Dict:
#         return self.d
# 
#     def __repr__(self):
#         return "ConfigWrapper({})".format(self.d)
# 
# Path: modules/game/AnalysedGame.py
# class GameAnalysedGame(NamedTuple('GameAnalysedGame', [
#         ('analysedGame', AnalysedGame),
#         ('game', Game)
#     ])):
#     """
#     Merger of Game and Analysed Game for a merged tensor
#     """
#     def length(self):
#         return self.analysedGame.length()
# 
#     def tensor(self):
#         try:
#             gt = self.game.boardTensorsByPlayerId(self.analysedGame.playerId, safe = False)
#             at = self.analysedGame.tensor()
#             return [
#                 [_1 + _2 for _1, _2 in zip(gt[0], at)],
#                 gt[1]
#             ]
#         except (TypeError, AttributeError):
#             return None


# Path: modules/irwin/AnalysedGameModel.py
from default_imports import *
from conf.ConfigWrapper import ConfigWrapper
from random import shuffle
from math import ceil
from modules.game.AnalysedGame import GameAnalysedGame
from keras.models import load_model, Model
from keras.layers import Dropout, Embedding, Reshape, Dense, LSTM, Input, concatenate, Conv1D, Flatten
from keras.optimizers import Adam
from keras.engine.training import Model
from numpy import ndarray
import numpy as np
import logging
import os


GamePrediction = NewType('GamePrediction', int)
MovePrediction = NewType('MovePrediction', int)

WeightedMovePrediction = NewType('WeightedMovePrediction', int)
WeightedGamePrediction = NewType('WeightedGamePrediction', int)

class AnalysedGamePrediction(NamedTuple('AnalysedGamePrediction', [
        ('game', GamePrediction),
        ('lstmMoves', List[MovePrediction]),
        ('isolatedMoves', List[MovePrediction])
    ])):
    @staticmethod
    def fromTensor(tensor: ndarray, length: int):
        return AnalysedGamePrediction(
            game = int(100*tensor[0][0]),
            lstmMoves = [int(100*i) for i in tensor[1][0][:length]],
            isolatedMoves = [int(100*i) for i in tensor[2][0][:length]])

    def weightedMovePredictions(self) -> List[WeightedMovePrediction]:
        return [int(0.5*(l + i)) for l, i in zip(self.lstmMoves, self.isolatedMoves)]
    
    def weightedGamePrediction(self) -> WeightedGamePrediction:
        moveActivations = sorted(self.weightedMovePredictions(), reverse=True)
        moveActivationsLen = len(moveActivations)

        nanToZero = lambda x: 0 if np.isnan(x) else x

        highest = nanToZero(np.average([i for i in moveActivations if i > 80]))
        topX = np.average(moveActivations[:ceil(0.3*moveActivationsLen)])

        topY = np.average(moveActivations[:ceil(0.9*moveActivationsLen)])