.
+
+This program is distributed in the hope that it will be useful, but
+without any warranty; without even the implied warranty of
+merchantability or fitness for a particular purpose.
+
+=cut
+
+# ex: set expandtab tabstop=4 textwidth=72 :
diff --git a/90_Tower/python/tower.py b/90_Tower/python/tower.py
new file mode 100644
index 00000000..5e4a25b7
--- /dev/null
+++ b/90_Tower/python/tower.py
@@ -0,0 +1,162 @@
+import sys
+
+class Disk:
+ def __init__(self, size):
+ self.__size = size
+
+ def size(self):
+ return self.__size
+
+ def print(self):
+ print("[ %s ]" % self.size())
+
+class Tower:
+ def __init__(self):
+ self.__disks = []
+
+ def empty(self):
+ return len(self.__disks) == 0
+
+ def top(self):
+ if self.empty():
+ return None
+ else:
+ return self.__disks[-1]
+
+ def add(self, disk):
+ if not self.empty():
+ t = self.top()
+ if disk.size() > t.size():
+ raise Exception("YOU CAN'T PLACE A LARGER DISK ON TOP OF A SMALLER ONE, IT MIGHT CRUSH IT!")
+ self.__disks.append(disk)
+
+ def pop(self):
+ if self.empty():
+ raise Exception("empty pop")
+ return self.__disks.pop()
+
+ def print(self):
+ r = "Needle: [%s]" % (", ".join([str(x.size()) for x in self.__disks]))
+ print(r)
+
+
+
+print("""
+IN THIS PROGRAM, WE SHALL REFER TO DISKS BY NUMERICAL CODE.
+3 WILL REPRESENT THE SMALLEST DISK, 5 THE NEXT SIZE,
+7 THE NEXT, AND SO ON, UP TO 15. IF YOU DO THE PUZZLE WITH
+2 DISKS, THEIR CODE NAMES WOULD BE 13 AND 15. WITH 3 DISKS
+THE CODE NAMES WOULD BE 11, 13 AND 15, ETC. THE NEEDLES
+ARE NUMBERED FROM LEFT TO RIGHT, 1 TO 3. WE WILL
+START WITH THE DISKS ON NEEDLE 1, AND ATTEMPT TO MOVE THEM
+TO NEEDLE 3.
+
+GOOD LUCK!
+
+""")
+
+
+
+
+class Game:
+ def __init__(self):
+ # use fewer sizes to make debugging easier
+ # self.__sizes = [3, 5, 7] # ,9,11,13,15]
+ self.__sizes = [3, 5, 7, 9, 11, 13, 15]
+
+ self.__sizes.sort()
+
+ self.__towers = []
+ self.__moves = 0
+ self.__towers = [Tower(), Tower(), Tower()]
+ self.__sizes.reverse()
+ for size in self.__sizes:
+ disk = Disk(size)
+ self.__towers[0].add(disk)
+
+ def winner(self):
+ return self.__towers[0].empty() and self.__towers[1].empty()
+
+ def print(self):
+ for t in self.__towers:
+ t.print()
+
+ def moves(self):
+ return self.__moves
+
+ def which_disk(self):
+ w = int(input("WHICH DISK WOULD YOU LIKE TO MOVE\n"))
+ if w in self.__sizes:
+ return w
+ else:
+ raise Exception()
+
+ def pick_disk(self):
+ which = None
+ while which is None:
+ try:
+ which = self.which_disk()
+ except:
+ print("ILLEGAL ENTRY... YOU MAY ONLY TYPE 3,5,7,9,11,13, OR 15.\n")
+
+ valids = [t for t in self.__towers if t.top() and t.top().size() == which]
+ assert len(valids) in (0, 1)
+ if not valids:
+ print("THAT DISK IS BELOW ANOTHER ONE. MAKE ANOTHER CHOICE.\n")
+ return None
+ else:
+ assert valids[0].top().size() == which
+ return valids[0]
+
+ def which_tower(self):
+ try:
+ needle = int(input("PLACE DISK ON WHICH NEEDLE\n"))
+ tower = self.__towers[needle - 1]
+ except:
+ print("I'LL ASSUME YOU HIT THE WRONG KEY THIS TIME. BUT WATCH IT,\nI ONLY ALLOW ONE MISTAKE.\n")
+ return None
+ else:
+ return tower
+
+ def take_turn(self):
+ from_tower = None
+ while from_tower is None:
+ from_tower = self.pick_disk()
+
+ to_tower = self.which_tower()
+ if not to_tower:
+ to_tower = self.which_tower()
+
+ if not to_tower:
+ print("I TRIED TO WARN YOU, BUT YOU WOULDN'T LISTEN.\nBYE BYE, BIG SHOT.\n")
+ sys.exit(0)
+
+ disk = from_tower.pop()
+ try:
+ to_tower.add( disk )
+ self.__moves += 1
+ except Exception as err:
+ print(err)
+ from_tower.add(disk)
+
+game = Game()
+while True:
+ game.print()
+
+ game.take_turn()
+
+ if game.winner():
+ print("CONGRATULATIONS!!\nYOU HAVE PERFORMED THE TASK IN %s MOVES.\n" % game.moves())
+ while True:
+ yesno = input("TRY AGAIN (YES OR NO)\n")
+ if yesno.upper() == "YES":
+ game = Game()
+ break
+ elif yesno.upper() == "NO":
+ print("THANKS FOR THE GAME!\n")
+ sys.exit(0)
+ else:
+ print("'YES' OR 'NO' PLEASE\n")
+ elif game.moves() > 128:
+ print("SORRY, BUT I HAVE ORDERS TO STOP IF YOU MAKE MORE THAN 128 MOVES.")
+ sys.exit(0)
diff --git a/90_Tower/python/tower_test.py b/90_Tower/python/tower_test.py
new file mode 100644
index 00000000..a7023a0f
--- /dev/null
+++ b/90_Tower/python/tower_test.py
@@ -0,0 +1,49 @@
+import unittest
+import tower
+
+class MyTestCase(unittest.TestCase):
+ def test_something(self):
+ t = tower.Tower()
+ self.assertTrue(t.empty())
+
+ d = tower.Disk(3)
+ t.add(d)
+ self.assertFalse(t.empty())
+
+ d5 = tower.Disk(5)
+ self.assertRaises(Exception, t.add, d5)
+ self.assertFalse(t.empty())
+
+ def test_oksize(self):
+ t = tower.Tower()
+ self.assertTrue(t.empty())
+
+ d5 = tower.Disk(5)
+ t.add(d5)
+ self.assertFalse(t.empty())
+
+ d3 = tower.Disk(3)
+ t.add(d3)
+ self.assertFalse(t.empty())
+
+ self.assertEqual(t.top(), d3)
+ self.assertEqual(t.pop(), d3)
+ self.assertEqual(t.pop(), d5)
+
+ def test_game(self):
+ g = tower.Game()
+ self.assertEqual(g.moves(), 0)
+ self.assertFalse(g.winner())
+
+ def test_format(self):
+ t = tower.Tower()
+ d3 = tower.Disk(3)
+ d5 = tower.Disk(5)
+ t.add(d5)
+ t.add(d3)
+
+ f = t.vertical_format(6, 3)
+ self.assertEqual(f, [' ', '[ 3 ] ', '[ 5 ] '])
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/91_Train/README.md b/91_Train/README.md
index c5a249fb..50e7cf6e 100644
--- a/91_Train/README.md
+++ b/91_Train/README.md
@@ -1,7 +1,16 @@
### Train
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=175
+TRAIN is a program which uses the computer to generate problems with random initial conditions to teach about the time-speed-distance relationship (distance = rate x time). You then input your answer and the computer verifies your response.
+
+TRAIN is merely an example of a student-generated problem. Maximum fun (and benefit) comes more from _writing_ programs like this as opposed to solving the specific problem posed. Exchange your program with others—you solve their problem and let them solve yours.
+
+TRAIN was originally written in FOCAL by one student for use by others in his class. It was submitted to us by Walt Koetke, Lexington High School, Lexington, Mass.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=175)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=190)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html
diff --git a/91_Train/csharp/Train/Train.sln b/91_Train/csharp/Train/Train.sln
new file mode 100644
index 00000000..7735a737
--- /dev/null
+++ b/91_Train/csharp/Train/Train.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.31129.286
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TrainGame", "Train\TrainGame.csproj", "{42617537-4E7C-4082-A17B-7F18DFA04C35}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TrainTests", "..\TrainTests\TrainTests\TrainTests.csproj", "{7C740A47-99C6-44E1-BDEE-140086BCFE8B}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {42617537-4E7C-4082-A17B-7F18DFA04C35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {42617537-4E7C-4082-A17B-7F18DFA04C35}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {42617537-4E7C-4082-A17B-7F18DFA04C35}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {42617537-4E7C-4082-A17B-7F18DFA04C35}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7C740A47-99C6-44E1-BDEE-140086BCFE8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7C740A47-99C6-44E1-BDEE-140086BCFE8B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7C740A47-99C6-44E1-BDEE-140086BCFE8B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7C740A47-99C6-44E1-BDEE-140086BCFE8B}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {919F73B8-DE34-4992-9B05-E1FEC2D2F7C6}
+ EndGlobalSection
+EndGlobal
diff --git a/91_Train/csharp/Train/Train/TrainGame.cs b/91_Train/csharp/Train/Train/TrainGame.cs
new file mode 100644
index 00000000..98e0db68
--- /dev/null
+++ b/91_Train/csharp/Train/Train/TrainGame.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Linq;
+
+namespace Train
+{
+ public class TrainGame
+ {
+ private Random Rnd { get; } = new Random();
+ private readonly int ALLOWED_PERCENTAGE_DIFFERENCE = 5;
+
+ static void Main()
+ {
+ TrainGame train = new TrainGame();
+ train.GameLoop();
+ }
+
+ public void GameLoop()
+ {
+ DisplayIntroText();
+
+ do
+ {
+ PlayGame();
+ } while (TryAgain());
+ }
+
+ private void PlayGame()
+ {
+ int carSpeed = (int)GenerateRandomNumber(40, 25);
+ int timeDifference = (int)GenerateRandomNumber(5, 15);
+ int trainSpeed = (int)GenerateRandomNumber(20, 19);
+
+ Console.WriteLine($"A CAR TRAVELING {carSpeed} MPH CAN MAKE A CERTAIN TRIP IN");
+ Console.WriteLine($"{timeDifference} HOURS LESS THAN A TRAIN TRAVELING AT {trainSpeed} MPH");
+ Console.WriteLine("HOW LONG DOES THE TRIP TAKE BY CAR?");
+
+ double userInputCarJourneyDuration = double.Parse(Console.ReadLine());
+ double actualCarJourneyDuration = CalculateCarJourneyDuration(carSpeed, timeDifference, trainSpeed);
+ int percentageDifference = CalculatePercentageDifference(userInputCarJourneyDuration, actualCarJourneyDuration);
+
+ if (IsWithinAllowedDifference(percentageDifference, ALLOWED_PERCENTAGE_DIFFERENCE))
+ {
+ Console.WriteLine($"GOOD! ANSWER WITHIN {percentageDifference} PERCENT.");
+ }
+ else
+ {
+ Console.WriteLine($"SORRY. YOU WERE OFF BY {percentageDifference} PERCENT.");
+ }
+ Console.WriteLine($"CORRECT ANSWER IS {actualCarJourneyDuration} HOURS.");
+ }
+
+ public static bool IsWithinAllowedDifference(int percentageDifference, int allowedDifference)
+ {
+ return percentageDifference <= allowedDifference;
+ }
+
+ private static int CalculatePercentageDifference(double userInputCarJourneyDuration, double carJourneyDuration)
+ {
+ return (int)(Math.Abs((carJourneyDuration - userInputCarJourneyDuration) * 100 / userInputCarJourneyDuration) + .5);
+ }
+
+ public static double CalculateCarJourneyDuration(double carSpeed, double timeDifference, double trainSpeed)
+ {
+ return timeDifference * trainSpeed / (carSpeed - trainSpeed);
+ }
+
+ public double GenerateRandomNumber(int baseSpeed, int multiplier)
+ {
+ return multiplier * Rnd.NextDouble() + baseSpeed;
+ }
+
+ private bool TryAgain()
+ {
+ Console.WriteLine("ANOTHER PROBLEM (YES OR NO)? ");
+ return IsInputYes(Console.ReadLine());
+ }
+
+ public static bool IsInputYes(string consoleInput)
+ {
+ var options = new string[] { "Y", "YES" };
+ return options.Any(o => o.Equals(consoleInput, StringComparison.CurrentCultureIgnoreCase));
+ }
+
+ private void DisplayIntroText()
+ {
+ Console.WriteLine("TRAIN");
+ Console.WriteLine("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
+ Console.WriteLine();
+ Console.WriteLine("TIME - SPEED DISTANCE EXERCISE");
+ Console.WriteLine();
+ }
+ }
+}
diff --git a/91_Train/csharp/Train/Train/TrainGame.csproj b/91_Train/csharp/Train/Train/TrainGame.csproj
new file mode 100644
index 00000000..c73e0d16
--- /dev/null
+++ b/91_Train/csharp/Train/Train/TrainGame.csproj
@@ -0,0 +1,8 @@
+
+
+
+ Exe
+ netcoreapp3.1
+
+
+
diff --git a/91_Train/csharp/TrainTests/TrainTests/TrainGameTests.cs b/91_Train/csharp/TrainTests/TrainTests/TrainGameTests.cs
new file mode 100644
index 00000000..a9804283
--- /dev/null
+++ b/91_Train/csharp/TrainTests/TrainTests/TrainGameTests.cs
@@ -0,0 +1,53 @@
+using Train;
+using Xunit;
+
+namespace TrainTests
+{
+ public class TrainGameTests
+ {
+ [Fact]
+ public void MiniumRandomNumber()
+ {
+ TrainGame game = new TrainGame();
+ Assert.True(game.GenerateRandomNumber(10, 10) >= 10);
+ }
+
+ [Fact]
+ public void MaximumRandomNumber()
+ {
+ TrainGame game = new TrainGame();
+ Assert.True(game.GenerateRandomNumber(10, 10) <= 110);
+ }
+
+ [Fact]
+ public void IsInputYesWhenY()
+ {
+ Assert.True(TrainGame.IsInputYes("y"));
+ }
+
+ [Fact]
+ public void IsInputYesWhenNotY()
+ {
+ Assert.False(TrainGame.IsInputYes("a"));
+ }
+
+ [Fact]
+ public void CarDurationTest()
+ {
+ Assert.Equal(1, TrainGame.CalculateCarJourneyDuration(30, 1, 15) );
+ }
+
+ [Fact]
+ public void IsWithinAllowedDifference()
+ {
+ Assert.True(TrainGame.IsWithinAllowedDifference(5,5));
+ }
+
+
+ [Fact]
+ public void IsNotWithinAllowedDifference()
+ {
+ Assert.False(TrainGame.IsWithinAllowedDifference(6, 5));
+ }
+ }
+}
diff --git a/91_Train/csharp/TrainTests/TrainTests/TrainTests.csproj b/91_Train/csharp/TrainTests/TrainTests/TrainTests.csproj
new file mode 100644
index 00000000..a8de6dde
--- /dev/null
+++ b/91_Train/csharp/TrainTests/TrainTests/TrainTests.csproj
@@ -0,0 +1,26 @@
+
+
+
+ netcoreapp3.1
+
+ false
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+
+
diff --git a/92_Trap/README.md b/92_Trap/README.md
index 7778ecb5..18d5db48 100644
--- a/92_Trap/README.md
+++ b/92_Trap/README.md
@@ -1,7 +1,19 @@
### Trap
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=176
+This is another in the family of “guess the mystery number” games. In TRAP the computer selects a random number between 1 and 100 (or other limit set). Your object is to find the number. On each guess, you enter 2 numbers trying to trap the mystery number between your two trap numbers. The computer will tell you if you have trapped the number.
+
+To win the game, you must guess the mystery number by entering it as the same value for both of your trap numbers. You get 6 guesses (this should be changed if you change the guessing limit).
+
+After you have played GUESS, STARS, and TRAP, compare the guessing strategy you have found best for each game. Do you notice any similarities? What are the differences? Can you write a new guessing game with still another approach?
+
+TRAP was suggested by a 10-year-old when he was playing GUESS. It was originally programmed by Steve Ullman and extensively modified into its final form by Bob Albrecht of People’s Computer Co.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=176)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=191)
+
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html
diff --git a/93_23_Matches/README.md b/93_23_Matches/README.md
index eb368c88..3e48212e 100644
--- a/93_23_Matches/README.md
+++ b/93_23_Matches/README.md
@@ -1,7 +1,18 @@
### 23 Matches
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=177
+In the game of twenty-three matches, you start with 23 matches lying on a table. On each turn, you may take 1, 2, or 3 matches. You alternate moves with the computer and the one who has to take the last match loses.
+
+The easiest way to devise a winning strategy is to start at the end of the game. Since your wish to leave the last match to your opponent, you would like to have either 4, 3, or 2 on your last turn you so can take away 3, 2, or 1 and leave 1. Consequently, you would like to leave your opponent with 5 on his next to last turn so, no matter what his move, you are left with 4, 3, or 2. Work this backwards to the beginning and you’ll find the game can effectively be won on the first move. Fortunately, the computer gives you the first move, so if you play wisely, you can win.
+
+After you’ve mastered 23 Matches, move on to BATNUM and then to NUM.
+
+This version of 23 Matches was originally written by Bob Albrecht of People’s Computer Company.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=177)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=192)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html
diff --git a/93_23_Matches/java/CoinSide.java b/93_23_Matches/java/CoinSide.java
new file mode 100644
index 00000000..7126e9b4
--- /dev/null
+++ b/93_23_Matches/java/CoinSide.java
@@ -0,0 +1,4 @@
+public enum CoinSide {
+ HEADS,
+ TAILS
+}
diff --git a/93_23_Matches/java/Messages.java b/93_23_Matches/java/Messages.java
new file mode 100644
index 00000000..c0e52b7c
--- /dev/null
+++ b/93_23_Matches/java/Messages.java
@@ -0,0 +1,70 @@
+public class Messages {
+
+ // This is a utility class and contains only static members.
+ // Utility classes are not meant to be instantiated.
+ private Messages() {
+ throw new IllegalStateException("Utility class");
+ }
+
+ public static final String INTRO = """
+ 23 MATCHES
+ CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
+
+
+
+ THIS IS A GAME CALLED '23 MATCHES'.
+
+ WHEN IT IS YOUR TURN, YOU MAY TAKE ONE, TWO, OR THREE
+ MATCHES. THE OBJECT OF THE GAME IS NOT TO HAVE TO TAKE
+ THE LAST MATCH.
+
+ LET'S FLIP A COIN TO SEE WHO GOES FIRST.
+ IF IT COMES UP HEADS, I WILL WIN THE TOSS.
+ """;
+
+ public static final String HEADS = """
+ HEADS! I WIN! HA! HA!
+ PREPARE TO LOSE, MEATBALL-NOSE!!
+
+ I TAKE 2 MATCHES
+ """;
+
+ public static final String TAILS = """
+ TAILS! YOU GO FIRST.
+ """;
+
+ public static final String MATCHES_LEFT = """
+ THE NUMBER OF MATCHES IS NOW %d
+
+ YOUR TURN -- YOU MAY TAKE 1, 2 OR 3 MATCHES.
+ """;
+
+ public static final String REMOVE_MATCHES_QUESTION = "HOW MANY DO YOU WISH TO REMOVE? ";
+
+ public static final String REMAINING_MATCHES = """
+ THERE ARE NOW %d MATCHES REMAINING.
+ """;
+
+ public static final String INVALID = """
+ VERY FUNNY! DUMMY!
+ DO YOU WANT TO PLAY OR GOOF AROUND?
+ NOW, HOW MANY MATCHES DO YOU WANT?
+ """;
+
+ public static final String WIN = """
+ YOU WON, FLOPPY EARS !
+ THINK YOU'RE PRETTY SMART !
+ LETS PLAY AGAIN AND I'LL BLOW YOUR SHOES OFF !!
+ """;
+
+ public static final String CPU_TURN = """
+ MY TURN ! I REMOVE %d MATCHES.
+ """;
+
+ public static final String LOSE = """
+ YOU POOR BOOB! YOU TOOK THE LAST MATCH! I GOTCHA!!
+ HA ! HA ! I BEAT YOU !!!
+
+ GOOD BYE LOSER!
+ """;
+}
diff --git a/93_23_Matches/java/TwentyThreeMatches.java b/93_23_Matches/java/TwentyThreeMatches.java
index 46b12793..f0c9836a 100644
--- a/93_23_Matches/java/TwentyThreeMatches.java
+++ b/93_23_Matches/java/TwentyThreeMatches.java
@@ -1,163 +1,79 @@
+import java.util.Random;
import java.util.Scanner;
-import java.lang.Math;
-/**
- * Game of 23 Matches
- *
- * Based on the BASIC game of 23 Matches here
- * https://github.com/coding-horror/basic-computer-games/blob/main/93%2023%20Matches/23matches.bas
- *
- * Note: The idea was to create a version of the 1970's BASIC game in Java, without introducing
- * new features - no additional text, error checking, etc has been added.
- *
- * Converted from BASIC to Java by Darren Cardenas.
- */
-
-public class TwentyThreeMatches {
+public class TwentyThreeMatches {
- private static final int MATCH_COUNT_START = 23;
+ private static final int MATCH_COUNT_START = 23;
+ private static final Random RAND = new Random();
+ private final Scanner scan = new Scanner(System.in);
- private static final int HEADS = 1;
-
- private final Scanner scan; // For user input
-
- public TwentyThreeMatches() {
-
- scan = new Scanner(System.in);
-
- } // End of constructor TwentyThreeMatches
+ public void startGame() {
+ //Initialize values
+ int cpuRemoves = 0;
+ int matchesLeft = MATCH_COUNT_START;
+ int playerRemoves = 0;
- public void play() {
-
- showIntro();
- startGame();
-
- } // End of method play
-
- private static void showIntro() {
-
- System.out.println(" ".repeat(30) + "23 MATCHES");
- System.out.println(" ".repeat(14) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
- System.out.println("\n\n");
+ //Flip coin and decide who goes first.
+ CoinSide coinSide = flipCoin();
+ if (coinSide == CoinSide.HEADS) {
+ System.out.println(Messages.HEADS);
+ matchesLeft -= 2;
+ } else {
+ System.out.println(Messages.TAILS);
+ }
- System.out.println(" THIS IS A GAME CALLED '23 MATCHES'.");
- System.out.println("");
- System.out.println("WHEN IT IS YOUR TURN, YOU MAY TAKE ONE, TWO, OR THREE");
- System.out.println("MATCHES. THE OBJECT OF THE GAME IS NOT TO HAVE TO TAKE");
- System.out.println("THE LAST MATCH.");
- System.out.println("");
- System.out.println("LET'S FLIP A COIN TO SEE WHO GOES FIRST.");
- System.out.println("IF IT COMES UP HEADS, I WILL WIN THE TOSS.");
- System.out.println("");
-
- } // End of method showIntro
-
- private void startGame() {
+ // Game loop
+ while (true) {
+ //Show matches left if CPU went first or Player already removed matches
+ if (coinSide == CoinSide.HEADS) {
+ System.out.format(Messages.MATCHES_LEFT, matchesLeft);
+ }
+ coinSide = CoinSide.HEADS;
- int coinSide = (int) (3 * Math.random());
- int cpuRemoves = 0;
- int matchesLeft = MATCH_COUNT_START;
- int playerRemoves = 0;
-
- if (coinSide == HEADS) {
-
- System.out.println("HEADS! I WIN! HA! HA!");
- System.out.println("PREPARE TO LOSE, MEATBALL-NOSE!!");
- System.out.println("");
- System.out.println("I TAKE 2 MATCHES");
-
- matchesLeft -= 2;
-
- } else {
-
- System.out.println("TAILS! YOU GO FIRST. ");
- System.out.println("");
-
+ // Player removes matches
+ System.out.println(Messages.REMOVE_MATCHES_QUESTION);
+ playerRemoves = turnOfPlayer();
+ matchesLeft -= playerRemoves;
+ System.out.format(Messages.REMAINING_MATCHES, matchesLeft);
+
+ // If 1 match is left, the CPU has to take it. You win!
+ if (matchesLeft <= 1) {
+ System.out.println(Messages.WIN);
+ return;
+ }
+
+ // CPU removes matches
+ // At least two matches are left, because win condition above was not triggered.
+ if (matchesLeft <= 4) {
+ cpuRemoves = matchesLeft - 1;
+ } else {
+ cpuRemoves = 4 - playerRemoves;
+ }
+ System.out.format(Messages.CPU_TURN, cpuRemoves);
+ matchesLeft -= cpuRemoves;
+
+ // If 1 match is left, the Player has to take it. You lose!
+ if (matchesLeft <= 1) {
+ System.out.println(Messages.LOSE);
+ return;
+ }
+ }
}
- // Begin outer while loop
- while (true) {
-
- if (coinSide == HEADS) {
-
- System.out.println("THE NUMBER OF MATCHES IS NOW " + matchesLeft);
- System.out.println("");
- System.out.println("YOUR TURN -- YOU MAY TAKE 1, 2 OR 3 MATCHES.");
-
- }
-
- coinSide = HEADS;
+ private CoinSide flipCoin() {
+ return RAND.nextBoolean() ? CoinSide.HEADS : CoinSide.TAILS;
+ }
- System.out.print("HOW MANY DO YOU WISH TO REMOVE? ");
-
- // Begin match removal while loop
- while (true) {
-
- playerRemoves = scan.nextInt();
-
- // Handle invalid entries
- if ((playerRemoves > 3) || (playerRemoves <= 0)) {
-
- System.out.println("VERY FUNNY! DUMMY!");
- System.out.println("DO YOU WANT TO PLAY OR GOOF AROUND?");
- System.out.print("NOW, HOW MANY MATCHES DO YOU WANT? ");
- continue;
-
+ private int turnOfPlayer() {
+ while (true) {
+ int playerRemoves = scan.nextInt();
+ // Handle invalid entries
+ if ((playerRemoves > 3) || (playerRemoves <= 0)) {
+ System.out.println(Messages.INVALID);
+ continue;
+ }
+ return playerRemoves;
}
-
- break;
-
- } // End match removal while loop
-
- matchesLeft -= playerRemoves;
-
- System.out.println("THERE ARE NOW " + matchesLeft + " MATCHES REMAINING.");
-
- // Win condition
- if (matchesLeft <= 1) {
-
- // Win condition
- System.out.println("YOU WON, FLOPPY EARS !");
- System.out.println("THINK YOU'RE PRETTY SMART !");
- System.out.println("LETS PLAY AGAIN AND I'LL BLOW YOUR SHOES OFF !!");
- System.out.println("");
- return;
-
- } else if ((matchesLeft >= 2) && (matchesLeft <= 4)) {
-
- cpuRemoves = matchesLeft - 1;
-
- } else {
-
- cpuRemoves = 4 - playerRemoves;
-
- }
-
- System.out.println("MY TURN ! I REMOVE " + cpuRemoves + " MATCHES");
-
- matchesLeft -= cpuRemoves;
+ }
- // Lose condition
- if (matchesLeft <= 1) {
-
- System.out.println("");
- System.out.println("YOU POOR BOOB! YOU TOOK THE LAST MATCH! I GOTCHA!!");
- System.out.println("HA ! HA ! I BEAT YOU !!!");
- System.out.println("");
- System.out.println("GOOD BYE LOSER!");
- return;
-
- }
-
- } // End outer while loop
-
- } // End of method startGame
-
- public static void main(String[] args) {
-
- TwentyThreeMatches game = new TwentyThreeMatches();
- game.play();
-
- } // End of method main
-
-} // End of class TwentyThreeMatches
+}
diff --git a/93_23_Matches/java/TwentyThreeMatchesGame.java b/93_23_Matches/java/TwentyThreeMatchesGame.java
new file mode 100644
index 00000000..01735a91
--- /dev/null
+++ b/93_23_Matches/java/TwentyThreeMatchesGame.java
@@ -0,0 +1,24 @@
+/**
+ * Game of 23 Matches
+ *
+ * Based on the BASIC game of 23 Matches here
+ * https://github.com/coding-horror/basic-computer-games/blob/main/93%2023%20Matches/23matches.bas
+ *
+ * Note: The idea was to create a version of the 1970's BASIC game in Java, without introducing
+ * new features - no additional text, error checking, etc has been added.
+ *
+ * Converted from BASIC to Java by Darren Cardenas.
+ */
+public class TwentyThreeMatchesGame {
+
+ public static void main(String[] args) {
+ showIntro();
+ TwentyThreeMatches game = new TwentyThreeMatches();
+ game.startGame();
+ }
+
+ private static void showIntro() {
+ System.out.println(Messages.INTRO);
+ }
+
+}
diff --git a/93_23_Matches/perl/23matches.pl b/93_23_Matches/perl/23matches.pl
new file mode 100644
index 00000000..c575d9cb
--- /dev/null
+++ b/93_23_Matches/perl/23matches.pl
@@ -0,0 +1,86 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+print ' ' x 31 . "23 MATCHES\n";
+print ' ' x 15 . "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n";
+print "\n\n\n";
+
+print " THIS IS A GAME CALLED '23 MATCHES'.\n\n";
+
+print "WHEN IT IS YOUR TURN, YOU MAY TAKE ONE, TWO, OR THREE\n";
+print "MATCHES. THE OBJECT OF THE GAME IS NOT TO HAVE TO TAKE\n";
+print "THE LAST MATCH.\n\n";
+
+print "LET'S FLIP A COIN TO SEE WHO GOES FIRST.\n";
+print "IF IT COMES UP HEADS, I WILL WIN THE TOSS.\n\n";
+
+my $N = 23;
+my $Q = int( 2 * rand(5) );
+
+if ( $Q == 1 ) {
+ print "HEADS! I WIN! HA! HA!\n";
+ print "PREPARE TO LOSE, MEATBALL-NOSE!!\n\n";
+
+ print "I TAKE 2 MATCHES\n";
+ $N -= 2;
+
+ print "THE NUMBER OF MATCHES IS NOW $N\n\n";
+
+ print "YOUR TURN -- YOU MAY TAKE 1, 2 OR 3 MATCHES.\n";
+}
+else {
+ print "TAILS! YOU GO FIRST.\n\n";
+}
+
+print "HOW MANY DO YOU WISH TO REMOVE?\n";
+
+INPUT:
+{
+ chomp( my $K = );
+
+ if ( $K > 3 or $K <= 0 ) {
+ print "VERY FUNNY! DUMMY!\n";
+ print "DO YOU WANT TO PLAY OR GOOF AROUND?\n";
+ print "NOW, HOW MANY MATCHES DO YOU WANT?\n";
+ redo INPUT;
+ }
+
+ $N -= $K;
+
+ print "THERE ARE NOW $N MATCHES REMAINING.\n";
+
+ my $Z;
+
+ if ( $N <= 1 ) {
+ print "YOU WON, FLOPPY EARS!\n";
+ print "THINK YOU'RE PRETTY SMART!\n";
+ print "LET'S PLAY AGAIN AND I'LL BLOW YOUR SHOES OFF!!\n";
+ exit;
+ }
+ elsif ( $N > 4 ) {
+ $Z = 4 - $K;
+ }
+ else {
+ $Z = $N - 1;
+ }
+
+ print "MY TURN! I REMOVE $Z MATCHES\n";
+
+ $N -= $Z;
+
+ if ( $N <= 1 ) {
+ print "\nYOU POOR BOOB! YOU TOOK THE LAST MATCH! I GOTCHA!!\n";
+ print "HA! HA! I BEAT YOU!!!\n\n";
+
+ print "GOOD BYE LOSER!\n";
+ }
+ else {
+ print "THE NUMBER OF MATCHES IS NOW $N\n\n";
+
+ print "YOUR TURN -- YOU MAY TAKE 1, 2 OR 3 MATCHES.\n";
+ print "HOW MANY DO YOU WISH TO REMOVE?\n";
+ redo INPUT;
+ }
+}
diff --git a/94_War/README.md b/94_War/README.md
index 4345c4ac..a850f73e 100644
--- a/94_War/README.md
+++ b/94_War/README.md
@@ -1,7 +1,14 @@
### War
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=178
+This program plays the card game of War. In War, the card deck is shuffled, then two cards are dealt, one to each player. Players compare cards and the higher card (numerically) wins. In case of a tie, no one wins. The game ends when you have gone through the whole deck (52 cards, 26 games) or when you decide to quit.
+
+The computer gives cards by suit and number, for example, S-7 is the 7 of spades.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=178)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=193)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html
diff --git a/94_War/kotlin/README.md b/94_War/kotlin/README.md
new file mode 100644
index 00000000..f43a5b70
--- /dev/null
+++ b/94_War/kotlin/README.md
@@ -0,0 +1,3 @@
+Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
+
+Conversion to [Kotlin](https://kotlinlang.org/)
diff --git a/94_War/kotlin/War.kt b/94_War/kotlin/War.kt
new file mode 100644
index 00000000..0b8b9ee3
--- /dev/null
+++ b/94_War/kotlin/War.kt
@@ -0,0 +1,108 @@
+/**
+ * Converted FROM BASIC to Kotlin by John Long, with hints from the Java by Nahid Mondol.
+ *
+ */
+
+val suits = listOf("S", "H", "C", "D")
+val ranks = listOf("2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A")
+
+// Create the deck programmatically
+val fullDeck = suits.flatMap { suit ->
+ ranks.map { rank ->
+ Card(suit, rank)
+ }
+}
+
+class Card(private val suit: String, private val rank: String) {
+ // Allow comparison of cards to each other
+ operator fun compareTo(other: Card): Int = this.rankValue.compareTo(other.rankValue)
+ // We can figure relative rank by the order in the ranks value
+ private val rankValue: Int = ranks.indexOf(rank)
+ override fun toString(): String = "$suit-$rank"
+}
+
+fun main() {
+ introMessage()
+ showDirectionsBasedOnInput()
+ playGame()
+ println("THANKS FOR PLAYING. IT WAS FUN.")
+}
+
+private fun introMessage() {
+ println("\t WAR")
+ println("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY")
+ println("THIS IS THE CARD GAME OF WAR. EACH CARD IS GIVEN BY SUIT-#")
+ print("AS S-7 FOR SPADE 7. DO YOU WANT DIRECTIONS? ")
+}
+
+private fun showDirectionsBasedOnInput() {
+ if (getYesOrNo()) {
+ println("THE COMPUTER GIVES YOU AND IT A 'CARD'. THE HIGHER CARD")
+ println("(NUMERICALLY) WINS. THE GAME ENDS WHEN YOU CHOOSE NOT TO ")
+ println("CONTINUE OR WHEN YOU HAVE FINISHED THE PACK.\n")
+ }
+}
+
+// Stay in loop until player chooses an option, then return "true" for yes or "false" for no
+private fun getYesOrNo() = generateSequence { readln() }.firstNotNullOf { it.asYesOrNo }
+
+// Since this returns null for an incorrect value, above firstNotNullOf will keep looping until
+// we get something valid
+private val String.asYesOrNo: Boolean?
+ get() =
+ when (this.lowercase()) {
+ "yes" -> true
+ "no" -> false
+ else -> {
+ println("YES OR NO, PLEASE. ")
+ null
+ }
+ }
+
+
+private fun playGame() {
+ // Shuffle the deck than break it into 26 pairs
+ val pairs = fullDeck.shuffled().chunked(2)
+ val score = Score(0, 0)
+ val lastPlayerCard = pairs.last().first()
+ // We use "destructuring" to extract the pair of cards directly to a variable here
+ pairs.forEach { (playerCard, computerCard) ->
+ println("YOU: $playerCard\tCOMPUTER: $computerCard")
+ when {
+ playerCard > computerCard -> score.playerWins()
+ computerCard > playerCard -> score.computerWins()
+ else -> println("TIE. NO SCORE CHANGE.")
+ }
+ // Doesn't make sense to ask to continue if we have no more cards left to deal
+ if (playerCard != lastPlayerCard) {
+ println("DO YOU WANT TO CONTINUE")
+ if (!getYesOrNo()) {
+ return
+ }
+ }
+ }
+ score.printFinalScore()
+ return
+}
+
+
+class Score(private var player: Int, private var computer: Int) {
+ fun playerWins() {
+ player++
+ printScore("YOU WIN.")
+ }
+
+ fun computerWins() {
+ computer++
+ printScore("THE COMPUTER WINS!!!")
+ }
+
+ private fun printScore(text: String) {
+ println("$text YOU HAVE $player AND THE COMPUTER HAS $computer")
+ }
+
+ // Only print if you go through the whole deck
+ fun printFinalScore() {
+ println("WE HAVE RUN OUT OF CARDS. FINAL SCORE: YOU: $player THE COMPUTER:$computer")
+ }
+}
diff --git a/94_War/perl/war.pl b/94_War/perl/war.pl
new file mode 100755
index 00000000..367a1185
--- /dev/null
+++ b/94_War/perl/war.pl
@@ -0,0 +1,153 @@
+#!/usr/bin/env perl
+
+use 5.010; # To get 'say'
+
+use strict; # Require explicit declaration of variables
+use warnings; # Enable optional compiler warnings
+
+use English; # Use more friendly names for Perl's magic variables
+use Getopt::Long 2.33 qw{ :config auto_version };
+use List::Util qw{ shuffle };
+use Pod::Usage;
+use Term::ReadLine; # Prompt and return user input
+
+our $VERSION = '0.000_01';
+
+my %opt;
+
+GetOptions( \%opt,
+ qw{ unicode! },
+ help => sub { pod2usage( { -verbose => 2 } ) },
+) or pod2usage( { -verbose => 0 } );
+
+my @cards;
+my $current_rank_value = 1;
+my %rank_value;
+my @suits = $opt{unicode} ?
+ ( map { chr } 0x2660 .. 0x2663 ) :
+ ( qw{ S H D C } );
+foreach my $rank ( ( 2 .. 10 ), qw{ J Q K A } ) {
+ $rank_value{$rank} = $current_rank_value++;
+ foreach my $suit ( @suits ) {
+ push @cards, "$suit-$rank";
+ }
+}
+
+$opt{unicode}
+ and binmode STDOUT, ':encoding(utf-8)';
+
+@cards = shuffle( @cards );
+
+print <<'EOD';
+ WAR
+ Creative Computing Morristown, New Jersey
+
+
+
+This is the card game of War. Each card is given by suit-#
+EOD
+
+# Create the readline object.
+state $term = Term::ReadLine->new( 'word' );
+
+my $resp = $term->readline(
+ "as $suits[0]-7 for Spade 7. Do you want directions? [y/N]: " );
+exit unless defined $resp;
+if ( $resp =~ m/ \A y /smxi ) {
+ print <<'EOD';
+The computer gives you and it a 'card'. The higher card
+(numerically) wins. The game ends when you choose not to
+continue or when you have finished the pack.
+
+EOD
+}
+
+my $your_score = my $computer_score = 0;
+
+while ( 1 ) {
+ my ( $you, $computer ) = splice @cards, 0, 2;
+ say '';
+ say "You: $you; computer: $computer";
+ my $result = $rank_value{ substr $you, 2 } <=>
+ $rank_value{ substr $computer, 2 };
+ if ( $result < 0 ) {
+ $computer_score++;
+ say "The computer wins!!! ",
+ "You have $your_score and the computer has $computer_score";
+ } elsif ( $result > 0 ) {
+ $your_score++;
+ say "You win. ",
+ "You have $your_score and the computer has $computer_score";
+ } else {
+ say 'Tie. No score change.';
+ }
+
+ last unless @cards;
+
+ $resp = $term->readline( 'Do you want to continue? [Y/n]: ' );
+ last unless defined $resp;
+ last if $resp =~ m/ \A n /smxi;
+}
+
+say "We have run out of cards. ",
+ "Final score: you: $your_score; the computer: $computer_score"
+ unless @cards;
+say '';
+say 'Thanks for playing. It was fun.';
+__END__
+
+=head1 TITLE
+
+war.pl - Play the game 'War' from Basic Computer Games
+
+=head1 SYNOPSIS
+
+ war.pl
+ war.pl --help
+ war.pl --version
+
+=head1 OPTIONS
+
+=head2 --help
+
+This option displays the documentation for this script. The script then
+exits.
+
+=head2 --unicode
+
+If this Boolean option is asserted, the suits are designated by their
+Unicode glyphs rather than by ASCII letters. For these to display
+properly your terminal must properly interpret Unicode.
+
+The default is C<--no-unicode>.
+
+=head2 --version
+
+This option displays the version of this script. The script then exits.
+
+=head1 DETAILS
+
+This Perl script is a port of C, which is the 94th entry in Basic
+Computer Games.
+
+=head1 PORTED BY
+
+Thomas R. Wyant, III F
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2022 by Thomas R. Wyant, III
+
+This program is free software; you can redistribute it and/or modify it
+under the same terms as Perl 5.10.0. For more details, see the Artistic
+License 1.0 at
+L, and/or the
+Gnu GPL at L.
+
+This program is distributed in the hope that it will be useful, but
+without any warranty; without even the implied warranty of
+merchantability or fitness for a particular purpose.
+
+=cut
+
+# ex: set expandtab tabstop=4 textwidth=72 :
diff --git a/95_Weekday/README.md b/95_Weekday/README.md
index 16f825a3..445c2c4b 100644
--- a/95_Weekday/README.md
+++ b/95_Weekday/README.md
@@ -1,7 +1,16 @@
### Weekday
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=179
+This program gives facts about your date of birth (or some other day of interest). It is not prepared to give information on people born before the use of the current type of calendar, i.e. year 1582.
+
+You merely enter today’s date in the form—month, day, year and your date of birth in the same form. The computer then tells you the day of the week of your birth date, your age, and how much time you have spent sleeping, eating, working, and relaxing.
+
+This program was adapted from a GE timesharing program by Tom Kloos at the Oregon Museum of Science and Industry.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=179)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=194)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html
diff --git a/96_Word/README.md b/96_Word/README.md
index 404135e8..8db22c6c 100644
--- a/96_Word/README.md
+++ b/96_Word/README.md
@@ -1,7 +1,16 @@
### Word
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=181
+WORD is a combination of HANGMAN and BAGELS. In this game, the player must guess a word with clues as to a letter position furnished by the computer. However, instead of guessing one letter at a time, in WORD you guess an entire word (or group of 5 letters, such as ABCDE). The computer will tell you if any letters that you have guessed are in the mystery word and if any of them are in the correct position. Armed with these clues, you go on guessing until you get the word or, if you can’t get it, input a “?” and the computer will tell you the mystery word.
+
+You may change the words in Data Statements, but they must be 5-letter words.
+
+The author of this program is Charles Reid of Lexington High School, Lexington, Massachusetts.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=181)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=194)
Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html
diff --git a/96_Word/javascript/word.js b/96_Word/javascript/word.js
index 6e5acf50..4df5d3fd 100644
--- a/96_Word/javascript/word.js
+++ b/96_Word/javascript/word.js
@@ -12,10 +12,10 @@ 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");
@@ -61,7 +61,7 @@ async function main()
print("\n");
print("\n");
print("I AM THINKING OF A WORD -- YOU GUESS IT. I WILL GIVE YOU\n");
- print("CLUE TO HELP YO GET IT. GOOD LUCK!!\n");
+ print("CLUES TO HELP YOU GET IT. GOOD LUCK!!\n");
print("\n");
print("\n");
while (1) {
diff --git a/96_Word/perl/word.pl b/96_Word/perl/word.pl
new file mode 100755
index 00000000..5369cc71
--- /dev/null
+++ b/96_Word/perl/word.pl
@@ -0,0 +1,169 @@
+#!/usr/bin/env perl
+
+use 5.010; # To get 'state' and 'say'
+
+use strict; # Require explicit declaration of variables
+use warnings; # Enable optional compiler warnings
+
+use English; # Use more friendly names for Perl's magic variables
+use Term::ReadLine; # Prompt and return user input
+
+our $VERSION = '0.000_01';
+
+print <<'EOD';
+ WORD
+ Creative Computing Morristown, New Jersey
+
+
+
+I am thinking of a word -- you guess it. I will give you
+clues to help you get it. Good luck!!
+
+
+EOD
+
+# Read the content of __DATA__, remove the trailing newlines, and store
+# each line into @words. Stop at __END__, since Perl does not see this
+# as an end-of-file.
+my @words;
+while ( ) {
+ chomp;
+ last if $ARG eq '__END__';
+ push @words, lc $ARG; # Normalize case to lower.
+}
+
+# This loop represents an actual game. We execute it until the player
+# does something that makes us explicitly break out.
+while ( 1 ) {
+ print <<'EOD';
+
+
+You are starting a new game ...
+EOD
+
+ # Choose a random target word. The rand() function returns a number
+ # from 0 to its argument, and coerces its argument to a scalar. In
+ # scalar context, an array evaluates to the number of elements it
+ # contains.
+ my $target = $words[ rand @words ];
+
+ # We generalize the code by using the actual length of the target.
+ my $target_length = length $target;
+
+ my $count = 0; # Number of guesses
+
+ # Make an array of the individual letters in the target. We will
+ # iterate over this to determine matching letters.
+ my @target_array = split qr<>, $target;
+
+ # Make a hash of those letters. We will use this to determine common
+ # letters. Any true value will do for the value of the hash. By
+ # making use of this hash we avoid the nested loops of the original
+ # BASIC program.
+ my %target_hash = map { $ARG => 1 } @target_array;
+
+ # We keep prompting the player until we get a response that causes
+ # us to break out of the loop.
+ while ( 1 ) {
+
+ # Create the readline object. The state keyword means the
+ # variable is only initialized once, no matter how many times
+ # execution passes this point.
+ state $term = Term::ReadLine->new( 'word' );
+
+ # Read the next guess. A return of undef means end-of-file.
+ my $guess = $term->readline( "Guess a $target_length letter word: " );
+ exit unless defined $guess;
+
+ last if $guess eq '?'; # A question mark means we give up
+ if ( length( $guess ) != $target_length ) {
+ # Wrong length. Ask again.
+ say "You must guess a $target_length letter word. Try again.";
+ redo; # Redo the innermost loop
+ }
+
+ $guess = lc $guess; # Lower-case the guess
+ $count++; # Count another guess
+
+ if ( $guess eq $target ) {
+ # We guessed the word.
+ say "You have guessed the word. It took $count guesses!";
+ my $answer = $term->readline( 'Want to play again? [y/N]: ');
+ exit unless defined $guess; # End of file
+ exit unless $guess =~ m/ \A y /smxi;
+ last; # Exit the innermost loop.
+ }
+
+ my @common_letters; # Letters common to guess and target
+ my $match = '-' x length $target; # Assume no matches
+ my $inx = 0; # Iterator
+ foreach my $letter ( split qr<>, $guess ) {
+ if ( $target_hash{$letter} ) {
+ # If the letter is in the hash, it occurs in the target
+ push @common_letters, $letter;
+ # If it is at the current position in the target, it is
+ # an actual match.
+ $target_array[$inx] eq $letter
+ and substr $match, $inx, 1, $letter;
+ }
+ $inx++;
+ }
+
+ say 'There were ', scalar @common_letters,
+ ' matches and the common letters were... ', @common_letters;
+ say "From the exact letter matches, you know................ $match";
+ say '';
+ say q;
+ redo;
+ }
+
+}
+__DATA__
+dinky
+smoke
+water
+grass
+train
+might
+first
+candy
+champ
+would
+clump
+dopey
+__END__
+
+=head1 TITLE
+
+word.pl - Play the game 'word' from Basic Computer Games
+
+=head1 SYNOPSIS
+
+ word.pl
+
+=head1 DETAILS
+
+This Perl script is a port of C, which is the 96th entry in Basic
+Computer Games.
+
+=head1 PORTED BY
+
+Thomas R. Wyant, III F
+
+=head1 COPYRIGHT AND LICENSE
+
+Copyright (C) 2022 by Thomas R. Wyant, III
+
+This program is free software; you can redistribute it and/or modify it
+under the same terms as Perl 5.10.0. For more details, see the Artistic
+License 1.0 at
+L, and/or the
+Gnu GPL at L.
+
+This program is distributed in the hope that it will be useful, but
+without any warranty; without even the implied warranty of
+merchantability or fitness for a particular purpose.
+
+=cut
+
+# ex: set expandtab tabstop=4 textwidth=72 :
diff --git a/README.md b/README.md
index d481611c..266aaac8 100644
--- a/README.md
+++ b/README.md
@@ -42,3 +42,5 @@ Feel free to begin converting these classic games into the above list of modern,
### Have fun!
Thank you for taking part in this project to update a classic programming book -- one of the most influential programming books in computing history -- for 2022 and beyond!
+
+NOTE: per [the official blog post announcement](https://blog.codinghorror.com/updating-the-single-most-influential-book-of-the-basic-era/), I will be **donating $5 for each contributed program in the 8 agreed upon languages to [Girls Who Code](https://girlswhocode.com/)**.