diff --git a/01_Acey_Ducey/javascript/.prettierrc.json b/01_Acey_Ducey/javascript/.prettierrc.json new file mode 100644 index 00000000..f9148847 --- /dev/null +++ b/01_Acey_Ducey/javascript/.prettierrc.json @@ -0,0 +1,6 @@ +{ + "trailingComma": "es5", + "tabWidth": 4, + "semi": true, + "singleQuote": true +} diff --git a/01_Acey_Ducey/javascript/aceyducey.html b/01_Acey_Ducey/javascript/aceyducey.html index 991e3b5c..61c362a7 100644 --- a/01_Acey_Ducey/javascript/aceyducey.html +++ b/01_Acey_Ducey/javascript/aceyducey.html @@ -1,9 +1,7 @@ - - + + + ACEY DUCEY - - -

+
+

 
-
-
diff --git a/01_Acey_Ducey/javascript/aceyducey.js b/01_Acey_Ducey/javascript/aceyducey.js
index d0983443..8bf5e9e1 100644
--- a/01_Acey_Ducey/javascript/aceyducey.js
+++ b/01_Acey_Ducey/javascript/aceyducey.js
@@ -1,135 +1,216 @@
-// ACEY DUCEY
-//
-// Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
-//
+// UTILITY VARIABLES
 
-function print(str)
-{
-    document.getElementById("output").appendChild(document.createTextNode(str));
+// By default:
+// — Browsers have a window object
+// — Node.js does not
+// Checking for an undefined window object is a loose check
+// to enable browser and Node.js support
+const isRunningInBrowser = typeof window !== 'undefined';
+
+// To easily validate input strings with utility functions
+const validLowerCaseYesStrings = ['yes', 'y'];
+const validLowerCaseNoStrings = ['no', 'n'];
+const validLowerCaseYesAndNoStrings = [
+    ...validLowerCaseYesStrings,
+    ...validLowerCaseNoStrings,
+];
+// UTILITY VARIABLES
+
+// Function to get a random number (card) 2-14 (ACE is 14)
+function getRandomCard() {
+    // In our game, the value of ACE is greater than face cards;
+    // instead of having the value of ACE be 1, we’ll have it be 14.
+    // So, we want to shift the range of random numbers from 1-13 to 2-14
+    let min = 2;
+    let max = 14;
+    // Return random integer between two values, inclusive
+    return Math.floor(Math.random() * (max - min + 1) + min);
 }
 
-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 newGameCards() {
+    let cardOne = getRandomCard();
+    let cardTwo = getRandomCard();
+    let cardThree = getRandomCard();
+    // We want:
+    // 1. cardOne and cardTwo to be different cards
+    // 2. cardOne to be lower than cardTwo
+    // So, while cardOne is greater than or equal too cardTwo
+    // we will continue to generate random cards.
+    while (cardOne >= cardTwo) {
+        cardOne = getRandomCard();
+        cardTwo = getRandomCard();
+    }
+    return [cardOne, cardTwo, cardThree];
 }
 
-function tab(space)
-{
-    var str = "";
-    while (space-- > 0)
-        str += " ";
-    return str;
+// Function to get card value
+function getCardValue(card) {
+    let faceOrAce = {
+        11: 'JACK',
+        12: 'QUEEN',
+        13: 'KING',
+        14: 'ACE',
+    };
+    // If card value matches a key in faceOrAce, use faceOrAce value;
+    // Else, return undefined and handle with the Nullish Coalescing Operator (??)
+    // and default to card value.
+    let cardValue = faceOrAce[card] ?? card;
+    return cardValue;
 }
 
-print(tab(26) + "ACEY DUCEY CARD GAME\n");
-print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
-print("\n");
-print("\n");
-print("ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER\n");
-print("THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP\n");
-print("YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING\n");
-print("ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE\n");
-print("A VALUE BETWEEN THE FIRST TWO.\n");
-print("IF YOU DO NOT WANT TO BET, INPUT A 0\n");
+print(spaces(26) + 'ACEY DUCEY CARD GAME');
+print(spaces(15) + 'CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n\n');
+print('ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER');
+print('THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP');
+print('YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING');
+print('ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE');
+print('A VALUE BETWEEN THE FIRST TWO.');
+print("IF YOU DO NOT WANT TO BET, INPUT '0'");
 
-function show_card(card)
-{
-    if (card < 11)
-        print(card + "\n");
-    else if (card == 11)
-        print("JACK\n");
-    else if (card == 12)
-        print("QUEEN\n");
-    else if (card == 13)
-        print("KING\n");
-    else
-        print("ACE\n");
-}
+main();
 
-// Main program
-async function main()
-{
-    q = 100;
-    while (1) {
-        print("YOU NOW HAVE " + q + " DOLLARS.\n");
-        print("\n");
-        
-        do {
-            print("HERE ARE YOUR NEXT TWO CARDS: \n");
-            do {
-                a = Math.floor(Math.random() * 13 + 2);
-                b = Math.floor(Math.random() * 13 + 2);
-            } while (a >= b) ;
-            show_card(a);
-            show_card(b);
-            print("\n");
-            while (1) {
-                print("\n");
-                print("WHAT IS YOUR BET");
-                m = parseInt(await input());
-                if (m > 0) {
-                    if (m > q) {
-                        print("SORRY, MY FRIEND, BUT YOU BET TOO MUCH.\n");
-                        print("YOU HAVE ONLY " + q + "DOLLARS TO BET.\n");
-                        continue;
-                    }
-                    break;
-                }
-                m = 0;
-                print("CHICKEN!!\n");
-                print("\n");
-                break;
-            }
-        } while (m == 0) ;
-        c = Math.floor(Math.random() * 13 + 2);
-        show_card(c);
-        if (c > a && c < b) {
-            print("YOU WIN!!!\n");
-            q = q + m;
-        } else {
-            print("SORRY, YOU LOSE\n");
-            if (m >= q) {
-                print("\n");
-                print("\n");
-                print("SORRY, FRIEND, BUT YOU BLEW YOUR WAD.\n");
-                print("\n");
-                print("\n");
-                print("TRY AGAIN (YES OR NO)");
-                a = await input();
-                print("\n");
-                print("\n");
-                if (a == "YES") {
-                    q = 100;
+async function main() {
+    let bet;
+    let availableDollars = 100;
+
+    // Loop game forever
+    while (true) {
+        let [cardOne, cardTwo, cardThree] = newGameCards();
+
+        print(`YOU NOW HAVE ${availableDollars} DOLLARS.\n`);
+
+        print('HERE ARE YOUR NEXT TWO CARDS: ');
+        print(getCardValue(cardOne));
+        print(getCardValue(cardTwo));
+        print('');
+
+        // Loop until receiving a valid bet
+        let validBet = false;
+        while (!validBet) {
+            print('\nWHAT IS YOUR BET? ');
+            bet = parseInt(await input(), 10);
+            let minimumRequiredBet = 0;
+            if (bet > minimumRequiredBet) {
+                if (bet > availableDollars) {
+                    print('SORRY, MY FRIEND, BUT YOU BET TOO MUCH.');
+                    print(`YOU HAVE ONLY ${availableDollars} DOLLARS TO BET.`);
                 } else {
-                    print("O.K., HOPE YOU HAD FUN!");
+                    validBet = true;
+                }
+            } else {
+                // Does not meet minimum required bet
+                print('CHICKEN!!');
+                print('');
+            }
+        }
+
+        print('\n\nHERE IS THE CARD WE DREW: ');
+        print(getCardValue(cardThree));
+
+        // Determine if player won or lost
+        if (cardThree > cardOne && cardThree < cardTwo) {
+            print('YOU WIN!!!');
+            availableDollars = availableDollars + bet;
+        } else {
+            print('SORRY, YOU LOSE');
+
+            if (bet >= availableDollars) {
+                print('');
+                print('');
+                print('SORRY, FRIEND, BUT YOU BLEW YOUR WAD.');
+                print('');
+                print('');
+                print('TRY AGAIN (YES OR NO)');
+
+                let tryAgainInput = await input();
+
+                print('');
+                print('');
+
+                if (isValidYesNoString(tryAgainInput)) {
+                    availableDollars = 100;
+                } else {
+                    print('O.K., HOPE YOU HAD FUN!');
                     break;
                 }
             } else {
-                q = q - m;
+                availableDollars = availableDollars - bet;
             }
         }
     }
 }
 
-main();
+// UTILITY FUNCTIONS
+function isValidYesNoString(string) {
+    return validLowerCaseYesAndNoStrings.includes(string.toLowerCase());
+}
+
+function isValidYesString(string) {
+    return validLowerCaseYesStrings.includes(string.toLowerCase());
+}
+
+function isValidNoString(string) {
+    return validLowerCaseNoStrings.includes(string.toLowerCase());
+}
+
+function print(string) {
+    if (isRunningInBrowser) {
+        // Adds trailing newline to match console.log behavior
+        document
+            .getElementById('output')
+            .appendChild(document.createTextNode(string + '\n'));
+    } else {
+        console.log(string);
+    }
+}
+
+function input() {
+    if (isRunningInBrowser) {
+        // Accept input from the browser DOM input
+        return new Promise((resolve) => {
+            const outputElement = document.querySelector('#output');
+            const inputElement = document.createElement('input');
+            outputElement.append(inputElement);
+            inputElement.focus();
+
+            inputElement.addEventListener('keydown', (event) => {
+                if (event.key === 'Enter') {
+                    const result = inputElement.value;
+                    inputElement.remove();
+                    print(result);
+                    print('');
+                    resolve(result);
+                }
+            });
+        });
+    } else {
+        // Accept input from the command line in Node.js
+        // See: https://nodejs.dev/learn/accept-input-from-the-command-line-in-nodejs
+        return new Promise(function (resolve) {
+            const readline = require('readline').createInterface({
+                input: process.stdin,
+                output: process.stdout,
+            });
+            readline.question('', function (input) {
+                resolve(input);
+                readline.close();
+            });
+        });
+    }
+}
+
+function printInline(string) {
+    if (isRunningInBrowser) {
+        document
+            .getElementById('output')
+            .appendChild(document.createTextNode(string));
+    } else {
+        process.stdout.write(string);
+    }
+}
+
+function spaces(numberOfSpaces) {
+    return ' '.repeat(numberOfSpaces);
+}
+
+// UTILITY FUNCTIONS
diff --git a/01_Acey_Ducey/python/acey_ducey.py b/01_Acey_Ducey/python/acey_ducey.py
index 6832c7e0..8461dad1 100644
--- a/01_Acey_Ducey/python/acey_ducey.py
+++ b/01_Acey_Ducey/python/acey_ducey.py
@@ -78,7 +78,7 @@ if __name__ == "__main__":
         """
 Acey-Ducey is played in the following manner
 The dealer (computer) deals two cards face up
-You have an option to be or not bet depending
+You have an option to bet or not bet depending
 on whether or not you feel the card will have
 a value between the first two.
 If you do not want to bet, input a 0
diff --git a/01_Acey_Ducey/python/aceyducey.py b/01_Acey_Ducey/python/aceyducey.py
index 9aaef681..87147181 100644
--- a/01_Acey_Ducey/python/aceyducey.py
+++ b/01_Acey_Ducey/python/aceyducey.py
@@ -174,7 +174,7 @@ print("OK Hope you had fun\n")
 #
 #   Give the user the ability to quit the game, perhaps
 #   by typing "quit" instead of making a bet.  Provide a
-#   final assement based on how much of the original
+#   final assessment based on how much of the original
 #   bankroll they have left.
 #
 #   Or have the game run for a set number of rounds or
diff --git a/18_Bullseye/csharp/Bullseye.csproj b/18_Bullseye/csharp/Bullseye.csproj
new file mode 100644
index 00000000..74abf5c9
--- /dev/null
+++ b/18_Bullseye/csharp/Bullseye.csproj
@@ -0,0 +1,10 @@
+
+
+  
+    Exe
+    net6.0
+    enable
+    enable
+  
+
+
diff --git a/18_Bullseye/csharp/Bullseye.sln b/18_Bullseye/csharp/Bullseye.sln
new file mode 100644
index 00000000..2a65a449
--- /dev/null
+++ b/18_Bullseye/csharp/Bullseye.sln
@@ -0,0 +1,22 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.30114.105
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bullseye", "Bullseye.csproj", "{04C164DB-594F-41C4-BC0E-0A203A5536C7}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{04C164DB-594F-41C4-BC0E-0A203A5536C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{04C164DB-594F-41C4-BC0E-0A203A5536C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{04C164DB-594F-41C4-BC0E-0A203A5536C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{04C164DB-594F-41C4-BC0E-0A203A5536C7}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+EndGlobal
diff --git a/18_Bullseye/csharp/BullseyeGame.cs b/18_Bullseye/csharp/BullseyeGame.cs
new file mode 100644
index 00000000..58c9799d
--- /dev/null
+++ b/18_Bullseye/csharp/BullseyeGame.cs
@@ -0,0 +1,237 @@
+namespace Bullseye
+{
+    /// 
+    /// Class encompassing the game
+    /// 
+    public class BullseyeGame
+    {
+        private readonly List _players;
+
+        // define a constant for the winning score so that it is
+        // easy to change again in the future
+        private const int WinningScore = 200;
+
+        public BullseyeGame()
+        {
+            // create the initial list of players; list is empty, but
+            // the setup of the game will add items to this list
+            _players = new List();
+        }
+
+        public void Run()
+        {
+            PrintIntroduction();
+
+            SetupGame();
+
+            PlayGame();
+
+            PrintResults();
+        }
+
+        private void SetupGame()
+        {
+            // First, allow the user to enter how many players are going
+            // to play. This could be weird if the user enters negative
+            // numbers, words, or too many players, so there are some
+            // extra checks on the input to make sure the user didn't do
+            // anything too crazy. Loop until the user enters valid input.
+            bool validPlayerCount;
+            int playerCount;
+            do
+            {
+                Console.WriteLine();
+                Console.Write("HOW MANY PLAYERS? ");
+                string? input = Console.ReadLine();
+
+                // assume the user has entered something incorrect - the
+                // next steps will validate the input
+                validPlayerCount = false;
+
+                if (Int32.TryParse(input, out playerCount))
+                {
+                    if (playerCount > 0 && playerCount <= 20)
+                    {
+                        validPlayerCount = true;
+                    }
+                    else
+                    {
+                        Console.WriteLine("YOU MUST ENTER A NUMBER BETWEEN 1 AND 20!");
+                    }
+                }
+                else
+                {
+                    Console.WriteLine("YOU MUST ENTER A NUMBER");
+                }
+
+            }
+            while (!validPlayerCount);
+
+            // Next, allow the user to enter names for the players; as each
+            // name is entered, create a Player object to track the name
+            // and their score, and save the object to the list in this class
+            // so the rest of the game has access to the set of players
+            for (int i = 0; i < playerCount; i++)
+            {
+                string? playerName = String.Empty;
+                do
+                {
+                    Console.Write($"NAME OF PLAYER #{i+1}? ");
+                    playerName = Console.ReadLine();
+
+                    // names can be any sort of text, so allow whatever the user
+                    // enters as long as it isn't a blank space
+                }
+                while (String.IsNullOrWhiteSpace(playerName));
+
+                _players.Add(new Player(playerName));
+            }
+        }
+
+        private void PlayGame()
+        {
+            Random random = new Random(DateTime.Now.Millisecond);
+
+            int round = 0;
+            bool isOver = false;
+            do
+            {
+                // starting a new round, increment the counter
+                round++;
+                Console.WriteLine($"ROUND {round}");
+                Console.WriteLine("--------------");
+
+                foreach (Player player in _players)
+                {
+                    // ask the user how they want to throw
+                    Console.Write($"{player.Name.ToUpper()}'S THROW: ");
+                    string? input = Console.ReadLine();
+
+                    // based on the input, figure out the probabilities
+                    int[] probabilities;
+                    switch (input)
+                    {
+                        case "1":
+                        {
+                            probabilities = new int[] { 65, 55, 50, 50 };
+                            break;
+                        }
+                        case "2":
+                        {
+                            probabilities = new int[] { 99, 77, 43, 1 };
+                            break;
+                        }
+                        case "3":
+                        {
+                            probabilities = new int[] { 95, 75, 45, 5 };
+                            break;
+                        }
+                        default:
+                        {
+                            // in case the user types something bad, pretend it's
+                            // as if they tripped over themselves while throwing
+                            // the dart - they'll either hit a bullseye or completely
+                            // miss
+                            probabilities = new int[] { 95, 95, 95, 95 };
+                            Console.Write("TRIP! ");
+                            break;
+                        }
+                    }
+
+
+                    // Next() returns a number in the range: min <= num < max, so specify 101
+                    // as the maximum so that 100 is a number that could be returned
+                    int chance = random.Next(0, 101);
+
+                    if (chance > probabilities[0])
+                    {
+                        player.Score += 40;
+                        Console.WriteLine("BULLSEYE!!  40 POINTS!");
+                    }
+                    else if (chance > probabilities[1])
+                    {
+                        player.Score += 30;
+                        Console.WriteLine("30-POINT ZONE!");
+                    }
+                    else if (chance > probabilities[2])
+                    {
+                        player.Score += 20;
+                        Console.WriteLine("20-POINT ZONE");
+                    }
+                    else if (chance > probabilities[3])
+                    {
+                        player.Score += 10;
+                        Console.WriteLine("WHEW!  10 POINTS.");
+                    }
+                    else
+                    {
+                        // missed it
+                        Console.WriteLine("MISSED THE TARGET!  TOO BAD.");
+                    }
+
+                    // check to see if the player has won - if they have, then
+                    // break out of the loops
+                    if (player.Score > WinningScore)
+                    {
+                        Console.WriteLine();
+                        Console.WriteLine("WE HAVE A WINNER!!");
+                        Console.WriteLine($"{player.Name.ToUpper()} SCORED {player.Score} POINTS.");
+                        Console.WriteLine();
+
+                        isOver = true; // out of the do/while round loop
+                        break; // out of the foreach (player) loop
+                    }
+
+                    Console.WriteLine();
+                }
+            }
+            while (!isOver);
+        }
+
+        private void PrintResults()
+        {
+            // For bragging rights, print out all the scores, but sort them
+            // by who had the highest score
+            var sorted = _players.OrderByDescending(p => p.Score);
+
+            // padding is used to get things to line up nicely - the results
+            // should look something like:
+            //      PLAYER       SCORE
+            //      Bravo          210
+            //      Charlie         15
+            //      Alpha            1
+            Console.WriteLine("PLAYER       SCORE");
+            foreach (var player in sorted)
+            {
+                Console.WriteLine($"{player.Name.PadRight(12)} {player.Score.ToString().PadLeft(5)}");
+            }
+
+            Console.WriteLine();
+            Console.WriteLine("THANKS FOR THE GAME.");
+        }
+
+        private void PrintIntroduction()
+        {
+            Console.WriteLine(Title);
+            Console.WriteLine();
+            Console.WriteLine(Introduction);
+            Console.WriteLine();
+            Console.WriteLine(Operations);
+        }
+
+        private const string Title = @"
+                    BULLSEYE
+    CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY";
+
+        private const string Introduction = @"
+IN THIS GAME, UP TO 20 PLAYERS THROW DARTS AT A TARGET
+WITH 10, 20, 30, AND 40 POINT ZONES.  THE OBJECTIVE IS
+TO GET 200 POINTS.";
+
+        private const string Operations = @"
+THROW   DESCRIPTION         PROBABLE SCORE
+  1     FAST OVERARM        BULLSEYE OR COMPLETE MISS
+  2     CONTROLLED OVERARM  10, 20, OR 30 POINTS
+  3     UNDERARM            ANYTHING";
+    }
+}
diff --git a/18_Bullseye/csharp/Player.cs b/18_Bullseye/csharp/Player.cs
new file mode 100644
index 00000000..4d18fd43
--- /dev/null
+++ b/18_Bullseye/csharp/Player.cs
@@ -0,0 +1,28 @@
+namespace Bullseye
+{
+    /// 
+    /// Object to track the name and score of a player
+    /// 
+    public class Player
+    {
+        /// 
+        /// Creates a play with the given name
+        /// 
+        /// Name of the player
+        public Player(string name)
+        {
+            Name = name;
+            Score = 0;
+        }
+
+        /// 
+        /// Name of the player
+        /// 
+        public string Name { get; private set; }
+
+        /// 
+        /// Current score of the player
+        /// 
+        public int Score { get; set; }
+    }
+}
diff --git a/18_Bullseye/csharp/Program.cs b/18_Bullseye/csharp/Program.cs
new file mode 100644
index 00000000..1e4316ef
--- /dev/null
+++ b/18_Bullseye/csharp/Program.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace Bullseye
+{
+    public static class Program
+    {
+        // Entry point to the application; create an instance of the
+        // game class and call Run()
+        public static void Main(string[] args)
+        {
+            new BullseyeGame().Run();
+        }
+    }
+}
diff --git a/22_Change/perl/change.pl b/22_Change/perl/change.pl
new file mode 100755
index 00000000..51813cd8
--- /dev/null
+++ b/22_Change/perl/change.pl
@@ -0,0 +1,68 @@
+#!/usr/bin/perl
+
+use v5.24; # for say and use strict
+use warnings;
+
+sub get_pennies {
+  my $query = shift;
+
+  print "$query? ";
+  my $in = <>;
+  chomp $in;
+  $in =~ /([\d.]+)/; # the first match of digits and decimal points
+  return int( $1 * 100 );
+}
+
+sub make_change {
+  my $change = shift;
+
+  state %change_options = (
+    'Penny' => { value => 1, plural => 'Pennies' },
+    'Nickel' => { value => 5 },
+    'Dime' => { value => 10 },
+    'Quarter' => { value => 25 },
+    'One Half Dollar' => { value => 50 },
+    'One Dollar Bill' => { value => 100 * 1 },
+    'Five Dollar Bill' => { value => 100 * 5 },
+    '10 Dollar Bill' => { value => 100 * 10 },
+  );
+
+  foreach my $unit ( sort { $change_options{$b}->{value} <=> $change_options{$a}->{value} } keys %change_options ) {
+    my $value = $change_options{$unit}->{value};
+    next if $value > $change;
+    my $number = int( $change / $value );
+    if ( $number > 1 ) {
+      $unit = exists $change_options{$unit}->{plural} ? $change_options{$unit}->{plural} : "${unit}s";
+    }
+    say "$number $unit";
+    $change -= $number * $value;
+  }
+}
+
+print <<'__END_OF_INTRO';
+                              Change
+               Creative Computing  Morristown, New Jersey
+
+
+I, Your friendly microcomputer, will determine
+the correct change for items costing up to $100.
+
+
+__END_OF_INTRO
+
+while ( 1 ) {
+  my $cost = get_pennies( 'Cost of item' );
+  my $payment = get_pennies( 'Amount of payment');
+
+  my $change = $payment - $cost;
+  my $change_formatted = sprintf( "%.2f", $change / 100 );
+  if ( $change == 0 ) {
+    say 'Correct amount, thank you.';
+  } elsif ( $change < 0 ) {
+    say 'Sorry, you have short-changed me $', abs($change_formatted);
+  } else {
+    say 'Your change, $', $change_formatted;
+    make_change( $change );
+    say "Thank you, come again\n\n";
+  }
+}
diff --git a/47_Hi-Lo/kotlin/HiLo.kt b/47_Hi-Lo/kotlin/HiLo.kt
new file mode 100644
index 00000000..7c4b6b65
--- /dev/null
+++ b/47_Hi-Lo/kotlin/HiLo.kt
@@ -0,0 +1,50 @@
+
+fun main() {
+    println(introText)
+    var winnings = 0
+    do {
+        winnings += playGame()
+        println("YOUR TOTAL WINNINGS ARE NOW $winnings DOLLARS")
+    } while(playAgain())
+    
+    println("SO LONG.  HOPE YOU ENJOYED YOURSELF!!!")
+}
+
+fun playGame():Int {
+    val amount = (1..100).random()
+    repeat(6) {
+        println("YOUR GUESS")
+        val guess = readln().toInt()
+        when {
+            guess == amount -> {
+                println("GOT IT!!!!!!!! YOU WIN $amount DOLLARS.")
+                return amount
+            }
+            guess > amount -> println("YOUR GUESS IS TOO HIGH")
+            else -> println("YOUR GUESS IS TOO LOW")
+        }
+    }
+    println("YOU BLEW IT...TOO BAD...THE NUMBER WAS $amount")
+    return 0
+}
+
+fun playAgain():Boolean {
+    println("PLAY AGAIN (YES OR NO)")
+    return readLine()?.uppercase() == "YES"
+}
+
+
+val introText = """
+    HI LO
+    CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY
+    
+    
+    THIS IS THE GAME OF HI LO.
+    
+    YOU WILL HAVE 6 TRIES TO GUESS THE AMOUNT OF MONEY IN THE
+    HI LO JACKPOT, WHICH IS BETWEEN 1 AND 100 DOLLARS.  IF YOU
+    GUESS THE AMOUNT, YOU WIN ALL THE MONEY IN THE JACKPOT!
+    THEN YOU GET ANOTHER CHANCE TO WIN MORE MONEY.  HOWEVER,
+    IF YOU DO NOT GUESS THE AMOUNT, THE GAME ENDS
+
+""".trimIndent()
\ No newline at end of file
diff --git a/47_Hi-Lo/kotlin/README.md b/47_Hi-Lo/kotlin/README.md
new file mode 100644
index 00000000..f43a5b70
--- /dev/null
+++ b/47_Hi-Lo/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/50_Horserace/python/horserace.py b/50_Horserace/python/horserace.py
new file mode 100644
index 00000000..cf7c266e
--- /dev/null
+++ b/50_Horserace/python/horserace.py
@@ -0,0 +1,278 @@
+import random
+import math
+import time
+
+def basic_print(*zones, **kwargs):
+    """Simulates the PRINT command from BASIC to some degree.
+    Supports `printing zones` if given multiple arguments."""
+
+    line = ""
+    if len(zones) == 1:
+        line = str(zones[0])
+    else:
+        line = "".join(["{:<14}".format(str(zone)) for zone in zones])
+    identation = kwargs.get("indent", 0)
+    end = kwargs.get("end", "\n")
+    print(" " * identation + line, end=end)
+
+
+def basic_input(prompt, type_conversion=None):
+    """BASIC INPUT command with optional type conversion"""
+
+    while True:
+        try:
+            inp = input(f"{prompt}? ")
+            if type_conversion is not None:
+                inp = type_conversion(inp)
+            break
+        except ValueError:
+            basic_print("INVALID INPUT!")
+    return inp
+
+
+# horse names do not change over the program, therefore making it a global.
+# throught the game, the ordering of the horses is used to indentify them
+HORSE_NAMES = [
+    "JOE MAW",
+    "L.B.J.",
+    "MR.WASHBURN",
+    "MISS KAREN",
+    "JOLLY",
+    "HORSE",
+    "JELLY DO NOT",
+    "MIDNIGHT"
+]
+
+
+def introduction():
+    """Print the introduction, and optional the instructions"""
+
+    basic_print("HORSERACE", indent=31)
+    basic_print("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY", indent=15)
+    basic_print("\n\n")
+    basic_print("WELCOME TO SOUTH PORTLAND HIGH RACETRACK")
+    basic_print("                      ...OWNED BY LAURIE CHEVALIER")
+    y_n = basic_input("DO YOU WANT DIRECTIONS")
+
+    # if no instructions needed, return
+    if y_n.upper() == "NO":
+        return
+
+    basic_print("UP TO 10 MAY PLAY.  A TABLE OF ODDS WILL BE PRINTED.  YOU")
+    basic_print("MAY BET ANY + AMOUNT UNDER 100000 ON ONE HORSE.")
+    basic_print("DURING THE RACE, A HORSE WILL BE SHOWN BY ITS")
+    basic_print("NUMBER.  THE HORSES RACE DOWN THE PAPER!")
+    basic_print("")
+
+
+def setup_players():
+    """Gather the number of players and their names"""
+
+    # ensure we get an integer value from the user
+    number_of_players = basic_input("HOW MANY WANT TO BET", int)
+
+    # for each user query their name and return the list of names
+    player_names = []
+    basic_print("WHEN ? APPEARS,TYPE NAME")
+    for _ in range(number_of_players):
+        player_names.append(basic_input(""))
+    return player_names
+
+
+def setup_horses():
+    """Generates random odds for each horse. Returns a list of
+    odds, indexed by the order of the global HORSE_NAMES."""
+
+    odds = [random.randrange(1, 10) for _ in HORSE_NAMES]
+    total = sum(odds)
+
+    # rounding odds to two decimals for nicer output,
+    # this is not in the origin implementation
+    return [round(total/odd, 2) for odd in odds]
+
+
+def print_horse_odds(odds):
+    """Print the odds for each horse"""
+
+    basic_print("")
+    for i in range(len(HORSE_NAMES)):
+        basic_print(HORSE_NAMES[i], i, f"{odds[i]}:1")
+    basic_print("")
+
+
+def get_bets(player_names):
+    """For each player, get the number of the horse to bet on,
+    as well as the amount of money to bet"""
+
+    basic_print("--------------------------------------------------")
+    basic_print("PLACE YOUR BETS...HORSE # THEN AMOUNT")
+
+    bets = []
+    for name in player_names:
+        horse = basic_input(name, int)
+        amount = None
+        while amount is None:
+            amount = basic_input("", float)
+            if amount < 1 or amount >= 100000:
+                basic_print("  YOU CAN'T DO THAT!")
+                amount = None
+        bets.append((horse, amount))
+    
+    basic_print("")
+
+    return bets
+
+
+def get_distance(odd):
+    """Advances a horse during one step of the racing simulation.
+    The amount travelled is random, but scaled by the odds of the horse"""
+
+    d = random.randrange(1,100)
+    s = math.ceil(odd)
+    if d < 10:
+        return 1
+    elif d < s + 17:
+        return 2
+    elif d < s + 37:
+        return 3
+    elif d < s + 57:
+        return 4
+    elif d < s + 77:
+        return 5
+    elif d < s + 77:
+        return 5
+    elif d < s + 92:
+        return 6
+    else:
+        return 7
+
+
+def print_race_state(total_distance, race_pos):
+    """Outputs the current state/stop of the race.
+    Each horse is placed according to the distance they have travelled. In
+    case some horses travelled the same distance, their numbers are printed
+    on the same name"""
+
+    # we dont want to modify the `race_pos` list, since we need
+    # it later. Therefore we generating an interator from the list
+    race_pos_iter = iter(race_pos)
+
+    # race_pos is stored by last to first horse in the race.
+    # we get the next horse we need to print out
+    next_pos = next(race_pos_iter)
+    
+    # start line
+    basic_print("XXXXSTARTXXXX")
+
+    # print all 28 lines/unit of the race course
+    for l in range(28):
+
+        # ensure we still have a horse to print and if so, check if the
+        # next horse to print is not the current line
+        # needs iteration, since multiple horses can share the same line
+        while next_pos is not None and l == total_distance[next_pos]:
+            basic_print(f"{next_pos} ", end="")
+            next_pos = next(race_pos_iter, None)
+        else:
+            # if no horses are left to print for this line, print a new line
+            basic_print("")
+
+    # finish line
+    basic_print("XXXXFINISHXXXX")
+
+
+def simulate_race(odds):
+    num_horses = len(HORSE_NAMES)
+
+    # in spirit of the original implementation, using two arrays to
+    # track the total distance travelled, and create an index from
+    # race position -> horse index
+    total_distance = [0] * num_horses
+
+    # race_pos maps from the position in the race, to the index of the horse
+    # it will later be sorted from last to first horse, based on the
+    # distance travelled by each horse.
+    # e.g. race_pos[0] => last horse
+    #      race_pos[-1] => winning horse
+    race_pos = list(range(num_horses))
+    
+    basic_print("\n1 2 3 4 5 6 7 8")
+
+    while True:
+
+        # advance each horse by a random amount
+        for i in range(num_horses):
+            total_distance[i] += get_distance(odds[i])
+
+        # bubble sort race_pos based on total distance travelled
+        # in the original implementation, race_pos is reset for each 
+        # simulation step, so we keep this behaviour here
+        race_pos = list(range(num_horses))
+        for l in range(num_horses):
+            for i in range(num_horses-1-l):
+                if total_distance[race_pos[i]] < total_distance[race_pos[i+1]]:
+                    continue
+                race_pos[i], race_pos[i+1] = race_pos[i+1], race_pos[i]
+
+        # print current state of the race
+        print_race_state(total_distance, race_pos)
+
+        # goal line is defined as 28 units from start
+        # check if the winning horse is already over the finish line
+        if total_distance[race_pos[-1]] >= 28:
+            return race_pos
+        
+        # this was not in the original BASIC implementation, but it makes the
+        # race visualization a nice animation (if the terminal size is set to 31 rows)
+        time.sleep(1)
+
+
+def print_race_results(race_positions, odds, bets, player_names):
+    """Print the race results, as well as the winnings of each player"""
+
+    # print the race positions first
+    basic_print("THE RACE RESULTS ARE:")
+    position = 1
+    for horse_idx in reversed(race_positions):
+        line = f"{position} PLACE HORSE NO. {horse_idx} AT {odds[horse_idx]}:1"
+        basic_print("")
+        basic_print(line)
+        position += 1
+
+    # followed by the amount the players won
+    winning_horse_idx = race_positions[-1]
+    for idx, name in enumerate(player_names):
+        (horse, amount) = bets[idx]
+        if horse == winning_horse_idx:
+            basic_print("")
+            basic_print(f"{name} WINS ${amount * odds[winning_horse_idx]}")
+    
+
+def main_loop(player_names, horse_odds):
+    """Main game loop"""
+
+    while True:
+        print_horse_odds(horse_odds)
+        bets = get_bets(player_names)
+        final_race_positions = simulate_race(horse_odds)
+        print_race_results(final_race_positions, horse_odds, bets, player_names)
+
+        basic_print("DO YOU WANT TO BET ON THE NEXT RACE ?")
+        one_more = basic_input("YES OR NO")
+        if one_more.upper() != "YES":
+            break
+
+
+def main():
+    # introduction, player names and horse odds are only generated once
+    introduction()
+    player_names = setup_players()
+    horse_odds = setup_horses()
+
+    # main loop of the game, the player can play multiple races, with the
+    # same odds
+    main_loop(player_names, horse_odds)
+    
+
+if __name__ == "__main__":
+    main()
\ No newline at end of file
diff --git a/59_Lunar_LEM_Rocket/javascript/lem.js b/59_Lunar_LEM_Rocket/javascript/lem.js
index 4f5ffd30..68bab1cf 100644
--- a/59_Lunar_LEM_Rocket/javascript/lem.js
+++ b/59_Lunar_LEM_Rocket/javascript/lem.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");
@@ -112,13 +112,13 @@ async function main()
             print("\n");
             print("WHICH SYSTEM OF MEASUREMENT DO YOU PREFER?\n");
             print(" 1=METRIC     0=ENGLISH\n");
-            print("ENTER THE APPROPIATE NUMBER");
+            print("ENTER THE APPROPRIATE NUMBER");
         }
         while (1) {
             k = parseInt(await input());
             if (k == 0 || k == 1)
                 break;
-            print("ENTER THE APPROPIATE NUMBER");
+            print("ENTER THE APPROPRIATE NUMBER");
         }
         if (k == 1) {
             z = 1852.8;
diff --git a/78_Sine_Wave/java/src/SineWave.java b/78_Sine_Wave/java/src/SineWave.java
index f3b3c7eb..0908791e 100644
--- a/78_Sine_Wave/java/src/SineWave.java
+++ b/78_Sine_Wave/java/src/SineWave.java
@@ -1,5 +1,3 @@
-import java.util.Arrays;
-
 /**
  * Sine Wave
  *
@@ -12,24 +10,19 @@ import java.util.Arrays;
 public class SineWave {
 
     public static void main(String[] args) {
-
-        System.out.println("SINE WAVE");
-        System.out.println("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
-        System.out.println();
-
-        int toggle = 0;
-        for(double t = 0; t<40; t += .25) {
-            int a = 26 + (int) (25 * Math.sin(t));
-            char[] repeat = new char[a];
-            Arrays.fill(repeat,' ');
-            System.out.print(new String(repeat));
-            if (toggle == 1) {
-                System.out.println("COMPUTING");
-                toggle = 0;
-            } else {
-                System.out.println("CREATIVE");
-                toggle = 1;
-            }
+        System.out.println("""
+           SINE WAVE
+           CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY
+           """);
+        var isCreative = true;
+        for(var t = 0d; t<40; t += .25) {
+            //Indent output
+            var indentations = 26 + (int) (25 * Math.sin(t));
+            System.out.print(" ".repeat(indentations));
+            //Change output every iteration
+            var word = isCreative ? "CREATIVE" : "COMPUTING";
+            System.out.println(word);
+            isCreative = !isCreative ;
         }
     }
 }
diff --git a/89_Tic-Tac-Toe/kotlin/Board.kt b/89_Tic-Tac-Toe/kotlin/Board.kt
new file mode 100644
index 00000000..287a8d37
--- /dev/null
+++ b/89_Tic-Tac-Toe/kotlin/Board.kt
@@ -0,0 +1,80 @@
+/**
+ * @author John Long based on Java by Ollie Hensman-Crook
+ */
+
+enum class Player(val char: Char) {
+    X('X'),
+    O('O')
+}
+
+class Board {
+    // Initialize an array of size nine with all values set to null
+    private var boxes: Array = arrayOfNulls(9)
+
+    /**
+     * Place 'X' or 'O' on the board position passed
+     * @param position
+     * @param player
+     */
+    fun setArr(position: Int, player: Player) {
+        boxes[position - 1] = player
+    }
+
+    fun printBoard() {
+        System.out.format(
+            """
+  %c !  %c !  %c
+----+----+----
+  %c !  %c !  %c
+----+----+----
+  %c !  %c !  %c
+""",
+            // converts each box to a char and then passes them in order to format
+            // If the person is unassigned, use a space ' '
+     *(boxes.map{it?.char ?: ' '}.toTypedArray()))
+    }
+
+    /**
+     * @param x
+     * @return the value of the char at a given position
+     */
+    fun getBoardValue(x: Int): Player? {
+        return boxes[x - 1]
+    }
+
+    private val winningCombos = listOf(
+        // horizontal
+        listOf(0,1,2),
+        listOf(3,4,5),
+        listOf(6,7,8),
+        // diagonal
+        listOf(0,4,8),
+        listOf(2,4,6),
+        // vertical
+        listOf(0,3,6),
+        listOf(1,4,7),
+        listOf(2,5,8)
+    )
+    /**
+     * Go through the board and check for win
+     * @param player
+     * @return whether a win has occurred
+     */
+    fun isWinFor(player: Player): Boolean {
+        // Check if any winningCombos have all their boxes set to player
+        return winningCombos.any{ combo ->
+            combo.all { boxes[it] == player }
+        }
+    }
+
+    fun isDraw(): Boolean {
+        return !isWinFor(Player.X) && !isWinFor(Player.O) && boxes.all { it != null }
+    }
+
+    /**
+     * Reset the board
+     */
+    fun clear() {
+        boxes = arrayOfNulls(9)
+    }
+}
\ No newline at end of file
diff --git a/89_Tic-Tac-Toe/kotlin/README.md b/89_Tic-Tac-Toe/kotlin/README.md
new file mode 100644
index 00000000..f43a5b70
--- /dev/null
+++ b/89_Tic-Tac-Toe/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/89_Tic-Tac-Toe/kotlin/TicTacToe2.kt b/89_Tic-Tac-Toe/kotlin/TicTacToe2.kt
new file mode 100644
index 00000000..389174f0
--- /dev/null
+++ b/89_Tic-Tac-Toe/kotlin/TicTacToe2.kt
@@ -0,0 +1,96 @@
+import java.util.Random
+import kotlin.system.exitProcess
+
+/**
+ * @author John Long based on Java from Ollie Hensman-Crook
+ */
+private val compChoice = Random()
+private val gameBoard = Board()
+
+fun main() {
+    println("              TIC-TAC-TOE")
+    println("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY")
+    println("\nTHE BOARD IS NUMBERED: ")
+    println(" 1  2  3\n 4  5  6\n 7  8  9\n")
+    while (true) {
+        // Let the player choose whether to be X or O (Player.X or Player.O)
+        val (human, computer) = readXOrO()
+        while (true) {
+            // Get a valid move from the user and then move there
+            val validMoveIndex = readValidMove()
+            gameBoard.setArr(validMoveIndex, human)
+            gameBoard.printBoard()
+
+            // Computer randomly fills a square (if the game isn't already over)
+            // This uses Kotlin's null handling and will only set the board
+            // if validRandomMove returned a non-null value
+            validRandomMove()?.let {
+                gameBoard.setArr(it, computer)
+                gameBoard.printBoard()
+            }
+
+            // if there is a win print if player won or the computer won and ask if they
+            // want to play again
+            when {
+                gameBoard.isWinFor(human) -> {
+                    checkPlayAgain("YOU WIN")
+                    break
+                }
+                gameBoard.isWinFor(computer) -> {
+                    checkPlayAgain("YOU LOSE")
+                    break
+                }
+                gameBoard.isDraw() -> {
+                    checkPlayAgain("DRAW")
+                    break
+                }
+            }
+        }
+    }
+}
+
+private fun checkPlayAgain(result: String) {
+    println("$result, PLAY AGAIN? (Y/N)")
+    gameBoard.clear()
+    if (!readYesOrNo()) exitProcess(0)
+}
+
+private fun readYesOrNo(): Boolean {
+    while (true) {
+        when (readLine()?.get(0)?.uppercaseChar()) {
+            'Y' -> return true
+            'N' -> return false
+            else -> println("THAT'S NOT 'Y' OR 'N', TRY AGAIN")
+        }
+    }
+}
+
+private fun validRandomMove(): Int? {
+    if (gameBoard.isDraw() || gameBoard.isWinFor(Player.O) || gameBoard.isWinFor(Player.X)) return null
+    println("THE COMPUTER MOVES TO")
+    // keep generating a random value until we find one that is null (unset)
+    return generateSequence { 1 + compChoice.nextInt(9) }.first { gameBoard.getBoardValue(it) == null }
+}
+
+private fun readValidMove(): Int {
+    println("WHERE DO YOU MOVE")
+    while (true) {
+        val input = readln().toIntOrNull()
+        if (input != null && gameBoard.getBoardValue(input) == null) {
+            return input
+        } else {
+            println("INVALID INPUT, TRY AGAIN")
+        }
+    }
+}
+
+private fun readXOrO(): Pair {
+    println("DO YOU WANT 'X' OR 'O'")
+    while (true) {
+        when (readln()[0].uppercaseChar()) {
+            'X' -> return Player.X to Player.O
+            'O' -> return Player.O to Player.X
+            else -> println("THAT'S NOT 'X' OR 'O', TRY AGAIN")
+        }
+    }
+}
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/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/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/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/)**.