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 mainString[] arguments ) {
072 
073         Rules rules = null;
074         Options options = new Optionsarguments );
075         if !options.argsCorrect ) {
076 
077             printHelp();
078             System.exit);
079         }
080         else if options.helpSelected ) {
081 
082             printHelp();
083             System.exit);
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.loadinput )
094                 rules = new Rulesprops );
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 offerInsuranceHand 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 offerEarlySurrenderHand hand ) {
140 
141         try {
142 
143             printDealerCardIfNeededhand );
144             return promptForSurrender();
145         }
146         catch Exception e ) {
147 
148             fatalErrorOccurred);
149             return false;
150         }
151     }
152 
153 
154     public
155     Move offerRegularTurnHand hand ) {
156         try {
157             printDealerCardIfNeededhand );
158             System.out.print"For this hand you have " );
159             printCardshand.getCards() );
160             System.out.println" ("
161                 hand.isSoft() "soft " "" )
162                 + Integer.toStringhand.getBestValue() ) ")." );
163             return getMoveFromUserhand );
164         }
165         catch Exception e ) {
166             fatalErrorOccurred);
167             return null;
168         }
169     }
170 
171     public
172     void fatalErrorOccurredException e ) {
173 
174         System.err.println"a fatal error occurred: " + e.getMessage() );
175         e.printStackTrace();
176         System.exit);
177     }
178 
179 
180     public
181     void handFinishedHand hand,
182                        double gainOrLoss,
183                        Result result,
184                        CardList dealerCards ) {
185 
186         System.out.print"game over: you " );
187         if Result.PUSH.equalsresult ) ) {
188             System.out.println"pushed: 0.0" );
189         }
190         else if Result.WIN.equalsresult ) ) {
191             System.out.println"won: " + Double.toStringgainOrLoss ) );
192         }
193         else if Result.LOSE.equalsresult ) ) {
194             System.out.println"lost: " + Double.toStringgainOrLoss ) );
195         }
196         else if Result.LATE_SURRENDER.equalsresult ) ) {
197             System.out.println"surrendered: "
198                 + Double.toStringgainOrLoss ) );
199         }
200         else if Result.DEALER_BLACKJACK.equalsresult ) ) {
201             System.out.println"lost: " + Double.toStringgainOrLoss )
202                 "; the dealer had a blackjack" );
203         }
204         else if Result.BUSTED.equalsresult ) ) {
205             System.out.println"BUSTED: " + Double.toStringgainOrLoss ) );
206         }
207         else if Result.DEALER_BUSTED.equalsresult ) ) {
208             System.out.println"won; the dealer BUSTED: "
209                 + Double.toStringgainOrLoss ) );
210         }
211         else if Result.BLACKJACK.equalsresult ) ) {
212             System.out.println"got BLACKJACK!: "
213                 + Double.toStringgainOrLoss ) );
214         }
215         else if Result.BLACKJACK_PUSH.equalsresult ) ) {
216             System.out.println"pushed, you BOTH had blackjack: "
217                 + Double.toStringgainOrLoss ) );
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         printCardsdealerCards );
225         System.out.println" ("
226             + Integer.toStringBlackjack.calculateBestValuedealerCards ) )
227             ")," );
228         System.out.print"and you had " );
229         printCardshand.getCards() );
230         System.out.println" ("
231             + Blackjack.calculateBestValuehand.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 formatCardCard card ) {
240 
241         if Card.Value.ACE.equalscard.getValue() ) ) {
242 
243             return "an ace";
244         }
245         else if Card.Value.KING.equalscard.getValue() ) ) {
246 
247             return "a king";
248         }
249         else if Card.Value.QUEEN.equalscard.getValue() ) ) {
250 
251             return "a queen";
252         }
253         else if Card.Value.JACK.equalscard.getValue() ) ) {
254 
255             return "a jack";
256         }
257         else if Card.Value.TEN.equalscard.getValue() ) ) {
258 
259             return "a ten";
260         }
261         else if Card.Value.NINE.equalscard.getValue() ) ) {
262 
263             return "a nine";
264         }
265         else if Card.Value.EIGHT.equalscard.getValue() ) ) {
266 
267             return "an eight";
268         }
269         else if Card.Value.SEVEN.equalscard.getValue() ) ) {
270 
271             return "a seven";
272         }
273         else if Card.Value.SIX.equalscard.getValue() ) ) {
274 
275             return "a six";
276         }
277         else if Card.Value.FIVE.equalscard.getValue() ) ) {
278 
279             return "a five";
280         }
281         else if Card.Value.FOUR.equalscard.getValue() ) ) {
282 
283             return "a four";
284         }
285         else if Card.Value.THREE.equalscard.getValue() ) ) {
286 
287             return "a three";
288         }
289         else if Card.Value.TWO.equalscard.getValue() ) ) {
290 
291             return "a two";
292         }
293         throw new IllegalArgumentException"unknown card type: "
294                                                             + card.toString() );
295     }
296 
297     private static
298     int interpretYesNoString input ) {
299 
300         input = input.trim().toLowerCase();
301 
302         if input.length() <= ) {
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.playGamethis, );
337 
338         return promptForAnotherGame();
339     }
340 
341 
342     private
343     void printDealerCardHand hand ) {
344 
345         System.out.println"For this hand the dealer is showing "
346                                 + formatCardhand.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 printDealerCardIfNeededHand hand ) {
356 
357         if !_hasDealerCardBeenPrinted ) {
358 
359             printDealerCardhand );
360             _hasDealerCardBeenPrinted = true;
361         }
362     }
363 
364 
365     private static
366     void printCardsList cards ) {
367 
368         Card card;
369         for int i = 0; i < cards.size(); i++ ) {
370 
371             card = Card cards.get);
372             
373             if i < cards.size() ) {
374 
375                 System.out.printformatCardcard ", " );
376             }
377             else if i == cards.size() ) {
378 
379                 System.out.printformatCardcard " " );
380             }
381             else {
382 
383                 System.out.print"and " + formatCardcard ) );
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 = interpretYesNoinput );
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].equalsinput ) ) return true;
425         }
426         return false;
427     }
428 
429 
430     private static
431     void promptForMoveHand 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.printprompt );
448     }
449 
450 
451     private static
452     String getInputFromUser()
453         throws IOException {
454 
455         byte[] input = new byte2048 ];
456         int length;
457         length = System.in.readinput );
458         return new Stringinput, 0, length - );
459     }
460 
461 
462     private static
463     Move getMoveFromUserHand hand )
464         throws IOException {
465 
466         String input;
467         Move response;
468 
469         while true ) {
470 
471             promptForMovehand );
472             input = getInputFromUser();
473             response = interpretMoveinput );
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.isMoveAllowedresponse ) ) {
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 interpretMoveString 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 = interpretYesNoinput );
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 setRulesRules rules ) {
569         _rules = rules;
570     }
571 
572 
573     private
574     TextPlayer() {}
575 
576     private static class Options {
577 
578         private OptionsString[] arguments ) {
579 
580             LongOpt[] longOpts = new LongOpt];
581             longOptsnew LongOpt"help",
582                                          LongOpt.NO_ARGUMENT,
583                                          null,
584                                          'h' );
585             longOptsnew 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() ) != -) {
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 '" (charvalue + "'" );
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 }