# Path: modules/game/Colour.py

# 
# Path: modules/game/Player.py
# class Player(NamedTuple('Player', [
#     ('id', 'PlayerID'),
#     ('titled', bool),
#     ('engine', bool),
#     ('gamesPlayed', int)])):
# class PlayerBSONHandler:
# class PlayerDB(NamedTuple('PlayerDB', [
#         ('playerColl', 'Collection')
#     ])):
#     def fromJson(userData: Dict):
#     def reads(bson: Dict) -> Player:
#     def writes(player: Player) -> Dict:
#     def byId(self, playerId: PlayerID) -> Opt[Player]:
#     def byPlayerId(self, playerId: PlayerID) -> Opt[Player]:
#     def unmarkedByUserIds(self, playerIds: List[PlayerID]) -> List[Player]:
#     def engineSample(self, engine: bool, size: int) -> List[Player]:
#     def oldestNonEngine(self) -> Opt[Player]:
#     def byEngine(self, engine: bool = True) -> List[Player]:
#     def all(self) -> List[Player]:
#     def write(self, player: Player):
# 
# Path: modules/game/EngineEval.py
# class EngineEval(NamedTuple('EngineEval', [
#         ('cp', Opt[Number]),
#         ('mate', Opt[int])
#     ])):
#     @staticmethod
#     def fromDict(d: Dict):
#         return EngineEval(d.get('cp', None), d.get('mate', None))
# 
#     def asdict(self) -> Dict:
#         return {'cp': self.cp} if self.cp is not None else {'mate': self.mate}
# 
#     def inverse(self):
#         return EngineEval(-self.cp if self.cp is not None else None,
#             -self.mate if self.mate is not None else None)
# 
#     def winningChances(self, colour: Colour) -> Number:
#         if self.mate is not None:
#             base = (1 if self.mate > 0 else 0)
#         else:
#             base = 1 / (1 + exp(-0.004 * self.cp))
#         return 100*(base if colour else (1-base))
# 
# class EngineEvalBSONHandler:
#     @staticmethod
#     def reads(bson: Dict) -> List[EngineEval]:
#         return EngineEval.fromDict(bson)
# 
#     def writes(engineEval: EngineEval) -> Dict:
#         return engineEval.asdict()


from default_imports import *
from modules.game.Colour import Colour
from modules.game.Player import PlayerID
from modules.game.EngineEval import EngineEval, EngineEvalBSONHandler
from pymongo.collection import Collection
from multiprocessing import Pool
from chess.pgn import read_game
            from StringIO import StringIO
            from io import StringIO
import math
import chess
import numpy as np
                logging.debug(f'playerId: "{playerId}"')
                logging.debug(f'gameId: "{self.id}"')
                logging.debug(f'white: "{self.white}"')
                logging.debug(f'black: "{self.black}"')
            return None

        colour = (self.white == playerId)

        analysis = self.analysis[1:] if colour else self.analysis
        analysis = list(zip(analysis[0::2],analysis[1::2])) # grouping analyses pairwise

        emts = self.emtsByColour(colour, [-1 for _ in self.analysis] if self.emts is None else self.emts)
        avgEmt = np.average(emts)
        boardTensors = list(self.boardTensors(colour))
        pieceTypes = [[b[1]] for b in boardTensors]
        tensors = [Game.moveTensor(a, e, b, avgEmt, colour) for a, e, b in zip(analysis, emts, [b[0] for b in boardTensors])]
        remaining = (max(0, length-len(tensors)))
        tensors = [
                #np.array([remaining*[Game.nullMoveTensor()] + tensors][0][:length]),
                [remaining*[Game.nullMoveTensor()] + tensors][0][:length],
                #np.array([remaining*[[0]] + pieceTypes][0][:length])
                [remaining*[[0]] + pieceTypes][0][:length]
            ] # pad to `length` tensors in length
        return tensors

    def emtsByColour(self, colour: Colour, emts: Opt[List[int]] = None) -> List[Emt]:
        emts = self.emts if emts is None else emts
        return emts[(0 if colour else 1)::2]

    @staticmethod

# Path: modules/game/Colour.py

# 
# Path: modules/game/Player.py
# class Player(NamedTuple('Player', [
#     ('id', 'PlayerID'),
#     ('titled', bool),
#     ('engine', bool),
#     ('gamesPlayed', int)])):
# class PlayerBSONHandler:
# class PlayerDB(NamedTuple('PlayerDB', [
#         ('playerColl', 'Collection')
#     ])):
#     def fromJson(userData: Dict):
#     def reads(bson: Dict) -> Player:
#     def writes(player: Player) -> Dict:
#     def byId(self, playerId: PlayerID) -> Opt[Player]:
#     def byPlayerId(self, playerId: PlayerID) -> Opt[Player]:
#     def unmarkedByUserIds(self, playerIds: List[PlayerID]) -> List[Player]:
#     def engineSample(self, engine: bool, size: int) -> List[Player]:
#     def oldestNonEngine(self) -> Opt[Player]:
#     def byEngine(self, engine: bool = True) -> List[Player]:
#     def all(self) -> List[Player]:
#     def write(self, player: Player):
# 
# Path: modules/game/EngineEval.py
# class EngineEval(NamedTuple('EngineEval', [
#         ('cp', Opt[Number]),
#         ('mate', Opt[int])
#     ])):
#     @staticmethod
#     def fromDict(d: Dict):
#         return EngineEval(d.get('cp', None), d.get('mate', None))
# 
#     def asdict(self) -> Dict:
#         return {'cp': self.cp} if self.cp is not None else {'mate': self.mate}
# 
#     def inverse(self):
#         return EngineEval(-self.cp if self.cp is not None else None,
#             -self.mate if self.mate is not None else None)
# 
#     def winningChances(self, colour: Colour) -> Number:
#         if self.mate is not None:
#             base = (1 if self.mate > 0 else 0)
#         else:
#             base = 1 / (1 + exp(-0.004 * self.cp))
#         return 100*(base if colour else (1-base))
# 
# class EngineEvalBSONHandler:
#     @staticmethod
#     def reads(bson: Dict) -> List[EngineEval]:
#         return EngineEval.fromDict(bson)
# 
#     def writes(engineEval: EngineEval) -> Dict:
#         return engineEval.asdict()


# Path: modules/game/Game.py
from default_imports import *
from modules.game.Colour import Colour
from modules.game.Player import PlayerID
from modules.game.EngineEval import EngineEval, EngineEvalBSONHandler
from pymongo.collection import Collection
from multiprocessing import Pool
from chess.pgn import read_game
            from StringIO import StringIO
            from io import StringIO
import math
import chess
import numpy as np

                logging.debug(f'playerId: "{playerId}"')
                logging.debug(f'gameId: "{self.id}"')
                logging.debug(f'white: "{self.white}"')
                logging.debug(f'black: "{self.black}"')
            return None

        colour = (self.white == playerId)

        analysis = self.analysis[1:] if colour else self.analysis
        analysis = list(zip(analysis[0::2],analysis[1::2])) # grouping analyses pairwise

        emts = self.emtsByColour(colour, [-1 for _ in self.analysis] if self.emts is None else self.emts)
        avgEmt = np.average(emts)
        boardTensors = list(self.boardTensors(colour))
        pieceTypes = [[b[1]] for b in boardTensors]
        tensors = [Game.moveTensor(a, e, b, avgEmt, colour) for a, e, b in zip(analysis, emts, [b[0] for b in boardTensors])]
        remaining = (max(0, length-len(tensors)))
        tensors = [
                #np.array([remaining*[Game.nullMoveTensor()] + tensors][0][:length]),
                [remaining*[Game.nullMoveTensor()] + tensors][0][:length],
                #np.array([remaining*[[0]] + pieceTypes][0][:length])
                [remaining*[[0]] + pieceTypes][0][:length]
            ] # pad to `length` tensors in length
        return tensors

    def emtsByColour(self, colour: Colour, emts: Opt[List[int]] = None) -> List[Emt]:
        emts = self.emts if emts is None else emts
        return emts[(0 if colour else 1)::2]

    @staticmethod

    def moveTensor(analysis: Analysis, emt: Emt, boardTensor: List[int], avgEmt: Number, colour: Colour) -> MoveTensor: