mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-22 15:16:33 -08:00
@@ -2,159 +2,244 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Scanner;
|
import java.util.Scanner;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ANIMAL
|
* ANIMAL
|
||||||
* <p>
|
* <p>
|
||||||
* Converted from BASIC to Java by Aldrin Misquitta (@aldrinm)
|
* Converted from BASIC to Java by Aldrin Misquitta (@aldrinm)
|
||||||
|
* The original BASIC program uses an array to maintain the questions and answers and to decide which question to
|
||||||
|
* ask next. Updated this Java implementation to use a tree instead of the earlier faulty one based on a list (thanks @patimen).
|
||||||
*/
|
*/
|
||||||
public class Animal {
|
public class Animal {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
printIntro();
|
printIntro();
|
||||||
Scanner scan = new Scanner(System.in);
|
Scanner scan = new Scanner(System.in);
|
||||||
|
|
||||||
List<Question> questions = new ArrayList<>();
|
Node root = new QuestionNode("DOES IT SWIM",
|
||||||
questions.add(new Question("DOES IT SWIM", "FISH", "BIRD"));
|
new AnimalNode("FISH"), new AnimalNode("BIRD"));
|
||||||
|
|
||||||
boolean stopGame = false;
|
boolean stopGame = false;
|
||||||
while (!stopGame) {
|
while (!stopGame) {
|
||||||
String choice = readMainChoice(scan);
|
String choice = readMainChoice(scan);
|
||||||
switch (choice) {
|
switch (choice) {
|
||||||
case "LIST":
|
case "TREE":
|
||||||
printKnownAnimals(questions);
|
printTree(root);
|
||||||
break;
|
break;
|
||||||
case "Q":
|
case "LIST":
|
||||||
case "QUIT":
|
printKnownAnimals(root);
|
||||||
stopGame = true;
|
break;
|
||||||
break;
|
case "Q":
|
||||||
default:
|
case "QUIT":
|
||||||
if (choice.toUpperCase(Locale.ROOT).startsWith("Y")) {
|
stopGame = true;
|
||||||
int k = 0;
|
break;
|
||||||
boolean correctGuess = false;
|
default:
|
||||||
while (questions.size() > k && !correctGuess) {
|
if (choice.toUpperCase(Locale.ROOT).startsWith("Y")) {
|
||||||
Question question = questions.get(k);
|
Node current = root; //where we are in the question tree
|
||||||
correctGuess = askQuestion(question, scan);
|
Node previous; //keep track of parent of current in order to place new questions later on.
|
||||||
if (correctGuess) {
|
|
||||||
System.out.println("WHY NOT TRY ANOTHER ANIMAL?");
|
|
||||||
} else {
|
|
||||||
k++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!correctGuess) {
|
while (current instanceof QuestionNode) {
|
||||||
askForInformationAndSave(scan, questions);
|
var currentQuestion = (QuestionNode) current;
|
||||||
}
|
var reply = askQuestionAndGetReply(currentQuestion, scan);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
previous = current;
|
||||||
|
current = reply ? currentQuestion.getTrueAnswer() : currentQuestion.getFalseAnswer();
|
||||||
|
if (current instanceof AnimalNode) {
|
||||||
|
//We have reached a animal node, so offer it as the guess
|
||||||
|
var currentAnimal = (AnimalNode) current;
|
||||||
|
System.out.printf("IS IT A %s ? ", currentAnimal.getAnimal());
|
||||||
|
var animalGuessResponse = readYesOrNo(scan);
|
||||||
|
if (animalGuessResponse) {
|
||||||
|
//we guessed right! end this round
|
||||||
|
System.out.println("WHY NOT TRY ANOTHER ANIMAL?");
|
||||||
|
} else {
|
||||||
|
//we guessed wrong :(, ask for feedback
|
||||||
|
//cast previous to QuestionNode since we know at this point that it is not a leaf node
|
||||||
|
askForInformationAndSave(scan, currentAnimal, (QuestionNode) previous, reply);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void askForInformationAndSave(Scanner scan, List<Question> questions) {
|
/**
|
||||||
//Failed to get it right and ran out of questions
|
* Prompt for information about the animal we got wrong
|
||||||
//Let's ask the user for the new information
|
* @param current The animal that we guessed wrong
|
||||||
System.out.print("THE ANIMAL YOU WERE THINKING OF WAS A ");
|
* @param previous The root of current
|
||||||
String animal = scan.nextLine();
|
* @param previousToCurrentDecisionChoice Whether it was a Y or N answer that got us here. true = Y, false = N
|
||||||
System.out.printf("PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A %s FROM A %s ", animal, questions.get(
|
*/
|
||||||
questions.size() - 1).falseAnswer);
|
private static void askForInformationAndSave(Scanner scan, AnimalNode current, QuestionNode previous, boolean previousToCurrentDecisionChoice) {
|
||||||
String newQuestion = scan.nextLine();
|
//Failed to get it right and ran out of questions
|
||||||
System.out.printf("FOR A %s THE ANSWER WOULD BE ", animal);
|
//Let's ask the user for the new information
|
||||||
boolean newAnswer = readYesOrNo(scan);
|
System.out.print("THE ANIMAL YOU WERE THINKING OF WAS A ");
|
||||||
//Add it to our list
|
String animal = scan.nextLine();
|
||||||
addNewAnimal(questions, animal, newQuestion, newAnswer);
|
System.out.printf("PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A %s FROM A %s ", animal, current.getAnimal());
|
||||||
}
|
String newQuestion = scan.nextLine();
|
||||||
|
System.out.printf("FOR A %s THE ANSWER WOULD BE ", animal);
|
||||||
|
boolean newAnswer = readYesOrNo(scan);
|
||||||
|
//Add it to our question store
|
||||||
|
addNewAnimal(current, previous, animal, newQuestion, newAnswer, previousToCurrentDecisionChoice);
|
||||||
|
}
|
||||||
|
|
||||||
private static void addNewAnimal(List<Question> questions, String animal, String newQuestion, boolean newAnswer) {
|
private static void addNewAnimal(Node current,
|
||||||
Question lastQuestion = questions.get(questions.size() - 1);
|
QuestionNode previous,
|
||||||
String lastAnimal = lastQuestion.falseAnswer;
|
String animal,
|
||||||
lastQuestion.falseAnswer = null; //remove the false option to indicate that there is a next question
|
String newQuestion,
|
||||||
|
boolean newAnswer,
|
||||||
|
boolean previousToCurrentDecisionChoice) {
|
||||||
|
var animalNode = new AnimalNode(animal);
|
||||||
|
var questionNode = new QuestionNode(newQuestion,
|
||||||
|
newAnswer ? animalNode : current,
|
||||||
|
!newAnswer ? animalNode : current);
|
||||||
|
|
||||||
Question newOption;
|
if (previous != null) {
|
||||||
if (newAnswer) {
|
if (previousToCurrentDecisionChoice) {
|
||||||
newOption = new Question(newQuestion, animal, lastAnimal);
|
previous.setTrueAnswer(questionNode);
|
||||||
} else {
|
} else {
|
||||||
newOption = new Question(newQuestion, lastAnimal, animal);
|
previous.setFalseAnswer(questionNode);
|
||||||
}
|
}
|
||||||
questions.add(newOption);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean askQuestion(Question question, Scanner scanner) {
|
private static boolean askQuestionAndGetReply(QuestionNode questionNode, Scanner scanner) {
|
||||||
System.out.printf("%s ? ", question.question);
|
System.out.printf("%s ? ", questionNode.question);
|
||||||
|
return readYesOrNo(scanner);
|
||||||
|
}
|
||||||
|
|
||||||
boolean chosenAnswer = readYesOrNo(scanner);
|
private static boolean readYesOrNo(Scanner scanner) {
|
||||||
if (chosenAnswer) {
|
boolean validAnswer = false;
|
||||||
if (question.trueAnswer != null) {
|
Boolean choseAnswer = null;
|
||||||
System.out.printf("IS IT A %s ? ", question.trueAnswer);
|
while (!validAnswer) {
|
||||||
return readYesOrNo(scanner);
|
String answer = scanner.nextLine();
|
||||||
}
|
if (answer.toUpperCase(Locale.ROOT).startsWith("Y")) {
|
||||||
//else go to the next question
|
validAnswer = true;
|
||||||
} else {
|
choseAnswer = true;
|
||||||
if (question.falseAnswer != null) {
|
} else if (answer.toUpperCase(Locale.ROOT).startsWith("N")) {
|
||||||
System.out.printf("IS IT A %s ? ", question.falseAnswer);
|
validAnswer = true;
|
||||||
return readYesOrNo(scanner);
|
choseAnswer = false;
|
||||||
}
|
}
|
||||||
//else go to the next question
|
}
|
||||||
}
|
return choseAnswer;
|
||||||
return false;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean readYesOrNo(Scanner scanner) {
|
private static void printKnownAnimals(Node root) {
|
||||||
boolean validAnswer = false;
|
System.out.println("\nANIMALS I ALREADY KNOW ARE:");
|
||||||
Boolean choseAnswer = null;
|
|
||||||
while (!validAnswer) {
|
|
||||||
String answer = scanner.nextLine();
|
|
||||||
if (answer.toUpperCase(Locale.ROOT).startsWith("Y")) {
|
|
||||||
validAnswer = true;
|
|
||||||
choseAnswer = true;
|
|
||||||
} else if (answer.toUpperCase(Locale.ROOT).startsWith("N")) {
|
|
||||||
validAnswer = true;
|
|
||||||
choseAnswer = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return choseAnswer;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void printKnownAnimals(List<Question> questions) {
|
List<AnimalNode> leafNodes = collectLeafNodes(root);
|
||||||
System.out.println("\nANIMALS I ALREADY KNOW ARE:");
|
String allAnimalsString = leafNodes.stream().map(AnimalNode::getAnimal).collect(Collectors.joining("\t\t"));
|
||||||
List<String> animals = new ArrayList<>();
|
|
||||||
questions.forEach(q -> {
|
|
||||||
if (q.trueAnswer != null) {
|
|
||||||
animals.add(q.trueAnswer);
|
|
||||||
}
|
|
||||||
if (q.falseAnswer != null) {
|
|
||||||
animals.add(q.falseAnswer);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
System.out.println(String.join("\t\t", animals));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String readMainChoice(Scanner scan) {
|
System.out.println(allAnimalsString);
|
||||||
System.out.print("ARE YOU THINKING OF AN ANIMAL ? ");
|
}
|
||||||
return scan.nextLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void printIntro() {
|
//Traverse the tree and collect all the leaf nodes, which basically have all the animals.
|
||||||
System.out.println(" ANIMAL");
|
private static List<AnimalNode> collectLeafNodes(Node root) {
|
||||||
System.out.println(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
|
List<AnimalNode> collectedNodes = new ArrayList<>();
|
||||||
System.out.println("\n\n");
|
if (root instanceof AnimalNode) {
|
||||||
System.out.println("PLAY 'GUESS THE ANIMAL'");
|
collectedNodes.add((AnimalNode) root);
|
||||||
System.out.println("\n");
|
} else {
|
||||||
System.out.println("THINK OF AN ANIMAL AND THE COMPUTER WILL TRY TO GUESS IT.");
|
var q = (QuestionNode) root;
|
||||||
}
|
collectedNodes.addAll(collectLeafNodes(q.getTrueAnswer()));
|
||||||
|
collectedNodes.addAll(collectLeafNodes(q.getFalseAnswer()));
|
||||||
|
}
|
||||||
|
return collectedNodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String readMainChoice(Scanner scan) {
|
||||||
|
System.out.print("ARE YOU THINKING OF AN ANIMAL ? ");
|
||||||
|
return scan.nextLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void printIntro() {
|
||||||
|
System.out.println(" ANIMAL");
|
||||||
|
System.out.println(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
|
||||||
|
System.out.println("\n\n");
|
||||||
|
System.out.println("PLAY 'GUESS THE ANIMAL'");
|
||||||
|
System.out.println("\n");
|
||||||
|
System.out.println("THINK OF AN ANIMAL AND THE COMPUTER WILL TRY TO GUESS IT.");
|
||||||
|
}
|
||||||
|
|
||||||
|
//Based on https://stackoverflow.com/a/8948691/74057
|
||||||
|
private static void printTree(Node root) {
|
||||||
|
StringBuilder buffer = new StringBuilder(50);
|
||||||
|
print(root, buffer, "", "");
|
||||||
|
System.out.println(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void print(Node root, StringBuilder buffer, String prefix, String childrenPrefix) {
|
||||||
|
buffer.append(prefix);
|
||||||
|
buffer.append(root.toString());
|
||||||
|
buffer.append('\n');
|
||||||
|
|
||||||
|
if (root instanceof QuestionNode) {
|
||||||
|
var questionNode = (QuestionNode) root;
|
||||||
|
print(questionNode.getTrueAnswer(), buffer, childrenPrefix + "├─Y─ ", childrenPrefix + "│ ");
|
||||||
|
print(questionNode.getFalseAnswer(), buffer, childrenPrefix + "└─N─ ", childrenPrefix + " ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static class Question {
|
/**
|
||||||
String question;
|
* Base interface for all nodes in our question tree
|
||||||
String trueAnswer;
|
*/
|
||||||
String falseAnswer;
|
private interface Node {
|
||||||
|
}
|
||||||
|
|
||||||
public Question(String question, String trueAnswer, String falseAnswer) {
|
private static class QuestionNode implements Node {
|
||||||
this.question = question;
|
private final String question;
|
||||||
this.trueAnswer = trueAnswer;
|
private Node trueAnswer;
|
||||||
this.falseAnswer = falseAnswer;
|
private Node falseAnswer;
|
||||||
}
|
|
||||||
}
|
public QuestionNode(String question, Node trueAnswer, Node falseAnswer) {
|
||||||
|
this.question = question;
|
||||||
|
this.trueAnswer = trueAnswer;
|
||||||
|
this.falseAnswer = falseAnswer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getQuestion() {
|
||||||
|
return question;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node getTrueAnswer() {
|
||||||
|
return trueAnswer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTrueAnswer(Node trueAnswer) {
|
||||||
|
this.trueAnswer = trueAnswer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Node getFalseAnswer() {
|
||||||
|
return falseAnswer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFalseAnswer(Node falseAnswer) {
|
||||||
|
this.falseAnswer = falseAnswer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Question{'" + question + "'}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class AnimalNode implements Node {
|
||||||
|
private final String animal;
|
||||||
|
|
||||||
|
public AnimalNode(String animal) {
|
||||||
|
this.animal = animal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAnimal() {
|
||||||
|
return animal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Animal{'" + animal + "'}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user