From 68e278095d8168d821eb91fafa8a714240a7708c Mon Sep 17 00:00:00 2001 From: Joseph Nellis Date: Sat, 8 Jan 2022 20:24:12 -0800 Subject: [PATCH] Add files via upload --- 01_Acey_Ducey/java/src/AceyDucey17.java | 196 ++++++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 01_Acey_Ducey/java/src/AceyDucey17.java diff --git a/01_Acey_Ducey/java/src/AceyDucey17.java b/01_Acey_Ducey/java/src/AceyDucey17.java new file mode 100644 index 00000000..9b229f0b --- /dev/null +++ b/01_Acey_Ducey/java/src/AceyDucey17.java @@ -0,0 +1,196 @@ +import java.util.Random; +import java.util.Scanner; + +/** + * A modern version (JDK17) of ACEY DUCEY using post Java 8 features. Notes + * regarding new java features or differences in the original basic + * implementation are numbered and at the bottom of this code. + * The goal is to recreate the exact look and feel of the original program + * minus a large glaring bug in the original code that lets you cheat. + */ +public class AceyDucey17 { + + public static void main(String[] args) { + // notes [1] + System.out.println(""" + ACEY DUCEY CARD GAME + CREATIVE COMPUTING MORRISTOWN, NEW JERSEY + + + ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER + THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP + YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING + ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE + A VALUE BETWEEN THE FIRST TWO. + IF YOU DO NOT WANT TO BET, INPUT A 0"""); + + do { + playGame(); + } while (stillInterested()); + System.out.println("O.K., HOPE YOU HAD FUN!"); + } + + public static void playGame() { + int cashOnHand = 100; // our only mutable variable note [11] + System.out.println("YOU NOW HAVE "+ cashOnHand +" DOLLARS.");// note [6] + while (cashOnHand > 0) { + System.out.println(); + System.out.println("HERE ARE YOUR NEXT TWO CARDS:"); + + final Card lowCard = Card.getRandomCard(2, Card.KING); //note [3] + System.out.println(lowCard); + final Card highCard = Card.getRandomCard(lowCard.rank() + 1, Card.ACE); + System.out.println(highCard); + + final int bet = getBet(cashOnHand); + final int winnings = determineWinnings(lowCard,highCard,bet); + cashOnHand += winnings; + if(winnings != 0){ //note [2] + System.out.println("YOU NOW HAVE "+ cashOnHand +" DOLLARS.");//note [6] + } + } + } + + public static int determineWinnings(Card lowCard, Card highCard, int bet){ + if (bet <= 0) { // note [5] + System.out.println("CHICKEN!!"); + return 0; + } + Card nextCard = Card.getRandomCard(2, Card.ACE); + System.out.println(nextCard); + if(nextCard.between(lowCard,highCard)){ + System.out.println("YOU WIN!!!"); + return bet; + } + System.out.println("SORRY, YOU LOSE"); + return -bet; + } + + public static boolean stillInterested(){ + System.out.println(); + System.out.println(); + System.out.println("SORRY, FRIEND, BUT YOU BLEW YOUR WAD."); + System.out.println(); + System.out.println(); + System.out.println("TRY AGAIN (YES OR NO)"); + Scanner input = new Scanner(System.in); + return input.nextLine() + .toUpperCase() + .startsWith("Y"); // note [9] + } + + public static int getBet(int cashOnHand){ + int bet; + do{ + System.out.println(); + System.out.print("WHAT IS YOUR BET? "); + bet = inputNumber(); + if (bet > cashOnHand) { + System.out.println("SORRY, MY FRIEND, BUT YOU BET TOO MUCH."); + System.out.println("YOU HAVE ONLY "+cashOnHand+" DOLLARS TO BET."); + } + }while(bet > cashOnHand); + return bet; + } + + public static int inputNumber() { + final Scanner input = new Scanner(System.in); + // set to negative to mark as not entered yet in case of input error. + int number = -1; + while (number < 0) { + try { + number = input.nextInt(); + } catch(Exception ex) { // note [7] + System.out.println("!NUMBER EXPECTED - RETRY INPUT LINE"); + System.out.print("? "); + try{ + input.nextLine(); + } + catch(Exception ns_ex){ // received EOF (ctrl-d or ctrl-z if windows) + System.out.println("END OF INPUT, STOPPING PROGRAM."); + System.exit(1); + } + } + } + return number; + } + + record Card(int rank){ + // Some constants to describe face cards. + public static final int JACK = 11, QUEEN = 12, KING = 13, ACE = 14; + private static final Random random = new Random(); + + public static Card getRandomCard(int from, int to){ + return new Card(random.nextInt(from, to+1)); // note [4] + } + + public boolean between(Card lower, Card higher){ + return lower.rank() < this.rank() && this.rank() < higher.rank(); + } + + @Override + public String toString() { // note [13] + return switch (rank) { + case JACK -> "JACK"; + case QUEEN -> "QUEEN"; + case KING -> "KING"; + case ACE -> "ACE\n"; // note [10] + default -> " "+rank+" "; // note [6] + }; + } + } + + /* + Notes: + 1. Multiline strings, a.k.a. text blocks, were added in JDK15. + 2. The original game only displays the players balance if it changed, + which it does not when the player chickens out and bets zero. + 3. To pick two cards to show, the original BASIC implementation has a + bug that could cause a race condition if the RND function never chose + a lower number first and higher number second. It loops infinitely + re-choosing random numbers until the condition is met of the first + one being lower. The logic is changed a bit here so that the first + card picked is anything but an ACE, the highest possible card, + and then the second card is between the just picked first card upto + and including the ACE. + 4. Random.nextInt(origin, bound) was added in JDK17, and allows to + directly pick a range for a random integer to be generated. The second + parameter is exclusive of the range and thus why they are stated with + +1's to the face card. + 5. The original BASIC implementation has a bug that allows negative value + bets. Since you can't bet MORE cash than you have you can always bet + less including a very, very large negative value. You would do this when + the chances of winning are slim or zero since losing a hand SUBTRACTS + your bet from your cash; subtracting a negative number actually ADDS + to your cash, potentially making you an instant billionaire. + This loophole is now closed. + 6. The subtle behavior of the BASIC PRINT command causes a space to be + printed before all positive numbers as well as a trailing space. Any + place a non-face card or the players balance is printed has extra space + to mimic this behavior. + 7. Errors on input were probably specific to the interpreter. This program + tries to match the Vintage Basic interpreter's error messages. The final + input.nextLine() command exists to clear the blockage of whatever + non-number input was entered. But even that could fail if the user + types Ctrl-D (windows Ctrl-Z), signifying an EOF (end of file) and thus + the closing of STDIN channel. The original program on an EOF signal prints + "END OF INPUT IN LINE 660" and thus we cover it roughly the same way. + All of this is necessary to avoid a messy stack trace from being + printed as the program crashes. + 9. The original game only accepted a full upper case "YES" to continue + playing if bankrupted. This program is more lenient and will accept + any input that starts with the letter 'y', uppercase or not. + 10. The original game prints an extra blank line if the card is an ACE. There + is seemingly no rationale for this. + 11. Modern java best practices are edging toward a more functional paradigm + and as such, mutating state is discouraged. All other variables besides + the cashOnHand are final and initialized only once. + 12. Refactoring of the concept of a card is done with a record. Records were + introduced in JDK14. Card functionality is encapsulated in this example + of a record. An enum could be a better alternative since there are + technically only 13 cards possible. + 13. Switch expressions were introduced as far back as JDK12 but continue to + be refined for clarity, exhaustiveness. As of JDK17 pattern matching + for switch expressions can be accessed by enabling preview features. + */ +}