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: TextPlayer.java.html,v 1.1 2005/06/14 06:50:55 gsmith Exp $
025
026 package garrettsmith.blackjack.refimpl;
027
028 // Java packages
029 import java.io.*;
030 import java.util.*;
031
032 // third-party packages
033 import gnu.getopt.*;
034
035 // garrett_smith packages
036 import garrettsmith.blackjack.*;
037 import garrettsmith.playingcards.*;
038
039
040 /**
041 * Provides for playing Blackjack via the console.
042 * Use argument --help for command line parameter use.
043 *
044 * @author Garrett Smith, gsmith at northwestern dot edu
045 * @version Blackjack v1.0
046 * @since Blackjack v1.0
047 */
048 public class TextPlayer implements EventHandler {
049
050 private static final String[] blanks = new String[] { "", "\r", "\r\n", "\n" };
051 private static final int NO = 0;
052 private static final int YES = 1;
053 private static final int NEITHER = -1;
054
055 private static final String _APP_NAME = EventHandler.class.getName();
056
057 private static TextPlayer _player = new TextPlayer();
058
059 private boolean _hasDealerCardBeenPrinted = false;
060 private Blackjack _blackjack;
061 private Map _dealerMappings = new HashMap();
062 private double _purse = 0.0;
063 private Rules _rules = null;
064
065 /**
066 * <p>
067 * Plays blackjack via the console. Use argument --help for command line parameter usage.
068 * </p>
069 */
070 public static
071 void main( String[] arguments ) {
072
073 Rules rules = null;
074 Options options = new Options( arguments );
075 if ( !options.argsCorrect ) {
076
077 printHelp();
078 System.exit( 1 );
079 }
080 else if ( options.helpSelected ) {
081
082 printHelp();
083 System.exit( 0 );
084 }
085 else if ( options.rulesSpecified ) {
086
087 try {
088
089 Properties props = new Properties();
090 InputStream input = new BufferedInputStream(
091 new FileInputStream(
092 options.rulesPath ) );
093 props.load( input );
094 rules = new Rules( props );
095 input.close();
096 }
097 catch ( IOException ioE ) {
098
099 System.err.println( _APP_NAME + ": trouble opening file "
100 + options.rulesPath );
101 System.err.println( _APP_NAME + ": more information: "
102 + ioE.getMessage() );
103 printHelp();
104 return;
105 }
106 }
107
108 try {
109
110 _player.setRules ( rules );
111 while ( _player.playGame() ) {
112
113 /* no operation */
114 }
115 }
116 catch ( Exception e ) {
117
118 System.err.println( "an unknown error occurred; detail below" );
119 e.printStackTrace();
120 return;
121 }
122 }
123
124
125 /**
126 * Invoked when insurance if available; always returns false.
127 */
128 public
129 boolean offerInsurance( Hand hand ) {
130
131 return false;
132 }
133
134
135 /**
136 * Invoked when early surrender is offered; this method then prompts the user.
137 */
138 public
139 boolean offerEarlySurrender( Hand hand ) {
140
141 try {
142
143 printDealerCardIfNeeded( hand );
144 return promptForSurrender();
145 }
146 catch ( Exception e ) {
147
148 fatalErrorOccurred( e );
149 return false;
150 }
151 }
152
153
154 public
155 Move offerRegularTurn( Hand hand ) {
156 try {
157 printDealerCardIfNeeded( hand );
158 System.out.print( "For this hand you have " );
159 printCards( hand.getCards() );
160 System.out.println( " ("
161 + ( hand.isSoft() ? "soft " : "" )
162 + Integer.toString( hand.getBestValue() ) + ")." );
163 return getMoveFromUser( hand );
164 }
165 catch ( Exception e ) {
166 fatalErrorOccurred( e );
167 return null;
168 }
169 }
170
171 public
172 void fatalErrorOccurred( Exception e ) {
173
174 System.err.println( "a fatal error occurred: " + e.getMessage() );
175 e.printStackTrace();
176 System.exit( 1 );
177 }
178
179
180 public
181 void handFinished( Hand hand,
182 double gainOrLoss,
183 Result result,
184 CardList dealerCards ) {
185
186 System.out.print( "game over: you " );
187 if ( Result.PUSH.equals( result ) ) {
188 System.out.println( "pushed: 0.0" );
189 }
190 else if ( Result.WIN.equals( result ) ) {
191 System.out.println( "won: " + Double.toString( gainOrLoss ) );
192 }
193 else if ( Result.LOSE.equals( result ) ) {
194 System.out.println( "lost: " + Double.toString( gainOrLoss ) );
195 }
196 else if ( Result.LATE_SURRENDER.equals( result ) ) {
197 System.out.println( "surrendered: "
198 + Double.toString( gainOrLoss ) );
199 }
200 else if ( Result.DEALER_BLACKJACK.equals( result ) ) {
201 System.out.println( "lost: " + Double.toString( gainOrLoss )
202 + "; the dealer had a blackjack" );
203 }
204 else if ( Result.BUSTED.equals( result ) ) {
205 System.out.println( "BUSTED: " + Double.toString( gainOrLoss ) );
206 }
207 else if ( Result.DEALER_BUSTED.equals( result ) ) {
208 System.out.println( "won; the dealer BUSTED: "
209 + Double.toString( gainOrLoss ) );
210 }
211 else if ( Result.BLACKJACK.equals( result ) ) {
212 System.out.println( "got BLACKJACK!: "
213 + Double.toString( gainOrLoss ) );
214 }
215 else if ( Result.BLACKJACK_PUSH.equals( result ) ) {
216 System.out.println( "pushed, you BOTH had blackjack: "
217 + Double.toString( gainOrLoss ) );
218 }
219 else {
220 throw new IllegalArgumentException( "unknown value of parameter "
221 + "result: " + result.value() );
222 }
223 System.out.print( "In the end, the dealer had " );
224 printCards( dealerCards );
225 System.out.println( " ("
226 + Integer.toString( Blackjack.calculateBestValue( dealerCards ) )
227 + ")," );
228 System.out.print( "and you had " );
229 printCards( hand.getCards() );
230 System.out.println( " ("
231 + Blackjack.calculateBestValue( hand.getCards() ) + ")." );
232 _purse += gainOrLoss;
233 System.out.println( "You have " + Double.toString( _purse )
234 + " in your purse." );
235 }
236
237
238 private static
239 String formatCard( Card card ) {
240
241 if ( Card.Value.ACE.equals( card.getValue() ) ) {
242
243 return "an ace";
244 }
245 else if ( Card.Value.KING.equals( card.getValue() ) ) {
246
247 return "a king";
248 }
249 else if ( Card.Value.QUEEN.equals( card.getValue() ) ) {
250
251 return "a queen";
252 }
253 else if ( Card.Value.JACK.equals( card.getValue() ) ) {
254
255 return "a jack";
256 }
257 else if ( Card.Value.TEN.equals( card.getValue() ) ) {
258
259 return "a ten";
260 }
261 else if ( Card.Value.NINE.equals( card.getValue() ) ) {
262
263 return "a nine";
264 }
265 else if ( Card.Value.EIGHT.equals( card.getValue() ) ) {
266
267 return "an eight";
268 }
269 else if ( Card.Value.SEVEN.equals( card.getValue() ) ) {
270
271 return "a seven";
272 }
273 else if ( Card.Value.SIX.equals( card.getValue() ) ) {
274
275 return "a six";
276 }
277 else if ( Card.Value.FIVE.equals( card.getValue() ) ) {
278
279 return "a five";
280 }
281 else if ( Card.Value.FOUR.equals( card.getValue() ) ) {
282
283 return "a four";
284 }
285 else if ( Card.Value.THREE.equals( card.getValue() ) ) {
286
287 return "a three";
288 }
289 else if ( Card.Value.TWO.equals( card.getValue() ) ) {
290
291 return "a two";
292 }
293 throw new IllegalArgumentException( "unknown card type: "
294 + card.toString() );
295 }
296
297 private static
298 int interpretYesNo( String input ) {
299
300 input = input.trim().toLowerCase();
301
302 if ( input.length() <= 0 ) {
303
304 return NEITHER;
305 }
306 if ( input.startsWith( "y" ) ) {
307
308 return YES;
309 }
310 else if ( input.startsWith( "n" ) ) {
311
312 return NO;
313 }
314 return NEITHER;
315 }
316
317
318 private
319 boolean playGame()
320 throws IOException {
321
322 if ( _blackjack == null ) {
323
324 if ( _rules == null ) {
325
326 _blackjack = new Blackjack();
327 }
328 else {
329
330 _blackjack = new Blackjack( _rules );
331 }
332 }
333
334 System.out.println();
335 _hasDealerCardBeenPrinted = false;
336 _blackjack.playGame( this, 1 );
337
338 return promptForAnotherGame();
339 }
340
341
342 private
343 void printDealerCard( Hand hand ) {
344
345 System.out.println( "For this hand the dealer is showing "
346 + formatCard( hand.getDealerCard() ) + "." );
347 }
348
349
350 /**
351 * Prints the dealer's card in human-readable format to standard output if
352 * and only if it has not already been printed for this game.
353 */
354 private
355 void printDealerCardIfNeeded( Hand hand ) {
356
357 if ( !_hasDealerCardBeenPrinted ) {
358
359 printDealerCard( hand );
360 _hasDealerCardBeenPrinted = true;
361 }
362 }
363
364
365 private static
366 void printCards( List cards ) {
367
368 Card card;
369 for ( int i = 0; i < cards.size(); i++ ) {
370
371 card = ( Card ) cards.get( i );
372
373 if ( i < cards.size() - 2 ) {
374
375 System.out.print( formatCard( card ) + ", " );
376 }
377 else if ( i == cards.size() - 2 ) {
378
379 System.out.print( formatCard( card ) + " " );
380 }
381 else {
382
383 System.out.print( "and " + formatCard( card ) );
384 }
385 }
386 }
387
388
389 private static
390 boolean promptForAnotherGame()
391 throws IOException {
392
393 String input;
394 int result;
395
396 while ( true ) {
397
398 System.out.print( "Would you like to play another game (YES or no)? ");
399 input = getInputFromUser();
400 result = interpretYesNo( input );
401 if ( result == YES ) {
402
403 return true;
404 }
405 else if ( result == NO ) {
406
407 return false;
408 }
409 else if ( isBlank(input) ) {
410
411 // then it was the default answer
412 return true;
413 }
414 else {
415
416 System.out.println( "Unknown answer \"" + input
417 + "\"; please try again." );
418 }
419 }
420 }
421
422 static boolean isBlank(String input) {
423 for ( int i = 0; i < blanks.length; i++ ) {
424 if ( blanks[ i ].equals( input ) ) return true;
425 }
426 return false;
427 }
428
429
430 private static
431 void promptForMove( Hand hand ) {
432
433 String prompt = "What would you like to do? ( Hit | STand ";
434 if ( hand.isSurrenderAllowed() ) {
435
436 prompt += "| SUrrender ";
437 }
438 if ( hand.isDoubleDownAllowed() ) {
439
440 prompt += "| Double ";
441 }
442 if ( hand.isSplitAllowed() ) {
443
444 prompt += "| SPlit ";
445 }
446 prompt += "): ";
447 System.out.print( prompt );
448 }
449
450
451 private static
452 String getInputFromUser()
453 throws IOException {
454
455 byte[] input = new byte[ 2048 ];
456 int length;
457 length = System.in.read( input );
458 return new String( input, 0, length - 1 );
459 }
460
461
462 private static
463 Move getMoveFromUser( Hand hand )
464 throws IOException {
465
466 String input;
467 Move response;
468
469 while ( true ) {
470
471 promptForMove( hand );
472 input = getInputFromUser();
473 response = interpretMove( input );
474 if ( response == null ) {
475
476 System.out.println( "I'm sorry, I didn't understand \"" + input
477 + "\"; please try again." );
478 }
479 else if ( !hand.isMoveAllowed( response ) ) {
480
481 System.out.println( "I'm sorry, but that move is not allowed.");
482 }
483 else {
484
485 break;
486 }
487 }
488 return response;
489 }
490
491
492 private static
493 Move interpretMove( String input ) {
494
495 input = input.trim().toLowerCase();
496 if ( input.startsWith( "h" ) ) {
497
498 return Move.HIT;
499 }
500 else if ( input.startsWith( "st" ) ) {
501
502 return Move.STAND;
503 }
504 else if ( input.startsWith( "su" ) ) {
505
506 return Move.SURRENDER;
507 }
508 else if ( input.startsWith( "sp" ) ) {
509
510 return Move.SPLIT;
511 }
512 else if ( input.startsWith( "d" ) ) {
513
514 return Move.DOUBLE;
515 }
516 return null;
517 }
518
519
520 private static
521 void printHelp() {
522
523 System.out.println(
524 "usage: " + _APP_NAME + " [options...]\n"
525 + "no options will run the application with the default rules\n"
526 + "-h/--help Print this help information\n"
527 + "-r/--rules <properties-file>\n"
528 + " Run the application with the rules specified in "
529 + "<properties-file>" );
530 }
531
532
533 private static
534 boolean promptForSurrender()
535 throws IOException {
536
537 String input;
538 int result;
539
540 while ( true ) {
541
542 System.out.print( "Would you like to surrender (yes or NO)? " );
543 input = getInputFromUser();
544 result = interpretYesNo( input );
545 if ( result == YES ) {
546
547 return true;
548 }
549 else if ( result == NO ) {
550
551 return false;
552 }
553 else if ( isBlank(input) ) {
554
555 // default value
556 return false;
557 }
558 else {
559
560 System.out.println( "Unknown answer \"" + input
561 + "\"; please try again." );
562 }
563 }
564 }
565
566
567 private
568 void setRules( Rules rules ) {
569 _rules = rules;
570 }
571
572
573 private
574 TextPlayer() {}
575
576 private static class Options {
577
578 private Options( String[] arguments ) {
579
580 LongOpt[] longOpts = new LongOpt[ 3 ];
581 longOpts[ 0 ] = new LongOpt( "help",
582 LongOpt.NO_ARGUMENT,
583 null,
584 'h' );
585 longOpts[ 1 ] = new LongOpt( "rules",
586 LongOpt.REQUIRED_ARGUMENT,
587 null,
588 'r' );
589 Getopt getopt = new Getopt( _APP_NAME,
590 arguments,
591 ":hr:",
592 longOpts );
593 int value;
594 while ( ( value = getopt.getopt() ) != -1 ) {
595
596 switch ( value ) {
597
598 case 'h':
599 helpSelected = true;
600 break;
601
602 case 'r':
603 rulesPath = getopt.getOptarg();
604 rulesSpecified = true;
605 break;
606
607 case '?':
608 case ':':
609 argsCorrect = false;
610 break;
611
612 default:
613 throw new IllegalArgumentException(
614 "getopt() returned '" + (char) value + "'" );
615 }
616 }
617 }
618
619 private boolean helpSelected = false;
620 private boolean rulesSpecified = false;
621 private String rulesPath = null;
622 private boolean argsCorrect = true;
623
624 }
625 }
|