CS 173: Homework 2
Solitaire
Due Sunday, March 2
In this assignment, to write a spider solitaire game using the Deck and Card
classes you created in Lab 1 and using linked lists.
Basic rules of the assignment
- No variable should be public.
- All methods should have comments above the definition.
- All variables should have comments next to the variable.
- All classes should have comments above the class.
- All loops and complicated code should have comments.
- All comments should be in JavaDoc style.
Important rules for coding!
- Only your Deck class should know anything about the deck of cards. You should not hard code any
other information into your other classes (such as number of cards in a deck, the number of suits in the deck,
or the fact that the ace is the smallest card and the king is the largest). I should be able to substitute a
new Deck class with a different number of suits, different colors, and different number of cards in each
suit and the rest of your code should work with no modifications!.
- Every method should be short and do only one thing. Other than a method that initializes the game and
a method that runs the loop for user input, your methods should not have a lot of code.
LinkedList code
For the homework, we will use the linked list code developed in class, minus the Comparable interface.
The main change is that there are two protected methods, getHead and setHead. These methods
can be used to change the head pointer of the linked list. They are protected so only code that is within
the LinkedList class or a class that extends LinkedList can manipulate the head pointer.
If you wish to use the linked lists with the Comparable interface, you need to make the Card
class implement the Comparable interface.
You need to add the following methods to LinkedList.java.
- String toString(): outputs a String listing the contents of the linked list in order.
LinkedList<Integer> l = new LinkedList<Integer>();
l.addToFront(3);
l.addToFront(2);
l.addToFront(1);
l.toString() => "1 2 3"
- String toStringReverse(): output a String listing the contents of the linked list in reverse.
You can either create an iterative method or you can create a recursive method by adding an appropriate method to LLNode.java.
LinkedList<Integer> l = new LinkedList<Integer>();
l.addToFront(3);
l.addToFront(2);
l.addToFront(1);
l.toStringReverse() => "3 2 1"
- void append(LinkedList<T> list): appends list to the end of this list. You can
use the recursive append from lab or write your own interative append method.
LinkedList<Integer> l = new LinkedList<Integer>();
l.addToFront(3);
l.addToFront(2);
l.addToFront(1);
LinkedList<Integer> l2 = new LinkedList<Integer>();
l2.addToFront(13);
l2.addToFront(12);
l2.addToFront(11);
l.append(l2);
l.toString() => "1 2 3 11 12 13"
- void prepend(LinkedList<T>): prepends list to the start of this list.
LinkedList<Integer> l = new LinkedList<Integer>();
l.addToFront(3);
l.addToFront(2);
l.addToFront(1);
LinkedList<Integer> l2 = new LinkedList<Integer>();
l2.addToFront(13);
l2.addToFront(12);
l2.addToFront(11);
l.prepend(l2);
l.toString() => "11 12 13 1 2 3"
- LinkedList<T> removeUpTo(T element): remove every element up to element from this list, and
place these elements in a new list in the same order and return this new list. If element is not in the
list then you can either remove all or none of the elements, which ever is easier for you to code.
LinkedList<Integer> l = new LinkedList<Integer>();
l.addToFront(5);
l.addToFront(4);
l.addToFront(3);
l.addToFront(2);
l.addToFront(1);
l.toString() => "1 2 3 4 5"
LinkedList<Integer> l2 = l.removeUpTo(3);
l.toString() => "4 5"
l2.toString() => "1 2 3"
Creating the Spider game
Rules of the game
You are to create a simplified version of spider solitaire. Here are the rules
of the simplified game.
- The game has 10 tableau piles, and it has one foundation pile for each suit.
- Initially, the foundation piles are empty, the tableau piles each get 2 face down cards except
that some of the tableau piles get an additional face down card
so that the number of cards remaining in the deck is a multiple of 10. (You may assume that
the deck has at least 32 cards.) Then one face up card is placed on top of each
tableau pile.
- If the last remaining face-up card from a tableau pile is removed, the top upside down card is
turned face up.
- The game ends if all cards are moved into the foundation piles.
- At any time, you can deal a new row of face up cards from the deck to the tableau piles.
The legal moves for our game are as follows:
- A face up card can be moved onto another card with the next highest face value. For example, a 5 can be placed on a 6.
The suits of the cards do not matter.
- You can move a sequence of face up cards as long as the cards are all of the same suit and the sequence is in
ascending order (starting with the top card) with no
skips. For example, you can move the 6 of Spades, 5 of Spades, and 4 of Spades as a sequence onto any 7.
- Only the top most card or sequence of any pile can be moved.
- Any card or sequence of cards can be moved to an empty tableau pile. For our game, if you move a card to an empty pile,
then you will move the largest legal sequence containing that card.
- Only a complete sequence (all cards of the suit, in order) can be moved to a foundation pile.
Implementation details.
Each pile should be implemented as a linked list. Make the Pile class extend the linked list class,
with the appropriate generic. Hint: The coding is a much easier if you organize the piles so that the top
most card is the head of the linked list.
Your Pile class should contain the following methods:
A method that returns the top card of the sequence that starts with the first card of the pile. For example, if your
pile is X X H_K D_Q S_J S_10 S_9 (9 of spades is the card on top of the pile) then the method should return
the Card representing the jack of spades.
A method that takes Card and if there is a sequence from this pile that can be legally moved onto the Card,
the method returns the top card of that sequence. If not, then it returns null or throws an exception.
Your Spider class should have a main method that calls one method to initialize the game (by creating
the necessary piles, shuffling the deck, and dealing out the initial cards) and calls a second method to play the game.
The method for playing the game should contain a loop that
repeatedly prints the current board, gets the next move, and updates
the game. For example, here is a possible game state at the start of the game:
C:
D:
H:
S:
0: X X X H_K
1: X X X S_6
2: X X H_Q
3: X X C_5
4: X X S_5
5: X X S_4
6: X X D_9
7: X X H_4
8: X X S_J
9: X X S_A
Move:
And here is a possible state after you have played for a while:
C:
D:
H:
S: S_K S_Q S_J S_10 S_9 S_8 S_7 S_6 S_5 S_4 S_3 S_2 S_A
0: C_10
1: H_K H_Q D_J D_6 C_5 C_4
2: D_K
3: X H_A
4:
5: X C_7
6: X X D_9 D_8 D_7 C_K
7:
8: X D_A C_J
9: X D_5 H_4
Move:
This is only an example. You can make your board look differently. I
implemented this by setting the toString method in Card to
return "X" if the card is face down, and I set the toString method
for Pile call the toStringReverse method of LinkedList.
For the user entered actions, use the Scanner class to
get user input. You can use
Scanner scanner = new Scanner(System.in);
to create a Scanner object, and then use
scanner.hasNext() to test for more input and
scanner.next() to return the next entered string.
Except for the quit and deal commands, you can assume every command consists of
two single character Strings. Here are the valid user commands:
| q | quit the game |
d | deal a new face up card from the deck to each tableau pile. |
| # f | move the sequence of cards from one tableau pile to a foundation pile. |
| # # | move a sequence of cards from one tableau pile to another |
where the # is a placeholder for a number between 0 and 9 that
indicates one of the tableau piles.
Some Hints to Make Coding Easier
Don't place the move code directly into this loop that reads the user input. Instead, create a method or methods that
perform each type of move and have the loop call the appropriate method. For example, create a method called
pile2pile(int fromPile, int toPile) that handles a move from one pile to another. Similarly, create a method
pile2foundation(int pile) that handles a move from a tableau pile to a foundation, and create a method
dealMoreCards() that handles the case when the user wants to deal additional cards to the tableau.
Don't create a separate variable for each tableau pile. Since every tableau pile behaves the same, create an array of piles.
Don't create a separate variable for each foundation pile. Since each foundation pile behaves the same, and since the number
of foundation piles depends on the number of suits, create an array of piles.
Take advantage of loops and the arrays to simplify your code. For example, use loops and the arrays of piles when you
deal out the initial cards. If you do so, you only need a few lines of code to deal out the cards appropriately.
Submit your assignment
Email all your class files to me by the assignment deadline.