Sunteți pe pagina 1din 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 }

S-ar putea să vă placă și