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: Hand.java.html,v 1.1 2005/06/14 06:50:55 gsmith Exp $
025
026 package garrettsmith.blackjack;
027
028 import garrettsmith.playingcards.*;
029
030 /**
031 * Represents a player's hand of cards.
032 *
033 * @author Garrett Smith, gsmith at northwestern dot edu
034 * @version Blackjack v1.0
035 * @since Blackjack v1.0
036 */
037 public class Hand {
038
039 private CardList _cards = null;
040 private final Blackjack _blackjack;
041 private boolean _isPlayerDone = false;
042 private boolean _isDealerPlayNeeded = true;
043 private double _wager;
044 private SplitCounter _splitCounter = new SplitCounter();
045 private boolean _isEvaluationNeeded = true;
046
047 Hand( final double wager,
048 final Blackjack blackjack,
049 final CardList cards ) {
050 _wager = wager;
051 _blackjack = blackjack;
052 _cards = cards;
053 }
054
055 /**
056 * Returns the highest hand value without busting, if possible. For example, a hand
057 * with a nine and an ace is scored as 20, but a hand with a nine, five, and an
058 * ace is worth 15.
059 *
060 * @see #getValue()
061 */
062 public
063 int getBestValue() {
064 return Blackjack.calculateBestValue( _cards );
065 }
066
067 /**
068 * Returns the value of the hand assuming all aces are worth 1.
069 *
070 * @return the value of the hand assuming any aces are worth 1
071 * @see #getBestValue()
072 */
073 public
074 int getValue() {
075 return Blackjack.calculateValue( _cards );
076 }
077
078 /**
079 * Returns the player's cards that represent this hand.
080 *
081 * @return a <code>List</code> of <code>Card</code> objects
082 * @see garrettsmith.playingcards.Card
083 */
084 public
085 CardList getCards() {
086 return _cards;
087 }
088
089 /**
090 * Returns the card that the dealer displays to the players.
091 *
092 * @return corresponding to the card that the dealer displays to the
093 * players.
094 */
095 public
096 Card getDealerCard() {
097 return _blackjack.getDealerCard();
098 }
099
100 /**
101 * Calculates the value of the dealer's card assuming an ace is worth 1.
102 *
103 * @return the value of the dealer's card assuming an ace is worth 1
104 */
105 public
106 int getDealerValue() {
107 return Blackjack.calculateValue( _blackjack.getDealerCard() );
108 }
109
110 /**
111 * Returns whether this hand has gone over 21.
112 */
113 public
114 boolean isBusted() {
115 return ( Blackjack.calculateValue( _cards ) > 21 );
116 }
117
118 /**
119 * Returns whether a double down is allowed at this point in the game.
120 *
121 * @return <code>true</code> if a double down is allowed, <code>false</code>
122 * if not
123 */
124 public
125 boolean isDoubleDownAllowed() {
126 int value = this.getValue();
127 if ( _cards.size() != 2 ) return false;
128 if ( hasSplit() && !_blackjack.getRules().canDoubleAfterSplit() ) return false;
129 if ( ( value < 9 || value > 11 ) && _blackjack.getRules().isDoubleDownRestricted() )
130 return false;
131 return true;
132 }
133
134 /**
135 * Returns whether standing is allowed at this point in the game.
136 */
137 public
138 boolean isStandAllowed() {
139 return true;
140 }
141
142 /**
143 * Returns the amount that was bet on this hand.
144 */
145 public
146 double getWager() {
147 return _wager;
148 }
149
150 /**
151 * Returns whether <code>move</code> is allowed at this point in the game.
152 */
153 public
154 boolean isMoveAllowed( Move move ) {
155 if ( Move.HIT.equals( move ) ) {
156 return this.isHitAllowed();
157 }
158 else if ( Move.STAND.equals( move ) ) {
159 return this.isStandAllowed();
160 }
161 else if ( Move.SURRENDER.equals( move ) ) {
162 return this.isSurrenderAllowed();
163 }
164 else if ( Move.DOUBLE.equals( move ) ) {
165 return this.isDoubleDownAllowed();
166 }
167 else if ( Move.SPLIT.equals( move ) ) {
168 return this.isSplitAllowed();
169 }
170 else {
171 throw new IllegalStateException( "unknown move: " + move.value() );
172 }
173 }
174
175 /**
176 * Returns whether a hit is allowed at this point in the hand.
177 *
178 * @return <code>true</code> if a hit is allowed, <code>false</code> if not
179 */
180 public
181 boolean isHitAllowed() {
182 return ( this.getValue() <= 21 );
183 }
184
185 /**
186 * Tells whether the hand is soft. A soft hand is where one of the cards
187 * in the hand is an ace and where ace may take the value of 1 or 11
188 * without causing the total value of the hand to exceed 21.
189 *
190 * @return <code>true</code> if the hand is soft, <code>false</code> if
191 * not
192 */
193 public
194 boolean isSoft() {
195 return Blackjack.isSoft( _cards );
196 }
197
198 /**
199 * Returns whether a split is allowed at this point in the hand.
200 *
201 * @return <code>true</code> if a split is allowed, <code>false</code> if
202 * not
203 */
204 public
205 boolean isSplitAllowed() {
206
207 // you must have 2 cards and only 2 cards to split
208 if ( _cards.size() != 2 ) return false;
209 Card card1 = ( Card ) _cards.get( 0 );
210 Card card2 = ( Card ) _cards.get( 1 );
211
212 // you may only split two face cards or two identical non-face cards
213 final boolean hasTwoFaceCards = Blackjack.isNonAceFaceCard( card1 ) && Blackjack.isNonAceFaceCard( card2 );
214 final boolean hasIdenticalCards = card1.getValue().equals( card2.getValue() );
215 if ( !( hasTwoFaceCards || hasIdenticalCards ) ) return false;
216
217 // the rules may only allow one original hand to be split a few times;
218 // check to make sure that the maximum hasn't been exceeded
219 if ( this.getSplitCount() >= _blackjack.getRules().getMaxSplits() ) return false;
220
221 // you may resplit aces depending on the rules
222 if ( hasSplit()
223 && Card.Value.ACE.equals( card1.getValue() )
224 && ! _blackjack.getRules().isResplittingAcesAllowed() ) return false;
225
226 // if all of these conditions have been met, a split is allowed
227 return true;
228 }
229
230 /**
231 * Returns the state of this hand as a human-readable String.
232 */
233 public String toString() {
234 return "Hand[_wager=" + _wager + ",_isPlayerDone="
235 + _isPlayerDone + ",_cards=" + Blackjack.cardsToString( _cards ) + "]";
236 }
237
238 /**
239 * Returns whether a surrender is allowed.
240 *
241 * @return <code>true</code> if a surrender is allowed, <code>false</code>
242 * if not
243 */
244 public
245 boolean isSurrenderAllowed() {
246 return ( _cards.size() == 2
247 && _blackjack.getRules().canSurrenderLate()
248 && !hasSplit() );
249 }
250
251 /**
252 * Performs a double down. A single, final card is issued and the
253 * player's wager is doubled. No additional action can be taken
254 * after a double down.
255 *
256 * @returns a <code>List</code> of <code>Card</code>s that represent
257 * the player's final hand
258 * @throws NotAllowedException if a double down is not allowed
259 */
260 void doubleDown()
261 throws NotAllowedException, NoMoreCardsException {
262 if ( !this.isDoubleDownAllowed() ) {
263 throw new NotAllowedException( "double down" );
264 }
265 _cards.add( _blackjack.getCard() );
266 _isPlayerDone = true;
267 _wager *= 2;
268 }
269
270 boolean isInPlay() {
271 return ( !isPlayerDone() && !isBusted() );
272 }
273
274 boolean isEvaluationNeeded() {
275 return _isEvaluationNeeded;
276 }
277
278 void surrender() {
279 _isPlayerDone = true;
280 _isDealerPlayNeeded = false;
281 _isEvaluationNeeded = false;
282 }
283
284 boolean isDealerPlayNeeded() {
285 return _isDealerPlayNeeded;
286 }
287
288 int getSplitCount() {
289 return _splitCounter.getCount();
290 }
291
292 /**
293 * Performs a hit. A single card is issued.
294 *
295 * @returns a <code>List</code> of <code>Card</code> objects that
296 * represent the player's current hand
297 * @throws NotAllowedException if a hit is not allowed
298 */
299 void hit()
300 throws NotAllowedException, NoMoreCardsException {
301 if ( !this.isHitAllowed() ) {
302 throw new NotAllowedException( "a hit is not allowed at this time");
303 }
304 _cards.add( _blackjack.getCard() );
305 }
306
307 boolean isPlayerDone() {
308 return _isPlayerDone;
309 }
310
311 /**
312 * Performs a split. A reference to a new, second
313 * <code>Hand</code> will be returned; the
314 * <code>Hand</code> that this method was invoked on will
315 * represent the other hand.
316 *
317 * @returns representing the new, second hand resulting from the split
318 */
319 Hand split()
320 throws NotAllowedException, NoMoreCardsException {
321 if ( !this.isSplitAllowed() ) {
322 throw new NotAllowedException( "split is not allowed" );
323 }
324 CardList newCards = new CardList();
325 newCards.add( _cards.remove( 0 ) );
326 _cards.add( _blackjack.getCard() );
327 newCards.add( _blackjack.getCard() );
328 _splitCounter.increment();
329 return new Hand( _wager, _blackjack, newCards, _splitCounter );
330 }
331
332 void stand()
333 throws NotAllowedException {
334 if ( !this.isStandAllowed() ) {
335 throw new NotAllowedException( "stand is not allowed at this time");
336 }
337 _isPlayerDone = true;
338 }
339
340 void addCard() {
341 _cards.add( _blackjack.getCard() );
342 }
343
344 private
345 Hand( double wager,
346 Blackjack blackjack,
347 CardList newCards,
348 SplitCounter splitCounter ) {
349 this( wager, blackjack, newCards );
350 _splitCounter = splitCounter;
351 }
352
353 private boolean hasSplit() {
354 return _splitCounter._count > 0;
355 }
356
357 private class SplitCounter {
358
359 private int _count = 0;
360
361 private SplitCounter() {}
362
363 private int getCount() {
364 return _count;
365 }
366
367 private void increment() {
368 _count++;
369 }
370 }
371 }
|