TwentyOne.java
Download TwentyOne.java
1: /** TwentyOne.java - A program that allows the user to play the card game 21
2: * against the computer "dealer".
3: *
4: * Written 3/2006 by Wayne Pollock, Tampa Florida USA.
5: */
6:
7: import java.util.*;
8: import javax.swing.JOptionPane;
9:
10: /** Only the dealer's first card is shown during play. The value of a hand
11: * is determined by adding the ranks of its cards, with
12: * picture cards worth 10, and an Ace worth either 1 or 11. As long as
13: * the value of the player's hand is less than 21 they can take a "hit",
14: * that is add a card from the "shoe" to their hand. The higher score wins,
15: * with a tie going to the dealer. In this simple version of the game,
16: * the dealer will always hit on 16 and "stand pat" on 17.
17: */
18: class TwentyOne
19: {
20: private static final String title = "The Card Game 21";
21: private static final int STAND_PAT_POINT = 17;
22:
23: private static Shoe shoe = new Shoe();
24:
25: public static void main ( String [] args )
26: {
27: int rc = 0; // The return code from the showConfirmDialog
28:
29: do
30: { playGame();
31: rc = JOptionPane.showConfirmDialog( null, "Play Again?",
32: title, JOptionPane.YES_NO_OPTION );
33: } while ( rc == JOptionPane.YES_OPTION );
34: }
35:
36: private static void playGame ()
37: {
38: int rc = 0; // The return code from the showConfirmDialog
39: Hand dealersHand = new Hand( shoe ),
40: playersHand = new Hand( shoe );
41:
42: StringBuilder initMsg = new StringBuilder( "Dealer shows : " );
43: initMsg.append( dealersHand.firstCard() );
44: initMsg.append( "\n\nPlayer's Hand: " );
45:
46: while ( playersHand.value() < 21 )
47: {
48: StringBuilder msg = new StringBuilder( initMsg );
49: msg.append( playersHand.toString() );
50: msg.append( "\n\nDo you want another card?" );
51:
52: rc = JOptionPane.showConfirmDialog( null, msg, title,
53: JOptionPane.YES_NO_CANCEL_OPTION );
54:
55: if ( rc != JOptionPane.YES_OPTION )
56: break;
57: playersHand.hit();
58: }
59:
60: if ( rc == JOptionPane.CANCEL_OPTION )
61: return;
62:
63: // Determine who won, and display appropriate message:
64: String msg = null;
65:
66: if ( playersHand.value() > 21 )
67: {
68: msg = "Sorry, you lost!\n\n";
69: } else
70: {
71: while ( dealersHand.value() < STAND_PAT_POINT )
72: dealersHand.hit();
73:
74: if ( dealersHand.value() > 21 ||
75: playersHand.value() > dealersHand.value() )
76: msg = "Congratulations, you won!\n\n";
77: else
78: msg = "Sorry, you lost!\n\n";
79: }
80:
81: msg += "Dealers Hand: " + dealersHand.toString() + "\n\n" +
82: "Players Hand: " + playersHand.toString();
83:
84: JOptionPane.showMessageDialog( null, msg, title,
85: JOptionPane.PLAIN_MESSAGE );
86: }
87: }
88:
89: /** Class Shoe represents a set of decks of standard (52) playing cards.
90: * In a real casino, they shuffle and deal from a "shoe" containing
91: * 4 or more decks. No matter how many decks are in the show, when only
92: * a few are left, to prevent couting, the entire shoe of cards is
93: * re-shuffled. In this program I used 5% unused as the time to re-shuffle.
94: */
95:
96: class Shoe
97: {
98: private static final int NUM_DECKS = 4;
99: private static final int RESHUFFLE_POINT = (int) (0.05 * (NUM_DECKS * 52) );
100:
101: private ArrayList<Card> deck;
102:
103: public Shoe ()
104: { shuffleCards();
105: }
106:
107: public ArrayList<Card> drawCards ( int numCardsToDeal )
108: {
109: ArrayList<Card> cards = new ArrayList<Card>( numCardsToDeal );
110:
111: for ( int i = 0; i < numCardsToDeal; ++i )
112: {
113: if ( deck.size() <= RESHUFFLE_POINT )
114: shuffleCards();
115: cards.add( deck.remove(0) );
116: }
117: return cards;
118: }
119:
120: private void shuffleCards ()
121: {
122: deck = new ArrayList<Card>( NUM_DECKS * 52 );
123:
124: for ( int numDecks = 0; numDecks < NUM_DECKS; ++numDecks )
125: for ( int suit = 1; suit <= 4; ++suit )
126: for ( int rank = 1; rank <= 13; ++rank )
127: deck.add( new Card( suit, rank ) );
128:
129: Collections.shuffle( deck );
130: }
131: }
132:
133: /** Class Hand represents the "hand" of cards held by some player.
134: * In the came 21, each hand contains two cards initially.
135: */
136:
137: class Hand
138: {
139: private ArrayList<Card> cards = new ArrayList<Card>();
140: private Shoe shoe;
141:
142: public Hand ( Shoe shoe )
143: {
144: this.shoe = shoe;
145:
146: // Deal two cards to initialize the hand:
147: cards.addAll( shoe.drawCards( 2 ) );
148: }
149:
150: /** Calculates the value of the hand. Picture cards are worth 10 and an
151: * Ace is worth 1 or 11.
152: */
153: public int value ()
154: {
155: int total = 0;
156: boolean hasAce = false;
157: for ( Card card : cards )
158: {
159: int val = card.value();
160: if ( val == 1 ) hasAce = true;
161: total += val;
162: }
163:
164: while ( total < (21 - 10) && hasAce )
165: total += 10;
166: return total;
167: }
168:
169: /** Add one card to the hand from the shoe.
170: */
171: public void hit () { cards.addAll( shoe.drawCards(1) ); }
172:
173: /** During the play only the first card of the dealer's hand should be shown.
174: */
175: public Card firstCard() { return cards.get( 0 ); }
176:
177: public String toString ()
178: {
179: StringBuilder sb = new StringBuilder();
180: for ( Card card : cards )
181: {
182: sb.append( card );
183: sb.append( " " );
184: }
185: return sb.toString();
186: }
187: }
188:
189: /** Represents one card from a standard "bridge" pack of (52) cards.
190: */
191: class Card
192: {
193: final int suit; // A Java 5 "enum" would be a good choice instead of int!
194: final int rank;
195:
196: Card ( int suit, int rank )
197: {
198: if ( suit < 1 || suit > 4 )
199: throw new IllegalArgumentException(
200: "suit must be between 1 (clubs) and 4 (spades)." );
201:
202: if ( rank < 1 || rank > 13 )
203: throw new IllegalArgumentException(
204: "rank must be between 1 (ace) and 13 (king)" );
205:
206: this.suit = suit;
207: this.rank = rank;
208: }
209:
210: /** Calculates the value of the card.
211: * @return the rank of the card, with Ace = 1, picture cards = 10.
212: */
213: public int value ()
214: {
215: int val = rank;
216: if ( rank > 10 )
217: val = 10;
218: return val;
219: }
220:
221: /** Uses ASCII to show the rank (A,2,3,4,5,6,7,8,9,10,J,Q,K) and suit.
222: * Note that if using console output it would be better to use letters
223: * for the suits (C, D, H, and S) rather than symbols, as they wouldn't
224: * show up properly on all platforms.
225: */
226: public String toString()
227: {
228: StringBuilder sb = new StringBuilder( 2 );
229: switch ( rank )
230: {
231: case 1: sb.append( 'A' ); break;
232: case 11: sb.append( 'J' ); break;
233: case 12: sb.append( 'Q' ); break;
234: case 13: sb.append( 'K' ); break;
235: default: sb.append( rank ); break;
236: }
237:
238: switch ( suit )
239: {
240: case 1: sb.append( '\u2663' ); break; // Club
241: case 2: sb.append( '\u2666' ); break; // Diamond
242: case 3: sb.append( '\u2665' ); break; // Heart
243: case 4: sb.append( '\u2660' ); break; // Spade
244: }
245: return sb.toString();
246: }
247: }