def __init__(self, size=(6, 6), initial_tiles=4, goal_value=13, min_group=2, animation=True): """Constructor Parameters: size (tuple<int, int>): The number of (rows, columns) in the game. initial_tiles (int): The number of tiles. goal_value (int): The value of the goal tile. min_group (int): The minimum number of tiles required for a connected group to be joinable. animation (bool): If True, animation will be enabled. """ self.goal_value = goal_value self.initial_tiles = initial_tiles self._selector = WeightedSelector({1: 1}) self.reset() generator = tile_generators.WeightedGenerator(self._selector, self._construct_tile) super().__init__(size=size, min_group=min_group, animation=animation) rows, columns = size self.grid = model.LoloGrid(generator, rows=rows, columns=columns, animation=animation) self.grid.fill() self.generator = generator self._score = max(tile.get_value() for _, tile in self.grid.items())
def __init__(self, size=(6, 6), types=3, min_group=3, max_tile_value=50, max_tile_type='max', normal_weight=20, max_weight=2, animation=True, autofill=True): """Constructor Parameters: size (tuple<int, int>): The number of (rows, columns) in the game. types (int): The number of types of basic tiles. min_group (int): The minimum number of tiles required for a connected group to be joinable. normal_weight (int): The relative weighted probability that a basic tile will be generated. max_weight (int): The relative weighted probability that a maximum tile will be generated. animation (bool): If True, animation will be enabled. autofill (bool): Automatically fills the grid iff True. """ # Basic properties self.max_tile_value = max_tile_value self.max_tile_type = max_tile_type self.types = types self._max_unlocked = False # Tile probabilities self.normal_likelihood = normal_weight self.max_likelihood = max_weight weighted_types = {i: normal_weight for i in range(1, types + 1)} self._selector = WeightedSelector(weighted_types) generator = tile_generators.WeightedGenerator(self._selector, self._construct_tile) super().__init__(size, generator, min_group, animation=animation, autofill=autofill)
def __init__(self, dot_weights, kinds=(1, 2, 3), size=(6, 6), dead_cells=None, objectives: ObjectiveManager = None, min_group=2, moves=20, animation=True): """Constructor Parameters: dot_weights (dict<class, float>): The weighting for picking the class of dot to initiate kinds (set<int|str>): All possible kinds that a dot could be size (tuple<int, int>): The number of (rows, columns) in the game dead_cells (set<tuple<int, int>>): Set of cells that are disabled (i.e. VoidCells) objectives (ObjectiveManager): Objectives for the game min_group (int): The minimum number of dots required for a connected group to be joinable moves (int): The number of moves allowed before game over animation (bool): If True, animation will be enabled """ # Tile probabilities self.kind_selector = WeightedSelector.from_equals(set(kinds)) dot_selector = WeightedSelector(dot_weights) dot_factory = DotFactory(self.kind_selector, dot_selector) super().__init__(dot_factory, size=size, dead_cells=dead_cells, objectives=objectives, min_group=min_group, moves=moves, animation=animation)
class RegularGame(model.AbstractGame): """Regular game of Lolo. Join groups of three or more until max tiles are formed. Join max tiles to destroy all surrounding tiles.""" GAME_NAME = "Regular" def __init__(self, size=(6, 6), types=3, min_group=3, max_tile_value=50, max_tile_type='max', normal_weight=20, max_weight=2, animation=True, autofill=True): """Constructor Parameters: size (tuple<int, int>): The number of (rows, columns) in the game. types (int): The number of types of basic tiles. min_group (int): The minimum number of tiles required for a connected group to be joinable. normal_weight (int): The relative weighted probability that a basic tile will be generated. max_weight (int): The relative weighted probability that a maximum tile will be generated. animation (bool): If True, animation will be enabled. autofill (bool): Automatically fills the grid iff True. """ # Basic properties self.max_tile_value = max_tile_value self.max_tile_type = max_tile_type self.types = types self._max_unlocked = False # Tile probabilities self.normal_likelihood = normal_weight self.max_likelihood = max_weight weighted_types = {i: normal_weight for i in range(1, types + 1)} self._selector = WeightedSelector(weighted_types) generator = tile_generators.WeightedGenerator(self._selector, self._construct_tile) super().__init__(size, generator, min_group, animation=animation, autofill=autofill) def get_default_score(self): """(int) Returns the default score.""" return 0 def reset(self): """Resets the game.""" super().reset() self._lock_max() def _construct_tile(self, type, position, *args, **kwargs): """(RegularTile) Returns a new tile from the generator's selection. Parameters: type (*): The type of the tile. position (tuple<int, int>): The position the tile will initially exist in. Unused. *args: Extra positional arguments for the tile. **kwargs: Extra keyword arguments for the tile. """ return RegularTile(type, *args, max_value=self.max_tile_value, **kwargs) def _check_unlock_max(self, current): """Unlocks the max tile if the current tile is a max tile. Parameters: current (RegularTile): The current tile. """ if not self._max_unlocked and current.is_max(): self._selector.update({self.max_tile_type: self.max_likelihood}) self._max_unlocked = True def _lock_max(self): """Locks max tile.""" del self._selector[self.max_tile_type] self._max_unlocked = False def update_score_on_activate(self, current, connected): """Updates the score based upon the current tile & connected tiles that were joined to it. Parameter: current (RegularTile): The tile recently current to. connected (tuple<RegularTiles>): The tiles that were joined to current. """ factor = 50 if current.is_combo_max() else 1 points = (len(connected) + 1) * factor self.set_score(self.get_score() + points) def activate(self, position): """Attempts to activate the tile at the given position. Parameters: position (tuple<int, int>): The position to activate. Yield: Yields None for each frame of drops and "DONE" when the dropping has finished. """ connected_cells = self._attempt_activate_collect(position) connected_cells.remove(position) self._resolving = True current = self.grid[position] connected_tiles = [self.grid[cell] for cell in connected_cells] # Join tiles current.join(connected_tiles) self.update_score_on_activate(current, connected_tiles) self._check_unlock_max(current) for cell in connected_cells: del self.grid[cell] yield from self.grid.replace_blanks() # Find tile, in case it moved. # Hack, but it works. Ideally above logic would indicate movement. position = self.find_tile_position(current) # Perform combo yield from self._explode_combo(position) # Final step yield "DONE" self._resolving = False self.emit('resolve') # Check for game over. if self.game_over(): self.emit('game_over') def remove(self, *positions): """Attempts to remove the tiles at the given positions. Parameters: *positions (tuple<int, int>): The position to activate. Yield: Yields None for each frame of drops and "DONE" when the dropping has finished. """ self._resolving = True connected_cells = positions connected_tiles = [self.grid[cell] for cell in connected_cells] for cell in connected_cells: del self.grid[cell] yield from self.grid.replace_blanks() # Final step yield "DONE" self._resolving = False self.emit('resolve') # Check for game over. if self.game_over(): self.emit('game_over') def find_tile_position(self, tile): """(tuple<int, int>) Returns the row, column position of the tile if it exists in the game grid, else None.""" for position, a_tile in self.grid.items(): if a_tile is tile: return position return None def _explode_combo(self, position): """Internal helper method to check if the tile at a position is a combination maximum. If so, explodes it, deleting the tile and all surrounding tiles. Parameters: position (tuple<int, int>): Row, column position of the tile. """ current = self.grid[position] if current.is_combo_max(): yield "REMOVE" exploded_cells = self.grid.get_adjacent_cells( position, deltas=matrix.RADIAL_DELTAS) del self.grid[position] for cell in exploded_cells: tile = self.grid[cell] if tile is None or not tile.get_disabled(): del self.grid[cell] self.set_score(self.get_score() + current.get_value()) yield from self.grid.replace_blanks()
class Make13Game(game_regular.RegularGame): """Make13 Lolo game. Groups of two or more can be combined to increase tile's value by one. Game is won when a 13 is made. """ def __init__(self, size=(6, 6), initial_tiles=4, goal_value=13, min_group=2, animation=True): """Constructor Parameters: size (tuple<int, int>): The number of (rows, columns) in the game. initial_tiles (int): The number of tiles. goal_value (int): The value of the goal tile. min_group (int): The minimum number of tiles required for a connected group to be joinable. animation (bool): If True, animation will be enabled. """ self.goal_value = goal_value self.initial_tiles = initial_tiles self._selector = WeightedSelector({1: 1}) self.reset() generator = tile_generators.WeightedGenerator(self._selector, self._construct_tile) super().__init__(size=size, min_group=min_group, animation=animation) rows, columns = size self.grid = model.LoloGrid(generator, rows=rows, columns=columns, animation=animation) self.grid.fill() self.generator = generator self._score = max(tile.get_value() for _, tile in self.grid.items()) def reset(self): # super().reset() weights = {i: self.get_tile_weight(i) for i in range(1, self.initial_tiles + 1)} self._selector.update(weights, clear=True) def get_tile_weight(self, value): return 2 ** (self.goal_value - value) def _construct_tile(self, type, position): """(LevelTile) Returns a randomly generated tile.""" return LevelTile(type) def update_score_on_activate(self, current, connections): if current.get_value() > self._score: # Update score score = current.get_value() self._score = score # Unlock new tile self._selector[score] = self.get_tile_weight(score) self.increase_score(score) if current.get_value() == self.goal_value: self.emit('game_over')
class Make13Game(game_regular.RegularGame): """Make13 Lolo game. Groups of two or more can be combined to increase tile's value by one. Game is won when a 13 is made. """ GAME_NAME = "Make 13" def __init__(self, size=(6, 6), initial_tiles=4, goal_value=13, min_group=2, animation=True, autofill=True): """Constructor Parameters: size (tuple<int, int>): The number of (rows, columns) in the game. initial_tiles (int): The number of tiles. goal_value (int): The value of the goal tile. min_group (int): The minimum number of tiles required for a connected group to be joinable. animation (bool): If True, animation will be enabled. autofill (bool): Automatically fills the grid iff True. """ self.goal_value = goal_value self.initial_tiles = initial_tiles super().__init__(size=size, min_group=min_group, animation=animation, autofill=False) self._selector = WeightedSelector({1: 1}) self.reset() generator = tile_generators.WeightedGenerator(self._selector, self._construct_tile) rows, columns = size self.grid = model.LoloGrid(generator, rows=rows, columns=columns, animation=animation) if autofill: self.grid.fill() self._score = self.get_default_score() self.generator = generator def get_default_score(self): """(int) Returns the default score.""" return max(tile.get_value() for _, tile in self.grid.items()) def reset(self): """Resets the game.""" weights = { i: self.get_tile_weight(i) for i in range(1, self.initial_tiles + 1) } self._selector.update(weights, clear=True) super().reset() def get_tile_weight(self, value): """(float) Returns the weighting for a tile of given value.""" return 2**(self.goal_value - value) def _construct_tile(self, type, position, *args, **kwargs): """(LevelTile) Returns a new tile from the generator's selection. Parameters: type (*): The type of the tile. position (tuple<int, int>): The position the tile will initially exist in. Unused. *args: Extra positional arguments for the tile. **kwargs: Extra keyword arguments for the tile. """ # TODO: remove when serialize is implemented properly args = args[1:] return LevelTile(type, *args, **kwargs) def update_score_on_activate(self, current, connections): """Updates the score based upon the current tile & connected tiles that were joined to it. Parameter: current (AbstractTile): The tile recently current to. connected (tuple<AbstractTiles>): The tiles that were joined to current. """ if current.get_value() > self._score: # Update score score = current.get_value() self._score = score # Unlock new tile self._selector[score] = self.get_tile_weight(score) self.set_score(score) if current.get_value() == self.goal_value: self.emit('game_over')