Assignment 2: Painting a Winner, Part 1: Solo Red
Due dates:
Implementation:
Thu, Sep. 26Fri, Sep. 27 at 8:59pmSelf-evaluation: Fri, Sep. 27 at 11: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 tasks, classes, and methods you are actually supposed to implement 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!
1 Purpose
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
cs3500.solored.model.hw02
and
cs3500.solored.view.hw02
packages. Note that the model package
refers to hw02
, and the view does not.
(See Package Management below.)
There will be two submissions for this assignment:
Your actual implementation and full test suite
A self-evaluation, due one day plus three hours later than the actual implementation, where we will ask you to reflect on your implementation and testing choices.
The Handins assignment will be delayed by for a little while This is to encourage you to focus on design and planning testing of your code before implementing a representation. It is tempting to get lost in the Handins tests because that is a quantitative measure of progress, but a wrong-headed design can lead to many lost hours of frustration. Please plan ahead before writing your code.
A reminder about late days: Each submission is a distinct submission, and each one will independently use up your late days if you submit after the deadline. (Submitting late for the implementation does not mean you automatically get a “free” late day for the assignment as well – submitting both parts late will use two late days.) The 27-hour window for the self-evaluation is deliberate, so that even if you submit your assignment late, you can still submit your self-evaluation on time.
Read below for more details of each submission.
2 The game of Solo Red
2.1 Context
In the next three assignments, you will implement a solitaire variation of the party card matching game called “Red7.” The goal of the game is to play cards in such a way that you are constantly winning according to an ever changing ruleset.
A video showing the solitaire game of Solo Red is coming shortly! In the meantime, here is an image of the game being played.
Note that our version will have some differences to the real version of the game. Furthermore, a future assignment might involve adding more features to this game. Keep the above in mind when thinking about your representation in this assignment. However, read this assignment carefully and from beginning to the end for what features you are required to implement in this assignment.
2.2 Cards in the Game
Our game uses a special set of playing cards for the game. Each card consists of a color and a number. The valid colors are as follows
Red (R)
Orange (O)
Blue (B)
Indigo (I)
Violet (V)
The numbers are the integers 1 to 7, inclusive. For example, Red 3, Blue 1, and Violet 7, are all possible cards. We will present cards as two character strings, a letter for the color and the number itself. So Red 3 is printed as R3, Blue as B1 and so on.
2.3 Layout of the Game
The game consists of 4 structures of cards:
the deck: the cards a player will draw from
the
n
palettes wheren
is decided on game start: face-up cards that the player will interact withthe canvas: the face-up card that details the current rule for a palette to win
a hand: the cards a player can use to change the canvas or update a palette
2.4 The Concept of Winning
Before we explain the rules of the game, we need to explain the concept of a palette "winning". Palettes can either be winning or losing, depending on the current rule for winning. The current rule is dictated by the canvas, specifically the *color* of the card on the top of the canvas. In other words, colors have rules.
Each color represents a different rule. For example, define the following palettes P and Q
P has the cards Red 4, Blue 5, Red 6
Q has the cards Orange 6, Violet 7, Blue 3, Indigo 3
The rules for the colors are defined below and we use the above palettes as a way to explain who is winning under that color’s rule.
Red: The palette with the highest card wins. Let C, D be cards. C is higher than D if the number of C is greater than the number of D OR if the numbers are equal, C’s color is closer to Red than D’s color. We use the rainbow ordering when it comes to declaring closeness. So this means R > O > B > I > V.
For example, Blue 7 is higher than Red 1 because 7 is greater than 1. With our example palettes, that means Q is winning. Q has a Violet 7 as it’s highest card and P has a Red 6 as it’s highest card. Since 7 > 6, Violet 7 is higher.
Orange: The palette with the most of a single number wins. With our example palettes, Q is winning because it has two cards with the number 3. P has one card with the same number.
Blue: The palette with the most cards of different colors wins. With our example palettes, Q is winning because it has 4 cards with different colors. P only has 2 cards with different colors.
Indigo: The palette with the cards that form the longest run wins. A run is an unbroken increasing sequence of consecutive numbers after sorting. For instance 1-2-3 is a run as is the single number 4. However, 4-6 is not a run because it is missing the number 5 and 2-2 is not a run because it is not increasing. By extension, 3-1-2 is a run under this rule but 6-4 is not. With our current example, P is winning because it has a run of length 3 made of Red 4, Green 5, and Red 6. Q only has a run of length 2 made of the colors Orange 6 and Violet 7.
Violet: The palette with the most cards below the value of 4 wins. With our example palettes, Q wins. Q has 2 cards with a number below 4: Blue 3 and Indigo 3. P has none.
2.4.1 Ties under the Color’s Rule
Note that it is possible that with the color rule alone, there can be a tie. For example, define the following pair of palettes
P1 has the cards Red 1, Blue 5, Red 6
Q1 has the cards Violet 4, Blue 2, Orange 6
Under Violet’s rule, P1 and Q1 are tied: both have a single card below 4. P1 has a Red 1 and Q1 has a Blue 2.
If there is a tie under the current rule, focus on only the tied palettes. For each palette, look at the cards that help the palette win under the current rule. Choose the highest card amongst only those cards according to the Red rule. The palette with the highest card wins. Since all cards are unique, this ruling will always break a tie.
Returning to the example, P1 has a Red 1 and Q1 has a Blue 2. By the rules of breaking ties, Q1 wins because blue 2 is a higher card than Red 1.
2.5 Game setup
To play, we need a deck of unique cards, enough to fill the palettes with one card and give the player a full hand. The game can be played with two or more palettes and the maximum hand size must be positive (e.g. greater than 0)
First, the palettes are created, from first to last. Each palette is created by putting the current top card of the deck face-up on the palette.
Next, the canvas is created by assuming it is Red. Note it does not use a card from the deck but a special card separate from the deck that is Red. (In the packaging, it is an actual separate card but you will improvise in your implementation.)
Finally, draw cards from the deck to the hand until the hand is full.
2.6 Game play
With the game setup, the game starts. The player must perform one of the following actions
Play one card from their hand to a palette that is losing.
Play one card from their hand to the canvas to change the rule and then play one card from their hand to a palette that is losing.
After the player plays a card from their hand to the current palette, the player checks if the current palette is winning under the current rule. If so,
The player draws from the deck until either their hand is full or the deck is empty.
There used to be a statement about shifting palettes here. That is not possible in this version of the game.
2.7 Examples of Playing a Card to A Palette
Consider the game state displayed below for a 4-palette, 7-card hand game. At this point, P3 is winning under the current rule.
Canvas: R P1: B2 P2: R1 B5 > P3: O1 V7 P4: O4 Hand: R2 B4 V6 B7 I1 O3 R3
The player needs to make a palette win, so they play the B7 card to P1. Since B7 is now the highest card on P1 and it is higher than the B5. So now palette 1 is winning. The player then draws a card from the deck. The visualization below reflects the state of the game. Also notice that every card to the right of where B7 was has slid down!
Canvas: R > P1: B2 B7 P2: R1 B5 P3: O1 V7 P4: O4 Hand: R2 B4 V6 I1 O3 R3 R4
Notice the player’s hand. There is nothing in their hand higher than a B7. In this scenario, the player decides to play the R2 to P3. Notice this means the palette P3, which is now losing, continues to lose. This ends the game, resulting in the final state below. Notice no card has been drawn because the player does not have the chance to do so.
Canvas: R > P1: B2 B7 P2: R1 B5 P3: O1 V7 R2 P4: O4 Hand: B4 V6 I1 O3 R3 R4
2.8 Examples of Changing the Canvas and then Playing a Card to a Palette
As seen in the above section, if the player could only play cards to a palette, then it is pretty simple to lose. However, that is where the second possible action occurs. Suppose the game is in the state below
Canvas: R P1: B2 B7 > P2: R1 R7 P3: O1 V7 P4: O4 Hand: R2 B4 V6 I1 O3 R3 R4
The player then decides to play I1 to the canvas, changing the rule from Red to Indigo and giving us the state below. Notice a couple of changes.
The canvas has changed to I to reflect the indigo color
The winning palette has not changed because P2 is still winning under this rule. It has the a run of 1 card and the B7, part of the best run, is the highest card amongst all palettes with a run of 1 card.
The hand has NOT been replenished because the player does not draw yet.
Also notice that every card to the right of where I1 was has slid down in the hand!
Canvas: I P1: B2 B7 > P2: R1 R7 P3: O1 V7 P4: O4 Hand: R2 B4 V6 O3 R3 R4
Now the player can play a new card to a losing palette and thus make it win. They will play R3 to P4. This will give P4 a run of 2 cards, making it the palette with the longest run. The game continues and the player draws enough cards to fill their hand if possible.
Canvas: I P1: B2 B7 P2: R1 B5 P3: O1 V7 > P4: O4 R3 Hand: R2 B4 V6 O3 R4 B1 I5
Now the player can choose from one of the two actions as the game has continued.
If you look at the first state again, this is not the only way the player could have proceeded. They could have instead played V6 to the Canvas to change the rule first. That results in a new state depicted below. Notice that the winning palette changed under the current rule to P1.
Canvas: V > P1: B2 R7 P2: R1 B5 P3: O1 V7 P4: O4 Hand: R2 B4 I1 O3 R3 B3 R4
Now the player must make another palette the winner by playing a card to a losing palette. Since P2 is not winning, P2 is a viable candidate. The player plays I1 to P2, which means P2 is winning because there are 2 cards below 4 there. Again, notice that every card to the right of where I1 was has slid down in the hand!
Canvas: V P1: B2 R7 > P2: R1 B5 I1 P3: O1 V7 P4: O4 Hand: R2 B4 O3 R3 B3 R4
The move changed the winning palette again and so the game continues. Note the player must now draw cards to fill their hand.
2.9 Drawing a card when the deck is empty
If the deck runs out of cards during a draw, then the hand is as full as it is going to get. Consider the following state
Canvas: R > P1: B2 B7 P2: R1 B5 P3: O1 V7 P4: O4 Hand: R2 B4 V6 O3 R3 B3 R4 B1
Say the player plays V6 to the canvas and then plays R2 to P3. We get the following state.
Canvas: V P1: B2 B7 P2: R1 B5 > P3: O1 V7 R2 P4: O4 Hand: B4 O3 R3 B3 R4 B1
Now the player must draw cards to fill the hand. If the deck has no cards left, the state does not change but the game continues. If the deck has exactly one card left, then the player draws the last card, resulting in the following state. Note that the game continues because the hand is not yet empty and the player was able to successfully change the winning palette.
Canvas: V P1: B2 B7 P2: R1 B5 > P3: O1 V7 R2 P4: O4 Hand: B4 O3 R3 B3 R4 B1 O2
2.10 Ending the game
The game continues at this pace until the game ends. The game ends under one of two conditions
The player played to a losing palette, but that palette did not win. In this case, the player has lost.
The player has no cards in hand and the deck is empty. In this case, the player has won.
Below are some examples of game states, annotated with whether the game is over in that state. These examples are not exhaustive, but provide some idea of when the game is over and when it is not. Assume for each of these, the deck is empty.
|
| |||
State A: Game is over |
| State B: Game is NOT over |
| State C: Game is NOT over |
Canvas: R P1: R1 P2: R2 > P3: R3 Hand: |
| Canvas: V P1: R1 B7 > P2: R2 B2 P3: V1 Hand: B1 |
| Canvas: R P1: R1 P2: R2 > P3: R3 Hand: B2 |
Notice that it can be difficult to tell from looking at the state if the game has ended in a loss because that condition only occurs after the player plays a card to the palette.
Below are the same three examples, but captioned as to whether the game has been won, can be won, or can only result in a loss.
|
| |||
State A: Game is won |
| State B: Game can be won |
| State C: Game can only lose |
Canvas: R P1: R1 P2: R2 > P3: R3 Hand: |
| Canvas: V P1: R1 B7 > P2: R2 B2 P3: V1 Hand: B3 |
| Canvas: R P1: R1 P2: R2 > P3: R3 Hand: B2 |
3 Building SoloRed
In this assignment you will design the model for this game. The model will maintain the state of the game and update itself when a client specifies moves. You are not required to make the game playable by a user at this point: only you-the-programmer can manipulate the model right now, and there is no mechanism yet for you-the-player to actually specify moves and play the game.
Finally, no method in your implementation should exceed 50 lines. This hampers clarity of your code.
3.1 Cards
Start by modeling a card in the game. You are free to name the class and its methods whatever you want, but it must implement the Card interface. You cannot modify the Card interface, but you may create a new interface that extends the Card interface to define new public methods your model can rely on. Then your Card implementation can implement the new interface. This is how we can extend the functionality of code without changing existing code!
Your card implementation should
behave like a proper “good citizen of Java”, and implement its own
toString
, equals
and hashCode
methods. (See below for
some hints.) The toString
method should render the card as described
above in the Cards section: e.g. Red 4 is printed as "R4"
, etc.
Note: If you realize you do not need to implement an equals
method,
state in the Java documentation of your Card concrete class why it is not necessary. We
want to know the reason for this design decision.
3.2 Expected operations
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 RedGameModel
interface.
You are not allowed to change the interface in any way!
A short explanation of most of the interface follows (the explanation here supplements the documentation provided in the interface):
The
RedGameModel
interface itself takes in a type parameter representing the type of your piece. Your implementation will state what concrete type that is.getAllCards()
should return aList
containing all the concreteCard
s the game can be played with. This is exposed so others can create decks to play or test your game (seestartGame(List<C>, boolean, int, int)
for more information).startGame(List<C> deck, boolean shuffle, int numPalettes, int handSize)
follows the description above, and lets the caller specify the cards to play with, the number of palettes, and the maximum hand size. It also specifies whether the model should shuffle the deck prior to setting up the game.playToPalette
andplayToCanvas
implement the player moves described above. Whenever present, any indices in the parameters are assumed to be zero-based.drawForHand
is what fills the player’s hand and allows the player to play cards to the canvas again.numPalettes()
is useful for determining the setup of the game.numOfCardsInDeck()
,getCanvas()
,getPalette(int)
,winningPaletteIndex()
, andgetHand()
allow the client to peek into the state of the game.isGameOver()
returnstrue
if the game is over, andfalse
otherwise.isGameWon()
returnstrue
if the player won the game andfalse
otherwise.
3.3 Your Model Implementation
Implement the RedGameModel
interface in a class called SoloRedGameModel
:
When defining the class and implementing the interface, fill the type parameter in the
extends
clause with the name of your concrete card class. This will replace the type parameter in your class with your card type. Do not make your class fully generic. This will break the Handins tests.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. Remember that a data representation makes it easy to implement behaviors.
Instantiating the game: Your class should define at least two constructors:
One with zero arguments which initializes your game into a state that’s ready for someone to call one of the
startGame
methods and begin playing. Note the game has not started yet.One with a single argument, a
Random
object that you use to perform the shuffling. If the object is null, this constructor must throw anIllegalArgumentException
. This will be useful for testing randomness related properties.
You may define whatever other constructors you wish on top of those; consider carefully all the methods you are expected to implement, and design your code to avoid as much duplication as possible. Keep in mind that a client should not be able to start a game without calling either
startGame
method!Encapsulation: Your
SoloRedGameModel
class should not have any public fields, nor any public methods other than constructors and the public methods required by theRedGameModel
interface.
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.
3.4 Viewing the model
Our game should have some way of showing us the game board during game
play. You have been provided with an empty RedGameView
interface that
represents a view —SoloRedGameTextView
in the cs3500.solored.view.hw02
package.
public interface RedGameView {
}
public class SoloRedGameTextView implements RedGameView{
private final RedGameModel<?> model;
// ... any other fields you need
public SoloRedGameTextView(RedGameModel<?> model) {
this.model = model;
}
// your implementation goes here
}
Notice the wildcard generic. That is intentional since your view does not need to know the model is filled with Card objects.
Your class should at least have a constructor with exactly one argument of type
RedGameModel
—this model provides all the information the view needs in order to be rendered. The
toString()
method of this class returns aString
that may be used to display the state of the game. Here is an example rendering of a recently-started 4 palette, 6 hand size game; yourtoString()
method should reproduce this:Canvas: R P1: B2 P2: R1 B5 > P3: O1 V7 P4: V4 Hand: R2 B4 V6 I1 O3 R3 V3
Every line should end with a newline character, except the final line —
in this example, the first character of output is the 'C'
in Canvas on the first line and the final character is the last'3'
on the last line.If the hand is ever empty, there will be no cards printed after the colon in "Hand:".
When writing toString, you may need to observe a card in the model. The wildcard type argument means you don’t know the dynamic type, but you do know it is still an Object. Can you still call toString on such an object? If you are unsure, look up the Object java documentation.
3.5 Testing
You will need to add tests to assess whether your model implementation implements
the behavior specified by both the interfaces, this assignment description, and
possibly interesting interactions that are not entirely specified by the interface
(e.g., that createListOfPieces
returns pieces in a particular order and no more
than what is needed to make a board).
To do that, you should create two test classes. One of them should
go in the cs3500.solored
package of the test directory, and it
should test properties of the public model interface.
To test implementation-specific details (i.e. protected or package-private details),
you should create one last test class that you would place in the
cs3500.solored.model.hw02
package itself, so that you can check
protected and package-private implementation details if needed. Note if you do not have
anything with protected or default access modifiers, then this test file is unnecessary.
Note: When writing tests with the public interface, you cannot assume you have access to your Card class. After all, those are implementation-specific. Instead, use the methods on the model’s interface. Is there a method you can use to get a set of cards that the game allows you to use? Can you then rearrange those cards and pass them into the startGame
method to start the game?
Be mindful of which test cases you place in which test class! Technically, you could run all the tests from a single class. But using multiple classes like this helps convey to the reader of your code some of your thought processes behind each test: the reader should understand the examples first, then look at the tests of public behavior, and finally look at implementation-specific fiddly details.
Note: When you submit your full implementation, you will see automated tests that I wrote and run against your code. I gave some of my test methods long but specific names, so that you can try to deduce what my tests are checking for. Just because I have a test for a given scenario, though, does not mean that you shouldn’t write your own test case to confirm your understanding!
4 Package Management
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
src
directory is marked blue, which means IntelliJ believes that this directory contains the source files of your project. If it isn’t marked blue, you need to tell IntelliJ that it should be: right-click on thesrc
folder and select Mark Directory As -> Sources root. To create a new package, right-click on thesrc
directory, select New -> Package. In the dialog box that pops up, enter the new package nameTo 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
src
directory itself.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.
The
src/
andtest/
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!
5 Suggestions on Tackling the Assignment
Read through the assignment and consider what objects exist. Can you use separate classes to keep your design cohesive while giving each object a single purpose? This will help decrease the complexity of the implementation as there are a lot of parts working together.
I suggest getting key observations working and then finishing the view. Being able to see the model makes debugging the operations much easier.
Before implementing the rules for colors, make an example and break down how you would do this by hand. Create a list of steps and use that to guide your implementation.
Do the same for finding a winning palette, but also start with the simplest version of the problem. Break down the steps you would go through to find the winning palette under to the canvas alone. Then consider what breaking ties looks like. A plan goes a long way when designing large code.
6 What to submit
For your implementation: submit a properly-structured zip containing
The model interface (
RedGameModel.java
)Implementation of the model interface (
SoloRedGameModel.java
)The view interface (
RedGameView.java
)Implementation of the view (
SoloRedGameTextView.java
)Any additional classes you saw fit to write
All your tests in one or more JUnit test classes
Note the src folder cannot be empty this time as our tests need your source code.
Again, please ensure all of your project’s sources are in the
cs3500.solored.model.hw02
and cs3500.solored.view.hw02
packages,
accordingly. Please ensure that your project’s test cases are in the
packages explained above. The autograder will give you an automatic 0 if it
cannot compile your code!
7 Grading Standards
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, including length of your methods as established in this assignment
the comprehensiveness of your test coverage
how well you have documented your code
how well you follow the style guide.
whether you submitted unneeded files like the out folder or .idea folder
8 Submission
Wait! Please read the assignment again and verify that you have not forgotten anything!
Please compress the src/
and test/
folders into a zip file and
submit it. After submission, check your submitted code to ensure that you see
two top-level folders: src/
and test/
. If you see anything else,
you did not create the zip file correctly! Please do not include your
output/
or .idea/
directories —
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.