Due: Tues 01/29 at 8:59pm; self-evaluation due Weds 01/30 at 9:59pm
Starter files: code.zip
Note: The description may make assignments seem longer than they are. Distilling the description to make a list of all the things you are actually supposed to do will go a long way in having a good plan to tackle it. Read the description several times to confirm this list before acting on it!
The primary goal of this assignment is to practice implementing an interface based on given specifications, and by choosing an appropriate data representation that helps in providing the functionality promised by the interface.
Please ensure all of your source code is in the
(See Package Management below.)
In the next three assignments, you will implement a game called “Peg Solitaire”. This is a board game played by a single player. The game involves moving pegs on a game board with holes. Many modern versions using marbles instead of pegs, giving it the name “Marble Solitaire”. The image above shows a Marble Solitaire board. It is referred to as a Solitaire-type game, and is also called Brainvita in some countries. If you have not played this game before, try it here!
The game starts by arranging marbles on the board. Exactly one slot is empty, and is traditionally the center slot.
A move is made by making one marble jump over exactly one marble and land in an empty slot exactly two positions away. When such a move is made, the marble that is jumped over is removed. Therefore, each single valid move increases the number of empty slots by one. A marble can only jump orthogonally (i.e. horizontally or vertically). The game ends when no more valid moves can be made. At any point in the game, the score is the number of marbles on the board. The objective of the game is to make moves so as to end up with the minimum score when the game ends.
In this assignment you will write the model for this game. The model will maintain the state of the game and allow a client to specify moves. You are not required to make the game playable at this point.
In order to play the game, the client would expect the following operations:
start a new game, make a move, get the current state of the game, get the
current score and know when the game has ended. These operations have been
specified and documented in the provided
You are not allowed to change the interface in any way!
A short explanation of the methods in the interface follows:
move(int fromRow, int fromColumn, int toRow, int toColumn)is called to make a move according to the rules of the game. Specifically it will move a marble from the position
(fromRow, fromColumn)to the position
(toRow, toColumn)if the move is valid. It will throw an
IllegalArgumentExceptionif the move cannot be made.
See below for implementation-specific details of how to refer to positions on the board.
String getGameState()returns the current state of the game as a
Stringobject. The exact format of this string is dependent on the implementation (see below).
trueif the game is over, and
int getScore()returns the current score in the game.
You must check that your
MarbleSolitaireModelImpl implementation of the
MarbleSolitaireModel interface works as specified. We recommend that you
create an empty implementation, and then proceed to write your tests. This will
allow you to understand how your class will be used, which will help you to
implement it. Please review the testing
recommendations. Since you are testing the public-facing behavior of this
interface, following those guidelines means that you should not
place this testing code in the
package, but rather place it in the default package.
MarbleSolitaireModel interface in a class called
Design a suitable representation of this game. Think carefully about what fields and types you will need, and how possible values of the fields correspond to game states.
Although the traditional game is as shown above, we can generalize the dimensions of this board while retaining its shape. We define the arm thickness as the number of marbles in the top row (or bottom row, or left or right columns). In the traditional game, the arm thickness is 3, making the length of the largest row/column 7. In general, a board of arm thickness a is a board with a single square of size a, with 4 squares of the same size joined at its four sides to form a plus shape. In order to keep the “center” unique, the arm thickness must be an odd number to be valid. The board scales while keeping the four arms of the plus shape the same size.
Positioning: A position is specified using a pair
(row, column), assuming that the board is laid on a rectangular grid. The row and column numbers start at 0 in the top-left corner, increasing top to bottom and left to right respectively. For example in the above picture, the positions of the three marbles in the top row are
(0, 4)respectively. The empty slot is at
Instantiating the game: Your class should support four ways to instantiate it:
The first constructor should take no parameters, and initialize the game board as shown above (arm thickness 3 with the empty slot at the center).
A second constructor should take two parameters:
sCol. It should initialize the game board so that the arm thickness is 3 and the empty slot is at the position
(sRow, sCol). If this specified position is invalid, it should throw an
IllegalArgumentExceptionwith a message
"Invalid empty cell position (r,c)"with r and c replaced with the row and column passed to it.
The third constructor should take the arm thickness as its only parameter and initialize a game board with the empty slot at the center. It should throw an
IllegalArgumentExceptionif the arm thickness is not a positive odd number.
Finally a fourth constructor should take the arm thickness, row and column of the empty slot in that order. It should throw an
IllegalArgumentExceptionif the arm thickness is not a positive odd number, or the empty cell position is invalid.
Carefully think about the shape of the board and our positioning scheme to infer which positions are invalid.
The game state: The
getGameStatemethod may be used to print the game state in the format illustrated below. Specifically each slot should be one character (
'_') and there should be one space between the positions. There should be no spaces after the last slot in a row. The following shows the output corresponding to the screenshot above:
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 O O O O O O O
NOTE: The string you return should not have a newline at the end of the last line.
movemethod should make the move and change the game state appropriately. A move is valid if all these conditions are true: (a) the “from” and “to” positions are valid (b) there is a marble at the specified “from” position (c) the “to” position is empty (d) the “to” and “from” positions are exactly two positions away (horizontally or vertically) (e) there is a marble in the slot between the “to” and “from” positions. Any invalid move should result in an
IllegalArgumentExceptionexception with an appropriate message on a single line. The text of the message is up to you.
For example, the only valid moves on the board shown above start from
(3, 5), and all move to
Be sure to properly document your code with Javadoc as appropriate. Method implementations that inherit Javadoc need not provide their own unless they implement something different or in addition to what is specified in the inherited documentation.
Hw02TypeChecks.java contains a small class designed to help you
detect when your code may not compile against the grading tests. In particular,
if your project cannot compile while
Read the testing recommendations again.
To make sure that your packages are in the correct layout, you should tell IntelliJ to do the following. Do this early, before you’ve written much code, to ensure that your files wind up in the right locations automatically, instead of having to fix it afterward:
When you create a new project, you should see something like this:
Notice that the
srcdirectory is marked blue, which means IntelliJ believes that this directory contains the source files of your project. To create a new package, right-click on the
srcdirectory, select New -> Package. In the dialog box that pops up, enter the new package name
To create new files within a particular package, right-click on the package folder and select New -> Java Class. If you want to create a new file in the default package, then select the
To create a test directory, right-click on the project itself, and select New -> Directory. In the dialog box that pops up, enter “test” as the name. Right-click on the directory, select Mark Directory As -> Test Sources root. Henceforth, you should add any test classes in this folder. See the tutorial video for a demo of this.
test/directories can parallel each other in structure. However, keeping your sources and tests separated is always a good idea, so you don’t inadvertently release your tests as part of your source!
The interface (
Implementation of the interface(
Any additional classes you saw fit to write
Tests in one or more JUnit test classes
Again, please ensure all of your project’s source is in the
cs3500.marblesolitaire.model.hw02 package. The autograder will give you
an automatic 0 if it cannot compile your code! Place your tests in the default
For this assignment, you will be graded on
whether your code implements the specification (functional correctness),
the appropriateness of your chosen representation,
the clarity of your code,
the comprehensiveness of your test coverage
how well you have documented your code
how well you follow the style guide.
Wait! Please read the assignment again and verify that you have not forgotten anything!
Please compress the
test/ folders into a zip file and
submit it. After submission, check your submitted code to ensure that you see
two top-level folders:
test/. If you see anything else,
you did not create the zip file correctly!
Please submit your assignment to https://handins.ccs.neu.edu/ by the above deadline. Then be sure to complete your self evaluation by the second deadline.