15 PuzzleNovember 2023
Northeastern University
Skills Learned: Java, Object-Oriented Programming, ArrayLists, Mutation, Random Code, Imperative World Libaries, Graphics Libraries
Code Available Upon Request
15 Puzzle is a single player game where the player moves tiles one a time into a singular empty space in order to place the tiles into the correct numerically increasing sequence.
The player uses the arrow keys to move tiles into the sole empty space on the 4x4 grid. Once a tile is moved into it's correct position, it turns green. When all tiles are in
their correct spot (and all tiles are green) the game ends and the player is declared the winner. A game runthrough is show below.
First, a random board is generated for the player. Looks like some tiles are already in the right spot.
Next, the player presses the "up" key. The 14 tile then moves into the empty space.
After a few more moves, the player manages to get the entire first row.
Looks like the player is really making some progress.
They soon realize the end is the hardest part. Nevertheless, all tiles turn green.
Victory!
Class Diagram
The following diagram shows the relationships and fields of all classes used in the creation of Bridgit. Methods within classes are not shown for academic honesty purposes.
Design Strategies
To make a working game, I realized he needed to find solutions to few major concepts. These include:
- Design a structure to represent the game board and a tile.
- Create a graphical representation of the board.
- Initialize a starting board with tiles at random positions.
- Process keyboard inputs so that when the appropriate key is pressed, the tiles move correctly.
- Save the previous board state so that a player can press the "u" key and undo their latest move.
- Determine when a tile is in the correct spot, and thus determine when all tiles are in the correct spot and the player has won.
I started by creating the Tile class to represent a tile, whose only fields included an integer for it's value and a Utils class object. Then, in the overarching FifteenGame class
(extended from Northeastern's imperative world class), I represented the Board using a 2D ArrayList of Tiles of size 4x4. Therefore, a Tile's location can now be thought of
as its row and column in the ArrayList. Next, I created the Utils class to hold methods which shouldn't be local to any particular class. This allowed me to write methods to create
random 2D ArrayLists of tiles (one method with a seed for testing, one without for actual gameplay) to initialize the game board. In this class, I also wrote methods
to translate a tile's column and row into a pixel coordinate, draw an ArrayList of tiles using Northeastern's image library, swap two tiles in an ArrayList, move tiles based on a given string (key input),
and determine if all tiles are in the correct position. These methods greatly improved the functionality of the game and allowed me to keep his Tile class and FifteenGame
class simple and loosely coupled.
In order to process keyboard inputs, I used methods from Northeastern's imperative library to detect a keyboard press and recognize the specific key. I then passed
the resulting string (indicating which key had been pressed) to methods in the Utils class to make the appropriate changes to the game. At this point, the game had almost 100% functionality. My last goal
was to save the previous game state so that a player could undo their last move. To do this, I copied the data in the tiles field (which held the 2D ArrayList of tiles)
of the FifteenGame class into the prevTiles field each time a change occurred on the board. Then, I added logic so that when the "u" key was pressed, the prevTiles
field will overwrite the tiles field, thus reverting the tiles back to their previous state.
Difficulties and Learning Outcomes
- Testing random functions can be tricky. I had to learn how to design unit tests to account for random code. This involved using seeding to ensure that the same "random" outputs would occur every time, therefore allowing tests to properly predict a pseudo-random game state.
- Handling keyboard inputs seemed simple on the face, however I realized that controls needed to be put in place to ensure that pressing a random key wouldn't cause bugs in the code. Initially, pressing any key would copy the ArrayList of tiles into the prevTiles variable, thus saving the game state. This was an issue because if a key was pressed that didn't change the game, the field would get overridden and the old game state would be lost. I thus had to implement controls to ensure that this field was only overridden when actual changes were made to the game.
Overall, I was proud of my work on this game. I feel the end product was well designed and bug-free. In addition, I now love challenging my friends (especially my younger sister) with playing the game to see if they can figure out it's solution.