Project 2 -- Unfair Solitaire?
Due: Tuesday, January 25, 2011
Objectives
- Practice using familiar concepts such as arrays, loops, and
conditionals in the Java programming language
- Practice with meaningful instance variables
- See an example of how a computer can be used to perform simulations
- See how real-world objects translate into Java classes
and how they can work together in a program
Introduction
There is a version of solitaire that you've been playing all of your life.
In all that time, however, you've only won twice. Perhaps the game is just
really hard to win. Or perhaps you're just really unlucky. To find out
which, you'll write a program that simulates playing the game a bunch of
times and you'll count the number of times that it wins. Then you'll be
able to tell if you should go out and buy a lucky horseshoe or not.
The Game
Starting with a standard 52-card deck, the game is played as follows.
Suits are the only important things in this game; card values are
ignored.
- Shuffle the deck and deal 4 cards, face up and in a single row.
- Remove cards from those showing. You can remove cards as follows:
- If the rightmost 4 cards all have the same suit, remove those 4 to the
discard pile. (At the beginning of the game, they'll only be 4 cards
showing, but you'll eventually have more.)
- If the first card and the fourth card of the rightmost 4 cards have the
same suit, remove the two cards in between them (the second and third
cards) to the discard pile.
- Every time you remove some cards, repeat (a) and (b) (in that order)
until you either can't remove any more cards or you have less than four
cards showing.
- Deal a card face up to the right of those already showing. Repeat this
if there are less than four face-up cards.
- Repeat steps 2 and 3 until you have no more cards in the deck. You win
if all of the cards have been removed to the discard pile.
The two pictures below demonstrate the two cases under which you can remove
cards to the discard pile.
Your Mission
Create a program that simulates playing solitaire as described above.
Remember, you're not building a game to be played by a user.
You're simply building a program that will play the game internally. The
output of your program should be the number of times that the computer wins
out of 1000 simulated games. Include the win-rate as a percentage. Then
repeat the experiment for 2000 games, 3000 games, and so on up to 10000
games. Thus your program's output will look something like this (with the
questions marks replaced by real numbers):
?/1000 games won = ?%
?/2000 games won = ?%
?/3000 games won = ?%
?/4000 games won = ?%
?/5000 games won = ?%
?/6000 games won = ?%
?/7000 games won = ?%
?/8000 games won = ?%
?/9000 games won = ?%
?/10000 games won = ?%
I should only have to run your code once to get the above output.
Don't forget to think about what your output means. Is the game hard to
win? Or are you just unlucky?
The Details
The object-oriented way of programming will be a big advantage to us here
since you can easily imagine real-world objects that will be needed in
order to design the simulation. To that end, your program should consist
of at least the following four classes:
- Card: This class represents a single playing card.
- instance variables: the card's suit and value
- methods:
- a non-default constructor
- a toString method that returns the card as a printable string
(like "Jack of clubs").
- appropriate getter methods (setter methods aren't really
needed since once a card is created, it won't really change its value or
suit.)
- Deck: This class represents a collection of Cards.
- instance variables:
- the collection of Card objects
- a variable to keep track of which card is next to be dealt. See the
deal method below for how to use this variable.
- methods:
- a constructor
- a toString method that returns the entire deck as a printable
string (concatenate "\n" when you want to include a new line)
- a deal method that returns the next card dealt. When we deal a
card, we won't actually remove it from the deck. Instead, we'll just keep
track of an instance variable that marks the place in the deck between
already-dealt cards and cards yet to be dealt. So this method updates that
variable. This is a common trick in computer science since it's much more
time efficient than having to move Card objects around. Make
sure you understand this (and see me if you don't)!
- a shuffle method that randomizes the deck. One way to do this
is to pick a random number between 0-51 and swap that index's card with the
card at the first (index 0) position in the array. Then pick a number from
1-51 and swap that index's card with the second (index 1) position in the
array. Then pick a number from 2-51 and swap that index's card with the
card at index 2. Lather, rinse, repeat. How do you pick a random number?
Check out the docs for the java.util.Random class. Don't forget the
import statement.
- SolitaireSimulator: This class handles the simulation.
- instance variables:
- a Deck of cards
- an array of cards representing the face-up ones
- the current number of cards that are facing up
- methods:
- a constructor
- a playGame method which is the heart of the simulation. This
method should repeatedly deal a card and then try to remove cards according
to the rules described above. It should continue this until all cards are
dealt. This method should return true if the game was won
and false if the game was lost. Try to be modular here by
breaking up this method into subtasks (submethods) that you can call on.
- Client: This class has the main method. It should
call playGame the appropriate number of times and print out
statistics to System.out.
You don't have to follow the exact structure I have outlined above (you can
have other methods and classes, for example), but you should at least have
the four classes listed above.
Remember to practice good programming skills:
- Comment each method and class describing what it does along with any
non-obvious preconditions (things you are assuming to be true when
the method starts) or postconditions (things you know will be true
when the method ends). For example, the playGame method may
assume a precondition that the deck of cards has already been shuffled.
- Indicate special cases in your comments. For example, what does
the deal method return if there are no more cards to be dealt?
- Use meaningful variable names (and method and class names too)
- Test each method individually
- Use private methods where appropriate
Grading
This project will be worth 50 points. It will
be divided up this way:
- 20 points for correct output.
- 10 points for the overall design of your code, including correct
instance variables and having straightforward logic.
- 10 points for understandability. This includes things like naming,
constants, and documentation.
- 10 points for good modularity.
Remember to turn in both a paper and an electronic copy of your
project.
Administrative statement
Programming assignments, like homework assignments,
are individual projects. I encourage you to talk to others
about the general nature of the project and ideas about how to pursue it.
However, the technical work, the writing, and the inspiration behind these
must be substantially your own. If any person besides you contributes in
any way to the project, you must credit their work on your project.
Similarly, if you include information that you have gleaned from other
published sources, you must cite them as references. Looking at, and/or
copying, other people's programs or written work is inappropriate, and will
be considered cheating.