Week 1 Class Exercise 1: Building The 2048 Game Together
Chris Tralie
Overview / Logistics
The purpose of this class exercise is to teach you how to write and test methods that do very specific tasks, and then to see how they all fit together to accomplish a complex task. This should nail home that point that you should always split complex programs up into simpler, small tasks and test them each rigorously before combining them into a whole.
Students will split up into groups to specialize on one small piece of the logic for the game 2048, and we will then put them all together to make a complete game that we play as a class in the NetBeans console.
Click here to download the skeleton code for this exercise, which you can open with NetBeans. You will be editing src/Game.java
. Additionally, below is a repository on repl.it with the starter code.
Repl.it
Learning Objectives
- Write methods to fit a specification
- Access and modify elements in 2D array
- Use methods together in concert to accomplish a task
- Write a complete program with very little code in the
main
function - Write loops that work in concert with multi-dimensional arrays
- Access array indices carefully
Background: 2048
2048 is a simple but surprisingly addictive game that was all the rage about six years ago. It also happens to be at the right level of complexity for where we are now with our coding skills. Click here to read the rules for the game, and please play the game yourself below with the 4 arrow keys to familiarize yourself with the rules (Javascript implementation courtesy of Kunal Mohta)
Programming Tasks
There are many ways to implement the logic in this game, but we will be breaking it down into a set of very specific tasks that each group will work on. First, make sure you've downloaded the netbeans project. I will now briefly explain some of the methods I've already filled in in the short video below
Next, here are the different tasks that each group will implement. Each group should have a designated coder who shares their screen, a note-taker who keeps track of the approaches that were tried and the difficulties that the group ran into, and a spokesperson who summarizes the approach to the group when we all check back in.
- Group 1:
addRandom()
and makeRandomGrid()
-
Group 2:
moveRight()
-
Group 3:
moveDown()
-
Group 4:
combineRight()
-
Group 5:
combineDown()
-
Group 6:
flipHorizontally()
and flipVertically()
-
Group 7:
isFull()
and isOver()
Students should test their methods rigorously before declaring victory. You may want to fill in the method addRandom()
and makeRandomGrid()
moveRight()
moveDown()
combineRight()
combineDown()
flipHorizontally()
and flipVertically()
isFull()
and isOver()
makeMyOwnGrid()
to fill in specific example to test your code on to make sure it handles all cases. I've given some examples below in the different task sections that you should verify give the right outputs. As an example of how you might setup a grid, the following code
Prints out a grid that looks like this
Group 1: addRandom()
and makeRandomGrid()
The addRandom()
method should turn exactly one empty cell anywhere on the board at random into either a 2 or a 4. The method should not overwrite a cell that is already occupied. Below is an example of two calls of addRandom()
in a row. The first one adds a 4 in the upper right, and the second one adds a 2 on the left of the second row.
game.printBoard() | game.printBoard() | |
Once you are finished this method, you should create another method makeRandomGrid(int numInitial)
that takes one integer, which is the number of random 2s or 4s to put down on the grid in random positions. This should be as simple as calling your makeRandomGrid()
function numInitial
times in a row.
To aid you, recall that the java.util.Random
class can be used as follows:
Group 2: moveRight()
Fill in the method moveRight()
to take the nonzero elements in the current board and move them all to the right so that there are no gaps and everything is touching the right of the board. Don't worry about combining adjacent elements that are the same; that will be the job of another group.
As an example, consider a call of moveRight()
on the board below
game.moveRight() |
|
Group 3: moveDown()
Fill in the method moveDown()
to take the nonzero elements in the current board and move them all down so there
are no gaps and everything is touching the bottom. Don't worry about combining adjacent elements that are the same; that will be the job of another group.
As an example, consider a call of moveDown()
on the board below
game.moveDown() |
|
Group 4: combineRight()
Fill in the method combineRight()
to combine all adjacent numbers that are the same in a particular row. There are three cases to consider
- There are two adjacent elements that are the same. In this case, put a zero in the place of the first one, and replace the second one with twice its value
- There are three in a row. In this case, put a zero in the first place to make it empty, keep the original value in the second place, and put twice the original value in the third place.
- There are 4 in a row. Put zeros in the first two, and put twice the value in the second two.
Below is an example of the effect this would have on a particular board
board.combineRight() | |
Group 5: combineDown()
Fill in the method combineDown()
to combine all adjacent numbers that are the same in a particular column. There are three cases to consider
- There are two adjacent elements that are the same. In this case, put a zero in the place of the first one, and replace the second one with twice its value
- There are three in a row. In this case, put a zero in the first place to make it empty, keep the original value in the second place, and put twice the original value in the third place.
- There are 4 in a row. Put zeros in the first two, and put twice the value in the second two.
Below is an example of the effect this would have on a particular board
board.combineDown() | |
Group 6: flipHorizontally()
and flipVertically()
Fill in the method flipHorizontally()
to flip all of the elements in the board to the from left/right, right/left in a mirror reflection. Here's an example of the effect of this on a particular board
board.flipHorizontally() | |
Then fill in the method flipVertically()
to flip all of the elements in the board from up/down, down/up in a mirror reflection.Here's an example of the effect of this on a particular board
board.flipVertically() | |
Group 7: isFull()
and isOver()
Fill in the method isFull()
, which returns true
if the board is full, or false
otherwise. Then, if time permits, implement the method isOver()
that returns true
if the the board is full and it is not possible to make any moves. or false
otherwise.
For example, here is a board that is full but in which it is still possible to make a move (isFull()
is true
but isOver()
is false
)
And here is a board that is full and where no more moves are possible, so the game should be over (isFull()
is true
and isOver()
is true
)