1 import java.applet.*;
2 import java.awt.*;
3 import java.awt.event.*;
4 import java.lang.Math;
5
6 /**
7 * Applet to show BlackJack odds
8 * Creation date: (02/5/2002 10:16:43 AM)
9 * @author: Andrew Freed, arf132@psu.edu
10 */
11
12 /*
13 Development of this applet was sponsored by the Penn State Fund for
14 Excellence in Learning and Teaching (FELT), project "Java-based
15 Teaching of Mathematics in Information Sciences and Technology",
16 supervised by Frank Ritter and David Mudgett.
17 */
18
19 public class BlackjackApplet extends Applet {
20 private Label lblInfo = new Label("Odds of busting: ");
21 private Label lblUser = new Label("User hand:");
22 private Button btnDeal = new Button("Deal");
23 private Button btnHit = new Button("Hit!");
24 private Button btnStand = new Button("Stand");
25 private Label lblUserHand = new Label("<Your hand>");
26 private Label lblSum = new Label("Sum: ");
27
28 private int deck[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
29 private int numDecks = 1; //Change this to simulate more decks
30 private int cardsDealt = 0; //To figure out when it is time to reshuffle
31 private int numHighAces = 0; //To help counting functions determine hand value/bust odds
32
33 //Deal two random cards to user's hand
34 private void deal()
35 {
36 //Re-shuffle if half of deck is used
37 if( cardsDealt > (numDecks * 52 /2) )
38 initDeck();
39
40 String newHand = "";
41 String newCard;
42
43 lblUserHand.setText("");
44
45 for( int i=1; i<=2; i++)
46 {
47 newCard = getRandomCard();
48 newHand = addNewCard(newHand, newCard);
49 }
50
51 btnHit.setEnabled(true);
52 btnStand.setEnabled(true);
53 btnDeal.setEnabled(false);
54 btnHit.requestFocus();
55
56 lblUserHand.setText(newHand);
57 calculateBustOdds(newHand);
58
59 }
60
61 //Add one card to the user's hand
62 private void hit()
63 {
64 String myHand = lblUserHand.getText();
65 String newCard = getRandomCard();
66 myHand = addNewCard(myHand, newCard);
67
68 lblUserHand.setText(myHand);
69 calculateBustOdds(myHand);
70
71 }
72
73 //End a user's turn, calculate card sum. Prepare for next deal.
74 private void stand()
75 {
76 String myHand = lblUserHand.getText();
77 int sum = sumCards(myHand);
78 if( sum <= 21 )
79 lblInfo.setText("Safe play... but would you have won with " + sum + "?");
80 else
81 lblInfo.setText("Tough luck, but such is gambling...");
82
83 btnHit.setEnabled(false);
84 btnStand.setEnabled(false);
85 btnDeal.setEnabled(true);
86 btnDeal.requestFocus();
87 }
88
89 //Counts enough Aces as ones to stay below 21
90 private int sumCards(String myCards)
91 {
92 int result = highSum(myCards);
93
94 while( result > 21 && numHighAces > 0 )
95 {
96 result -= 10;
97 numHighAces--;
98 }
99
100 return result;
101 }
102
103 //Counts all Aces as ones -- this helps find probability for busting
104 // Example: 9 A sums to 20, but has zero probability of busting
105 private int lowSumCards(String myCards)
106 {
107 int result = highSum(myCards);
108
109 while( numHighAces > 0 )
110 {
111 result -= 10;
112 numHighAces--;
113 }
114
115 return result;
116 }
117
118 //This should never be called directly. Either call sumCards or lowSumCards.
119 //Both of those functions handle the "lowering" of the Ace cards.
120 //For a hand like A A A this function will return 33. lowSumCards would return 3 and
121 // sumCards would return 13 on the same hand
122 private int highSum(String myCards)
123 {
124 int result = 0;
125 numHighAces = 0;
126
127 //Read all values from user hand
128 for( int idx = 0; idx < myCards.length(); idx++ )
129 {
130 String myCard = myCards.substring(idx, idx+1);
131
132 //Hand will contain spaces, skip these
133 if( myCard.equals(" ") )
134 {
135 continue;
136 }
137
138 else if( myCard.equals("J") || myCard.equals("Q") ||
139 myCard.equals("K") )
140 result += 10;
141
142 //If you read in a "1", it is part of a "10". Thus skip the next character
143 else if( myCard.equals("1") )
144 {
145 result += 10;
146 idx++;
147 }
148
149 else if( myCard.equals("A") )
150 {
151 result += 11;
152 numHighAces++;
153 }
154
155 //If it is a 2-9, we can just add this
156 else
157 result += Integer.parseInt(myCard);
158 }
159
160 return result;
161 }
162
163 private void calculateBustOdds(String myHand)
164 {
165 //For the busting odds, we want the lowest possible sum, with each A=1
166 int sum = lowSumCards(myHand);
167 if (sum > 21)
168 {
169 lblInfo.setText("You bust!");
170 lblSum.setText("Sum: " + sum);
171 btnHit.setEnabled(false);
172 btnStand.setEnabled(false);
173 btnDeal.setEnabled(true);
174 btnDeal.requestFocus();
175 return;
176 }
177 else if (sum == 21)
178 {
179 lblInfo.setText("21! You win!");
180 lblSum.setText("Sum: 21");
181 btnHit.setEnabled(false);
182 btnStand.setEnabled(false);
183 btnDeal.setEnabled(true);
184 btnDeal.requestFocus();
185 return;
186 }
187 else if (sum < 12)
188 {
189 // It is possible that we have a 10-value card and an ace.
190 // The low sum is then 11, but the high sum is 21, Blackjack!
191 sum = sumCards(myHand);
192
193 //Need to check if we have Blackjack
194 if( sum == 21 )
195 {
196 lblInfo.setText("21! You win!");
197 lblSum.setText("Sum: 21");
198 btnHit.setEnabled(false);
199 btnStand.setEnabled(false);
200 btnDeal.setEnabled(true);
201 btnDeal.requestFocus();
202 return;
203 }
204
205 //Standard case, we still cannot bust because our low value is 11 or less
206 //But we still want to display the sum counting as many high-aces as possible
207 lblSum.setText("Sum: " + sum);
208 lblInfo.setText("You cannot bust on your next card!");
209 btnHit.requestFocus();
210 return;
211 }
212 else // between 12 and 20, most common case
213 {
214 lblSum.setText("Sum: " + sum);
215 btnHit.requestFocus();
216 }
217
218 // Margin is between 1 and 9
219 int margin = 21 - sum;
220 int totalCards = 0; // remaining cards
221 int availCards = 0; // safe cards
222
223 //You can always safely get an Ace
224 totalCards += deck[0];
225 availCards += deck[0];
226
227 //You can never safely add a 10, J, Q, K since margin always < 10
228 totalCards += deck[9];
229 totalCards += deck[10];
230 totalCards += deck[11];
231 totalCards += deck[12];
232
233 //Rememeber that deck[1] = "2", deck[2] = "3", etc by the array storage of the deck
234 for( int idx = 1; idx <= 8; idx++ )
235 {
236 if( idx + 1 <= margin )
237 availCards += deck[idx];
238
239 //Always add to total cards
240 totalCards += deck[idx];
241 }
242
243 double odds = ( (double) availCards ) /double) totalCards);
244 String output = "There are " + availCards + " out of the " +
245 totalCards + " remaining cards with which you will not bust. ";
246 output += ("P = " + doubleToString(odds));
247
248 lblInfo.setText(output);
249
250 return;
251 }
252
253 //Simple function to add one new card to a hand
254 private String addNewCard(String myHand, String myCard)
255 {
256 String result = myHand;
257 if( myCard == "0" )
258 result += (" 10 ");
259 else
260 result += (" " + myCard + " ");
261
262 return result;
263 }
264
265 private String getRandomCard()
266 {
267 String result = "";
268
269 // myRand is between 0 and 12, inclusive
270 int myRand = -1;
271 boolean newCard = false;
272
273 while(!newCard)
274 {
275 myRand = (int) ( 13 * java.lang.Math.random() );
276
277 //With the given random value, make sure there is a card of that type available!
278 if( deck[myRand] >= 0 )
279 newCard = true;
280 }
281
282 deck[myRand]--;
283 cardsDealt++;
284 int cardNum = myRand;
285
286 switch( cardNum )
287 {
288 case 0: result = "A"; break;
289 case 9: result = "0"; break; // deck[9] is count of "10" cards
290 case 10: result = "J"; break;
291 case 11: result = "Q"; break;
292 case 12: result = "K"; break;
293
294 //[1] = "2", [2] = "3", etc.
295 default: result = Integer.toString(++cardNum);
296 }
297
298 return result;
299 }
300
301 //This function takes a floating point value and returns a string with two decimal digits
302 private String doubleToString( double myNum )
303 {
304 String result = "";
305 String buf = "" + myNum + "";
306
307 //Seems that this should always be the case, given myNum is float.
308 int pos = buf.indexOf(".");
309 if( pos != -1)
310 {
311 if( buf.length() - pos > 3 )
312 result = buf.substring(0, buf.indexOf(".") + 3);
313 else
314 result = buf;
315 }
316 else
317 {
318 result = buf;
319 }
320
321 return result;
322 }
323
324 //Shuffles the deck. Notice that this is actually just resetting the deck array,
325 //so that all the card values are available again. Cards are instead selected in
326 //random order by the getRandomCard() function.
327 private void initDeck()
328 {
329 lblInfo.setText("Shuffling...");
330 for( int i=0; i < 13; i++ )
331 {
332 deck[i] = 4 * numDecks;
333 }
334
335 btnHit.setEnabled(false);
336 btnStand.setEnabled(false);
337 btnDeal.setEnabled(true); // should already be enabled...
338 lblInfo.setText("Shuffling complete. Please hit 'Deal' to get a new hand");
339
340 btnDeal.requestFocus();
341 }
342
343 /**
344 * Initializes the applet.
345 */
346 public void init() {
347 try {
348 super.init();
349 setName("BlackjackApplet");
350 setLayout(null);
351 setSize(500, 350);
352
353 lblInfo.setBounds(10,70,480,30);
354 lblUser.setBounds(10, 10, 70, 30);
355 btnDeal.setBounds(300, 160, 50, 30);
356 btnHit.setBounds(300, 190, 50, 30);
357 btnStand.setBounds(300, 220, 50, 30);
358 lblUserHand.setBounds(80, 10, 200, 30);
359 lblSum.setBounds(10,30,100,30);
360
361 add(lblInfo);
362 add(lblUser);
363 add(btnDeal);
364 add(btnHit);
365 add(btnStand);
366 add(lblUserHand);
367 add(lblSum);
368
369
370 btnDeal.setActionCommand("btnDeal");
371 btnDeal.addActionListener(new java.awt.event.ActionListener(){
372 public void actionPerformed(java.awt.event.ActionEvent e)
373 {
374 deal();
375 }
376 });
377 btnHit.setActionCommand("btnHit");
378 btnHit.addActionListener(new java.awt.event.ActionListener(){
379 public void actionPerformed(java.awt.event.ActionEvent e)
380 {
381 hit();
382 }
383 });
384 btnStand.setActionCommand("btnStand");
385 btnStand.addActionListener(new java.awt.event.ActionListener(){
386 public void actionPerformed(java.awt.event.ActionEvent e)
387 {
388 stand();
389 }
390 });
391
392 initDeck();
393
394 } catch (java.lang.Throwable Exc) {
395 handleException(Exc);
396 }
397 }
398
399 /**
400 * Called whenever the part throws an exception.
401 * @param exception java.lang.Throwable
402 */
403 private void handleException(java.lang.Throwable exception) {
404 //Currently exceptions are not handled since the applet does not have
405 //stdout or stderr available to it.
406 }
407
408 //Main function allows you to run this applet as an application
409 /**
410 * main entrypoint - starts the part when it is run as an application
411 * @param args java.lang.String[]
412 */
413 public static void main(java.lang.String[] args) {
414 try {
415 Frame frame = new java.awt.Frame();
416 BlackjackApplet aBlackjackApplet;
417 Class iiCls = Class.forName("BlackjackApplet");
418 ClassLoader iiClsLoader = iiCls.getClassLoader();
419 aBlackjackApplet = (BlackjackApplet)java.beans.Beans.instantiate(iiClsLoader,"BlackjackApplet");
420 frame.add("Center", aBlackjackApplet);
421 frame.setSize(aBlackjackApplet.getSize());
422 frame.addWindowListener(new java.awt.event.WindowAdapter() {
423 public void windowClosing(java.awt.event.WindowEvent e) {
424 System.exit(0);
425 };
426 });
427 frame.show();
428 java.awt.Insets insets = frame.getInsets();
429 frame.setSize(frame.getWidth() + insets.left + insets.right, frame.getHeight() + insets.top + insets.bottom);
430 frame.setVisible(true);
431 } catch (Throwable exception) {
432 System.err.println("Exception occurred in main() of java.applet.Applet");
433 exception.printStackTrace(System.out);
434 }
435 }
436
437 }
438