diff --git a/05 Bagels/java/BagelGame.java b/05 Bagels/java/BagelGame.java new file mode 100644 index 00000000..15dcf497 --- /dev/null +++ b/05 Bagels/java/BagelGame.java @@ -0,0 +1,177 @@ +/****************************************************************************** +* +* Encapsulates all the state and rules for one single game of Bagels +* +******************************************************************************/ + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Random; +import java.util.Set; + +public class BagelGame { + + public static final String CORRECT = "FERMI FERMI FERMI"; + public static final int MAX_GUESSES = 20; + + enum GameState { + RUNNING, + WON, + LOST + } + + private GameState state = GameState.RUNNING; + private List secretNum; + private int guessNum = 1; + + public BagelGame() { + // No-arg constructor for when you don't need to set the seed + this(new Random()); + } + + public BagelGame(long seed) { + // Setting the seed as a long value + this(new Random(seed)); + } + + public BagelGame(Random rand) { + // This is the "real" constructor, which expects an instance of + // Random to use for shuffling the digits of the secret number. + + // Since the digits cannot repeat in our "number", we can't just + // pick three random 0-9 integers. Instead, we'll treat it like + // a deck of ten cards, numbered 0-9. + List digits = new ArrayList(10); + // The 10 specified initial allocation, not actual size, + // which is why we add rather than set each element... + for (int i = 0; i < 10; i++) { + digits.add(i); + } + // Collections offers a handy-dandy shuffle method. Normally it + // uses a fresh Random class PRNG, but we're supplying our own + // to give us controll over whether or not we set the seed + Collections.shuffle(digits, rand); + + // Just take the first three digits + secretNum = digits.subList(0, 3); + } + + public boolean isOver() { + return state != GameState.RUNNING; + } + + public boolean isWon() { + return state == GameState.WON; + } + + public int getGuessNum() { + return guessNum; + } + + public String getSecretAsString() { + // Convert the secret number to a three-character string + String secretString = ""; + for (int n : secretNum) { + secretString += n; + } + return secretString; + } + + @Override + public String toString() { + // Quick report of game state for debugging purposes + String s = "Game is " + state + "\n"; + s += "Current Guess Number: " + guessNum + "\n"; + s += "Secret Number: " + secretNum; + return s; + } + + public String validateGuess(String guess) { + // Checks the passed string and returns null if it's a valid guess + // (i.e., exactly three numeric characters) + // If not valid, returns an "error" string to display to user. + String error = ""; + + if (guess.length() == 3) { + // Correct length. Are all the characters numbers? + try { + Integer.parseInt(guess); + } catch (NumberFormatException ex) { + error = "What?"; + } + if (error == "") { + // Check for unique digits by placing each character in a set + Set uniqueDigits = new HashSet(); + for (int i = 0; i < guess.length(); i++){ + uniqueDigits.add(guess.charAt(i)); + } + if (uniqueDigits.size() != guess.length()) { + error = "Oh, I forgot to tell you that the number I have in mind\n"; + error += "has no two digits the same."; + } + } + } else { + error = "Try guessing a three-digit number."; + } + + return error; + } + + public String makeGuess(String s) throws IllegalArgumentException { + // Processes the passed guess string (which, ideally, should be + // validated by previously calling validateGuess) + // Return a response string (PICO, FERMI, etc.) if valid + // Also sets game state accordingly (sets win state or increments + // number of guesses) + + // Convert string to integer list, just to keep things civil + List guess = new ArrayList(3); + for (int i = 0; i < 3; i++) { + guess.add((int)s.charAt(i) - 48); + } + + // Build response string... + String response = ""; + // Correct digit, but in wrong place? + for (int i = 0; i < 2; i++) { + if (secretNum.get(i) == guess.get(i+1)) { + response += "PICO "; + } + if (secretNum.get(i+1) == guess.get(i)) { + response += "PICO "; + } + } + if (secretNum.get(0) == guess.get(2)) { + response += "PICO "; + } + if (secretNum.get(2) == guess.get(0)) { + response += "PICO "; + } + // Correct digits in right place? + for (int i = 0; i < 3; i++) { + if (secretNum.get(i) == guess.get(i)) { + response += "FERMI "; + } + } + // Nothin' right? + if (response == "") { + response = "BAGELS"; + } + // Get rid of any space that might now be at the end + response = response.trim(); + // If correct, change state + if (response.equals(CORRECT)) { + state = GameState.WON; + } else { + // If not, increment guess counter and check for game over + guessNum++; + if (guessNum > MAX_GUESSES) { + state = GameState.LOST; + } + } + return response; + } + +} \ No newline at end of file diff --git a/05 Bagels/java/Bagels.java b/05 Bagels/java/Bagels.java new file mode 100644 index 00000000..c5c0d71c --- /dev/null +++ b/05 Bagels/java/Bagels.java @@ -0,0 +1,129 @@ +/****************************************************************************** +* +* Bagels +* +* From: BASIC Computer Games (1978) +* Edited by David H. Ahl +* +* "In this game, the computer picks a 3-digit secret number using +* the digits 0 to 9 and you attempt to guess what it is. You are +* allowed up to twenty guesses. No digit is repeated. After +* each guess the computer will give you clues about your guess +* as follows: +* +* PICO One digit is correct, but in the wrong place +* FERMI One digit is in the correct place +* BAGELS No digit is correct +* +* "You will learn to draw inferences from the clues and, with +* practice, you'll learn to improve your score. There are several +* good strategies for playing Bagels. After you have found a good +* strategy, see if you can improve it. Or try a different strategy +* altogether and see if it is any better. While the program allows +* up to twenty guesses, if you use a good strategy it should not +* take more than eight guesses to get any number. +* +* "The original authors of this program are D. Resek and P. Rowe of +* the Lawrence Hall of Science, Berkeley, California." +* +* Java port by Jeff Jetton, 2020, based on an earlier Python port +* +******************************************************************************/ + +import java.util.Scanner; + +public class Bagels { + + public static void main(String[] args) { + + int gamesWon = 0; + + // Intro text + System.out.println("\n\n Bagels"); + System.out.println("Creative Computing Morristown, New Jersey"); + System.out.println("\n\n"); + System.out.print("Would you like the rules (Yes or No)? "); + + // Need instructions? + Scanner scan = new Scanner(System.in); + String s = scan.nextLine(); + if (s.length() == 0 || s.toUpperCase().charAt(0) != 'N') { + System.out.println(); + BagelInstructions.printInstructions(); + } + + // Loop for playing multiple games + boolean stillPlaying = true; + while(stillPlaying) { + + // Set up a new game + BagelGame game = new BagelGame(); + System.out.println("\nO.K. I have a number in mind."); + + // Loop guess and responsses until game is over + while (!game.isOver()) { + String guess = getValidGuess(game); + String response = game.makeGuess(guess); + // Don't print a response if the game is won + if (!game.isWon()) { + System.out.println(response); + } + } + + // Game is over. But did we win or lose? + if (game.isWon()) { + System.out.println("You got it!!!\n"); + gamesWon++; + } else { + System.out.println("Oh well"); + System.out.print("That's " + BagelGame.MAX_GUESSES + " guesses. "); + System.out.println("My number was " + game.getSecretAsString()); + } + + stillPlaying = getReplayResponse(); + } + + // Print goodbye message + if (gamesWon > 0) { + System.out.println("\nA " + gamesWon + " point Bagels buff!!"); + } + System.out.println("Hope you had fun. Bye.\n"); + } + + private static String getValidGuess(BagelGame game) { + // Keep asking for a guess until valid + Scanner scan = new Scanner(System.in); + boolean valid = false; + String guess = ""; + String error; + + while (!valid) { + System.out.print("Guess # " + game.getGuessNum() + " ? "); + guess = scan.nextLine().trim(); + error = game.validateGuess(guess); + if (error == "") { + valid = true; + } else { + System.out.println(error); + } + } + return guess; + } + + private static boolean getReplayResponse() { + // keep asking for response until valid + Scanner scan = new Scanner(System.in); + // Keep looping until a non-zero-length string is entered + while (true) { + System.out.print("Play again (Yes or No)? "); + String response = scan.nextLine().trim(); + if (response.length() > 0) { + return response.toUpperCase().charAt(0) == 'Y'; + } + } + } + +} + + +