# 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
# class Game(NamedTuple('Game', [
#         ('id', GameID),
#         ('white', PlayerID),
#         ('black', PlayerID),
#         ('pgn', List[str]),
#         ('emts', Opt[List[Emt]]),
#         ('analysis', Analysis)
#     ])):
# class GameBSONHandler:
# class GameDB(NamedTuple('GameDB', [
#         ('gameColl', Collection)
#     ])):
#     def fromDict(d: Dict):
#     def fromJson(json: Dict):
#     def toJson(self):
#     def playable(self):
#     def boardTensors(self, colour):
#     def boardTensorsByPlayerId(self, playerId: PlayerID, length: int = 60, safe: bool = True):
#     def tensor(self, playerId: PlayerID, length: int = 60, noisey: bool = False, safe: bool = True) -> Opt[GameTensor]:
#     def emtsByColour(self, colour: Colour, emts: Opt[List[int]] = None) -> List[Emt]:
#     def moveTensor(analysis: Analysis, emt: Emt, boardTensor: List[int], avgEmt: Number, colour: Colour) -> MoveTensor:
#     def nullBoardTensor():
#     def nullMoveTensor() -> MoveTensor:
#     def ply(moveNumber: int, colour: Colour) -> int:
#     def reads(bson: Dict) -> Game:
#     def writes(game: Game) -> Dict:
#     def byId(self, _id: GameID) -> Opt[Game]:
#     def byIds(self, ids: List[GameID]) -> List[Game]:
#     def byPlayerId(self, playerId: PlayerID) -> List[Game]:
#     def byPlayerIdAndAnalysed(self, playerId: PlayerID, analysed: bool = True) -> List[Game]:
#     def write(self, game: Game):
#     def writeMany(self, games: List[Game]):


from default_imports import *
from modules.game.EngineEval import EngineEval, EngineEvalBSONHandler
from modules.game.Game import Emt
from functools import lru_cache
from math import exp
import numpy as np
        return 1 if engineEval.mate > 0 else 0
    else:
        return 1 / (1 + exp(-0.004 * engineEval.cp))

def similarChances(c1: Number, c2: Number) -> bool:
    return abs(c1 - c2) < 0.05

class AnalysisBSONHandler:
    @staticmethod
    def reads(bson: Dict) -> Analysis:
        return Analysis(
            uci = bson['uci'],
            engineEval = EngineEvalBSONHandler.reads(bson['score'])
        )

    @staticmethod
    def writes(analysis: Analysis) -> Dict:
        return {
            'uci': analysis.uci,
            'score': EngineEvalBSONHandler.writes(analysis.engineEval)
        }


class AnalysedMoveBSONHandler:
    @staticmethod
    def reads(bson: Dict) -> AnalysedMove:
        return AnalysedMove(
            uci = bson['uci'],
            move = bson['move'],
            emt = bson['emt'],

# 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
# class Game(NamedTuple('Game', [
#         ('id', GameID),
#         ('white', PlayerID),
#         ('black', PlayerID),
#         ('pgn', List[str]),
#         ('emts', Opt[List[Emt]]),
#         ('analysis', Analysis)
#     ])):
# class GameBSONHandler:
# class GameDB(NamedTuple('GameDB', [
#         ('gameColl', Collection)
#     ])):
#     def fromDict(d: Dict):
#     def fromJson(json: Dict):
#     def toJson(self):
#     def playable(self):
#     def boardTensors(self, colour):
#     def boardTensorsByPlayerId(self, playerId: PlayerID, length: int = 60, safe: bool = True):
#     def tensor(self, playerId: PlayerID, length: int = 60, noisey: bool = False, safe: bool = True) -> Opt[GameTensor]:
#     def emtsByColour(self, colour: Colour, emts: Opt[List[int]] = None) -> List[Emt]:
#     def moveTensor(analysis: Analysis, emt: Emt, boardTensor: List[int], avgEmt: Number, colour: Colour) -> MoveTensor:
#     def nullBoardTensor():
#     def nullMoveTensor() -> MoveTensor:
#     def ply(moveNumber: int, colour: Colour) -> int:
#     def reads(bson: Dict) -> Game:
#     def writes(game: Game) -> Dict:
#     def byId(self, _id: GameID) -> Opt[Game]:
#     def byIds(self, ids: List[GameID]) -> List[Game]:
#     def byPlayerId(self, playerId: PlayerID) -> List[Game]:
#     def byPlayerIdAndAnalysed(self, playerId: PlayerID, analysed: bool = True) -> List[Game]:
#     def write(self, game: Game):
#     def writeMany(self, games: List[Game]):


# Path: modules/game/AnalysedMove.py
from default_imports import *
from modules.game.EngineEval import EngineEval, EngineEvalBSONHandler
from modules.game.Game import Emt
from functools import lru_cache
from math import exp
import numpy as np

        return 1 if engineEval.mate > 0 else 0
    else:
        return 1 / (1 + exp(-0.004 * engineEval.cp))

def similarChances(c1: Number, c2: Number) -> bool:
    return abs(c1 - c2) < 0.05

class AnalysisBSONHandler:
    @staticmethod
    def reads(bson: Dict) -> Analysis:
        return Analysis(
            uci = bson['uci'],
            engineEval = EngineEvalBSONHandler.reads(bson['score'])
        )

    @staticmethod
    def writes(analysis: Analysis) -> Dict:
        return {
            'uci': analysis.uci,
            'score': EngineEvalBSONHandler.writes(analysis.engineEval)
        }


class AnalysedMoveBSONHandler:
    @staticmethod
    def reads(bson: Dict) -> AnalysedMove:
        return AnalysedMove(
            uci = bson['uci'],
            move = bson['move'],
            emt = bson['emt'],

            engineEval = EngineEvalBSONHandler.reads(bson['score']),