Monte-Carlo Simulation: Blackjack
The main purpose of this project is to give you practice building classes in Java. To do this, we'll simulate a simple version of the card game Blackjack. The objects in a card game include: a Card, a Deck, a Hand, and Game. You'll make a class for each one, connecting them together as appropriate. We'll also start to make use of a few of the many Java library classes.
For this assignment you will implement classes to represent a card, a hand, a deck, and the game Blackjack. The result will be a text-based version of the game that runs in the terminal. You are free to make the game more complex as an extension.
The purpose of the assignment is not to create a Blackjack game for people to play, however, but to study the properties of Blackjack, given a particular rule set, when played over many hands. You are using CS to study the properties of a system defined by a set of rules.
Basic Blackjack Rules
The link above contains an extensive description of the full rules of Blackjack. For this project, you are required to implement only a simplified rule set. You can implement more of the rules as part of project extensions.
There will be only two players in the game: the player and the house. In our simplified rule set, an ace is worth 11 points. Play should use the following procedure.
- The player and the house should each receive two cards dealt from a deck
- The player should play their entire turn before the dealer.
- The player should calculate the value of their hand (the sum of the card values) and choose whether to take a card (hit) or stop at the current value (stand).
- If the player chooses to hit, then if the new value of their hand is greater than 21, the player loses (busts). If the value is less than 21, the player repeats the process until they stand or bust.
- If the player did not bust, the dealer plays.
- The dealer will continue to take a card (hit) until the value of their hand is 17 or greater.
- If the dealer busts, then the player wins.
- If player and dealer both avoided a bust, then the hand with the highest value wins the game. If both hands are of equal value, then the hand is a tie (push).
When playing Blackjack, the deck is usually not shuffled between each game. Instead, the deck is shuffled when it gets below a certain number of cards. For example, when there are fewer than 26 cards (out of 52), then the deck should be reshuffled. Therefore, shuffling should not be done every game, but should be an action that is conditioned on the number of cards left in the deck. How often the deck is shuffled will affect game play because it affects the probabilities of certain cards appearing.
The Card Class
Create a java class called Card, which should hold all information unique to the card. For this assignment, it needs to hold only the value of the card, which must be in the range 1-11. The Card class should have the following methods. In addition, make a main method that uses each of the class methods.
public Card(int v)a constructor with the value of the card, possibly doing range checking.
public int getValue()return the numeric value of the card.
The Hand Class
Create a java class called Hand, which should hold a set of
cards. You can use an ArrayList (
import java.util.ArrayList) to hold
the Card objects. The class should have at least the following
public Hand()initialize the ArrayList.
public void reset()reset the hand to empty.
public void add( Card card )add the card object to the hand.
public int size()returns the number of cards in the hand.
public Card getCard( int i )returns the card with index i. Cast as appropriate.
public int getTotalValue()returns the sum of the values of the cards in the hand.
public String toString()returns a String that has the contents of the hand "written" in a nice format. This will override the default toString method in the Object class.
The Deck Class
Create a java class called Deck, which should hold a set of cards and be able to shuffle and deal the cards. You should use an ArrayList to hold the cards. The class should support the following methods.
public Deck()builds a deck of 52 cards, 4 each of cards with values 2-9 and 11, and 16 cards with the value 10. Note, you probably want the constructor to call the build() method, below.
public void build()builds a deck of 52 cards, 4 each of cards with values 2-9 and 11, and 16 cards with the value 10.
public int size()returns the number of cards in the deck.
public Card deal()returns the top card (position zero) and removes it from the deck.
public Card pick( int i)(optional) returns the card at position i and removes it from the deck.
public void shuffle()shuffles the deck. This method should put the deck in random order. One way to do it is to build a fresh second deck and then make n random picks from it, where n is the size of the deck at the time of the shuffle. Note that the first pick should be in the range [0, n-1], the second pick should be in the range [0, n-2] and so on. You may want to generate a seed for the Random object by using the function
System.currentTimeMillis()so that each run of a game is different. You are expected to implement the shuffle method. Please do not use Collection.shuffle().
public String toString()returns a String that has the contents of the deck "written" in a nice format (so that you can see the ordering of the card values).
The Blackjack Class
Create a class called Blackjack that implements a simple version of the card game. The class will need to have fields for a Deck, a Hand for the player, and a Hand for the dealer. The main function for the Blackjack class should implement one complete game.
public Blackjack()should set up and reset the game (call reset(), below). Make sure to tell the reset function to create a new deck.
public void reset( boolean newDeck )should set up and reset the game. If newDeck is true, then the function should create a fresh, shuffled deck. Otherwise, it should not modify the deck, just clear the player and dealer hands.
public void deal()should deal out two cards to both players.
public String toString()returns a String that has describes the state of the game (the total values in the player and dealer hands).
public boolean playerTurn()have the player draw cards until the total value of the player's hand is equal to or above 16. The function should return false if the player goes over 21 (bust).
public boolean dealerTurn()have the dealer draw cards until the total of the dealer's hand is equal to or above 17. The function should return false if the dealer goes over 21.
Use the main function of your Blackjack class to test the above functions. Make sure they are working properly by dealing two hands and then calling the playerTurn and dealerTurn functions, printing out the game state and making sure the rules are correctly implemented.
Create a function to play a single game.
public int game(boolean verbose)the game function should play a single game of Blackjack following the procedure outlined above. The reset method should be called prior to each game, specifying whether the deck should be reset and re-shuffled. The game function should return a -1 if the dealer wins, 0 in case of a push (tie), and a 1 if the player wins.
If the parameter
verboseis true, then the game function should print out the initial and final hands of the game and a statement about the result (dealer/push/player).
Update your main function so it creates a Blackjack object and then uses it to play three games with verbose being true. Make sure your game function is working correctly.
Storing Game Output
Once you have completed the above four classes, generate a printout of three different games. You can send output to a file using the greater than symbol on the command line. For example, the command
java Blackjack > mygames.txt
would play a game and send it to the file mygames.txt.
Make one more class called Simulation. This class should have only a main function that executes 1000 games of Blackjack. The code should create and re-use a single Blackjack object. It should keep track of how many games the player wins, how many the dealer wins, and many are pushes. Print out the total in the end both as raw numbers and as percentages.
Projects are your opportunity to learn by doing. They are also an opportunity to explore and try out new things. Rather than list out every activity for a project, the defined part of each project will be about 85% of the assignment. You are free to stop there and hand in your work. If you do all of the required tasks and do them well, you will earn a B/B+.
To earn a higher grade, you can undertake one or more extensions. The difficulty and quality of the extension or extensions will determine your final grade for the assignment. Extensions are your opportunity to customize the assignment by doing something of interest to you. Each week we will suggest some things to try, but you are free to choose your own.
A common question is "how much is an extension worth?" The answer to that question depends on the extension, how well it is done, and how far you pushed it. As teachers, we prefer to see one significant extension, done well, where you explore a topic in some depth. But doing 2-3 simpler extensions is also fine, if that's what you want to do. Choose extensions that you find interesting and challenging.
The following are a few suggestions on things you can do as extensions to this assignment. You are free to choose other extensions, and we are happy to discuss ideas with you.
- Create a new function in your Blackjack class called playerTurnInteractive() that uses input from the terminal to control the player's actions. Then create a new class Interactive that lets you play Blackjack on the terminal.
- Add in more of the rules for Blackjack. For example, an Ace (value 11 in the default case above) can have the value 1 or 11, whichever is more advantageous. If you add in the Ace rule, then you will also want to take into account a real Blackjack (2 cards totaling 21) when evaluating the winning hand. A Blackjack beats a 21 with more than 2 cards. See how these rules affect the simulation results.
- Make your game use 6 decks, and reshuffle if only 1 deck is left. Play the game 1000 times, and observe how many games the player wins, how many the dealer wins, and many are pushes. Compare with using only a single deck.
- Run the simulation with different decision rules for the player and see how it affects the outcome percentages over many games (>= 1000).
- Add a type of betting strategy to the simulation and see if the player can win money even while losing more games than winning.
- Try running the simulation with different numbers of games and see how variable the results are. For example, run the simulation using M games (e.g. M = 100) and do this N times (e.g. N = 10). Then calculate the standard deviation of the results. Then you can plot the standard deviation versus the number of games (M) in the simulation to see how the results stabilize as you use a larger number of games.
- For any assignment, a good extension will be to implement a Java class yourself and demonstrate that it has the same functionality as the Java class. For example, you could implement your own ArrayList class for this assignment.
- Another good extension for any assignment is to identify in comments
when dynamically allocated memory is being "lost." Dynamic memory is any memory created with a
newcommand. Indicate the line of code in which the last reference to an object referencing dynamic memory is removed.)
Your report should have a simple format.
A brief summary of the project, in your own words. This should be no more than a few sentences. Give the reader context and identify the key purpose of the assignment.
Writing an effective abstract is an important skill. Consider the following questions while writing it.
- Does it describe the CS concepts of the project (e.g. writing well-organized and efficient code)?
- Does it describe the specific project application?
- Does it describe the data structure you used?
- Does it describe the results of your simulation or analysis?
- Is it concise?
- Are all of the terms well-defined?
- Does it read logically and in the proper order?
- An explanation of your solution, focusing on the interesting bits. In this assignment, for example, shuffling the deck and doing the overall simulation are the interesting bits (e.g. does the house always win?). You can include code snippets in your writeup to help describe your solution, but do not include whole functions. A code snippet should be less than 5 lines of code.
- Are 1000 games sufficient to provide an accurate estimate of the win percentage for the player and dealer? What are those percentages?
- What is the number of games the simulation has to play in order for the dealer's win percentage always to be above the player's win percentage over ten runs of the simulation?
- Printouts, pictures, or results to show what you did. For this assignment, you should include the results of the three test games and the overall simulation.
- Other results to demonstrate extensions you undertook. If you tried different player strategies, for example, show how those affected the overall simulation results.
- A brief conclusion and description of what you learned.
- A list of people you worked with, including TAs, and instructors. Include in that list anyone whose code you may have seen, such as those of friends who have taken the course in a previous semester.
Make your report for the project a wiki page in your personal space. If you have questions about making a wiki page, stop by a professor's office or ask in lab.
Once you have written up your assignment, give the page the label:
You can give any wiki page a label using the label field at the bottom of the page. The label is different from the title.
Do not put code on your writeup page or anywhere it can be publicly accessed. To hand in code, put it in your folder on the Courses fileserver. Create a directory for each project inside the private folder inside your username folder. Please use a naming convention like project01 for your project folders.