Programming project 2

I'm sure you all know the 2048 game.

The Board class

Your task is to finish the implementation of the class Board that manages the 4x4 board of number tiles. You start with the file board.scala. A Board object stores a 4x4 array of integers. A zero represents an empty cell, other numbers represent themselves.

The provided class already contains a method toString, that returns the board in a nice format like this:

o----o----o----o----o
|    |    |    |    |
|    |  2 |    |  2 |
|    |    |    |    |
o----o----o----o----o
|    |    |    |    |
|    |    |    |  2 |
|    |    |    |    |
o----o----o----o----o
|    |    |    |    |
|  2 |  2 |  2 |  2 |
|    |    |    |    |
o----o----o----o----o
|    |    |    |    |
|  2 |    |    |  2 |
|    |    |    |    |
o----o----o----o----o

There is also a method insertRandom, that randomly selects an empty cell (a cell with contents zero) and set its contents to \(2\) (with probability 90%) or \(4\) (with 10% probability).

Please start by compiling and testing the provided class:

$ fsc board.scala 
$ scala
Welcome to Scala version 2.11.7

scala> val b = new Board
b: Board =
o----o----o----o----o
|    |    |    |    |
|    |    |    |    |
|    |    |    |    |
o----o----o----o----o
|    |    |    |    |
|    |    |    |    |
|    |    |    |    |
o----o----o----o----o
|    |    |    |    |
|    |    |    |    |
|    |    |    |    |
o----o----o----o----o
|    |    |    |    |
|    |    |    |    |
|    |    |    |    |
o----o----o----o----o

scala> b.insertRandom()

scala> b.insertRandom()

scala> b
res2: Board =
o----o----o----o----o
|    |    |    |    |
|    |    |    |    |
|    |    |    |    |
o----o----o----o----o
|    |    |    |    |
|    |    |    |    |
|    |    |    |    |
o----o----o----o----o
|    |    |    |    |
|    |    |    |    |
|    |    |    |    |
o----o----o----o----o
|    |    |    |    |
|  2 |    |  2 |    |
|    |    |    |    |
o----o----o----o----o

scala> for (i <- 1 to 7)
     |   b.insertRandom()

scala> b
res4: Board =
o----o----o----o----o
|    |    |    |    |
|  2 |  2 |  2 |    |
|    |    |    |    |
o----o----o----o----o
|    |    |    |    |
|  2 |    |  4 |    |
|    |    |    |    |
o----o----o----o----o
|    |    |    |    |
|    |  2 |    |  4 |
|    |    |    |    |
o----o----o----o----o
|    |    |    |    |
|  2 |    |  2 |    |
|    |    |    |    |
o----o----o----o----o

A test suite

I am providing a test suite checksuite.scala, which allows you to test the Board class and your implementations of the missing methods. Download, compile, and try the test suite as follows:

$ fsc checksuite.scala 
$ scala org.scalatest.run Game2048CheckSuite
Run starting. Expected test count is: 5
Game2048CheckSuite:
- creation of board
- left pushes *** FAILED ***
  scala.NotImplementedError: an implementation is missing
- right pushes *** FAILED ***
  scala.NotImplementedError: an implementation is missing
- up pushes *** FAILED ***
  scala.NotImplementedError: an implementation is missing
- down pushes *** FAILED ***
  scala.NotImplementedError: an implementation is missing
Run completed in 342 milliseconds.
Total number of tests run: 5
Suites: completed 1, aborted 0
Tests: succeeded 1, failed 4, canceled 0, ignored 0, pending 0
*** 4 TESTS FAILED ***
(It's normal that four of the five tests fail. The first test just tests that creating and displaying boards works, it passes correctly. The remaining four tests are for the methods that you still have to write.)

The test suite requires ScalaTest, so if you get a compilation error, you forgot to install that (see the installation instructions).

Note that running a test suite is a bit unusual: We don't execute the Game2048CheckSuite directly, but instead we execute the object org.scalatest.run (which is defined in the ScalaTest library).

You can learn more about test suites and ScalaTest in the tutorial (but it should not be necessary to read that now to do the homework).

Your task: pushLeft, pushRight, pushUp, pushDown

The 2048 game has four basic moves: Pushing cells left, right, up, and down. Your task is to implement the four methods pushLeft(), pushRight(), pushUp(), and pushDown(). They are already in the board.scala file, but marked as ???, which means not implemented in Scala.

Start by implementing the left push, that is the method pushLeft(). Here is what happens exactly:

The pushLeft method returns the number of points received by the left push.

Here are some examples:

scala> b
res1: Board =
o----o----o----o----o
|    |    |    |    |
|    |  2 |  2 |    |
|    |    |    |    |
o----o----o----o----o
|    |    |    |    |
|  2 |  2 |  2 |    |
|    |    |    |    |
o----o----o----o----o
|    |    |    |    |
|    |  2 |    |  2 |
|    |    |    |    |
o----o----o----o----o
|    |    |    |    |
|    |    |  2 |    |
|    |    |    |    |
o----o----o----o----o

scala> b.pushLeft()
res2: Int = 12

scala> b
res3: Board =
o----o----o----o----o
|    |    |    |    |
|  4 |    |    |    |
|    |    |    |    |
o----o----o----o----o
|    |    |    |    |
|  4 |  2 |    |    |
|    |    |    |    |
o----o----o----o----o
|    |    |    |    |
|  4 |    |    |    |
|    |    |    |    |
o----o----o----o----o
|    |    |    |    |
|  2 |    |    |    |
|    |    |    |    |
o----o----o----o----o

scala> b.insertRandom(); b.insertRandom(

scala> b
res5: Board =
o----o----o----o----o
|    |    |    |    |
|  4 |    |    |    |
|    |    |    |    |
o----o----o----o----o
|    |    |    |    |
|  4 |  2 |    |    |
|    |    |    |    |
o----o----o----o----o
|    |    |    |    |
|  4 |    |    |    |
|    |    |    |    |
o----o----o----o----o
|    |    |    |    |
|  2 |    |  2 |  2 |
|    |    |    |    |
o----o----o----o----o

scala> b.pushLeft()
res6: Int = 4

scala> b
res7: Board =
o----o----o----o----o
|    |    |    |    |
|  4 |    |    |    |
|    |    |    |    |
o----o----o----o----o
|    |    |    |    |
|  4 |  2 |    |    |
|    |    |    |    |
o----o----o----o----o
|    |    |    |    |
|  4 |    |    |    |
|    |    |    |    |
o----o----o----o----o
|    |    |    |    |
|  4 |  2 |    |    |
|    |    |    |    |
o----o----o----o----o

After you have implemented pushLeft(), run the test suite again. Note that you don't need to compile checksuite.scala again, since the test suite has not changed. You only need to compile board.scala after every change to the source code. If your implementation of pushLeft() is correct, the output should look like this:

$ fsc board.scala
$ scala org.scalatest.run Game2048CheckSuite
Run starting. Expected test count is: 5
Game2048CheckSuite:
- creation of board
- left pushes
- right pushes *** FAILED ***
  scala.NotImplementedError: an implementation is missing
- up pushes *** FAILED ***
  scala.NotImplementedError: an implementation is missing
- down pushes *** FAILED ***
  scala.NotImplementedError: an implementation is missing
Run completed in 640 milliseconds.
Total number of tests run: 5
Suites: completed 1, aborted 0
Tests: succeeded 2, failed 3, canceled 0, ignored 0, pending 0
*** 3 TESTS FAILED ***
You should see that the "left pushes" test has now passed, and only three remaining tests are failing.

The other three moves work exactly the same, only the direction is different. An elegant way to implement this would be to write private methods for mirroring the board horizontally and for transposing it (that is, flipping rows and columns). Then you can implement the right push as mirror, left push, mirror, and the upwards push as transpose, left push, transpose, etc.

Every time you implement a method, run the test suite again. One by one, the test cases should pass correctly. In the end, you should get this output:

$ scala org.scalatest.run Game2048CheckSuite
Run starting. Expected test count is: 5
Game2048CheckSuite:
- creation of board
- left pushes
- right pushes
- up pushes
- down pushes
Run completed in 1 second, 10 milliseconds.
Total number of tests run: 5
Suites: completed 1, aborted 0
Tests: succeeded 5, failed 0, canceled 0, ignored 0, pending 0
All tests passed.

Don't forget that all four methods must return the number of points gained. My test suite does not test this yet, but the TAs will check it when grading your submission!

The game

Finally, when all four push methods are implemented, you can play the 2048 game interactively. Download game.scala, compile, and run it as follows:

$ fsc game.scala 
$ scala The2048Game

The moves are "l", "r", "u", "d", and you need to press Enter after the letter.

Submitting your homework

You must upload only the file "board.scala" to the electronic submission server.

You have to register with your student id the first time you use the server.

When you register, you have to choose an alias. This alias will be used when I post your homework and exam scores, so you should pick an alias that you can remember, but which other students do not know (everybody who knows your alias will know your scores).

You must remember your password. There is currently no way to retrieve or change your password (except for asking me).

A graphical user interface

As a bonus, you can implement a graphical user interface for the 2048 game, following these instructions.

(Sorry, no extra points for this—it's purely for your personal enjoyment.)