Puralax is a tile-based puzzle game, playable
online or on most mobile phones. The graphics are fairly simple, the rules are
reasonably straightforward, but the overall game is quite subtle. As the game
progresses, new kinds of tiles are introduced. It’s taken the developer
several years to introduce the latest kinds of tiles, and I suspect the
internal design of the game was revised to accommodate those changes. We’ll
repeat this process—
In this board we see the three simplest parts of the game. Tiles can have colors, and can have zero or more dots on them. The player can spend a dot to move a tile to an adjacent empty cell:
or to paint an adjacent tile of a different color:
The goal of the game is to make all the tiles the same as the border color of the level. The game ends when all tiles are the target color, or there are no more valid moves.
Painting is a flood-fill operation: all adjacent tiles of the same color will get painted:
Design a model to represent this game so far. Consider how to represent tiles, dots, colors, and goals. What operations can a player take to modify the game as it’s being played?
Design a mechanism to more easily construct a level of this game. Separate your design into two layers: a factory that can construct the various tile types, and a builder that parses an input string and uses the factory to construct the model. For example, the first board above was written (the comments are for clarity, not part of the input)
3 3 B // height 3, width 3, goal color Blue 1 1 M B 1 // cell (1,1) is Mobile, colored Blue, with 1 dot 1 2 I G // cell (1,2) is Immobile, colored Green
Hints: use interfaces frequently. A key question will be how to represent “empty” cells: when a dot is spent to move a tile, how will your model update? Also, consider whether movable tiles (with more than zero dots on them) and immobile tiles (with zero dots on them) should be represented in the same way. What are the advantages and disadvantages of doing so? (There is no single “correct” answer here; you can justify several designs here.)
Design a textual view for the game, that gives back a mostly-for-debugging-purposes string explaining the state of the game.
Design a visual view for the game. Recall that we want to ensure that the view cannot modify the model. How might you structure the rendering of tiles, given that more tile types are going to be added in the future?
Hint: we really don’t want to use
instanceof here, but we also
really don’t want to add methods directly to our model that are
graphics-specific (i.e., your model classes should not need to
import java.awt.*). What patterns allow us to add this kind of
Dark gray tiles are walls that cannot be moved or painted.
Revise your model to account for walls. What representations do you need to change? What observations or model operations change as well?
Hint: consider what happens after you use the final dot on this blue tile to paint the green tile.
Revise your view to handle walls.
A nested tile has multiple colors. Painting a cell like this paints only the outermost layer. For instance, here we use the blue tile first to paint the center tile:
But if that new color matches the next color in, then the layers merge. If we now use the yellow tile to paint this previous result, we would see:
Starting from the original board in this section, solve this level. Draw each of the intermediate boards.
Revise your model to account for nested cells. What representations do you need to change? What observations or model operations change as well?
Hint: suppose we “solved” the level by using the blue tile, then the yellow, the green, and finally the purple tile. Did we solve the level?
Revise your view to handle nested tiles.
It’s finally time to build a controller for the game, to make it playable. Do so. Consider how to ensure that your view knows nothing about the controller, and how to ensure that the controller knows nothing of the internal GUI details of your view. What high-level events occur in your game?
Add the ability to undo and redo moves.
Add the ability to customize the colors of the game, so that instead of the primary colors seen so far, the player can choose a custom theme.
A painter tile is indicated by a bar at the bottom of the cell. When a tile is moved into that cell, it is automatically painted the color of that paint bar, and (as usual) any adjacent tiles that were the same color as the moved tile will be painted as well. In the example above, if we move the top tile to the middle, it will be painted blue, and the other green tile will get painted as well:
Solve the following board:
Solve the following board:
What do you think should happen when this painter tile gets used?
Revise your model to account for painter tiles. What representations do you need to change? What observations or model operations change as well?
Hint: what becomes trickier with moving tiles?
Revise your view to handle painter tiles.
Try writing a solver that automatically “plays” a level, and produces a sequence of moves that would solve it. It should only require your model, and be independent of your view or controller.
Try writing a level generator. This should only require your model, and be independent of your view or controller.
Try writing a level designer. This entails a new view and a new controller, but should not affect your model.
Try coming up with new tile types. This should affect your model and view, but not your controller.
Building Puralax has required nearly all of the patterns we’ve encountered so far, and motivated new uses for several of them. Reviewing how each of these patterns applies to each part of the MVC design is a good way to solidify your grasp of how we can use these patterns: