Merge branch 'coding-horror:main' into 48_High_IQ_Python

This commit is contained in:
Thomas Kwashnak
2022-01-04 14:01:19 -05:00
committed by GitHub
15 changed files with 850 additions and 258 deletions

2
01_Acey_Ducey/d/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*.exe
*.obj

29
01_Acey_Ducey/d/README.md Normal file
View File

@@ -0,0 +1,29 @@
Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html)
Converted to [D](https://dlang.org/) by [Bastiaan Veelo](https://github.com/veelo).
Two versions are supplied that are functionally equivalent, but differ in source layout:
<dl>
<dt><tt>aceyducey_literal.d</tt></dt>
<dd>A largely literal transcription of the original Basic source. All unnecessary uglyness is preserved.</dd>
<dt><tt>aceyducey.d</tt></dt>
<dd>An idiomatic D refactoring of the original, with a focus on increasing the readability and robustness.
Memory-safety <A href="https://dlang.org/spec/memory-safe-d.html">is ensured by the language</a>, thanks to the
<tt>@safe</tt> annotation.</dd>
</dl>
## Running the code
Assuming the reference [dmd](https://dlang.org/download.html#dmd) compiler:
```shell
dmd -run aceyducey.d
```
[Other compilers](https://dlang.org/download.html) also exist.
Note that there are compiler switches related to memory-safety (`-preview=dip25` and `-preview=dip1000`) that are not
used here because they are unnecessary in this case. What these do is to make the analysis more thorough, so that with
them some code that needed to be `@system` can then be inferred to be in fact `@safe`. [Code that compiles without
these switches is just as safe as when compiled with them]
(https://forum.dlang.org/post/dftgjalswvwfjpyushgn@forum.dlang.org).

131
01_Acey_Ducey/d/aceyducey.d Normal file
View File

@@ -0,0 +1,131 @@
@safe: // Make @safe the default for this file, enforcing memory-safety.
void main()
{
import std.stdio : write, writeln;
import std.string : center, toUpper, wrap;
import std.exception : ifThrown;
enum width = 80;
writeln(center("Acey Ducey Card Game", width));
writeln(center("(After Creative Computing Morristown, New Jersey)\n", width));
writeln(wrap("Acey-Ducey is played in the following manner: The dealer (computer) deals two cards face up. " ~
"You have an option to bet or not bet depending on whether or not you feel the third card will " ~
"have a value between the first two. If you do not want to bet, input a 0.", width));
enum Hand {low, middle, high}
Card[Hand.max + 1] cards; // Three cards.
bool play = true;
while (play)
{
int cash = 100;
while (cash > 0)
{
writeln("\nYou now have ", cash, " dollars.");
int bet = 0;
while (bet <= 0)
{
do // Draw new cards, until the first card has a smaller value than the last card.
{
foreach (ref card; cards)
card.drawNew;
} while (cards[Hand.low] >= cards[Hand.high]);
writeln("Here are your next two cards:\n", cards[Hand.low], "\n", cards[Hand.high]);
int askBet() // A nested function.
{
import std.conv : to;
write("\nWhat is your bet? ");
int answer = readString.to!int.
ifThrown!Exception(askBet); // Try again when answer does not convert to int.
if (answer <= cash)
return answer;
writeln("Sorry, my friend, but you bet too much.\nYou have only ", cash, " dollars to bet.");
return askBet; // Recurse: Ask again.
}
bet = askBet;
if (bet <= 0) // Negative bets are interpreted as 0.
writeln("CHICKEN!!");
} // bet is now > 0.
writeln(cards[Hand.middle]);
if (cards[Hand.low] < cards[Hand.middle] && cards[Hand.middle] < cards[Hand.high])
{
writeln("YOU WIN!!!");
cash += bet;
}
else
{
writeln("Sorry, you lose.");
cash -= bet;
if (cash <= 0)
{
writeln("\n\nSorry, friend, but you blew your wad.");
write("\n\nTry again (Yes or No)? ");
play = readString.toUpper == "YES";
}
}
}
}
writeln("O.K., hope you had fun!");
}
struct Card
{
int value = 2;
alias value this; // Enables Card to stand in as an int, so that cards can be compared as ints.
invariant
{
assert(2 <= value && value <= 14); // Ensure cards always have a valid value.
}
/// Adopt a new value.
void drawNew()
{
import std.random : uniform;
value = uniform!("[]", int, int)(2, 14); // A random int between inclusive bounds.
}
/// Called for implicit conversion to string.
string toString() const pure
{
import std.conv : text;
switch (value)
{
case 11: return "Jack";
case 12: return "Queen";
case 13: return "King";
case 14: return "Ace";
default: return text(" ", value); // Basic prepends a space.
}
}
}
/// Read a string from standard input, stripping newline and other enclosing whitespace.
string readString() nothrow
{
import std.string : strip;
try
return trustedReadln.strip;
catch (Exception) // readln throws on I/O and Unicode errors, which we handle here.
return "";
}
/** An @trusted wrapper around readln.
*
* This is the only function that formally requires manual review for memory-safety.
* [Arguably readln should be safe already](https://forum.dlang.org/post/rab398$1up$1@digitalmars.com)
* which would remove the need to have any @trusted code in this program.
*/
string trustedReadln() @trusted
{
import std.stdio : readln;
return readln;
}

View File

@@ -0,0 +1,104 @@
void main()
{
import std;
L10: writef("%26s", ' '); writeln("ACEY DUCEY CARD GAME");
L20: writef("%15s", ' '); writeln("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
L21: writeln;
L22: writeln;
L30: writeln("ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER ");
L40: writeln("THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP");
L50: writeln("YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING");
L60: writeln("ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE");
L70: writeln("A VALUE BETWEEN THE FIRST TWO.");
L80: writeln("IF YOU DO NOT WANT TO BET, INPUT A 0");
L100: int N=100;
L110: int Q=100, M;
L120: writeln("YOU NOW HAVE ",Q," DOLLARS.");
L130: writeln;
L140: goto L260;
L210: Q=Q+M;
L220: goto L120;
L240: Q=Q-M;
L250: goto L120;
L260: writeln("HERE ARE YOUR NEXT TWO CARDS: ");
L270: auto A=to!int(14*uniform01)+2;
L280: if (A<2) goto L270;
L290: if (A>14) goto L270;
L300: auto B=to!int(14*uniform01)+2;
L310: if (B<2) goto L300;
L320: if (B>14) goto L300;
L330: if (A>=B) goto L270;
L350: if (A<11) goto L400;
L360: if (A==11) goto L420;
L370: if (A==12) goto L440;
L380: if (A==13) goto L460;
L390: if (A==14) goto L480;
L400: writefln("%2d", A);
L410: goto L500;
L420: writeln("JACK");
L430: goto L500;
L440: writeln("QUEEN");
L450: goto L500;
L460: writeln("KING");
L470: goto L500;
L480: writeln("ACE");
L500: if (B<11) goto L550;
L510: if (B==11) goto L570;
L520: if (B==12) goto L590;
L530: if (B==13) goto L610;
L540: if (B==14) goto L630;
L550: writefln("%2d", B);
L560: goto L650;
L570: writeln("JACK");
L580: goto L650;
L590: writeln("QUEEN");
L600: goto L650;
L610: writeln("KING");
L620: goto L650;
L630: writeln("ACE");
L640: writeln;
L650: writeln;
L660: write("WHAT IS YOUR BET? "); M = stdin.readln.strip.to!int;
L670: if (M!=0) goto L680;
L675: writeln("CHICKEN!!");
L676: writeln;
L677: goto L260;
L680: if (M<=Q) goto L730;
L690: writeln("SORRY, MY FRIEND, BUT YOU BET TOO MUCH.");
L700: writeln("YOU HAVE ONLY ",Q," DOLLARS TO BET.");
L710: goto L650;
L730: auto C=to!int(14*uniform01)+2;
L740: if (C<2) goto L730;
L750: if (C>14) goto L730;
L760: if (C<11) goto L810;
L770: if (C==11) goto L830;
L780: if (C==12) goto L850;
L790: if (C==13) goto L870;
L800: if (C==14) goto L890;
L810: writeln(C);
L820: goto L910;
L830: writeln("JACK");
L840: goto L910;
L850: writeln("QUEEN");
L860: goto L910;
L870: writeln("KING");
L880: goto L910;
L890: writeln( "ACE");
L900: writeln;
L910: if (C>A) goto L930;
L920: goto L970;
L930: if (C>=B) goto L970;
L950: writeln("YOU WIN!!!");
L960: goto L210;
L970: writeln("SORRY, YOU LOSE");
L980: if (M<Q) goto L240;
L990: writeln;
L1000: writeln;
L1010: writeln("SORRY, FRIEND, BUT YOU BLEW YOUR WAD.");
L1015: writeln;writeln;
L1020: write("TRY AGAIN (YES OR NO)? "); auto AS=stdin.readln;
L1025: writeln;writeln;
L1030: if (AS.strip.toUpper=="YES") goto L110;
L1040: writeln("O.K., HOPE YOU HAD FUN!");
}

View File

@@ -12,3 +12,7 @@ As published in Basic Computer Games (1978):
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html
---
**2022-01-04:** patched original source in [#400](https://github.com/coding-horror/basic-computer-games/pull/400) to fix a minor bug where a generated maze may be missing an exit, particularly at small maze sizes.

View File

@@ -0,0 +1,156 @@
########################################################
# Calendar
#
# From: BASIC Computer Games (1978)
# Edited by David Ahl#
#
# This program prints out a calendar
# for any year. You must specify the
# starting day of the week of the year in
# statement 130. (Sunday(0), Monday
# (-1), Tuesday(-2), etc.) You can determine
# this by using the program WEEKDAY.
# You must also make two changes
# for leap years in statement 360 and 620.
# The program listing describes the necessary
# changes. Running the program produces a
# nice 12-month calendar.
# The program was written by Geofrey
# Chase of the Abbey, Portsmouth, Rhode Island.
#
########################################################
def parse_input():
"""
function to parse input for weekday and leap year boolean
"""
days_mapping = {
"sunday": 0,
"monday": -1,
"tuesday": -2,
"wednesday": -3,
"thursday": -4,
"friday": -5,
"saturday": -6
}
day = 0
leap_day = False
correct_day_input = False
while not correct_day_input:
weekday = input("INSERT THE STARTING DAY OF THE WEEK OF THE YEAR:")
for day_k in days_mapping.keys():
if weekday.lower() in day_k:
day = days_mapping[day_k]
correct_day_input = True
break
while True:
leap = input("IS IT A LEAP YEAR?:")
if 'y' in leap.lower():
leap_day = True
break
if 'n' in leap.lower():
leap_day = False
break
return day, leap_day
def calendar(weekday, leap_year):
"""
function to print a year's calendar.
input:
_weekday_: int - the initial day of the week (0=SUN, -1=MON, -2=TUES...)
_leap_year_: bool - indicates if the year is a leap year
"""
months_days = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
days = 'S M T W T F S\n'
sep = "*" * 59
years_day = 365
d = weekday
if leap_year:
months_days[2] = 29
years_day = 366
months_names = [" JANUARY ",
" FEBRUARY",
" MARCH ",
" APRIL ",
" MAY ",
" JUNE ",
" JULY ",
" AUGUST ",
"SEPTEMBER",
" OCTOBER ",
" NOVEMBER",
" DECEMBER"]
days_count = 0 # S in the original program
# main loop
for n in range(1, 13):
days_count += months_days[n-1]
print("** {} ****************** {} ****************** {} **\n".format(days_count,
months_names[n-1], years_day-days_count))
print(days)
print(sep)
for w in range(1, 7):
print("\n")
for g in range(1, 8):
d += 1
d2 = d - days_count
if d2 > months_days[n]:
break
if d2 <= 0:
print("{}".format(' '), end=' ')
elif d2 < 10:
print(" {}".format(d2), end=' ')
else:
print("{}".format(d2), end=' ')
print()
if d2 >= months_days[n]:
break
if d2 > months_days[n]:
d -= g
print("\n")
print("\n")
def main():
print(" "*32 + "CALENDAR")
print(" "*15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY")
print("\n"*11)
day, leap_year = parse_input()
calendar(day, leap_year)
if __name__ == "__main__":
main()
########################################################
#
########################################################
#
# Porting notes:
#
# It has been added an input at the beginning of the
# program so the user can specify the first day of the
# week of the year and if the year is leap or not.
#
########################################################

69
25_Chief/perl/chief.pl Normal file
View File

@@ -0,0 +1,69 @@
#!/usr/bin/perl
use strict;
use warnings;
print ' ' x 30 . "CHIEF\n";
print ' ' x 15 . "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n";
print "\n\n\n";
print "I AM CHIEF NUMBERS FREEK, THE GREAT INDIAN MATH GOD.\n";
print "ARE YOU READY TO TAKE THE TEST YOU CALLED ME OUT FOR?\n";
chomp( my $A = uc <STDIN> );
print "SHUT UP, PALE FACE WITH WISE TONGUE.\n" unless ( $A eq 'YES' );
print " TAKE A NUMBER AND ADD 3. DIVIDE THIS NUMBER BY 5 AND\n";
print "MULTIPLY BY 8. DIVIDE BY 5 AND ADD THE SAME. SUBTRACT 1.\n";
print " WHAT DO YOU HAVE?\n";
chomp( my $B = <STDIN> );
my $C = ( $B + 1 - 5 ) * 5 / 8 * 5 - 3;
print "I BET YOUR NUMBER WAS $C. AM I RIGHT?\n";
chomp( my $D = uc <STDIN> );
if ( $D eq 'YES' ) {
print "BYE!!!\n";
exit;
}
print "WHAT WAS YOUR ORIGINAL NUMBER?\n";
chomp( my $K = <STDIN> );
my $F = $K + 3;
my $G = $F / 5;
my $H = $G * 8;
my $I = $H / 5 + 5;
my $J = $I - 1;
print "SO YOU THINK YOU'RE SO SMART, EH?\n";
print "NOW WATCH.\n";
print "$K PLUS 3 EQUALS $F. THIS DIVIDED BY 5 EQUALS $G;\n";
print "THIS TIMES 8 EQUALS $H. IF WE DIVIDE BY 5 AND ADD 5,\n";
print "WE GET $I , WHICH, MINUS 1, EQUALS $J.\n";
print "NOW DO YOU BELIEVE ME?\n";
chomp( my $Z = uc <STDIN> );
if ( $Z eq 'YES' ) {
print "BYE!!!\n";
exit;
}
print "YOU HAVE MADE ME MAD!!!\n";
print "THERE MUST BE A GREAT LIGHTNING BOLT!\n\n\n";
for my $i ( reverse 22 .. 30 ) {
print ' ' x $i . "X X\n";
}
print ' ' x 21 . "X XXX\n";
print ' ' x 20 . "X X\n";
print ' ' x 19 . "XX X\n";
for my $i ( reverse 13 .. 20 ) {
print ' ' x $i . "X X\n";
}
print ' ' x 12 . "XX\n";
print ' ' x 11 . "X\n";
print ' ' x 10 . "*\n";
print "\n#########################\n\n";
print "I HOPE YOU BELIEVE ME NOW, FOR YOUR SAKE!!\n";

57
31_Depth_Charge/ruby/.gitignore vendored Normal file
View File

@@ -0,0 +1,57 @@
# Package Shell Specific Things
## Build Process
/build
## Transient files
/src/input
/src/output
/src/log
/drop
# Programming Languages
## Java
/src/java/**/*.class
## Rakudo / Perl6
/src/**/.precomp
## PHP
/vendor/
## Python
*.swp
__pycache__/
*.pyc
*.egg-info
/dist
## Ruby
*.gem
.bundle
# Editors
## Dia
*.dia.autosave
## Emacs
\#*\#
.\#*
## Vi / Vim
*.swp
# Filesystem Artifacts
## Mac
.DS_Store
## NFS
.nfs*

View File

@@ -13,7 +13,6 @@ class DepthCharge
break if ! get_input_another_game()
end
# 420 PRINT "OK. HOPE YOU ENJOYED YOURSELF." : GOTO 600
printf("OK. HOPE YOU ENJOYED YOURSELF.\n")
end
@@ -51,7 +50,7 @@ class DepthCharge
the_input = Integer(value) rescue nil
if the_input == nil || the_input < 0
if the_input == nil || the_input < 1
printf("PLEASE ENTER A POSITIVE NUMBER\n\n")
next
@@ -61,25 +60,7 @@ class DepthCharge
end
end
def get_search_area_dimension
# 20 INPUT "DIMENSION OF SEARCH AREA";G: PRINT
@search_area_dimension = get_input_positive_integer("DIMENSION OF SEARCH AREA: ")
# 30 N=INT(LOG(G)/LOG(2))+1
@num_tries = Integer(
Math.log(@search_area_dimension)/Math.log(2)
)
end
def print_instructions
# 40 PRINT "YOU ARE THE CAPTAIN OF THE DESTROYER USS COMPUTER"
# 50 PRINT "AN ENEMY SUB HAS BEEN CAUSING YOU TROUBLE. YOUR"
# 60 PRINT "MISSION IS TO DESTROY IT. YOU HAVE";N;"SHOTS."
# 70 PRINT "SPECIFY DEPTH CHARGE EXPLOSION POINT WITH A"
# 80 PRINT "TRIO OF NUMBERS -- THE FIRST TWO ARE THE"
# 90 PRINT "SURFACE COORDINATES; THE THIRD IS THE DEPTH."
# 100 PRINT : PRINT "GOOD LUCK !": PRINT
printf( <<~INSTRUCTIONS
YOU ARE THE CAPTAIN OF THE DESTROYER USS COMPUTER
AN ENEMY SUB HAS BEEN CAUSING YOU TROUBLE. YOUR
@@ -110,19 +91,21 @@ GOOD LUCK !
end
def setup_game
get_search_area_dimension()
@search_area_dimension = get_input_positive_integer("DIMENSION OF SEARCH AREA: ")
@num_tries = Integer(
Math.log(@search_area_dimension)/Math.log(2) + 1
)
setup_enemy()
end
def setup_enemy
# 110 A=INT(G*RND(1)) : B=INT(G*RND(1)) : C=INT(G*RND(1))
@enemy_x = rand(1..@search_area_dimension)
@enemy_y = rand(1..@search_area_dimension)
@enemy_z = rand(1..@search_area_dimension)
end
end
def game_loop
# 120 FOR D=1 TO N : PRINT : PRINT "TRIAL #";D; : INPUT X,Y,Z
for @trial in 1..@num_tries do
output_game_status()
@@ -130,7 +113,6 @@ GOOD LUCK !
@shot_y = get_input_positive_integer("Y: ")
@shot_z = get_input_positive_integer("Z: ")
# 130 IF ABS(X-A)+ABS(Y-B)+ABS(Z-C)=0 THEN 300
if (
(@enemy_x - @shot_x).abs \
+ (@enemy_y - @shot_y).abs \
@@ -140,7 +122,6 @@ GOOD LUCK !
you_win()
return
else
# 140 GOSUB 500 : PRINT : NEXT D
missed_shot()
end
end
@@ -156,54 +137,41 @@ GOOD LUCK !
printf("TRIAL \#%d\n", @trial)
end
def you_win
printf("B O O M ! ! YOU FOUND IT IN %d TRIES!\n\n", @trial )
printf("\nB O O M ! ! YOU FOUND IT IN %d TRIES!\n\n", @trial )
end
def missed_shot
missed_directions = []
# 530 IF X>A THEN PRINT "EAST";
# 540 IF X<A THEN PRINT "WEST";
if @shot_x > @enemy_x
missed_directions.push('TOO FAR EAST')
elsif @shot_x < @enemy_x
missed_directions.push('TOO FAR WEST')
end
# 510 IF Y>B THEN PRINT "NORTH";
# 520 IF Y<B THEN PRINT "SOUTH";
if @shot_y > @enemy_y
missed_directions.push('TOO FAR NORTH')
elsif @shot_y < @enemy_y
missed_directions.push('TOO FAR SOUTH')
end
# 560 IF Z>C THEN PRINT " TOO LOW."
# 570 IF Z<C THEN PRINT " TOO HIGH."
# 580 IF Z=C THEN PRINT " DEPTH OK."
if @shot_z > @enemy_z
missed_directions.push('TOO DEEP')
elsif @shot_z < @enemy_z
missed_directions.push('TOO SHALLOW')
end
# 500 PRINT "SONAR REPORTS SHOT WAS ";
printf("SONAR REPORTS SHOT WAS: \n")
printf("%s\n", "\t" + missed_directions.join("\n\t"))
# 550 IF Y<>B OR X<>A THEN PRINT " AND";
# 590 RETURN
end
def you_lose
# You took too long!
printf("YOU HAVE BEEN TORPEDOED! ABANDON SHIP!\n")
printf("THE SUBMARINE WAS AT %d %d %d\n", @enemy_x, @enemy_y, @enemy_z)
end
def get_input_another_game
# 400 PRINT : PRINT: INPUT "ANOTHER GAME (Y OR N)";A$
return get_input_y_or_n("ANOTHER GAME (Y OR N): ")
# 410 IF A$="Y" THEN 100
end
end

View File

@@ -21,7 +21,7 @@ sub game_play {
if ($choice == 0) {
until ($marbles == 0) {
my $computer_choice = &computer_select($marbles,$turn);
my $computer_choice = &computer_select($marbles,$turn,$player_total);
$marbles = $marbles - $computer_choice;
$computer_total = $computer_total + $computer_choice;
print "MY TOTAL IS $computer_total\n";
@@ -52,7 +52,7 @@ sub game_play {
if ($marbles == 0) {&determine_winner($computer_total,$player_total)};
my $computer_choice = &computer_select($marbles,$turn);
my $computer_choice = &computer_select($marbles,$turn,$player_total);
$marbles = $marbles - $computer_choice;
$computer_total = $computer_total + $computer_choice;
print "MY TOTAL IS $computer_total\n";
@@ -114,6 +114,7 @@ sub player_select {
sub computer_select {
my $marbles = shift;
my $turn = shift;
my $player_total = shift;
my $num = 2;
my $validity = "invalid";
if ($turn == 0) {
@@ -122,14 +123,24 @@ sub computer_select {
else {
until ($validity eq "valid") {
my $R=$marbles-6*int(($marbles/6));
if ($marbles < 4.2) {
if (int($player_total/2) == $player_total/2) {
if ($R < 1.5 || $R > 5.3) {
$num = 1;
}
else {
$num = $R - 1;
}
}
elsif ($marbles < 4.2) {
$num = $marbles;
}
if ($R > 3.4) {
elsif ($R > 3.4) {
if ($R < 4.7 || $R > 3.5) {
$num = 4;
}
else {
else {
$num = 1;
}
}

View File

@@ -142,17 +142,32 @@ def game_over():
print('')
def computer_turn():
global marbles_in_middle
global computer_marbles
global marbles_in_middle
global computer_marbles
global human_marbles
print("It's the computer's turn ...")
max_choice = min(4, marbles_in_middle)
marbles_to_take=0
# choose at random
n = random.randint(1, max_choice)
print(f'Computer takes {marbles_str(n)} ...')
marbles_in_middle -= n
computer_marbles += n
print("It's the computer's turn ...")
r = marbles_in_middle - 6 * int((marbles_in_middle/6)) #line 500
if int(human_marbles/2) == human_marbles/2: #line 510
if r < 1.5 or r > 5.3: #lines 710 and 720
marbles_to_take = 1
else:
marbles_to_take = r - 1
elif marbles_in_middle < 4.2: #line 580
marbles_to_take = marbles_in_middle
elif r > 3.4: #line 530
if r < 4.7 or r > 3.5:
marbles_to_take = 4
else:
marbles_to_take = r + 1
print(f'Computer takes {marbles_str(marbles_to_take)} ...')
marbles_in_middle -= marbles_to_take
computer_marbles += marbles_to_take
def play_game():
global marbles_in_middle

View File

@@ -1,4 +1,4 @@
### King
## King
This is one of the most comprehensive, difficult, and interesting games. (If youve 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.
## Bugs
⚠️ This game includes references to suicide or self-harm.
### Bugs
Implementers should be aware that this game contains at least one bug.

View File

@@ -3,7 +3,8 @@ import kotlin.random.Random
import kotlin.system.exitProcess
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()
fun tab(i: Int) = " ".repeat(i)
@@ -19,29 +20,25 @@ fun main() {
}
?: throw EndOfInputException()
try {
with(gameState) {
while(currentYear < yearsRequired) {
recalculateLandCost()
displayStatus()
inputLandSale()
performLandSale()
inputWelfare()
performWelfare()
inputPlantingArea()
performPlanting()
inputPollutionControl()
if (zeroInput()) {
displayExitMessage()
exitProcess(0)
}
simulateOneYear()
currentYear ++
with(gameState) {
do {
recalculateLandCost()
displayStatus()
inputLandSale()
performLandSale()
inputWelfare()
performWelfare()
inputPlantingArea()
performPlanting()
inputPollutionControl()
if (zeroInput()) {
displayExitMessage()
exitProcess(0)
}
}
win(gameState.yearsRequired)
} catch (e: GameEndingException) {
e.displayConsequences()
val yearResult = simulateOneYear().also {
it.displayConsequences()
}
} while (yearResult == YearOutcome.ContinueNextYear)
}
}
@@ -54,7 +51,8 @@ private fun header() {
}
fun instructions(yearsRequired: Int) {
println("""
println(
"""
CONGRATULATIONS! YOU'VE JUST BEEN ELECTED PREMIER OF SETATS
@@ -80,10 +78,7 @@ fun loadOldGame(): GameState = GameState().apply {
do {
var retry = false
print("HOW MANY YEARS HAD YOU BEEN IN OFFICE WHEN INTERRUPTED? ")
currentYear = numberInput()
if (currentYear <= 0)
throw GameEndingException.DataEntryValidation()
currentYear = validatedInput { it > 0 }
if (currentYear >= yearsRequired) {
println(" COME ON, YOUR TERM IN OFFICE IS ONLY $yearsRequired YEARS.")
@@ -92,21 +87,19 @@ fun loadOldGame(): GameState = GameState().apply {
} while (retry)
print("HOW MUCH DID YOU HAVE IN THE TREASURY? ")
rallods = numberInput()
if (rallods < 0)
throw GameEndingException.DataEntryValidation()
rallods = validatedInput { it >= 0 }
print("HOW MANY COUNTRYMEN? ")
countrymen = validatedInput { it >= 0 }
print("HOW MANY WORKERS? ")
foreignWorkers = numberInput()
if (foreignWorkers < 0)
throw GameEndingException.DataEntryValidation()
foreignWorkers = validatedInput { it >= 0 }
do {
var retry = false
print("HOW MANY SQUARE MILES OF LAND? ")
landArea = numberInput()
if (landArea<0)
throw GameEndingException.DataEntryValidation()
landArea = validatedInput { it >= 0 }
if (landArea > 2000 || landArea <= 1000) {
println(" COME ON, YOU STARTED WITH 1000 SQ. MILES OF FARM 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
* to mismanagement, starvation, revolution, or mis-entry of a game state.
* Possible outcomes for a year.
*/
sealed class GameEndingException : Throwable() {
abstract fun displayConsequences()
sealed class YearOutcome {
open fun displayConsequences() {
// Default display nothing
}
fun finalFate() {
if (rnd < .5) {
@@ -135,7 +130,28 @@ sealed class GameEndingException : Throwable() {
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() {
println()
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.
override fun displayConsequences() {
println("""
println(
"""
OVER ONE THIRD OF THE POPULTATION HAS DIED SINCE YOU
WERE ELECTED TO OFFICE. THE PEOPLE (REMAINING)
HATE YOUR GUTS.
""".trimIndent())
""".trimIndent()
)
finalFate()
}
}
class AntiImmigrationRevolution : GameEndingException() {
object AntiImmigrationRevolution : YearOutcome() {
override fun displayConsequences() {
println("""
println(
"""
THE NUMBER OF FOREIGN WORKERS HAS EXCEEDED THE NUMBER
OF COUNTRYMEN. AS A MINORITY, THEY HAVE REVOLTED AND
TAKEN OVER THE COUNTRY.
""".trimIndent())
""".trimIndent()
)
finalFate()
}
}
class StarvationWithFullTreasury : GameEndingException() {
object StarvationWithFullTreasury : YearOutcome() {
override fun displayConsequences() {
println("""
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 EITHER RESIGN OR COMMIT SUICIDE.
THE CHOICE IS YOURS.
IF YOU CHOOSE THE LATTER, PLEASE TURN OFF YOUR COMPUTER
BEFORE PROCEEDING.
""".trimIndent())
println(
if (KEEP_ORIGINAL_SUICIDE_REFERENCE) {
"""
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 EITHER RESIGN OR COMMIT SUICIDE.
THE CHOICE IS YOURS.
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.
*/
class GameState(val yearsRequired: Int = 8) {
/**
* The current year. Years start with zero, but we never
* output the current year.
@@ -240,8 +250,8 @@ class GameState(val yearsRequired: Int = 8) {
private var tourists = 0
private var moneySpentOnPollutionControl = 0
private var moneySpentOnPlanting = 0
private var moneySpentOnPlanting = 0
/**
* Current stock of rallods.
* Player starts with between 59000 and 61000 rallods, but
@@ -252,10 +262,10 @@ class GameState(val yearsRequired: Int = 8) {
/**
* Population.
* Initial population is about to 500.
* Initial population is about 500.
* 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
@@ -265,8 +275,8 @@ class GameState(val yearsRequired: Int = 8) {
private var landPrice = (10 * rnd + 95).toInt()
private var plantingArea = 0
private var welfareThisYear = 0
private var welfareThisYear = 0
/**
* Land area in square miles. Arable land is 1000 square miles less.
* Almost all calculations use landArea-1000 because only arable
@@ -339,7 +349,6 @@ class GameState(val yearsRequired: Int = 8) {
rallods -= welfareThisYear
}
/**
* Ask how much land we want to sell. Immediately get the money.
* 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)
}
/**
* Input the value of `welfareThisYear`
*/
@@ -444,7 +454,7 @@ class GameState(val yearsRequired: Int = 8) {
plantingArea == 0 &&
moneySpentOnPollutionControl == 0
fun simulateOneYear() {
fun simulateOneYear(): YearOutcome {
rallods -= moneySpentOnPollutionControl
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
*/
if (welfareThisYear / 100.0 < 50)
throw GameEndingException.TooManyPeopleDead()
return YearOutcome.TooManyPeopleDead
starvationDeaths = (countrymen - (welfareThisYear / 100.0)).toInt()
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)
*/
if (INCLUDE_BUGS_FROM_ORIGINAL) {
if (KEEP_ORIGINAL_BUGS) {
tourists += rallods
} else {
tourists = abs(v1 - v2)
}
rallods += tourists
if (death > 200)
throw GameEndingException.ExtremeMismanagement(death)
if (countrymen < 343)
throw GameEndingException.TooManyPeopleDead()
if (rallodsAfterPollutionControl / 100 > 5 && death - pollutionDeaths >= 2)
throw GameEndingException.StarvationWithFullTreasury()
if (foreignWorkers > countrymen)
throw GameEndingException.AntiImmigrationRevolution()
return if (death > 200)
YearOutcome.ExtremeMismanagement(death)
else if (countrymen < 343)
YearOutcome.TooManyPeopleDead
else if (rallodsAfterPollutionControl / 100 > 5 && death - pollutionDeaths >= 2)
YearOutcome.StarvationWithFullTreasury
else if (foreignWorkers > countrymen)
YearOutcome.AntiImmigrationRevolution
else {
if (currentYear++ > yearsRequired)
YearOutcome.Win(yearsRequired)
else
YearOutcome.ContinueNextYear
}
}
}
private fun numberInput() = try {
readLine()?.toInt() ?: throw EndOfInputException()
} catch (r: NumberFormatException) {
0
}
class DataEntryValidationException : Throwable()
private fun validatedInput(predicate : (Int)->Boolean) =
numberInput().apply { if (!predicate(this)) throw DataEntryValidationException() }

View File

@@ -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.
⚠️ 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):

View File

@@ -1,60 +1,95 @@
// WAR
//
// Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
// Original conversion from BASIC to Javascript by Oscar Toledo G. (nanochess)
//
function print(str)
{
function print(str) {
document.getElementById("output").appendChild(document.createTextNode(str));
}
function input()
{
var input_element;
var input_str;
return new Promise(function (resolve) {
input_element = document.createElement("INPUT");
print("? ");
input_element.setAttribute("type", "text");
input_element.setAttribute("length", "50");
document.getElementById("output").appendChild(input_element);
input_element.focus();
input_str = undefined;
input_element.addEventListener("keydown", function (event) {
if (event.keyCode == 13) {
input_str = input_element.value;
document.getElementById("output").removeChild(input_element);
print(input_str);
print("\n");
resolve(input_str);
}
});
});
}
function tab(space)
{
var str = "";
while (space-- > 0)
function tab(space) {
let str = "";
while (space-- > 0) {
str += " ";
}
return str;
}
var a = [, "S-2","H-2","C-2","D-2","S-3","H-3","C-3","D-3",
"S-4","H-4","C-4","D-4","S-5","H-5","C-5","D-5",
"S-6","H-6","C-6","D-6","S-7","H-7","C-7","D-7",
"S-8","H-8","C-8","D-8","S-9","H-9","C-9","D-9",
"S-10","H-10","C-10","D-10","S-J","H-J","C-J","D-J",
"S-Q","H-Q","C-Q","D-Q","S-K","H-K","C-K","D-K",
"S-A","H-A","C-A","D-A"];
function input() {
return new Promise(function (resolve) {
const input_element = document.createElement("INPUT");
var l = [];
print("? ");
input_element.setAttribute("type", "text");
input_element.setAttribute("length", "50");
document.getElementById("output").appendChild(input_element);
input_element.focus();
input_element.addEventListener("keydown", function (event) {
if (event.keyCode == 13) {
const input_str = input_element.value;
document.getElementById("output").removeChild(input_element);
print(input_str);
print("\n");
resolve(input_str);
}
});
});
}
// Main control section
async function main()
{
async function askYesOrNo(question) {
while (1) {
print(question);
const str = await input();
if (str == "YES") {
return true;
}
else if (str == "NO") {
return false;
}
else {
print("YES OR NO, PLEASE. ");
}
}
}
async function askAboutInstructions() {
const playerWantsInstructions = await askYesOrNo("DO YOU WANT DIRECTIONS");
if (playerWantsInstructions) {
print("THE COMPUTER GIVES YOU AND IT A 'CARD'. THE HIGHER CARD\n");
print("(NUMERICALLY) WINS. THE GAME ENDS WHEN YOU CHOOSE NOT TO\n");
print("CONTINUE OR WHEN YOU HAVE FINISHED THE PACK.\n");
}
print("\n");
print("\n");
}
function createGameDeck(cards, gameSize) {
const deck = [];
const deckSize = cards.length;
for (let j = 0; j < gameSize; j++) {
let card;
// Compute a new card index until we find one that isn't already in the new deck
do {
card = Math.floor(deckSize * Math.random());
} while (deck.includes(card));
deck.push(card);
}
return deck;
}
function computeCardValue(cardIndex) {
return Math.floor(cardIndex / 4);
}
function printGameOver(playerScore, computerScore) {
print("\n");
print("\n");
print(`WE HAVE RUN OUT OF CARDS. FINAL SCORE: YOU: ${playerScore} THE COMPUTER: ${computerScore}\n`);
print("\n");
}
function printTitle() {
print(tab(33) + "WAR\n");
print(tab(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n");
print("\n");
@@ -62,72 +97,65 @@ async function main()
print("\n");
print("THIS IS THE CARD GAME OF WAR. EACH CARD IS GIVEN BY SUIT-#\n");
print("AS S-7 FOR SPADE 7. ");
while (1) {
print("DO YOU WANT DIRECTIONS");
str = await input();
if (str == "YES") {
print("THE COMPUTER GIVES YOU AND IT A 'CARD'. THE HIGHER CARD\n");
print("(NUMERICALLY) WINS. THE GAME ENDS WHEN YOU CHOOSE NOT TO\n");
print("CONTINUE OR WHEN YOU HAVE FINISHED THE PACK.\n");
break;
}
if (str == "NO")
break;
print("YES OR NO, PLEASE. ");
}
print("\n");
}
function printCards(playerCard, computerCard) {
print("\n");
print(`YOU: ${playerCard}\tCOMPUTER: ${computerCard}\n`);
}
const cards = [
"S-2", "H-2", "C-2", "D-2",
"S-3", "H-3", "C-3", "D-3",
"S-4", "H-4", "C-4", "D-4",
"S-5", "H-5", "C-5", "D-5",
"S-6", "H-6", "C-6", "D-6",
"S-7", "H-7", "C-7", "D-7",
"S-8", "H-8", "C-8", "D-8",
"S-9", "H-9", "C-9", "D-9",
"S-10", "H-10", "C-10", "D-10",
"S-J", "H-J", "C-J", "D-J",
"S-Q", "H-Q", "C-Q", "D-Q",
"S-K", "H-K", "C-K", "D-K",
"S-A", "H-A", "C-A", "D-A"
];
// Main control section
async function main() {
printTitle();
await askAboutInstructions();
a1 = 0;
b1 = 0;
p = 0;
let computerScore = 0;
let playerScore = 0;
// Generate a random deck
for (j = 1; j <= 52; j++) {
do {
l[j] = Math.floor(52 * Math.random()) + 1;
for (k = 1; k < j; k++) {
if (l[k] == l[j]) // Already in deck?
break;
}
} while (j != 1 && k < j) ;
}
l[j] = 0; // Mark the end of the deck
const gameSize = cards.length; // Number of cards to shuffle into the game deck. Can be <= cards.length.
const deck = createGameDeck(cards, gameSize);
let shouldContinuePlaying = true;
while (1) {
m1 = l[++p]; // Take a card
m2 = l[++p]; // Take a card
print("\n");
print("YOU: " + a[m1] + "\tCOMPUTER: " + a[m2] + "\n");
n1 = Math.floor((m1 - 0.5) / 4);
n2 = Math.floor((m2 - 0.5) / 4);
if (n1 < n2) {
a1++;
print("THE COMPUTER WINS!!! YOU HAVE " + b1 + " AND THE COMPUTER HAS " + a1 + "\n");
} else if (n1 > n2) {
b1++;
print("YOU WIN. YOU HAVE " + b1 + " AND THE COMPUTER HAS " + a1 + "\n");
while (deck.length > 0 && shouldContinuePlaying) {
const playerCard = deck.shift(); // Take a card
const computerCard = deck.shift(); // Take a card
printCards(cards[playerCard], cards[computerCard]);
const playerCardValue = computeCardValue(playerCard);
const computerCardValue = computeCardValue(computerCard);
if (playerCardValue < computerCardValue) {
computerScore++;
print("THE COMPUTER WINS!!! YOU HAVE " + playerScore + " AND THE COMPUTER HAS " + computerScore + "\n");
} else if (playerCardValue > computerCardValue) {
playerScore++;
print("YOU WIN. YOU HAVE " + playerScore + " AND THE COMPUTER HAS " + computerScore + "\n");
} else {
print("TIE. NO SCORE CHANGE.\n");
}
if (l[p + 1] == 0) {
print("\n");
print("\n");
print("WE HAVE RUN OUT OF CARDS. FINAL SCORE: YOU: " + b1 + " THE COMPUTER: " + a1 + "\n");
print("\n");
break;
if (deck.length === 0) {
printGameOver(playerScore, computerScore);
}
while (1) {
print("DO YOU WANT TO CONTINUE");
str = await input();
if (str == "YES")
break;
if (str == "NO")
break;
print("YES OR NO, PLEASE. ");
else {
shouldContinuePlaying = await askYesOrNo("DO YOU WANT TO CONTINUE");
}
if (str == "NO")
break;
}
print("THANKS FOR PLAYING. IT WAS FUN.\n");
print("\n");