mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-23 07:29:02 -08:00
Initial commit in new repo
This commit is contained in:
177
05 Bagels/java/BagelGame.java
Normal file
177
05 Bagels/java/BagelGame.java
Normal file
@@ -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<Integer> 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<Integer> digits = new ArrayList<Integer>(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<Character> uniqueDigits = new HashSet<Character>();
|
||||||
|
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<Integer> guess = new ArrayList<Integer>(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
129
05 Bagels/java/Bagels.java
Normal file
129
05 Bagels/java/Bagels.java
Normal file
@@ -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';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user