def load(cls, uuid, username): """ Load an already existing game from persistent storage """ gm = GameModel.fetch(uuid) if gm is None: # A game with this uuid is not found in the database: give up return None # Initialize a new Game instance with a pre-existing uuid game = cls(uuid) game.username = username game.state = State(drawtiles = False) if gm.player0 is None: # Player 0 is an Autoplayer game.player_index = 1 # Human (local) player is 1 else: assert gm.player1 is None game.player_index = 0 # Human (local) player is 0 game.state.set_player_name(game.player_index, username) game.state.set_player_name(1 - game.player_index, u"Netskrafl") # Load the current racks game.state._racks[0].set_tiles(gm.rack0) game.state._racks[1].set_tiles(gm.rack1) # Process the moves player = 0 for mm in gm.moves: m = None if mm.coord: # Normal tile move # Decode the coordinate: A15 = horizontal, 15A = vertical if mm.coord[0] in Board.ROWIDS: row = Board.ROWIDS.index(mm.coord[0]) col = int(mm.coord[1:]) - 1 horiz = True else: row = Board.ROWIDS.index(mm.coord[-1]) col = int(mm.coord[0:-1]) - 1 horiz = False # The tiles string may contain wildcards followed by their meaning # Remove the ? marks to get the "plain" word formed m = Move(mm.tiles.replace(u'?', u''), row, col, horiz) m.make_covers(game.state.board(), mm.tiles) elif mm.tiles[0:4] == u"EXCH": # Exchange move m = ExchangeMove(mm.tiles[5:]) elif mm.tiles == u"PASS": # Pass move m = PassMove() elif mm.tiles == u"RSGN": # Game resigned m = ResignMove(- mm.score) assert m is not None if m: # Do a "shallow apply" of the move, which updates # the board and internal state variables but does # not modify the bag or the racks game.state.apply_move(m, True) # Append to the move history game.moves.append((player, m)) player = 1 - player # If the moves were correctly applied, the scores should match assert game.state._scores[0] == gm.score0 assert game.state._scores[1] == gm.score1 # Find out what tiles are now in the bag game.state.recalc_bag() # Cache the game so it can be looked up by user id user = User.current() if user is not None: Game._cache[user.id()] = game return game
def _load_locked(cls, uuid, use_cache = True): """ Load an existing game from cache or persistent storage under lock """ gm = GameModel.fetch(uuid, use_cache) if gm is None: # A game with this uuid is not found in the database: give up return None # Initialize a new Game instance with a pre-existing uuid game = cls(uuid) # Set the timestamps game.timestamp = gm.timestamp game.ts_last_move = gm.ts_last_move if game.ts_last_move is None: # If no last move timestamp, default to the start of the game game.ts_last_move = game.timestamp # Initialize the preferences game._preferences = gm.prefs # Initialize a fresh, empty state with no tiles drawn into the racks game.state = State(drawtiles = False) # A player_id of None means that the player is an autoplayer (robot) game.player_ids[0] = None if gm.player0 is None else gm.player0.id() game.player_ids[1] = None if gm.player1 is None else gm.player1.id() game.robot_level = gm.robot_level # Load the initial racks game.initial_racks[0] = gm.irack0 game.initial_racks[1] = gm.irack1 # Load the current racks game.state.set_rack(0, gm.rack0) game.state.set_rack(1, gm.rack1) # Process the moves player = 0 # mx = 0 # Move counter for debugging/logging for mm in gm.moves: # mx += 1 # logging.info(u"Game move {0} tiles '{3}' score is {1}:{2}".format(mx, game.state._scores[0], game.state._scores[1], mm.tiles).encode("latin-1")) m = None if mm.coord: # Normal tile move # Decode the coordinate: A15 = horizontal, 15A = vertical if mm.coord[0] in Board.ROWIDS: row = Board.ROWIDS.index(mm.coord[0]) col = int(mm.coord[1:]) - 1 horiz = True else: row = Board.ROWIDS.index(mm.coord[-1]) col = int(mm.coord[0:-1]) - 1 horiz = False # The tiles string may contain wildcards followed by their meaning # Remove the ? marks to get the "plain" word formed if mm.tiles is not None: m = Move(mm.tiles.replace(u'?', u''), row, col, horiz) m.make_covers(game.state.board(), mm.tiles) elif mm.tiles[0:4] == u"EXCH": # Exchange move m = ExchangeMove(mm.tiles[5:]) elif mm.tiles == u"PASS": # Pass move m = PassMove() elif mm.tiles == u"RSGN": # Game resigned m = ResignMove(- mm.score) assert m is not None if m: # Do a "shallow apply" of the move, which updates # the board and internal state variables but does # not modify the bag or the racks game.state.apply_move(m, True) # Append to the move history game.moves.append(MoveTuple(player, m, mm.rack, mm.timestamp)) player = 1 - player # Find out what tiles are now in the bag game.state.recalc_bag() # Account for the final tiles in the rack and overtime, if any if game.is_over(): game.finalize_score() if not gm.over: # The game was not marked as over when we loaded it from # the datastore, but it is over now. One of the players must # have lost on overtime. We need to update the persistent state. game._store_locked() return game
def _load_locked(cls, uuid, use_cache=True): """ Load an existing game from cache or persistent storage under lock """ gm = GameModel.fetch(uuid, use_cache) if gm is None: # A game with this uuid is not found in the database: give up return None # Initialize a new Game instance with a pre-existing uuid game = cls(uuid) # Set the timestamps game.timestamp = gm.timestamp game.ts_last_move = gm.ts_last_move if game.ts_last_move is None: # If no last move timestamp, default to the start of the game game.ts_last_move = game.timestamp # Initialize the preferences game._preferences = gm.prefs # Initialize a fresh, empty state with no tiles drawn into the racks game.state = State(drawtiles=False, tileset=game.tileset) # A player_id of None means that the player is an autoplayer (robot) game.player_ids[0] = None if gm.player0 is None else gm.player0.id() game.player_ids[1] = None if gm.player1 is None else gm.player1.id() game.robot_level = gm.robot_level # Load the initial racks game.initial_racks[0] = gm.irack0 game.initial_racks[1] = gm.irack1 # Load the current racks game.state.set_rack(0, gm.rack0) game.state.set_rack(1, gm.rack1) # Process the moves player = 0 # mx = 0 # Move counter for debugging/logging for mm in gm.moves: # mx += 1 # logging.info(u"Game move {0} tiles '{3}' score is {1}:{2}".format(mx, game.state._scores[0], game.state._scores[1], mm.tiles).encode("latin-1")) m = None if mm.coord: # Normal tile move # Decode the coordinate: A15 = horizontal, 15A = vertical if mm.coord[0] in Board.ROWIDS: row = Board.ROWIDS.index(mm.coord[0]) col = int(mm.coord[1:]) - 1 horiz = True else: row = Board.ROWIDS.index(mm.coord[-1]) col = int(mm.coord[0:-1]) - 1 horiz = False # The tiles string may contain wildcards followed by their meaning # Remove the ? marks to get the "plain" word formed if mm.tiles is not None: m = Move(mm.tiles.replace(u'?', u''), row, col, horiz) m.make_covers(game.state.board(), mm.tiles) elif mm.tiles[0:4] == u"EXCH": # Exchange move m = ExchangeMove(mm.tiles[5:]) elif mm.tiles == u"PASS": # Pass move m = PassMove() elif mm.tiles == u"RSGN": # Game resigned m = ResignMove(-mm.score) assert m is not None if m: # Do a "shallow apply" of the move, which updates # the board and internal state variables but does # not modify the bag or the racks game.state.apply_move(m, True) # Append to the move history game.moves.append(MoveTuple(player, m, mm.rack, mm.timestamp)) player = 1 - player # Find out what tiles are now in the bag game.state.recalc_bag() # Account for the final tiles in the rack and overtime, if any if game.is_over(): game.finalize_score() if not gm.over: # The game was not marked as over when we loaded it from # the datastore, but it is over now. One of the players must # have lost on overtime. We need to update the persistent state. game._store_locked() return game