0% found this document useful (0 votes)
230 views34 pages

Final Blackjack

This document contains the code for a Blackjack card game GUI. It defines classes for the view (BlackjackView), model (BlackjackModel), and controller (BlackjackController). The main method initializes an instance of each class. BlackjackView contains methods to initialize and update the GUI components like labels and buttons to display the card hands, player stats, and control buttons. It uses CardTable and Hand classes to represent the game state.

Uploaded by

api-359332959
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
230 views34 pages

Final Blackjack

This document contains the code for a Blackjack card game GUI. It defines classes for the view (BlackjackView), model (BlackjackModel), and controller (BlackjackController). The main method initializes an instance of each class. BlackjackView contains methods to initialize and update the GUI components like labels and buttons to display the card hands, player stats, and control buttons. It uses CardTable and Hand classes to represent the game state.

Uploaded by

api-359332959
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 34

1 import javax.swing.

*;
2 import javax.swing.border.Border;
3 import java.awt.*;
4 import java.awt.event.ActionEvent;
5 import java.awt.event.ActionListener;
6 import java.util.Random;
7
8 /**
9 * Final Project: Blackjack
10 * School: CSU, Monterey Bay
11 * Course: CST 338 Software Design
12 * Professor: Jesse Cecil, MS
13 *
14 * Final Project: Blackjack card game
15 *
16 */
17 public class Blackjack
18 {
19 public static void main(String[] args)
20 {
21 BlackjackView gameView = new BlackjackView();
22 BlackjackModel gameModel = new BlackjackModel(gameView);
23 BlackjackController gameController = new BlackjackController(gameModel);
24 gameView.initButtonListeners(gameController.newGameListener,
25 gameController.hitButtonListener,
26 gameController.dealNextHandListener,
27 gameController.standButtonListener,
28 gameController.doubleButtonListener);
29 }
30 }
31
32 /**
33 * Class definition for the view of the Blackjack game
34 *
35 *
36 */
37 class BlackjackView
38 {
39 private static JLabel[] dealerLabels;
40 private static JLabel[] playerLabels;
41 private static JButton[] playerButtons;
42 private static JButton newGameButton;
43 private static JButton dealNextHandButton;
44 private static JLabel[] statusLabels;
45 private static CardTable cardTable;
46
47 /**
48 * Default no-argument constructor. Sets up the cardTable object
49 * and all needed labels and buttons.
50 */
51 public BlackjackView()
52 {
53 GUICard.loadCardIcons();
54 setCardTable();
55 setDealerLabels();
56 setPlayerLabels();
57 setStatusLabels();
58 setPlayerButtons();
59 initialButtonVisibility();
60 cardTable.setVisible(true);
61 }
62
63 /**
64 * Adds actionListener objects to the buttons in the view
65 * @param startNewGame An actionListener object that starts a new game
66 * @param doHit An actionListener object that deals a single card to a
67 * player
68 * @param dealNextHand An actionListener object that starts a new round
69 * of the game
70 * @param standButton An actionListener object that ends the players
71 * turn and begins the dealer's turn
72 * @param doubleButton An actionListener object that doubles the wager
73 * of a player and then deals them a single card, ending their turn
74 */
75 public void initButtonListeners(ActionListener startNewGame,
76 ActionListener doHit,
77 ActionListener dealNextHand,
78 ActionListener standButton,
79 ActionListener doubleButton)
80 {
81 newGameButton.addActionListener(startNewGame);
82 playerButtons[0].addActionListener(doHit);
83 dealNextHandButton.addActionListener(dealNextHand);
84 playerButtons[1].addActionListener(standButton);
85 playerButtons[2].addActionListener(doubleButton);
86
87 }
88
89 /**
90 * A private helper method to initialize the cardTable object
91 * for the view, used by the constructor
92 */
93 private void setCardTable()
94 {
95 cardTable = new CardTable("Blackjack", 9, 2);
96 cardTable.setSize(900, 800);
97 cardTable.setLocation(200, 200);
98 cardTable.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
99 }
100
101 /**
102 * A private helper method to instantiate and set the player
103 * labels, used by the constructor
104 * @return Returns true if completed successfully, false otherwise
105 */
106 private boolean setPlayerLabels()
107 {
108 if (cardTable == null)
109 {
110 return false;
111 }
112 // Instantiate labels in array
113 playerLabels = new JLabel[cardTable.getNumCardsPerHand()];
114 for (int i = 0; i < cardTable.getNumCardsPerHand(); i++)
115 {
116 playerLabels[i] = new JLabel("");
117 }
118 // Add labels from array into panel
119 for (int i = 0; i < cardTable.getNumCardsPerHand(); i++)
120 {
121 cardTable.pnlHumanHand.add(playerLabels[i]);
122 }
123 return true;
124 }
125
126 /**
127 * A private helper method to instantiate and set the dealer
128 * labels, used by the constructor
129 * @return Returns true if completed successfully, false otherwise
130 */
131 private boolean setDealerLabels()
132 {
133 if (cardTable == null)
134 {
135 return false;
136 }
137 // Instantiate labels in dealerlabels array
138 dealerLabels = new JLabel[cardTable.getNumCardsPerHand()];
139 for (int i = 0; i < cardTable.getNumCardsPerHand(); i++)
140 {
141 dealerLabels[i] = new JLabel("");
142 }
143 // Add labels from dealerLabels into panel
144 for (int i = 0; i < cardTable.getNumCardsPerHand(); i++)
145 {
146 cardTable.pnlDealerHand.add(dealerLabels[i]);
147 }
148 return true;
149 }
150
151 /**
152 * Private helper method to instantiate player buttons and add
153 * them to the panel, used by the constructor
154 * @return Returns true if successful, false otherwise
155 */
156 private boolean setPlayerButtons()
157 {
158 if (cardTable == null)
159 {
160 return false;
161 }
162 // PlayerButtons array holds JButton objects that represent player
163 // actions Hit, Stand, and Double Down
164 playerButtons = new JButton[3];
165 playerButtons[0] = new JButton("Hit");
166 playerButtons[1] = new JButton("Stand");
167 playerButtons[2] = new JButton("Double Down");
168
169 // NewGameButton resets game state back to original state
170 newGameButton = new JButton("New Game");
171
172 // Add buttons to pnlPlayerButton, we use anonymous
173 // labels with empty strings to serve as blank spaces
174 // in our button grid.
175 cardTable.pnlPlayerButtons.add(newGameButton);
176 cardTable.pnlPlayerButtons.add(new JLabel(""));
177 for (int i = 0; i < playerButtons.length; i++)
178 {
179 cardTable.pnlPlayerButtons.add(playerButtons[i]);
180 }
181 // Add two blank button spaces at the end of the button row
182 for (int i = 0; i < 2; i++)
183 {
184 cardTable.pnlPlayerButtons.add(new JLabel(""));
185 }
186 return true;
187 }
188
189 /**
190 * Private helper method to set the initial state of the status
191 * labels and add them to the panel, used by the constructor
192 * @return Returns true if successful, false otherwise
193 */
194 private boolean setStatusLabels()
195 {
196 if (cardTable == null)
197 {
198 return false;
199 }
200 // Set up labels
201 statusLabels = new JLabel[6];
202 // Index 0 is for player hand value
203 statusLabels[0] = new JLabel("Your hand: ", JLabel.CENTER);
204 // Index 1 is for win/loss/status messages
205 statusLabels[1] = new JLabel("Good Luck!", JLabel.CENTER);
206 // Index 2 is for dealer hand value
207 statusLabels[2] = new JLabel("Dealer's hand: ", JLabel.CENTER);
208 // Index 3 is for player's current bankroll
209 statusLabels[3] = new JLabel("Your bank: $0", JLabel.CENTER);
210 // Index 4 is for dealNextHandButton
211 statusLabels[4] = new JLabel("");
212 // Index 5 is for player's current wager
213 statusLabels[5] = new JLabel("Your bet: $0", JLabel.CENTER);
214
215 // Set up button
216 dealNextHandButton = new JButton("Deal Next Hand");
217
218 // Add labels and button to panel
219 for (int i = 0; i < statusLabels.length; i++)
220 {
221 cardTable.pnlMessageArea.add(i == 4 ? dealNextHandButton : statusLabels[i]);
222 }
223 return true;
224 }
225
226 /**
227 * Updates the GUI to reflect the current state of the dealer's hand
228 * @param dealerHand A hand object that represents the dealer's hand
229 * @param numCards The number of cards currently in the dealer's hand
230 * @return Returns true if successful, false otherwise.
231 */
232 public boolean updateDealerHand(Hand dealerHand, int numCards)
233 {
234 if (dealerHand == null)
235 {
236 return false;
237 }
238 for (int i = 0; i < numCards; i++)
239 {
240 dealerLabels[i].setIcon(GUICard.getIcon(dealerHand.inspectCard(i)));
241 }
242 return true;
243 }
244
245 /**
246 * Updates the GUI to reflect the initial state of the dealer's hand,
247 * with one card remaining face down
248 * @param dealerHand A Hand object that represents the dealer's hand
249 * @return Returns true if successful, false otherwise
250 */
251 public boolean initialDealerView(Hand dealerHand)
252 {
253 if (dealerHand == null)
254 {
255 return false;
256 }
257 dealerLabels[0].setIcon(GUICard.getIcon(dealerHand.inspectCard(0)));
258 dealerLabels[1].setIcon(GUICard.getBackCardIcon());
259 return true;
260 }
261
262 /**
263 * Updates the GUI with the current state of the given Hand
264 * object
265 * @param playerHand A Hand object representing a hand of cards
266 * @param numCards An integer representing the number of cards
267 * in the given hand
268 * @return Returns true if successful, false otherwise
269 */
270 public boolean updatePlayerHand(Hand playerHand, int numCards)
271 {
272 if (playerHand == null)
273 {
274 return false;
275 }
276 for (int i = 0; i < numCards; i++)
277 {
278 playerLabels[i].setIcon(GUICard.getIcon(playerHand.inspectCard(i)));
279 }
280 return true;
281 }
282
283 /**
284 * Updates the GUI with the current value of the player's hand
285 * @param value An integer representing the current value of the
286 * player's hand
287 * @return Returns true if successful, false otherwise
288 */
289 public boolean updatePlayerHandValue(int value)
290 {
291 if (value < 0)
292 {
293 return false;
294 }
295 statusLabels[0].setText("Your hand: " + value);
296 return true;
297 }
298
299 /**
300 * Updates GUI with the current value of the dealer's hand
301 * @param value An integer that represents the current value
302 * of the dealer's hand
303 * @return Returns true if successful, false otherwise
304 */
305 public boolean updateDealerHandValue(int value)
306 {
307 if (value < 0)
308 {
309 return false;
310 }
311 statusLabels[2].setText("Dealer's Hand: " + value);
312 return true;
313 }
314
315 /**
316 * Updates the GUI with the current value of the player's
317 * bankroll
318 * @param bankroll An integer representing the current value of
319 * the player's bankroll
320 * @return Returns true if successful, false otherwise
321 */
322 public boolean updateBankroll(int bankroll)
323 {
324 if (statusLabels == null)
325 {
326 return false;
327 }
328 statusLabels[3].setText("Your bank: $" + bankroll);
329 return true;
330 }
331
332 /**
333 * Updates the GUI with the current amount being wagered
334 * @param wager An integer representing the current amount
335 * being wagered by the player
336 * @return Returns true if successful, false otherwise
337 */
338 public boolean updateStatusMsgWager(int wager)
339 {
340 if (statusLabels == null || wager < 0)
341 {
342 return false;
343 }
344 statusLabels[5].setText("Your bet: $" + wager);
345 return true;
346 }
347
348 /**
349 * Updates the GUI with a status message informing the player
350 * that a new round has started
351 * @return Returns true if successful, false otherwise
352 */
353 public boolean updateStatusMsgNewRound()
354 {
355 if (statusLabels == null)
356 {
357 return false;
358 }
359 statusLabels[1].setText("New cards coming out, good luck!");
360 return true;
361 }
362
363 /**
364 * Updates the GUI with a status message informing the player
365 * they have won with a blackjack
366 * @return Returns true if successul, false otherwise
367 */
368 public boolean updateStatusBlackjackWinMsg()
369 {
370 if (statusLabels == null)
371 {
372 return false;
373 }
374 statusLabels[1].setText("Blackjack!! You win!");
375 return true;
376 }
377
378 /**
379 * Updates the GUI with a status message informing the player
380 * that they have lost to a dealer blackjack
381 * @return Returns true if successful, false otherwise
382 */
383 public boolean updateStatusBlackjackLossMsg()
384 {
385 if (statusLabels == null)
386 {
387 return false;
388 }
389 statusLabels[1].setText("The dealer got blackjack, you lost.");
390 return true;
391 }
392
393 /**
394 * Updates the GUI with a status message informing the player
395 * that they have won
396 * @return Returns true if successful, false otherwise
397 */
398 public boolean updateStatusWinMsg()
399 {
400 if (statusLabels == null)
401 {
402 return false;
403 }
404 statusLabels[1].setText("Woo Hoo! You won, play another round!");
405 return true;
406 }
407
408 /**
409 * Updates the GUI with a status message informing the player
410 * that they have lost
411 * @return Returns true if successful, false otherwise
412 */
413 public boolean updateStatusLossMsg()
414 {
415 if (statusLabels == null)
416 {
417 return false;
418 }
419 statusLabels[1].setText("Bummer. You lost that one. Try again.");
420 return true;
421 }
422
423 /**
424 * Updates the GUI with a status message informing the player
425 * that they have pushed (tied)
426 * @return Returns true if successful, false otherwise
427 */
428 public boolean updateStatusPushMsg()
429 {
430 if (statusLabels == null)
431 {
432 return false;
433 }
434 statusLabels[1].setText("That one was a push, no win, no loss.");
435 return true;
436 }
437
438 /**
439 * Updates the GUI with a status message informing the player that
440 * they have busted (gone over 21)
441 * @return Returns true if successful, false otherwise
442 */
443 public boolean updateStatusBustMsg()
444 {
445 if (statusLabels == null)
446 {
447 return false;
448 }
449 statusLabels[1].setText("Bust!! You shouldn't have taken that last card.");
450 return true;
451 }
452
453 /**
454 * A method to clear all card icons for both player and dealer
455 * @return Returns true if successful, false otherwise
456 */
457 public boolean clearCardIcons()
458 {
459 if (playerLabels == null || dealerLabels == null)
460 {
461 return false;
462 }
463 for (int i = 0; i < playerLabels.length; i++)
464 {
465 playerLabels[i].setIcon(null);
466 dealerLabels[i].setIcon(null);
467 }
468 return true;
469 }
470
471 /**
472 * A method for hiding the Double Down button
473 * @return Returns true if successful, false otherwise
474 */
475 public boolean hideDoubleButton()
476 {
477 if (playerButtons == null)
478 {
479 return false;
480 }
481 playerButtons[2].setVisible(false);
482 return true;
483 }
484
485 /**
486 * Sets button visibility to initial state: All buttons except
487 * Start New Game are set to false
488 * @return Returns true if successful, false otherwise
489 */
490 public boolean initialButtonVisibility()
491 {
492 if (cardTable == null)
493 {
494 return false;
495 }
496 dealNextHandButton.setVisible(false);
497 for (int i = 0; i < playerButtons.length; i++)
498 {
499 playerButtons[i].setVisible(false);
500 }
501 return true;
502 }
503
504 /**
505 * Sets buttons to the start of a new round state:
506 * Deal Next Hand is false, Hit/Stand/Double Down are true
507 * @return Returns true if successful, false otherwise
508 */
509 public boolean newRound()
510 {
511 if (cardTable == null)
512 {
513 return false;
514 }
515 dealNextHandButton.setVisible(false);
516 for (int i = 0; i < playerButtons.length; i++)
517 {
518 playerButtons[i].setVisible(true);
519 }
520 return true;
521 }
522
523 /**
524 * Sets buttons to the end of round state:
525 * Deal Next Hand is true, Hit/Stand/Double Down are false
526 * @return Returns true if successful, false otherwise
527 */
528 public boolean endRound()
529 {
530 if (cardTable == null)
531 {
532 return false;
533 }
534 dealNextHandButton.setVisible(true);
535 for (int i = 0; i < playerButtons.length; i++)
536 {
537 playerButtons[i].setVisible(false);
538 }
539 return true;
540 }
541 }
542
543 /**
544 * Controller class for Blackjack game. Contains
545 * ActionListeners for buttons in BlackjackView
546 * class.
547 *
548 */
549 class BlackjackController
550 {
551 private BlackjackModel blackjackModel;
552
553 /**
554 * Single argument constructor, takes a BlackjackModel object
555 * as an argument.
556 * @param blackjackModel A blackjackModel object, representing the
557 * game logic for a game of blackjack
558 */
559 public BlackjackController(BlackjackModel blackjackModel)
560 {
561 this.blackjackModel = blackjackModel;
562 }
563
564 /**
565 * ActionListener for the Start New Game button
566 */
567 ActionListener newGameListener = new ActionListener()
568 {
569 @Override
570 public void actionPerformed(ActionEvent e)
571 {
572 blackjackModel.startNewGame();
573 }
574 };
575
576 /**
577 * ActionListener for the Deal Next Hand button
578 */
579 ActionListener dealNextHandListener = new ActionListener()
580 {
581 @Override
582 public void actionPerformed(ActionEvent e)
583 {
584 blackjackModel.dealNextHand();
585 }
586 };
587
588 /**
589 * ActionListener for the Hit button
590 */
591 ActionListener hitButtonListener = new ActionListener()
592 {
593 @Override
594 public void actionPerformed(ActionEvent e)
595 {
596 blackjackModel.doHit(1);
597 }
598 };
599
600 /**
601 * ActionListener for the Stand button
602 */
603 ActionListener standButtonListener = new ActionListener()
604 {
605 @Override
606 public void actionPerformed(ActionEvent e)
607 {
608 blackjackModel.doDealerTurn();
609 blackjackModel.selectWinner();
610 }
611 };
612
613 /**
614 * ActionListener for the Double Down button
615 */
616 ActionListener doubleButtonListener = new ActionListener()
617 {
618 @Override
619 public void actionPerformed(ActionEvent e)
620 {
621 if (blackjackModel.doDouble(1))
622 {
623 blackjackModel.doDealerTurn();
624 blackjackModel.selectWinner();
625 }
626 }
627 };
628 }
629
630 /**
631 * Model class for the game of Blackjack
632 *
633 */
634 class BlackjackModel
635 {
636 public static final int NUM_CARDS_PER_HAND = 2;
637 public static final int NUM_PLAYERS = 2;
638 public static final int NUM_PACKS = 1;
639 public static final int NUM_JOKERS_PER_PACK = 0;
640 public static final int NUM_UNUSED_CARDS_PER_PACK = 4;
641 private int wagerAmount = 5;
642 private static Card[] unusedCards = null;
643 private static int playerBankroll;
644 private static CardGameFramework blackjackGame = null;
645 private BlackjackView blackjackView;
646 private static int playerHandValue;
647 private static int dealerHandValue;
648
649 /**
650 * Single argument constructor, takes a BlackjackView object
651 * as an argument. Sets up the unusedCards array with jokers.
652 * Instantiates the blackjackGame object
653 * @param blackjackView A blackjackView object to reference as the
654 * GUI
655 */
656 public BlackjackModel(BlackjackView blackjackView)
657 {
658 this.blackjackView = blackjackView;
659 unusedCards = new Card[4];
660 unusedCards[0] = new Card('X', Card.Suit.CLUBS);
661 unusedCards[1] = new Card('X', Card.Suit.DIAMONDS);
662 unusedCards[2] = new Card('X', Card.Suit.HEARTS);
663 unusedCards[3] = new Card('X', Card.Suit.SPADES);
664 blackjackGame = new CardGameFramework(NUM_PACKS,NUM_JOKERS_PER_PACK,
665 NUM_UNUSED_CARDS_PER_PACK,
666 unusedCards, NUM_PLAYERS,
667 NUM_CARDS_PER_HAND);
668 }
669
670 /**
671 * Sets up the game environment for a new game. Sets player bankroll,
672 * clears out all card icons, sets the wager amount, gets a new deck of cards,
673 * deals out a hand, and uses blackjackView to update the GUI
674 * @return Returns true if successful, false otherwise
675 */
676 public boolean startNewGame()
677 {
678 if (blackjackGame == null || blackjackView == null)
679 {
680 return false;
681 }
682 setPlayerBankroll(50);
683 blackjackView.clearCardIcons();
684 setWagerAmount(5);
685 blackjackView.updateStatusMsgWager(getWagerAmount());
686 blackjackGame.newGame();
687 blackjackGame.deal();
688 blackjackView.newRound();
689 blackjackView.updatePlayerHand(blackjackGame.getHand(1),
690 blackjackGame.getHand(1).getNumCards());
691 blackjackView.initialDealerView(blackjackGame.getHand(0));
692 blackjackView.updateBankroll(this.getPlayerBankroll());
693
694 // If we are successful in setting the player hand value
695 // we go ahead and use blackjackView to update the GUI
696 if (setPlayerHandValue())
697 {
698 blackjackView.updatePlayerHandValue(playerHandValue);
699 }
700
701 return true;
702 }
703
704 /**
705 * Uses hand determined by playerIndex to recieve one card
706 * from the deck. The player's wager is also doubled. The player
707 * is not allowed to receive any other cards after this.
708 * @param playerIndex An integer representing the index of
709 * a players hand in the blackjackGame Hand array
710 * @return Returns true if successful, false if player busts
711 */
712 public boolean doDouble(int playerIndex)
713 {
714 if (blackjackGame.getHand(playerIndex) == null)
715 {
716 return false;
717 }
718 Hand hand = blackjackGame.getHand(playerIndex);
719 while (!hand.takeCard(blackjackGame.getCardFromDeck()))
720 {
721 // empty loop is used to force takeCard to eventually
722 // deal a valid card.
723 }
724 setWagerAmount(10);
725 blackjackView.updateStatusMsgWager(getWagerAmount());
726 blackjackView.updatePlayerHand(hand, hand.getNumCards());
727 setPlayerHandValue();
728 blackjackView.updatePlayerHandValue(playerHandValue);
729 // If the double card busts the player, the round can be ended
730 // right here
731 if (handBusted(hand))
732 {
733 playerLost();
734 blackjackView.updateStatusBustMsg();
735 blackjackView.endRound();
736 return false;
737 }
738 return true;
739 }
740
741 /**
742 * Allows the player indicated by playerIndex to receive one
743 * new card from the deck
744 * @param playerIndex An integer indicating the player's index
745 * location in the blackjackGame Hand array
746 * @return Returns true if successful, false otherwise
747 */
748 public boolean doHit(int playerIndex)
749 {
750 if (blackjackGame.getHand(playerIndex) == null)
751 {
752 return false;
753 }
754 // If we use the Hit button, we are no longer eligible to use
755 // the Double Down button, so we hide it
756 blackjackView.hideDoubleButton();
757 Hand hand = blackjackGame.getHand(playerIndex);
758 while (!hand.takeCard(blackjackGame.getCardFromDeck()))
759 {
760 // We use an empty loop to force takeCard to always
761 // return a valid card
762 }
763
764 // Used for the dealer using doHit
765 if (playerIndex == 0)
766 {
767 blackjackView.updateDealerHand(hand, hand.getNumCards());
768 setDealerHandValue();
769 blackjackView.updateDealerHandValue(dealerHandValue);
770 }
771 else
772 {
773 // Used for the player using doHit
774 blackjackView.updatePlayerHand(hand, hand.getNumCards());
775 setPlayerHandValue();
776 blackjackView.updatePlayerHandValue(playerHandValue);
777 // If the hit card busted the player, end the round here.
778 if (handBusted(hand))
779 {
780 playerLost();
781 blackjackView.updateStatusBustMsg();
782 return true;
783 }
784 }
785 return true;
786 }
787
788 /**
789 * Method that allows for starting a new round of blackjack
790 * without starting a new game. Resets the views, updates the
791 * hand values, deals out a new hand, then checks to see if there
792 * was a blackjack made by either the dealer or player or both
793 * @return Returns true if successful, false otherwise
794 */
795 public boolean dealNextHand()
796 {
797 if (blackjackGame == null || blackjackView == null)
798 {
799 return false;
800 }
801 blackjackView.newRound();
802 blackjackView.clearCardIcons();
803 setWagerAmount(5);
804 blackjackView.updateStatusMsgWager(getWagerAmount());
805 // If the deck is getting low on cards, restock the deck
806 // before dealing
807 if (blackjackGame.getNumCardsRemainingInDeck() < 20)
808 {
809 blackjackGame.newGame();
810 }
811
812 blackjackGame.deal();
813 // Update the Hand views
814 blackjackView.updateDealerHandValue(0);
815 blackjackView.updatePlayerHand(blackjackGame.getHand(1),
816 blackjackGame.getHand(1).getNumCards());
817 blackjackView.initialDealerView(blackjackGame.getHand(0));
818 blackjackView.updateBankroll(this.getPlayerBankroll());
819 if (setPlayerHandValue())
820 {
821 blackjackView.updatePlayerHandValue(playerHandValue);
822 }
823
824 blackjackView.updateStatusMsgNewRound();
825 // Check to see if the dealer and/or the player have a blackjack
826 // If there is a blackjack here, we will start a new round regardless
827 // of who won or a tie
828 if (hasBlackjack(blackjackGame.getHand(0)) &&
!hasBlackjack(blackjackGame.getHand(1)))
829 {
830 playerLost();
831 blackjackView.updateDealerHand(blackjackGame.getHand(0),
blackjackGame.getHand(0).getNumCards());
832 blackjackView.updateStatusBlackjackLossMsg();
833 blackjackView.endRound();
834 }
835 else if (!hasBlackjack(blackjackGame.getHand(0)) &&
hasBlackjack(blackjackGame.getHand(1)))
836 {
837 playerWon();
838 blackjackView.updateStatusBlackjackWinMsg();
839 blackjackView.endRound();
840 }
841 else if (hasBlackjack(blackjackGame.getHand(0)) &&
hasBlackjack(blackjackGame.getHand(1)))
842 {
843 blackjackView.updateDealerHand(blackjackGame.getHand(0),
blackjackGame.getHand(0).getNumCards());
844 blackjackView.updateStatusPushMsg();
845 blackjackView.endRound();
846 }
847 return true;
848 }
849
850 /**
851 * Private helper method to set the player's current
852 * bankroll value
853 * @param bankroll An integer representing the current bankroll
854 * value
855 * @return Returns true if successful, false otherwise
856 */
857 private boolean setPlayerBankroll(int bankroll)
858 {
859 if (bankroll < 0)
860 {
861 playerBankroll = 0;
862 return false;
863 }
864 playerBankroll = bankroll;
865 return true;
866 }
867
868 /**
869 * Accessor method to return the current value of
870 * bankroll
871 * @return Returns an integer representing the player's current
872 * bankroll
873 */
874 public int getPlayerBankroll()
875 {
876 return playerBankroll;
877 }
878
879 /**
880 * Determines if the given hand value has exceeded 21
881 * @param hand A Hand object to be tested against 21
882 * @return Returns true if the hand exceeds 21, false otherwise
883 */
884 public boolean handBusted(Hand hand)
885 {
886 if (hand == null)
887 {
888 return false;
889 }
890
891 if (getHandValue(hand) > 21)
892 {
893 return true;
894 }
895 return false;
896 }
897
898 /**
899 * Mutator method to set the player's hand value
900 * @return Returns true if successful, false otherwise
901 */
902 public boolean setPlayerHandValue()
903 {
904 if (blackjackGame.getHand(1) == null)
905 {
906 return false;
907 }
908 playerHandValue = getHandValue(blackjackGame.getHand(1));
909 return true;
910 }
911
912 /**
913 * Mutator method to set the dealer's hand value
914 * @return Returns true if successful, false otherwise
915 */
916 public boolean setDealerHandValue()
917 {
918 if (blackjackGame.getHand(0) == null)
919 {
920 return false;
921 }
922 dealerHandValue = getHandValue(blackjackGame.getHand(0));
923 return true;
924 }
925
926 /**
927 * Determines which hand is the winner. Compares hand values of
928 * dealer hand and player hand. Updates status message and bankroll
929 * depending on win/loss
930 * @return Returns true if successful, false otherwise
931 */
932 public boolean selectWinner()
933 {
934 if (blackjackGame.getHand(0) == null || blackjackGame.getHand(1) == null)
935 {
936 return false;
937 }
938 // If the dealer busts, or the player has a better hand, the
939 // result is the same, player wins
940 if (dealerHandValue > 21 || (dealerHandValue < playerHandValue))
941 {
942 playerWon();
943 blackjackView.updateStatusWinMsg();
944 }
945 else if (dealerHandValue > playerHandValue)
946 {
947 playerLost();
948 blackjackView.updateStatusLossMsg();
949 }
950 else
951 {
952 blackjackView.updateStatusPushMsg();
953 }
954 // After determining results, end the round
955 blackjackView.endRound();
956 return true;
957 }
958
959 /**
960 * Checks if the Hand object is a blackjack (2 cards = 21)
961 * @param hand A Hand object representing a hand of blackjack
962 * @return Returns true if 2 cards = 21, false otherwise
963 */
964 public boolean hasBlackjack(Hand hand)
965 {
966 if (blackjackGame == null || blackjackView == null)
967 {
968 return false;
969 }
970 // Check to make sure we only have 2 cards
971 if (blackjackGame.getHand(0).getNumCards() > 2 ||
972 blackjackGame.getHand(1).getNumCards() > 2)
973 {
974 return false;
975 }
976 return getHandValue(hand) == 21;
977 }
978
979 /**
980 * Method for handling the logic of the dealer turn. The dealer
981 * will take cards until they have at least 17 or they have busted
982 * @return Returns true if successful, false otherwise
983 */
984 public boolean doDealerTurn()
985 {
986 if (blackjackGame == null || blackjackView == null)
987 {
988 return false;
989 }
990 Hand hand = blackjackGame.getHand(0);
991 blackjackView.updateDealerHand(hand, hand.getNumCards());
992 setDealerHandValue();
993 blackjackView.updateDealerHandValue(getHandValue(hand));
994 // Take cards until handValue is at least 17 or the dealer
995 // busts
996 while (getHandValue(hand) < 17 && !(handBusted(hand)))
997 {
998 doHit(0);
999 blackjackView.updateDealerHand(hand, hand.getNumCards());
1000 setDealerHandValue();
1001 blackjackView.updateDealerHandValue(getHandValue(hand));
1002 }
1003
1004 return true;
1005 }
1006
1007 /**
1008 * Mutator method for setting the wager amount
1009 * @param amount An integer representing the amount
1010 * to set the wager to
1011 * @return Returns true if successful, false otherwise
1012 */
1013 public boolean setWagerAmount(int amount)
1014 {
1015 if (amount < 1)
1016 {
1017 return false;
1018 }
1019 wagerAmount = amount;
1020 return true;
1021 }
1022
1023 /**
1024 * Accessor method to return the current value of the
1025 * player's wager
1026 * @return An integer representing the current value of the
1027 * player's wager
1028 */
1029 public int getWagerAmount()
1030 {
1031 return wagerAmount;
1032 }
1033
1034 /**
1035 * Accessor method used to calculate and return the value
1036 * of a given Hand object
1037 * @param hand A Hand object to calculate the value of
1038 * @return An integer representing the value of the given Hand
1039 * object
1040 */
1041 private int getHandValue(Hand hand)
1042 {
1043 if (hand == null)
1044 {
1045 return 0;
1046 }
1047 int numCards = hand.getNumCards();
1048 int handValue = 0;
1049 // Add up all of the values of the cards in the
1050 // hand, except the Aces, which are handled below
1051 for (int i = 0; i < numCards; i++)
1052 {
1053 handValue += getCardValue(hand.inspectCard(i));
1054 }
1055 // We account for the special case of the Ace here
1056 // If there is at least one Ace in the hand, we
1057 // cycle through the hand, checking if we should add
1058 // 1 or 11 for the value of the Ace
1059 if (hasAce(hand))
1060 {
1061 for (int i = 0; i < numCards; i++)
1062 {
1063 if (hand.inspectCard(i).getValue() == 'A')
1064 {
1065 if ((handValue + 11) > 21)
1066 {
1067 handValue += 1;
1068 }
1069 else
1070 {
1071 handValue += 11;
1072 }
1073 }
1074 }
1075 }
1076 return handValue;
1077 }
1078
1079 /**
1080 * Calculates the value of a single Card object
1081 * @param card A Card object to find the value of
1082 * @return Returns an integer representing the value of
1083 * the given Card object
1084 */
1085 private int getCardValue(Card card)
1086 {
1087 if (card == null)
1088 {
1089 return 0;
1090 }
1091 // Aces can be 1 or 11 in blackjack, so we handle that
1092 // special case in the calculation of the hand, getHandValue()
1093 switch (card.getValue())
1094 {
1095 case '2':
1096 return 2;
1097 case '3':
1098 return 3;
1099 case '4':
1100 return 4;
1101 case '5':
1102 return 5;
1103 case '6':
1104 return 6;
1105 case '7':
1106 return 7;
1107 case '8':
1108 return 8;
1109 case '9':
1110 return 9;
1111 case 'T':
1112 case 'J':
1113 case 'Q':
1114 case 'K':
1115 return 10;
1116 default:
1117 return 0;
1118 }
1119 }
1120
1121 /**
1122 * Checks to see if the given Hand object contains
1123 * at least one Ace
1124 * @param hand A Hand object to test for aces
1125 * @return Returns true if the hand contains at least one Ace,
1126 * false otherwise
1127 */
1128 private boolean hasAce(Hand hand)
1129 {
1130 if (hand == null)
1131 {
1132 return false;
1133 }
1134 for (int i = 0; i < hand.getNumCards(); i++)
1135 {
1136 if (hand.inspectCard(i).getValue() == 'A')
1137 {
1138 return true;
1139 }
1140 }
1141 return false;
1142 }
1143
1144 /**
1145 * Private helper method to handle the logic for when
1146 * the player wins. Adds wager to the bankroll, uses
1147 * blackjackView to update bankroll display and set
1148 * end of round state
1149 * @return Returns true if successful, false otherwise
1150 */
1151 private boolean playerWon()
1152 {
1153 if (blackjackView == null)
1154 {
1155 return false;
1156 }
1157 playerBankroll += getWagerAmount();
1158 blackjackView.updateBankroll(playerBankroll);
1159 blackjackView.endRound();
1160 return true;
1161 }
1162
1163 /**
1164 * Private helper method to handle the logic for when
1165 * the player loses. Deducts wager from the bankroll, uses
1166 * blackjackView to update bankroll display and set end of
1167 * round state
1168 * @return Returns true if successful, false otherwise
1169 */
1170 private boolean playerLost()
1171 {
1172 if (blackjackView == null)
1173 {
1174 return false;
1175 }
1176 playerBankroll -= getWagerAmount();
1177 blackjackView.updateBankroll(playerBankroll);
1178 blackjackView.endRound();
1179 return true;
1180 }
1181 }
1182
1183 /**
1184 * The frame that displays the card game, which includes the computer's hand,
1185 * human hand, and the played cards.
1186 */
1187 class CardTable extends JFrame
1188 {
1189 static int MAX_CARDS_PER_HAND = 56; // Cards include jokers
1190 static int MAX_PLAYERS = 2; // Only 2 players allowed in the game
1191
1192 private int numCardsPerHand;
1193 private int numPlayers;
1194
1195 public JPanel pnlDealerHand, pnlHumanHand, pnlMessageArea, pnlPlayerButtons;
1196
1197 /**
1198 * Create a new card table.
1199 *
1200 * @param title the title for the card table
1201 * @param numCardsPerHand the number of cards a player can hold
1202 * @param numPlayers the number of players
1203 */
1204 CardTable(String title, int numCardsPerHand, int numPlayers)
1205 {
1206 super();
1207 setTitle(title);
1208 // Set the number of cards per hand and the number of players to 0 if
1209 // their parameters are not in a valid range.
1210 boolean isNumCardsPerHandInRange = numCardsPerHand > 0
1211 && numCardsPerHand <= MAX_CARDS_PER_HAND;
1212 boolean isNumPlayersInRange = numPlayers > 1 && numPlayers <= MAX_PLAYERS;
1213 this.numCardsPerHand = isNumCardsPerHandInRange ? numCardsPerHand : 0;
1214 this.numPlayers = isNumPlayersInRange ? numPlayers : 2;
1215
1216 // This is the main layout for the table
1217 GridLayout tableLayout = new GridLayout(4, 1);
1218 setLayout(tableLayout);
1219
1220 // Create dealer panel, with titled border
1221 pnlDealerHand = new JPanel();
1222 Border dealerBorder = BorderFactory.createTitledBorder("Dealer");
1223 pnlDealerHand.setBorder(dealerBorder);
1224 //GridLayout dealerLayout = new GridLayout(1, numCardsPerHand);
1225 FlowLayout dealerLayout = new FlowLayout();
1226 pnlDealerHand.setLayout(dealerLayout);
1227
1228 // Create player's hand panel.
1229 Border playerHandBorder = BorderFactory.createTitledBorder("Your Hand");
1230 FlowLayout playerHandLayout = new FlowLayout();
1231 pnlHumanHand = new JPanel();
1232 pnlHumanHand.setBorder(playerHandBorder);
1233 pnlHumanHand.setLayout(playerHandLayout);
1234
1235 // Create message area panel.
1236 Border messageAreaBorder = BorderFactory.createTitledBorder(
1237 "Wagers and Results");
1238 GridLayout messageAreaLayout = new GridLayout(2, 3);
1239 pnlMessageArea = new JPanel();
1240 pnlMessageArea.setBorder(messageAreaBorder);
1241 pnlMessageArea.setLayout(messageAreaLayout);
1242
1243 // Create game buttons panel
1244 GridLayout buttonAreaLayout = new GridLayout(1, 7);
1245 pnlPlayerButtons = new JPanel();
1246 pnlPlayerButtons.setLayout(buttonAreaLayout);
1247
1248 // Add panels to window.
1249 add(pnlDealerHand);
1250 add(pnlMessageArea);
1251 add(pnlHumanHand);
1252 add(pnlPlayerButtons);
1253 }
1254
1255 /**
1256 * Get the number of cards per hand.
1257 *
1258 * @return the number of cards per hand
1259 */
1260 public int getNumCardsPerHand()
1261 {
1262 return numCardsPerHand;
1263 }
1264
1265 /**
1266 * Get the number of players.
1267 *
1268 * @return the number of players
1269 */
1270 public int getNumPlayers()
1271 {
1272 return numPlayers;
1273 }
1274 }
1275
1276 /**
1277 * Manage the reading and building of the card image Icons.
1278 */
1279 class GUICard
1280 {
1281 // 14 = A through K + joker and 4 = # of Suits for iconCards.
1282 private static String[] cardSuit = new String[]{"C", "D", "H", "S"};
1283 private static Icon[][] iconCards = new ImageIcon[14][4];
1284 private static Icon iconBack;
1285 static boolean iconsLoaded = false;
1286
1287 /**
1288 * Create a new card GUI. This will load all of the card icons.
1289 */
1290 public GUICard()
1291 {
1292 // Call on the loadCardIcons() method to
1293 // instantiate each of 57 icons in icon[] array
1294 loadCardIcons();
1295 }
1296
1297 /**
1298 * Build files names. Instantiate 57 icons in icon[] array.
1299 */
1300 static void loadCardIcons()
1301 {
1302 // Don't load icons again if they've already been loaded.
1303 if (iconsLoaded)
1304 {
1305 return;
1306 }
1307 // Create icons using the card file name and their location.
1308 String[][] fileNames = getFileNames();
1309 iconBack = new ImageIcon("images/BK.gif");
1310 for (int i = 0; i < 14; i++)
1311 {
1312 for (int j = 0; j < 4; j++)
1313 {
1314 iconCards[i][j] = new ImageIcon("images/" + fileNames[i][j]);
1315 }
1316 }
1317 iconsLoaded = true;
1318 }
1319
1320 /**
1321 * Get all of the image file names for each card in a standard deck.
1322 *
1323 * @return the image file names all card types
1324 */
1325 private static String[][] getFileNames()
1326 {
1327 String[][] fileNames = new String[14][4];
1328 // Create the file names to be used later for creating icons.
1329 // The card back is a special case and will be added explicitly.
1330 for (int i = 0; i < 4; i++) // 4 card suits available
1331 {
1332 String cardSuit = turnIntIntoCardSuit(i);
1333 for (int j = 0; j < 14; j++) // 14 card suits available
1334 {
1335 String cardValue = turnIntIntoCardValue(j);
1336 fileNames[j][i] = cardValue + cardSuit + ".gif";
1337 }
1338 }
1339 return fileNames;
1340 }
1341
1342 /**
1343 * Get a card value based on its integer representation, where 0 - 13
1344 * represent "A", "2", "3", ... "Q", "K", and "X".
1345 *
1346 * @param k the integer that represents a card value
1347 *
1348 * @return the card value represented by the requested integer
1349 */
1350 static String turnIntIntoCardValue(int k)
1351 {
1352 // Where int k is the returned card into card Value
1353 return String.valueOf(Card.cardValues[k]);
1354 }
1355
1356 /**
1357 * Get a card suit based on its integer representation, where 0 - 3
1358 * represent "C", "D", "H", and "S".
1359 *
1360 * @param j the integer that represents a card suit
1361 *
1362 * @return the card suit represented by the requested integer
1363 */
1364 static String turnIntIntoCardSuit(int j)
1365 {
1366 // Where int j is the returned int into card Suit
1367 return cardSuit[j];
1368 }
1369
1370 /**
1371 * Get an integer representation of a card value for a requested card.
1372 *
1373 * @param card the card to get the value position for
1374 *
1375 * @return the value position represented by an integer
1376 */
1377 private static int valueAsInt(Card card)
1378 {
1379 String values = new String(Card.cardValues);
1380 return values.indexOf(card.getValue());
1381 }
1382
1383 /**
1384 * Get an integer representation of a card suit for a requested card.
1385 *
1386 * @param card the card to get the suit position for
1387 *
1388 * @return the suit position represented by an integer
1389 */
1390 private static int suitAsInt(Card card)
1391 {
1392 return card.getSuit().ordinal();
1393 }
1394
1395 /**
1396 * Get the icon associated with the card requested.
1397 *
1398 * @param card the card to get an associated icon of
1399 *
1400 * @return the icon for the card requested
1401 */
1402 static public Icon getIcon(Card card)
1403 {
1404 return iconCards[valueAsInt(card)][suitAsInt(card)];
1405 }
1406
1407 /**
1408 * Get back of card image icon.
1409 *
1410 * @return back of card image icon
1411 */
1412 static public Icon getBackCardIcon()
1413 {
1414 return iconBack;
1415 }
1416 }
1417
1418 /**
1419 * A playing card.
1420 */
1421 class Card
1422 {
1423 public enum Suit
1424 {
1425 CLUBS, DIAMONDS, HEARTS, SPADES
1426 }
1427
1428 private char value;
1429 private boolean errorFlag;
1430 private Suit suit;
1431
1432 // Set card values.
1433 public static char[] cardValues = {'A', '2', '3', '4', '5', '6', '7', '8',
1434 '9', 'T', 'J', 'Q', 'K', 'X'};
1435
1436 // Order the card values with smallest first, include 'X' for a joker/
1437 public static char[] valueRanks = {'2', '3', '4', '5', '6', '7', '8', '9',
1438 'T', 'J', 'Q', 'K', 'A', 'X'};
1439
1440 /**
1441 * Overloaded constructor, allows for passing in Card value and Card suit.
1442 * Uses set method to set variables.
1443 *
1444 * @param value a valid value for Card char value
1445 * @param suit a valid suit from Card enum suit
1446 */
1447 Card(char value, Suit suit)
1448 {
1449 set(value, suit);
1450 }
1451
1452 /**
1453 * Default constructor for Card class that requires no arguments, sets value
1454 * to 'A' and suit to spades using set method.
1455 */
1456 Card()
1457 {
1458 // Because no parameters were passed, create card with the default
1459 // value of 'A' and spades.
1460 set('A', Suit.SPADES);
1461 errorFlag = false;
1462 }
1463
1464 /**
1465 * Accessor method for Card suit.
1466 *
1467 * @return current value of suit
1468 */
1469 public Suit getSuit()
1470 {
1471 return suit;
1472 }
1473
1474 /**
1475 * Accessor method for Card value.
1476 *
1477 * @return current contents of value
1478 */
1479 public char getValue()
1480 {
1481 return value;
1482 }
1483
1484 /**
1485 * Accessor method for Card errorFlag.
1486 *
1487 * @return returns current value of errorFlag
1488 */
1489 public boolean getErrorFlag()
1490 {
1491 return errorFlag;
1492 }
1493
1494 /**
1495 * Gets the card value display. If there is an error flag raised on the card,
1496 * the card display will indicate so an not return the card display.
1497 *
1498 * @return the card display
1499 */
1500 public String toString()
1501 {
1502 // If the error flag is true, return error message. Otherwise, return
1503 // string of Card value and Card suit.
1504 return errorFlag ? "** illegal **" : value + " of " + suit;
1505 }
1506
1507 /**
1508 * Set Card value and Card suit. Sets errorFlag based on success or failure.
1509 * Uses isValid() to check validity of passed in value.
1510 *
1511 * @param value a Card char value
1512 * @param suit a Card enum Suit (CLUBS, DIAMONDS, HEARTS, or SPADES)
1513 *
1514 * @return true if set was successful, false otherwise
1515 */
1516 public boolean set(char value, Suit suit)
1517 {
1518 if (isValid(value, suit))
1519 {
1520 this.value = value;
1521 this.suit = suit;
1522 errorFlag = false;
1523 }
1524 else
1525 {
1526 errorFlag = true;
1527 }
1528 return !errorFlag;
1529 }
1530
1531 /**
1532 * Sort incoming array of cards using a bubble sort routine.
1533 *
1534 * @param cards the cards to sort
1535 * @param arraySize the size of the array
1536 */
1537 static void arraySort(Card[] cards, int arraySize)
1538 {
1539 // Set a temporary card
1540 Card temp;
1541 // Iterate over the Cards[] array
1542 for (int i = 0; i < arraySize; i++)
1543 {
1544 for (int j = 1; j < arraySize - i; j++)
1545 {
1546 // If there are no cards
1547 if (cards[j] == null)
1548 {
1549 // End process
1550 return;
1551 }
1552 // Else sort incoming array of cards using a bubble sort routine
1553 int previousRankIndex = findValueRankIndex(cards[j - 1].getValue());
1554 int currentRankIndex = findValueRankIndex(cards[j].getValue());
1555 if (previousRankIndex > currentRankIndex)
1556 {
1557 temp = cards[j - 1];
1558 cards[j - 1] = cards[j];
1559 cards[j] = temp;
1560 }
1561 }
1562 }
1563 }
1564
1565 /**
1566 * Get the rank for the card value, which is the index position of it
1567 * within the valueRanks array.
1568 *
1569 * @param cardValue a card value
1570 *
1571 * @return the value rank index
1572 */
1573 public static int findValueRankIndex(char cardValue)
1574 {
1575 for (int i = 0; i < valueRanks.length; i++)
1576 {
1577 if (cardValue == valueRanks[i])
1578 {
1579 return i;
1580 }
1581 }
1582 return 0;
1583 }
1584
1585 /**
1586 * Compare this card to a specific card to see if they're the same.
1587 *
1588 * @param card the card to compare to this card
1589 *
1590 * @return true if all the fields (members) are identical and false,
1591 * otherwise
1592 */
1593 public boolean equals(Card card)
1594 {
1595 return suit == card.getSuit() && value == card.getValue()
1596 && errorFlag == card.getErrorFlag();
1597 }
1598
1599 /**
1600 * Checks if the card value is valid.
1601 *
1602 * @param value a valid Card char value (must of value 1-9, T, J, Q, K, or
1603 * A)
1604 * @param suit a valid Suit from Card enum Suit
1605 *
1606 * @return true if the value is valid, false otherwise
1607 */
1608 private boolean isValid(char value, Suit suit)
1609 {
1610 // Suit does not need to be verified yet, but it is there for future use.
1611 switch (value)
1612 {
1613 case '2':
1614 case '3':
1615 case '4':
1616 case '5':
1617 case '6':
1618 case '7':
1619 case '8':
1620 case '9':
1621 case 'T':
1622 case 'J':
1623 case 'Q':
1624 case 'K':
1625 case 'A':
1626 case 'X': // For joker
1627 return true;
1628 default:
1629 return false;
1630 }
1631 }
1632 }
1633
1634 /**
1635 * An object of the type Hand represents a hand of cards. The hand is empty
1636 * when created and modified during the simulated game through increase/decrease
1637 * functions.
1638 */
1639 class Hand
1640 {
1641 // Maximum number of cards allowed in the hand
1642 public static final int MAX_CARDS = 52;
1643
1644 private Card[] myCards;
1645 private int numCards;
1646
1647 /**
1648 * Create a hand, containing zero elements.
1649 */
1650 public Hand()
1651 {
1652 // Default constructor
1653 myCards = new Card[MAX_CARDS];
1654 // Set num cards to zero
1655 numCards = 0;
1656 }
1657
1658 /**
1659 * Remove all cards from the hand.
1660 */
1661 public void resetHand()
1662 {
1663 myCards = new Card[MAX_CARDS];
1664 numCards = 0;
1665 }
1666
1667 /**
1668 * Get the number of cards in this hand.
1669 *
1670 * @return the number of cards
1671 */
1672 public int getNumCards()
1673 {
1674 return numCards;
1675 }
1676
1677 /**
1678 * Take a card from the deck and place it in this hand.
1679 *
1680 * @return true if successful, false otherwise
1681 */
1682 public boolean takeCard(Card card)
1683 {
1684 if (card == null)
1685 {
1686 return false;
1687 }
1688 // Create an object copy (not a reference copy) of the card and add it
1689 // to the end of the hand.
1690 Card receivedCard = new Card();
1691 if (receivedCard.set(card.getValue(), card.getSuit()))
1692 {
1693 myCards[numCards] = receivedCard;
1694 numCards++;
1695 return true;
1696 }
1697 return false;
1698 }
1699
1700 /**
1701 * Play card by removing it from this hand and returning it.
1702 *
1703 * @return the card to play
1704 */
1705 public Card playCard()
1706 {
1707 for (int i = myCards.length - 1; i >= 0; i--)
1708 {
1709 if (myCards[i] != null)
1710 {
1711 // Get and remove the card in highest position of the hand.
1712 Card playedCard = myCards[i];
1713 myCards[i] = null;
1714 numCards--;
1715 return playedCard;
1716
1717 }
1718 }
1719 // If a card was not found, return a bad card.
1720 Card badCard = new Card();
1721 badCard.set(' ', null); // Intentionally set errorFlag on card.
1722 return badCard;
1723 }
1724
1725 /**
1726 * Play a card from a particular place in the hand.
1727 *
1728 * @param cardIndex the card index for the card to play
1729 *
1730 * @return the card at the specified index
1731 */
1732 public Card playCard(int cardIndex)
1733 {
1734 if (numCards > 0 && cardIndex < myCards.length
1735 && myCards[cardIndex] != null)
1736 {
1737 // Get and remove the card in the specified location.
1738 Card playedCard = myCards[cardIndex];
1739 myCards[cardIndex] = null;
1740 numCards--;
1741 for (int i = cardIndex; i < numCards; i++)
1742 {
1743 myCards[i] = myCards[i + 1];
1744 }
1745
1746 myCards[numCards] = null;
1747 return playedCard;
1748 }
1749 // Return a bad card if there aren't any more cards in the hand or
1750 // if the requested card doesn't exist.
1751 Card badCard = new Card();
1752 badCard.set(' ', null); // Intentionally set errorFlag on card.
1753 return badCard;
1754 }
1755
1756 /**
1757 * View an individual card in this hand.
1758 *
1759 * @param k the position of the card to view
1760 *
1761 * @return the card requested
1762 */
1763 public Card inspectCard(int k)
1764 {
1765 if (k >= 0 && k < myCards.length && myCards[k] != null)
1766 {
1767 return myCards[k];
1768 }
1769 // Returns a card with the error flag set to "true" if k is bad.
1770 Card errorCard = new Card();
1771 errorCard.set(' ', null); // Intentionally set errorFlag on card.
1772 return errorCard;
1773 }
1774
1775 /**
1776 * Gets string display for the entire hand.
1777 *
1778 * @return the cards in this hand for display
1779 */
1780 public String toString()
1781 {
1782 String hand = "";
1783 for (int i = 0; i < numCards; i++)
1784 {
1785 hand += myCards[i].toString() + ", ";
1786 }
1787 return "Hand = ( " + hand + ")";
1788 }
1789
1790 /**
1791 * Sort the hand by calling on arraySort() method.
1792 */
1793 public void sort()
1794 {
1795 Card.arraySort(myCards, myCards.length);
1796 }
1797 }
1798
1799 /**
1800 * The Deck object is the source of all cards. It's where the dealer gets cards
1801 * to deal. If a player takes an individual card after the deal, they take it
1802 * from the Deck object.
1803 */
1804 class Deck
1805 {
1806 // Maximum cards number is six card packs or 336 cards (6 * 56 cards).
1807 public static final int MAX_CARDS = 336;
1808 private static Card[] masterPack;
1809
1810 private Card[] cards;
1811 private int topCard;
1812 private int numPacks;
1813
1814 /**
1815 * Create a new card deck.
1816 *
1817 * @param numPacks the number of pack to initialize the new card deck with
1818 */
1819 public Deck(int numPacks)
1820 {
1821 // Create master pack the cards.
1822 allocateMasterPack();
1823 init(numPacks);
1824 }
1825
1826 /**
1827 * Create a new card deck. Deck will be initialized with one pack.
1828 */
1829 public Deck()
1830 {
1831 // Create master pack the cards.
1832 allocateMasterPack();
1833 init(1);
1834 }
1835
1836 /**
1837 * Get the location of the top card.
1838 *
1839 * @return the location of the top card
1840 */
1841 public int getTopCard()
1842 {
1843 return topCard;
1844 }
1845
1846 /**
1847 * Initialize the card deck to a new, initial state.
1848 *
1849 * @param numPacks the number of packs to initial the deck with
1850 */
1851 public void init(int numPacks)
1852 {
1853 this.numPacks = numPacks;
1854 topCard = -1;
1855 // Re-populate cards[] with the standard 56 numPacks cards; adjusted for
1856 // jokers.
1857 cards = new Card[numPacks * 56];
1858
1859 for (int i = 0; i < numPacks; i++)
1860 {
1861 for (Card card : masterPack)
1862 {
1863 topCard++;
1864 cards[topCard] = card;
1865 }
1866 }
1867 }
1868
1869 /**
1870 * Shuffle the deck.
1871 */
1872 public void shuffle()
1873 {
1874 int newIndex;
1875 Card temp;
1876 Random randomIndex = new Random();
1877
1878 // We start at the bottom of the card[] array (position 56 card
1879 // deck). Each time through the loop we pick a random location that is
1880 // higher up in the deck and perform a swap.
1881 for (int i = cards.length - 1; i > 0; i--)
1882 {
1883 newIndex = randomIndex.nextInt(i);
1884 temp = cards[i];
1885 cards[i] = cards[newIndex];
1886 cards[newIndex] = temp;
1887 }
1888 }
1889
1890 /**
1891 * Pull a card from the top of the deck.
1892 *
1893 * @return the top card in the deck
1894 */
1895 public Card dealCard()
1896 {
1897 if (topCard < 1)
1898 {
1899 // Return null if there aren't any more cards in the deck.
1900 return null;
1901 }
1902 // Remove and return the top card in the deck.
1903 Card dealtCard = cards[topCard];
1904 cards[topCard] = null;
1905 topCard--;
1906 return dealtCard;
1907 }
1908
1909 /**
1910 * View the card at a particular location.
1911 *
1912 * @param k the location of the card to view
1913 *
1914 * @return the card at the specified location
1915 */
1916 public Card inspectCard(int k)
1917 {
1918 if (k >= 0 && k < cards.length && cards[k] != null)
1919 {
1920 return cards[k];
1921 }
1922 // Returns a card with the error flag set to "true" if k is bad.
1923 Card errorCard = new Card();
1924 errorCard.set(' ', null); // Intentionally set errorFlag.
1925 return errorCard;
1926 }
1927
1928 /**
1929 * Create a single, static master card deck. This deck will not contain
1930 * card duplicates and will be the sole reference for the card types. If
1931 * the master card deck hass already been allocated, this method will not
1932 * run again.
1933 */
1934 private static void allocateMasterPack()
1935 {
1936 if (masterPack != null)
1937 {
1938 return; // Return early if pack is already allocated.
1939 }
1940 // Create master pack where there is one of each card type.
1941 Card.Suit[] cardSuits = new Card.Suit[]{Card.Suit.SPADES, Card.Suit.CLUBS,
1942 Card.Suit.DIAMONDS, Card.Suit.HEARTS};
1943 char[] cardValues = new char[]{'A', '2', '3', '4', '5', '6', '7', '8',
1944 '9', 'T', 'J', 'Q', 'K', 'X'}; // Adjusted for the joker in the master
pack
1945 int insertPosition = 0;
1946 // The card suits multiplied by the card values should give us 56, a
1947 // standard deck with joker adjustment.
1948 masterPack = new Card[cardSuits.length * cardValues.length];
1949 for (Card.Suit cardSuit : cardSuits)
1950 {
1951 for (char cardValue : cardValues)
1952 {
1953 masterPack[insertPosition] = new Card(cardValue, cardSuit);
1954 insertPosition++;
1955 }
1956 }
1957 }
1958
1959 /**
1960 * Add a card to the deck.
1961 *
1962 * @param card card to add to the deck
1963 *
1964 * @return true if card was added to the deck, false otherwise
1965 */
1966 public boolean addCard(Card card)
1967 {
1968 int occurrences = 0;
1969 for (Card cardInHand : cards)
1970 {
1971 if (cardInHand != null && cardInHand.equals(card))
1972 {
1973 occurrences++;
1974 }
1975 }
1976 if (occurrences >= numPacks || topCard >= cards.length)
1977 {
1978 // Return false if there are too many instances of the card in the
1979 // deck or there are too many cards in this hand.
1980 return false;
1981 }
1982 cards[topCard] = card;
1983 return true;
1984 }
1985
1986 /**
1987 * Remove a card from the deck.
1988 *
1989 * @param card the card to remove from the deck
1990 *
1991 * @return true if card was in the deck and removed from it, false otherwise
1992 */
1993 public boolean removeCard(Card card)
1994 {
1995 for (int i = 0; i < this.getNumCards(); i++)
1996 {
1997 if (card.equals(cards[i]))
1998 {
1999 // When we find a matching card, direct the matching card's
2000 // position to the current top card, set the top card position to
2001 // empty, and decrement the position of the top card.
2002 cards[i] = cards[topCard];
2003 cards[topCard] = null;
2004 topCard--;
2005 return true;
2006 }
2007 }
2008 return false;
2009 }
2010
2011 /**
2012 * Sort cards in the deck.
2013 */
2014 public void sort()
2015 {
2016 Card.arraySort(cards, cards.length);
2017 }
2018
2019 /**
2020 * Getter for returning the number of cards remaining in the deck
2021 *
2022 * @return numberOfCards remaining
2023 */
2024 public int getNumCards()
2025 {
2026 int numberOfCards = 0; // Reset card counter to zero.
2027 // Count cards in the deck. There is a card in the deck if the space
2028 // for the card is not null.
2029 for (Card card : cards)
2030 {
2031 if (card != null)
2032 {
2033 numberOfCards++;
2034 }
2035 }
2036 return numberOfCards;
2037 }
2038 }
2039
2040
2041 //
2042 // Credits:
2043 // While we're using this class (CardGameFramework), we did not actually
2044 // write it. It is copied and pasted from:
2045 // http://www.siskiyous.edu/class/csci1007/cecil/cardGameFramework.txt
2046 //
2047 // class CardGameFramework ----------------------------------------------------
2048 class CardGameFramework
2049 {
2050 private static final int MAX_PLAYERS = 50;
2051
2052 private int numPlayers;
2053 private int numPacks; // # standard 52-card packs per deck
2054 // ignoring jokers or unused cards
2055 private int numJokersPerPack; // if 2 per pack & 3 packs per deck, get 6
2056 private int numUnusedCardsPerPack; // # cards removed from each pack
2057 private int numCardsPerHand; // # cards to deal each player
2058 private Deck deck; // holds the initial full deck and gets
2059 // smaller (usually) during play
2060 private Hand[] hand; // one Hand for each player
2061 private Card[] unusedCardsPerPack; // an array holding the cards not used
2062 // in the game. e.g. pinochle does not
2063 // use cards 2-8 of any suit
2064
2065 public CardGameFramework(int numPacks, int numJokersPerPack,
2066 int numUnusedCardsPerPack,
2067 Card[] unusedCardsPerPack,
2068 int numPlayers, int numCardsPerHand)
2069 {
2070 int k;
2071
2072 // filter bad values
2073 if (numPacks < 1 || numPacks > 6)
2074 { numPacks = 1; }
2075 if (numJokersPerPack < 0 || numJokersPerPack > 4)
2076 { numJokersPerPack = 0; }
2077 if (numUnusedCardsPerPack < 0 || numUnusedCardsPerPack > 50) // > 1 card
2078 { numUnusedCardsPerPack = 0; }
2079 if (numPlayers < 1 || numPlayers > MAX_PLAYERS)
2080 { numPlayers = 4; }
2081 // one of many ways to assure at least one full deal to all players
2082 if (numCardsPerHand < 1 ||
2083 numCardsPerHand > numPacks * (52 - numUnusedCardsPerPack)
2084 / numPlayers)
2085 {
2086 numCardsPerHand = numPacks * (52 - numUnusedCardsPerPack) / numPlayers;
2087 }
2088
2089 // allocate
2090 this.unusedCardsPerPack = new Card[numUnusedCardsPerPack];
2091 this.hand = new Hand[numPlayers];
2092 for (k = 0; k < numPlayers; k++)
2093 { this.hand[k] = new Hand(); }
2094 deck = new Deck(numPacks);
2095
2096 // assign to members
2097 this.numPacks = numPacks;
2098 this.numJokersPerPack = numJokersPerPack;
2099 this.numUnusedCardsPerPack = numUnusedCardsPerPack;
2100 this.numPlayers = numPlayers;
2101 this.numCardsPerHand = numCardsPerHand;
2102 for (k = 0; k < numUnusedCardsPerPack; k++)
2103 { this.unusedCardsPerPack[k] = unusedCardsPerPack[k]; }
2104
2105 // prepare deck and shuffle
2106 newGame();
2107 }
2108
2109 // constructor overload/default for game like bridge
2110 public CardGameFramework()
2111 {
2112 this(1, 0, 0, null, 4, 13);
2113 }
2114
2115 public Hand getHand(int k)
2116 {
2117 // hands start from 0 like arrays
2118
2119 // on error return automatic empty hand
2120 if (k < 0 || k >= numPlayers)
2121 { return new Hand(); }
2122
2123 return hand[k];
2124 }
2125
2126 public Card getCardFromDeck()
2127 {
2128 return deck.dealCard();
2129 }
2130
2131 public int getNumCardsRemainingInDeck()
2132 {
2133 return deck.getNumCards();
2134 }
2135
2136 public void newGame()
2137 {
2138 int k, j;
2139
2140 // clear the hands
2141 for (k = 0; k < numPlayers; k++)
2142 { hand[k].resetHand(); }
2143
2144 // restock the deck
2145 deck.init(numPacks);
2146
2147 // remove unused cards
2148 for (k = 0; k < numUnusedCardsPerPack; k++)
2149 { deck.removeCard(unusedCardsPerPack[k]); }
2150
2151 // add jokers
2152 for (k = 0; k < numPacks; k++)
2153 {
2154 for (j = 0; j < numJokersPerPack; j++)
2155 { deck.addCard(new Card('X', Card.Suit.values()[j])); }
2156 }
2157
2158 // shuffle the cards
2159 deck.shuffle();
2160 }
2161
2162 public boolean deal()
2163 {
2164 // returns false if not enough cards, but deals what it can
2165 int k, j;
2166 boolean enoughCards;
2167
2168 // clear all hands
2169 for (j = 0; j < numPlayers; j++)
2170 { hand[j].resetHand(); }
2171
2172 enoughCards = true;
2173 for (k = 0; k < numCardsPerHand && enoughCards; k++)
2174 {
2175 for (j = 0; j < numPlayers; j++)
2176 {
2177 if (deck.getNumCards() > 0)
2178 {
2179 while (!hand[j].takeCard(deck.dealCard()))
2180 {
2181
2182 }
2183 }
2184 else
2185 {
2186 enoughCards = false;
2187 break;
2188 }
2189 }
2190 }
2191 return enoughCards;
2192 }
2193
2194 void sortHands()
2195 {
2196 int k;
2197
2198 for (k = 0; k < numPlayers; k++)
2199 { hand[k].sort(); }
2200 }
2201
2202 Card playCard(int playerIndex, int cardIndex)
2203 {
2204 // returns bad card if either argument is bad
2205 if (playerIndex < 0 || playerIndex > numPlayers - 1 ||
2206 cardIndex < 0 || cardIndex > numCardsPerHand - 1)
2207 {
2208 //Creates a card that does not work
2209 return new Card('M', Card.Suit.SPADES);
2210 }
2211 // return the card played
2212 return hand[playerIndex].playCard(cardIndex);
2213
2214 }
2215
2216 boolean takeCard(int playerIndex)
2217 {
2218 // returns false if either argument is bad
2219 if (playerIndex < 0 || playerIndex > numPlayers - 1)
2220 { return false; }
2221
2222 // Are there enough Cards?
2223 if (deck.getNumCards() <= 0)
2224 { return false; }
2225
2226 return hand[playerIndex].takeCard(deck.dealCard());
2227 }
2228 }

You might also like