diff --git a/01_Acey_Ducey/aceyducey.bas b/01_Acey_Ducey/aceyducey.bas index 2c8b1c3b..0b6f72db 100644 --- a/01_Acey_Ducey/aceyducey.bas +++ b/01_Acey_Ducey/aceyducey.bas @@ -10,7 +10,7 @@ 80 PRINT"IF YOU DO NOT WANT TO BET, INPUT A 0" 100 N=100 110 Q=100 -120 PRINT "YOU NOW HAVE";Q;"DOLLARS." +120 PRINT "YOU NOW HAVE ";Q;" DOLLARS." 130 PRINT 140 GOTO 260 210 Q=Q+M 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 e526c3df..61c362a7 100644 --- a/01_Acey_Ducey/javascript/aceyducey.html +++ b/01_Acey_Ducey/javascript/aceyducey.html @@ -1,7 +1,7 @@ - - + + ACEY DUCEY -

-
+

+
diff --git a/01_Acey_Ducey/javascript/aceyducey.js b/01_Acey_Ducey/javascript/aceyducey.js
index 6c31d77c..8bf5e9e1 100644
--- a/01_Acey_Ducey/javascript/aceyducey.js
+++ b/01_Acey_Ducey/javascript/aceyducey.js
@@ -1,100 +1,216 @@
-import { readLine, print, spaces } from "./io.js";
+// UTILITY VARIABLES
 
-const minFaceCard = 11;
-const faceCards = {
-  11: "JACK",
-  12: "QUEEN",
-  13: "KING",
-  14: "ACE"
-};
+// 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';
 
-function randomCard() {
-  return Math.floor(Math.random() * 13 + 2);
+// 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 printCard(card) {
-  if (card < minFaceCard) {
-    print(card);
-  } else {
-    print(faceCards[card]);
-  }
-  print("\n");
+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];
 }
 
-print(spaces(26) + "ACEY DUCEY CARD GAME\n");
-print(spaces(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n\n\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 '0'\n");
+// 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;
+}
 
-let currentMoney = 100;
-while (true) {
-  print(`YOU NOW HAVE ${currentMoney} DOLLARS.\n\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'");
 
-  let card1, card2, currentBet;
-  do {
-    print("HERE ARE YOUR NEXT TWO CARDS: \n");
-    [card1, card2] = [randomCard(), randomCard()];
+main();
 
-    // Ensure we always show cards in order of lowest to highest, and we never
-    // get two of the same card.
-    do {
-      card1 = randomCard();
-      card2 = randomCard();
-    } while (card1 >= card2);
-
-    printCard(card1);
-    printCard(card2);
-    print("\n");
+async function main() {
+    let bet;
+    let availableDollars = 100;
 
+    // Loop game forever
     while (true) {
-      print("\nWHAT IS YOUR BET? ");
-      currentBet = parseInt(await readLine(), 10);
+        let [cardOne, cardTwo, cardThree] = newGameCards();
 
-      if (currentBet > 0) {
-        if (currentBet > currentMoney) {
-          print("SORRY, MY FRIEND, BUT YOU BET TOO MUCH.\n");
-          print(`YOU HAVE ONLY ${currentMoney} DOLLARS TO BET.\n`);
-          continue;
+        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 {
+                    validBet = true;
+                }
+            } else {
+                // Does not meet minimum required bet
+                print('CHICKEN!!');
+                print('');
+            }
         }
-        break;
-      }
 
-      // Invalid bet value. Output an error message and reset to undefined to
-      // restart the loop with new cards.
-      currentBet = undefined;
-      print("CHICKEN!!\n");
-      print("\n");
-      break;
+        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 {
+                availableDollars = availableDollars - bet;
+            }
+        }
     }
-  } while (currentBet === undefined);
-
-  const actualCard = randomCard();
-  print("\n\nHERE IS THE CARD WE DREW:\n")
-  printCard(actualCard);
-  print("\n\n");
-
-  if (actualCard > card1 && actualCard < card2) {
-    print("YOU WIN!!!\n");
-    currentMoney += currentBet;
-  } else {
-    print("SORRY, YOU LOSE\n");
-    if (currentBet < currentMoney) {
-      currentMoney -= currentBet;
-    } else {
-      print("\n\nSORRY, FRIEND, BUT YOU BLEW YOUR WAD.\n\n\n");
-      print("TRY AGAIN (YES OR NO)");
-      const tryAgain = await readLine();
-      print("\n\n");
-      if (tryAgain.toLowerCase() === "yes") {
-        currentMoney = 100;
-      } else {
-        print("O.K., HOPE YOU HAD FUN!");
-        break;
-      }
-    }
-  }
 }
+
+// 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/javascript/io.js b/01_Acey_Ducey/javascript/io.js
deleted file mode 100644
index a9211e9c..00000000
--- a/01_Acey_Ducey/javascript/io.js
+++ /dev/null
@@ -1,29 +0,0 @@
-const outputEl = document.querySelector("#output");
-
-export function print(string) {
-  outputEl.append(string);
-}
-
-export function readLine() {
-  return new Promise(resolve => {
-    const inputEl = document.createElement("input");
-    outputEl.append(inputEl);
-    inputEl.focus();
-
-    inputEl.addEventListener("keydown", event => {
-      if (event.key === "Enter") {
-        const result = inputEl.value;
-        inputEl.remove();
-
-        print(result);
-        print("\n");
-
-        resolve(result);
-      }
-    });
-  });
-}
-
-export function spaces(numberOfSpaces) {
-  return " ".repeat(numberOfSpaces);
-}
diff --git a/01_Acey_Ducey/python/acey_ducey_oo.py b/01_Acey_Ducey/python/acey_ducey_oo.py
new file mode 100644
index 00000000..d8b7c752
--- /dev/null
+++ b/01_Acey_Ducey/python/acey_ducey_oo.py
@@ -0,0 +1,125 @@
+#
+# AceyDuchy
+#
+# From: BASIC Computer Games (1978)
+#       Edited by David Ahl
+#
+# "The original BASIC program author was Bill Palmby
+#  of Prairie View, Illinois."
+#
+# Python port by Aviyam Fischer, 2022
+#
+######################################################
+
+class Card:
+    def __init__(self, suit, rank):
+        self.suit = suit
+        self.rank = rank
+
+    def __str__(self):
+        r = self.rank
+        if r == 11:
+            r = 'J'
+        elif r == 12:
+            r = 'Q'
+        elif r == 13:
+            r = 'K'
+        elif r == 14:
+            r = 'A'
+        return f'{r}{self.suit}'
+
+
+class Deck:
+    def __init__(self):
+        self.cards = []
+        self.build()
+
+    def build(self):
+        for suit in ['\u2665', '\u2666', '\u2663', '\u2660']:
+            for rank in range(1, 14):
+                self.cards.append(Card(suit, rank))
+
+    def shuffle(self):
+        import random
+        random.shuffle(self.cards)
+
+    def deal(self):
+        return self.cards.pop()
+
+
+class Game:
+    def __init__(self):
+        self.deck = Deck()
+        self.deck.shuffle()
+        self.card_a = self.deck.deal()
+        self.card_b = self.deck.deal()
+        self.money = 100
+        self.not_done = True
+
+    def play(self):
+        while self.not_done:
+            while self.money > 0:
+                card_a = self.card_a
+                card_b = self.card_b
+
+                if card_a.rank > card_b.rank:
+                    card_a, card_b = card_b, card_a
+
+                if card_a.rank == card_b.rank:
+                    self.card_b = self.deck.deal()
+                    card_b = self.card_b
+
+                print(f'You have:\t ${self.money} ')
+                print(f'Your cards:\t {card_a} {card_b}')
+
+                bet = int(input('What is your bet? '))
+                player_card = self.deck.deal()
+                if 0 < bet <= self.money:
+
+                    print(f'Your deal:\t {player_card}')
+                    if card_a.rank < player_card.rank < card_b.rank:
+                        print('You Win!')
+                        self.money += bet
+                    else:
+                        print('You Lose!')
+                        self.money -= bet
+                        self.not_done = False
+                else:
+                    print('Chicken!')
+                    print(f'Your deal should have been: {player_card}')
+                    if card_a.rank < player_card.rank < card_b.rank:
+                        print(f'You could have won!')
+                    else:
+                        print(f'You would lose, so it was wise of you to chicken out!')
+
+                    self.not_done = False
+                    break
+
+                if len(self.deck.cards) <= 1:
+                    print('You ran out of cards. Game over.')
+                    self.not_done = False
+                    break
+
+        if self.money == 0:
+            self.not_done = False
+
+
+if __name__ == '__main__':
+    print(''' 
+    Acey Ducey is a card game where you play against the computer.
+    The Dealer(computer) will deal two cards facing up.
+    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
+    ''')
+    GAME_OVER = False
+
+    while not GAME_OVER:
+        game = Game()
+        game.play()
+        print(f'You have ${game.money} left')
+        print('Would you like to play again? (y/n)')
+        if input() == 'n':
+            GAME_OVER = True
+
+    print('\nThanks for playing!')
diff --git a/01_Acey_Ducey/ruby/aceyducey.rb b/01_Acey_Ducey/ruby/aceyducey.rb
index b9648ef8..3d49754a 100644
--- a/01_Acey_Ducey/ruby/aceyducey.rb
+++ b/01_Acey_Ducey/ruby/aceyducey.rb
@@ -56,9 +56,9 @@ while true # Game loop
   puts
   puts "HERE ARE YOUR NEXT TWO CARDS:"
 
-  # Randomly draw two cards from 2 to 14 and make sure the first card is lower in value than the second
+  # Randomly draw two cards and make sure the first card is lower in value than the second
   # Using array destructuring, this sorted array can be assigned to `first_card` and `second_card`
-  first_card, second_card = [rand(2..14), rand(2..14)].sort
+  first_card, second_card = (2...14).to_a.shuffle.pop(2).sort
 
   # Helper method to convert a numeric card into a String for printing
   def card_name(card)
diff --git a/01_Acey_Ducey/vbnet/AceyDucy.sln b/01_Acey_Ducey/vbnet/AceyDucy.sln
new file mode 100644
index 00000000..881d7c9c
--- /dev/null
+++ b/01_Acey_Ducey/vbnet/AceyDucy.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31903.59
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "AceyDucy", "AceyDucy\AceyDucy.vbproj", "{37496710-B458-4502-ADCB-4C57203866F9}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{37496710-B458-4502-ADCB-4C57203866F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{37496710-B458-4502-ADCB-4C57203866F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{37496710-B458-4502-ADCB-4C57203866F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{37496710-B458-4502-ADCB-4C57203866F9}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {C01D9DAE-644C-455F-8365-E14E49074BC3}
+	EndGlobalSection
+EndGlobal
diff --git a/01_Acey_Ducey/vbnet/AceyDucy/AceyDucy.vbproj b/01_Acey_Ducey/vbnet/AceyDucy/AceyDucy.vbproj
new file mode 100644
index 00000000..98a07001
--- /dev/null
+++ b/01_Acey_Ducey/vbnet/AceyDucy/AceyDucy.vbproj
@@ -0,0 +1,9 @@
+
+
+  
+    Exe
+    AceyDucy
+    net6.0
+  
+
+
diff --git a/01_Acey_Ducey/vbnet/AceyDucy/Program.vb b/01_Acey_Ducey/vbnet/AceyDucy/Program.vb
new file mode 100644
index 00000000..bfa518ca
--- /dev/null
+++ b/01_Acey_Ducey/vbnet/AceyDucy/Program.vb
@@ -0,0 +1,178 @@
+Imports System
+
+''' 
+''' This is a modern adapation of Acey Ducey from BASIC Computer Games.
+''' 
+''' The structural changes primarily consist of replacing the many GOTOs with
+''' Do/Loop constructs to force the continual execution of the program.
+''' 
+''' Because modern Basic allows multi-line If/Then blocks, many GOTO jumps were
+''' able to be eliminated and the logic was able to be moved to more relevant areas,
+''' For example, the increment/decrement of the player's balance could be in the same
+''' area as the notification of win/loss.
+''' 
+''' Some modern improvements were added, primarily the inclusion of a function, which
+''' eliminated a thrice-repeated block of logic to display the card value.  The archaic
+''' RND function is greatly simplified with the .NET Framework's Random class.
+''' 
+''' Elementary comments are provided for non-programmers or novices.
+''' 
+Module Program
+    Sub Main(args As String())
+        ' These are the variables that will hold values during the program's execution
+        Dim input As String
+        Dim rnd As New Random ' You can create a new instance of an object during declaration
+        Dim currentBalance As Integer = 100 ' You can set a initial value at declaration
+        Dim currentWager As Integer
+        Dim cardA, cardB, cardC As Integer ' You can specify multiple variables of the same type in one declaration statement
+
+        ' Display the opening title and instructions
+        ' Use a preceding $ to insert calculated values within the string using {}
+        Console.WriteLine($"{Space((Console.WindowWidth \ 2) - 10)}ACEY DUCEY CARD GAME")
+        Console.WriteLine($"{Space((Console.WindowWidth \ 2) - 21)}CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY")
+        Console.WriteLine("")
+        Console.WriteLine("")
+        Console.WriteLine("ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER")
+        Console.WriteLine("THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP")
+        Console.WriteLine("YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING")
+        Console.WriteLine("ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE")
+        Console.WriteLine("A VALUE BETWEEN THE FIRST TWO.")
+        Console.WriteLine("IF YOU DO NOT WANT TO BET, INPUT A 0")
+
+        Do ' This loop continues as long as the player wants to keep playing
+
+            Do ' This loop continues as long as the player has money to play
+
+                Console.WriteLine("")
+                Console.WriteLine($"YOU NOW HAVE {currentBalance} DOLLARS.")
+                Console.WriteLine("")
+
+                Console.WriteLine("HERE ARE YOUR NEXT TWO CARDS:")
+
+                ' We need to ensure that card B is a higher value for our later comparison,
+                ' so we will loop until we have two cards that meet this criteria
+                Do
+                    cardA = rnd.Next(2, 14)
+                    cardB = rnd.Next(2, 14)
+
+                Loop While cardA > cardB
+
+                ' We use a function to display the text value of the numeric card value
+                ' because we do this 3 times and a function reduces repetition of code
+                Console.WriteLine(DisplayCard(cardA))
+                Console.WriteLine(DisplayCard(cardB))
+
+                Do ' This loop continues until the player provides a valid wager value
+                    Console.WriteLine("")
+                    Console.WriteLine("WHAT IS YOUR BET")
+
+                    currentWager = 0
+                    input = Console.ReadLine
+
+                    ' Any input from the console is a string, but we require a number. 
+                    ' Test the input to make sure it is a numeric value.
+                    If Integer.TryParse(input, currentWager) Then
+                        ' Test to ensure the player has not wagered more than their balance
+                        If currentWager > currentBalance Then
+                            Console.WriteLine("SORRY, MY FRIEND, BUT YOU BET TOO MUCH.")
+                            Console.WriteLine($"YOU HAVE ONLY {currentBalance} DOLLARS TO BET.")
+
+                        Else
+                            ' The player has provided a numeric value that is less/equal to their balance, 
+                            ' exit the loop and continue play
+                            Exit Do
+
+                        End If ' check player balance
+
+                    End If ' check numeric input
+
+                Loop ' wager loop
+
+                ' If the player is wagering, draw the third card, otherwise, mock them.
+                If currentWager > 0 Then
+                    cardC = rnd.Next(2, 14)
+
+                    Console.WriteLine(DisplayCard(cardC))
+
+                    ' The effort we made to have two cards in numeric order earlier makes this check easier,
+                    ' otherwise we would have to have a second check in the opposite direction
+                    If cardC < cardA OrElse cardC >= cardB Then
+                        Console.WriteLine("SORRY, YOU LOSE")
+                        currentBalance -= currentWager ' Shorthand code to decrement a number (currentBalance=currentBalance - currentWager)
+
+                    Else
+                        Console.WriteLine("YOU WIN!!!")
+                        currentBalance += currentWager ' Shorthand code to increment a number (currentBalance=currentBalance + currentWager)
+
+                    End If
+
+                Else
+                    Console.WriteLine("CHICKEN!!")
+                    Console.WriteLine("")
+
+                End If
+
+            Loop While currentBalance > 0 ' loop as long as the player has money
+
+            ' At this point, the player has no money (currentBalance=0).  Inform them of such.
+            Console.WriteLine("")
+            Console.WriteLine("SORRY, FRIEND, BUT YOU BLEW YOUR WAD.")
+            Console.WriteLine("")
+            Console.WriteLine("")
+
+            ' We will loop to ensure the player provides some answer.
+            Do
+                Console.WriteLine("TRY AGAIN (YES OR NO)")
+                Console.WriteLine("")
+
+                input = Console.ReadLine
+
+            Loop While String.IsNullOrWhiteSpace(input)
+
+            ' We will assume that the player wants to play again only if they answer yes.
+            ' (yeah and ya are valid as well, because we only check the first letter)
+            If input.Substring(0, 1).Equals("y", StringComparison.CurrentCultureIgnoreCase) Then ' This allows upper and lower case to be entered.
+                currentBalance = 100 ' Reset the players balance before restarting
+
+            Else
+                ' Exit the outer loop which will end the game.
+                Exit Do
+
+            End If
+
+        Loop ' The full game loop
+
+        Console.WriteLine("O.K., HOPE YOU HAD FUN!")
+
+    End Sub
+
+    ' This function is called for each of the 3 cards used in the game.
+    ' The input and the output are both consistent, making it a good candidate for a function.
+    Private Function DisplayCard(value As Integer) As String
+        ' We check the value of the input and run a block of code for whichever
+        ' evaluation matches
+        Select Case value
+            Case 2 To 10 ' Case statements can be ranges of values, also multiple values (Case 2,3,4,5,6,7,8,9,10)
+                Return value.ToString
+
+            Case 11
+                Return "JACK"
+
+            Case 12
+                Return "QUEEN"
+
+            Case 13
+                Return "KING"
+
+            Case 14
+                Return "ACE"
+
+        End Select
+
+        ' Although we have full knowledge of the program and never plan to send an invalid
+        ' card value, it's important to provide a message for the next developer who won't
+        Throw New ArgumentOutOfRangeException("Card value must be between 2 and 14")
+
+    End Function
+
+End Module
diff --git a/03_Animal/kotlin/Animal.kt b/03_Animal/kotlin/Animal.kt
new file mode 100644
index 00000000..bac9af08
--- /dev/null
+++ b/03_Animal/kotlin/Animal.kt
@@ -0,0 +1,119 @@
+/**
+ * ANIMAL
+ *
+ *
+ * Converted from BASIC to Kotlin by John Long (@patimen)
+ *
+ * Animal is basically a perfect example of a binary tree. Implement it
+ * as such, with the QuestionNode either having an answer if it is a terminal node
+ * or a Question
+ */
+
+fun main() {
+    printIntro()
+    val rootQuestionNode =
+        QuestionOrAnswer(question = Question("DOES IT SWIM", QuestionOrAnswer("FISH"), QuestionOrAnswer("BIRD")))
+    while (true) {
+        val choice = ask("ARE YOU THINKING OF AN ANIMAL")
+        when {
+            choice == "LIST" -> printKnownAnimals(rootQuestionNode)
+            choice.startsWith("Q") -> return
+            choice.startsWith("Y") -> {
+                // A wrong answer means it's a new animal!
+                val wrongAnswer = rootQuestionNode.getWrongAnswer()
+                if (wrongAnswer == null) {
+                    // The computer got the right answer!
+                    println("WHY NOT TRY ANOTHER ANIMAL?")
+                } else {
+                    // Get a new question to ask next time
+                    wrongAnswer.askForInformationAndSave()
+                }
+            }
+        }
+    }
+}
+
+// Takes care of asking a question (on the same line) and getting
+// an answer or a blank string
+fun ask(question: String): String {
+    print("$question? ")
+    return readLine()?.uppercase() ?: ""
+}
+
+// Special case for a "yes or no" question, returns true of yes
+fun askYesOrNo(question: String): Boolean {
+    return generateSequence {
+        print("$question? ")
+        readLine()
+    }.firstNotNullOf { yesOrNo(it) }
+}
+
+// If neither Y (true) or N (false), return null, so the above sequence
+// will just keep executing until it gets the answer
+private fun yesOrNo(string: String): Boolean? =
+    when (string.uppercase().firstOrNull()) {
+        'Y' -> true
+        'N' -> false
+        else -> null
+    }
+
+private fun printKnownAnimals(question: QuestionOrAnswer) {
+    println("\nANIMALS I ALREADY KNOW ARE:")
+    val animals = question.getAnswers().chunked(4)
+    animals.forEach { line ->
+        // The '*' in front of line.toTypedArray() "spreads" the array as a list of parameters instead
+        System.out.printf("%-15s".repeat(line.size), *line.toTypedArray())
+        println()
+    }
+}
+
+private fun printIntro() {
+    println("                                ANIMAL")
+    println("              CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY")
+    println("\n\n")
+    println("PLAY 'GUESS THE ANIMAL'")
+    println("\n")
+    println("THINK OF AN ANIMAL AND THE COMPUTER WILL TRY TO GUESS IT.")
+}
+
+class QuestionOrAnswer(private var answer: String? = null, var question: Question? = null) {
+    fun getAnswers(): List = answer?.let { listOf(it) } ?: question!!.getAnswers()
+    fun getWrongAnswer(): QuestionOrAnswer? {
+        if (answer != null) {
+            // "takeUnless" will return null if the answer is "yes". In this case
+            // we will return the "wrong answer", aka the terminal answer that was incorrect
+            return this.takeUnless { askYesOrNo("IS IT A $answer") }
+        }
+        return question?.getWrongAnswer()
+    }
+
+    fun askForInformationAndSave() {
+        //Failed to get it right and ran out of questions
+        //Let's ask the user for the new information
+        val newAnimal = ask("THE ANIMAL YOU WERE THINKING OF WAS A")
+        val newQuestion = ask("PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A \n$newAnimal FROM A $answer\n")
+        val newAnswer = askYesOrNo("FOR A $newAnimal THE ANSWER WOULD BE")
+
+        val trueAnswer = if (newAnswer) newAnimal else answer
+        val falseAnswer = if (newAnswer) answer else newAnimal
+        // Replace our answer with null  and set the question with the data we just got
+        // This makes it a question instead of an answer
+        this.answer = null
+        this.question = Question(newQuestion, QuestionOrAnswer(trueAnswer), QuestionOrAnswer(falseAnswer))
+    }
+}
+
+class Question(
+    private val question: String,
+    private val trueAnswer: QuestionOrAnswer,
+    private val falseAnswer: QuestionOrAnswer
+) {
+    fun getAnswers(): List = trueAnswer.getAnswers() + falseAnswer.getAnswers()
+
+    fun getWrongAnswer(): QuestionOrAnswer? =
+        if (askYesOrNo(question)) {
+            trueAnswer.getWrongAnswer()
+        } else {
+            falseAnswer.getWrongAnswer()
+        }
+}
diff --git a/03_Animal/kotlin/README.md b/03_Animal/kotlin/README.md
new file mode 100644
index 00000000..f43a5b70
--- /dev/null
+++ b/03_Animal/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/04_Awari/csharp/Game.cs b/04_Awari/csharp/Game.cs
new file mode 100644
index 00000000..8a673dea
--- /dev/null
+++ b/04_Awari/csharp/Game.cs
@@ -0,0 +1,264 @@
+namespace Awari;
+
+public class Game
+{
+    public int[] PlayerPits => _beans[0..6];
+    public int[] ComputerPits => _beans[7..13];
+    public int PlayerHome => _beans[_playerHome];
+    public int ComputerHome => _beans[_computerHome];
+
+    private bool IsDone =>
+        PlayerPits.All(b => b == 0) // if all the player's pits are empty
+     || ComputerPits.All(b => b == 0); // or if all the computer's pits are empty
+
+    public GameState State { get; private set; }
+
+    public void Reset()
+    {
+        State = GameState.PlayerMove;
+
+        Array.Fill(_beans, _initialPitValue);
+        _beans[_playerHome] = 0;
+        _beans[_computerHome] = 0;
+
+        _moveCount = 0;
+        _notWonGameMoves[^1] = 0;
+    }
+
+    public bool IsLegalPlayerMove(int move) =>
+        move is > 0 and < 7
+     && _beans[move - 1] > 0; // arrays are zero-based, but moves are one-based
+
+    public void PlayerMove(int move) => MoveAndRegister(move - 1, _playerHome);
+
+    public List ComputerTurn()
+    {
+        // keep a list of moves made by the computer in a single turn (1 or 2)
+        List moves = new();
+
+        moves.Add(ComputerMove()); // ComputerMove() returns the move made
+
+        // only if a second move is possible, do it
+        if (State == GameState.ComputerSecondMove)
+            moves.Add(ComputerMove());
+
+        return moves;
+    }
+
+    public GameOutcome GetOutcome()
+    {
+        if (State != GameState.Done)
+            throw new InvalidOperationException("Game is not yet done.");
+
+        int difference = _beans[_playerHome] - _beans[_computerHome];
+        var winner = difference switch
+        {
+            < 0 => GameWinner.Computer,
+            0 => GameWinner.Draw,
+            > 0 => GameWinner.Player,
+        };
+
+        return new GameOutcome(winner, Math.Abs(difference));
+    }
+
+    private void MoveAndRegister(int pit, int homePosition)
+    {
+        int lastMovedBean = Move(_beans, pit, homePosition);
+
+        // encode moves by player and computer into a 'base 6' number
+        // e.g. if the player moves 5, the computer moves 2, and the player moves 4,
+        // that would be encoded as ((5 * 6) * 6) + (2 * 6) + 4 = 196
+        if (pit > 6) pit -= 7;
+        _moveCount++;
+        if (_moveCount < 9)
+            _notWonGameMoves[^1] = _notWonGameMoves[^1] * 6 + pit;
+
+        // determine next state based on current state, whether the game's done, and whether the last moved bean moved
+        // into the player's home position
+        State = (State, IsDone, lastMovedBean == homePosition) switch
+        {
+            (_, true, _) => GameState.Done,
+            (GameState.PlayerMove, _, true) => GameState.PlayerSecondMove,
+            (GameState.PlayerMove, _, false) => GameState.ComputerMove,
+            (GameState.PlayerSecondMove, _, _) => GameState.ComputerMove,
+            (GameState.ComputerMove, _, true) => GameState.ComputerSecondMove,
+            (GameState.ComputerMove, _, false) => GameState.PlayerMove,
+            (GameState.ComputerSecondMove, _, _) => GameState.PlayerMove,
+            _ => throw new InvalidOperationException("Unexpected game state"),
+        };
+
+        // do some bookkeeping if the game is done, but not won by the computer
+        if (State == GameState.Done
+         && _beans[_playerHome] >= _beans[_computerHome])
+            // add an entry for the next game
+            _notWonGameMoves.Add(0);
+    }
+
+    private static int Move(int[] beans, int pit, int homePosition)
+    {
+        int beansToMove = beans[pit];
+        beans[pit] = 0;
+
+        // add the beans that were in the pit to other pits, moving clockwise around the board
+        for (; beansToMove >= 1; beansToMove--)
+        {
+            // wrap around if pit exceeds 13
+            pit = (pit + 1) % 14;
+
+            beans[pit]++;
+        }
+
+        if (beans[pit] == 1 // if the last bean was sown in an empty pit
+         && pit is not _playerHome and not _computerHome // which is not either player's home
+         && beans[12 - pit] != 0) // and the pit opposite is not empty
+        {
+            // move the last pit sown and the _beans in the pit opposite to the player's home
+            beans[homePosition] = beans[homePosition] + beans[12 - pit] + 1;
+            beans[pit] = 0;
+            beans[12 - pit] = 0;
+        }
+
+        return pit;
+    }
+
+    private int ComputerMove()
+    {
+        int move = DetermineComputerMove();
+        MoveAndRegister(move, homePosition: _computerHome);
+
+        // the result is only used to return it to the application, so translate it from an array index (between 7 and
+        // 12) to a pit number (between 1 and 6)
+        return move - 6;
+    }
+
+    private int DetermineComputerMove()
+    {
+        int bestScore = -99;
+        int move = 0;
+
+        // for each of the computer's possible moves, simulate them to calculate a score and pick the best one
+        for (int j = 7; j < 13; j++)
+        {
+            if (_beans[j] <= 0)
+                continue;
+
+            int score = SimulateMove(j);
+
+            if (score >= bestScore)
+            {
+                move = j;
+                bestScore = score;
+            }
+        }
+
+        return move;
+    }
+
+    private int SimulateMove(int move)
+    {
+        // make a copy of the current state, so we can safely mess with it
+        var hypotheticalBeans = new int[14];
+        _beans.CopyTo(hypotheticalBeans, 0);
+
+        // simulate the move in our copy
+        Move(hypotheticalBeans, move, homePosition: _computerHome);
+
+        // determine the 'best' move the player could make after this (best for them, not for the computer)
+        int score = ScoreBestNextPlayerMove(hypotheticalBeans);
+
+        // score this move by calculating how far ahead we would be after the move, and subtracting the player's next
+        // move score
+        score = hypotheticalBeans[_computerHome] - hypotheticalBeans[_playerHome] - score;
+
+        // have we seen the current set of moves before in a drawn/lost game? after 8 moves it's unlikely we'll find any
+        // matches, since games will have diverged. also we don't have space to store that many moves.
+        if (_moveCount < 8)
+        {
+            int translatedMove = move - 7;  // translate from 7 through 12 to 0 through 5
+
+            // if the first two moves in this game were 1 and 2, and this hypothetical third move would be a 3,
+            // movesSoFar would be (1 * 36) + (2 * 6) + 3 = 51
+            int movesSoFar = _notWonGameMoves[^1] * 6 + translatedMove;
+
+            // since we store moves as a 'base 6' number, we need to divide stored moves by a power of 6
+            // let's say we've a stored lost game where the moves were, in succession, 1 through 8, the value stored
+            // would be:
+            // 8 + (7 * 6) + (6 * 36) + (5 * 216) + (4 * 1296) + (3 * 7776) + (2 * 46656) + (1 * 279936) = 403106
+            // to figure out the first three moves, we'd need to divide by 7776, resulting in 51.839...
+            double divisor = Math.Pow(6.0, 7 - _moveCount);
+
+            foreach (int previousGameMoves in _notWonGameMoves)
+                // if this combination of moves so far ultimately resulted in a draw/loss, give it a lower score
+                // note that this can happen multiple times
+                if (movesSoFar == (int) (previousGameMoves / divisor + 0.1))
+                    score -= 2;
+        }
+
+        return score;
+    }
+
+    private static int ScoreBestNextPlayerMove(int[] hypotheticalBeans)
+    {
+        int bestScore = 0;
+
+        for (int i = 0; i < 6; i++)
+        {
+            if (hypotheticalBeans[i] <= 0)
+                continue;
+
+            int score = ScoreNextPlayerMove(hypotheticalBeans, i);
+
+            if (score > bestScore)
+                bestScore = score;
+        }
+
+        return bestScore;
+    }
+
+    private static int ScoreNextPlayerMove(int[] hypotheticalBeans, int move)
+    {
+        // figure out where the last bean will land
+        int target = hypotheticalBeans[move] + move;
+        int score = 0;
+
+        // if it wraps around, that means the player is adding to his own pits, which is good
+        if (target > 13)
+        {
+            // prevent overrunning the number of pits we have
+            target %= 14;
+            score = 1;
+        }
+
+        // if the player's move ends up in an empty pit, add the value of the pit on the opposite side to the score
+        if (hypotheticalBeans[target] == 0 && target is not _playerHome and not _computerHome)
+            score += hypotheticalBeans[12 - target];
+
+        return score;
+    }
+
+    private const int _playerHome = 6;
+    private const int _computerHome = 13;
+    private const int _initialPitValue = 3;
+
+    private readonly int[] _beans = new int[14];
+    private readonly List _notWonGameMoves = new() { 0 };    // not won means draw or lose
+    private int _moveCount;
+}
+
+public enum GameState
+{
+    PlayerMove,
+    PlayerSecondMove,
+    ComputerMove,
+    ComputerSecondMove,
+    Done,
+}
+
+public enum GameWinner
+{
+    Player,
+    Computer,
+    Draw,
+}
+
+public record struct GameOutcome(GameWinner Winner, int Difference);
\ No newline at end of file
diff --git a/04_Awari/csharp/Program.cs b/04_Awari/csharp/Program.cs
new file mode 100644
index 00000000..750787e7
--- /dev/null
+++ b/04_Awari/csharp/Program.cs
@@ -0,0 +1,98 @@
+using Awari;
+
+Console.WriteLine(Tab(34) + "AWARI");
+Console.WriteLine(Tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
+
+Game game = new();
+
+while (true)
+{
+    game.Reset();
+    DisplayGame();
+
+    while (game.State != GameState.Done)
+    {
+        switch (game.State)
+        {
+            case GameState.PlayerMove:
+                PlayerMove(second: false);
+                break;
+            case GameState.PlayerSecondMove:
+                PlayerMove(second: true);
+                break;
+            case GameState.ComputerMove:
+                ComputerTurn();
+                break;
+        }
+
+        DisplayGame();
+    }
+
+    var outcome = game.GetOutcome();
+
+    string outcomeLabel =
+        outcome.Winner switch
+        {
+            GameWinner.Computer => $"I WIN BY {outcome.Difference} POINTS",
+            GameWinner.Draw => "DRAWN GAME",
+            GameWinner.Player => $"YOU WIN BY {outcome.Difference} POINTS",
+            _ => throw new InvalidOperationException($"Unexpected winner {outcome.Winner}."),
+        };
+    Console.WriteLine(outcomeLabel);
+    Console.WriteLine();
+}
+
+void DisplayGame()
+{
+    // display the computer's pits
+    Console.Write("   ");
+    foreach (var pit in game.ComputerPits.Reverse())
+        Console.Write($"{pit,2} ");
+    Console.WriteLine();
+
+    // display both homes
+    Console.WriteLine($"{game.ComputerHome,2}{Tab(19)}{game.PlayerHome,2}");
+
+    // display the player's pits
+    Console.Write("   ");
+    foreach (var pit in game.PlayerPits)
+        Console.Write($"{pit,2} ");
+    Console.WriteLine();
+
+    Console.WriteLine();
+}
+
+void PlayerMove(bool second = false)
+{
+    int move = GetMove(second);
+    game.PlayerMove(move);
+}
+
+int GetMove(bool second)
+{
+    string prompt = second ? "AGAIN? " : "YOUR MOVE? ";
+
+    while (true)
+    {
+        Console.Write(prompt);
+
+        string input = Console.ReadLine() ?? "";
+
+        // input must be a number between 1 and 6, and the pit must have > 0 beans
+        if (int.TryParse(input, out int move)
+         && game.IsLegalPlayerMove(move))
+            return move;
+
+        Console.WriteLine("ILLEGAL MOVE");
+    }
+}
+
+void ComputerTurn()
+{
+    var moves = game.ComputerTurn();
+    string movesString = string.Join(",", moves);
+
+    Console.WriteLine($"MY MOVE IS {movesString}");
+}
+
+string Tab(int n) => new(' ', n);
\ No newline at end of file
diff --git a/04_Awari/csharp/csharp.csproj b/04_Awari/csharp/csharp.csproj
new file mode 100644
index 00000000..6bf2a525
--- /dev/null
+++ b/04_Awari/csharp/csharp.csproj
@@ -0,0 +1,11 @@
+
+
+  
+    Exe
+    net6.0
+    enable
+    enable
+    Awari
+  
+
+
diff --git a/05_Bagels/perl/bagels.pl b/05_Bagels/perl/bagels.pl
new file mode 100755
index 00000000..739ded0e
--- /dev/null
+++ b/05_Bagels/perl/bagels.pl
@@ -0,0 +1,195 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+# global variable declaration (just the user's score)
+my($Y) = 0;
+
+
+# yes_input is a subroutine that returns a true value
+# if the first character of the user's input from STDIN
+# is a 'Y' (checking case-insensitively via regex)
+sub yes_input {
+    chomp(my $A = );
+    return $A =~ m/^Y/i;
+}
+
+# Main code starts here.
+
+print ' 'x32; print "Bagels\n";
+print ' 'x14; print "Creative Computing  Morristown, New Jersey\n\n";
+
+# Provide instructions if requested 
+print "Would you like the rules (yes or no)? ";
+if (yes_input()) {
+
+    # Print out the instructions using a here doc
+    # (useful for large blocks of text)
+    print <);
+
+            # Use a regex to check if the user entered three digits,
+            # and complain if they did not.
+            if ($A !~ m{^(\d)(\d)(\d)$}) {
+                print "What?\n";
+                # Program execution will now pass through the rest
+                # of the logic below and loop back to the start 
+                # of the CHECK loop.
+            } else {
+
+                # As a side effect of the regex match above, the 
+                # $1, $2, and $3 variables are each of the digits
+                # of the user's guess. Perl treats numbers and
+                # strings interchangably, so we will not have to
+                # use the ASC() conversion functions required 
+                # by the BASIC program.
+                my @B = ($1,$2,$3);
+
+                # Check for duplicate digits in the user's guess
+                if ($B[0] == $B[1] || $B[0] == $B[2] || $B[1] == $B[2]) {
+                    print "Oh, I forgot to tell you that the number I have in mind\n";
+                    print "has no two digits the same.\n";
+                    # Again, no further action is required here
+                    # because we want to loop back to the start
+                    # of the CHECK loop.
+                } else {
+
+                    # This code block is the actual game logic, so
+                    # it's executed only if the user's input has
+                    # passed all the above checks.
+                    my($C,$D);
+                    $C = 0; $D = 0;
+
+                    # As a replacement for the original BASIC logic,
+                    # this for loop works over an anonymous array of
+                    # pairs of digits to compare the computer's and
+                    # the user's digits to see how many similar ones
+                    # there are. Keep in mind that Perl arrays are
+                    # zero-indexed, so we're comparing items numbered
+                    # 0, 1, and 2, instead of 1, 2, and 3 in BASIC.
+
+                    for my $PAIR ( [0,1], [1,0], [1,2], [2,1], [0,2], [2,0] ) {
+                        if ($A[$PAIR->[0]] == $B[$PAIR->[1]]) {
+                            ++$C;
+                        }
+                    }
+
+                    # Check for digits that are correctly guessed
+                    for my $i (0..2) {
+                        if ($A[$i] == $B[$i]) {
+                            ++$D;
+                        }
+                    }
+
+                    # If the user guessed all 3 digits they get
+                    # a point, and the 'PLAY' loop is restarted
+                    # (see the 'continue' loop below)
+                    if ($D == 3) {
+                        print "You got it!!!\n\n";
+                        ++$Y;
+                        next PLAY;
+                    }
+
+                    # Print out the clues. The 'x' operator
+                    # prints out the string the indicated number
+                    # of times. The "BAGELS" line uses Perl's 
+                    # ternary operator to print the word if 
+                    # the expression ($C + $D) is equal to 0.
+
+                    printf("%s%s%s\n", 
+                        "PICO " x$C,
+                        "FERMI "x$D,
+                        ($C+$D==0 ? "BAGELS" : '')
+                    );
+
+                    # Program execution leaves the CHECK loop and
+                    # goes to the next iteration of the $i loop.
+                    last CHECK;
+
+                } # end of game logic else block
+            } # end of regex match else block
+
+            # If program execution reaches this particular point,
+            # then the user's input has not been accepted (the 
+            # only ways out of this loop are the "next PLAY" statement
+            # when the user wins, and the "last CHECK" statement
+            # when the user's input is successfully parsed).
+            # So the program execution goes back to the top of the 
+            # CHECK loop, printing the request for user input
+            # again.
+
+        } # end of CHECK loop
+
+        # This location is reached by the "last CHECK" statement,
+        # and it's another execution of the $i loop.
+
+    } # end of $i loop
+
+    # If program execution reaches here, the user has guessed 20
+    # times and not won.
+
+    print "Oh well.\n";
+    printf("That's twenty guesses. My number was %s\n", join('',@A));
+
+} # end of the PLAY loop
+
+# This 'continue' block is executed before the conditional part of the
+# PLAY loop is evaluated, so we can ask if the user wants another game
+# (i.e., if we should restart the PLAY loop).
+
+continue {
+
+    # This 'continue' loop is reached either when the PLAY loop has completed
+    # or via the 'next PLAY' statement when the user wins a game. In either
+    # case we ask if the player wants to go again, and use the 'last' 
+    # statement to exit the loop if the response is not yes.
+    print "Play again (yes or no) ? ";
+    last unless yes_input();
+}
+
+# And as in the original BASIC program, print out
+# the user's score only if it is > 0.
+printf("A %d point bagels buff!\n", $Y) if $Y > 0;
+print "Hope you had fun.  Bye.\n";
diff --git a/22_Change/csharp/Change.csproj b/22_Change/csharp/Change.csproj
new file mode 100644
index 00000000..1d2d39a9
--- /dev/null
+++ b/22_Change/csharp/Change.csproj
@@ -0,0 +1,8 @@
+
+
+  
+    Exe
+    net5.0
+  
+
+
diff --git a/22_Change/csharp/Change.sln b/22_Change/csharp/Change.sln
new file mode 100644
index 00000000..c0f2e5e8
--- /dev/null
+++ b/22_Change/csharp/Change.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.810.16
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Change", "Change.csproj", "{AE094667-8496-4ECF-8B42-B1648EE26073}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{AE094667-8496-4ECF-8B42-B1648EE26073}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{AE094667-8496-4ECF-8B42-B1648EE26073}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{AE094667-8496-4ECF-8B42-B1648EE26073}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{AE094667-8496-4ECF-8B42-B1648EE26073}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {65684CBD-CD74-46AF-8E9E-0F69DCF72697}
+	EndGlobalSection
+EndGlobal
diff --git a/22_Change/csharp/Program.cs b/22_Change/csharp/Program.cs
new file mode 100644
index 00000000..12804731
--- /dev/null
+++ b/22_Change/csharp/Program.cs
@@ -0,0 +1,129 @@
+using System;
+
+namespace Change
+{
+    class Program
+    {
+        /// 
+        /// Prints header.
+        /// 
+        static void Header()
+        {
+            Console.WriteLine("Change".PadLeft(33));
+            Console.WriteLine("Creative Computing Morristown, New Jersey".PadLeft(15));
+            Console.WriteLine();
+            Console.WriteLine();
+            Console.WriteLine();
+            Console.WriteLine("I, your friendly microcomputer, will determine\n"
+            + "the correct change for items costing up to $100.");
+            Console.WriteLine();
+            Console.WriteLine();
+        }
+
+        /// 
+        /// Gets user input for price and payment.
+        /// 
+        /// 
+        /// False if any input can't be parsed to double. Price and payment returned would be 0.
+        /// True if it was possible to parse inputs into doubles. Price and payment returned 
+	    /// would be as provided by the user.
+	    /// 
+        static (bool status, double price, double payment) GetInput()
+        {
+            Console.Write("Cost of item? ");
+            var priceString = Console.ReadLine();
+            if (!double.TryParse(priceString, out double price))
+            {
+                Console.WriteLine($"{priceString} isn't a number!");
+                return (false, 0, 0);
+            }
+
+            Console.Write("Amount of payment? ");
+            var paymentString = Console.ReadLine();
+            if (!double.TryParse(paymentString, out double payment))
+            {
+                Console.WriteLine($"{paymentString} isn't a number!");
+                return (false, 0, 0);
+            }
+
+            return (true, price, payment);
+        }
+
+        /// 
+        /// Prints bills and coins for given change.
+        /// 
+        /// 
+        static void PrintChange(double change)
+        {
+            var tens = (int)(change / 10);
+            if (tens > 0)
+                Console.WriteLine($"{tens} ten dollar bill(s)");
+
+            var temp = change - (tens * 10);
+            var fives = (int)(temp / 5);
+            if (fives > 0)
+                Console.WriteLine($"{fives} five dollar bill(s)");
+
+            temp -= fives * 5;
+            var ones = (int)temp;
+            if (ones > 0)
+                Console.WriteLine($"{ones} one dollar bill(s)");
+
+            temp -= ones;
+            var cents = temp * 100;
+            var half = (int)(cents / 50);
+            if (half > 0)
+                Console.WriteLine($"{half} one half dollar(s)");
+
+            temp = cents - (half * 50);
+            var quarters = (int)(temp / 25);
+            if (quarters > 0)
+                Console.WriteLine($"{quarters} quarter(s)");
+
+            temp -= quarters * 25;
+            var dimes = (int)(temp / 10);
+            if (dimes > 0)
+                Console.WriteLine($"{dimes} dime(s)");
+
+            temp -= dimes * 10;
+            var nickels = (int)(temp / 5);
+            if (nickels > 0)
+                Console.WriteLine($"{nickels} nickel(s)");
+
+            temp -= nickels * 5;
+            var pennies = (int)(temp + 0.5);
+            if (pennies > 0)
+                Console.WriteLine($"{pennies} penny(s)");
+        }
+
+        static void Main(string[] args)
+        {
+            Header();
+
+            while (true)
+            {
+                (bool result, double price, double payment) = GetInput();
+                if (!result)
+                    continue;
+
+                var change = payment - price;
+                if (change == 0)
+                {
+                    Console.WriteLine("Correct amount, thank you!");
+                    continue;
+                }
+
+                if (change < 0)
+                {
+                    Console.WriteLine($"Sorry, you have short-changed me ${price - payment:N2}!");
+                    continue;
+                }
+
+                Console.WriteLine($"Your change ${change:N2}");
+                PrintChange(change);
+                Console.WriteLine("Thank you, come again!");
+                Console.WriteLine();
+            }
+        }
+    }
+}
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/24_Chemist/perlchemist.pl b/24_Chemist/perl/chemist.pl
similarity index 50%
rename from 24_Chemist/perlchemist.pl
rename to 24_Chemist/perl/chemist.pl
index 01ed9f02..9f75bfac 100755
--- a/24_Chemist/perlchemist.pl
+++ b/24_Chemist/perl/chemist.pl
@@ -8,30 +8,30 @@ print "THE FICTITIOUS CHECMICAL KRYPTOCYANIC ACID CAN ONLY BE\n";
 print "DILUTED BY THE RATIO OF 7 PARTS WATER TO 3 PARTS ACID.\n";
 print "IF ANY OTHER RATIO IS ATTEMPTED, THE ACID BECOMES UNSTABLE\n";
 print "AND SOON EXPLODES. GIVEN THE AMOUNT OF ACID, YOU MUST\n";
-print "DECIDE WHO MUCH WATER TO ADD FOR DILUTION. IF YOU MISS\n";
+print "DECIDE HOW MUCH WATER TO ADD FOR DILUTION. IF YOU MISS\n";
 print "YOU FACE THE CONSEQUENCES.\n";
 
 my $T=0;
 while ($T<9) {
-	my $A= int(rand(1)*50);
+	my $A= int(rand(50) + 1);
 	my $W= 7*$A/3;
-	print "$A LITERS OF KRYPTOCYANIC ACID. HOW MUCH WATER ($W)";
-	print "? "; chomp(my $R = );
+	print " $A LITERS OF KRYPTOCYANIC ACID. HOW MUCH WATER? ";
+	chomp(my $R = );
 	my $D= abs($W-$R);
 	if ($D>$W/20) {
-		print " SIZZLE! YOU HAVE JUST BEEN DESALINATED INTO A BLOB\n";
-		print " OF QUIVERING PROTOPLASM!\n";
-		print " HOWEVER, YOU MAY TRY AGAIN WITH ANOTHER LIFE.\n";
+		print "SIZZLE! YOU HAVE JUST BEEN DESALINATED INTO A BLOB\n";
+		print "OF QUIVERING PROTOPLASM!\n";
+		print "HOWEVER, YOU MAY TRY AGAIN WITH ANOTHER LIFE.\n";
 		print "\n";
 		$T++;
-		} else {
-		print " GOOD JOB! YOU MAY BREATHE NOW, BUT DON'T INHALE THE FUMES!\n";
+	} else {
+		print "GOOD JOB! YOU MAY BREATHE NOW, BUT DON'T INHALE THE FUMES!\n";
 		print "\n";
-		}
 	}
+}
 
-print " YOUR 9 LIVES ARE USED, BUT YOU WILL BE LONG REMEMBERED FOR\n";
-print " YOUR CONTRIBUTIONS TO THE FIELD OF COMIC BOOK CHEMISTRY.\n";
+print "YOUR 9 LIVES ARE USED, BUT YOU WILL BE LONG REMEMBERED FOR\n";
+print "YOUR CONTRIBUTIONS TO THE FIELD OF COMIC BOOK CHEMISTRY.\n";
 exit;
 
 
diff --git a/29_Craps/python/craps.py b/29_Craps/python/craps.py
new file mode 100644
index 00000000..38faeed3
--- /dev/null
+++ b/29_Craps/python/craps.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python3
+"""This game simulates the games of craps played according to standard Nevada craps table rules.
+
+That is:
+
+1. A 7 or 11 on the first roll wins
+2. A 2, 3, or 12 on the first roll loses
+3. Any other number rolled becomes your "point." You continue to roll; if you get your point you win. If you
+   roll a 7, you lose and the dice change hands when this happens.
+
+This version of craps was modified by Steve North of Creative Computing. It is based on an original which
+appeared one day one a computer at DEC.
+"""
+from random import randint
+
+
+def throw_dice():
+    return randint(1, 6) + randint(1, 6)
+
+
+print(" " * 33 + "Craps")
+print(" " * 15 + "Creative Computing  Morristown, New Jersey")
+print()
+print()
+print()
+
+winnings = 0
+print("2,3,12 are losers; 4,5,6,8,9,10 are points; 7,11 are natural winners.")
+
+play_again = True
+while play_again:
+    wager = int(input("Input the amount of your wager: "))
+
+    print("I will now throw the dice")
+    roll_1 = throw_dice()
+
+    if roll_1 in [7, 11]:
+        print(f"{roll_1} - natural.... a winner!!!!")
+        print(f"{roll_1} pays even money, you win {wager} dollars")
+        winnings += wager
+    elif roll_1 == 2:
+        print(f"{roll_1} - snake eyes.... you lose.")
+        print(f"You lose {wager} dollars")
+        winnings -= wager
+    elif roll_1 in [3, 12]:
+        print(f"{roll_1} - craps.... you lose.")
+        print(f"You lose {wager} dollars")
+        winnings -= wager
+    else:
+        print(f"{roll_1} is the point. I will roll again")
+        roll_2 = 0
+        while roll_2 not in [roll_1, 7]:
+            roll_2 = throw_dice()
+            if roll_2 == 7:
+                print(f"{roll_2} - craps. You lose.")
+                print(f"You lose $ {wager}")
+                winnings -= wager
+            elif roll_2 == roll_1:
+                print(f"{roll_1} - a winner.........congrats!!!!!!!!")
+                print(f"{roll_1} at 2 to 1 odds pays you...let me see... {2 * wager} dollars")
+                winnings += 2 * wager
+            else:
+                print(f"{roll_2} - no point. I will roll again")
+
+    m = input("  If you want to play again print 5 if not print 2: ")
+    if winnings < 0:
+        print(f"You are now under ${-winnings}")
+    elif winnings > 0:
+        print(f"You are now ahead ${winnings}")
+    else:
+        print("You are now even at 0")
+    play_again = (m == "5")
+
+if winnings < 0:
+    print(f"Too bad, you are in the hole. Come again.")
+elif winnings > 0:
+    print(f"Congratulations---you came out a winner. Come again.")
+else:
+    print(f"Congratulations---you came out even, not bad for an amateur")
diff --git a/31_Depth_Charge/ruby/.editorconfig b/31_Depth_Charge/ruby/.editorconfig
new file mode 100644
index 00000000..9d7e93d0
--- /dev/null
+++ b/31_Depth_Charge/ruby/.editorconfig
@@ -0,0 +1,69 @@
+# EditorConfig is awesome: https://EditorConfig.org
+# .editorconfig
+
+# Please see doc/developer_notes.md
+# If you find anything egregious or missing, please consider submitting a pull request
+# to https://github.com/theias/ias_package_shell
+
+
+# top-most EditorConfig file
+root = true
+
+# Sensible defaults for everything
+[*]
+charset = utf-8
+end_of_line = lf
+insert_final_newline = true
+
+# JavaScript
+[**.js]
+indent_style = space
+indent_size = 2
+insert_final_newline = true
+
+# Ruby
+[**.rb]
+indent_style = space
+indent_size = 2
+trim_trailing_whitespace = true
+
+# Python
+[**.py]
+charset = utf-8
+indent_style = space
+indent_size = 4
+insert_final_newline = true
+
+# Perl
+[**.pl]
+charset = utf-8
+insert_final_newline = true
+[**.pm]
+charset = utf-8
+insert_final_newline = true
+
+# PHP
+[**.php]
+charset = utf-8
+indent_size = 4
+indent_style = space
+end_of_line = lf
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+# Makefiles
+[Makefile]
+indent_style = tab
+
+[**.gmk]
+indent_style = tab
+
+# Configuration Files
+# Matches the exact files either package.json or .travis.yml
+[{package.json,.travis.yml}]
+indent_style = space
+indent_size = 2
+
+# Diff files
+[*.{diff,patch}]
+trim_trailing_whitespace = false
diff --git a/31_Depth_Charge/ruby/depthcharge.rb b/31_Depth_Charge/ruby/depthcharge.rb
new file mode 100755
index 00000000..5ba36ba6
--- /dev/null
+++ b/31_Depth_Charge/ruby/depthcharge.rb
@@ -0,0 +1,211 @@
+#!/usr/bin/ruby
+
+class DepthCharge
+
+  def run_game
+    output_title()
+    while true
+      printf("----------\n")
+      print_instructions()
+      setup_game()
+      printf("\n")
+      game_loop()
+      break if ! get_input_another_game()
+    end
+
+    # 420 PRINT "OK.  HOPE YOU ENJOYED YOURSELF." : GOTO 600
+    printf("OK.  HOPE YOU ENJOYED YOURSELF.\n")
+  end
+
+  def output_title
+    printf("--- DEPTH CHARGE ---\n")
+    printf("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n")
+    printf("\n")
+  end
+
+  def get_input_y_or_n(message)
+    while true
+      print(message)
+
+      value = gets.chomp
+
+      if (value == 'Y' || value == 'y')
+        return true
+      elsif value == 'N' || value == 'n'
+        return false
+      end
+
+      printf("PLEASE ENTER Y/y OR N/n...\n\n")
+    end
+  end
+
+  def get_input_positive_integer(message)
+
+    while true
+      print(message)
+      value = gets.chomp
+      if (value == 'd')
+        debug_game()
+        next
+      end
+
+      the_input = Integer(value) rescue nil
+
+      if the_input == nil || the_input < 0
+        printf("PLEASE ENTER A POSITIVE NUMBER\n\n")
+        next
+
+      end
+
+      return the_input
+    end
+  end
+
+  def get_search_area_dimension
+    # 20 INPUT "DIMENSION OF SEARCH AREA";G: PRINT
+    @search_area_dimension = get_input_positive_integer("DIMENSION OF SEARCH AREA: ")
+    # 30 N=INT(LOG(G)/LOG(2))+1
+
+    @num_tries = Integer(
+      Math.log(@search_area_dimension)/Math.log(2)
+    )
+
+  end
+
+  def print_instructions
+    # 40 PRINT "YOU ARE THE CAPTAIN OF THE DESTROYER USS COMPUTER"
+    # 50 PRINT "AN ENEMY SUB HAS BEEN CAUSING YOU TROUBLE.  YOUR"
+    # 60 PRINT "MISSION IS TO DESTROY IT.  YOU HAVE";N;"SHOTS."
+    # 70 PRINT "SPECIFY DEPTH CHARGE EXPLOSION POINT WITH A"
+    # 80 PRINT "TRIO OF NUMBERS -- THE FIRST TWO ARE THE"
+    # 90 PRINT "SURFACE COORDINATES; THE THIRD IS THE DEPTH."
+    # 100 PRINT : PRINT "GOOD LUCK !": PRINT
+    printf( <<~INSTRUCTIONS
+YOU ARE THE CAPTAIN OF THE DESTROYER USS COMPUTER
+AN ENEMY SUB HAS BEEN CAUSING YOU TROUBLE.  YOUR
+MISSION IS TO DESTROY IT.
+
+SPECIFY DEPTH CHARGE EXPLOSION POINT WITH A
+TRIO OF NUMBERS -- THE FIRST TWO ARE THE
+SURFACE COORDINATES (X, Y):
+     WEST < X < EAST
+    SOUTH < Y < NORTH
+
+THE THIRD IS THE DEPTH (Z):
+  SHALLOW < Z < DEEP
+
+GOOD LUCK !
+
+    INSTRUCTIONS
+    )
+  end
+
+  def debug_game
+    printf("@enemy_x: %d\n", @enemy_x)
+    printf("@enemy_y: %d\n", @enemy_y)
+    printf("@enemy_z: %d\n", @enemy_z)
+    printf("@num_tries: %d\n", @num_tries)
+    printf("@trial: %d\n", @trial)
+    printf("\n")
+  end
+
+  def setup_game
+    get_search_area_dimension()
+    setup_enemy()
+  end
+
+  def setup_enemy
+    # 110 A=INT(G*RND(1)) : B=INT(G*RND(1)) : C=INT(G*RND(1))
+    @enemy_x = rand(1..@search_area_dimension)
+    @enemy_y = rand(1..@search_area_dimension)
+    @enemy_z = rand(1..@search_area_dimension)
+    end
+
+  def game_loop
+    # 120 FOR D=1 TO N : PRINT : PRINT "TRIAL #";D; : INPUT X,Y,Z
+    for @trial in 1..@num_tries do
+      output_game_status()
+
+      @shot_x = get_input_positive_integer("X: ")
+      @shot_y = get_input_positive_integer("Y: ")
+      @shot_z = get_input_positive_integer("Z: ")
+
+      # 130 IF ABS(X-A)+ABS(Y-B)+ABS(Z-C)=0 THEN 300
+      if (
+        (@enemy_x - @shot_x).abs \
+        + (@enemy_y - @shot_y).abs \
+        + (@enemy_z - @shot_z).abs \
+        == 0
+      )
+        you_win()
+        return
+      else
+        # 140 GOSUB 500 : PRINT : NEXT D
+        missed_shot()
+      end
+    end
+
+    printf("\n")
+
+    you_lose()
+
+  end
+
+  def output_game_status
+    printf("YOU HAVE %d SHOTS REMAINING.\n", @num_tries - @trial + 1)
+    printf("TRIAL \#%d\n", @trial)
+  end
+  def you_win
+    printf("B O O M ! ! YOU FOUND IT IN %d TRIES!\n\n", @trial )
+  end
+  def missed_shot
+    missed_directions = []
+
+    # 530 IF X>A THEN PRINT "EAST";
+    # 540 IF X @enemy_x
+      missed_directions.push('TOO FAR EAST')
+    elsif @shot_x < @enemy_x
+      missed_directions.push('TOO FAR WEST')
+    end
+
+    # 510 IF Y>B THEN PRINT "NORTH";
+    # 520 IF Y @enemy_y
+      missed_directions.push('TOO FAR NORTH')
+    elsif @shot_y < @enemy_y
+      missed_directions.push('TOO FAR SOUTH')
+    end
+
+    # 560 IF Z>C THEN PRINT " TOO LOW."
+    # 570 IF Z @enemy_z
+      missed_directions.push('TOO DEEP')
+    elsif @shot_z < @enemy_z
+      missed_directions.push('TOO SHALLOW')
+    end
+
+    # 500 PRINT "SONAR REPORTS SHOT WAS ";
+    printf("SONAR REPORTS SHOT WAS: \n")
+    printf("%s\n", "\t" + missed_directions.join("\n\t"))
+    # 550 IF Y<>B OR X<>A THEN PRINT " AND";
+    # 590 RETURN
+  end
+
+  def you_lose
+    # You took too long!
+    printf("YOU HAVE BEEN TORPEDOED!  ABANDON SHIP!\n")
+    printf("THE SUBMARINE WAS AT %d %d %d\n", @enemy_x, @enemy_y, @enemy_z)
+
+  end
+
+  def get_input_another_game
+    # 400 PRINT : PRINT: INPUT "ANOTHER GAME (Y OR N)";A$
+    return get_input_y_or_n("ANOTHER GAME (Y OR N): ")
+    # 410 IF A$="Y" THEN 100
+  end
+end
+
+game = DepthCharge.new
+game.run_game()
diff --git a/36_Flip_Flop/python/flipflop.py b/36_Flip_Flop/python/flipflop.py
new file mode 100644
index 00000000..9b6e2323
--- /dev/null
+++ b/36_Flip_Flop/python/flipflop.py
@@ -0,0 +1,129 @@
+# Flip Flop
+#
+# The object of this game is to change a row of ten X's
+# X X X X X X X X X X
+# to a row of ten O's:
+# O O O O O O O O O O
+# by typing in a number corresponding
+# to the position of an "X" in the line. On
+# some numbers one position will
+# change while on other numbers, two
+# will change. For example, inputting a 3
+# may reverse the X and O in position 3,
+# but it might possibly reverse some
+# other position too! You ought to be able
+# to change all 10 in 12 or fewer
+# moves. Can you figure out a good win-
+# ning strategy?
+# To reset the line to all X's (same
+# game), type 0 (zero). To start a new
+# game at any point, type 11.
+# The original author of this game was
+# Michael Kass of New Hyde Park, New
+# York.
+import random
+import math
+from typing import Callable, List, Tuple
+
+flip_dict = {"X": "O", "O": "X"}
+
+
+def flip_bits(
+    row: List[str], m: int, n: int, r_function: Callable[[int], float]
+) -> Tuple[List[str], int]:
+    """
+    Function that flips the positions at the computed steps
+    """
+    while m == n:
+        r = r_function(n)
+        n = r - int(math.floor(r))
+        n = int(10 * n)
+        if row[n] == "X":
+            row[n] = "O"
+            break
+        elif row[n] == "O":
+            row[n] = "X"
+    return row, n
+
+
+def print_instructions():
+    print(" " * 32 + "FLIPFLOP")
+    print(" " * 15 + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY")
+    print("\n" * 2)
+    print("THE OBJECT OF THIS PUZZLE IS TO CHANGE THIS:\n")
+    print("X X X X X X X X X X\n")
+    print("TO THIS:\n")
+    print("O O O O O O O O O O\n")
+    print("BY TYPING TH NUMBER CORRESPONDING TO THE POSITION OF THE")
+    print("LETTER ON SOME NUMBERS, ONE POSITION WILL CHANGE, ON")
+    print("OTHERS, TWO WILL CHANGE. TO RESET LINE TO ALL X'S, TYPE 0")
+    print("(ZERO) AND TO START OVER IN THE MIDDLE OF A GAME, TYPE ")
+    print("11 (ELEVEN).\n")
+
+
+def main():
+    q = random.random()
+
+    print("HERE IS THE STARTING LINE OF X'S.\n")
+    # We add an extra 0-th item because this sometimes is set to something
+    # but we never check what it is for completion of the puzzle
+    row = [""] + ["X"] * 10
+    counter_turns = 0
+    n = -1
+    legal_move = True
+    while row[1:] != ["O"] * 10:
+        if legal_move:
+            print(" ".join([str(i) for i in range(1, 11)]))
+            print(" ".join(row[1:]) + "\n")
+        m = input("INPUT THE NUMBER\n")
+        try:
+            m = int(m)
+            if m > 11 or m < 0:
+                raise ValueError()
+        except ValueError:
+            print("ILLEGAL ENTRY--TRY AGAIN")
+            legal_move = False
+            continue
+        legal_move = True
+        if m == 11:
+            # completely reset the puzzle
+            counter_turns = 0
+            row = [""] + ["X"] * 10
+            q = random.random()
+            continue
+        elif m == 0:
+            # reset the board, but not the counter or the random number
+            row = [""] + ["X"] * 10
+        elif m == n:
+            row[n] = flip_dict[row[n]]
+            r_function = lambda n_t: 0.592 * (1 / math.tan(q / n_t + q)) / math.sin(
+                n_t * 2 + q
+            ) - math.cos(n_t)
+            row, n = flip_bits(row, m, n, r_function)
+        else:
+            n = m
+            row[n] = flip_dict[row[n]]
+            r_function = lambda n_t: (
+                math.tan(q + n_t / q - n_t)
+                - math.sin(n_t * 2 + q)
+                + 336 * math.sin(8 * n_t)
+            )
+            row, n = flip_bits(row, m, n, r_function)
+
+        counter_turns += 1
+        print()
+
+    if counter_turns <= 12:
+        print(f"VERY GOOD. YOU GUESSED IT IN ONLY {counter_turns} GUESSES.")
+    else:
+        print(f"TRY HARDER NEXT TIME. IT TOOK YOU {counter_turns} GUESSES.")
+    return
+
+
+if __name__ == "__main__":
+    print_instructions()
+
+    another = ""
+    while another != "NO":
+        main()
+        another = input("DO YOU WANT TO TRY ANOTHER PUZZLE\n")
diff --git a/45_Hello/perl/hello.pl b/45_Hello/perl/hello.pl
new file mode 100644
index 00000000..53590595
--- /dev/null
+++ b/45_Hello/perl/hello.pl
@@ -0,0 +1,136 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+print ' ' x 33 . "HELLO\n";
+print ' ' x 15 . "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n";
+print "\n\n\n";
+
+print "HELLO.  MY NAME IS CREATIVE COMPUTER.\n\n\n";
+print "WHAT'S YOUR NAME?\n";
+chomp( my $N = uc  );
+
+print "\nHI THERE, $N, ARE YOU ENJOYING YOURSELF HERE?\n";
+
+GREET:
+{
+    chomp( my $B = uc  );
+    print "\n";
+
+    if ( $B eq 'YES' ) {
+        print "I'M GLAD TO HEAR THAT, $N.\n\n";
+    }
+    elsif ( $B eq 'NO' ) {
+        print "OH, I'M SORRY TO HEAR THAT, $N. MAYBE WE CAN\n";
+        print "BRIGHTEN UP YOUR VISIT A BIT.\n";
+    }
+    else {
+        print "$N, I DON'T UNDERSTAND YOUR ANSWER OF '$B'.\n";
+        print "PLEASE ANSWER 'YES' OR 'NO'.  DO YOU LIKE IT HERE?\n";
+        redo GREET;
+    }
+}
+
+print "\nSAY, $N, I CAN SOLVE ALL KINDS OF PROBLEMS EXCEPT\n";
+print "THOSE DEALING WITH GREECE.  WHAT KIND OF PROBLEMS DO\n";
+print "YOU HAVE (ANSWER SEX, HEALTH, MONEY, OR JOB)?\n";
+
+ADVICE:
+{
+    chomp( my $C = uc  );
+    print "\n";
+
+    if ( $C eq 'SEX' ) {
+        print "IS YOUR PROBLEM TOO MUCH OR TOO LITTLE?\n";
+
+        SEX:
+        {
+            chomp( my $D = uc  );
+            print "\n";
+
+            if ( $D eq 'TOO MUCH' ) {
+                print "YOU CALL THAT A PROBLEM?!!  I SHOULD HAVE SUCH PROBLEMS!\n";
+                print "IF IT BOTHERS YOU, $N, TAKE A COLD SHOWER.\n";
+            }
+            elsif ( $D eq 'TOO LITTLE' ) {
+                print "WHY ARE YOU HERE IN SUFFERN, $N?  YOU SHOULD BE\n";
+                print "IN TOKYO OR NEW YORK OR AMSTERDAM OR SOMEPLACE WITH SOME\n";
+                print "REAL ACTION.\n";
+            }
+            else {
+                print "DON'T GET ALL SHOOK, $N, JUST ANSWER THE QUESTION\n";
+                print "WITH 'TOO MUCH' OR 'TOO LITTLE'.  WHICH IS IT?\n";
+                redo SEX;
+            }
+        }
+    }
+    elsif ( $C eq 'HEALTH' ) {
+        print "MY ADVICE TO YOU $N IS:\n";
+        print "     1.  TAKE TWO ASPRIN\n";
+        print "     2.  DRINK PLENTY OF FLUIDS (ORANGE JUICE, NOT BEER!)\n";
+        print "     3.  GO TO BED (ALONE)\n";
+    }
+    elsif ( $C eq 'MONEY' ) {
+        print "SORRY, $N, I'M BROKE TOO.  WHY DON'T YOU SELL\n";
+        print "ENCYCLOPEADIAS OR MARRY SOMEONE RICH OR STOP EATING\n";
+        print "SO YOU WON'T NEED SO MUCH MONEY?\n";
+    }
+    elsif ( $C eq 'JOB' ) {
+        print "I CAN SYMPATHIZE WITH YOU $N.  I HAVE TO WORK\n";
+        print "VERY LONG HOURS FOR NO PAY -- AND SOME OF MY BOSSES\n";
+        print "REALLY BEAT ON MY KEYBOARD.  MY ADVICE TO YOU, $N,\n";
+        print "IS TO OPEN A RETAIL COMPUTER STORE.  IT'S GREAT FUN.\n";
+    }
+    else {
+        print "OH, $N, YOUR ANSWER OF '$C' IS GREEK TO ME.\n";
+    }
+
+    MORE:
+    {
+        print "\nANY MORE PROBLEMS YOU WANT SOLVED, $N?\n";
+        chomp( my $E = uc  );
+        print "\n";
+
+        if ( $E eq 'YES' ) {
+            print "WHAT KIND (SEX, MONEY, HEALTH, JOB)?\n";
+            redo ADVICE;
+        }
+        elsif ( $E eq 'NO' ) {
+            print "\nTHAT WILL BE \$5.00 FOR THE ADVICE, $N.\n";
+            print "PLEASE LEAVE THE MONEY ON THE TERMINAL.\n";
+        }
+        else {
+            print "JUST A SIMPLE 'YES' OR 'NO' PLEASE, $N.\n";
+            redo MORE;
+        }
+    }
+
+    sleep 2;
+    print "\n\n\n";
+
+    MONEY:
+    {
+        print "DID YOU LEAVE THE MONEY?\n";
+        chomp( my $G = uc  );
+        print "\n";
+
+        if ( $G eq 'YES' ) {
+            print "HEY, $N??? YOU LEFT NO MONEY AT ALL!\n";
+            print "YOU ARE CHEATING ME OUT OF MY HARD-EARNED LIVING.\n";
+            print "\nWHAT A RIP OFF, $N!!!\n\n";
+        }
+        elsif ( $G eq 'NO' ) {
+            print "THAT'S HONEST, $N, BUT HOW DO YOU EXPECT\n";
+            print "ME TO GO ON WITH MY PSYCHOLOGY STUDIES IF MY PATIENTS\n";
+            print "DON'T PAY THEIR BILLS?\n";
+        }
+        else {
+            print "YOUR ANSWER OF '$G' CONFUSES ME, $N.\n";
+            print "PLEASE RESPOND WITH 'YES' OR 'NO'.\n";
+            redo MONEY;
+        }
+
+        print "\nTAKE A WALK, $N.\n\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/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/61_Math_Dice/perl/mathdice.pl b/61_Math_Dice/perl/mathdice.pl
new file mode 100644
index 00000000..4a2b6f6e
--- /dev/null
+++ b/61_Math_Dice/perl/mathdice.pl
@@ -0,0 +1,122 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+&main;
+
+# Main subroutine
+
+sub main {
+	&print_intro;
+	while (1==1) {
+		&game_play; 					#function that actually plays the game
+	}
+}
+
+sub game_play {
+	my $num = 0;
+	my $sum = 0;
+	my $tries = 0;
+	until ($num == 2) {               	# there are 2 dice rolls so we do it until the num equals 2
+		$num++;
+		my $roll = 1+int rand(6);		# getting a random number between 1 and 6
+		&print_dice($roll);				# function call to print out the dice
+		$sum = $sum + $roll;			# keeping track of the summary
+		#print "Sum: $sum Roll: $roll\n";
+		if ($num == 1) {
+			print "\n   +\n\n";			# if its the first roll then print an addition sign
+		}
+		if ($num == 2) {
+			print "     =? ";			# if its the second roll print the equals sign and wait for an answer
+			my $answer = ;
+			chomp($answer);
+			if ($answer == 0) {
+				die "You input '0', Thanks for playing!\n";
+			}
+			elsif ($answer == $sum) {
+				print "RIGHT!\n\nTHE DICE ROLL AGAIN\n\n";
+			}
+			else {						# code execution if they don't get the right answer
+				print "NO,COUNT THE SPOTS AND GIVE ANOTHER ANSWER\n";
+				print "     =? ";
+				$answer = ;
+				chomp($answer);
+				if ($answer == $sum){
+					print "RIGHT!\n\nTHE DICE ROLL AGAIN\n\n";
+				}
+			    else {
+					print "N0, THE ANSWER IS $sum\n";
+				}
+				
+			}
+		}
+	}
+}
+
+sub print_dice {
+	my $roll = shift;
+	print " -----\n";
+	if ($roll == 1) {
+		&print_blank;
+		&print_one_mid;
+		&print_blank;
+	}
+	if ($roll == 2) {
+		&print_one_left;
+		&print_blank;
+		&print_one_right;
+	}
+	if ($roll == 3) {
+		&print_one_left;
+		&print_one_mid;
+		&print_one_right;
+	}
+	if ($roll == 4) {
+		&print_two;
+		&print_blank;
+		&print_two;
+	}
+	if ($roll == 5) {
+		&print_two;
+		&print_one_mid;
+		&print_two;
+	}
+	if ($roll == 6) {
+		&print_two;
+		&print_two;
+		&print_two;
+	}
+	print " -----\n";
+}
+
+sub print_one_left {
+	print "I *   I\n";
+}
+
+sub print_one_mid {
+	print "I  *  I\n";
+}
+
+sub print_one_right {
+	print "I   * I\n";
+}
+
+sub print_two {
+	print "I * * I\n";
+}
+
+sub print_blank {
+	print "I     I\n";
+}
+
+sub print_intro {
+	my $spaces = " "x31;
+	print "$spaces MATH DICE\n";
+	$spaces = " "x15;
+	print "$spaces CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n";
+	print "THIS PROGRAM GENERATES SUCCESSIVE PICTURES OF TWO DICE.\n";
+	print "WHEN TWO DICE AND AN EQUAL SIGN FOLLOWED BY A QUESTION\n";
+	print "MARK HAVE BEEN PRINTED, TYPE YOUR ANSWER AND THE RETURN KEY.\n";
+	print "TO CONCLUDE THE LESSON, TYPE '0' AS YOUR ANSWER.\n\n\n";
+}
diff --git a/80_Slots/csharp/README.md b/80_Slots/csharp/README.md
index 4daabb5c..4983614c 100644
--- a/80_Slots/csharp/README.md
+++ b/80_Slots/csharp/README.md
@@ -1,3 +1,18 @@
 Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
 
 Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
+
+This C# implementation of slots was done using a [C# script](https://github.com/filipw/dotnet-script).  
+
+# Required
+[.NET Core SDK (i.e., .NET 6.0)](https://dotnet.microsoft.com/en-us/download)
+
+Install dotnet-script.  On the command line run:
+```
+dotnet tool install -g dotnet-script
+```
+
+# Run
+```
+dotnet script .\slots.csx
+```
\ No newline at end of file
diff --git a/80_Slots/csharp/slots.csx b/80_Slots/csharp/slots.csx
new file mode 100644
index 00000000..b812d942
--- /dev/null
+++ b/80_Slots/csharp/slots.csx
@@ -0,0 +1,190 @@
+Print("SLOTS");
+Print("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
+Print(); Print(); Print();
+Print("YOU ARE IN THE H&M CASINO,IN FRONT OF ONE OF OUR");
+Print("ONE-ARM BANDITS. BET FROM $1 TO $100.");
+Print("TO PULL THE ARM, PUNCH THE RETURN KEY AFTER MAKING YOUR BET.");
+
+var _standings = 0;
+
+var play = true;
+while(play)
+{
+    Play();
+    play = PlayAgain();
+}
+
+Done();
+
+public void Play()
+{
+    var bet = GetBet();
+    Print();
+    Ring();
+
+    var random = new Random();
+    var x = GetSlot();
+    var y = GetSlot();
+    var z = GetSlot();
+
+    Print();
+    Print($"{x.ToString()} {y.ToString()} {z.ToString()}");
+
+    if(x == y && x == z)
+    {
+        if(z == Slot.BAR)
+        {
+            // BAR BAR BAR
+            Print();
+            Print("***JACKPOT***");
+            Print("YOU WON!");
+            _standings = (100*bet) + bet + _standings;
+        }
+        else
+        {
+            Print();
+            Print("**TOP DOLLAR**");
+            Print("YOU WON!");
+            _standings = (10*bet) + bet + _standings;
+        }
+    }
+    else if(x == y)
+    {       
+        if(y == Slot.BAR)
+        {
+            DoubleBar(bet);
+        }
+        else
+        {
+            Double(bet);
+        }        
+    }
+    else if(x == z)
+    {
+        if(z == Slot.BAR)
+        {
+            DoubleBar(bet);
+        }
+        else
+        {
+            Lost(bet);
+        }
+    }
+    else if(y == z)
+    {
+        if(z == Slot.BAR)
+        {
+            DoubleBar(bet);
+        }
+        else
+        {
+            Double(bet);
+        }
+    }
+    else
+    {
+        Lost(bet);
+    }
+
+    Print($"YOUR STANDINGS ARE ${_standings}");
+}
+
+public bool PlayAgain()
+{
+    Console.Write("AGAIN? (Y) ");
+    var playAgain = Console.ReadKey(true);
+    Print();
+    return playAgain.Key == ConsoleKey.Y || playAgain.Key == ConsoleKey.Enter;
+}
+
+public void Done()
+{
+    Print();
+    if(_standings < 0)
+    {
+        Print("PAY UP!  PLEASE LEAVE YOUR MONEY ON THE TERMINAL.");
+    }
+    else if (_standings == 0)
+    {
+        Print("HEY, YOU BROKE EVEN.");
+    }
+    else
+    {
+        Print("COLLECT YOUR WINNINGS FROM THE H&M CASHIER");
+    }
+}
+
+// Prints the text provided.  Default is a blank line
+public void Print(string line = "")
+{
+    Console.WriteLine(line);
+}
+
+public int GetBet()
+{
+    Print();
+    Console.Write("YOUR BET ");
+    var betInput = ReadLine();
+    int bet;
+    var inputValid = int.TryParse(betInput, out bet);
+    if (!inputValid)
+    {
+        Print("NUMBER EXPECTED - RETRY");
+        return GetBet();
+    }
+
+    if(bet > 100)
+    {
+        Print("HOUSE LIMITS ARE $100");
+        inputValid = false;
+    }
+    else if(bet < 1)
+    {
+        Print("MINIMUM BET IS $1");
+        inputValid = false;
+    }
+
+    return inputValid ? bet : GetBet();
+}
+
+public enum Slot { BAR, BELL, ORANGE, LEMON, PLUM, CHERRY };
+
+public Slot GetSlot()
+{
+    var rand = new Random();
+    var num = rand.Next(0, 5);
+    return (Slot)num;
+}
+
+public void DoubleBar(int bet)
+{
+    Print();
+    Print("*DOUBLE BAR*");
+    Print("YOU WON!");
+    _standings = (5*bet) + bet + _standings;
+}
+
+public void Double(int bet)
+{
+    Print();
+    Print("DOUBLE!!");
+    Print("YOU WON!");
+    _standings = (2*bet) + bet + _standings;
+}
+
+public void Lost(int bet)
+{
+    Print();
+    Print("YOU LOST.");
+    _standings = _standings - bet;
+}
+
+public void Ring()
+{
+    for(int i = 1; i <= 10; i++)
+    {
+        // https://stackoverflow.com/a/321148/1497
+        Console.Beep();
+        // Console.Beep(800, 501 - (i * 50)); // Uncomment for a fancier bell
+    }
+}
diff --git a/82_Stars/perl/stars.pl b/82_Stars/perl/stars.pl
new file mode 100755
index 00000000..6c1b5392
--- /dev/null
+++ b/82_Stars/perl/stars.pl
@@ -0,0 +1,56 @@
+#!/usr/bin/perl
+
+use v5.11; # for say and use strict
+use warnings;
+
+my $MAX_NUMBER = 100;
+my $MAX_GUESSES = 7;
+
+print<<__END_OF_INTRO;
+                                  Stars
+               Creative Computing  Morristown, New Jersey
+
+
+
+__END_OF_INTRO
+
+print "Do you want instructions? ";
+chomp( my $answer = <> );
+if ( $answer !~ /^N/i ) {
+  print<<__END_OF_INSTRUCTIONS;
+I am thinking of a whole number from 1 to $MAX_NUMBER
+Try to guess my number.  After you guess, I
+will type one or more stars (*).  The more
+stars I type, the closer you are to my number.
+One star (*) means far away, seven stars (*******)
+means really close!  You get $MAX_GUESSES guesses.
+__END_OF_INSTRUCTIONS
+}
+
+
+while (1) {
+  my $number_to_guess = int(rand($MAX_NUMBER) + 1);
+  say "\n\nOK, I am thinking of a number, start guessing.";
+
+  my $guess_number = 1;
+  while ( $guess_number <= $MAX_GUESSES ) {
+    print "\nYour Guess? ";
+    chomp( my $guess = <> );
+    last if $guess == $number_to_guess;
+    $guess_number++;
+    my $difference = abs $guess - $number_to_guess;
+    print '*' if $difference < 2;
+    print '*' if $difference < 4;
+    print '*' if $difference < 8;
+    print '*' if $difference < 16;
+    print '*' if $difference < 32;
+    print '*' if $difference < 64;
+    print "*\n";
+  }
+  if ( $guess_number > $MAX_GUESSES ) { # didn't guess
+    say "\nSorry, that's $MAX_GUESSES guesses, number was $number_to_guess";
+  } else { # winner!
+    say '*' x 50, '!!!';
+    say "You got it in $guess_number guesses!!!  Let's play again...";
+  }
+}
diff --git a/85_Synonym/perl/README.md b/85_Synonym/perl/README.md
index e69c8b81..f947aea0 100644
--- a/85_Synonym/perl/README.md
+++ b/85_Synonym/perl/README.md
@@ -1,3 +1,7 @@
 Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
 
 Conversion to [Perl](https://www.perl.org/)
+
+I used List::Util to do all the heavy work to show that perl can handle all the various
+array functions.  It would be interesting to see a version that handled all of this 
+manually as there ended up being very little code left in this program.
diff --git a/85_Synonym/perl/synonym.pl b/85_Synonym/perl/synonym.pl
new file mode 100755
index 00000000..37f32366
--- /dev/null
+++ b/85_Synonym/perl/synonym.pl
@@ -0,0 +1,58 @@
+#!/usr/bin/perl
+
+use v5.32; # for sample from List::Util, also includes 'use strict'
+use warnings; # always a good idea
+
+use List::Util qw/ any sample shuffle /; # Rather than write our own utilities, use the built in ones
+
+my @correct = qw/ Right Correct Fine Good! Check /;
+
+# lowercase all words here
+my @synonyms = (
+  [ qw/ first start beginning onset initial / ],
+  [ qw/ similar alike same like resembling / ],
+  [ qw/ model pattern prototype standard criterion /],
+  [ qw/ small insignificant little tiny minute /],
+  [ qw/ stop halt stay arrest check standstill /],
+  [ qw/ house dwelling residense domicile lodging habitation /],
+  [ qw/ pit hole hollow well gulf chasm abyss /],
+  [ qw/ push shove thrust prod poke butt press /],
+  [ qw/ red rouge scarlet crimson flame ruby /],
+  [ qw/ pain suffering hurt misery distress ache discomfort /],
+);
+
+print <<__END_OF_INTRO;
+                                 Synonym
+                Creative Computing  Morristown, New Jersey
+
+
+
+A synonym of a word means another word in the English
+language which has the same or very nearly the same meaning
+I choose a word -- you type a synonym.
+If you can't think of a synonym, type the word 'HELP'
+and I will tell you a synonym.
+
+__END_OF_INTRO
+
+foreach my $drill ( shuffle @synonyms ) {
+  my $word = $drill->[0];
+  my @answers = $drill->@[1 .. $drill->$#*];
+  print "     What is a synonym of $word? ";
+  my $response = <>;
+  chomp $response;
+  $response = lc $response;
+
+  if ( $response eq 'help' ) {
+    say "**** A synonym of $word is ", sample(1, @answers);
+    redo;
+  } elsif ( not any { $response eq $_ } @answers ) {
+    say '     Try again.';
+    redo;
+  } else {
+    say sample 1, @correct;
+  }
+}
+
+say "\nSynonym drill completed.";
+
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/csharp/Tower.sln b/90_Tower/csharp/Tower.sln
similarity index 100%
rename from 90 Tower/csharp/Tower.sln
rename to 90_Tower/csharp/Tower.sln
diff --git a/90 Tower/csharp/Tower/Game.cs b/90_Tower/csharp/Tower/Game.cs
similarity index 100%
rename from 90 Tower/csharp/Tower/Game.cs
rename to 90_Tower/csharp/Tower/Game.cs
diff --git a/90 Tower/csharp/Tower/Models/Needle.cs b/90_Tower/csharp/Tower/Models/Needle.cs
similarity index 100%
rename from 90 Tower/csharp/Tower/Models/Needle.cs
rename to 90_Tower/csharp/Tower/Models/Needle.cs
diff --git a/90 Tower/csharp/Tower/Models/Towers.cs b/90_Tower/csharp/Tower/Models/Towers.cs
similarity index 100%
rename from 90 Tower/csharp/Tower/Models/Towers.cs
rename to 90_Tower/csharp/Tower/Models/Towers.cs
diff --git a/90 Tower/csharp/Tower/Program.cs b/90_Tower/csharp/Tower/Program.cs
similarity index 100%
rename from 90 Tower/csharp/Tower/Program.cs
rename to 90_Tower/csharp/Tower/Program.cs
diff --git a/90 Tower/csharp/Tower/Resources/Congratulations.txt b/90_Tower/csharp/Tower/Resources/Congratulations.txt
similarity index 100%
rename from 90 Tower/csharp/Tower/Resources/Congratulations.txt
rename to 90_Tower/csharp/Tower/Resources/Congratulations.txt
diff --git a/90 Tower/csharp/Tower/Resources/DiskCountPrompt.txt b/90_Tower/csharp/Tower/Resources/DiskCountPrompt.txt
similarity index 100%
rename from 90 Tower/csharp/Tower/Resources/DiskCountPrompt.txt
rename to 90_Tower/csharp/Tower/Resources/DiskCountPrompt.txt
diff --git a/90 Tower/csharp/Tower/Resources/DiskCountQuit.txt b/90_Tower/csharp/Tower/Resources/DiskCountQuit.txt
similarity index 100%
rename from 90 Tower/csharp/Tower/Resources/DiskCountQuit.txt
rename to 90_Tower/csharp/Tower/Resources/DiskCountQuit.txt
diff --git a/90 Tower/csharp/Tower/Resources/DiskCountRetry.txt b/90_Tower/csharp/Tower/Resources/DiskCountRetry.txt
similarity index 100%
rename from 90 Tower/csharp/Tower/Resources/DiskCountRetry.txt
rename to 90_Tower/csharp/Tower/Resources/DiskCountRetry.txt
diff --git a/90 Tower/csharp/Tower/Resources/DiskNotInPlay.txt b/90_Tower/csharp/Tower/Resources/DiskNotInPlay.txt
similarity index 100%
rename from 90 Tower/csharp/Tower/Resources/DiskNotInPlay.txt
rename to 90_Tower/csharp/Tower/Resources/DiskNotInPlay.txt
diff --git a/90 Tower/csharp/Tower/Resources/DiskPrompt.txt b/90_Tower/csharp/Tower/Resources/DiskPrompt.txt
similarity index 100%
rename from 90 Tower/csharp/Tower/Resources/DiskPrompt.txt
rename to 90_Tower/csharp/Tower/Resources/DiskPrompt.txt
diff --git a/90 Tower/csharp/Tower/Resources/DiskQuit.txt b/90_Tower/csharp/Tower/Resources/DiskQuit.txt
similarity index 100%
rename from 90 Tower/csharp/Tower/Resources/DiskQuit.txt
rename to 90_Tower/csharp/Tower/Resources/DiskQuit.txt
diff --git a/90 Tower/csharp/Tower/Resources/DiskRetry.txt b/90_Tower/csharp/Tower/Resources/DiskRetry.txt
similarity index 100%
rename from 90 Tower/csharp/Tower/Resources/DiskRetry.txt
rename to 90_Tower/csharp/Tower/Resources/DiskRetry.txt
diff --git a/90 Tower/csharp/Tower/Resources/DiskUnavailable.txt b/90_Tower/csharp/Tower/Resources/DiskUnavailable.txt
similarity index 100%
rename from 90 Tower/csharp/Tower/Resources/DiskUnavailable.txt
rename to 90_Tower/csharp/Tower/Resources/DiskUnavailable.txt
diff --git a/90 Tower/csharp/Tower/Resources/IllegalMove.txt b/90_Tower/csharp/Tower/Resources/IllegalMove.txt
similarity index 100%
rename from 90 Tower/csharp/Tower/Resources/IllegalMove.txt
rename to 90_Tower/csharp/Tower/Resources/IllegalMove.txt
diff --git a/90 Tower/csharp/Tower/Resources/Instructions.txt b/90_Tower/csharp/Tower/Resources/Instructions.txt
similarity index 100%
rename from 90 Tower/csharp/Tower/Resources/Instructions.txt
rename to 90_Tower/csharp/Tower/Resources/Instructions.txt
diff --git a/90 Tower/csharp/Tower/Resources/Intro.txt b/90_Tower/csharp/Tower/Resources/Intro.txt
similarity index 100%
rename from 90 Tower/csharp/Tower/Resources/Intro.txt
rename to 90_Tower/csharp/Tower/Resources/Intro.txt
diff --git a/90 Tower/csharp/Tower/Resources/NeedlePrompt.txt b/90_Tower/csharp/Tower/Resources/NeedlePrompt.txt
similarity index 100%
rename from 90 Tower/csharp/Tower/Resources/NeedlePrompt.txt
rename to 90_Tower/csharp/Tower/Resources/NeedlePrompt.txt
diff --git a/90 Tower/csharp/Tower/Resources/NeedleQuit.txt b/90_Tower/csharp/Tower/Resources/NeedleQuit.txt
similarity index 100%
rename from 90 Tower/csharp/Tower/Resources/NeedleQuit.txt
rename to 90_Tower/csharp/Tower/Resources/NeedleQuit.txt
diff --git a/90 Tower/csharp/Tower/Resources/NeedleRetry.txt b/90_Tower/csharp/Tower/Resources/NeedleRetry.txt
similarity index 100%
rename from 90 Tower/csharp/Tower/Resources/NeedleRetry.txt
rename to 90_Tower/csharp/Tower/Resources/NeedleRetry.txt
diff --git a/90 Tower/csharp/Tower/Resources/PlayAgainPrompt.txt b/90_Tower/csharp/Tower/Resources/PlayAgainPrompt.txt
similarity index 100%
rename from 90 Tower/csharp/Tower/Resources/PlayAgainPrompt.txt
rename to 90_Tower/csharp/Tower/Resources/PlayAgainPrompt.txt
diff --git a/90 Tower/csharp/Tower/Resources/Strings.cs b/90_Tower/csharp/Tower/Resources/Strings.cs
similarity index 100%
rename from 90 Tower/csharp/Tower/Resources/Strings.cs
rename to 90_Tower/csharp/Tower/Resources/Strings.cs
diff --git a/90 Tower/csharp/Tower/Resources/TaskFinished.txt b/90_Tower/csharp/Tower/Resources/TaskFinished.txt
similarity index 100%
rename from 90 Tower/csharp/Tower/Resources/TaskFinished.txt
rename to 90_Tower/csharp/Tower/Resources/TaskFinished.txt
diff --git a/90 Tower/csharp/Tower/Resources/Thanks.txt b/90_Tower/csharp/Tower/Resources/Thanks.txt
similarity index 100%
rename from 90 Tower/csharp/Tower/Resources/Thanks.txt
rename to 90_Tower/csharp/Tower/Resources/Thanks.txt
diff --git a/90 Tower/csharp/Tower/Resources/Title.txt b/90_Tower/csharp/Tower/Resources/Title.txt
similarity index 100%
rename from 90 Tower/csharp/Tower/Resources/Title.txt
rename to 90_Tower/csharp/Tower/Resources/Title.txt
diff --git a/90 Tower/csharp/Tower/Resources/TooManyMoves.txt b/90_Tower/csharp/Tower/Resources/TooManyMoves.txt
similarity index 100%
rename from 90 Tower/csharp/Tower/Resources/TooManyMoves.txt
rename to 90_Tower/csharp/Tower/Resources/TooManyMoves.txt
diff --git a/90 Tower/csharp/Tower/Resources/YesNoPrompt.txt b/90_Tower/csharp/Tower/Resources/YesNoPrompt.txt
similarity index 100%
rename from 90 Tower/csharp/Tower/Resources/YesNoPrompt.txt
rename to 90_Tower/csharp/Tower/Resources/YesNoPrompt.txt
diff --git a/90 Tower/csharp/Tower/Tower.csproj b/90_Tower/csharp/Tower/Tower.csproj
similarity index 100%
rename from 90 Tower/csharp/Tower/Tower.csproj
rename to 90_Tower/csharp/Tower/Tower.csproj
diff --git a/90 Tower/csharp/Tower/UI/Input.cs b/90_Tower/csharp/Tower/UI/Input.cs
similarity index 100%
rename from 90 Tower/csharp/Tower/UI/Input.cs
rename to 90_Tower/csharp/Tower/UI/Input.cs
diff --git a/90 Tower/csharp/Tower/UI/Prompt.cs b/90_Tower/csharp/Tower/UI/Prompt.cs
similarity index 100%
rename from 90 Tower/csharp/Tower/UI/Prompt.cs
rename to 90_Tower/csharp/Tower/UI/Prompt.cs
diff --git a/90 Tower/csharp/Tower/UI/TowerDisplay.cs b/90_Tower/csharp/Tower/UI/TowerDisplay.cs
similarity index 100%
rename from 90 Tower/csharp/Tower/UI/TowerDisplay.cs
rename to 90_Tower/csharp/Tower/UI/TowerDisplay.cs
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/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/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/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 :