def accept(self, matched, final): """ Called to inform the navigator of a match and whether it is a final word """ # pylint: disable=bad-continuation if (final and len(matched) > 1 and (self._index >= Board.SIZE or self._axis.is_empty(self._index))): # Solution found - make a Move object for it and add it to the AutoPlayer's list ix = self._index - len( matched) # The word's starting index within the axis row, col = self._axis.coordinate_of(ix) xd, yd = self._axis.coordinate_step() move = Move(matched, row, col, self._axis.is_horizontal()) # Fetch the rack as it was at the beginning of move generation autoplayer = self._axis.autoplayer rack = autoplayer.rack() tiles = u"" for c in matched: if self._axis.is_empty(ix): # Empty square that is being covered by this move # Find out whether it is a blank or normal letter tile if c in rack: rack = rack.replace(c, u"", 1) tile = c tiles += c else: # Must be a wildcard match rack = rack.replace(u"?", u"", 1) tile = u"?" tiles += tile + c # assert row in range(Board.SIZE) # assert col in range(Board.SIZE) # Add this cover to the Move object move.add_validated_cover(Cover(row, col, tile, c)) else: tiles += c ix += 1 row += xd col += yd # Note the tiles played in the move move.set_tiles(tiles) # Check that we've picked off the correct number of tiles # assert len(rack) == len(self._rack) autoplayer.add_candidate(move)
def test_move(state, movestring): """ Test placing a simple tile move """ coord, word = movestring.split(u' ') rowid = Board.ROWIDS xd, yd = 0, 0 horiz = True if coord[0] in rowid: row = rowid.index(coord[0]) col = int(coord[1:]) - 1 yd = 1 else: row = rowid.index(coord[-1]) col = int(coord[0:-1]) - 1 xd = 1 horiz = False move = Move(word, row, col, horiz) next_is_blank = False for c in word: if c == u'?': next_is_blank = True continue if not state.board().is_covered(row, col): move.add_cover(row, col, u'?' if next_is_blank else c, c) next_is_blank = False row += xd col += yd legal = state.check_legality(move) msg = "" if isinstance(legal, tuple): legal, msg = legal if legal != Error.LEGAL: print(u"Play is not legal, code {0} {1}".format( Error.errortext(legal), msg)) return False print(u"Play {0} is legal and scores {1} points".format( move, state.score(move))) state.apply_move(move) print(state.__str__()) return True
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