# 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/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/AnalysedMove.py
# UCI = NewType('UCI', str)
# class AnalysedMove(NamedTuple('AnalysedMove', [
#         ('uci', UCI),
#         ('move', MoveNumber),
#         ('emt', Emt),
#         ('engineEval', EngineEval),
#         ('analyses', List[Analysis])
#     ])):
# class AnalysisBSONHandler:
# class AnalysedMoveBSONHandler:
#     def tensor(self, timeAvg: Number, wclAvg: Number) -> List[Number]:
#     def nullTensor() -> List[int]:
#     def top(self) -> Opt[Analysis]:
#     def difToNextBest(self) -> Number:
#     def difToNextWorst(self) -> Number:
#     def PVsWinningChancesLoss(self) -> Number:
#     def averageWinningChancesLoss(self) -> Number:
#     def winningChancesLoss(self, usePV: bool = False) -> Number:
#     def advantage(self) -> Number:
#     def ambiguity(self) -> int: # 1 = only one top move, 5 = all moves good
#     def trueRank(self) -> TrueRank:
#     def rank(self) -> Rank:
#     def projectedRank(self) -> Number:
# def winningChances(engineEval: EngineEval) -> Number:
# def similarChances(c1: Number, c2: Number) -> bool:
#     def reads(bson: Dict) -> Analysis:
#     def writes(analysis: Analysis) -> Dict:
#     def reads(bson: Dict) -> AnalysedMove:
#     def writes(analysedMove: AnalysedMove) -> Dict:
# 
# Path: modules/game/AnalysedPosition.py
# class AnalysedPosition(NamedTuple('AnalysedPosition', [
#         ('id', AnalysedPositionID),
#         ('analyses', List[Analysis])
#     ])):
#     """
#     Like an analysed move, but only with SF analysis. Does not contain any other move data.
#     This is used for accelerating stockfish analysis.
#     """
#     @staticmethod
#     def fromBoardAndAnalyses(board: Board, analyses: List[Analysis]):
#         return AnalysedPosition(
#             id=AnalysedPosition.idFromBoard(board),
#             analyses=analyses)
# 
#     @staticmethod
#     def idFromBoard(board: Board) -> AnalysedPositionID:
#         return str(polyglot.zobrist_hash(board))


from default_imports import *
from math import log10, floor
from modules.game.Game import Game, GameID, Emt
from modules.game.Colour import Colour
from modules.game.Player import PlayerID
from modules.game.AnalysedMove import AnalysedMove, AnalysedMoveBSONHandler, EngineEval, Analysis, Rank, TrueRank
from modules.game.AnalysedPosition import AnalysedPosition
from pymongo.collection import Collection
import numpy as np
import json




AnalysedGameID = NewType('AnalysedGameID', str) # <GameID>/<white|black>

AnalysedGameTensor = NewType('AnalysedGameTensor', np.ndarray)

class AnalysedGame(NamedTuple('AnalysedGame', [
        ('id', AnalysedGameID),
        ('playerId', PlayerID),
        ('gameId', GameID),
        ('analysedMoves', List[AnalysedMove])
    ])):
    """
    An analysed game is a game that has been deeply analysed from a single
    player's perspective.
    """
    @staticmethod

# 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/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/AnalysedMove.py
# UCI = NewType('UCI', str)
# class AnalysedMove(NamedTuple('AnalysedMove', [
#         ('uci', UCI),
#         ('move', MoveNumber),
#         ('emt', Emt),
#         ('engineEval', EngineEval),
#         ('analyses', List[Analysis])
#     ])):
# class AnalysisBSONHandler:
# class AnalysedMoveBSONHandler:
#     def tensor(self, timeAvg: Number, wclAvg: Number) -> List[Number]:
#     def nullTensor() -> List[int]:
#     def top(self) -> Opt[Analysis]:
#     def difToNextBest(self) -> Number:
#     def difToNextWorst(self) -> Number:
#     def PVsWinningChancesLoss(self) -> Number:
#     def averageWinningChancesLoss(self) -> Number:
#     def winningChancesLoss(self, usePV: bool = False) -> Number:
#     def advantage(self) -> Number:
#     def ambiguity(self) -> int: # 1 = only one top move, 5 = all moves good
#     def trueRank(self) -> TrueRank:
#     def rank(self) -> Rank:
#     def projectedRank(self) -> Number:
# def winningChances(engineEval: EngineEval) -> Number:
# def similarChances(c1: Number, c2: Number) -> bool:
#     def reads(bson: Dict) -> Analysis:
#     def writes(analysis: Analysis) -> Dict:
#     def reads(bson: Dict) -> AnalysedMove:
#     def writes(analysedMove: AnalysedMove) -> Dict:
# 
# Path: modules/game/AnalysedPosition.py
# class AnalysedPosition(NamedTuple('AnalysedPosition', [
#         ('id', AnalysedPositionID),
#         ('analyses', List[Analysis])
#     ])):
#     """
#     Like an analysed move, but only with SF analysis. Does not contain any other move data.
#     This is used for accelerating stockfish analysis.
#     """
#     @staticmethod
#     def fromBoardAndAnalyses(board: Board, analyses: List[Analysis]):
#         return AnalysedPosition(
#             id=AnalysedPosition.idFromBoard(board),
#             analyses=analyses)
# 
#     @staticmethod
#     def idFromBoard(board: Board) -> AnalysedPositionID:
#         return str(polyglot.zobrist_hash(board))


# Path: modules/game/AnalysedGame.py
from default_imports import *
from math import log10, floor
from modules.game.Game import Game, GameID, Emt
from modules.game.Colour import Colour
from modules.game.Player import PlayerID
from modules.game.AnalysedMove import AnalysedMove, AnalysedMoveBSONHandler, EngineEval, Analysis, Rank, TrueRank
from modules.game.AnalysedPosition import AnalysedPosition
from pymongo.collection import Collection
import numpy as np
import json





AnalysedGameID = NewType('AnalysedGameID', str) # <GameID>/<white|black>

AnalysedGameTensor = NewType('AnalysedGameTensor', np.ndarray)

class AnalysedGame(NamedTuple('AnalysedGame', [
        ('id', AnalysedGameID),
        ('playerId', PlayerID),
        ('gameId', GameID),
        ('analysedMoves', List[AnalysedMove])
    ])):
    """
    An analysed game is a game that has been deeply analysed from a single
    player's perspective.
    """
    @staticmethod

    def new(gameId: GameID, colour: Colour, playerId: PlayerID, analysedMoves: List[AnalysedMove]):