def test_getstatus1(self): """Testing bare black king is not draw in Atomic variant""" board = AtomicBoard(setup=FEN2) board = board.move(parseSAN(board, 'Qxc2')) print(board) self.assertEqual(getStatus(board), (BLACKWON, WON_KINGEXPLODE))
def loadToModel(self, gameno, position, model=None): if not model: model = GameModel() # We have to set full move number to 1 to make sure LBoard and GameModel # are synchronized. #fenlist = self.games[gameno].split(" ") #if len(fenlist) == 6: # fen = " ".join(fenlist[:5]) + " 1" fen = self.games[gameno] try: board = model.variant(setup=fen) except SyntaxError as err: board = model.variant() raise LoadingError( _("The game can't be loaded, because of an error parsing FEN"), err.args[0]) model.boards = [board] model.variations = [model.boards] model.moves = [] if model.status == WAITING_TO_START: status, reason = getStatus(model.boards[-1]) if status in (BLACKWON, WHITEWON, DRAW): model.status, model.reason = status, reason return model
def loadToModel(self, rec, position, model=None): if not model: model = GameModel() if self.fen_is_string: rec = self.games[0] if "Variant" in rec: model.variant = FischerandomBoard fen = self.games[0]["FEN"] try: board = model.variant(setup=fen) except SyntaxError as err: board = model.variant() raise LoadingError( _("The game can't be loaded, because of an error parsing FEN"), err.args[0]) model.boards = [board] model.variations = [model.boards] model.moves = [] if model.status == WAITING_TO_START: status, reason = getStatus(model.boards[-1]) if status in (BLACKWON, WHITEWON, DRAW): model.status, model.reason = status, reason return model
def test1(self): """Testing both king goes to 8.row draw in racingkings variant""" board = RacingKingsBoard(setup=FEN) board = board.move(parseSAN(board, 'Kh8')) print(board) # White king reached 8th row, but this is not a win # because black can reach 8th row also with hes next move self.assertEqual(getStatus(board), (RUNNING, UNKNOWN_REASON)) self.assertTrue(validate(board, parseSAN(board, 'Kb8'))) self.assertTrue(not validate(board, parseSAN(board, 'Kd8'))) board = board.move(parseSAN(board, 'Kb8')) print(board) self.assertEqual(getStatus(board), (DRAW, DRAW_KINGSINEIGHTROW))
def loadToModel(self, rec, position, model=None): if not model: model = GameModel() if self.fen_is_string: rec = self.games[0] if isinstance(rec, dict) and "Variant" in rec: model.variant = FischerandomBoard fen = self.games[0]["FEN"] try: board = model.variant(setup=fen) except SyntaxError as err: board = model.variant() raise LoadingError( _("The game can't be loaded, because of an error parsing FEN"), err.args[0]) model.boards = [board] model.variations = [model.boards] model.moves = [] if model.status == WAITING_TO_START: status, reason = getStatus(model.boards[-1]) if status in (BLACKWON, WHITEWON, DRAW): model.status, model.reason = status, reason return model
def loadToModel(self, gameno, position, model=None): if not model: model = GameModel() # We have to set full move number to 1 to make sure LBoard and GameModel # are synchronized. # fenlist = self.games[gameno].split(" ") # if len(fenlist) == 6: # fen = " ".join(fenlist[:5]) + " 1" fen = self.games[gameno] try: board = model.variant(setup=fen) except SyntaxError as err: board = model.variant() raise LoadingError( _("The game can't be loaded, because of an error parsing FEN"), err.args[0]) model.boards = [board] model.variations = [model.boards] model.moves = [] if model.status == WAITING_TO_START: status, reason = getStatus(model.boards[-1]) if status in (BLACKWON, WHITEWON, DRAW): model.status, model.reason = status, reason return model
def loadToModel(self, rec, position, model=None): if not model: model = GameModel() if "Variant" in rec: model.variant = FischerandomBoard fieldlist = rec["FEN"].split(" ") if len(fieldlist) == 4: fen = rec["FEN"] opcodestr = "" elif len(fieldlist) > 4: fen = " ".join(fieldlist[:4]) opcodestr = " ".join(fieldlist[4:]) else: raise LoadingError("EPD string can not have less than 4 field") opcodes = {} for opcode in map(str.strip, opcodestr.split(";")): space = opcode.find(" ") if space == -1: opcodes[opcode] = True else: opcodes[opcode[:space]] = opcode[space + 1:] if "hmvc" in opcodes: fen += " " + opcodes["hmvc"] else: fen += " 0" if "fmvn" in opcodes: fen += " " + opcodes["fmvn"] else: fen += " 1" model.boards = [model.variant(setup=fen)] model.variations = [model.boards] model.status = WAITING_TO_START # rc is kinda broken # if "rc" in opcodes: # model.boards[0].board.rc = int(opcodes["rc"]) if "resign" in opcodes: if fieldlist[1] == "w": model.status = BLACKWON else: model.status = WHITEWON model.reason = WON_RESIGN if model.status == WAITING_TO_START: status, reason = getStatus(model.boards[-1]) if status in (BLACKWON, WHITEWON, DRAW): model.status, model.reason = status, reason return model
def _searchNow(self, ponderhit=False): log.debug("_searchNow: self.needBestmove=%s ponderhit=%s self.board=%s" % ( self.needBestmove, ponderhit, self.board), extra={"task": self.defname}) commands = [] if ponderhit: commands.append("ponderhit") elif self.mode == NORMAL: commands.append("position %s" % self.uciPosition) if self.strength <= 3: commands.append("go depth %d" % self.strength) else: if self.moves > 0: commands.append("go wtime %d winc %d btime %d binc %d movestogo %s" % ( self.wtime, self.incr, self.btime, self.incr, self.moves)) else: commands.append("go wtime %d winc %d btime %d binc %d" % ( self.wtime, self.incr, self.btime, self.incr)) else: print("stop", file=self.engine) if self.mode == INVERSE_ANALYZING: if self.board.board.opIsChecked(): # Many engines don't like positions able to take down enemy # king. Therefore we just return the "kill king" move # automaticaly self.emit("analyze", [([toAN( self.board, getMoveKillingKing(self.board))], MATE_VALUE - 1, "")]) return commands.append("position fen %s" % self.board.asFen()) else: commands.append("position %s" % self.uciPosition) # commands.append("go infinite") move_time = int(conf.get("max_analysis_spin", 3)) * 1000 commands.append("go movetime %s" % move_time) if self.hasOption("MultiPV") and self.multipvSetting > 1: self.multipvExpected = min(self.multipvSetting, legalMoveCount(self.board)) else: self.multipvExpected = 1 self.analysis = [None] * self.multipvExpected if self.needBestmove: self.commands.append(commands) log.debug("_searchNow: self.needBestmove==True, appended to self.commands=%s" % self.commands, extra={"task": self.defname}) else: for command in commands: print(command, file=self.engine) if getStatus(self.board)[ 1] != WON_MATE: # XXX This looks fishy. self.needBestmove = True self.readyForStop = True
def test_getstatus2(self): """Testing bare white king is not draw in Atomic variant""" board = AtomicBoard(setup=FEN3) self.assertTrue(not validate(board, parseSAN(board, 'Kxg7'))) self.assertTrue(not validate(board, parseSAN(board, 'Kg8'))) self.assertTrue(not validate(board, parseSAN(board, 'Kh7'))) print(board) self.assertEqual(getStatus(board), (DRAW, DRAW_STALEMATE))
def _searchNow (self, ponderhit=False): log.debug("_searchNow: self.needBestmove=%s ponderhit=%s self.board=%s\n" % \ (self.needBestmove, ponderhit, self.board), self.defname) with self.moveLock: commands = [] if ponderhit: commands.append("ponderhit") elif self.mode == NORMAL: commands.append("position %s" % self.uciPosition) if self.strength <= 3: commands.append("go depth %d" % self.strength) else: commands.append("go wtime %d winc %d btime %d binc %d" % \ (self.wtime, self.incr, self.btime, self.incr)) else: print >> self.engine, "stop" if self.mode == INVERSE_ANALYZING: if self.board.board.opIsChecked(): # Many engines don't like positions able to take down enemy # king. Therefore we just return the "kill king" move # automaticaly #self.emit("analyze", [([getMoveKillingKing(self.board)], MATE_VALUE-1)]) return commands.append("position fen %s" % self.board.asFen()) else: commands.append("position %s" % self.uciPosition) commands.append("go infinite") if self.hasOption("MultiPV") and self.multipvSetting > 1: self.multipvExpected = min(self.multipvSetting, legalMoveCount(self.board)) else: self.multipvExpected = 1 self.analysis = [None] * self.multipvExpected if self.needBestmove: self.commands.append(commands) log.debug("_searchNow: self.needBestmove==True, appended to self.commands=%s\n" % \ self.commands, self.defname) else: for command in commands: print >> self.engine, command if getStatus(self.board)[1] != WON_MATE: # XXX This looks fishy. self.needBestmove = True self.readyForStop = True
def check_if_game_over(board: Board, game_id: int) -> str: winner = "" status, _ = getStatus(board) if status == WHITEWON: winner = WHITE_STR elif status == BLACKWON: winner = BLACK_STR if winner != "": game = Games.objects.get(id=game_id) game.ongoing = False game.save() return winner
def loadToModel (self, gameno, position, model=None): if not model: model = GameModel() # We have to set full move number to 1 to make sure LBoard and GameModel # are synchronized. #fenlist = self.games[gameno].split(" ") #if len(fenlist) == 6: # fen = " ".join(fenlist[:5]) + " 1" fen = self.games[gameno] model.boards = [model.variant.board(setup=fen)] if model.status == WAITING_TO_START: model.status, model.reason = getStatus(model.boards[-1]) return model
def game_changed(self, gamemodel, ply): if gamemodel.practice_game: if len(gamemodel.moves) % 2 == 0: # engine moved, we can enable retry self.set_response_sensitive(RETRY, True) return # print(gamemodel.hint, repr(gamemodel.moves[-1])) status, reason = getStatus(gamemodel.boards[-1]) if status in UNDOABLE_STATES: self.get_next_puzzle() elif gamemodel.hint and gamemodel.hint != repr( gamemodel.moves[-1]): self.retry() else: self.your_turn()
def _searchNow(self, ponderhit=False): log.debug("_searchNow: self.needBestmove=%s ponderhit=%s self.board=%s\n" % \ (self.needBestmove, ponderhit, self.board), self.defname) with self.moveLock: commands = [] if ponderhit: commands.append("ponderhit") elif self.mode == NORMAL: commands.append("position fen %s" % self.board.asFen()) if self.strength <= 3: commands.append("go depth %d" % self.strength) else: commands.append("go wtime %d btime %d winc %d binc %d" % \ (self.wtime, self.btime, self.incr, self.incr)) else: if self.mode == INVERSE_ANALYZING: if self.board.board.opIsChecked(): # Many engines don't like positions able to take down enemy # king. Therefore we just return the "kill king" move # automaticaly self.emit("analyze", [getMoveKillingKing(self.board)], MATE_VALUE - 1) return print >> self.engine, "stop" if self.board.asFen() == FEN_START: commands.append("position startpos") else: commands.append("position fen %s" % self.board.asXFen()) commands.append("go infinite") if self.needBestmove: self.commands.append(commands) log.debug("_searchNow: self.needBestmove==True, appended to self.commands=%s\n" % \ self.commands, self.defname) else: for command in commands: print >> self.engine, command if self.board.asFen() != FEN_START and getStatus( self.board)[1] != WON_MATE: self.needBestmove = True self.readyForStop = True
def loadToModel (self, gameno, position, model=None): if not model: model = GameModel() board = model.variant.board() for y,row in enumerate(self.games[gameno]): for x,letter in enumerate(reg2.findall(row)): if letter in entitydefs: letter = entitydefs[letter] if letter not in piecesDia: raise LoadingError (_("Couldn't load the diagram '%s'")%repr(letter)) col, pie = piecesDia[letter] if pie != EMPTY: board.addPiece(Cord(x,7-y), Piece(col,pie)) model.boards = [board] if model.status == WAITING_TO_START: model.status, model.reason = getStatus(model.boards[-1]) return model
def _searchNow (self, ponderhit=False): log.debug("_searchNow: self.needBestmove=%s ponderhit=%s self.board=%s\n" % \ (self.needBestmove, ponderhit, self.board), self.defname) with self.moveLock: commands = [] if ponderhit: commands.append("ponderhit") elif self.mode == NORMAL: commands.append("position fen %s" % self.board.asFen()) if self.strength <= 3: commands.append("go depth %d" % self.strength) else: commands.append("go wtime %d btime %d winc %d binc %d" % \ (self.wtime, self.btime, self.incr, self.incr)) else: if self.mode == INVERSE_ANALYZING: if self.board.board.opIsChecked(): # Many engines don't like positions able to take down enemy # king. Therefore we just return the "kill king" move # automaticaly self.emit("analyze", [getMoveKillingKing(self.board)], MATE_VALUE-1) return print >> self.engine, "stop" if self.board.asFen() == FEN_START: commands.append("position startpos") else: commands.append("position fen %s" % self.board.asXFen()) commands.append("go infinite") if self.needBestmove: self.commands.append(commands) log.debug("_searchNow: self.needBestmove==True, appended to self.commands=%s\n" % \ self.commands, self.defname) else: for command in commands: print >> self.engine, command if self.board.asFen() != FEN_START and getStatus(self.board)[1] != WON_MATE: self.needBestmove = True self.readyForStop = True
def test_getstatus3(self): """Testing possible to mate with the queen unaided in Atomic variant""" board = AtomicBoard(setup=FEN4) print(board) self.assertEqual(getStatus(board), (WHITEWON, WON_MATE))
def loadToModel(self, rec, position=-1, model=None): """ Parse game text and load game record header tags to a GameModel object """ if model is None: model = GameModel() if self.pgn_is_string: rec = self.games[0] # Load mandatory tags for tag in mandatory_tags: model.tags[tag] = rec[tag] # Load other tags for tag in ('WhiteElo', 'BlackElo', 'ECO', 'TimeControl', 'Annotator'): model.tags[tag] = rec[tag] if self.pgn_is_string: for tag in rec: if isinstance(rec[tag], str) and rec[tag]: model.tags[tag] = rec[tag] else: model.info = self.tag_database.get_info(rec) extra_tags = self.tag_database.get_exta_tags(rec) for et in extra_tags: model.tags[et['tag_name']] = et['tag_value'] if self.pgn_is_string: variant = rec["Variant"].capitalize() else: variant = self.get_variant(rec) if model.tags['TimeControl']: tc = parseTimeControlTag(model.tags['TimeControl']) if tc is not None: secs, gain, moves = tc model.timed = True model.timemodel.secs = secs model.timemodel.gain = gain model.timemodel.minutes = secs / 60 model.timemodel.moves = moves for tag, color in (('WhiteClock', WHITE), ('BlackClock', BLACK)): if tag in model.tags: try: millisec = parseClockTimeTag(model.tags[tag]) # We need to fix when FICS reports negative clock time like this # [TimeControl "180+0"] # [WhiteClock "0:00:15.867"] # [BlackClock "23:59:58.820"] start_sec = ( millisec - 24 * 60 * 60 * 1000 ) / 1000. if millisec > 23 * 60 * 60 * 1000 else millisec / 1000. model.timemodel.intervals[color][0] = start_sec except ValueError: raise LoadingError("Error parsing '%s'" % tag) fenstr = rec["FEN"] if variant: if variant not in name2variant: raise LoadingError("Unknown variant %s" % variant) model.tags["Variant"] = variant # Fixes for some non statndard Chess960 .pgn if (fenstr is not None) and variant == "Fischerandom": parts = fenstr.split() parts[0] = parts[0].replace(".", "/").replace("0", "") if len(parts) == 1: parts.append("w") parts.append("-") parts.append("-") fenstr = " ".join(parts) model.variant = name2variant[variant] board = LBoard(model.variant.variant) else: model.variant = NormalBoard board = LBoard() if fenstr: try: board.applyFen(fenstr) model.tags["FEN"] = fenstr except SyntaxError as err: board.applyFen(FEN_EMPTY) raise LoadingError( _("The game can't be loaded, because of an error parsing FEN" ), err.args[0]) else: board.applyFen(FEN_START) boards = [board] del model.moves[:] del model.variations[:] self.error = None movetext = self.get_movetext(rec) boards = self.parse_movetext(movetext, boards[0], position) # The parser built a tree of lboard objects, now we have to # create the high level Board and Move lists... for board in boards: if board.lastMove is not None: model.moves.append(Move(board.lastMove)) self.has_emt = False self.has_eval = False def _create_board(model, node): if node.prev is None: # initial game board board = model.variant(setup=node.asFen(), lboard=node) else: move = Move(node.lastMove) try: board = node.prev.pieceBoard.move(move, lboard=node) except Exception: raise LoadingError( _("Invalid move."), "%s%s" % (move_count(node, black_periods=True), move)) return board def walk(model, node, path): boards = path stack = [] current = node while current is not None: board = _create_board(model, current) boards.append(board) stack.append(current) current = current.next else: model.variations.append(list(boards)) while stack: current = stack.pop() boards.pop() for child in current.children: if isinstance(child, list): if len(child) > 1: # non empty variation, go walk walk(model, child[1], list(boards)) else: if not self.has_emt: self.has_emt = child.find("%emt") >= 0 if not self.has_eval: self.has_eval = child.find("%eval") >= 0 # Collect all variation paths into a list of board lists # where the first one will be the boards of mainline game. # model.boards will allways point to the current shown variation # which will be model.variations[0] when we are in the mainline. walk(model, boards[0], []) model.boards = model.variations[0] self.has_emt = self.has_emt and model.timed if self.has_emt or self.has_eval: if self.has_emt: blacks = len(model.moves) // 2 whites = len(model.moves) - blacks model.timemodel.intervals = [ [model.timemodel.intervals[0][0]] * (whites + 1), [model.timemodel.intervals[1][0]] * (blacks + 1), ] model.timemodel.intervals[0][0] = secs model.timemodel.intervals[1][0] = secs for ply, board in enumerate(boards): for child in board.children: if isinstance(child, str): if self.has_emt: match = move_time_re.search(child) if match: movecount, color = divmod(ply + 1, 2) hour, minute, sec, msec = match.groups() prev = model.timemodel.intervals[color][ movecount - 1] hour = 0 if hour is None else int(hour[:-1]) minute = 0 if minute is None else int( minute[:-1]) msec = 0 if msec is None else int(msec) msec += int(sec) * 1000 + int( minute) * 60 * 1000 + int( hour) * 60 * 60 * 1000 model.timemodel.intervals[color][ movecount] = prev - msec / 1000. + gain if self.has_eval: match = move_eval_re.search(child) if match: sign, num, fraction, depth = match.groups() sign = 1 if sign is None or sign == "+" else -1 num = int(num) fraction = 0 if fraction is None else int( fraction) value = sign * (num * 100 + fraction) depth = "" if depth is None else depth if board.color == BLACK: value = -value model.scores[ply] = ("", value, depth) log.debug("pgn.loadToModel: intervals %s" % model.timemodel.intervals) # Find the physical status of the game model.status, model.reason = getStatus(model.boards[-1]) # Apply result from .pgn if the last position was loaded if position == -1 or len(model.moves) == position - model.lowply: if self.pgn_is_string: result = rec["Result"] if result in pgn2Const: status = pgn2Const[result] else: status = RUNNING else: status = rec["Result"] if status in (WHITEWON, BLACKWON) and status != model.status: model.status = status model.reason = WON_RESIGN elif status == DRAW and status != model.status: model.status = DRAW model.reason = DRAW_AGREE if model.timed: model.timemodel.movingColor = model.boards[-1].color # If parsing gave an error we throw it now, to enlarge our possibility # of being able to continue the game from where it failed. if self.error: raise self.error return model
def loadToModel(self, gameno, position=-1, model=None): if not model: model = GameModel() # the seven mandatory PGN headers model.tags['Event'] = self._getTag(gameno, 'Event') model.tags['Site'] = self._getTag(gameno, 'Site') model.tags['Date'] = self._getTag(gameno, 'Date') model.tags['Round'] = self.get_round(gameno) model.tags['White'], model.tags['Black'] = self.get_player_names( gameno) model.tags['Result'] = reprResult[self.get_result(gameno)] if model.tags['Date']: date_match = re.match(".*(\d{4}).(\d{2}).(\d{2}).*", model.tags['Date']) if date_match: year, month, day = date_match.groups() model.tags['Year'] = year model.tags['Month'] = month model.tags['Day'] = day # non-mandatory headers for tag in ('Annotator', 'ECO', 'WhiteElo', 'BlackElo', 'TimeControl'): if self._getTag(gameno, tag): model.tags[tag] = self._getTag(gameno, tag) else: model.tags[tag] = "" model.info = self.get_info(gameno) # TODO: enable this when NewGameDialog is altered to give user option of # whether to use PGN's clock time, or their own custom time. Also, # dialog should set+insensitize variant based on the variant of the # game selected in the dialog if model.tags['TimeControl']: secs, gain = parseTimeControlTag(model.tags['TimeControl']) model.timed = True model.timemodel.secs = secs model.timemodel.gain = gain model.timemodel.minutes = secs / 60 for tag, color in (('WhiteClock', WHITE), ('BlackClock', BLACK)): if self._getTag(gameno, tag): try: millisec = parseClockTimeTag(self._getTag(gameno, tag)) # We need to fix when FICS reports negative clock time like this # [TimeControl "180+0"] # [WhiteClock "0:00:15.867"] # [BlackClock "23:59:58.820"] start_sec = ( millisec - 24 * 60 * 60 * 1000 ) / 1000. if millisec > 23 * 60 * 60 * 1000 else millisec / 1000. model.timemodel.intervals[color][0] = start_sec except ValueError: raise LoadingError( "Error parsing '%s' Header for gameno %s" % (tag, gameno)) fenstr = self._getTag(gameno, "FEN") variant = self.get_variant(gameno) if variant: if variant not in name2variant: raise LoadingError("Unknown variant %s for gameno %s" % (variant, gameno)) model.tags["Variant"] = variant # Fixes for some non statndard Chess960 .pgn if (fenstr is not None) and variant == "Fischerandom": parts = fenstr.split() parts[0] = parts[0].replace(".", "/").replace("0", "") if len(parts) == 1: parts.append("w") parts.append("-") parts.append("-") fenstr = " ".join(parts) model.variant = name2variant[variant] board = LBoard(model.variant.variant) else: model.variant = NormalBoard board = LBoard() if fenstr: try: board.applyFen(fenstr) except SyntaxError as err: board.applyFen(FEN_EMPTY) raise LoadingError( _("The game can't be loaded, because of an error parsing FEN" ), err.args[0]) else: board.applyFen(FEN_START) boards = [board] del model.moves[:] del model.variations[:] self.error = None movetext = self.get_movetext(gameno) boards = self.parse_movetext(movetext, boards[0], position) # The parser built a tree of lboard objects, now we have to # create the high level Board and Move lists... for board in boards: if board.lastMove is not None: model.moves.append(Move(board.lastMove)) self.has_emt = False self.has_eval = False def walk(model, node, path): if node.prev is None: # initial game board board = model.variant(setup=node.asFen(), lboard=node) else: move = Move(node.lastMove) try: board = node.prev.pieceBoard.move(move, lboard=node) except: raise LoadingError( _("Invalid move."), "%s%s" % (move_count(node, black_periods=True), move)) if node.next is None: model.variations.append(path + [board]) else: walk(model, node.next, path + [board]) for child in node.children: if isinstance(child, list): if len(child) > 1: # non empty variation, go walk walk(model, child[1], list(path)) else: if not self.has_emt: self.has_emt = child.find("%emt") >= 0 if not self.has_eval: self.has_eval = child.find("%eval") >= 0 # Collect all variation paths into a list of board lists # where the first one will be the boards of mainline game. # model.boards will allways point to the current shown variation # which will be model.variations[0] when we are in the mainline. walk(model, boards[0], []) model.boards = model.variations[0] self.has_emt = self.has_emt and "TimeControl" in model.tags if self.has_emt or self.has_eval: if self.has_emt: blacks = len(model.moves) // 2 whites = len(model.moves) - blacks model.timemodel.intervals = [ [model.timemodel.intervals[0][0]] * (whites + 1), [model.timemodel.intervals[1][0]] * (blacks + 1), ] secs, gain = parseTimeControlTag(model.tags['TimeControl']) model.timemodel.intervals[0][0] = secs model.timemodel.intervals[1][0] = secs for ply, board in enumerate(boards): for child in board.children: if isinstance(child, basestring): if self.has_emt: match = movetime.search(child) if match: movecount, color = divmod(ply + 1, 2) hour, minute, sec, msec = match.groups() prev = model.timemodel.intervals[color][ movecount - 1] hour = 0 if hour is None else int(hour[:-1]) minute = 0 if minute is None else int( minute[:-1]) msec = 0 if msec is None else int(msec) msec += int(sec) * 1000 + int( minute) * 60 * 1000 + int( hour) * 60 * 60 * 1000 model.timemodel.intervals[color][ movecount] = prev - msec / 1000. + gain if self.has_eval: match = moveeval.search(child) if match: sign, num, fraction, depth = match.groups() sign = 1 if sign is None or sign == "+" else -1 num = int(num) if int( num) == MATE_VALUE else int(num) fraction = 0 if fraction is None else int( fraction) value = sign * (num * 100 + fraction) depth = "" if depth is None else depth if board.color == BLACK: value = -value model.scores[ply] = ("", value, depth) log.debug("pgn.loadToModel: intervals %s" % model.timemodel.intervals) # Find the physical status of the game model.status, model.reason = getStatus(model.boards[-1]) # Apply result from .pgn if the last position was loaded if position == -1 or len(model.moves) == position - model.lowply: status = self.get_result(gameno) if status in (WHITEWON, BLACKWON) and status != model.status: model.status = status model.reason = WON_RESIGN elif status == DRAW and status != model.status: model.status = DRAW model.reason = DRAW_AGREE # If parsing gave an error we throw it now, to enlarge our possibility # of being able to continue the game from where it failed. if self.error: raise self.error return model
if model.timemodel: if quick_parse: blacks = len(movstrs)/2 whites = len(movstrs)-blacks else: blacks = len(model.moves)/2 whites = len(model.moves)-blacks model.timemodel.intervals = [ [model.timemodel.intervals[0][0]]*(whites+1), [model.timemodel.intervals[1][0]]*(blacks+1), ] log.debug("intervals %s\n" % model.timemodel.intervals) if model.status == WAITING_TO_START: model.status, model.reason = getStatus(model.boards[-1]) model.status = self.get_result(gameno) if error: raise error return model def _getTag (self, gameno, tagkey): if gameno in self.tagcache: if tagkey in self.tagcache[gameno]: return self.tagcache[gameno][tagkey] else: return None else: if self.games: self.tagcache[gameno] = dict(tagre.findall(self.games[gameno][0]))
def loadToModel(self, gameno, position=-1, model=None): if not model: model = GameModel() # the seven mandatory PGN headers model.tags['Event'] = self._getTag(gameno, 'Event') model.tags['Site'] = self._getTag(gameno, 'Site') model.tags['Date'] = self._getTag(gameno, 'Date') model.tags['Round'] = self.get_round(gameno) model.tags['White'], model.tags['Black'] = self.get_player_names( gameno) model.tags['Result'] = reprResult[self.get_result(gameno)] pgnHasYearMonthDay = True for tag in ('Year', 'Month', 'Day'): if not self._getTag(gameno, tag): pgnHasYearMonthDay = False break if model.tags['Date'] and not pgnHasYearMonthDay: date_match = re.match(".*(\d{4}).(\d{2}).(\d{2}).*", model.tags['Date']) if date_match: year, month, day = date_match.groups() model.tags['Year'] = year model.tags['Month'] = month model.tags['Day'] = day # non-mandatory headers for tag in ('Annotator', 'ECO', 'EventDate', 'Time', 'WhiteElo', 'BlackElo', 'TimeControl'): if self._getTag(gameno, tag): model.tags[tag] = self._getTag(gameno, tag) # TODO: enable this when NewGameDialog is altered to give user option of # whether to use PGN's clock time, or their own custom time. Also, # dialog should set+insensitize variant based on the variant of the # game selected in the dialog # if model.timed: # for tag, color in (('WhiteClock', WHITE), ('BlackClock', BLACK)): # if self._getTag(gameno, tag): # try: # ms = parseClockTimeTag(self._getTag(gameno, tag)) # model.timemodel.intervals[color][0] = ms / 1000 # except ValueError: # raise LoadingError( \ # "Error parsing '%s' Header for gameno %s" % (tag, gameno)) # if model.tags['TimeControl']: # minutes, gain = parseTimeControlTag(model.tags['TimeControl']) # model.timemodel.minutes = minutes # model.timemodel.gain = gain fenstr = self._getTag(gameno, "FEN") variant = self.get_variant(gameno) if variant: # Fixes for some non statndard Chess960 .pgn if (fenstr is not None) and variant == "Fischerandom": model.tags["Variant"] = "Fischerandom" parts = fenstr.split() parts[0] = parts[0].replace(".", "/").replace("0", "") if len(parts) == 1: parts.append("w") parts.append("-") parts.append("-") fenstr = " ".join(parts) elif variant == "Atomic": model.tags["Variant"] = "Atomic" elif variant == "Crazyhouse": model.tags["Variant"] = "Crazyhouse" elif variant == "Wildcastle": model.tags["Variant"] = "Wildcastle" elif variant == "Suicide": model.tags["Variant"] = "Suicide" elif variant == "Losers": model.tags["Variant"] = "Losers" elif variant == "Kingofthehill": model.tags["Variant"] = "Kingofthehill" if variant == "Fischerandom": board = LBoard(FISCHERRANDOMCHESS) model.variant = FischerRandomChess elif variant == "Atomic": board = LBoard(ATOMICCHESS) model.variant = AtomicChess elif variant == "Crazyhouse": board = LBoard(CRAZYHOUSECHESS) model.variant = CrazyhouseChess elif variant == "Wildcastle": board = LBoard(WILDCASTLECHESS) model.variant = WildcastleChess elif variant == "Suicide": board = LBoard(SUICIDECHESS) model.variant = SuicideChess elif variant == "Losers": board = LBoard(LOSERSCHESS) model.variant = LosersChess elif variant == "Kingofthehill": board = LBoard(KINGOFTHEHILLCHESS) model.variant = KingOfTheHillChess else: board = LBoard() if fenstr: try: board.applyFen(fenstr) except SyntaxError as e: board.applyFen(FEN_EMPTY) raise LoadingError( _("The game can't be loaded, because of an error parsing FEN" ), e.args[0]) else: board.applyFen(FEN_START) boards = [board] del model.moves[:] del model.variations[:] self.error = None movetext = self.get_movetext(gameno) boards = self.parse_string(movetext, boards[0], position) # The parser built a tree of lboard objects, now we have to # create the high level Board and Move lists... for board in boards: if board.lastMove is not None: model.moves.append(Move(board.lastMove)) self.has_emt = False self.has_eval = False def walk(node, path): if node.prev is None: # initial game board if variant == "Fischerandom": board = FRCBoard(setup=node.asFen(), lboard=node) elif variant == "Atomic": board = AtomicBoard(setup=node.asFen(), lboard=node) elif variant == "Crazyhouse": board = CrazyhouseBoard(setup=node.asFen(), lboard=node) elif variant == "Wildcastle": board = WildcastleBoard(setup=node.asFen(), lboard=node) elif variant == "Suicide": board = SuicideBoard(setup=node.asFen(), lboard=node) elif variant == "Losers": board = LosersBoard(setup=node.asFen(), lboard=node) elif variant == "Kingofthehill": board = KingOfTheHillBoard(setup=node.asFen(), lboard=node) else: board = Board(setup=node.asFen(), lboard=node) else: move = Move(node.lastMove) try: board = node.prev.pieceBoard.move(move, lboard=node) except: raise LoadingError( _("Invalid move."), "%s%s" % (move_count(node, black_periods=True), move)) if node.next is None: model.variations.append(path + [board]) else: walk(node.next, path + [board]) for child in node.children: if isinstance(child, list): if len(child) > 1: # non empty variation, go walk walk(child[1], list(path)) else: if not self.has_emt: self.has_emt = child.find("%emt") >= 0 if not self.has_eval: self.has_eval = child.find("%eval") >= 0 # Collect all variation paths into a list of board lists # where the first one will be the boards of mainline game. # model.boards will allways point to the current shown variation # which will be model.variations[0] when we are in the mainline. walk(boards[0], []) model.boards = model.variations[0] self.has_emt = self.has_emt and "TimeControl" in model.tags if self.has_emt or self.has_eval: if self.has_emt: blacks = len(model.moves) // 2 whites = len(model.moves) - blacks model.timemodel.intervals = [ [model.timemodel.intervals[0][0]] * (whites + 1), [model.timemodel.intervals[1][0]] * (blacks + 1), ] secs, gain = parseTimeControlTag(model.tags['TimeControl']) model.timemodel.intervals[0][0] = secs model.timemodel.intervals[1][0] = secs for ply, board in enumerate(boards): for child in board.children: if isinstance(child, basestring): if self.has_emt: match = movetime.search(child) if match: movecount, color = divmod(ply + 1, 2) hour, minute, sec, msec = match.groups() prev = model.timemodel.intervals[color][ movecount - 1] msec = 0 if msec is None else int(msec) msec += int(sec) * 1000 + int( minute) * 60 * 1000 + int( hour) * 60 * 60 * 1000 model.timemodel.intervals[color][ movecount] = prev - msec / 1000 if self.has_eval: match = moveeval.search(child) if match: sign, num, fraction, depth = match.groups() sign = 1 if sign is None or sign == "+" else -1 num = int(num) if int( num) == MATE_VALUE else int(num) fraction = 0 if fraction is None else float( fraction) / 100 value = sign * (num + fraction) depth = "" if depth is None else depth model.scores[ply] = ("", value, depth) log.debug("pgn.loadToModel: intervals %s" % model.timemodel.intervals) # Find the physical status of the game model.status, model.reason = getStatus(model.boards[-1]) # Apply result from .pgn if the last position was loaded if position == -1 or len(model.moves) == position - model.lowply: status = self.get_result(gameno) if status in (WHITEWON, BLACKWON) and status != model.status: model.status = status model.reason = WON_RESIGN elif status == DRAW and status != model.status: model.status = DRAW model.reason = DRAW_AGREE # If parsing gave an error we throw it now, to enlarge our possibility # of being able to continue the game from where it failed. if self.error: raise self.error return model
def loadToModel (self, gameno, position=-1, model=None): if not model: model = GameModel() # the seven mandatory PGN headers model.tags['Event'] = self._getTag(gameno, 'Event') model.tags['Site'] = self._getTag(gameno, 'Site') model.tags['Date'] = self._getTag(gameno, 'Date') model.tags['Round'] = self.get_round(gameno) model.tags['White'], model.tags['Black'] = self.get_player_names(gameno) model.tags['Result'] = reprResult[self.get_result(gameno)] pgnHasYearMonthDay = True for tag in ('Year', 'Month', 'Day'): if not self._getTag(gameno, tag): pgnHasYearMonthDay = False break if model.tags['Date'] and not pgnHasYearMonthDay: date_match = re.match(".*(\d{4}).(\d{2}).(\d{2}).*", model.tags['Date']) if date_match: year, month, day = date_match.groups() model.tags['Year'] = year model.tags['Month'] = month model.tags['Day'] = day # non-mandatory headers for tag in ('Annotator', 'ECO', 'EventDate', 'Time', 'WhiteElo', 'BlackElo', 'TimeControl'): if self._getTag(gameno, tag): model.tags[tag] = self._getTag(gameno, tag) else: model.tags[tag] = "" # TODO: enable this when NewGameDialog is altered to give user option of # whether to use PGN's clock time, or their own custom time. Also, # dialog should set+insensitize variant based on the variant of the # game selected in the dialog if model.tags['TimeControl']: secs, gain = parseTimeControlTag(model.tags['TimeControl']) model.timed = True model.timemodel.secs = secs model.timemodel.gain = gain model.timemodel.minutes = secs / 60 for tag, color in (('WhiteClock', WHITE), ('BlackClock', BLACK)): if self._getTag(gameno, tag): try: ms = parseClockTimeTag(self._getTag(gameno, tag)) model.timemodel.intervals[color][0] = ms / 1000 except ValueError: raise LoadingError( \ "Error parsing '%s' Header for gameno %s" % (tag, gameno)) fenstr = self._getTag(gameno, "FEN") variant = self.get_variant(gameno) if variant: model.tags["Variant"] = variant # Fixes for some non statndard Chess960 .pgn if (fenstr is not None) and variant == "Fischerandom": parts = fenstr.split() parts[0] = parts[0].replace(".", "/").replace("0", "") if len(parts) == 1: parts.append("w") parts.append("-") parts.append("-") fenstr = " ".join(parts) model.variant = name2variant[variant] board = LBoard(model.variant.variant) else: model.variant = NormalBoard board = LBoard() if fenstr: try: board.applyFen(fenstr) except SyntaxError as e: board.applyFen(FEN_EMPTY) raise LoadingError(_("The game can't be loaded, because of an error parsing FEN"), e.args[0]) else: board.applyFen(FEN_START) boards = [board] del model.moves[:] del model.variations[:] self.error = None movetext = self.get_movetext(gameno) boards = self.parse_string(movetext, boards[0], position) # The parser built a tree of lboard objects, now we have to # create the high level Board and Move lists... for board in boards: if board.lastMove is not None: model.moves.append(Move(board.lastMove)) self.has_emt = False self.has_eval = False def walk(node, path): if node.prev is None: # initial game board board = model.variant(setup=node.asFen(), lboard=node) else: move = Move(node.lastMove) try: board = node.prev.pieceBoard.move(move, lboard=node) except: raise LoadingError(_("Invalid move."), "%s%s" % (move_count(node, black_periods=True), move)) if node.next is None: model.variations.append(path+[board]) else: walk(node.next, path+[board]) for child in node.children: if isinstance(child, list): if len(child) > 1: # non empty variation, go walk walk(child[1], list(path)) else: if not self.has_emt: self.has_emt = child.find("%emt") >= 0 if not self.has_eval: self.has_eval = child.find("%eval") >= 0 # Collect all variation paths into a list of board lists # where the first one will be the boards of mainline game. # model.boards will allways point to the current shown variation # which will be model.variations[0] when we are in the mainline. walk(boards[0], []) model.boards = model.variations[0] self.has_emt = self.has_emt and "TimeControl" in model.tags if self.has_emt or self.has_eval: if self.has_emt: blacks = len(model.moves)//2 whites = len(model.moves)-blacks model.timemodel.intervals = [ [model.timemodel.intervals[0][0]]*(whites+1), [model.timemodel.intervals[1][0]]*(blacks+1), ] secs, gain = parseTimeControlTag(model.tags['TimeControl']) model.timemodel.intervals[0][0] = secs model.timemodel.intervals[1][0] = secs for ply, board in enumerate(boards): for child in board.children: if isinstance(child, basestring): if self.has_emt: match = movetime.search(child) if match: movecount, color = divmod(ply+1, 2) hour, minute, sec, msec = match.groups() prev = model.timemodel.intervals[color][movecount-1] msec = 0 if msec is None else int(msec) msec += int(sec)*1000 + int(minute)*60*1000 + int(hour)*60*60*1000 model.timemodel.intervals[color][movecount] = prev - msec/1000 if self.has_eval: match = moveeval.search(child) if match: sign, num, fraction, depth = match.groups() sign = 1 if sign is None or sign == "+" else -1 num = int(num) if int(num) == MATE_VALUE else int(num) fraction = 0 if fraction is None else float(fraction)/100 value = sign * (num + fraction) depth = "" if depth is None else depth model.scores[ply] = ("", value, depth) log.debug("pgn.loadToModel: intervals %s" % model.timemodel.intervals) # Find the physical status of the game model.status, model.reason = getStatus(model.boards[-1]) # Apply result from .pgn if the last position was loaded if position == -1 or len(model.moves) == position - model.lowply: status = self.get_result(gameno) if status in (WHITEWON, BLACKWON) and status != model.status: model.status = status model.reason = WON_RESIGN elif status == DRAW and status != model.status: model.status = DRAW model.reason = DRAW_AGREE # If parsing gave an error we throw it now, to enlarge our possibility # of being able to continue the game from where it failed. if self.error: raise self.error return model
def loadToModel(self, rec, position=-1, model=None): """ Parse game text and load game record header tags to a GameModel object """ if not model: model = GameModel() if self.pgn_is_string: rec = self.games[0] game_date = rec["Date"] result = rec["Result"] variant = rec["Variant"] else: game_date = self.get_date(rec) result = reprResult[rec["Result"]] variant = self.get_variant(rec) # the seven mandatory PGN headers model.tags['Event'] = rec["Event"] model.tags['Site'] = rec["Site"] model.tags['Date'] = game_date model.tags['Round'] = rec["Round"] model.tags['White'] = rec["White"] model.tags['Black'] = rec["Black"] model.tags['Result'] = result if model.tags['Date']: date_match = re.match(".*(\d{4}).(\d{2}).(\d{2}).*", model.tags['Date']) if date_match: year, month, day = date_match.groups() model.tags['Year'] = year model.tags['Month'] = month model.tags['Day'] = day # non-mandatory tags for tag in ('Annotator', 'ECO', 'WhiteElo', 'BlackElo', 'TimeControl'): value = rec[tag] if value: model.tags[tag] = value else: model.tags[tag] = "" if not self.pgn_is_string: model.info = self.tag_database.get_info(rec) if model.tags['TimeControl']: secs, gain = parseTimeControlTag(model.tags['TimeControl']) model.timed = True model.timemodel.secs = secs model.timemodel.gain = gain model.timemodel.minutes = secs / 60 for tag, color in (('WhiteClock', WHITE), ('BlackClock', BLACK)): if hasattr(rec, tag): try: millisec = parseClockTimeTag(rec[tag]) # We need to fix when FICS reports negative clock time like this # [TimeControl "180+0"] # [WhiteClock "0:00:15.867"] # [BlackClock "23:59:58.820"] start_sec = ( millisec - 24 * 60 * 60 * 1000 ) / 1000. if millisec > 23 * 60 * 60 * 1000 else millisec / 1000. model.timemodel.intervals[color][0] = start_sec except ValueError: raise LoadingError( "Error parsing '%s'" % tag) fenstr = rec["FEN"] if variant: if variant not in name2variant: raise LoadingError("Unknown variant %s" % variant) model.tags["Variant"] = variant # Fixes for some non statndard Chess960 .pgn if (fenstr is not None) and variant == "Fischerandom": parts = fenstr.split() parts[0] = parts[0].replace(".", "/").replace("0", "") if len(parts) == 1: parts.append("w") parts.append("-") parts.append("-") fenstr = " ".join(parts) model.variant = name2variant[variant] board = LBoard(model.variant.variant) else: model.variant = NormalBoard board = LBoard() if fenstr: try: board.applyFen(fenstr) except SyntaxError as err: board.applyFen(FEN_EMPTY) raise LoadingError( _("The game can't be loaded, because of an error parsing FEN"), err.args[0]) else: board.applyFen(FEN_START) boards = [board] del model.moves[:] del model.variations[:] self.error = None movetext = self.get_movetext(rec) boards = self.parse_movetext(movetext, boards[0], position) # The parser built a tree of lboard objects, now we have to # create the high level Board and Move lists... for board in boards: if board.lastMove is not None: model.moves.append(Move(board.lastMove)) self.has_emt = False self.has_eval = False def walk(model, node, path): if node.prev is None: # initial game board board = model.variant(setup=node.asFen(), lboard=node) else: move = Move(node.lastMove) try: board = node.prev.pieceBoard.move(move, lboard=node) except: raise LoadingError( _("Invalid move."), "%s%s" % (move_count(node, black_periods=True), move)) if node.next is None: model.variations.append(path + [board]) else: walk(model, node.next, path + [board]) for child in node.children: if isinstance(child, list): if len(child) > 1: # non empty variation, go walk walk(model, child[1], list(path)) else: if not self.has_emt: self.has_emt = child.find("%emt") >= 0 if not self.has_eval: self.has_eval = child.find("%eval") >= 0 # Collect all variation paths into a list of board lists # where the first one will be the boards of mainline game. # model.boards will allways point to the current shown variation # which will be model.variations[0] when we are in the mainline. walk(model, boards[0], []) model.boards = model.variations[0] self.has_emt = self.has_emt and "TimeControl" in model.tags if self.has_emt or self.has_eval: if self.has_emt: blacks = len(model.moves) // 2 whites = len(model.moves) - blacks model.timemodel.intervals = [ [model.timemodel.intervals[0][0]] * (whites + 1), [model.timemodel.intervals[1][0]] * (blacks + 1), ] secs, gain = parseTimeControlTag(model.tags['TimeControl']) model.timemodel.intervals[0][0] = secs model.timemodel.intervals[1][0] = secs for ply, board in enumerate(boards): for child in board.children: if isinstance(child, str): if self.has_emt: match = movetime.search(child) if match: movecount, color = divmod(ply + 1, 2) hour, minute, sec, msec = match.groups() prev = model.timemodel.intervals[color][ movecount - 1] hour = 0 if hour is None else int(hour[:-1]) minute = 0 if minute is None else int(minute[:-1]) msec = 0 if msec is None else int(msec) msec += int(sec) * 1000 + int( minute) * 60 * 1000 + int( hour) * 60 * 60 * 1000 model.timemodel.intervals[color][ movecount] = prev - msec / 1000. + gain if self.has_eval: match = moveeval.search(child) if match: sign, num, fraction, depth = match.groups() sign = 1 if sign is None or sign == "+" else -1 num = int(num) if int( num) == MATE_VALUE else int(num) fraction = 0 if fraction is None else int( fraction) value = sign * (num * 100 + fraction) depth = "" if depth is None else depth if board.color == BLACK: value = -value model.scores[ply] = ("", value, depth) log.debug("pgn.loadToModel: intervals %s" % model.timemodel.intervals) # Find the physical status of the game model.status, model.reason = getStatus(model.boards[-1]) # Apply result from .pgn if the last position was loaded if position == -1 or len(model.moves) == position - model.lowply: status = rec["Result"] if status in (WHITEWON, BLACKWON) and status != model.status: model.status = status model.reason = WON_RESIGN elif status == DRAW and status != model.status: model.status = DRAW model.reason = DRAW_AGREE # If parsing gave an error we throw it now, to enlarge our possibility # of being able to continue the game from where it failed. if self.error: raise self.error return model
if model.timemodel: if quick_parse: blacks = len(movstrs) / 2 whites = len(movstrs) - blacks else: blacks = len(model.moves) / 2 whites = len(model.moves) - blacks model.timemodel.intervals = [ [model.timemodel.intervals[0][0]] * (whites + 1), [model.timemodel.intervals[1][0]] * (blacks + 1), ] log.debug("intervals %s\n" % model.timemodel.intervals) if model.status == WAITING_TO_START: model.status, model.reason = getStatus(model.boards[-1]) model.status = self.get_result(gameno) if error: raise error return model def _getTag(self, gameno, tagkey): if gameno in self.tagcache: if tagkey in self.tagcache[gameno]: return self.tagcache[gameno][tagkey] else: return None else: if self.games: