This coursework involves you designing and implementing a simulation of a card game called Blackjack. You will first implement general classes that could be used in any card game, then implement players for Blackjack that utilise different playing strategies. You should complete part 1 before attempting part 2. Some questions in part 1 are to test your general understanding and are not used in the game.
Information on Cards:
There are two variables associated with a card:
Suit: clubs, diamonds, hearts and spades and
Rank:TWO, THREE, FOUR, FIVE, SIX,SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING, ACE.
Each rank has a value. TWO has value 2, THREE 3 etc. JACK, QUEEN, KING all count for 10, and ACE counts 11. There are 52 different possible cards.
Question 1: Implement classes Card, Hand and Deck that can be used for a variety of card games.
Question 2: Implement classes BlackjackTable, BlackjackDealer, BasicPlayer, HumanPlayer, IntermediatePlayer and AdvancedPlayer for the card game BlackJack.
Design and implement three classes to be used in the card game described in Question 2.
Class Card
1. Make the class Serializable with serialisation ID 111.
2. Use two enum types for Rank and Suit. The Rank enum should store the value of each card. The Rank enum should also have a method getPrevious, which returns the previous enum value in the list. So, for example, if the method is called on FOUR, THREE should be returned. If the method is called on TWO, ACE should be returned. Rank should also have a method getValue that returns the integer value of the card.
3. The Card class should contain two variables called rank and suit of type Rank and Suit. The class Card should have a single constructor with the Rank and Suit passed as arguments.
4. Make this class Comparable so that compareTo can be used to sort the cards into descending order (*see below).
5. Implement accessor methods getRank()and getSuit() that simply return the rank and suit.
6. add a toString()method.
7. Add a static method called sum that returns the sum of the value of two cards (so the sum of EIGHT and QUEEN is 18).
8. Add a static method called isBlackjack that returns true if the two cards passed are an ACE and a card of value 10 (so, for example < ACE, QUEEN> and < KING, ACE> are both blackjacks).
9. Add two Comparator classes. One, called CompareAscending, should be used to sort the cards into ascending order by rank (*see below), the other, CompareSuit, should be used to sort into ascending order of suit then rank, i.e. all the clubs sorted by rank, then all the diamonds, then the hearts and finally the spades.
10. Write a main method that demonstrates your code is correct by calling all the methods you have implemented with informative output to the console.
(*) Note that you sort first by rank, then by suit. So a List
10 Diamonds, 10 Spades, 2 Clubs, 6 Hearts
sorts to ascending order as
10 Diamonds, 10 Spades, 6 Hearts, 2 Clubs
i.e. for the descending sort the rank order is reversed, but the suit order is maintained.
Class Deck
1. Deck should contain a list of Cards.
2. The Deck constructor should create the list and initialise all the cards in the deck. A Deck should start with all possible 52 cards.
3. Write a method to shuffle the deck that randomises the cards. To gain full marks for this section you should write your own method to shuffle rather than use the built in Collections method.
4. Implement a method deal that removes the top card from the deck and returns it.
5. Add methods size (returns number of cards remaining in the deck) and a final method newDeck (which reinitialises the deck).
6. Add a nested Iterator class called SecondCardIterator that traverses the Cards by going every other card. So a deck 10 Diamonds, 10 Spades, 2 Clubs, 6 Hearts, 8 Hearts Would iterate in the order 10 Diamonds, 2 Clubs, 8 Hearts (this part of exercise is just to show you understand iterators).
7. Make the class Iterable, so that by default it traverses in the order they will be dealt.
8. Make the class Serializable with serialisation ID 112. Make it so that the deck is saved with the cards in SecondCardIterator order (this may seem a strange thing to do, as it wont save all cards, but it is an exercise to demonstrate you understand Serialization).
9. Write a main method that demonstrates your code is correct by calling all the methods you have implemented with informative output to the console.
Class Hand
1. A Hand contains a collection of Cards. The class should provide a default constructor (creates an empty hand), a constructor that takes an array of cards and adds them to the hand and a constructor that takes a different hand and adds all the cards to this hand.
2. A Hand should store a count of the number of each rank that is currently in the hand. These counts should be modified when cards are added or removed from the hand.
3. A Hand should store the total value(s) of cards in the hand, with ACES counted both low and high. So a Hand < 10 Diamonds, 10 Spades, 2 Clubs> has total value 22, a Hand < 10 Diamonds, 10 Spades, Ace Clubs> has total value of either 21 or 31 and a Hand < 10 Diamonds, Ace Spades, Ace Clubs> has total value 12, 22 or 32.
4. Hand should have three add methods: add a single Card, add a Collection typed to Card and add a Hand
5. Hand should have three remove methods: remove a single Card (if present), remove all cards from another hand passed as an argument (if present) and remove a card at a specific position in the hand. The first two methods should return a boolean (true if all cards passed were successfully removed), the last should return the removed card.
6. Hand should be Serializable with serialisation ID 102.
7. Hand should be Iterable. The Iterator should traverse the cards in order they were added. Note this should still be possible even if the sort routines (part 8 and 9 below) have been called.
8. sortDescending to sort a Hand into descending order (using Card compareTo),
9. sortAscending to sort ascending order (using CompareAscending).
10. countSuit that takes a suit as an argument and returns the number of cards of that suit.
11. countRank that takes a rank as an argument and returns the number of cards of that rank.
12. toString displays the hand.
13. isOver that takes an integer as an argument and returns true if the lowest possible hand value is greater than the value passed.
14. reverseHand that returns a new hand, but with the cards in reverse order.
15. Write a main method that demonstrates your code is correct by calling all the methods you have implemented with informative output to the console.
Your second Java task is to implement a simulation of a simplified version of the game Blackjack that uses the classes from part 1.
Blackjack is a card game similar in structure to pontoon and 21. The game is run by the dealer who takes the bets and deals the cards to the players. Each player first places a bet. The dealer then deals two cards to each player, and one to himself. The dealer then goes to each player in turn, who then plays their hand. The dealer then plays their hand and finally settles the bets.
The game play principle is that the players win their bet if they get a higher total in card values than the dealer without exceeding 21. A hand exceeding the score of 21 is said to be bust. A hand is played by the dealer asking the player whether they way to take a card (this is called to hit) or to stay with their current score (to stick). It is up to the player to determine how many cards to take, as long as their score is below 21. You will be implementing different player strategies (see below).
Scoring a hand:
Hands are scored by their ranks (suits are irrelevant for hand scoring). Thus < Seven Clubs, Eight Diamonds> scores 15 and < Seven Clubs, Five Clubs, Ten Spades> scores 22. Jacks, Queens and Kings all score 10. So < Seven Clubs, Jack Diamonds> scores 17, as does < Seven Clubs, King Clubs>. The scoring of hands is made more complex by Aces, which score either 1 or 11. Thus < Seven Clubs, Ace Clubs> could be 8 (the so called soft total) or 18 (the hard total). You should already have methods to deal with hand scores in the class Hand (task 3 in the class Hand).
Dealer play:
Whereas players can stick on any number they wish, the dealer follows a fixed algorithm for playing their hand. They always draw cards until their total is 17 or higher, or they have bust. Aces always have the hard value, unless this will make the hand exceed 21. A dealer will stick on a soft total of 17 or more. So for example, < Ace, Six> scores 17, and the dealer will stick, but < Ace, Seven, Six> scores 14, so the dealer will hit.
Blackjacks
A black jack is a two card hand consisting of an Ace and a Ten, Jack, Queen or King. It is paid out differently to other hands (see below). If a player has a blackjack, they should not be able to take any more cards.
Settling Bets
Players lose their bet if they go bust, irrespective of what happens to the dealer. If the player sticks on any value 21 or lower and the dealer busts, the player wins a sum equal to their bet. Otherwise, the two scores are compared. If the player hand score is greater than that of the dealer, the player wins. If the player hand score is less than that of the dealer, the player loses. If they are equal (a push), the player retains their stake (i.e. neither wins nor loses).
The exception to this is when player and/or the dealer have a blackjack. If the player has a blackjack and the dealer does not, the player wins twice their stake (in real blackjack this is 1.5 times the stake, but we will use twice). If the dealer has a blackjack and the player does not, the dealer wins even if the player has a total of 21.
Game Cycle:
1. Dealer asks each player to place a bet (which the dealer records. The player cannot change their bet after the cards are dealt).
2. Dealer deals two cards to each player, then one to themselves.
3. For each player, the dealer deals cards until the player wants to stop or the hand is bust.
4. The dealer plays their hand.
5. The dealer settles bets.
Splitting and Doubling
In the real game, you are allowed to split two identical cards into two hands and double your bet on some hands. You do not have to implement this.
The Task
Please note there are many ways to simulate this game and there is not necessarily one correct solution I am looking for. However, I do want you to follow the basic structure I am advocating (even though you may prefer a different model, I want to see if you can follow the recommended structure). This exercise is intentionally less prescribed than the first exercise, and so there is room for interpretation. If you want clarification, post a question on the discussion board and if appropriate, I will explain. Please note:
1. You must use the Deck and Hand class from exercise 1.
2. Where possible, you should use Collection objects and Iterators instead of arrays.
3. Encapsulate rather than use global variables. Thus a player should not have direct access to the dealer or the table. Rather, information should be passed via methods
4. Comment your code. Remember, I have to read all of these, please make my life easier by explaining what is going on!
A game runs in a BlackjackTable class. The BlackjackTable should contain a Dealer object and a collection of Player objects. I have provided an interface for Dealer and Player and there is information on what each method should do in the code. The collection of Player objects created in the BlackjackTable should be connected to the Dealer by the method assignPlayers. The following variables are associated with a BlackjackTable.
Maximum number of players: default to 8
Minimum and maximum bet size: default to 1 and 500.
BlackjackTable should be Serializable, so it can be saved to bytecode. The game should be run in the BlackjackTable class by calling methods on the Dealer and Player objects.
2.1 Basic Game:
Design, implement and test a class BlackjackDealer that implements the Dealer interface. The BlackjackDealer should use a single deck of cards (in reality, it is usually 6 decks). Before the start of each hand, the dealer should check how many cards are left and if there are fewer than 1/4 of the total, the dealer should create a new deck, shuffle, and notify all players via the Player method newDeck.
Design, implement and test a class BasicPlayer that implements the Player interface. This player should start with a cash balance of 200 and always bet 10. The BasicPlayer should use the same strategy as the dealer, i.e. to take cards until the total is 17 or greater. BasicPlayer should continue to bet until they have run out of cash, at which point they should stop playing (how you model this is up to you).
In the BlackjackTable class, write a static method called basicGame() that creates a BlackjackTable object with four BasicPlayers, then runs a simulation of hands. After each hand, ask for input from the console on whether to continue, and if we continue how many hands to step through. So, for example, I may choose to execute one hand at a time, or run one hand, then 100 hands, then one again. If all players run out of funds, stop the current run and ask the user if they wish to create four new players and continue. BlackjackTable should be Serializable, so give the user the option of saving or loading the game at each request to continue. I should not be able to crash the program when running this method.
2.2 Different Players
Extend the BasicPlayer class to classes called HumanPlayer, IntermediatePlayer and AdvancedPlayer. The HumanPlayer should decide on bet size and drawing cards via interaction with the console. The IntermediatePlayer should base their decision to hit or not on the dealers single showing card. The rules for IntermediatePlayer are:
If the dealers card is 7 or higher (including ace), the player should hit until they are bust or have a total of 17 or more. If the dealers card is 6 or lower (excluding ace), the player should hit until they are bust or have a total of 12 or more. If the player has an Ace, they should always stick on a soft total of Nine or Ten, (e.g. < Ace, Eight>, < Ace, Nine>, < Ace, Ace, Seven> or < Ace, Ace, Ace, Ace, Five>), and always take a card on a soft total of Eight or less. IntermediatePlayer should still bet a fixed amount on each hand (default to 10).
The AdvancedPlayer should use a simple form of card counting. The AdvancedPlayer should follow exactly the same playing strategy as the IntermediatePlayer. The only difference is in the betting strategy. Card counting allows the player to get an advantage over the dealer, and is banned in real casinos. The basic principle of counting is that if there are more cards with the value of 10 in the deck, then the player has a better chance of winning (because the dealer is more likely to bust). The idea is to estimate the number of 10 cards there are, and if it is above some threshold, increase the bet size. After each hand, the player should update its count by observing how many low cards and high cards there have been. So starting from 0 when there is a new deck, every low card valued 2,3,4,5 and 6 adds 1 to the count, every high card valued 1, Jack, Queen, King or Ace subtracts one from the counts.
Thus if the following was the first hand for three players
Player 1: King, Seven, Player 2: Three, Five, Six, King
Player 3: Eight, Two, Four, Five Dealer: Ten, Ten
The counts would be 2:
Player 1: -1,0 Player 2: 1,1,1,-1 Player 3: 0, 1,1,1 Dealer: -1, -1
The current count determines bet size. If 1 unit is the basic bet, then if the count is negative bet one unit, otherwise bet the count times the basic bet. Thus if the player above has a basic bet of 10, the bet on the next game would be 20.
Write three static methods to demonstrate your players. The first should be identical to basicGame() but use intermediate players. The scond, called humanGame()should create one basic player and one human player, and run for as long as the human wants to play, or has any money left. The second, called advancedGame() should have a Basic, Intermediate and Advanced player. Run the game for a large number of hands (dependent on the speed of execution) and save the average profit or loss per deck to a text file (and print to the console). This method does not have to print out the game play as it is happening.