001 /*
002 Copyright (c) 2005 Garrett Smith
003 The MIT License
004 
005 Permission is hereby granted, free of charge, to any person obtaining a copy 
006 of this software and associated documentation files (the "Software"), to deal
007 in the Software without restriction, including without limitation the rights
008 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
009 copies of the Software, and to permit persons to whom the Software is 
010 furnished to do so, subject to the following conditions:
011 
012 The above copyright notice and this permission notice shall be included in all 
013 copies or substantial portions of the Software.
014 
015 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
016 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
017 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
018 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
019 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
020 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 
021 THE SOFTWARE.
022 */
023 
024 // $Id: Blackjack.java.html,v 1.1 2005/06/14 06:50:55 gsmith Exp $
025 
026 package garrettsmith.blackjack;
027 
028 import java.util.*;
029 import garrettsmith.playingcards.*;
030 
031 /**
032  <p>
033  * Provides for playing a game of blackjack.
034  </p>
035  
036  @author Garrett Smith, gsmith at northwestern dot edu
037  @version Blackjack v1.0
038  @since Blackjack v1.0
039  */
040 public class Blackjack {
041 
042     private             CardContainer   _cards     = null;
043     private             CardList        _dealerCards = new CardList();
044     private             Rules           _rules     = null;
045     
046     private             ArrayList       _handsToPlay     = new ArrayList();
047     private             ArrayList       _handsToEvaluate = new ArrayList();
048 
049     /**
050      * Creates a new Blackjack game simulator with the default {@link Rules}.
051      */
052     public
053     Blackjack() {
054         thisnew Rules() );
055     }
056 
057     /**
058      * Creates a new Blackjack simulator with the specified {@link Rules}.
059      *
060      @param rules specifying which rules this simulator will follow
061      */
062     public
063     Blackjackfinal Rules rules ) {
064         setRulesrules );
065         _cards = new CardContainerStandardDeck.DECK,
066                                     rules.getNumberOfDecks() );
067         _cards.setAutoResetrules.getShuffleLimit() );
068     }
069 
070     /**
071      * Calculates the highest possible value for a list of cards without
072      * busting, if possible.
073      */
074     public static
075     int calculateBestValuefinal CardList cards ) {
076         int value = 0;
077         int numAces = 0;
078         Card card;
079         for int i = 0; i < cards.size(); i++ ) {
080             card = cards.getCard);
081             if Card.Value.ACE.equalscard.getValue() ) ) {
082                 value += 11;
083                 numAces++;
084             }
085             else {
086                 value += Blackjack.calculateValuecard );
087             }
088         }
089         while value > 21 ) {
090             if numAces == return value;
091             value -= 10;
092             numAces--;
093         }
094         return value;
095     }
096 
097     /**
098      * Accepts a {@link CardList} and returns its contents as a human-readable String.
099      */
100     static
101     String cardsToStringfinal CardList cards ) {
102         StringBuffer b = new StringBuffer();
103         b.append"Cards[" );
104         for int i = 0; i < cards.size(); i++ ) {
105             b.appendcardToStringcards.getCard) ) );
106             if i != cards.size() b.append"," );
107         }
108         b.append"]" );
109         return b.toString();
110     }
111 
112     /**
113      * Accepts a {@link Card} and returns its contents as a human-readable String.
114      */
115     static String cardToString(final Card card) {
116         return card.getValue().toString();
117     }
118 
119     /**
120      * Calculates the value of a playing card according to the rules of
121      * Blackjack; assumes all aces are worth 1.
122      *
123      @return the value of card assuming an ace is worth 1
124      */
125     public static
126     int calculateValuefinal Card card ) {
127         final Card.Value value = card.getValue();
128         if Card.Value.ACE.equalsvalue ) ) {
129             return 1;
130         }
131         else if isNonAceFaceCardcard ) ) {
132             return 10;
133         }
134         else if Card.Value.NINE.equalsvalue ) ) {
135             return 9;
136         }
137         else if Card.Value.EIGHT.equalsvalue ) ) {
138             return 8;
139         }
140         else if Card.Value.SEVEN.equalsvalue ) ) {
141             return 7;
142         }
143         else if Card.Value.SIX.equalsvalue ) ) {
144             return 6;
145         }
146         else if Card.Value.FIVE.equalsvalue ) ) {
147             return 5;
148         }
149         else if Card.Value.FOUR.equalsvalue ) ) {
150             return 4;
151         }
152         else if Card.Value.THREE.equalsvalue ) ) {
153             return 3;
154         }
155         else if Card.Value.TWO.equalsvalue ) ) {
156             return 2;
157         }
158         throw new IllegalArgumentException();
159     }
160 
161     /**
162      * Calculates the value of a list of cards according to the rules of
163      * Blackjack; assumes all aces are worth 1.
164      */
165     public static
166     int calculateValuefinal CardList cards ) {
167         int value = 0;
168         for int i = 0; i < cards.size(); i++ ) {
169             value += Blackjack.calculateValuecards.getCard) );
170         }
171         return value;
172     }
173 
174     /**
175      * Returns the current {@link Rules} being used.
176      */
177     public Rules getRules() {
178         return _rules;
179     }
180 
181     /**
182      * Returns whether the card passed in is a non-Ace face card: a ten, jack, queen, or king.
183      */
184     public static
185     boolean isNonAceFaceCardfinal Card card ) {
186         final Card.Value value = card.getValue();
187         return Card.Value.KING.equalsvalue 
188              || Card.Value.QUEEN.equalsvalue 
189              || Card.Value.JACK.equalsvalue 
190              || Card.Value.TEN.equalsvalue ) );
191     }
192 
193     /**
194      * Returns whether the list of cards passed in represents a blackjack.  This method doesn't take
195      * into consideration whether these are the first cards dealt or the result of a split; in the
196      * latter case the cards are not a blackjack.
197      */
198     public static
199     boolean isBlackjackfinal CardList cards ) {
200         if cards.size() != return false;
201         for int i = 0; i < 2; i++ ) {
202             if Card.Value.ACE.equalscards.getCard).getValue() ) ) {
203                 CardList cardsCopy = CardList cards.clone();
204                 cardsCopy.remove);
205                 final Card.Value otherCardValue = cardsCopy.getCard).getValue();
206                 return Card.Value.KING.equalsotherCardValue )
207                      || Card.Value.QUEEN.equalsotherCardValue )
208                      || Card.Value.JACK.equalsotherCardValue 
209                      || Card.Value.TEN.equalsotherCardValue ) );
210             }
211         }
212         return false;
213     }
214 
215     /**
216      * Returns whether the list of cards is a soft hand.  That is, the list of cards
217      * contain an ace whose value could be 1 or 11 without exceeding 21.
218      */
219     public static
220     boolean isSoftfinal CardList cards ) {
221         boolean hasAce = false;
222         for int i = 0; i < cards.size(); i++ ) {
223             if Card.Value.ACE.equalscards.getCard).getValue() ) ) {
224                 hasAce = true;
225                 break;
226             }
227         }
228         return hasAce && calculateValuecards 12;
229     }
230 
231     /**
232      * Plays one game of blackjack betting <code>wager</code> and using <code>handler</code>
233      * to determine how the player's hand is played.
234      */
235     public
236     void playGamefinal EventHandler handler, final double wager ) {
237         try {
238             Hand hand = dealInitialCards(wager);
239             if offerEarlySurrenderhand, handler ) ) return;
240             if isBlackjackhand, handler ) ) return;
241             while !_handsToPlay.isEmpty() ) {
242                 hand = Hand _handsToPlay.remove);
243                 while hand.isInPlay() ) {
244                     playTurn(handler, hand);
245                 }
246                 if hand.isEvaluationNeeded() ) _handsToEvaluate.addhand );
247             }
248             if isDealerPlayNeeded_handsToEvaluate ) ) {
249                 evaluateOutcomeplayDealerHand(), handler );
250             }
251         }
252         catchRuntimeException e ) {
253             handler.fatalErrorOccurred);
254         }
255     }
256 
257     /**
258      * Sets the rules used to determine how the game is handled.
259      */
260     public
261     void setRulesfinal Rules rules ) {
262         _rules = rules;
263     }
264 
265     Card getDealerCard() {
266         return _dealerCards.getCard);
267     }
268 
269     Card getCard()
270         throws NoMoreCardsException {
271         return _cards.getCard();
272     }
273     
274     void setCardsCardContainer cards ) {
275         _cards = cards;
276     }
277 
278     private boolean isDealerPlayNeededfinal ArrayList hands ) {
279         for int i = 0; i < hands.size(); i++ ) {
280             if ( ((Handhands.get)).isDealerPlayNeeded() ) return true;
281         }
282         return false;
283     }
284 
285     private void evaluateOutcomefinal int dealerValue, final EventHandler handler ) {
286         while !_handsToEvaluate.isEmpty() ) {
287             final Hand hand = Hand _handsToEvaluate.remove);
288             int playerValue = hand.getBestValue();
289             if hand.isBusted() ) {
290                 handler.handFinishedhand,
291                                        -* hand.getWager(),
292                                        Result.BUSTED,
293                                        _dealerCards );
294             }
295             else if dealerValue > 21 ) {
296                 handler.handFinishedhand,
297                                        hand.getWager(),
298                                        Result.DEALER_BUSTED,
299                                        _dealerCards );
300             }
301             else if dealerValue > playerValue ) {
302                 handler.handFinishedhand,
303                                        -* hand.getWager(),
304                                        Result.LOSE,
305                                        _dealerCards );
306             }
307             else if playerValue > dealerValue ) {
308                 handler.handFinishedhand,
309                                        hand.getWager(),
310                                        Result.WIN,
311                                        _dealerCards );
312             }
313             else if playerValue == dealerValue ) {
314                 handler.handFinishedhand, 0, Result.PUSH, _dealerCards);
315             }
316             else {
317                 throw new IllegalStateException();
318             }
319         }
320     }
321 
322     private boolean isBlackjackfinal Hand hand, final EventHandler handler ) {
323         final boolean playerHasBlackjack = isBlackjackhand.getCards() );
324         final boolean dealerHasBlackjack = isBlackjack_dealerCards );
325         if (  dealerHasBlackjack && playerHasBlackjack ) {
326             handler.handFinishedhand,
327                     0.0,
328                     Result.BLACKJACK_PUSH,
329                     _dealerCards );
330             return true;
331         }
332         else if dealerHasBlackjack && ! playerHasBlackjack ) {
333             handler.handFinishedhand,
334                     -* hand.getWager(),
335                     Result.DEALER_BLACKJACK,
336                     _dealerCards );
337             return true;
338         }
339         else if playerHasBlackjack & ! dealerHasBlackjack ) {
340             handler.handFinishedhand,
341                                    _rules.getBlackjackPayoff() * hand.getWager(),
342                                    Result.BLACKJACK,
343                                    _dealerCards );
344             return true;
345         }
346         return false;
347     }
348 
349     private boolean offerEarlySurrenderfinal Hand hand, final EventHandler handler ) {
350         if _rules.canSurrenderEarly()
351              && handler.offerEarlySurrenderhand ) ) {
352                 handler.handFinishedhand,
353                                        -0.5 * hand.getWager(),
354                                        Result.EARLY_SURRENDER,
355                                        _dealerCards );
356             return true;
357         }
358         return false;
359     }
360 
361     private Hand dealInitialCards(final double wager) {
362         _handsToEvaluate.clear();
363         _handsToPlay.clear();
364         _dealerCards.clear();
365         CardList cards = new CardList();
366         cards.addgetCard() );
367         _dealerCards.addgetCard() );
368         cards.addgetCard() );
369         _dealerCards.addgetCard() );
370         Hand hand = new Handwager, this, cards );
371         _handsToPlay.addhand );
372         return hand;
373     }
374 
375     private
376     int playDealerHand() {
377         int value;
378         while true ) {
379             value = calculateBestValue_dealerCards );
380             if value < 17 ) {
381                 _dealerCards.addgetCard() );
382             }
383             else if value > 17 ) {
384                 return value;
385             }
386             else if value == 17 && !isSoft_dealerCards ) ) {
387                 return 17;
388             }
389             else if value == 17
390                       && _rules.doesDealerStandOnSoft17()
391                       && isSoft_dealerCards ) ) {
392                 return 17;
393             }
394             else if value == 17
395                       && !_rules.doesDealerStandOnSoft17()
396                       && isSoft_dealerCards ) ) {
397                 _dealerCards.addgetCard() );
398             }
399             else {
400                 throw new IllegalStateException();
401             }
402         }
403     }
404 
405     private void playTurn(final EventHandler handler, final Hand hand) {
406         Move move = handler.offerRegularTurnhand );
407         if move == null 
408             handler.fatalErrorOccurrednew NullPointerException"null move returned" ) );
409         Hand newHand = move.executehand, handler, _dealerCards );
410         if newHand != null _handsToPlay.addnewHand );
411     }
412 
413 // class Blackjack