def right_click(x, y, game): """ Set/increment/clear the flag in this cell. """ flag_map = DBSession.query(models.PlayerMap).filter_by( game_id=game.id, map_type=const.PlayerMapType.FLAG).first() # Get the FlagMapData, if it exists flag_data = DBSession.query(models.PlayerMapData).filter_by( player_map_id=flag_map.id, row_num=x, col_num=y).first() if flag_data is None: # UNCLICKED --> FLAG recorded_flag = models.PlayerMapData( player_map_id=flag_map.id, row_num=x, col_num=y, value=const.PlayerMapDataValue.FLAG) DBSession.add(recorded_flag) value = const.PlayerMapDataValue.FLAG else: if flag_data.value == const.PlayerMapDataValue.FLAG: # FLAG --> UNSURE value = const.PlayerMapDataValue.UNSURE flag_data.value = value DBSession.add(flag_data) else: # UNSURE --> UNCLICKED value = const.CellStates.UNCLICKED DBSession.delete(flag_data) DBSession.flush() return (value, game)
def view_game(request): """ Returns the game. """ game = get_game_or_none(request) if game is None: return HTTPNotFound('No such game.') # Get the MineMap mine_map = DBSession.query(models.MineMap).get(game.mine_map) number_of_mines = DBSession.query(models.MineMapData).filter_by( mine_map_id=mine_map.id).count() # If the game is over, just send over the full map if game.state is const.GameState.IN_PROGRESS: grid = derive_grid(mine_map, game.id) else: grid = mine_map.to_matrix() response = { 'game': True, 'height': mine_map.height, 'width': mine_map.width, 'mines': number_of_mines, 'state': game.state, 'grid': grid, } return response
def main(argv=sys.argv): if len(argv) < 2: usage(argv) config_uri = argv[1] setup_logging(config_uri) settings = get_appsettings(config_uri) engine = engine_from_config(settings, 'sqlalchemy.') DBSession.configure(bind=engine) ModelBase.metadata.create_all(engine)
def main(global_config, **settings): # Set up database stuff engine = engine_from_config(settings, 'sqlalchemy.') DBSession.configure(bind=engine) ModelBase.metadata.bind = engine # Configure config = Configurator(settings=settings) config.include('pyramid_mako') for route in routes: config.add_route(route.name, route.url) config.add_static_view('static', 'static', cache_max_age=3600) config.scan() return config.make_wsgi_app()
def derive_grid(mine_map, game_id): """ Returns the derived grid (mine matrix * click matrix) """ mine_matrix = mine_map.to_matrix() click_map = DBSession.query(models.PlayerMap).filter_by( game_id=game_id, map_type=const.PlayerMapType.CLICK).first() click_matrix = click_map.to_matrix() # Acts as a mask return multiply(mine_matrix, click_matrix)
def get_flags(request): game = get_game_or_none(request) if game is None: return HTTPNotFound('No such game.') # Get the FlagMap flag_map = DBSession.query(models.PlayerMap).filter_by( game_id=game.id, map_type=const.PlayerMapType.FLAG).first() # Each entry is either a flag, or a question flag_entries = DBSession.query(models.PlayerMapData).filter_by( player_map_id=flag_map.id, value=const.PlayerMapDataValue.FLAG).all() unsure_entries = DBSession.query(models.PlayerMapData).filter_by( player_map_id=flag_map.id, value=const.PlayerMapDataValue.UNSURE).all() flags = [[flag.row_num, flag.col_num] for flag in flag_entries] unsures = [[unsure.row_num, unsure.col_num] for unsure in unsure_entries] return {'flags': flags, 'unsures': unsures}
def to_matrix(self): from minesweeper.grids.matrices import Matrix mine_map = DBSession.query(MineMap).join(Game).filter_by( id=self.game_id).first() matrix = Matrix(mine_map.height, mine_map.width) for entry in self.map_data: # Each entry has a value that needs to be inserted into the matrix matrix[entry.row_num][entry.col_num] = entry.value return matrix
def update_cell(request): """ Executes the provided action upon the cell, returns the state. """ game = get_game_or_none(request) if game is None: return HTTPNotFound('No such game.') # If the game is not in progress (won or lost) then no action is allowed if game.state != const.GameState.IN_PROGRESS: return HTTPForbidden('The game is over.') try: x = int(request.matchdict['x']) y = int(request.matchdict['y']) except ValueError: return HTTPBadRequest('Cell parameters must be integers') # If there's a ClickMapData entry, the cell has already been clicked # It cannot be updated, so no further action is allowed click_map = DBSession.query(models.PlayerMap).filter_by( game_id=game.id, map_type=const.PlayerMapType.CLICK).first() click_data = DBSession.query(models.PlayerMapData).filter_by( player_map_id=click_map.id, row_num=x, col_num=y).first() if click_data: return HTTPForbidden('The cell has alread been revealed.') # Validate action try: action = int(request.POST.get('action')) except ValueError: return HTTPBadRequest('Action must be an integer') if action not in const.Action.choices(): return HTTPBadRequest('That action is incorrect.') value = None reveal = [] if action is const.Action.LEFT_CLICK: value, reveal, game = left_click(x, y, click_map, game) elif action is const.Action.RIGHT_CLICK: value, game = right_click(x, y, game) return {'state': game.state, 'value': value, 'reveal': reveal}
def cascade_reveal(start_x, start_y, mine_matrix, click_map_id): """ Returns the list of dicts, representing the values to reveal. """ # Where the revealed values go. The position records the x, y coordinates # Faster lookup than using the reveal list of dicts placeholder = Matrix(mine_matrix.height, width=mine_matrix.width, init_value=None) blank_spaces = [(start_x, start_y)] # queue of spaces to explore reveal = [] # Look around each blank space while(blank_spaces): (focus_x, focus_y) = blank_spaces.pop() for x in Matrix._adjacent_indices(focus_x, placeholder.height-1): for y in Matrix._adjacent_indices(focus_y, placeholder.width-1): # Have we already seen this entry? if (x, y) == (focus_x, focus_y) or placeholder[x][y] is not None: continue # skip this entry value = mine_matrix[x][y] # We haven't come across this entry yet, but it still may # already be clicked placeholder[x][y] = value # Check if there's a ClickMap entry for this datapoint click = DBSession.query(models.PlayerMapData).filter_by( player_map_id=click_map_id, row_num=x, col_num=y).first() if click is None: # Save a ClickMap entry for this cell recorded_click = models.PlayerMapData( player_map_id=click_map_id, row_num=x, col_num=y, value=const.PlayerMapDataValue.CLICKED) DBSession.add(recorded_click) reveal.append({'x': x, 'y': y, 'value': value}) # If the value is another blank space, save to look later if value is const.CellStates.CLUE[0]: blank_spaces.append((x, y)) return reveal
def post(self): """ Creates a new Game, MineMap, and PlayerMaps. """ # Create a MineMap mine_matrix = MineMatrix(const.DEFAULT_HEIGHT, width=const.DEFAULT_WIDTH, mine_number=const.DEFAULT_MINES) mine_map = mine_matrix.to_model() DBSession.add(mine_map) DBSession.flush() # Create a Game and PlayerMaps for it game = models.Game(mine_map=mine_map.id) game.player_maps = [ models.PlayerMap(map_type=const.PlayerMapType.CLICK), models.PlayerMap(map_type=const.PlayerMapType.FLAG), ] DBSession.add(game) DBSession.flush() return HTTPFound(location=self.request.route_url('view_game', game_id=game.id))
def left_click(x, y, click_map, game): reveal = [] # Record a successful click on the Click Map recorded_click = models.PlayerMapData( player_map_id=click_map.id, row_num=x, col_num=y, value=const.PlayerMapDataValue.CLICKED) DBSession.add(recorded_click) mine_map = DBSession.query(models.MineMap).get(game.mine_map) mine_matrix = mine_map.to_matrix() value = mine_matrix[x][y] # If the cell contains a mine, game over if value is const.MineMapDataValue.MINE: # Update game state game.state = const.GameState.LOSE DBSession.add(game) DBSession.flush() return (value, reveal, game) # If a blank space got revealed, time for a cascade reveal if value is const.MineMapDataValue.CLUE[0]: reveal = cascade_reveal(x, y, mine_matrix, click_map.id) DBSession.flush() # Compare the Click matrix to the Win matrix. If the same, the game is won click_map = DBSession.query(models.PlayerMap).filter_by( game_id=game.id, map_type=const.PlayerMapType.CLICK).first() click_matrix = click_map.to_matrix() win_matrix = derive_win_matrix(game) if click_matrix == win_matrix: # Update game state game.state = const.GameState.WIN DBSession.add(game) DBSession.flush() return (value, reveal, game)
def get_cell_mine_map_value(x, y, game): mine_map = DBSession.query(models.MineMap).get(game.mine_map) mine_matrix = mine_map.to_matrix() return mine_matrix[x][y]
def derive_win_matrix(game): mine_map = DBSession.query(models.MineMap).get(game.mine_map) mine_matrix = mine_map.to_matrix() mask = Matrix(mine_matrix.height, width=mine_matrix.width, init_value=0.1) return bit_flip(multiply(mine_matrix, mask))
def get_game_or_none(request): game_id = int(request.matchdict['game_id']) game = DBSession.query(models.Game).get(game_id) return game