mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-21 23:00:43 -08:00
readln throws an exception when a file hits EOF, wheras readLine returns null. That puts us in an infinite loop if we hit EOF.
120 lines
4.3 KiB
Kotlin
120 lines
4.3 KiB
Kotlin
/**
|
|
* ANIMAL
|
|
*
|
|
*
|
|
* Converted from BASIC to Kotlin by John Long (@patimen)
|
|
*
|
|
* Animal is basically a perfect example of a binary tree. Implement it
|
|
* as such, with the QuestionNode either having an answer if it is a terminal node
|
|
* or a Question
|
|
*/
|
|
|
|
fun main() {
|
|
printIntro()
|
|
val rootQuestionNode =
|
|
QuestionOrAnswer(question = Question("DOES IT SWIM", QuestionOrAnswer("FISH"), QuestionOrAnswer("BIRD")))
|
|
while (true) {
|
|
val choice = ask("ARE YOU THINKING OF AN ANIMAL")
|
|
when {
|
|
choice == "LIST" -> printKnownAnimals(rootQuestionNode)
|
|
choice.startsWith("Q") -> return
|
|
choice.startsWith("Y") -> {
|
|
// A wrong answer means it's a new animal!
|
|
val wrongAnswer = rootQuestionNode.getWrongAnswer()
|
|
if (wrongAnswer == null) {
|
|
// The computer got the right answer!
|
|
println("WHY NOT TRY ANOTHER ANIMAL?")
|
|
} else {
|
|
// Get a new question to ask next time
|
|
wrongAnswer.askForInformationAndSave()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Takes care of asking a question (on the same line) and getting
|
|
// an answer or a blank string
|
|
fun ask(question: String): String {
|
|
print("$question? ")
|
|
return readln().uppercase() ?: ""
|
|
}
|
|
|
|
// Special case for a "yes or no" question, returns true of yes
|
|
fun askYesOrNo(question: String): Boolean {
|
|
return generateSequence {
|
|
print("$question? ")
|
|
readln()
|
|
}.firstNotNullOf { yesOrNo(it) }
|
|
}
|
|
|
|
// If neither Y (true) or N (false), return null, so the above sequence
|
|
// will just keep executing until it gets the answer
|
|
private fun yesOrNo(string: String): Boolean? =
|
|
when (string.uppercase().firstOrNull()) {
|
|
'Y' -> true
|
|
'N' -> false
|
|
else -> null
|
|
}
|
|
|
|
private fun printKnownAnimals(question: QuestionOrAnswer) {
|
|
println("\nANIMALS I ALREADY KNOW ARE:")
|
|
val animals = question.getAnswers().chunked(4)
|
|
animals.forEach { line ->
|
|
// The '*' in front of line.toTypedArray() "spreads" the array as a list of parameters instead
|
|
System.out.printf("%-15s".repeat(line.size), *line.toTypedArray())
|
|
println()
|
|
}
|
|
}
|
|
|
|
private fun printIntro() {
|
|
println(" ANIMAL")
|
|
println(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY")
|
|
println("\n\n")
|
|
println("PLAY 'GUESS THE ANIMAL'")
|
|
println("\n")
|
|
println("THINK OF AN ANIMAL AND THE COMPUTER WILL TRY TO GUESS IT.")
|
|
}
|
|
|
|
class QuestionOrAnswer(private var answer: String? = null, var question: Question? = null) {
|
|
fun getAnswers(): List<String> = answer?.let { listOf(it) } ?: question!!.getAnswers()
|
|
fun getWrongAnswer(): QuestionOrAnswer? {
|
|
if (answer != null) {
|
|
// "takeUnless" will return null if the answer is "yes". In this case
|
|
// we will return the "wrong answer", aka the terminal answer that was incorrect
|
|
return this.takeUnless { askYesOrNo("IS IT A $answer") }
|
|
}
|
|
return question?.getWrongAnswer()
|
|
}
|
|
|
|
fun askForInformationAndSave() {
|
|
//Failed to get it right and ran out of questions
|
|
//Let's ask the user for the new information
|
|
val newAnimal = ask("THE ANIMAL YOU WERE THINKING OF WAS A")
|
|
val newQuestion = ask("PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A \n$newAnimal FROM A $answer\n")
|
|
val newAnswer = askYesOrNo("FOR A $newAnimal THE ANSWER WOULD BE")
|
|
|
|
val trueAnswer = if (newAnswer) newAnimal else answer
|
|
val falseAnswer = if (newAnswer) answer else newAnimal
|
|
// Replace our answer with null and set the question with the data we just got
|
|
// This makes it a question instead of an answer
|
|
this.answer = null
|
|
this.question = Question(newQuestion, QuestionOrAnswer(trueAnswer), QuestionOrAnswer(falseAnswer))
|
|
}
|
|
}
|
|
|
|
class Question(
|
|
private val question: String,
|
|
private val trueAnswer: QuestionOrAnswer,
|
|
private val falseAnswer: QuestionOrAnswer
|
|
) {
|
|
fun getAnswers(): List<String> = trueAnswer.getAnswers() + falseAnswer.getAnswers()
|
|
|
|
fun getWrongAnswer(): QuestionOrAnswer? =
|
|
if (askYesOrNo(question)) {
|
|
trueAnswer.getWrongAnswer()
|
|
} else {
|
|
falseAnswer.getWrongAnswer()
|
|
}
|
|
}
|