mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-22 07:10:42 -08:00
@@ -1,4 +1,4 @@
|
|||||||
### King
|
## King
|
||||||
|
|
||||||
This is one of the most comprehensive, difficult, and interesting games. (If you’ve never played one of these games, start with HAMMURABI.)
|
This is one of the most comprehensive, difficult, and interesting games. (If you’ve never played one of these games, start with HAMMURABI.)
|
||||||
|
|
||||||
@@ -8,7 +8,9 @@ The money system is Rollods; each person needs 100 Rallods per year to survive.
|
|||||||
|
|
||||||
The author of this program is James A. Storer who wrote it while a student at Lexington High School.
|
The author of this program is James A. Storer who wrote it while a student at Lexington High School.
|
||||||
|
|
||||||
## Bugs
|
⚠️ This game includes references to suicide or self-harm.
|
||||||
|
|
||||||
|
### Bugs
|
||||||
|
|
||||||
Implementers should be aware that this game contains at least one bug.
|
Implementers should be aware that this game contains at least one bug.
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ import kotlin.random.Random
|
|||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
lateinit var gameState: GameState
|
lateinit var gameState: GameState
|
||||||
const val INCLUDE_BUGS_FROM_ORIGINAL = false
|
const val KEEP_ORIGINAL_BUGS = false
|
||||||
|
const val KEEP_ORIGINAL_SUICIDE_REFERENCE = false
|
||||||
|
|
||||||
val rnd: Double get() = Random.nextDouble()
|
val rnd: Double get() = Random.nextDouble()
|
||||||
fun tab(i: Int) = " ".repeat(i)
|
fun tab(i: Int) = " ".repeat(i)
|
||||||
@@ -19,29 +20,25 @@ fun main() {
|
|||||||
}
|
}
|
||||||
?: throw EndOfInputException()
|
?: throw EndOfInputException()
|
||||||
|
|
||||||
try {
|
with(gameState) {
|
||||||
with(gameState) {
|
do {
|
||||||
while(currentYear < yearsRequired) {
|
recalculateLandCost()
|
||||||
recalculateLandCost()
|
displayStatus()
|
||||||
displayStatus()
|
inputLandSale()
|
||||||
inputLandSale()
|
performLandSale()
|
||||||
performLandSale()
|
inputWelfare()
|
||||||
inputWelfare()
|
performWelfare()
|
||||||
performWelfare()
|
inputPlantingArea()
|
||||||
inputPlantingArea()
|
performPlanting()
|
||||||
performPlanting()
|
inputPollutionControl()
|
||||||
inputPollutionControl()
|
if (zeroInput()) {
|
||||||
if (zeroInput()) {
|
displayExitMessage()
|
||||||
displayExitMessage()
|
exitProcess(0)
|
||||||
exitProcess(0)
|
|
||||||
}
|
|
||||||
simulateOneYear()
|
|
||||||
currentYear ++
|
|
||||||
}
|
}
|
||||||
}
|
val yearResult = simulateOneYear().also {
|
||||||
win(gameState.yearsRequired)
|
it.displayConsequences()
|
||||||
} catch (e: GameEndingException) {
|
}
|
||||||
e.displayConsequences()
|
} while (yearResult == YearOutcome.ContinueNextYear)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +51,8 @@ private fun header() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun instructions(yearsRequired: Int) {
|
fun instructions(yearsRequired: Int) {
|
||||||
println("""
|
println(
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
CONGRATULATIONS! YOU'VE JUST BEEN ELECTED PREMIER OF SETATS
|
CONGRATULATIONS! YOU'VE JUST BEEN ELECTED PREMIER OF SETATS
|
||||||
@@ -80,10 +78,7 @@ fun loadOldGame(): GameState = GameState().apply {
|
|||||||
do {
|
do {
|
||||||
var retry = false
|
var retry = false
|
||||||
print("HOW MANY YEARS HAD YOU BEEN IN OFFICE WHEN INTERRUPTED? ")
|
print("HOW MANY YEARS HAD YOU BEEN IN OFFICE WHEN INTERRUPTED? ")
|
||||||
currentYear = numberInput()
|
currentYear = validatedInput { it > 0 }
|
||||||
|
|
||||||
if (currentYear <= 0)
|
|
||||||
throw GameEndingException.DataEntryValidation()
|
|
||||||
|
|
||||||
if (currentYear >= yearsRequired) {
|
if (currentYear >= yearsRequired) {
|
||||||
println(" COME ON, YOUR TERM IN OFFICE IS ONLY $yearsRequired YEARS.")
|
println(" COME ON, YOUR TERM IN OFFICE IS ONLY $yearsRequired YEARS.")
|
||||||
@@ -92,21 +87,19 @@ fun loadOldGame(): GameState = GameState().apply {
|
|||||||
} while (retry)
|
} while (retry)
|
||||||
|
|
||||||
print("HOW MUCH DID YOU HAVE IN THE TREASURY? ")
|
print("HOW MUCH DID YOU HAVE IN THE TREASURY? ")
|
||||||
rallods = numberInput()
|
rallods = validatedInput { it >= 0 }
|
||||||
if (rallods < 0)
|
|
||||||
throw GameEndingException.DataEntryValidation()
|
print("HOW MANY COUNTRYMEN? ")
|
||||||
|
countrymen = validatedInput { it >= 0 }
|
||||||
|
|
||||||
print("HOW MANY WORKERS? ")
|
print("HOW MANY WORKERS? ")
|
||||||
foreignWorkers = numberInput()
|
foreignWorkers = validatedInput { it >= 0 }
|
||||||
if (foreignWorkers < 0)
|
|
||||||
throw GameEndingException.DataEntryValidation()
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
var retry = false
|
var retry = false
|
||||||
print("HOW MANY SQUARE MILES OF LAND? ")
|
print("HOW MANY SQUARE MILES OF LAND? ")
|
||||||
landArea = numberInput()
|
landArea = validatedInput { it >= 0 }
|
||||||
if (landArea<0)
|
|
||||||
throw GameEndingException.DataEntryValidation()
|
|
||||||
if (landArea > 2000 || landArea <= 1000) {
|
if (landArea > 2000 || landArea <= 1000) {
|
||||||
println(" COME ON, YOU STARTED WITH 1000 SQ. MILES OF FARM LAND")
|
println(" COME ON, YOU STARTED WITH 1000 SQ. MILES OF FARM LAND")
|
||||||
println(" AND 10,000 SQ. MILES OF FOREST LAND.")
|
println(" AND 10,000 SQ. MILES OF FOREST LAND.")
|
||||||
@@ -118,11 +111,13 @@ fun loadOldGame(): GameState = GameState().apply {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All exceptions which indicate the premature ending of the game, due
|
* Possible outcomes for a year.
|
||||||
* to mismanagement, starvation, revolution, or mis-entry of a game state.
|
|
||||||
*/
|
*/
|
||||||
sealed class GameEndingException : Throwable() {
|
sealed class YearOutcome {
|
||||||
abstract fun displayConsequences()
|
|
||||||
|
open fun displayConsequences() {
|
||||||
|
// Default display nothing
|
||||||
|
}
|
||||||
|
|
||||||
fun finalFate() {
|
fun finalFate() {
|
||||||
if (rnd < .5) {
|
if (rnd < .5) {
|
||||||
@@ -135,7 +130,28 @@ sealed class GameEndingException : Throwable() {
|
|||||||
println()
|
println()
|
||||||
}
|
}
|
||||||
|
|
||||||
class ExtremeMismanagement(private val death: Int) : GameEndingException() {
|
object ContinueNextYear : YearOutcome()
|
||||||
|
|
||||||
|
class Win(val yearsRequired: Int) : YearOutcome() {
|
||||||
|
override fun displayConsequences() {
|
||||||
|
// The misspelling of "successfully" is in the original code.
|
||||||
|
println(
|
||||||
|
"""
|
||||||
|
|
||||||
|
CONGRATULATIONS!!!!!!!!!!!!!!!!!!
|
||||||
|
YOU HAVE SUCCESFULLY COMPLETED YOUR $yearsRequired YEAR TERM
|
||||||
|
OF OFFICE. YOU WERE, OF COURSE, EXTREMELY LUCKY, BUT
|
||||||
|
NEVERTHELESS, IT'S QUITE AN ACHIEVEMENT. GOODBYE AND GOOD
|
||||||
|
LUCK - YOU'LL PROBABLY NEED IT IF YOU'RE THE TYPE THAT
|
||||||
|
PLAYS THIS GAME.
|
||||||
|
|
||||||
|
|
||||||
|
""".trimIndent()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ExtremeMismanagement(private val death: Int) : YearOutcome() {
|
||||||
override fun displayConsequences() {
|
override fun displayConsequences() {
|
||||||
println()
|
println()
|
||||||
println("$death COUNTRYMEN DIED IN ONE YEAR!!!!!")
|
println("$death COUNTRYMEN DIED IN ONE YEAR!!!!!")
|
||||||
@@ -151,74 +167,68 @@ sealed class GameEndingException : Throwable() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TooManyPeopleDead : GameEndingException() {
|
object TooManyPeopleDead : YearOutcome() {
|
||||||
// The mistyping of "population" is in the original game.
|
// The mistyping of "population" is in the original game.
|
||||||
override fun displayConsequences() {
|
override fun displayConsequences() {
|
||||||
println("""
|
println(
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
OVER ONE THIRD OF THE POPULTATION HAS DIED SINCE YOU
|
OVER ONE THIRD OF THE POPULTATION HAS DIED SINCE YOU
|
||||||
WERE ELECTED TO OFFICE. THE PEOPLE (REMAINING)
|
WERE ELECTED TO OFFICE. THE PEOPLE (REMAINING)
|
||||||
HATE YOUR GUTS.
|
HATE YOUR GUTS.
|
||||||
""".trimIndent())
|
""".trimIndent()
|
||||||
|
)
|
||||||
finalFate()
|
finalFate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AntiImmigrationRevolution : GameEndingException() {
|
object AntiImmigrationRevolution : YearOutcome() {
|
||||||
override fun displayConsequences() {
|
override fun displayConsequences() {
|
||||||
println("""
|
println(
|
||||||
|
"""
|
||||||
THE NUMBER OF FOREIGN WORKERS HAS EXCEEDED THE NUMBER
|
THE NUMBER OF FOREIGN WORKERS HAS EXCEEDED THE NUMBER
|
||||||
OF COUNTRYMEN. AS A MINORITY, THEY HAVE REVOLTED AND
|
OF COUNTRYMEN. AS A MINORITY, THEY HAVE REVOLTED AND
|
||||||
TAKEN OVER THE COUNTRY.
|
TAKEN OVER THE COUNTRY.
|
||||||
""".trimIndent())
|
""".trimIndent()
|
||||||
|
)
|
||||||
finalFate()
|
finalFate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class StarvationWithFullTreasury : GameEndingException() {
|
object StarvationWithFullTreasury : YearOutcome() {
|
||||||
override fun displayConsequences() {
|
override fun displayConsequences() {
|
||||||
println("""
|
println(
|
||||||
MONEY WAS LEFT OVER IN THE TREASURY WHICH YOU DID
|
if (KEEP_ORIGINAL_SUICIDE_REFERENCE) {
|
||||||
NOT SPEND. AS A RESULT, SOME OF YOUR COUNTRYMEN DIED
|
"""
|
||||||
OF STARVATION. THE PUBLIC IS ENRAGED AND YOU HAVE
|
MONEY WAS LEFT OVER IN THE TREASURY WHICH YOU DID
|
||||||
BEEN FORCED TO EITHER RESIGN OR COMMIT SUICIDE.
|
NOT SPEND. AS A RESULT, SOME OF YOUR COUNTRYMEN DIED
|
||||||
THE CHOICE IS YOURS.
|
OF STARVATION. THE PUBLIC IS ENRAGED AND YOU HAVE
|
||||||
IF YOU CHOOSE THE LATTER, PLEASE TURN OFF YOUR COMPUTER
|
BEEN FORCED TO EITHER RESIGN OR COMMIT SUICIDE.
|
||||||
BEFORE PROCEEDING.
|
THE CHOICE IS YOURS.
|
||||||
""".trimIndent())
|
IF YOU CHOOSE THE LATTER, PLEASE TURN OFF YOUR COMPUTER
|
||||||
|
BEFORE PROCEEDING.
|
||||||
|
""".trimIndent()
|
||||||
|
} else {
|
||||||
|
"""
|
||||||
|
MONEY WAS LEFT OVER IN THE TREASURY WHICH YOU DID
|
||||||
|
NOT SPEND. AS A RESULT, SOME OF YOUR COUNTRYMEN DIED
|
||||||
|
OF STARVATION. THE PUBLIC IS ENRAGED AND YOU HAVE
|
||||||
|
BEEN FORCED TO RESIGN.
|
||||||
|
PLEASE TURN OFF YOUR COMPUTER AND SURRENDER IT TO
|
||||||
|
THE NEAREST POLICE STATION.
|
||||||
|
""".trimIndent()
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DataEntryValidation : GameEndingException() {
|
|
||||||
override fun displayConsequences() {
|
|
||||||
// no action
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fun win(yearsRequired: Int) {
|
|
||||||
// The misspelling of "successfully" is in the original code.
|
|
||||||
println("""
|
|
||||||
|
|
||||||
CONGRATULATIONS!!!!!!!!!!!!!!!!!!
|
|
||||||
YOU HAVE SUCCESFULLY COMPLETED YOUR $yearsRequired YEAR TERM
|
|
||||||
OF OFFICE. YOU WERE, OF COURSE, EXTREMELY LUCKY, BUT
|
|
||||||
NEVERTHELESS, IT'S QUITE AN ACHIEVEMENT. GOODBYE AND GOOD
|
|
||||||
LUCK - YOU'LL PROBABLY NEED IT IF YOU'RE THE TYPE THAT
|
|
||||||
PLAYS THIS GAME.
|
|
||||||
|
|
||||||
|
|
||||||
""".trimIndent())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Record data, allow data input, and process the simulation for the game.
|
* Record data, allow data input, and process the simulation for the game.
|
||||||
*/
|
*/
|
||||||
class GameState(val yearsRequired: Int = 8) {
|
class GameState(val yearsRequired: Int = 8) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current year. Years start with zero, but we never
|
* The current year. Years start with zero, but we never
|
||||||
* output the current year.
|
* output the current year.
|
||||||
@@ -240,8 +250,8 @@ class GameState(val yearsRequired: Int = 8) {
|
|||||||
private var tourists = 0
|
private var tourists = 0
|
||||||
|
|
||||||
private var moneySpentOnPollutionControl = 0
|
private var moneySpentOnPollutionControl = 0
|
||||||
private var moneySpentOnPlanting = 0
|
|
||||||
|
|
||||||
|
private var moneySpentOnPlanting = 0
|
||||||
/**
|
/**
|
||||||
* Current stock of rallods.
|
* Current stock of rallods.
|
||||||
* Player starts with between 59000 and 61000 rallods, but
|
* Player starts with between 59000 and 61000 rallods, but
|
||||||
@@ -252,10 +262,10 @@ class GameState(val yearsRequired: Int = 8) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Population.
|
* Population.
|
||||||
* Initial population is about to 500.
|
* Initial population is about 500.
|
||||||
* 75% of the time it's between 495 and 505.
|
* 75% of the time it's between 495 and 505.
|
||||||
*/
|
*/
|
||||||
private var countrymen = (500 + (10 * rnd) - (10 * rnd)).toInt()
|
var countrymen = (500 + (10 * rnd) - (10 * rnd)).toInt()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Land sale price is evenly between 95 and 104 rallods per
|
* Land sale price is evenly between 95 and 104 rallods per
|
||||||
@@ -265,8 +275,8 @@ class GameState(val yearsRequired: Int = 8) {
|
|||||||
private var landPrice = (10 * rnd + 95).toInt()
|
private var landPrice = (10 * rnd + 95).toInt()
|
||||||
|
|
||||||
private var plantingArea = 0
|
private var plantingArea = 0
|
||||||
private var welfareThisYear = 0
|
|
||||||
|
|
||||||
|
private var welfareThisYear = 0
|
||||||
/**
|
/**
|
||||||
* Land area in square miles. Arable land is 1000 square miles less.
|
* Land area in square miles. Arable land is 1000 square miles less.
|
||||||
* Almost all calculations use landArea-1000 because only arable
|
* Almost all calculations use landArea-1000 because only arable
|
||||||
@@ -339,7 +349,6 @@ class GameState(val yearsRequired: Int = 8) {
|
|||||||
rallods -= welfareThisYear
|
rallods -= welfareThisYear
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ask how much land we want to sell. Immediately get the money.
|
* Ask how much land we want to sell. Immediately get the money.
|
||||||
* The player has to do the calculations to work out how much
|
* The player has to do the calculations to work out how much
|
||||||
@@ -362,6 +371,7 @@ class GameState(val yearsRequired: Int = 8) {
|
|||||||
} while (sellThisYear < 0 || sellThisYear > landArea - 1000)
|
} while (sellThisYear < 0 || sellThisYear > landArea - 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Input the value of `welfareThisYear`
|
* Input the value of `welfareThisYear`
|
||||||
*/
|
*/
|
||||||
@@ -444,7 +454,7 @@ class GameState(val yearsRequired: Int = 8) {
|
|||||||
plantingArea == 0 &&
|
plantingArea == 0 &&
|
||||||
moneySpentOnPollutionControl == 0
|
moneySpentOnPollutionControl == 0
|
||||||
|
|
||||||
fun simulateOneYear() {
|
fun simulateOneYear(): YearOutcome {
|
||||||
rallods -= moneySpentOnPollutionControl
|
rallods -= moneySpentOnPollutionControl
|
||||||
val rallodsAfterPollutionControl = rallods
|
val rallodsAfterPollutionControl = rallods
|
||||||
|
|
||||||
@@ -460,7 +470,7 @@ class GameState(val yearsRequired: Int = 8) {
|
|||||||
https://github.com/coding-horror/basic-computer-games/blob/main/53_King/king.bas#:~:text=1105%20IF%20I/100%3C50%20THEN%201700
|
https://github.com/coding-horror/basic-computer-games/blob/main/53_King/king.bas#:~:text=1105%20IF%20I/100%3C50%20THEN%201700
|
||||||
*/
|
*/
|
||||||
if (welfareThisYear / 100.0 < 50)
|
if (welfareThisYear / 100.0 < 50)
|
||||||
throw GameEndingException.TooManyPeopleDead()
|
return YearOutcome.TooManyPeopleDead
|
||||||
|
|
||||||
starvationDeaths = (countrymen - (welfareThisYear / 100.0)).toInt()
|
starvationDeaths = (countrymen - (welfareThisYear / 100.0)).toInt()
|
||||||
println("$starvationDeaths COUNTRYMEN DIED OF STARVATION")
|
println("$starvationDeaths COUNTRYMEN DIED OF STARVATION")
|
||||||
@@ -568,33 +578,37 @@ class GameState(val yearsRequired: Int = 8) {
|
|||||||
|
|
||||||
https://github.com/coding-horror/basic-computer-games/blob/main/53_King/king.bas#:~:text=1450%20V3%3DINT,INT(A%2BV3)
|
https://github.com/coding-horror/basic-computer-games/blob/main/53_King/king.bas#:~:text=1450%20V3%3DINT,INT(A%2BV3)
|
||||||
*/
|
*/
|
||||||
if (INCLUDE_BUGS_FROM_ORIGINAL) {
|
if (KEEP_ORIGINAL_BUGS) {
|
||||||
tourists += rallods
|
tourists += rallods
|
||||||
} else {
|
} else {
|
||||||
tourists = abs(v1 - v2)
|
tourists = abs(v1 - v2)
|
||||||
}
|
}
|
||||||
rallods += tourists
|
rallods += tourists
|
||||||
|
|
||||||
if (death > 200)
|
return if (death > 200)
|
||||||
throw GameEndingException.ExtremeMismanagement(death)
|
YearOutcome.ExtremeMismanagement(death)
|
||||||
if (countrymen < 343)
|
else if (countrymen < 343)
|
||||||
throw GameEndingException.TooManyPeopleDead()
|
YearOutcome.TooManyPeopleDead
|
||||||
if (rallodsAfterPollutionControl / 100 > 5 && death - pollutionDeaths >= 2)
|
else if (rallodsAfterPollutionControl / 100 > 5 && death - pollutionDeaths >= 2)
|
||||||
throw GameEndingException.StarvationWithFullTreasury()
|
YearOutcome.StarvationWithFullTreasury
|
||||||
if (foreignWorkers > countrymen)
|
else if (foreignWorkers > countrymen)
|
||||||
throw GameEndingException.AntiImmigrationRevolution()
|
YearOutcome.AntiImmigrationRevolution
|
||||||
|
else {
|
||||||
|
if (currentYear++ > yearsRequired)
|
||||||
|
YearOutcome.Win(yearsRequired)
|
||||||
|
else
|
||||||
|
YearOutcome.ContinueNextYear
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun numberInput() = try {
|
private fun numberInput() = try {
|
||||||
readLine()?.toInt() ?: throw EndOfInputException()
|
readLine()?.toInt() ?: throw EndOfInputException()
|
||||||
} catch (r: NumberFormatException) {
|
} catch (r: NumberFormatException) {
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DataEntryValidationException : Throwable()
|
||||||
|
private fun validatedInput(predicate : (Int)->Boolean) =
|
||||||
|
numberInput().apply { if (!predicate(this)) throw DataEntryValidationException() }
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ In this game, you are given by the computer a revolver loaded with one bullet an
|
|||||||
|
|
||||||
Tom Adametx wrote this program while a student at Curtis Jr. High School in Sudbury, Massachusetts.
|
Tom Adametx wrote this program while a student at Curtis Jr. High School in Sudbury, Massachusetts.
|
||||||
|
|
||||||
|
⚠️ This game includes EXPLICT references to suicide, and should not be included in most distributions, especially considering the extreme simplicity of the program.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
As published in Basic Computer Games (1978):
|
As published in Basic Computer Games (1978):
|
||||||
|
|||||||
Reference in New Issue
Block a user