diff --git a/01_Acey_Ducey/README.md b/01_Acey_Ducey/README.md index 071a80e3..3434eb8e 100644 --- a/01_Acey_Ducey/README.md +++ b/01_Acey_Ducey/README.md @@ -1,7 +1,16 @@ ### Acey Ducey -As published in Basic Computer Games (1978) -https://www.atariarchives.org/basicgames/showpage.php?page=2 +This is a simulation of the Acey Ducey card game. In the game, the dealer (the computer) deals two cards face up. You have an option to bet or not to bet depending on whether or not you feel the next card dealt will have a value between the first two. + +Your initial money is set to $100; you may want to alter this value if you want to start with more or less than $100. The game keeps going on until you lose all your money or interrupt the program. + +The original program author was Bill Palmby of Prairie View, Illinois. + +--- + +As published in Basic Computer Games (1978): +- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=2) +- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=17) Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html 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 991e3b5c..61c362a7 100644 --- a/01_Acey_Ducey/javascript/aceyducey.html +++ b/01_Acey_Ducey/javascript/aceyducey.html @@ -1,9 +1,7 @@ - - + + + ACEY DUCEY - - -

+
+

 
-
-
diff --git a/01_Acey_Ducey/javascript/aceyducey.js b/01_Acey_Ducey/javascript/aceyducey.js
index d0983443..8bf5e9e1 100644
--- a/01_Acey_Ducey/javascript/aceyducey.js
+++ b/01_Acey_Ducey/javascript/aceyducey.js
@@ -1,135 +1,216 @@
-// ACEY DUCEY
-//
-// Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
-//
+// UTILITY VARIABLES
 
-function print(str)
-{
-    document.getElementById("output").appendChild(document.createTextNode(str));
+// By default:
+// — Browsers have a window object
+// — Node.js does not
+// Checking for an undefined window object is a loose check
+// to enable browser and Node.js support
+const isRunningInBrowser = typeof window !== 'undefined';
+
+// To easily validate input strings with utility functions
+const validLowerCaseYesStrings = ['yes', 'y'];
+const validLowerCaseNoStrings = ['no', 'n'];
+const validLowerCaseYesAndNoStrings = [
+    ...validLowerCaseYesStrings,
+    ...validLowerCaseNoStrings,
+];
+// UTILITY VARIABLES
+
+// Function to get a random number (card) 2-14 (ACE is 14)
+function getRandomCard() {
+    // In our game, the value of ACE is greater than face cards;
+    // instead of having the value of ACE be 1, we’ll have it be 14.
+    // So, we want to shift the range of random numbers from 1-13 to 2-14
+    let min = 2;
+    let max = 14;
+    // Return random integer between two values, inclusive
+    return Math.floor(Math.random() * (max - min + 1) + min);
 }
 
-function input()
-{
-    var input_element;
-    var input_str;
-    
-    return new Promise(function (resolve) {
-                       input_element = document.createElement("INPUT");
-                       
-                       print("? ");
-                       input_element.setAttribute("type", "text");
-                       input_element.setAttribute("length", "50");
-                       document.getElementById("output").appendChild(input_element);
-                       input_element.focus();
-                       input_str = undefined;
-                       input_element.addEventListener("keydown", function (event) {
-                                                      if (event.keyCode == 13) {
-                                                      input_str = input_element.value;
-                                                      document.getElementById("output").removeChild(input_element);
-                                                      print(input_str);
-                                                      print("\n");
-                                                      resolve(input_str);
-                                                      }
-                                                      });
-                       });
+function newGameCards() {
+    let cardOne = getRandomCard();
+    let cardTwo = getRandomCard();
+    let cardThree = getRandomCard();
+    // We want:
+    // 1. cardOne and cardTwo to be different cards
+    // 2. cardOne to be lower than cardTwo
+    // So, while cardOne is greater than or equal too cardTwo
+    // we will continue to generate random cards.
+    while (cardOne >= cardTwo) {
+        cardOne = getRandomCard();
+        cardTwo = getRandomCard();
+    }
+    return [cardOne, cardTwo, cardThree];
 }
 
-function tab(space)
-{
-    var str = "";
-    while (space-- > 0)
-        str += " ";
-    return str;
+// Function to get card value
+function getCardValue(card) {
+    let faceOrAce = {
+        11: 'JACK',
+        12: 'QUEEN',
+        13: 'KING',
+        14: 'ACE',
+    };
+    // If card value matches a key in faceOrAce, use faceOrAce value;
+    // Else, return undefined and handle with the Nullish Coalescing Operator (??)
+    // and default to card value.
+    let cardValue = faceOrAce[card] ?? card;
+    return cardValue;
 }
 
-print(tab(26) + "ACEY DUCEY CARD GAME\n");
-print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
-print("\n");
-print("\n");
-print("ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER\n");
-print("THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP\n");
-print("YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING\n");
-print("ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE\n");
-print("A VALUE BETWEEN THE FIRST TWO.\n");
-print("IF YOU DO NOT WANT TO BET, INPUT A 0\n");
+print(spaces(26) + 'ACEY DUCEY CARD GAME');
+print(spaces(15) + 'CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n\n');
+print('ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER');
+print('THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP');
+print('YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING');
+print('ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE');
+print('A VALUE BETWEEN THE FIRST TWO.');
+print("IF YOU DO NOT WANT TO BET, INPUT '0'");
 
-function show_card(card)
-{
-    if (card < 11)
-        print(card + "\n");
-    else if (card == 11)
-        print("JACK\n");
-    else if (card == 12)
-        print("QUEEN\n");
-    else if (card == 13)
-        print("KING\n");
-    else
-        print("ACE\n");
-}
+main();
 
-// Main program
-async function main()
-{
-    q = 100;
-    while (1) {
-        print("YOU NOW HAVE " + q + " DOLLARS.\n");
-        print("\n");
-        
-        do {
-            print("HERE ARE YOUR NEXT TWO CARDS: \n");
-            do {
-                a = Math.floor(Math.random() * 13 + 2);
-                b = Math.floor(Math.random() * 13 + 2);
-            } while (a >= b) ;
-            show_card(a);
-            show_card(b);
-            print("\n");
-            while (1) {
-                print("\n");
-                print("WHAT IS YOUR BET");
-                m = parseInt(await input());
-                if (m > 0) {
-                    if (m > q) {
-                        print("SORRY, MY FRIEND, BUT YOU BET TOO MUCH.\n");
-                        print("YOU HAVE ONLY " + q + "DOLLARS TO BET.\n");
-                        continue;
-                    }
-                    break;
-                }
-                m = 0;
-                print("CHICKEN!!\n");
-                print("\n");
-                break;
-            }
-        } while (m == 0) ;
-        c = Math.floor(Math.random() * 13 + 2);
-        show_card(c);
-        if (c > a && c < b) {
-            print("YOU WIN!!!\n");
-            q = q + m;
-        } else {
-            print("SORRY, YOU LOSE\n");
-            if (m >= q) {
-                print("\n");
-                print("\n");
-                print("SORRY, FRIEND, BUT YOU BLEW YOUR WAD.\n");
-                print("\n");
-                print("\n");
-                print("TRY AGAIN (YES OR NO)");
-                a = await input();
-                print("\n");
-                print("\n");
-                if (a == "YES") {
-                    q = 100;
+async function main() {
+    let bet;
+    let availableDollars = 100;
+
+    // Loop game forever
+    while (true) {
+        let [cardOne, cardTwo, cardThree] = newGameCards();
+
+        print(`YOU NOW HAVE ${availableDollars} DOLLARS.\n`);
+
+        print('HERE ARE YOUR NEXT TWO CARDS: ');
+        print(getCardValue(cardOne));
+        print(getCardValue(cardTwo));
+        print('');
+
+        // Loop until receiving a valid bet
+        let validBet = false;
+        while (!validBet) {
+            print('\nWHAT IS YOUR BET? ');
+            bet = parseInt(await input(), 10);
+            let minimumRequiredBet = 0;
+            if (bet > minimumRequiredBet) {
+                if (bet > availableDollars) {
+                    print('SORRY, MY FRIEND, BUT YOU BET TOO MUCH.');
+                    print(`YOU HAVE ONLY ${availableDollars} DOLLARS TO BET.`);
                 } else {
-                    print("O.K., HOPE YOU HAD FUN!");
+                    validBet = true;
+                }
+            } else {
+                // Does not meet minimum required bet
+                print('CHICKEN!!');
+                print('');
+            }
+        }
+
+        print('\n\nHERE IS THE CARD WE DREW: ');
+        print(getCardValue(cardThree));
+
+        // Determine if player won or lost
+        if (cardThree > cardOne && cardThree < cardTwo) {
+            print('YOU WIN!!!');
+            availableDollars = availableDollars + bet;
+        } else {
+            print('SORRY, YOU LOSE');
+
+            if (bet >= availableDollars) {
+                print('');
+                print('');
+                print('SORRY, FRIEND, BUT YOU BLEW YOUR WAD.');
+                print('');
+                print('');
+                print('TRY AGAIN (YES OR NO)');
+
+                let tryAgainInput = await input();
+
+                print('');
+                print('');
+
+                if (isValidYesNoString(tryAgainInput)) {
+                    availableDollars = 100;
+                } else {
+                    print('O.K., HOPE YOU HAD FUN!');
                     break;
                 }
             } else {
-                q = q - m;
+                availableDollars = availableDollars - bet;
             }
         }
     }
 }
 
-main();
+// UTILITY FUNCTIONS
+function isValidYesNoString(string) {
+    return validLowerCaseYesAndNoStrings.includes(string.toLowerCase());
+}
+
+function isValidYesString(string) {
+    return validLowerCaseYesStrings.includes(string.toLowerCase());
+}
+
+function isValidNoString(string) {
+    return validLowerCaseNoStrings.includes(string.toLowerCase());
+}
+
+function print(string) {
+    if (isRunningInBrowser) {
+        // Adds trailing newline to match console.log behavior
+        document
+            .getElementById('output')
+            .appendChild(document.createTextNode(string + '\n'));
+    } else {
+        console.log(string);
+    }
+}
+
+function input() {
+    if (isRunningInBrowser) {
+        // Accept input from the browser DOM input
+        return new Promise((resolve) => {
+            const outputElement = document.querySelector('#output');
+            const inputElement = document.createElement('input');
+            outputElement.append(inputElement);
+            inputElement.focus();
+
+            inputElement.addEventListener('keydown', (event) => {
+                if (event.key === 'Enter') {
+                    const result = inputElement.value;
+                    inputElement.remove();
+                    print(result);
+                    print('');
+                    resolve(result);
+                }
+            });
+        });
+    } else {
+        // Accept input from the command line in Node.js
+        // See: https://nodejs.dev/learn/accept-input-from-the-command-line-in-nodejs
+        return new Promise(function (resolve) {
+            const readline = require('readline').createInterface({
+                input: process.stdin,
+                output: process.stdout,
+            });
+            readline.question('', function (input) {
+                resolve(input);
+                readline.close();
+            });
+        });
+    }
+}
+
+function printInline(string) {
+    if (isRunningInBrowser) {
+        document
+            .getElementById('output')
+            .appendChild(document.createTextNode(string));
+    } else {
+        process.stdout.write(string);
+    }
+}
+
+function spaces(numberOfSpaces) {
+    return ' '.repeat(numberOfSpaces);
+}
+
+// UTILITY FUNCTIONS
diff --git a/01_Acey_Ducey/pascal/object-pascal/game.pas b/01_Acey_Ducey/pascal/object-pascal/game.pas
index 1a263159..1e983f7c 100644
--- a/01_Acey_Ducey/pascal/object-pascal/game.pas
+++ b/01_Acey_Ducey/pascal/object-pascal/game.pas
@@ -85,6 +85,7 @@ end;
 
 constructor TGame.Create;
 begin
+  Randomize;
   FDeck:= TDeck.Create;
 end;
 
@@ -99,7 +100,6 @@ begin
   ClrScr;
   PrintGreeting;
   repeat
-    Randomize;
     FStash:= 100;
     repeat
       PrintBalance;
diff --git a/01_Acey_Ducey/pascal/simple/aceyducey.pas b/01_Acey_Ducey/pascal/simple/aceyducey.pas
index 35a9dd64..fa049eb0 100644
--- a/01_Acey_Ducey/pascal/simple/aceyducey.pas
+++ b/01_Acey_Ducey/pascal/simple/aceyducey.pas
@@ -118,10 +118,10 @@ begin
 end;
 
 begin
+  Randomize;
   ClrScr;
   PrintGreeting;
   repeat
-    Randomize;
     Stash:= 100;
     repeat
       PrintBalance;
diff --git a/01_Acey_Ducey/python/acey_ducey.py b/01_Acey_Ducey/python/acey_ducey.py
index 6832c7e0..8461dad1 100644
--- a/01_Acey_Ducey/python/acey_ducey.py
+++ b/01_Acey_Ducey/python/acey_ducey.py
@@ -78,7 +78,7 @@ if __name__ == "__main__":
         """
 Acey-Ducey is played in the following manner
 The dealer (computer) deals two cards face up
-You have an option to be or not bet depending
+You have an option to bet or not bet depending
 on whether or not you feel the card will have
 a value between the first two.
 If you do not want to bet, input a 0
diff --git a/01_Acey_Ducey/python/acey_ducey_oo.py b/01_Acey_Ducey/python/acey_ducey_oo.py
new file mode 100644
index 00000000..325af4b4
--- /dev/null
+++ b/01_Acey_Ducey/python/acey_ducey_oo.py
@@ -0,0 +1,128 @@
+#
+# 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) <= 3:
+                    print('You ran out of cards. Game over.')
+                    self.not_done = False
+                    break
+
+                self.card_a = self.deck.deal()
+                self.card_b = self.deck.deal()
+
+        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/python/aceyducey.py b/01_Acey_Ducey/python/aceyducey.py
index 9aaef681..87147181 100644
--- a/01_Acey_Ducey/python/aceyducey.py
+++ b/01_Acey_Ducey/python/aceyducey.py
@@ -174,7 +174,7 @@ print("OK Hope you had fun\n")
 #
 #   Give the user the ability to quit the game, perhaps
 #   by typing "quit" instead of making a bet.  Provide a
-#   final assement based on how much of the original
+#   final assessment based on how much of the original
 #   bankroll they have left.
 #
 #   Or have the game run for a set number of rounds or
diff --git a/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/02_Amazing/README.md b/02_Amazing/README.md
index 1e646723..8f7b271b 100644
--- a/02_Amazing/README.md
+++ b/02_Amazing/README.md
@@ -1,7 +1,14 @@
 ### Amazing
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=3
+This program will print out a different maze every time it is run and guarantees only one path through. You can choose the dimensions of the maze — i.e. the number of squares wide and long.
+
+The original program author was Jack Hauber of Windsor, Connecticut.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=3)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=18)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/03_Animal/README.md b/03_Animal/README.md
index 69256d87..defaa735 100644
--- a/03_Animal/README.md
+++ b/03_Animal/README.md
@@ -1,7 +1,22 @@
 ### Animal
 
+Unlike other computer games in which the computer picks a number or letter and you must guess what it is, in this game _you_ think of an animal and the _computer_ asks you questions and tries to guess the name of your animal. If the computer guesses incorrectly, it will ask you for a question that differentiates the animal you were thinking of. In this way the computer “learns” new animals. Questions to differentiate new animals should be input without a question mark.
+
+This version of the game does not have a SAVE feature. If your system allows, you may modify the program to save and reload the array when you want to play the game again. This way you can save what the computer learns over a series of games.
+
+At any time if you reply “LIST” to the question “ARE YOU THINKING OF AN ANIMAL,” the computer will tell you all the animals it knows so far.
+
+The program starts originally by knowing only FISH and BIRD. As you build up a file of animals you should use broad, general questions first and then narrow down to more specific ones with later animals. For example, if an elephant was to be your first animal, the computer would ask for a question to distinguish an elephant from a bird. Naturally, there are hundreds of possibilities, however, if you plan to build a large file of animals a good question would be “IS IT A MAMMAL.”
+
+This program can be easily modified to deal with categories of things other than animals by simply modifying the initial data and the dialogue references to animals. In an educational environment, this would be a valuable program to teach the distinguishing characteristics of many classes of objects — rock formations, geography, marine life, cell structures, etc.
+
+Originally developed by Arthur Luehrmann at Dartmouth College, Animal was subsequently shortened and modified by Nathan Teichholtz at DEC and Steve North at Creative Computing.
+
+---
+
 As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=4
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=4)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=19)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
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/03_Animal/perl/animal.pl b/03_Animal/perl/animal.pl
new file mode 100755
index 00000000..a7c8bf9e
--- /dev/null
+++ b/03_Animal/perl/animal.pl
@@ -0,0 +1,223 @@
+#!/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';
+
+# The Perl ref() built-in returns 'HASH' for a hash reference. But we
+# make it a manifest constant just to avoid typos.
+use constant REF_HASH   => ref {};
+
+print <<'EOD';
+                                ANIMAL
+               Creative Computing  Morristown, New Jersey
+
+
+
+Play 'Guess the Animal'
+Think of an animal and the computer will try to guess it.
+
+EOD
+
+# We keep the accumulated data in a tree structure, initialized here. As
+# we accumulate animals, we replace the 'yes' or 'no' keys with new hash
+# references.
+my $database = {
+    question    => 'Does it swim',  # Initial question
+    yes         => 'fish',          # Result of answering 'y'
+    no          => 'bird',          # Result of answering 'n'
+};
+
+while ( 1 ) {
+
+    my $resp = get_input(
+        'Are you thinking of an an animal? [y/n/list]: '
+    );
+
+    if ( $resp =~ m/ \A y /smxi ) {
+        # If we got an answer beginning with 'y', walk the database
+        walk_tree( $database );
+    } elsif ( $resp =~ m/ \A list \z /smxi ) {
+        # If we got 'list', list the currently-known animals.
+        say '';
+        say 'Animals I already know are:';
+        say "    $_" for sort( list_animals( $database ) );
+    }
+}
+
+# Get input from the user. The arguments are:
+# * The prompt
+# * A reference to validation code. This code receives the response in
+#   $ARG and returns true for a valid response.
+# * A warning to print if the response is not valid. This must end in a
+#   return.
+# The first valid response is returned. An end-of-file terminates the
+# script.
+sub get_input {
+    my ( $prompt, $validate, $warning ) = @ARG;
+
+    # If no validator is passed, default to one that always returns
+    # true.
+    $validate ||= sub { 1 };
+
+    # Create the readline object. The 'state' causes the variable to be
+    # initialized only once, no matter how many times this subroutine is
+    # called. The do { ... } is a compound statement used because we
+    # need to tweak the created object before we store it.
+    state $term = do {
+        my $obj = Term::ReadLine->new( 'animal' );
+        $obj->ornaments( 0 );
+        $obj;
+    };
+
+    while ( 1 ) {   # Iterate indefinitely
+
+        # Read the input into the topic variable, localized to prevent
+        # Spooky Action at a Distance. We exit on undef, which signals
+        # end-of-file.
+        exit unless defined( local $ARG = $term->readline( $prompt ) );
+
+        # Return the input if it is valid.
+        return $ARG if $validate->();
+
+        # Issue the warning, and go around the merry-go-round again.
+        warn $warning;
+    }
+}
+
+# Get a yes-or-no answer. The argument is the prompt, which will have
+# '? [y/n]: ' appended. The donkey work is done by get_input(), which is
+# requested to validate the response as beginning with 'y' or 'n',
+# case-insensitive. The return is a true value for 'y' and a false value
+# for 'n'.
+sub get_yes_no {
+    my ( $prompt ) = @ARG;
+    state $map_answer = {
+        n   => 0,
+        y   => 1,
+    };
+    my $resp = lc get_input(
+        "$prompt? [y/n]: ",
+        sub { m/ \A [yn] /smxi },
+        "Please respond 'y' or 'n'\n",
+    );
+    return $map_answer->{ substr $resp, 0, 1 };
+}
+
+# Recurse through the database, returning the names of all animals in
+# it, in an undefined order.
+sub list_animals {
+    my ( $node ) = @ARG;
+    return $node unless REF_HASH eq ref $node;
+    return( map { list_animals( $node->{$_} ) } qw{ yes no } );
+}
+
+# Find or create the desired animal.
+# Ask the question stored in the node given in its argument. If the key
+# selected by the answer ('yes' or 'no') is another node, recurse. If it
+# is an animal name, confirm it, or add a new animal as appropriate.
+sub walk_tree {
+    my ( $node ) = @ARG;
+
+    # Ask the question associated with this node. Turn the true/false
+    # response into 'yes' or 'no', since those are the names of the
+    # respective keys.
+    my $resp = get_yes_no ( $node->{question} ) ? 'yes' : 'no';
+
+    # Chose the datum for the response.
+    my $choice = $node->{ $resp };
+
+    # If the datum is a hash reference
+    if ( REF_HASH eq ref $choice ) {
+
+        # Recurse into it
+        walk_tree( $choice );
+
+    # Otherwise it is an actual animal (i.e. terminal node). Check it.
+    } else {
+
+        # If this is not the animal the player was thinking of
+        unless ( get_yes_no( "Is it a $choice" ) ) {
+
+            # Find out what animal the player was thinking of
+            my $animal = lc get_input(
+                'The animal you were thinking of was a ',
+            );
+
+            # Get a yes/no question that distinguishes the animal the
+            # player was thinking of from the animal we found in the
+            # tree.
+            say 'Please type in a question that would distinguish a';
+            my $question = get_input( "$animal from a $choice: " );
+
+            # Find out whether the new animal is selected by 'yes' or
+            # 'no'. If 'no', swap the original animal with the new one
+            # for convenience.
+            ( $choice, $animal ) = ( $animal, $choice ) if get_yes_no(
+                "For a $animal the answer would be",
+            );
+
+            # Replace the animal we originally found by a new node
+            # giving the original animal, the new animal, and the
+            # question that distinguishes them.
+            $node->{ $resp } = {
+                question    => $question,
+                no          => $animal,
+                yes         => $choice,
+            };
+        }
+
+        # Find out if the player wants to play again. If not, exit. If
+        # so, just return.
+        say '';
+        exit unless get_yes_no( 'Why not try another animal' );
+        return;
+    }
+}
+
+__END__
+
+=head1 TITLE
+
+animal.pl - Play the game 'animal' from Basic Computer Games
+
+=head1 SYNOPSIS
+
+ animal.pl
+
+=head1 DETAILS
+
+This Perl script is a port of C, which is the 3ed entry in Basic
+Computer Games.
+
+The original BASIC was greatly complicated by the need to emulate a
+binary tree with an array. The implementation using hashes as nodes in
+an actual binary tree is much simpler.
+
+=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/04_Awari/README.md b/04_Awari/README.md
index 9356db80..a4b4d2da 100644
--- a/04_Awari/README.md
+++ b/04_Awari/README.md
@@ -1,7 +1,38 @@
 ### Awari
 
+Awari is an ancient African game played with seven sticks and thirty-six stones or beans laid out as shown above. The board is divided into six compartments or pits on each side. In addition, there are two special home pits at the ends.
+
+A move is made by taking all the beans from any (non-empty) pit on your own side. Starting from the pit to the right of this one, these beans are ‘sown’ one in each pit working around the board anticlockwise.
+
+A turn consists of one or two moves. If the last bean of your move is sown in your own home you may take a second move.
+
+If the last bean sown in a move lands in an empty pit, provided that the opposite pit is not empty, all the beans in the opposite pit, together with the last bean sown are ‘captured’ and moved to the player’s home.
+
+When either side is empty, the game is finished. The player with the most beans in his home has won.
+
+In the computer version, the board is printed as 14 numbers representing the 14 pits.
+
+```
+    3   3   3   3   3   3
+0                           0
+    3   3   3   3   3   3
+```
+
+The pits on your (lower) side are numbered 1-6 from left to right. The pits on my (the computer’s) side are numbered from my left (your right).
+
+To make a move you type in the number of a pit. If the last bean lands in your home, the computer types ‘AGAIN?’ and then you type in your second move.
+
+The computer’s move is typed, followed by a diagram of the board in its new state. The computer always offers you the first move. This is considered to be a slight advantage.
+
+There is a learning mechanism in the program that causes the play of the computer to improve as it playes more games.
+
+The original version of Awari is adopted from one originally written by Geoff Wyvill of Bradford, Yorkshire, England.
+
+---
+
 As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=6
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=6)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=21)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
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/README.md b/05_Bagels/README.md
index e550e084..51d324bb 100644
--- a/05_Bagels/README.md
+++ b/05_Bagels/README.md
@@ -1,7 +1,20 @@
 ### Bagels
 
+In this game, the computer picks a 3-digit secret number using the digits 0 to 9 and you attempt to guess what it is. You are allowed up to twenty guesses. No digit is repeated. After each guess the computer will give you clues about your guess as follows:
+
+- PICO    One digit is correct, but in the wrong place
+- FERMI    One digit is in the correct place
+- BAGELS   No digit is correct
+
+You will learn to draw inferences from the clues and, with practice, you’ll learn to improve your score. There are several good strategies for playing Bagels. After you have found a good strategy, see if you can improve it. Or try a different strategy altogether to see if it is any better. While the program allows up to twenty guesses, if you use a good strategy it should not take more than eight guesses to get any number.
+
+The original authors of this program are D. Resek and P. Rowe of the Lawrence Hall of Science, Berkeley, California.
+
+---
+
 As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=9
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=9)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=21)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
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/06_Banner/README.md b/06_Banner/README.md
index ac26214e..960b76cb 100644
--- a/06_Banner/README.md
+++ b/06_Banner/README.md
@@ -1,7 +1,14 @@
 ### Banner
 
+This program creates a large banner on a terminal of any message you input. The letters may be any dimension of you wish although the letter height plus distance from left-hand side should not exceed 6 inches. Experiment with the height and width until you get a pleasing effect on whatever terminal you are using.
+
+This program was written by Leonard Rosendust of Brooklyn, New York.
+
+---
+
 As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=10
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=10)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=25)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/07_Basketball/README.md b/07_Basketball/README.md
index ef44f631..158cba43 100644
--- a/07_Basketball/README.md
+++ b/07_Basketball/README.md
@@ -1,7 +1,30 @@
 ### Basketball
 
+This program simulates a game of basketball between Dartmouth College and an opponent of your choice. You are the Dartmouth captain and control the type of shot and defense during the course of the game.
+
+There are four types of shots:
+1. Long Jump Shot (30ft)
+2. Short Jump Shot (15ft)
+3. Lay Up
+4. Set Shot
+
+Both teams use the same defense, but you may call it:
+- Enter (6): Press
+- Enter (6.5): Man-to-man
+- Enter (7): Zone
+- Enter (7.5): None
+
+To change defense, type “0” as your next shot.
+
+Note: The game is biased slightly in favor of Dartmouth. The average probability of a Dartmouth shot being good is 62.95% compared to a probability of 61.85% for their opponent. (This makes the sample run slightly remarkable in that Cornell won by a score of 45 to 42 Hooray for the Big Red!)
+
+Charles Bacheller of Dartmouth College was the original author of this game.
+
+---
+
 As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=12
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=12)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=27)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/08_Batnum/README.md b/08_Batnum/README.md
index 4590dcf8..e5b4db83 100644
--- a/08_Batnum/README.md
+++ b/08_Batnum/README.md
@@ -1,7 +1,20 @@
 ### Batnum
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=14
+The game starts with an imaginary pile of objects, coins for example. You and your opponent (the computer) alternately remove objects from the pile. You specify in advance the minimum and maximum number of objects that can be taken on each turn. You also specify in advance how winning is defined:
+1. To take the last object
+2. To avoid taking the last object
+
+You may also determine whether you or the computer go first.
+
+The strategy of this game is based on modulo arithmetic. If the maximum number of objects a player may remove in a turn is M, then to gain a winning position a player at the end of his turn must leave a stack of 1 modulo (M+1) coins. If you don’t understand this, play the game 23 Matches first, then BATNUM, and have fun!
+
+BATNUM is a generalized version of a great number of manual remove-the-object games. The original computer version was written by one of the two originators of the BASIC language, John Kemeny of Dartmouth College.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=14)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=29)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/09_Battle/README.md b/09_Battle/README.md
index 0f5ada08..815cacd2 100644
--- a/09_Battle/README.md
+++ b/09_Battle/README.md
@@ -1,7 +1,27 @@
 ### Battle
 
+BATTLE is based on the popular game Battleship which is primarily played to familiarize people with the location and designation of points on a coordinate plane.
+
+BATTLE first randomly sets up the bad guy’s fleet disposition on a 6 by 6 matrix or grid. The fleet consists of six ships:
+- Two destroyers (ships number 1 and 2) which are two units long
+- Two cruisers (ships number 3 and 4) which are three units long
+- Two aircraft carriers (ships number 5 and 6) which are four units long
+
+The program then prints out this fleet disposition in a coded or disguised format (see the sample computer print-out). You then proceed to sink the various ships by typing in the coordinates (two digits. each from 1 to 6, separated by a comma) of the place where you want to drop a bomb, if you’ll excuse the expression. The computer gives the appropriate response (splash, hit, etc.) which you should record on a 6 by 6 matrix. You are thus building a representation of the actual fleet disposition which you will hopefully use to decode the coded fleet disposition printed out by the computer. Each time a ship is sunk, the computer prints out which ships have been sunk so far and also gives you a “SPLASH/HIT RATIO.”
+
+The first thing you should learn is how to locate and designate positions on the matrix, and specifically the difference between “3,4” and “4,3.” Our method corresponds to the location of points on the coordinate plane rather than the location of numbers in a standard algebraic matrix: the first number gives the column counting from left to right and the second number gives the row counting from bottom to top.
+
+The second thing you should learn about is the splash/hit ratio. “What is a ratio?” A good reply is “It’s a fraction or quotient.” Specifically, the spash/hit ratio is the number of splashes divided by the number of hits. If you had 9 splashes and 15 hits, the ratio would be 9/15 or 3/5, both of which are correct. The computer would give this splash/hit ratio as .6.
+
+The main objective and primary education benefit of BATTLE comes from attempting to decode the bas guys’ fleet disposition code. To do this, you must make a comparison between the coded matrix and the actual matrix which you construct as you play the game.
+
+The original author of both the program and these descriptive notes is Ray Westergard of Lawrence Hall of Science, Berkeley, California.
+
+---
+
 As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=15
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=15)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=30)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/10_Blackjack/README.md b/10_Blackjack/README.md
index 3b3466bd..43cf263c 100644
--- a/10_Blackjack/README.md
+++ b/10_Blackjack/README.md
@@ -1,7 +1,16 @@
 ### Blackjack
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=18
+This is a simulation of the card game of Blackjack or 21, Las Vegas style. This rather comprehensive version allows for up to seven players. On each hand a player may get another card (a hit), stand, split a hand in the event two identical cards were received or double down. Also, the dealer will ask for an insurance bet if he has an exposed ace.
+
+Cards are automatically reshuffled as the 51st card is reached. For greater realism, you may wish to change this to the 41st card. Actually, fanatical purists will want to modify the program so it uses three decks of cards instead of just one.
+
+This program originally surfaced at Digital Equipment Corp.; the author is unknown.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=18)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=33)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/11_Bombardment/README.md b/11_Bombardment/README.md
index 6b4f6468..4ab31b05 100644
--- a/11_Bombardment/README.md
+++ b/11_Bombardment/README.md
@@ -1,7 +1,16 @@
 ### Bombardment
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=22
+BOMBARDMENT is played on two, 5x5 grids or boards with 25 outpost locations numbered 1 to 25. Both you and the computer have four platoons of troops that can be located at any four outposts on your respective grids.
+
+At the start of the game, you locate (or hide) your four platoons on your grid. The computer does the same on it’s grid. You then take turns firing missiles or bombs at each other’s outposts trying to destroy all four platoons. The one who finds all four opponents’ platoons first, wins.
+
+This program was slightly modified from the original written by Martin Burdash of Parlin, New Jersey.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=22)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=37)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/12_Bombs_Away/README.md b/12_Bombs_Away/README.md
index 1cdb7212..b1b60277 100644
--- a/12_Bombs_Away/README.md
+++ b/12_Bombs_Away/README.md
@@ -1,7 +1,14 @@
 ### Bombs Away
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=24
+In this program, you fly a World War II bomber for one of the four protagonists of the war. You then pick your target or the type of plane you are flying. Depending on your flying experience and the quality of enemy defenders, you then may accomplish your mission, get shot down, or make it back through enemy fire. In any case, you get a chance to fly again.
+
+David Ahl modified the original program which was created by David Sherman while a student at Curtis Jr. High School, Sudbury, Massachusetts.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=24)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=39)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/12_Bombs_Away/csharp/BombsAway.sln b/12_Bombs_Away/csharp/BombsAway.sln
new file mode 100644
index 00000000..8ae70157
--- /dev/null
+++ b/12_Bombs_Away/csharp/BombsAway.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.32014.148
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BombsAwayConsole", "BombsAwayConsole\BombsAwayConsole.csproj", "{D80015FA-423C-4A16-AA2B-16669245AD59}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BombsAwayGame", "BombsAwayGame\BombsAwayGame.csproj", "{F57AEC18-FEE9-4F08-9F20-DFC56EFE6BFC}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{D80015FA-423C-4A16-AA2B-16669245AD59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{D80015FA-423C-4A16-AA2B-16669245AD59}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{D80015FA-423C-4A16-AA2B-16669245AD59}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{D80015FA-423C-4A16-AA2B-16669245AD59}.Release|Any CPU.Build.0 = Release|Any CPU
+		{F57AEC18-FEE9-4F08-9F20-DFC56EFE6BFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{F57AEC18-FEE9-4F08-9F20-DFC56EFE6BFC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{F57AEC18-FEE9-4F08-9F20-DFC56EFE6BFC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{F57AEC18-FEE9-4F08-9F20-DFC56EFE6BFC}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {39B2ECFB-037D-4335-BBD2-64892E953DD4}
+	EndGlobalSection
+EndGlobal
diff --git a/12_Bombs_Away/csharp/BombsAwayConsole/BombsAwayConsole.csproj b/12_Bombs_Away/csharp/BombsAwayConsole/BombsAwayConsole.csproj
new file mode 100644
index 00000000..aae99def
--- /dev/null
+++ b/12_Bombs_Away/csharp/BombsAwayConsole/BombsAwayConsole.csproj
@@ -0,0 +1,14 @@
+
+
+  
+    Exe
+    net6.0
+    enable
+    enable
+  
+
+  
+    
+  
+
+
diff --git a/12_Bombs_Away/csharp/BombsAwayConsole/ConsoleUserInterface.cs b/12_Bombs_Away/csharp/BombsAwayConsole/ConsoleUserInterface.cs
new file mode 100644
index 00000000..66d6d4d3
--- /dev/null
+++ b/12_Bombs_Away/csharp/BombsAwayConsole/ConsoleUserInterface.cs
@@ -0,0 +1,134 @@
+namespace BombsAwayConsole;
+
+/// 
+/// Implements  by writing to and reading from .
+/// 
+internal class ConsoleUserInterface : BombsAwayGame.IUserInterface
+{
+    /// 
+    /// Write message to console.
+    /// 
+    /// Message to display.
+    public void Output(string message)
+    {
+        Console.WriteLine(message);
+    }
+
+    /// 
+    /// Write choices with affixed indexes, allowing the user to choose by index.
+    /// 
+    /// Message to display.
+    /// Choices to display.
+    /// Choice that user picked.
+    public int Choose(string message, IList choices)
+    {
+        IEnumerable choicesWithIndexes = choices.Select((choice, index) => $"{choice}({index + 1})");
+        string choiceText = string.Join(", ", choicesWithIndexes);
+        Output($"{message} -- {choiceText}");
+
+        ISet allowedKeys = ConsoleKeysFromList(choices);
+        ConsoleKey? choice;
+        do
+        {
+            choice = ReadChoice(allowedKeys);
+            if (choice is null)
+            {
+                Output("TRY AGAIN...");
+            }
+        }
+        while (choice is null);
+
+        return ListIndexFromConsoleKey(choice.Value);
+    }
+
+    /// 
+    /// Convert the given list to its  equivalents. This generates keys that map
+    /// the first element to , the second element to ,
+    /// and so on, up to the last element of the list.
+    /// 
+    /// List whose elements will be converted to  equivalents.
+    ///  equivalents from .
+    private ISet ConsoleKeysFromList(IList list)
+    {
+        IEnumerable indexes = Enumerable.Range((int)ConsoleKey.D1, list.Count);
+        return new HashSet(indexes.Cast());
+    }
+
+    /// 
+    /// Convert the given console key to its list index equivalent. This assumes the key was generated from
+    /// 
+    /// 
+    /// Key to convert to its list index equivalent.
+    /// List index equivalent of key.
+    private int ListIndexFromConsoleKey(ConsoleKey key)
+    {
+        return key - ConsoleKey.D1;
+    }
+
+    /// 
+    /// Read a key from the console and return it if it is in the given allowed keys.
+    /// 
+    /// Allowed keys.
+    /// Key read from , if it is in ; null otherwise./>
+    private ConsoleKey? ReadChoice(ISet allowedKeys)
+    {
+        ConsoleKeyInfo keyInfo = ReadKey();
+        return allowedKeys.Contains(keyInfo.Key) ? keyInfo.Key : null;
+    }
+
+    /// 
+    /// Read key from .
+    /// 
+    /// Key read from .
+    private ConsoleKeyInfo ReadKey()
+    {
+        ConsoleKeyInfo result = Console.ReadKey(intercept: false);
+        // Write a blank line to the console so the displayed key is on its own line.
+        Console.WriteLine();
+        return result;
+    }
+
+    /// 
+    /// Allow user to choose 'Y' or 'N' from .
+    /// 
+    /// Message to display.
+    /// True if user chose 'Y', false if user chose 'N'.
+    public bool ChooseYesOrNo(string message)
+    {
+        Output(message);
+        ConsoleKey? choice;
+        do
+        {
+            choice = ReadChoice(new HashSet(new[] { ConsoleKey.Y, ConsoleKey.N }));
+            if (choice is null)
+            {
+                Output("ENTER Y OR N");
+            }
+        }
+        while (choice is null);
+
+        return choice.Value == ConsoleKey.Y;
+    }
+
+    /// 
+    /// Get integer by reading a line from .
+    /// 
+    /// Integer read from .
+    public int InputInteger()
+    {
+        bool resultIsValid;
+        int result;
+        do
+        {
+            string? integerText = Console.ReadLine();
+            resultIsValid = int.TryParse(integerText, out result);
+            if (!resultIsValid)
+            {
+                Output("PLEASE ENTER A NUMBER");
+            }
+        }
+        while (!resultIsValid);
+
+        return result;
+    }
+}
diff --git a/12_Bombs_Away/csharp/BombsAwayConsole/Program.cs b/12_Bombs_Away/csharp/BombsAwayConsole/Program.cs
new file mode 100644
index 00000000..35728cfc
--- /dev/null
+++ b/12_Bombs_Away/csharp/BombsAwayConsole/Program.cs
@@ -0,0 +1,26 @@
+using BombsAwayConsole;
+using BombsAwayGame;
+
+/// Create and play s using a .
+PlayGameWhileUserWantsTo(new ConsoleUserInterface());
+
+void PlayGameWhileUserWantsTo(ConsoleUserInterface ui)
+{
+    do
+    {
+        new Game(ui).Play();
+    }
+    while (UserWantsToPlayAgain(ui));
+}
+
+bool UserWantsToPlayAgain(IUserInterface ui)
+{
+    bool result = ui.ChooseYesOrNo("ANOTHER MISSION (Y OR N)?");
+    if (!result)
+    {
+        Console.WriteLine("CHICKEN !!!");
+    }
+
+    return result;
+}
+
diff --git a/12_Bombs_Away/csharp/BombsAwayGame/AlliesSide.cs b/12_Bombs_Away/csharp/BombsAwayGame/AlliesSide.cs
new file mode 100644
index 00000000..c6c7105b
--- /dev/null
+++ b/12_Bombs_Away/csharp/BombsAwayGame/AlliesSide.cs
@@ -0,0 +1,22 @@
+namespace BombsAwayGame;
+
+/// 
+/// Allies protagonist. Can fly missions in a Liberator, B-29, B-17, or Lancaster.
+/// 
+internal class AlliesSide : MissionSide
+{
+    public AlliesSide(IUserInterface ui)
+        : base(ui)
+    {
+    }
+
+    protected override string ChooseMissionMessage => "AIRCRAFT";
+
+    protected override IList AllMissions => new Mission[]
+    {
+        new("LIBERATOR", "YOU'VE GOT 2 TONS OF BOMBS FLYING FOR PLOESTI."),
+        new("B-29", "YOU'RE DUMPING THE A-BOMB ON HIROSHIMA."),
+        new("B-17", "YOU'RE CHASING THE BISMARK IN THE NORTH SEA."),
+        new("LANCASTER", "YOU'RE BUSTING A GERMAN HEAVY WATER PLANT IN THE RUHR.")
+    };
+}
diff --git a/12_Bombs_Away/csharp/BombsAwayGame/BombsAwayGame.csproj b/12_Bombs_Away/csharp/BombsAwayGame/BombsAwayGame.csproj
new file mode 100644
index 00000000..132c02c5
--- /dev/null
+++ b/12_Bombs_Away/csharp/BombsAwayGame/BombsAwayGame.csproj
@@ -0,0 +1,9 @@
+
+
+  
+    net6.0
+    enable
+    enable
+  
+
+
diff --git a/12_Bombs_Away/csharp/BombsAwayGame/EnemyArtillery.cs b/12_Bombs_Away/csharp/BombsAwayGame/EnemyArtillery.cs
new file mode 100644
index 00000000..a810c8c0
--- /dev/null
+++ b/12_Bombs_Away/csharp/BombsAwayGame/EnemyArtillery.cs
@@ -0,0 +1,8 @@
+namespace BombsAwayGame;
+
+/// 
+/// Represents enemy artillery.
+/// 
+/// Name of artillery type.
+/// Accuracy of artillery. This is the `T` variable in the original BASIC.
+internal record class EnemyArtillery(string Name, int Accuracy);
diff --git a/12_Bombs_Away/csharp/BombsAwayGame/Game.cs b/12_Bombs_Away/csharp/BombsAwayGame/Game.cs
new file mode 100644
index 00000000..d6b5c3e9
--- /dev/null
+++ b/12_Bombs_Away/csharp/BombsAwayGame/Game.cs
@@ -0,0 +1,58 @@
+namespace BombsAwayGame;
+
+/// 
+/// Plays the Bombs Away game using a supplied .
+/// 
+public class Game
+{
+    private readonly IUserInterface _ui;
+
+    /// 
+    /// Create game instance using the given UI.
+    /// 
+    /// UI to use for game.
+    public Game(IUserInterface ui)
+    {
+        _ui = ui;
+    }
+
+    /// 
+    /// Play game. Choose a side and play the side's logic.
+    /// 
+    public void Play()
+    {
+        _ui.Output("YOU ARE A PILOT IN A WORLD WAR II BOMBER.");
+        Side side = ChooseSide();
+        side.Play();
+    }
+
+    /// 
+    /// Represents a .
+    /// 
+    /// Name of side.
+    /// Create instance of side that this descriptor represents.
+    private record class SideDescriptor(string Name, Func CreateSide);
+
+    /// 
+    /// Choose side and return a new instance of that side.
+    /// 
+    /// New instance of side that was chosen.
+    private Side ChooseSide()
+    {
+        SideDescriptor[] sides = AllSideDescriptors;
+        string[] sideNames = sides.Select(a => a.Name).ToArray();
+        int index = _ui.Choose("WHAT SIDE", sideNames);
+        return sides[index].CreateSide();
+    }
+
+    /// 
+    /// All side descriptors.
+    /// 
+    private SideDescriptor[] AllSideDescriptors => new SideDescriptor[]
+    {
+        new("ITALY", () => new ItalySide(_ui)),
+        new("ALLIES", () => new AlliesSide(_ui)),
+        new("JAPAN", () => new JapanSide(_ui)),
+        new("GERMANY", () => new GermanySide(_ui)),
+    };
+}
diff --git a/12_Bombs_Away/csharp/BombsAwayGame/GermanySide.cs b/12_Bombs_Away/csharp/BombsAwayGame/GermanySide.cs
new file mode 100644
index 00000000..99843fce
--- /dev/null
+++ b/12_Bombs_Away/csharp/BombsAwayGame/GermanySide.cs
@@ -0,0 +1,21 @@
+namespace BombsAwayGame;
+
+/// 
+/// Germany protagonist. Can fly missions to Russia, England, and France.
+/// 
+internal class GermanySide : MissionSide
+{
+    public GermanySide(IUserInterface ui)
+        : base(ui)
+    {
+    }
+
+    protected override string ChooseMissionMessage => "A NAZI, EH?  OH WELL.  ARE YOU GOING FOR";
+
+    protected override IList AllMissions => new Mission[]
+    {
+        new("RUSSIA", "YOU'RE NEARING STALINGRAD."),
+        new("ENGLAND", "NEARING LONDON.  BE CAREFUL, THEY'VE GOT RADAR."),
+        new("FRANCE", "NEARING VERSAILLES.  DUCK SOUP.  THEY'RE NEARLY DEFENSELESS.")
+    };
+}
diff --git a/12_Bombs_Away/csharp/BombsAwayGame/IUserInterface.cs b/12_Bombs_Away/csharp/BombsAwayGame/IUserInterface.cs
new file mode 100644
index 00000000..50b7828c
--- /dev/null
+++ b/12_Bombs_Away/csharp/BombsAwayGame/IUserInterface.cs
@@ -0,0 +1,38 @@
+namespace BombsAwayGame;
+
+/// 
+/// Represents an interface for supplying data to the game.
+/// 
+/// 
+/// Abstracting the UI allows us to concentrate its concerns in one part of our code and to change UI behavior
+/// without creating any risk of changing the game logic. It also allows us to supply an automated UI for tests.
+/// 
+public interface IUserInterface
+{
+    /// 
+    /// Display the given message.
+    /// 
+    /// Message to display.
+    void Output(string message);
+
+    /// 
+    /// Choose an item from the given choices.
+    /// 
+    /// Message to display.
+    /// Choices to choose from.
+    /// Index of choice in  that user chose.
+    int Choose(string message, IList choices);
+
+    /// 
+    /// Allow user to choose Yes or No.
+    /// 
+    /// Message to display.
+    /// True if user chose Yes, false if user chose No.
+    bool ChooseYesOrNo(string message);
+
+    /// 
+    /// Get integer from user.
+    /// 
+    /// Integer supplied by user.
+    int InputInteger();
+}
diff --git a/12_Bombs_Away/csharp/BombsAwayGame/ItalySide.cs b/12_Bombs_Away/csharp/BombsAwayGame/ItalySide.cs
new file mode 100644
index 00000000..9f8bcd83
--- /dev/null
+++ b/12_Bombs_Away/csharp/BombsAwayGame/ItalySide.cs
@@ -0,0 +1,21 @@
+namespace BombsAwayGame;
+
+/// 
+/// Italy protagonist. Can fly missions to Albania, Greece, and North Africa.
+/// 
+internal class ItalySide : MissionSide
+{
+    public ItalySide(IUserInterface ui)
+        : base(ui)
+    {
+    }
+
+    protected override string ChooseMissionMessage => "YOUR TARGET";
+
+    protected override IList AllMissions => new Mission[]
+    {
+        new("ALBANIA", "SHOULD BE EASY -- YOU'RE FLYING A NAZI-MADE PLANE."),
+        new("GREECE", "BE CAREFUL!!!"),
+        new("NORTH AFRICA", "YOU'RE GOING FOR THE OIL, EH?")
+    };
+}
diff --git a/12_Bombs_Away/csharp/BombsAwayGame/JapanSide.cs b/12_Bombs_Away/csharp/BombsAwayGame/JapanSide.cs
new file mode 100644
index 00000000..33abc83b
--- /dev/null
+++ b/12_Bombs_Away/csharp/BombsAwayGame/JapanSide.cs
@@ -0,0 +1,38 @@
+namespace BombsAwayGame;
+
+/// 
+/// Japan protagonist. Flies a kamikaze mission, which has a different logic from s.
+/// 
+internal class JapanSide : Side
+{
+    public JapanSide(IUserInterface ui)
+        : base(ui)
+    {
+    }
+
+    /// 
+    /// Perform a kamikaze mission. If first kamikaze mission, it will succeed 65% of the time. If it's not
+    /// first kamikaze mission, perform an enemy counterattack.
+    /// 
+    public override void Play()
+    {
+        UI.Output("YOU'RE FLYING A KAMIKAZE MISSION OVER THE USS LEXINGTON.");
+
+        bool isFirstMission = UI.ChooseYesOrNo("YOUR FIRST KAMIKAZE MISSION(Y OR N)?");
+        if (!isFirstMission)
+        {
+            // LINE 207 of original BASIC: hitRatePercent is initialized to 0,
+            // but R, the type of artillery, is not initialized at all. Setting
+            // R = 1, which is to say EnemyArtillery = Guns, gives the same result.
+            EnemyCounterattack(Guns, hitRatePercent: 0);
+        }
+        else if (RandomFrac() > 0.65)
+        {
+            MissionSucceeded();
+        }
+        else
+        {
+            MissionFailed();
+        }
+    }
+}
diff --git a/12_Bombs_Away/csharp/BombsAwayGame/Mission.cs b/12_Bombs_Away/csharp/BombsAwayGame/Mission.cs
new file mode 100644
index 00000000..c93892fd
--- /dev/null
+++ b/12_Bombs_Away/csharp/BombsAwayGame/Mission.cs
@@ -0,0 +1,8 @@
+namespace BombsAwayGame;
+
+/// 
+/// Represents a mission that can be flown by a .
+/// 
+/// Name of mission.
+/// Description of mission.
+internal record class Mission(string Name, string Description);
diff --git a/12_Bombs_Away/csharp/BombsAwayGame/MissionSide.cs b/12_Bombs_Away/csharp/BombsAwayGame/MissionSide.cs
new file mode 100644
index 00000000..b3a54275
--- /dev/null
+++ b/12_Bombs_Away/csharp/BombsAwayGame/MissionSide.cs
@@ -0,0 +1,208 @@
+namespace BombsAwayGame;
+
+/// 
+/// Represents a protagonist that chooses a standard (non-kamikaze) mission.
+/// 
+internal abstract class MissionSide : Side
+{
+    /// 
+    /// Create instance using the given UI.
+    /// 
+    /// UI to use.
+    public MissionSide(IUserInterface ui)
+        : base(ui)
+    {
+    }
+
+    /// 
+    /// Reasonable upper bound for missions flown previously.
+    /// 
+    private const int MaxMissionCount = 160;
+
+    /// 
+    /// Choose a mission and attempt it. If attempt fails, perform an enemy counterattack.
+    /// 
+    public override void Play()
+    {
+        Mission mission = ChooseMission();
+        UI.Output(mission.Description);
+
+        int missionCount = MissionCountFromUI();
+        CommentOnMissionCount(missionCount);
+
+        AttemptMission(missionCount);
+    }
+
+    /// 
+    /// Choose a mission.
+    /// 
+    /// Mission chosen.
+    private Mission ChooseMission()
+    {
+        IList missions = AllMissions;
+        string[] missionNames = missions.Select(a => a.Name).ToArray();
+        int index = UI.Choose(ChooseMissionMessage, missionNames);
+        return missions[index];
+    }
+
+    /// 
+    /// Message to display when choosing a mission.
+    /// 
+    protected abstract string ChooseMissionMessage { get; }
+
+    /// 
+    /// All aviailable missions to choose from.
+    /// 
+    protected abstract IList AllMissions { get; }
+
+    /// 
+    /// Get mission count from UI. If mission count exceeds a reasonable maximum, ask UI again.
+    /// 
+    /// Mission count from UI.
+    private int MissionCountFromUI()
+    {
+        const string HowManyMissions = "HOW MANY MISSIONS HAVE YOU FLOWN?";
+        string inputMessage = HowManyMissions;
+
+        bool resultIsValid;
+        int result;
+        do
+        {
+            UI.Output(inputMessage);
+            result = UI.InputInteger();
+            if (result < 0)
+            {
+                UI.Output($"NUMBER OF MISSIONS CAN'T BE NEGATIVE.");
+                resultIsValid = false;
+            }
+            else if (result > MaxMissionCount)
+            {
+                resultIsValid = false;
+                UI.Output($"MISSIONS, NOT MILES...{MaxMissionCount} MISSIONS IS HIGH EVEN FOR OLD-TIMERS.");
+                inputMessage = "NOW THEN, " + HowManyMissions;
+            }
+            else
+            {
+                resultIsValid = true;
+            }
+        }
+        while (!resultIsValid);
+
+        return result;
+    }
+
+    /// 
+    /// Display a message about the given mission count, if it is unusually high or low.
+    /// 
+    /// Mission count to comment on.
+    private void CommentOnMissionCount(int missionCount)
+    {
+        if (missionCount >= 100)
+        {
+            UI.Output("THAT'S PUSHING THE ODDS!");
+        }
+        else if (missionCount < 25)
+        {
+            UI.Output("FRESH OUT OF TRAINING, EH?");
+        }
+    }
+
+    /// 
+    /// Attempt mission.
+    /// 
+    /// Number of missions previously flown. Higher mission counts will yield a higher probability of success.
+    private void AttemptMission(int missionCount)
+    {
+        if (missionCount < RandomInteger(0, MaxMissionCount))
+        {
+            MissedTarget();
+        }
+        else
+        {
+            MissionSucceeded();
+        }
+    }
+
+    /// 
+    /// Display message indicating that target was missed. Choose enemy artillery and perform a counterattack.
+    /// 
+    private void MissedTarget()
+    {
+        UI.Output("MISSED TARGET BY " + (2 + RandomInteger(0, 30)) + " MILES!");
+        UI.Output("NOW YOU'RE REALLY IN FOR IT !!");
+
+        // Choose enemy and counterattack.
+        EnemyArtillery enemyArtillery = ChooseEnemyArtillery();
+
+        if (enemyArtillery == Missiles)
+        {
+            EnemyCounterattack(enemyArtillery, hitRatePercent: 0);
+        }
+        else
+        {
+            int hitRatePercent = EnemyHitRatePercentFromUI();
+            if (hitRatePercent < MinEnemyHitRatePercent)
+            {
+                UI.Output("YOU LIE, BUT YOU'LL PAY...");
+                MissionFailed();
+            }
+            else
+            {
+                EnemyCounterattack(enemyArtillery, hitRatePercent);
+            }
+        }
+    }
+
+    /// 
+    /// Choose enemy artillery from UI.
+    /// 
+    /// Artillery chosen.
+    private EnemyArtillery ChooseEnemyArtillery()
+    {
+        EnemyArtillery[] artilleries = new EnemyArtillery[] { Guns, Missiles, Both };
+        string[] artilleryNames = artilleries.Select(a => a.Name).ToArray();
+        int index = UI.Choose("DOES THE ENEMY HAVE", artilleryNames);
+        return artilleries[index];
+    }
+
+    /// 
+    /// Minimum allowed hit rate percent.
+    /// 
+    private const int MinEnemyHitRatePercent = 10;
+
+    /// 
+    /// Maximum allowed hit rate percent.
+    /// 
+    private const int MaxEnemyHitRatePercent = 50;
+
+    /// 
+    /// Get the enemy hit rate percent from UI. Value must be between zero and .
+    /// If value is less than , mission fails automatically because the user is
+    /// assumed to be untruthful.
+    /// 
+    /// Enemy hit rate percent from UI.
+    private int EnemyHitRatePercentFromUI()
+    {
+        UI.Output($"WHAT'S THE PERCENT HIT RATE OF ENEMY GUNNERS ({MinEnemyHitRatePercent} TO {MaxEnemyHitRatePercent})");
+
+        bool resultIsValid;
+        int result;
+        do
+        {
+            result = UI.InputInteger();
+            // Let them enter a number below the stated minimum, as they will be caught and punished.
+            if (0 <= result && result <= MaxEnemyHitRatePercent)
+            {
+                resultIsValid = true;
+            }
+            else
+            {
+                resultIsValid = false;
+                UI.Output($"NUMBER MUST BE FROM {MinEnemyHitRatePercent} TO {MaxEnemyHitRatePercent}");
+            }
+        }
+        while (!resultIsValid);
+
+        return result;
+    }
+}
diff --git a/12_Bombs_Away/csharp/BombsAwayGame/Side.cs b/12_Bombs_Away/csharp/BombsAwayGame/Side.cs
new file mode 100644
index 00000000..7e643971
--- /dev/null
+++ b/12_Bombs_Away/csharp/BombsAwayGame/Side.cs
@@ -0,0 +1,97 @@
+namespace BombsAwayGame;
+
+/// 
+/// Represents a protagonist in the game.
+/// 
+internal abstract class Side
+{
+    /// 
+    /// Create instance using the given UI.
+    /// 
+    /// UI to use.
+    public Side(IUserInterface ui)
+    {
+        UI = ui;
+    }
+
+    /// 
+    /// Play this side.
+    /// 
+    public abstract void Play();
+
+    /// 
+    /// User interface supplied to ctor.
+    /// 
+    protected IUserInterface UI { get; }
+
+    /// 
+    /// Random-number generator for this play-through.
+    /// 
+    private readonly Random _random = new();
+
+    /// 
+    /// Gets a random floating-point number greater than or equal to zero, and less than one.
+    /// 
+    /// Random floating-point number greater than or equal to zero, and less than one.
+    protected double RandomFrac() => _random.NextDouble();
+
+    /// 
+    /// Gets a random integer in a range.
+    /// 
+    /// The inclusive lower bound of the number returned.
+    /// The exclusive upper bound of the number returned.
+    /// Random integer in a range.
+    protected int RandomInteger(int minValue, int maxValue) => _random.Next(minValue: minValue, maxValue: maxValue);
+
+    /// 
+    /// Display messages indicating the mission succeeded.
+    /// 
+    protected void MissionSucceeded()
+    {
+        UI.Output("DIRECT HIT!!!! " + RandomInteger(0, 100) + " KILLED.");
+        UI.Output("MISSION SUCCESSFUL.");
+    }
+
+    /// 
+    /// Gets the Guns type of enemy artillery.
+    /// 
+    protected EnemyArtillery Guns { get; } = new("GUNS", 0);
+
+    /// 
+    /// Gets the Missiles type of enemy artillery.
+    /// 
+    protected EnemyArtillery Missiles { get; } = new("MISSILES", 35);
+
+    /// 
+    /// Gets the Both Guns and Missiles type of enemy artillery.
+    /// 
+    protected EnemyArtillery Both { get; } = new("BOTH", 35);
+
+    /// 
+    /// Perform enemy counterattack using the given artillery and hit rate percent.
+    /// 
+    /// Enemy artillery to use.
+    /// Hit rate percent for enemy.
+    protected void EnemyCounterattack(EnemyArtillery artillery, int hitRatePercent)
+    {
+        if (hitRatePercent + artillery.Accuracy > RandomInteger(0, 100))
+        {
+            MissionFailed();
+        }
+        else
+        {
+            UI.Output("YOU MADE IT THROUGH TREMENDOUS FLAK!!");
+        }
+    }
+
+    /// 
+    /// Display messages indicating the mission failed.
+    /// 
+    protected void MissionFailed()
+    {
+        UI.Output("* * * * BOOM * * * *");
+        UI.Output("YOU HAVE BEEN SHOT DOWN.....");
+        UI.Output("DEARLY BELOVED, WE ARE GATHERED HERE TODAY TO PAY OUR");
+        UI.Output("LAST TRIBUTE...");
+    }
+}
diff --git a/13_Bounce/README.md b/13_Bounce/README.md
index cf17c363..396b534d 100644
--- a/13_Bounce/README.md
+++ b/13_Bounce/README.md
@@ -1,7 +1,16 @@
 ### Bounce
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=25
+This program plots a bouncing ball. Most computer plots run along the paper in the terminal (top to bottom); however, this plot is drawn horizontally on the paper (left to right).
+
+You may specify the initial velocity of the ball and the coefficient of elasticity of the ball (a superball is about 0.85 — other balls are much less). You also specify the time increment to be used in “strobing” the flight of the ball. In other words, it is as though the ball is thrown up in a darkened room and you flash a light at fixed time intervals and photograph the progress of the ball.
+
+The program was originally written by Val Skalabrin while he was at DEC.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=25)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=40)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/14_Bowling/README.md b/14_Bowling/README.md
index 0270868b..99a7fade 100644
--- a/14_Bowling/README.md
+++ b/14_Bowling/README.md
@@ -1,7 +1,18 @@
 ### Bowling
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=26
+This is a simulated bowling game for up to four players. You play 10 frames. To roll the ball, you simply type “ROLL.” After each roll, the computer will show you a diagram of the remaining pins (“0” means the pin is down, “+” means it is still standing), and it will give you a roll analysis:
+- GUTTER
+- STRIKE
+- SPARE
+- ERROR (on second ball if pins still standing)
+
+Bowling was written by Paul Peraino while a student at Woodrow Wilson High School, San Francisco, California.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=26)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=41)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/15_Boxing/README.md b/15_Boxing/README.md
index 380dd047..e3440676 100644
--- a/15_Boxing/README.md
+++ b/15_Boxing/README.md
@@ -1,7 +1,16 @@
 ### Boxing
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=28
+This program simulates a three-round Olympic boxing match. The computer coaches one of the boxers and determines his punches and defences, while you do the same for your boxer. At the start of the match, you may specify your man’s best punch and his vulnerability.
+
+There are approximately seven major punches per round, although this may be varied. The best out if three rounds wins.
+
+Jesse Lynch of St. Paul, Minnesota created this program.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=28)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=43)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/16_Bug/README.md b/16_Bug/README.md
index 2ce4c19a..136123ce 100644
--- a/16_Bug/README.md
+++ b/16_Bug/README.md
@@ -1,7 +1,18 @@
 ### Bug
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=30
+The object of this game is to finish your drawing of a bug before the computer finishes.
+
+You and the computer roll a die alternately with each number standing for a part of the bug. You must add the parts in the right order; in other words, you cannot have a neck until you have a body, you cannot have a head until you have a neck, and so on. After each new part has been added, you have the option of seeing pictures of the two bugs.
+
+If you elect to see all the pictures, this program has the ability of consuming well over six feet of terminal paper per run. We can only suggest recycling the paper by using the other side.
+
+Brian Leibowitz wrote this program while in the 7th grade at Harrison Jr-Se High School in Harrison, New York.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=30)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=45)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/17_Bullfight/README.md b/17_Bullfight/README.md
index c666ea37..d31bc4e2 100644
--- a/17_Bullfight/README.md
+++ b/17_Bullfight/README.md
@@ -1,7 +1,25 @@
 ### Bullfight
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=32
+In this simulated bullfight, you are the matador — i.e., the one with the principle role and the one who must kill the bull or be killed (or run from the ring).
+
+On each pass of the bull, you may try:
+- 0: Veronica (dangerous inside move of the cape)
+- 1: Less dangerous outside move of the cape
+- 2: Ordinary swirl of the cape
+
+Or you may try to kill the bull:
+- 4: Over the horns
+- 5: In the chest
+
+The crowd will determine what award you deserve, posthumously if necessary. The braver you are, the better the reward you receive. It’s nice to stay alive too. The better the job the picadores and toreadores do, the better your chances.
+
+David Sweet of Dartmouth wrote the original version of this program. It was then modified by students at Lexington High School and finally by Steve North of Creative Computing.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=32)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=47)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/18_Bullseye/README.md b/18_Bullseye/README.md
index 789ad793..0c42872e 100644
--- a/18_Bullseye/README.md
+++ b/18_Bullseye/README.md
@@ -1,7 +1,36 @@
 ### Bullseye
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=34
+In this game, up to 20 players throw darts at a target with 10-, 20-, 30-, and 40-point zones. The objective is to get 200 points.
+
+You have a choice of three methods of throwing:
+
+| Throw | Description        | Probable Score            |
+|-------|--------------------|---------------------------|
+| 1     | Fast overarm       | Bullseye or complete miss |
+| 2     | Controlled overarm | 10, 20, or 30 points      |
+| 3     | Underarm           | Anything                  |
+
+You will find after playing a while that different players will swear by different strategies. However, considering the expected score per throw by always using throw 3:
+
+| Score (S) | Probability (P) | S x P |
+|-----------|-----------------|-------|
+|     40    |  1.00-.95 = .05 |   2   |
+|     30    |  .95-.75 = .20  |   6   |
+|     30    |  .75-.45 = .30  |   6   |
+|     10    |  .45-.05 = .40  |   4   |
+|     0     |  .05-.00 = .05  |   0   |
+
+Expected score per throw = 18
+
+Calculate the expected score for the other throws and you may be surprised!
+
+The program was written by David Ahl of Creative Computing.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=34)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=49)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/18_Bullseye/csharp/Bullseye.csproj b/18_Bullseye/csharp/Bullseye.csproj
new file mode 100644
index 00000000..74abf5c9
--- /dev/null
+++ b/18_Bullseye/csharp/Bullseye.csproj
@@ -0,0 +1,10 @@
+
+
+  
+    Exe
+    net6.0
+    enable
+    enable
+  
+
+
diff --git a/18_Bullseye/csharp/Bullseye.sln b/18_Bullseye/csharp/Bullseye.sln
new file mode 100644
index 00000000..2a65a449
--- /dev/null
+++ b/18_Bullseye/csharp/Bullseye.sln
@@ -0,0 +1,22 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.30114.105
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bullseye", "Bullseye.csproj", "{04C164DB-594F-41C4-BC0E-0A203A5536C7}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{04C164DB-594F-41C4-BC0E-0A203A5536C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{04C164DB-594F-41C4-BC0E-0A203A5536C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{04C164DB-594F-41C4-BC0E-0A203A5536C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{04C164DB-594F-41C4-BC0E-0A203A5536C7}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+EndGlobal
diff --git a/18_Bullseye/csharp/BullseyeGame.cs b/18_Bullseye/csharp/BullseyeGame.cs
new file mode 100644
index 00000000..58c9799d
--- /dev/null
+++ b/18_Bullseye/csharp/BullseyeGame.cs
@@ -0,0 +1,237 @@
+namespace Bullseye
+{
+    /// 
+    /// Class encompassing the game
+    /// 
+    public class BullseyeGame
+    {
+        private readonly List _players;
+
+        // define a constant for the winning score so that it is
+        // easy to change again in the future
+        private const int WinningScore = 200;
+
+        public BullseyeGame()
+        {
+            // create the initial list of players; list is empty, but
+            // the setup of the game will add items to this list
+            _players = new List();
+        }
+
+        public void Run()
+        {
+            PrintIntroduction();
+
+            SetupGame();
+
+            PlayGame();
+
+            PrintResults();
+        }
+
+        private void SetupGame()
+        {
+            // First, allow the user to enter how many players are going
+            // to play. This could be weird if the user enters negative
+            // numbers, words, or too many players, so there are some
+            // extra checks on the input to make sure the user didn't do
+            // anything too crazy. Loop until the user enters valid input.
+            bool validPlayerCount;
+            int playerCount;
+            do
+            {
+                Console.WriteLine();
+                Console.Write("HOW MANY PLAYERS? ");
+                string? input = Console.ReadLine();
+
+                // assume the user has entered something incorrect - the
+                // next steps will validate the input
+                validPlayerCount = false;
+
+                if (Int32.TryParse(input, out playerCount))
+                {
+                    if (playerCount > 0 && playerCount <= 20)
+                    {
+                        validPlayerCount = true;
+                    }
+                    else
+                    {
+                        Console.WriteLine("YOU MUST ENTER A NUMBER BETWEEN 1 AND 20!");
+                    }
+                }
+                else
+                {
+                    Console.WriteLine("YOU MUST ENTER A NUMBER");
+                }
+
+            }
+            while (!validPlayerCount);
+
+            // Next, allow the user to enter names for the players; as each
+            // name is entered, create a Player object to track the name
+            // and their score, and save the object to the list in this class
+            // so the rest of the game has access to the set of players
+            for (int i = 0; i < playerCount; i++)
+            {
+                string? playerName = String.Empty;
+                do
+                {
+                    Console.Write($"NAME OF PLAYER #{i+1}? ");
+                    playerName = Console.ReadLine();
+
+                    // names can be any sort of text, so allow whatever the user
+                    // enters as long as it isn't a blank space
+                }
+                while (String.IsNullOrWhiteSpace(playerName));
+
+                _players.Add(new Player(playerName));
+            }
+        }
+
+        private void PlayGame()
+        {
+            Random random = new Random(DateTime.Now.Millisecond);
+
+            int round = 0;
+            bool isOver = false;
+            do
+            {
+                // starting a new round, increment the counter
+                round++;
+                Console.WriteLine($"ROUND {round}");
+                Console.WriteLine("--------------");
+
+                foreach (Player player in _players)
+                {
+                    // ask the user how they want to throw
+                    Console.Write($"{player.Name.ToUpper()}'S THROW: ");
+                    string? input = Console.ReadLine();
+
+                    // based on the input, figure out the probabilities
+                    int[] probabilities;
+                    switch (input)
+                    {
+                        case "1":
+                        {
+                            probabilities = new int[] { 65, 55, 50, 50 };
+                            break;
+                        }
+                        case "2":
+                        {
+                            probabilities = new int[] { 99, 77, 43, 1 };
+                            break;
+                        }
+                        case "3":
+                        {
+                            probabilities = new int[] { 95, 75, 45, 5 };
+                            break;
+                        }
+                        default:
+                        {
+                            // in case the user types something bad, pretend it's
+                            // as if they tripped over themselves while throwing
+                            // the dart - they'll either hit a bullseye or completely
+                            // miss
+                            probabilities = new int[] { 95, 95, 95, 95 };
+                            Console.Write("TRIP! ");
+                            break;
+                        }
+                    }
+
+
+                    // Next() returns a number in the range: min <= num < max, so specify 101
+                    // as the maximum so that 100 is a number that could be returned
+                    int chance = random.Next(0, 101);
+
+                    if (chance > probabilities[0])
+                    {
+                        player.Score += 40;
+                        Console.WriteLine("BULLSEYE!!  40 POINTS!");
+                    }
+                    else if (chance > probabilities[1])
+                    {
+                        player.Score += 30;
+                        Console.WriteLine("30-POINT ZONE!");
+                    }
+                    else if (chance > probabilities[2])
+                    {
+                        player.Score += 20;
+                        Console.WriteLine("20-POINT ZONE");
+                    }
+                    else if (chance > probabilities[3])
+                    {
+                        player.Score += 10;
+                        Console.WriteLine("WHEW!  10 POINTS.");
+                    }
+                    else
+                    {
+                        // missed it
+                        Console.WriteLine("MISSED THE TARGET!  TOO BAD.");
+                    }
+
+                    // check to see if the player has won - if they have, then
+                    // break out of the loops
+                    if (player.Score > WinningScore)
+                    {
+                        Console.WriteLine();
+                        Console.WriteLine("WE HAVE A WINNER!!");
+                        Console.WriteLine($"{player.Name.ToUpper()} SCORED {player.Score} POINTS.");
+                        Console.WriteLine();
+
+                        isOver = true; // out of the do/while round loop
+                        break; // out of the foreach (player) loop
+                    }
+
+                    Console.WriteLine();
+                }
+            }
+            while (!isOver);
+        }
+
+        private void PrintResults()
+        {
+            // For bragging rights, print out all the scores, but sort them
+            // by who had the highest score
+            var sorted = _players.OrderByDescending(p => p.Score);
+
+            // padding is used to get things to line up nicely - the results
+            // should look something like:
+            //      PLAYER       SCORE
+            //      Bravo          210
+            //      Charlie         15
+            //      Alpha            1
+            Console.WriteLine("PLAYER       SCORE");
+            foreach (var player in sorted)
+            {
+                Console.WriteLine($"{player.Name.PadRight(12)} {player.Score.ToString().PadLeft(5)}");
+            }
+
+            Console.WriteLine();
+            Console.WriteLine("THANKS FOR THE GAME.");
+        }
+
+        private void PrintIntroduction()
+        {
+            Console.WriteLine(Title);
+            Console.WriteLine();
+            Console.WriteLine(Introduction);
+            Console.WriteLine();
+            Console.WriteLine(Operations);
+        }
+
+        private const string Title = @"
+                    BULLSEYE
+    CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY";
+
+        private const string Introduction = @"
+IN THIS GAME, UP TO 20 PLAYERS THROW DARTS AT A TARGET
+WITH 10, 20, 30, AND 40 POINT ZONES.  THE OBJECTIVE IS
+TO GET 200 POINTS.";
+
+        private const string Operations = @"
+THROW   DESCRIPTION         PROBABLE SCORE
+  1     FAST OVERARM        BULLSEYE OR COMPLETE MISS
+  2     CONTROLLED OVERARM  10, 20, OR 30 POINTS
+  3     UNDERARM            ANYTHING";
+    }
+}
diff --git a/18_Bullseye/csharp/Player.cs b/18_Bullseye/csharp/Player.cs
new file mode 100644
index 00000000..4d18fd43
--- /dev/null
+++ b/18_Bullseye/csharp/Player.cs
@@ -0,0 +1,28 @@
+namespace Bullseye
+{
+    /// 
+    /// Object to track the name and score of a player
+    /// 
+    public class Player
+    {
+        /// 
+        /// Creates a play with the given name
+        /// 
+        /// Name of the player
+        public Player(string name)
+        {
+            Name = name;
+            Score = 0;
+        }
+
+        /// 
+        /// Name of the player
+        /// 
+        public string Name { get; private set; }
+
+        /// 
+        /// Current score of the player
+        /// 
+        public int Score { get; set; }
+    }
+}
diff --git a/18_Bullseye/csharp/Program.cs b/18_Bullseye/csharp/Program.cs
new file mode 100644
index 00000000..1e4316ef
--- /dev/null
+++ b/18_Bullseye/csharp/Program.cs
@@ -0,0 +1,14 @@
+using System;
+
+namespace Bullseye
+{
+    public static class Program
+    {
+        // Entry point to the application; create an instance of the
+        // game class and call Run()
+        public static void Main(string[] args)
+        {
+            new BullseyeGame().Run();
+        }
+    }
+}
diff --git a/19_Bunny/README.md b/19_Bunny/README.md
index 4df27297..8d980287 100644
--- a/19_Bunny/README.md
+++ b/19_Bunny/README.md
@@ -1,7 +1,8 @@
 ### Bunny
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=35
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=35)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=50)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/20_Buzzword/README.md b/20_Buzzword/README.md
index 034d7e5f..22e76f6f 100644
--- a/20_Buzzword/README.md
+++ b/20_Buzzword/README.md
@@ -1,7 +1,14 @@
 ### Buzzword
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=36
+This program is an invaluable aid for preparing speeches and briefings about educational technology. This buzzword generator provides sets of three highly-acceptable words to work into your material. Your audience will never know that the phrases don’t really mean much of anything because they sound so great! Full instructions for running are given in the program.
+
+This version of Buzzword was written by David Ahl.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=36)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=51)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/20_Buzzword/csharp/Buzzword.csproj b/20_Buzzword/csharp/Buzzword.csproj
new file mode 100644
index 00000000..1d2d39a9
--- /dev/null
+++ b/20_Buzzword/csharp/Buzzword.csproj
@@ -0,0 +1,8 @@
+
+
+  
+    Exe
+    net5.0
+  
+
+
diff --git a/20_Buzzword/csharp/Buzzword.sln b/20_Buzzword/csharp/Buzzword.sln
new file mode 100644
index 00000000..ada2d937
--- /dev/null
+++ b/20_Buzzword/csharp/Buzzword.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.810.15
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Buzzword", "Buzzword.csproj", "{E342DEB2-F009-47FD-85F6-E84965EAAB56}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{E342DEB2-F009-47FD-85F6-E84965EAAB56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{E342DEB2-F009-47FD-85F6-E84965EAAB56}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{E342DEB2-F009-47FD-85F6-E84965EAAB56}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{E342DEB2-F009-47FD-85F6-E84965EAAB56}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {52F15A7F-671A-470D-91CE-F3B629B989B7}
+	EndGlobalSection
+EndGlobal
diff --git a/20_Buzzword/csharp/Program.cs b/20_Buzzword/csharp/Program.cs
new file mode 100644
index 00000000..e9970d82
--- /dev/null
+++ b/20_Buzzword/csharp/Program.cs
@@ -0,0 +1,115 @@
+using System;
+
+namespace Buzzword
+{
+    class Program
+    {
+        /// 
+        /// Displays header.
+        /// 
+        static void Header()
+        {
+            Console.WriteLine("Buzzword generator".PadLeft(26));
+            Console.WriteLine("Creating Computing Morristown, New Jersey".PadLeft(15));
+            Console.WriteLine();
+            Console.WriteLine();
+            Console.WriteLine();
+        }
+
+        // Information for the user about possible key input.
+        static string keys = "type a 'Y' for another phrase or 'N' to quit";
+
+        /// 
+        /// Displays instructions.
+        /// 
+        static void Instructions()
+        {
+            Console.WriteLine("This program prints highly acceptable phrases in\n"
+            + "'educator-speak' that you can work into reports\n"
+            + "and speeches. Whenever a question mark is printed,\n"
+            + $"{keys}.");
+            Console.WriteLine();
+            Console.WriteLine();
+            Console.Write("Here's the first phrase:");
+        }
+
+        static string[] Words = new[]
+            { "ability", "basal", "behavioral", "child-centered",
+            "differentiated", "discovery", "flexible", "heterogenous",
+            "homogeneous", "manipulative", "modular", "tavistock",
+            "individualized", "learning", "evaluative", "objective",
+            "cognitive", "enrichment", "scheduling", "humanistic",
+            "integrated", "non-graded", "training", "vertical age",
+            "motivational", "creative", "grouping", "modification",
+            "accountability", "process", "core curriculum", "algorithm",
+            "performance", "reinforcement", "open classroom", "resource",
+            "structure", "facility", "environment" };
+
+        /// 
+        /// Capitalizes first letter of given string.
+        /// 
+        /// 
+        /// string
+        static string Capitalize(string input)
+        {
+            if (string.IsNullOrWhiteSpace(input))
+                return string.Empty;
+
+            return char.ToUpper(input[0]) + input[1..];
+        }
+
+        // Seed has been calculated to get the same effect as in original,
+        // at least in first phrase
+        static readonly Random rnd = new Random(1486);
+
+        /// 
+        /// Generates random phrase from words available in Words array.
+        /// 
+        /// String representing random phrase where first letter is capitalized.
+        static string GeneratePhrase()
+        {
+            // Indexing from 0, so had to decrease generated numbers
+            return $"{Capitalize(Words[rnd.Next(13)])} "
+                + $"{Words[rnd.Next(13, 26)]} "
+                + $"{Words[rnd.Next(26, 39)]}";
+        }
+
+        /// 
+        /// Handles user input. On wrong input it displays information about 
+        /// valid keys in infinite loop.
+        /// 
+        /// True if user pressed 'Y', false if 'N'.
+        static bool Decision()
+        {
+            while (true)
+            {
+                Console.Write("?");
+                var answer = Console.ReadKey();
+                if (answer.Key == ConsoleKey.Y)
+                    return true;
+                else if (answer.Key == ConsoleKey.N)
+                    return false;
+                else
+                    Console.WriteLine($"\n{keys}");
+            }
+        }
+
+        static void Main(string[] args)
+        {
+            Header();
+            Instructions();
+
+            while (true)
+            {
+                Console.WriteLine();
+                Console.WriteLine(GeneratePhrase());
+                Console.WriteLine();
+
+                if (!Decision())
+                    break;
+            }
+
+            Console.WriteLine("\nCome back when you need help with another report!");
+        }
+    }
+}
diff --git a/21_Calendar/README.md b/21_Calendar/README.md
index b6198acc..5a93d50d 100644
--- a/21_Calendar/README.md
+++ b/21_Calendar/README.md
@@ -1,7 +1,23 @@
 ### Calendar
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=37
+This program prints out a calendar for any year. You must specify the starting day of the week of the year:
+- 0: Sunday
+- -1: Monday
+- -2: Tuesday
+- -3: Wednesday
+- -4: Thursday
+- -5: Friday
+- -6: Saturday
+
+You can determine this by using the program WEEKDAY. You must also make two changes for leap years. The program listing describes the necessary changes. Running the program produces a nice 12-month calendar.
+
+The program was written by Geoffrey Chase of the Abbey, Portsmouth, Rhode Island.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=37)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=52)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/21_Calendar/csharp/21_calendar.csproj b/21_Calendar/csharp/21_calendar.csproj
new file mode 100644
index 00000000..895f2f3f
--- /dev/null
+++ b/21_Calendar/csharp/21_calendar.csproj
@@ -0,0 +1,9 @@
+
+
+  
+    Exe
+    net5.0
+    _21_calendar
+  
+
+
diff --git a/21_Calendar/csharp/21_calendar.sln b/21_Calendar/csharp/21_calendar.sln
new file mode 100644
index 00000000..d8330a26
--- /dev/null
+++ b/21_Calendar/csharp/21_calendar.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.31613.86
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "21_calendar", "21_calendar.csproj", "{99AB85E1-A42B-4FEF-8BA6-0ED877F05249}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{99AB85E1-A42B-4FEF-8BA6-0ED877F05249}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{99AB85E1-A42B-4FEF-8BA6-0ED877F05249}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{99AB85E1-A42B-4FEF-8BA6-0ED877F05249}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{99AB85E1-A42B-4FEF-8BA6-0ED877F05249}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {1B423C35-492F-4AF8-97FC-BEC69B44EC8D}
+	EndGlobalSection
+EndGlobal
diff --git a/21_Calendar/csharp/Program.cs b/21_Calendar/csharp/Program.cs
new file mode 100644
index 00000000..4fd2b177
--- /dev/null
+++ b/21_Calendar/csharp/Program.cs
@@ -0,0 +1,144 @@
+using System;
+
+/*
+ 21_Calendar in C# for basic-computer-games
+ Converted by luminoso-256
+*/
+
+namespace _21_calendar
+{
+    class Program
+    {
+        //basic has a TAB function. We do not by default, so we make our own!
+        static string Tab(int numspaces)
+        {
+            string space = "";
+            //loop as many times as there are spaces specified, and add a space each time
+            while (numspaces > 0)
+            {
+                //add the space
+                space += " ";
+                //decrement the loop variable so we don't keep going forever!
+                numspaces--;
+            }
+            return space;
+        }
+
+        static void Main(string[] args)
+        {
+            // print the "title" of our program
+            // the usage of Write*Line* means we do not have to specify a newline (\n)
+            Console.WriteLine(Tab(32) + "CALENDAR");
+            Console.WriteLine(Tab(15) + "CREATE COMPUTING  MORRISTOWN, NEW JERSEY");
+            //give us some space. 
+            Console.WriteLine("");
+            Console.WriteLine("");
+            Console.WriteLine("");
+
+            //establish some variables needed to print out a calculator
+
+            //the length of each month in days. On a leap year, the start of this would be 
+            // 0, 31, 29 to account for Feb. the 0 at the start is for days elapsed to work right in Jan.
+            int[] monthLengths = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // m in original source
+
+            //the starting day of the month. in 1979 this was monday
+            // 0 = sun, -1 = mon, -2 = tue, -3 = wed, etc.
+            int day = -1; // called d in original source
+
+            //how much time in the year has gone by?
+            int elapsed = 0; // called s in original source
+
+            //loop through printing all the months.
+            for (int month = 1; month <= 12; month++) //month is called n in original source
+            {
+                //pad some space
+                Console.WriteLine("");
+                Console.WriteLine("");
+                //increment days elapsed
+                elapsed += monthLengths[month - 1];
+                //build our header for this month of the calendar
+                string header = "** " + elapsed;
+                //add padding as needed
+                while (header.Length < 7)
+                {
+                    header += " ";
+                }
+                for (int i = 1; i <= 18; i++)
+                {
+                    header += "*";
+                }
+                //determine what month it is, add text accordingly
+                switch (month) {
+                    case 1: header += " JANUARY "; break;
+                    case 2: header += " FEBRUARY"; break;
+                    case 3: header += "  MARCH  "; break;
+                    case 4: header += "  APRIL  "; break;
+                    case 5: header += "   MAY   "; break;
+                    case 6: header += "   JUNE  "; break;
+                    case 7: header += "   JULY  "; break;
+                    case 8: header += "  AUGUST "; break;
+                    case 9: header += "SEPTEMBER"; break;
+                    case 10: header += " OCTOBER "; break;
+                    case 11: header += " NOVEMBER"; break;
+                    case 12: header += " DECEMBER"; break;
+                }
+                //more padding
+                for (int i = 1; i <= 18; i++)
+                {
+                    header += "*";
+                }
+                header += "  ";
+                // how many days left till the year's over?
+                header += (365 - elapsed) + " **"; // on leap years 366 
+                Console.WriteLine(header);
+                //dates 
+                Console.WriteLine("     S       M       T       W       T       F       S");
+                Console.WriteLine(" ");
+
+                string weekOutput = "";
+                for (int i = 1; i <= 59; i++)
+                {
+                    weekOutput += "*";
+                }
+                //init some vars ahead of time
+                int g = 0;
+                int d2 = 0;
+                //go through the weeks and days
+                for (int week = 1; week <= 6; week++)
+                {
+                    Console.WriteLine(weekOutput);
+                    weekOutput = "    "; 
+                    for (g = 1; g <= 7; g++)
+                    {
+                        //add one to the day
+                        day++;
+                        d2 = day - elapsed;
+                        //check if we're done with this month
+                        if (d2 > monthLengths[month])
+                        {
+                            week = 6;
+                            break;
+                        }
+                        //should we print this day?
+                        if (d2 > 0)
+                        {
+                            weekOutput += d2;
+                        }
+                        //padding
+                        while (weekOutput.Length < 4 + 8 * g)
+                        {
+                            weekOutput += " ";
+                        }
+                    }
+                    if (d2 == monthLengths[month])
+                    {
+                        day += g;
+                        break;
+                    }
+                }
+                day -= g;
+                Console.WriteLine(weekOutput);
+            }
+        }
+    }
+}
diff --git a/22_Change/README.md b/22_Change/README.md
index 1a0f1297..840714a4 100644
--- a/22_Change/README.md
+++ b/22_Change/README.md
@@ -1,7 +1,12 @@
 ### Change
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=39
+In this program, the computer pretends it is the cashier at your friendly neighborhood candy store. You tell it the cost of the item(s) you are buying, the amount of your payment, and it will automatically (!) determine your correct change. Aren’t machines wonderful? Dennis Lunder of People’s Computer Company wrote this program.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=39)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=54)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
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/23_Checkers/README.md b/23_Checkers/README.md
index 0a3c57a2..d6936ea9 100644
--- a/23_Checkers/README.md
+++ b/23_Checkers/README.md
@@ -1,7 +1,16 @@
 ### Checkers
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=40
+This program plays checkers. The pieces played by the computer are marked with an “X”, yours are marked “O”. A move is made by specifying the coordinates of the piece to be moved (X, Y). Home (0,0) is in the bottom left and X specifies distance to the right of home (i.e., column) and Y specifies distance above home (i.e. row). You then specify where you wish to move to.
+
+THe original version of the program by Alan Segal was not able to recognize (or permit) a double or triple jump. If you tried one, it was likely that your piece would disappear altogether!
+
+Steve North of Creative Computing rectified this problem and Lawrence Neal contributed modifications to allow the program to tell which player has won the game. The computer does not play a particularly good game but we leave it to _you_ to improve that.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=40)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=55)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/24_Chemist/README.md b/24_Chemist/README.md
index afd39752..95db3f66 100644
--- a/24_Chemist/README.md
+++ b/24_Chemist/README.md
@@ -1,7 +1,14 @@
 ### Chemist
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=42
+The fictitious chemical, kryptocyanic acid, can only be diluted by the ratio of 7 parts water to 3 parts acid. Any other ratio causes an unstable compound which soon explodes. Given an amount of acid, you must determine how much water to add to the dilution. If you’re more than 5% off, you lose one of your nine lives. The program continues to play until you lose all nine lives or until it is interrupted.
+
+It was originally written by Wayne Teeter of Ridgecrest, California.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=42)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=57)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
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/25_Chief/README.md b/25_Chief/README.md
index f90a7121..5cb044c0 100644
--- a/25_Chief/README.md
+++ b/25_Chief/README.md
@@ -1,7 +1,16 @@
 ### Chief
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=43
+In the words of the program author, John Graham, “CHIEF is designed to give people (mostly kids) practice in the four operations (addition, multiplication, subtraction, and division).
+
+It does this while giving people some fun. And then, if the people are wrong, it shows them how they should have done it.
+
+CHIEF was written by John Graham of Upper Brookville, New York.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=43)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=58)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/26_Chomp/README.md b/26_Chomp/README.md
index 3774589a..e9a8b5ce 100644
--- a/26_Chomp/README.md
+++ b/26_Chomp/README.md
@@ -1,7 +1,16 @@
 ### Chomp
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=44
+This program is an adaptation of a mathematical game originally described by Martin Gardner in the January 1973 issue of _Scientific American_. Up to a 9x9 grid is set up by you with the upper left square in a poison square. This grid is the cookie. Players alternately chomp away at the cookie from the lower right. To take a chomp, input a row and column number of one of the squares remaining on the cookie. All of the squares below and to the right of that square, including that square, disappear.
+
+Any number of people can play — the computer is only the moderator; it is not a player. Two-person strategies are interesting to work out but strategies when three or more people are playing are the real challenge.
+
+The computer version of the game was written by Peter Sessions of People’s Computer Company.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=44)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=59)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/26_Chomp/perl/chomp.pl b/26_Chomp/perl/chomp.pl
new file mode 100644
index 00000000..34727626
--- /dev/null
+++ b/26_Chomp/perl/chomp.pl
@@ -0,0 +1,173 @@
+#!/usr/bin/perl
+
+
+my @cookie;
+
+&main;
+
+sub main {
+	my $answer = 1;
+	until ($answer == 0) {
+		$answer=&game_play;
+	}
+}
+
+sub game_play {
+	
+	# the initial game set up
+	&print_intro;
+	my ($players,$rows,$cols) = &get_user_info;
+	&create_gameboard($rows,$cols);
+	&print_gameboard($rows,$cols);
+	my $game_over = 0;
+	my $player = 0;
+
+	# continuous loop until the poison pill is swallowed
+	until ($game_over == 1) {
+		if ($player > ($players-1)) {		#checks to make sure we're just looping thru valid players 
+			$player = 0;
+		}
+		$player++;
+		my ($user_row,$user_col) = &get_player_row_col($player,$rows,$cols);
+		if ($cookie[$user_row][$user_col] == -1) {
+			print "YOU LOSE, PLAYER $player\n\n";
+			print "AGAIN (1=YES, 0=NO!)\n";
+			my $answer=;
+			chomp($answer);
+			return($answer);
+		}
+		&modify_gameboard($rows,$cols,$user_row,$user_col);
+		&print_gameboard($rows,$cols);
+	}
+	
+}
+
+sub get_player_row_col {
+	my ($player,$row,$col) = @_;
+	my @coords;
+	my $validity="invalid";
+	# Getting coordinates from user
+	until ($validity eq "valid") {
+		print "PLAYER $player COORDINATES OF CHOMP (ROW,COLUMN)\n";
+		my $input=;
+		chomp($input);
+		@coords = split/,/,$input;
+
+		#verifying coordinates are valid
+		if ($coords[0] < 1 || $coords[0] > $row || $coords[1] < 1 || $coords[1] > $col || $cookie[$coords[0]][$coords[1]] == 0) {
+			print "NO FAIR. YOU'RE TRYING TO CHOMP ON EMPTY SPACE!\n";
+		}
+		else {
+			$validity="valid";
+		}
+	}
+	return($coords[0],$coords[1]);
+}
+
+sub get_user_info {
+	my ($players,$rows,$cols)=0;
+	until ($players > 0) {
+		print "HOW MANY PLAYERS\n";
+		$players=;
+		chomp($players);
+	}
+	until ($rows > 0 && $rows < 10) {
+		print "HOW MANY ROWS\n";
+		$rows=;
+		chomp($rows);
+		if ($rows > 9) {
+			print "TOO MANY ROWS (9 IS MAXIMUM). NOW, ";
+		}
+	}
+	until ($cols > 0 && $cols < 10) {
+		print "HOW MANY COLUMNS\n";
+		$cols=;
+		chomp($cols);
+		if ($cols > 9) {
+			print "TOO MANY COLUMNS (9 IS MAXIMUM). NOW, ";
+		}
+	}
+	return($players,$rows,$cols);
+}
+
+sub print_intro{
+	print ' ' x 33 . "CHOMP\n";
+	print ' ' x 15 . "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n\n";
+	print "THIS IS THE GAME OF CHOMP (SCIENTIFIC AMERICAN, JAN 1973)\n";
+	print "DO YOU WANT THE RULES (1=YES, 0=NO!)";
+	my $answer = ;
+	chomp($answer);
+	if ($answer == 0) {
+		return;
+	}
+	else {
+	 	print "CHOMP IS FOR 1 OR MORE PLAYERS (HUMANS ONLY).\n\n";
+	 	print "HERE'S HOW A BOARD LOOKS (THIS ONE IS 5 BY 7):\n";
+		&create_gameboard(5,7);
+		&print_gameboard(5,7);
+	 	print "THE BOARD IS A BIG COOKIE - R ROWS HIGH AND C COLUMNS\n";
+	 	print "WIDE. YOU INPUT R AND C AT THE START. IN THE UPPER LEFT\n";
+	 	print "CORNER OF THE COOKIE IS A POISON SQUARE (P). THE ONE WHO\n";
+	 	print "CHOMPS THE POISON SQUARE LOSES. TO TAKE A CHOMP, TYPE THE\n";
+	 	print "ROW AND COLUMN OF ONE OF THE SQUARES ON THE COOKIE.\n";
+	 	print "ALL OF THE SQUARES BELOW AND TO THE RIGHT OF THAT SQUARE\n";
+	 	print "(INCLUDING THAT SQUARE, TOO) DISAPPEAR -- CHOMP!!\n";
+	 	print "NO FAIR CHOMPING SQUARES THAT HAVE ALREADY BEEN CHOMPED,\n";
+	 	print "OR THAT ARE OUTSIDE THE ORIGINAL DIMENSIONS OF THE COOKIE.\n\n";
+	 	print "HERE WE GO...\n";
+		undef @cookie;
+	}
+}
+
+#initial creation of the gameboard
+sub create_gameboard {
+	my $rows = shift;
+	my $cols = shift;
+	foreach my $row (1..($rows)) {
+		foreach my $col (1..($cols)) {
+			$cookie[$row][$col]=1;
+		}
+	}
+	$cookie[1][1]=-1;
+}
+
+#modification of the gameboard based on the input from the player
+sub modify_gameboard {
+	my ($rows,$cols,$user_row,$user_col) = @_;
+	foreach my $row ($user_row..($rows)) {
+		foreach my $col ($user_col..($cols)) {
+			$cookie[$row][$col]="  ";	
+		}
+	}
+}
+
+#prints the gameboard based on the current state of the gameboard
+sub print_gameboard {
+	my ($rows,$cols) = @_;
+	foreach my $col (1..$cols) {
+		if ($col == $cols) {
+			print "$col\n";
+		}
+		elsif ($col == 1) {
+			print "\t  $col ";
+		}
+		else {
+			print "$col ";
+		}
+	}
+	foreach my $row (1..($rows)) {
+		print "\t$row ";
+		foreach my $col (1..($cols)) {
+			if ($cookie[$row][$col] == 1) {
+				print "* ";
+			}
+			if ($cookie[$row][$col] == 0) {
+				print "  ";
+			}
+			if ($cookie[$row][$col] == -1) {
+				print "P ";
+			}
+		}
+		print "\n";
+	}
+}
diff --git a/27_Civil_War/README.md b/27_Civil_War/README.md
index 9f3e4126..17d6816f 100644
--- a/27_Civil_War/README.md
+++ b/27_Civil_War/README.md
@@ -1,7 +1,18 @@
 ### Civil War
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=46
+This simulation is based on 14 battles in the Civil War. Facts and figures are based on the actual occurrence. If you follow the same strategy used in the actual battle, the results will be the same. Generally, this is a good strategy since the generals in the Civil War were fairly good military strategists. However, you can frequently outperform the Civil War generals, particularly in cases where they did not have good enemy intelligence and consequently followed a poor course of action. Naturally, it helps to know your Civil War history, although the computer gives yuo the rudiments.
+
+After each of the 14 battles, your casualties are compared to the actual casualties of the battle, and you are told whether you win or lose the battle.
+
+You may play Civil War alone in which case the program simulates the Union general. Or two players may play in which case the computer becomes the moderator.
+
+Civil War was written in 1968 by three Students in Lexington High School, Massachusetts: L. Cram, L. Goodie, and D. Hibbard. It was modified into a 2-player game by G. Paul and R. Hess of TIES, St. Paul, Minnesota.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=46)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=61)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/28_Combat/README.md b/28_Combat/README.md
index cc4be30a..32b2af65 100644
--- a/28_Combat/README.md
+++ b/28_Combat/README.md
@@ -1,7 +1,16 @@
 ### Combat
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=50
+In this game, you are fighting a small-scale war with the computer. You have 72,000 troops which you first ust distribute among your Army, Navy, and Air Force. You may distribute them in any way you choose as long as you don’t use more than 72,000.
+
+You then attack your opponent (the computer) and input which service and the number of men you wish to use. The computer then tells you the outcome of the battle, gives you the current statistics and allows you to determine your next move.
+
+After the second battle, it is decided from the total statistics whether you win or lose or if a treaty is signed.
+
+This program was created by Bob Dores of Milton, Massachusetts.
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=50)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=65)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/29_Craps/README.md b/29_Craps/README.md
index 673957f4..7372b99f 100644
--- a/29_Craps/README.md
+++ b/29_Craps/README.md
@@ -1,7 +1,19 @@
 ### Craps
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=52
+This game simulates the game 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 on a computer at DEC.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=52)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=67)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
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/30_Cube/README.md b/30_Cube/README.md
index 067606e3..41973db1 100644
--- a/30_Cube/README.md
+++ b/30_Cube/README.md
@@ -1,7 +1,14 @@
 ### Cube
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=53
+CUBE is a game played on the facing sides of a cube with a side dimension of 2. A location is designated by three numbers — e.g., 1, 2, 1. The object is to travel from 1, 1, 1 to 3, 3, 3 by moving one horizontal or vertical (not diagonal) square at a time without striking one of 5 randomly placed landmines. You are staked to $500; prior to each play of the game you may make a wager whether you will reach your destination. You lose if you hit a mine or try to make an illegal move — i.e., change more than one digit from your previous position.
+
+Cube was created by Jerimac Ratliff of Fort Worth, Texas.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=53)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=68)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/31_Depth_Charge/README.md b/31_Depth_Charge/README.md
index 4c11da12..1a0ed7a4 100644
--- a/31_Depth_Charge/README.md
+++ b/31_Depth_Charge/README.md
@@ -1,7 +1,16 @@
 ### Depth Charge
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=55
+In this program you are captain of the destroyer USS Computer. An enemy submarine has been causing trouble and your mission is to destroy it. You may select the seize of the “cube” of water you wish to search in. The computer then determines how many depth charges you get to destroy the submarine.
+
+Each depth charge is exploded by you specifying a trio of numbers; the first two are the surface coordinates (X,Y), the third is the depth. After each depth charge, your sonar observer will tell you where the explosion was relative to the submarine.
+
+Dana Noftle wrote this program while a student at Acton High School, Acton, Massachusetts.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=55)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=70)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
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/32_Diamond/README.md b/32_Diamond/README.md
index 36bdb9e2..1f155ba3 100644
--- a/32_Diamond/README.md
+++ b/32_Diamond/README.md
@@ -1,7 +1,14 @@
 ### Diamond
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=56
+This program fills an 8.5x11 piece of paper with diamonds (plotted on a hard-copy terminal, of course). The program asks for an odd number to be input in the range 5 to 31. The diamonds printed will be this number of characters high and wide. The number of diamonds across the page will vary from 12 for 5-character wide diamonds to 1 for a diamond 31-characters wide. You can change the content of the pattern if you wish.
+
+The program was written by David Ahl of Creative Computing.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=56)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=71)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/33_Dice/README.md b/33_Dice/README.md
index 27cc7029..71406a01 100644
--- a/33_Dice/README.md
+++ b/33_Dice/README.md
@@ -1,7 +1,21 @@
 ### Dice
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=57
+Not exactly a game, this program simulates rolling a pair of dice a large number of times and prints out the frequency distribution. You simply input the number of rolls. It is interesting to see how many rolls are necessary to approach the theoretical distribution:
+
+|   |      |            |
+|---|------|------------|
+| 2 | 1/36 | 2.7777...% |
+| 3 | 2/36 | 5.5555...% |
+| 4 | 3/36 | 8.3333...% |
+etc.
+
+Daniel Freidus wrote this program while in the seventh grade at Harrison Jr-Sr High School, Harrison, New York.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=57)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=72)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/34_Digits/README.md b/34_Digits/README.md
index e2cf78a9..b1c96819 100644
--- a/34_Digits/README.md
+++ b/34_Digits/README.md
@@ -1,7 +1,17 @@
 ### Digits
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=58
+The player writes down a set of 30 numbers (0, 1, or 2) at random prior to playing the game. The computer program, using pattern recognition techniques, attempts to guess the next number in your list.
+
+The computer asks for 10 numbers at a time. It always guesses first and then examines the next number to see if it guessed correctly. By pure luck (or chance or probability), the computer ought to be right 10 times. It is uncanny how much better it generally does than that!
+
+This program originated at Dartmouth; original author unknown.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=58)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=73)
+
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/35_Even_Wins/README.md b/35_Even_Wins/README.md
index 71c7d18e..9db7654b 100644
--- a/35_Even_Wins/README.md
+++ b/35_Even_Wins/README.md
@@ -1,7 +1,18 @@
 ### Even Wins
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=60
+This is a game between you and the computer. To play, an odd number of objects (marbles, chips, matches) are placed in a row. You take turns with the computer picking up between one and four objects each turn. The game ends when there are no objects left, and the winner is the one with an even number of objects picked up.
+
+Two versions of this game are included. While to the player they appear similar, the programming approach is quite different. EVEN WINS, the first version, is deterministic — i.e., the computer plays by fixed, good rules and is impossible to beat if you don’t know how to play the game. It always starts with 27 objects, although you may change this.
+
+The second version, GAME OF EVEN WINS, is much more interesting because the computer starts out only knowing the rules of the game. Using simple techniques of artificial intelligence (cybernetics), the computer gradually learns to play this game from its mistakes until it plays a very good game. After 20 games, the computer is a challenge to beat. Variation in the human’s style of play seems to make the computer learn more quickly. If you plot the learning curve of this program, it closely resembles classical human learning curves from psychological experiments.
+
+Eric Peters at DEC wrote the GAME OF EVEN WINS. The original author of EVEN WINS is unknown.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=60)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=75)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/35_Even_Wins/python/evenwins.py b/35_Even_Wins/python/evenwins.py
new file mode 100644
index 00000000..fba20024
--- /dev/null
+++ b/35_Even_Wins/python/evenwins.py
@@ -0,0 +1,206 @@
+# evenwins.py 
+
+#
+# This version of evenwins.bas based on game decscription and does *not*
+# follow the source. The computer chooses marbles at random.
+#
+# For simplicity, global variables are used to store the game state.
+# A good exercise would be to replace this with a class.
+#
+# The code is not short, but hopefully it is easy for beginners to understand
+# and modify.
+#
+# Infinite loops of the style "while True:" are used to simplify some of the
+# code. The "continue" keyword is used in a few places to jump back to the top
+# of the loop. The "return" keyword is also used to break out of functions.
+# This is generally considered poor style, but in this case it simplifies the
+# code and makes it easier to read (at least in my opinion). A good exercise
+# would be to remove these infinite loops, and uses of continue, to follow a
+# more structured style.
+#
+
+import random
+
+# global variables
+marbles_in_middle = -1
+human_marbles     = -1
+computer_marbles  = -1
+whose_turn        = ''
+
+# Only called during development for serious errors that are due to mistakes
+# in the program. Should never be called during a regular game.
+def serious_error(msg):
+    print('serious_error: ' + msg)
+    exit(1)
+
+def welcome_screen():
+    print('Welcome to Even Wins!')
+    print('Based on evenwins.bas from Creative Computing')
+    print()
+    print('Even Wins is a two-person game. You start with')
+    print('27 marbles in the middle of the table.')
+    print()
+    print('Players alternate taking marbles from the middle.')
+    print('A player can take 1 to 4 marbles on their turn, and')
+    print('turns cannot be skipped. The game ends when there are')
+    print('no marbles left, and the winner is the one with an even')
+    print('number of marbles.')
+    print()
+
+def marbles_str(n):
+    if n == 1: return '1 marble'
+    return f'{n} marbles'
+
+def choose_first_player():
+    global whose_turn
+    while True:
+        ans = input('Do you want to play first? (y/n) --> ')
+        if ans == 'y': 
+            whose_turn = 'human'
+            return
+        elif ans == 'n':
+            whose_turn = 'computer'
+            return
+        else:
+            print()
+            print('Please enter "y" if you want to play first,')
+            print('or "n" if you want to play second.')
+            print()
+
+def next_player():
+    global whose_turn
+    if whose_turn == 'human':
+        whose_turn = 'computer'
+    elif whose_turn == 'computer':
+        whose_turn = 'human'
+    else:
+        serious_error(f'play_game: unknown player {whose_turn}')
+
+# Converts a string s to an int, if possible.
+def to_int(s):
+    try:
+        n = int(s)
+        return True, n
+    except:
+        return False, 0
+
+def print_board():
+    global marbles_in_middle
+    global human_marbles
+    global computer_marbles
+    print()
+    print(f' marbles in the middle: {marbles_in_middle} ' + marbles_in_middle*'*')
+    print(f'    # marbles you have: {human_marbles}')
+    print(f'# marbles computer has: {computer_marbles}')
+    print()
+
+def human_turn():
+    global marbles_in_middle
+    global human_marbles
+
+    # get number in range 1 to min(4, marbles_in_middle)
+    max_choice = min(4, marbles_in_middle)
+    print("It's your turn!")
+    while True:
+        s = input(f'Marbles to take? (1 - {max_choice}) --> ')
+        ok, n = to_int(s)
+        if not ok:
+            print()
+            print(f'  Please enter a whole number from 1 to {max_choice}')
+            print()
+            continue
+        if n < 1:
+            print()
+            print('  You must take at least 1 marble!')
+            print()
+            continue
+        if n > max_choice:
+            print()
+            print(f'  You can take at most {marbles_str(max_choice)}')
+            print()
+            continue
+        print()
+        print(f'Okay, taking {marbles_str(n)} ...')
+        marbles_in_middle -= n
+        human_marbles += n
+        return
+
+def game_over():
+    global marbles_in_middle
+    global human_marbles
+    global computer_marbles
+    print()
+    print('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
+    print('!! All the marbles are taken: Game Over!')
+    print('!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!')
+    print()
+    print_board()
+    if human_marbles % 2 == 0:
+        print('You are the winner! Congratulations!')
+    else:
+        print('The computer wins: all hail mighty silicon!')
+    print('')
+
+def computer_turn():
+    global marbles_in_middle
+    global computer_marbles
+
+    print("It's the computer's turn ...")
+    max_choice = min(4, marbles_in_middle)
+
+    # choose at random
+    n = random.randint(1, max_choice)
+    print(f'Computer takes {marbles_str(n)} ...')
+    marbles_in_middle -= n
+    computer_marbles += n
+
+def play_game():
+    global marbles_in_middle
+    global human_marbles
+    global computer_marbles
+
+    # initialize the game state
+    marbles_in_middle = 27
+    human_marbles = 0
+    computer_marbles = 0
+    print_board()
+
+    while True:
+        if marbles_in_middle == 0:
+            game_over()
+            return
+        elif whose_turn == 'human':
+            human_turn()
+            print_board()
+            next_player()
+        elif whose_turn == 'computer':
+            computer_turn()
+            print_board()
+            next_player()
+        else:
+            serious_error(f'play_game: unknown player {whose_turn}')
+
+def main():
+    global whose_turn
+
+    welcome_screen()
+
+    while True:
+        choose_first_player()
+        play_game()
+
+        # ask if the user if they want to play again
+        print()
+        again = input('Would you like to play again? (y/n) --> ')
+        if again == 'y':
+            print()
+            print("Ok, let's play again ...")
+            print()
+        else:
+            print()
+            print('Ok, thanks for playing ... goodbye!')
+            print()
+            return
+
+if __name__ == '__main__':
+    main()
diff --git a/36_Flip_Flop/README.md b/36_Flip_Flop/README.md
index 689e6f7a..5f32111d 100644
--- a/36_Flip_Flop/README.md
+++ b/36_Flip_Flop/README.md
@@ -1,7 +1,28 @@
 ### Flip Flop
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=63
+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 0’s
+
+```
+0 0 0 0 0 0 0 0 0 0
+```
+
+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 0 in position 3, but it might possibly reverse some of other position too! You ought to be able to change all 10 in 12 or fewer moves. Can you figure out a good winning 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 Micheal Kass of New Hyde Park, New York.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=63)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=78)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
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/37_Football/README.md b/37_Football/README.md
index ccfe99e3..7f791861 100644
--- a/37_Football/README.md
+++ b/37_Football/README.md
@@ -1,7 +1,18 @@
 ### Football
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=64
+Football is probably the most popular simulated sports game. I have seen some people play to elect to play computerized football in preference to watching a football game on television.
+
+Two versions of football are presented. The first is somewhat “traditional” in that you, the player, are playing against the computer. You have a choice of seven offensive plays. On defense the computer seems to play a zone defence, but you have no choice of plays. The computer program presents the necessary rules as you play, and it is also the referee and determines penalties when an infraction is committed. FTBALL was written by John Kemeny at Dartmouth.
+
+IN the second version of football, the computer referees a game played between two human players. Each player gets a list of twenty plays with a code value for each one. This list should be kept confidential from your opponent. The codes can be changes in data. All twenty plays are offensive; a defensive play is specified by defending against a type of offensive play. A defense is good for other similar types of plays, for example, a defense against a flare pass is very good against a screen pass but much less good against a half-back option.
+
+This game was originally written by Raymond Miseyka of Butler, Pennsylvania.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=64)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=79)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/38_Fur_Trader/README.md b/38_Fur_Trader/README.md
index 503c089b..676b605b 100644
--- a/38_Fur_Trader/README.md
+++ b/38_Fur_Trader/README.md
@@ -1,7 +1,16 @@
 ### Fur Trader
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=69
+You are the leader of a French fur trading expedition in 1776 leaving the Ontario area to sell furs and get supplies for the next year. You have a choice of three forts at which you may trade. The cost of supplies and the amount you recieve for your furs will depend upon the fort you choose. You also specify what types of furs that you have to trade.
+
+The game goes on and on until you elect to trade no longer.
+
+Author of the program is Dan Bachor, University of Calgary, Alberta, Canada.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=69)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=84)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/39_Golf/README.md b/39_Golf/README.md
index a2292d02..28da3594 100644
--- a/39_Golf/README.md
+++ b/39_Golf/README.md
@@ -1,7 +1,15 @@
 ### Golf
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=71
+This is a single player golf game. In other words it’s you against the golf course (the computer). The program asks for your handicap (maximum of 30) and your area of difficulty. You have a bag of 29 clubs plus a putter. On the course you have to contend with rough, trees, on and off fairway, sand traps, and water hazards. In addition, you can hook, slice, go out of bounds, or hit too far. On putting, you determine the potency factor (or percent of swing). Until you get the swing of the game (no pun intended), you’ll probably was to use a fairly high handicap.
+
+Steve North of Creative Computing modified the original version of this game, the author of which is unknown.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=71)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=86)
+
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/40_Gomoko/README.md b/40_Gomoko/README.md
index 3f32ea2a..9b6ad6b6 100644
--- a/40_Gomoko/README.md
+++ b/40_Gomoko/README.md
@@ -1,7 +1,16 @@
 ### Gomoko
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=74
+GOMOKO or GOMOKU is a traditional game of the Orient. It is played by two people on a board of intersecting lines (19 left-to-right lines, 19 top-to-bottom lines, 361 intersections in all). Players take turns. During his turn, a player may cover one intersection with a marker; (one player uses white markers; the other player uses black markers). The object of the game is to get five adjacent markers in a row, horizontally, vertically or along either diagonal.
+
+Unfortunately, this program does not make the computer a very good player. It does not know when you are about to win or even who has won. But some of its moves may surprise you.
+
+The original author of this program is Peter Sessions of People’s Computer Company.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=74)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=89)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/41_Guess/README.md b/41_Guess/README.md
index e9876af9..cb041db7 100644
--- a/41_Guess/README.md
+++ b/41_Guess/README.md
@@ -1,7 +1,16 @@
 ### Guess
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=75
+In Program GUESS, the computer chooses a random integer between 0 and any limit you set. You must then try to guess the number the computer has chosen using the clues provided by the computer.
+
+You should be able to guess the number in one less than the number of digits needed to represent the number in binary notation — i.e., in base 2. This ought to give you a clue as to the optimum search technique.
+
+GUESS converted from the original program in FOCAL which appeared in the book “Computers in the Classroom” by Walt Koetke of Lexington High School, Lexington, Massachusetts.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=75)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=90)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/42_Gunner/README.md b/42_Gunner/README.md
index 4969ff20..0621ec54 100644
--- a/42_Gunner/README.md
+++ b/42_Gunner/README.md
@@ -1,7 +1,16 @@
 ### Gunner
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=77
+GUNNER allows you to adjust the fire of a field artillery weapon to hit a stationary target. You specify the number of degrees of elevation of your weapon; 45 degrees provides maximum range with values under or over 45 degrees providing less range.
+
+You get up to five shots to destroy the enemy before he destroys you. Gun range varies between 20,000 and 60,000 yards and burst radius is 100 yards. You must specify elevation within approximately 0.2 degrees to get a hit.
+
+Tom Kloos of the Oregon Museum of Science and Industry in Portland, Oregon originally wrote GUNNER. Extensive modifications were added by David Ahl.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=77)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=92)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/42_Gunner/csharp/Program.cs b/42_Gunner/csharp/Program.cs
new file mode 100644
index 00000000..f93c57d5
--- /dev/null
+++ b/42_Gunner/csharp/Program.cs
@@ -0,0 +1,124 @@
+namespace Gunner
+{
+    class Program
+    {
+        static void Main(string[] args)
+        {
+            PrintIntro();
+        
+            string keepPlaying = "Y";
+
+            while (keepPlaying == "Y") {
+                PlayGame();
+                Console.WriteLine("TRY AGAIN (Y OR N)");
+                keepPlaying = Console.ReadLine();
+            }
+        }
+
+        static void PlayGame()
+        {
+            int totalAttempts = 0;
+            int amountOfGames = 0;
+
+            while (amountOfGames < 4) {
+
+                int maximumRange = new Random().Next(0, 40000) + 20000;
+                Console.WriteLine($"MAXIMUM RANGE OF YOUR GUN IS {maximumRange} YARDS." + Environment.NewLine + Environment.NewLine + Environment.NewLine);
+
+                int distanceToTarget = (int) (maximumRange * (0.1 + 0.8 * new Random().NextDouble()));
+                Console.WriteLine($"DISTANCE TO THE TARGET IS {distanceToTarget} YARDS.");
+
+                (bool gameWon, int attempts) = HitTheTarget(maximumRange, distanceToTarget);
+
+                if(!gameWon) {
+                    Console.WriteLine(Environment.NewLine + "BOOM !!!!   YOU HAVE JUST BEEN DESTROYED" + Environment.NewLine +
+                        "BY THE ENEMY." + Environment.NewLine + Environment.NewLine + Environment.NewLine
+                    );
+                    PrintReturnToBase();
+                    break;
+                } else {
+                    amountOfGames += 1;
+                    totalAttempts += attempts;
+
+                    Console.WriteLine($"TOTAL ROUNDS EXPENDED WERE:{totalAttempts}");
+
+                    if (amountOfGames < 4) {
+                        Console.WriteLine("THE FORWARD OBSERVER HAS SIGHTED MORE ENEMY ACTIVITY...");                    
+                    } else {
+                        if (totalAttempts > 18) {
+                            PrintReturnToBase();
+                        } else {
+                            Console.WriteLine($"NICE SHOOTING !!");
+                        }
+                    }
+                }
+            }
+        }
+
+        static (bool, int) HitTheTarget(int maximumRange, int distanceToTarget)
+        {
+            int attempts = 0;
+
+            while (attempts < 6)
+            {
+                int elevation = GetElevation();
+
+                int differenceBetweenTargetAndImpact = CalculateDifferenceBetweenTargetAndImpact(maximumRange, distanceToTarget, elevation);
+
+                if (Math.Abs(differenceBetweenTargetAndImpact) < 100)
+                {
+                    Console.WriteLine($"*** TARGET DESTROYED *** {attempts} ROUNDS OF AMMUNITION EXPENDED.");
+                    return (true, attempts);
+                }
+                else if (differenceBetweenTargetAndImpact > 100)
+                {
+                    Console.WriteLine($"OVER TARGET BY {Math.Abs(differenceBetweenTargetAndImpact)} YARDS.");
+                }
+                else
+                {
+                    Console.WriteLine($"SHORT OF TARGET BY {Math.Abs(differenceBetweenTargetAndImpact)} YARDS.");
+                }
+
+                attempts += 1;
+            }
+            return (false, attempts);
+        }
+
+        static int CalculateDifferenceBetweenTargetAndImpact(int maximumRange, int distanceToTarget, int elevation)
+        {
+            double weirdNumber = 2 * elevation / 57.3;
+            double distanceShot = maximumRange * Math.Sin(weirdNumber);
+            return (int)distanceShot - distanceToTarget;
+        }
+
+        static void PrintReturnToBase()
+        {
+            Console.WriteLine("BETTER GO BACK TO FORT SILL FOR REFRESHER TRAINING!");
+        }
+
+        static int GetElevation()
+        {
+            Console.WriteLine("ELEVATION");
+            int elevation = int.Parse(Console.ReadLine());
+            if (elevation > 89) {
+                Console.WriteLine("MAXIMUM ELEVATION IS 89 DEGREES");
+                return GetElevation();
+            }
+            if (elevation < 1) {
+                Console.WriteLine("MINIMUM ELEVATION IS 1 DEGREE");
+                return GetElevation();
+            }
+            return elevation;
+        }
+
+        static void PrintIntro() 
+        {
+            Console.WriteLine(new String(' ', 30) + "GUNNER");
+            Console.WriteLine(new String(' ', 15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY" + Environment.NewLine + Environment.NewLine + Environment.NewLine);
+            Console.WriteLine("YOU ARE THE OFFICER-IN-CHARGE, GIVING ORDERS TO A GUN");
+            Console.WriteLine("CREW, TELLING THEM THE DEGREES OF ELEVATION YOU ESTIMATE");
+            Console.WriteLine("WILL PLACE A PROJECTILE ON TARGET.  A HIT WITHIN 100 YARDS");
+            Console.WriteLine("OF THE TARGET WILL DESTROY IT." + Environment.NewLine);
+        }
+    }
+}
diff --git a/42_Gunner/csharp/csharp.csproj b/42_Gunner/csharp/csharp.csproj
new file mode 100644
index 00000000..74abf5c9
--- /dev/null
+++ b/42_Gunner/csharp/csharp.csproj
@@ -0,0 +1,10 @@
+
+
+  
+    Exe
+    net6.0
+    enable
+    enable
+  
+
+
diff --git a/43_Hammurabi/README.md b/43_Hammurabi/README.md
index 22378ac0..c92b7ee3 100644
--- a/43_Hammurabi/README.md
+++ b/43_Hammurabi/README.md
@@ -1,7 +1,22 @@
 ### Hammurabi
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=78
+In this game you direct the administrator of Sumeria, Hammurabi, how to manage the city. The city initially has 1,000 acres, 100 people and 3,000 bushels of grain in storage.
+
+You may buy and sell land with your neighboring city-states for bushels of grain — the price will vary between 17 and 26 bushels per acre. You also must use grain to feed your people and as seed to plant the next year’s crop.
+
+You will quickly find that a certain number of people can only tend a certain amount of land and that people starve if they are not fed enough. You also have the unexpected to contend with such as a plague, rats destroying stored grain, and variable harvests.
+
+You will also find that managing just the few resources in this game is not a trivial job over a period of say ten years. The crisis of population density rears its head very rapidly.
+
+This program was originally written in Focal at DEC; author unknown. David Ahl converted it to BASIC and added the 10-year performance assessment. If you wish to change any of the factors, the extensive remarks in the program should make modification fairly straightforward.
+
+Note for trivia buffs: somewhere along the line an m was dropped out of the spelling of Hammurabi in hte Ahl version of the computer program. This error has spread far and wide until a generation of students now think that Hammurabi is the incorrect spelling.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=78)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=93)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/44_Hangman/README.md b/44_Hangman/README.md
index e44b0c0a..ee533251 100644
--- a/44_Hangman/README.md
+++ b/44_Hangman/README.md
@@ -1,7 +1,26 @@
 ### Hangman
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=80
+This is a simulation of the word guessing game, hangman. The computer picks a word, tells you how many letters in the word it has picked and then you guess a letter in the word. If you are right, the computer tells you where that letter belongs; if your letter is wrong, the computer starts to hang you. You get ten guesses before you are completely hanged:
+1. Head
+2. Body
+3. Right Arm
+4. Left Arm
+5. Right Leg
+6. Left Leg
+7. Right Hand
+8. Left Hand
+9. Right Foot
+10. Left Foot
+
+You may add words in Data statements; however if you do, you must also change the random word selector.
+
+David Ahl modified this program into its current form from the one created by Kenneth Aupperle of Melville, New York.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=80)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=95)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/45_Hello/README.md b/45_Hello/README.md
index 7c72beca..117db0ce 100644
--- a/45_Hello/README.md
+++ b/45_Hello/README.md
@@ -1,7 +1,16 @@
 ### Hello
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=82
+This is a sample of one of the great number of conversational programs. In a sense, it is like a CAI program except that its responses are just good fun. Whenever a computer is exhibited at a convention or conference with people that have not used a computer before, the conversational programs seem to get the first activity.
+
+In this particular program, the computer dispenses advice on various problems such as sex. health, money, or job.
+
+David Ahl is the author of HELLO.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=82)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=97)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
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/46_Hexapawn/README.md b/46_Hexapawn/README.md
index 6380a1b3..0197fa14 100644
--- a/46_Hexapawn/README.md
+++ b/46_Hexapawn/README.md
@@ -1,7 +1,16 @@
 ### Hexapawn
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=83
+The game of Hexapawn and a method to learn a strategy for playing the game was described in Martin Gardner’s “Mathematical Games” column in the March 1962 issue of _Scientific American_. The method described in the article was for a hypothetical learning machine composed of match boxes and colored beads. This has been generalized in the program HEX.
+
+The program learns by elimination of bad moves. All positions encountered by the program and acceptable moves from them are stored in an array. When the program encounters an unfamiliar position, the position and all legal moves from it are added to the list. If the program loses a game, it erases the move that led to defeat. If it hits a position from which all moves have been deleted (they all led to defeat), it erases the move that got it there and resigns. Eventually, the program learns to play extremely well and, indeed, is unbeatable. The learning strategy could be adopted to other simple games with a finite number of moves (tic-tac-toe, small board checkers, or other chess-based games).
+
+The original version of this program was written by R.A. Kaapke. It was subsequently modified by Jeff Dalton and finally by Steve North of Creative Computing.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=83)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=98)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/47_Hi-Lo/README.md b/47_Hi-Lo/README.md
index cf2f93be..7fb37221 100644
--- a/47_Hi-Lo/README.md
+++ b/47_Hi-Lo/README.md
@@ -1,7 +1,21 @@
 ### Hi-Lo
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=85
+This game is an adaptation of the game GUESS; however, instead of just guessing a number between 1 and 100, in this game you win dollars when you guess the number. The directions, in the words of the author, are as follows:
+1. There is an amount of money, between one and one hundred dollars, in the “HI-LO” jackpot.
+2. You will have six chances in which to guess the amount of money in the jackpot.
+3. After each guess, the computer will tell whether the guess was too high or too low.
+4. If the correct amount of money is not guessed after six chances, the computer will print the amount in the jackpot.
+5. If the correct amount of money is guessed within the six chance limit, the computer will register this amount.
+6. After each sequence of guesses, you have the choice of playing again or ending the program. If a new game is played, a new amount of money will constitute the jackpot.
+7. If youwin more than once, then your earnings are totalled.
+
+The author is Dean ALtman of Fort Worth, Texas.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=85)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=100)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
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/48_High_IQ/README.md b/48_High_IQ/README.md
index 3da523da..7dee9c95 100644
--- a/48_High_IQ/README.md
+++ b/48_High_IQ/README.md
@@ -1,7 +1,16 @@
 ### High IQ
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=86
+This is a computerized version of an old European solitaire game of logic. The game starts with a pegboard shaped like a cross having pegs in every hole but the center. The object is to remove all 32 pegs, or as many as possible, by jumping into an empty hole, then removing the jumped peg.
+
+There are several different winning strategies for playing, and of course, each strategy can be played eight different ways on the board. Can you find a consistent winner?
+
+Charles Lund wrote this game while at The American School in The Hague, Netherlands.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=86)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=101)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/49_Hockey/README.md b/49_Hockey/README.md
index 8d83017d..41e6950e 100644
--- a/49_Hockey/README.md
+++ b/49_Hockey/README.md
@@ -1,7 +1,16 @@
 ### Hockey
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=88
+This is a simulation of a ice hockey game. The computer, in this case, moderates and referees the pay between two human opponents. Of course, one person could play both sides.
+
+The program asks for team names, player names, and even the name of the referee. Four types of shot are permitted and a shot may be aimed at one of four areas. You are also asked about passing. The game is very comprehensive with lots of action, face offs, blocks, passes, 4 on 2 situations, and so on. Unfortunately there are no penalties.
+
+The original author is Robert Puopolo; modifications by Steve North of Creative Computing.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=88)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=103)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/50_Horserace/README.md b/50_Horserace/README.md
index ce781ffd..00e24df5 100644
--- a/50_Horserace/README.md
+++ b/50_Horserace/README.md
@@ -1,7 +1,14 @@
 ### Horserace
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=92
+This program simulates a one-mile horse race for three-year old throughbreds. Up to ten people may place bets on the race up to $10,000 each. However, you may only bet to win. You place your bet by inputting the number of the horse, a comma, and the amount of your bet. The computer then shows the position of the horses at seven points around the track and at the finish. Payoffs and winnings are shown at the end.
+
+The program was written by Laurie Chevalier while a student at South Portland High School.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=92)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=107)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/50_Horserace/python/horserace.py b/50_Horserace/python/horserace.py
new file mode 100644
index 00000000..cf7c266e
--- /dev/null
+++ b/50_Horserace/python/horserace.py
@@ -0,0 +1,278 @@
+import random
+import math
+import time
+
+def basic_print(*zones, **kwargs):
+    """Simulates the PRINT command from BASIC to some degree.
+    Supports `printing zones` if given multiple arguments."""
+
+    line = ""
+    if len(zones) == 1:
+        line = str(zones[0])
+    else:
+        line = "".join(["{:<14}".format(str(zone)) for zone in zones])
+    identation = kwargs.get("indent", 0)
+    end = kwargs.get("end", "\n")
+    print(" " * identation + line, end=end)
+
+
+def basic_input(prompt, type_conversion=None):
+    """BASIC INPUT command with optional type conversion"""
+
+    while True:
+        try:
+            inp = input(f"{prompt}? ")
+            if type_conversion is not None:
+                inp = type_conversion(inp)
+            break
+        except ValueError:
+            basic_print("INVALID INPUT!")
+    return inp
+
+
+# horse names do not change over the program, therefore making it a global.
+# throught the game, the ordering of the horses is used to indentify them
+HORSE_NAMES = [
+    "JOE MAW",
+    "L.B.J.",
+    "MR.WASHBURN",
+    "MISS KAREN",
+    "JOLLY",
+    "HORSE",
+    "JELLY DO NOT",
+    "MIDNIGHT"
+]
+
+
+def introduction():
+    """Print the introduction, and optional the instructions"""
+
+    basic_print("HORSERACE", indent=31)
+    basic_print("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY", indent=15)
+    basic_print("\n\n")
+    basic_print("WELCOME TO SOUTH PORTLAND HIGH RACETRACK")
+    basic_print("                      ...OWNED BY LAURIE CHEVALIER")
+    y_n = basic_input("DO YOU WANT DIRECTIONS")
+
+    # if no instructions needed, return
+    if y_n.upper() == "NO":
+        return
+
+    basic_print("UP TO 10 MAY PLAY.  A TABLE OF ODDS WILL BE PRINTED.  YOU")
+    basic_print("MAY BET ANY + AMOUNT UNDER 100000 ON ONE HORSE.")
+    basic_print("DURING THE RACE, A HORSE WILL BE SHOWN BY ITS")
+    basic_print("NUMBER.  THE HORSES RACE DOWN THE PAPER!")
+    basic_print("")
+
+
+def setup_players():
+    """Gather the number of players and their names"""
+
+    # ensure we get an integer value from the user
+    number_of_players = basic_input("HOW MANY WANT TO BET", int)
+
+    # for each user query their name and return the list of names
+    player_names = []
+    basic_print("WHEN ? APPEARS,TYPE NAME")
+    for _ in range(number_of_players):
+        player_names.append(basic_input(""))
+    return player_names
+
+
+def setup_horses():
+    """Generates random odds for each horse. Returns a list of
+    odds, indexed by the order of the global HORSE_NAMES."""
+
+    odds = [random.randrange(1, 10) for _ in HORSE_NAMES]
+    total = sum(odds)
+
+    # rounding odds to two decimals for nicer output,
+    # this is not in the origin implementation
+    return [round(total/odd, 2) for odd in odds]
+
+
+def print_horse_odds(odds):
+    """Print the odds for each horse"""
+
+    basic_print("")
+    for i in range(len(HORSE_NAMES)):
+        basic_print(HORSE_NAMES[i], i, f"{odds[i]}:1")
+    basic_print("")
+
+
+def get_bets(player_names):
+    """For each player, get the number of the horse to bet on,
+    as well as the amount of money to bet"""
+
+    basic_print("--------------------------------------------------")
+    basic_print("PLACE YOUR BETS...HORSE # THEN AMOUNT")
+
+    bets = []
+    for name in player_names:
+        horse = basic_input(name, int)
+        amount = None
+        while amount is None:
+            amount = basic_input("", float)
+            if amount < 1 or amount >= 100000:
+                basic_print("  YOU CAN'T DO THAT!")
+                amount = None
+        bets.append((horse, amount))
+    
+    basic_print("")
+
+    return bets
+
+
+def get_distance(odd):
+    """Advances a horse during one step of the racing simulation.
+    The amount travelled is random, but scaled by the odds of the horse"""
+
+    d = random.randrange(1,100)
+    s = math.ceil(odd)
+    if d < 10:
+        return 1
+    elif d < s + 17:
+        return 2
+    elif d < s + 37:
+        return 3
+    elif d < s + 57:
+        return 4
+    elif d < s + 77:
+        return 5
+    elif d < s + 77:
+        return 5
+    elif d < s + 92:
+        return 6
+    else:
+        return 7
+
+
+def print_race_state(total_distance, race_pos):
+    """Outputs the current state/stop of the race.
+    Each horse is placed according to the distance they have travelled. In
+    case some horses travelled the same distance, their numbers are printed
+    on the same name"""
+
+    # we dont want to modify the `race_pos` list, since we need
+    # it later. Therefore we generating an interator from the list
+    race_pos_iter = iter(race_pos)
+
+    # race_pos is stored by last to first horse in the race.
+    # we get the next horse we need to print out
+    next_pos = next(race_pos_iter)
+    
+    # start line
+    basic_print("XXXXSTARTXXXX")
+
+    # print all 28 lines/unit of the race course
+    for l in range(28):
+
+        # ensure we still have a horse to print and if so, check if the
+        # next horse to print is not the current line
+        # needs iteration, since multiple horses can share the same line
+        while next_pos is not None and l == total_distance[next_pos]:
+            basic_print(f"{next_pos} ", end="")
+            next_pos = next(race_pos_iter, None)
+        else:
+            # if no horses are left to print for this line, print a new line
+            basic_print("")
+
+    # finish line
+    basic_print("XXXXFINISHXXXX")
+
+
+def simulate_race(odds):
+    num_horses = len(HORSE_NAMES)
+
+    # in spirit of the original implementation, using two arrays to
+    # track the total distance travelled, and create an index from
+    # race position -> horse index
+    total_distance = [0] * num_horses
+
+    # race_pos maps from the position in the race, to the index of the horse
+    # it will later be sorted from last to first horse, based on the
+    # distance travelled by each horse.
+    # e.g. race_pos[0] => last horse
+    #      race_pos[-1] => winning horse
+    race_pos = list(range(num_horses))
+    
+    basic_print("\n1 2 3 4 5 6 7 8")
+
+    while True:
+
+        # advance each horse by a random amount
+        for i in range(num_horses):
+            total_distance[i] += get_distance(odds[i])
+
+        # bubble sort race_pos based on total distance travelled
+        # in the original implementation, race_pos is reset for each 
+        # simulation step, so we keep this behaviour here
+        race_pos = list(range(num_horses))
+        for l in range(num_horses):
+            for i in range(num_horses-1-l):
+                if total_distance[race_pos[i]] < total_distance[race_pos[i+1]]:
+                    continue
+                race_pos[i], race_pos[i+1] = race_pos[i+1], race_pos[i]
+
+        # print current state of the race
+        print_race_state(total_distance, race_pos)
+
+        # goal line is defined as 28 units from start
+        # check if the winning horse is already over the finish line
+        if total_distance[race_pos[-1]] >= 28:
+            return race_pos
+        
+        # this was not in the original BASIC implementation, but it makes the
+        # race visualization a nice animation (if the terminal size is set to 31 rows)
+        time.sleep(1)
+
+
+def print_race_results(race_positions, odds, bets, player_names):
+    """Print the race results, as well as the winnings of each player"""
+
+    # print the race positions first
+    basic_print("THE RACE RESULTS ARE:")
+    position = 1
+    for horse_idx in reversed(race_positions):
+        line = f"{position} PLACE HORSE NO. {horse_idx} AT {odds[horse_idx]}:1"
+        basic_print("")
+        basic_print(line)
+        position += 1
+
+    # followed by the amount the players won
+    winning_horse_idx = race_positions[-1]
+    for idx, name in enumerate(player_names):
+        (horse, amount) = bets[idx]
+        if horse == winning_horse_idx:
+            basic_print("")
+            basic_print(f"{name} WINS ${amount * odds[winning_horse_idx]}")
+    
+
+def main_loop(player_names, horse_odds):
+    """Main game loop"""
+
+    while True:
+        print_horse_odds(horse_odds)
+        bets = get_bets(player_names)
+        final_race_positions = simulate_race(horse_odds)
+        print_race_results(final_race_positions, horse_odds, bets, player_names)
+
+        basic_print("DO YOU WANT TO BET ON THE NEXT RACE ?")
+        one_more = basic_input("YES OR NO")
+        if one_more.upper() != "YES":
+            break
+
+
+def main():
+    # introduction, player names and horse odds are only generated once
+    introduction()
+    player_names = setup_players()
+    horse_odds = setup_horses()
+
+    # main loop of the game, the player can play multiple races, with the
+    # same odds
+    main_loop(player_names, horse_odds)
+    
+
+if __name__ == "__main__":
+    main()
\ No newline at end of file
diff --git a/51_Hurkle/README.md b/51_Hurkle/README.md
index 1a13ac8e..0204e405 100644
--- a/51_Hurkle/README.md
+++ b/51_Hurkle/README.md
@@ -1,7 +1,16 @@
 ### Hurkle
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=94
+Hurkle? A Hurkle is a happy beast and lives in another galaxy on a planet named Lirht that has three moons. Hurkle are favorite pets of the Gwik, the dominant race of Lihrt and … well, to find out more, read “The Hurkle is a Happy Beast,” a story in the book _A Way Home_ by Theodore Sturgeon.
+
+In this program a shy hurkle is hiding on a 10 by 10 grid. Homebase is point 0,0 in the _Southwest_ corner. Your guess as to the gridpoint where the hurkle is hiding should be a pair of whole numbers, separated by a comma. After each try, the computer will tell you the approximate direction to go look for the Hurkle. You get five guesses to find him; you may change this number, although four guesses is actually enough.
+
+This program was written by Bob Albrecht of People’s Computer Company.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=94)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=109)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/52_Kinema/README.md b/52_Kinema/README.md
index d3c74687..31a69199 100644
--- a/52_Kinema/README.md
+++ b/52_Kinema/README.md
@@ -1,7 +1,19 @@
 ### Kinema
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=95
+This program tests your fundamental knowledge of kinematics. It presents a simple problem: a ball is thrown straight up into the air at some random velocity. You then must answer three questions about the flight of the ball:
+1. How high will it go?
+2. How long until it returns to earth?
+3. What will be its velocity after a random number of seconds?
+
+The computer evaluates your performance; within 15% of the correct answer is considered close enough. After each run, the computer gives you another problem until you interrupt it.
+
+KINEMA was shorted from the original Huntington Computer Project Program, KINERV, by Richard Pav of Patchogue High School, Patchogue, New York.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=95)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=110)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/53_King/README.md b/53_King/README.md
index cf570189..eb0ba8ab 100644
--- a/53_King/README.md
+++ b/53_King/README.md
@@ -1,7 +1,18 @@
 ### King
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=96
+This is one of the most comprehensive, difficult, and interesting games. (If you’ve never played one of these games, start with HAMMURABI.)
+
+In this game, you are Premier of Setats Detinu, a small communist island 30 by 70 miles long. Your job is to decide upon the budget of your country and distribute money to your country from the communal treasury.
+
+The money system is Rollods; each person needs 100 Rallods per year to survive. Your country’s income comes from farm produce and tourists visiting your magnificent forests, hunting, fishing, etc. Part of your land is farm land but it also has an excellent mineral content and may be sold to foreign industry for strip mining. Industry import and support their own workers. Crops cost between 10 and 15 Rallods per square mile to plant, cultivate, and harvest. Your goal is to complete an eight-year term of office without major mishap. A word of warning: it isn’t easy!
+
+The author of this program is James A. Storer who wrote it while a student at Lexington High School.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=96)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=111)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/54_Letter/README.md b/54_Letter/README.md
index eef18d38..fab67494 100644
--- a/54_Letter/README.md
+++ b/54_Letter/README.md
@@ -1,7 +1,14 @@
 ### Letter
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=99
+LETTER is similar to the game GUESS in which you guess a number chosen by the computer; in this program, the computer picks a random letter of the alphabet and you must guess which one it is using the clues provided as you go along. It should not take you more than five guesses to get the mystery letter.
+
+The program which appears here is loosely based on the original written by Bob Albrect of People’s Computer Company.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=99)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=114)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/55_Life/README.md b/55_Life/README.md
index e6a2b702..149bf25d 100644
--- a/55_Life/README.md
+++ b/55_Life/README.md
@@ -1,7 +1,35 @@
 ### Life
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=100
+The Game of Life was originally described in _Scientific American_, October 1970, in an article by Martin Gardner. The game itself was originated by John Conway of Gonville and Caius College, University of Cambridge England.
+
+In the “manual” game, organisms exist in the form of counters (chips or checkers) on a large checkerboard and die or reproduce according to some simple genetic rules. Conway’s criteria for choosing his genetic laws were carefully delineated as follows:
+1. There should be no initial pattern for which there is a simple proof that the population can grow without limit.
+2. There should be simple initial patterns that apparently do grow without limit.
+3. There should be simple initial patterns that grow and change for a considerable period of time before coming to an end in three possible ways:
+    1. Fading away completely (from overcrowding or from becoming too sparse)
+    2. Settling into a stable configuration that remains unchanged thereafter
+    3. Entering an oscillating phase in which they repeat an endless cycle of two or more periods
+
+In brief, the rules should be such as to make the behavior of the population relatively unpredictable. Conway’s genetic laws are delightfully simple. First note that each cell of the checkerboard (assumed to be an infinite plane) has eight neighboring cells, four adjacent orthogonally, four adjacent diagonally. The rules are:
+1. Survivals. Every counter with two or three neighboring counters survives for the next generation.
+2. Deaths. Each counter with four or more neighbors dies (is removed) from overpopulation. Every counter with one neighbor or none dies from isolation.
+3. Births. Each empty cell adjacent to exactly three neighbors — no more — is a birth cell. A counter is placed on it at the next move.
+
+It is important to understand that all births and deaths occur simultaneously. Together they constitute a single generation or, as we shall call it, a “move” in the complete “life history” of the initial configuration.
+
+You will find the population constantly undergoing unusual, sometimes beautiful and always unexpected change. In a few cases the society eventually dies out (all counters vanishing), although this may not happen until after a great many generations. Most starting patterns either reach stable figures — Conway calls them “still lifes” — that cannot change or patterns that oscillate forever. Patterns with no initial symmetry tend to become symmetrical. Once this happens the symmetry cannot be lost, although it may increase in richness.
+
+Conway used a DEC PDP-7 with a graphic display to observe long-lived populations. You’ll probably find this more enjoyable to watch on a CRT than a hard-copy terminal.
+
+Since MITS 8K BASIC does not have LINE INPUT, to enter leading blanks in the patter, type a “.” at the start of the line. This will be converted to a space by BASIC, but it permits you to type leading spaces. Typing DONE indicates that you are finished entering the pattern. See sample run.
+
+Clark Baker of Project DELTA originally wrote this version of LIFE which was further modified by Steve North of Creative Computing.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=100)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=115)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/56_Life_for_Two/README.md b/56_Life_for_Two/README.md
index 34030043..d880653d 100644
--- a/56_Life_for_Two/README.md
+++ b/56_Life_for_Two/README.md
@@ -1,7 +1,44 @@
 ### Life for Two
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=102
+LIFE-2 is based on Conway’s game of Life. You must be familiar with the rules of LIFE before attempting to play LIFE-2.
+
+There are two players; the game is played on a 5x5 board and each player has a symbol to represent his own pieces of ‘life.’ Live cells belonging to player 1 are represented by `*` and live cells belonging to player 2 are represented by the symbol `#`.
+
+The # and * are regarded as the same except when deciding whether to generate a live cell. An empty cell having two `#` and one `*` for neighbors will generate a `#`, i.e. the live cell generated belongs to the player who has the majority of the 3 live cells surrounding the empty cell where life is to be generated, for example:
+
+|   | 1 | 2 | 3 | 4 | 5 |
+|:-:|:-:|:-:|:-:|:-:|:-:|
+| 1 |   |   |   |   |   |
+| 2 |   |   | * |   |   |
+| 3 |   |   |   | # |   |
+| 4 |   |   | # |   |   |
+| 5 |   |   |   |   |   |
+
+A new cell will be generated at (3,3) which will be a `#` since there are two `#` and one `*` surrounding. The board will then become:
+
+|   | 1 | 2 | 3 | 4 | 5 |
+|:-:|:-:|:-:|:-:|:-:|:-:|
+| 1 |   |   |   |   |   |
+| 2 |   |   |   |   |   |
+| 3 |   |   | # | # |   |
+| 4 |   |   |   |   |   |
+| 5 |   |   |   |   |   |
+
+On the first most each player positions 3 pieces of life on the board by typing in the co-ordinates of the pieces. (In the event of the same cell being chosen by both players that cell is left empty.)
+
+The board is then adjusted to the next generation and printed out.
+
+On each subsequent turn each player places one piece on the board, the object being to annihilate his opponent’s pieces. The board is adjusted for the next generation and printed out after both players have entered their new piece.
+
+The game continues until one player has no more live pieces. The computer will then print out the board and declare the winner.
+
+The idea for this game, the game itself, and the above write-up were written by Brian Wyvill of Bradford University in Yorkshire, England.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=102)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=117)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/57_Literature_Quiz/README.md b/57_Literature_Quiz/README.md
index 54605904..2e55251f 100644
--- a/57_Literature_Quiz/README.md
+++ b/57_Literature_Quiz/README.md
@@ -1,7 +1,14 @@
 ### Literature Quiz
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=104
+This is a simple CAI-type program which presents four multiple-choice questions from children’s literature. Running the program is self-explanatory.
+
+The program was written by Pamela McGinley while at DEC.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=104)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=117)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/58_Love/README.md b/58_Love/README.md
index 35fb6f7d..1b2431aa 100644
--- a/58_Love/README.md
+++ b/58_Love/README.md
@@ -1,7 +1,14 @@
 ### Love
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=105
+This program is designed to reproduce Robert Indiana’s great art work “Love” with a message of your choice up to 60 characters long.
+
+The love program was created by David Ahl.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=105)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=120)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/59_Lunar_LEM_Rocket/README.md b/59_Lunar_LEM_Rocket/README.md
index eced07e0..9d1890ac 100644
--- a/59_Lunar_LEM_Rocket/README.md
+++ b/59_Lunar_LEM_Rocket/README.md
@@ -1,7 +1,24 @@
 ### Lunar LEM Rocket
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=106
+This game in its many different versions and names (ROCKET, LUNAR, LEM, and APOLLO) is by far and away the single most popular computer game. It exists in various versions that start you anywhere from 500 feet to 200 miles away from the moon, or other planets, too. Some allow the control of directional stabilization rockets and/or the retro rocket. The three versions presented here represent the most popular of the many variations.
+
+In most versions of this game, the temptation is to slow up too soon and then have no fuel left for the lower part of the journey. This, of course, is disastrous (as you will find out when you land your own capsule)!
+
+LUNAR was originally in FOCAL by Jim Storer while a student at Lexington High School and subsequently converted to BASIC by David Ahl. ROCKET was written by Eric Peters at DEC and LEM by William Labaree II of Alexandria, Virginia.
+
+In this program, you set the burn rate of the retro rockets (pounds of fuel per second) every 10 seconds and attempt to achieve a soft landing on the moon. 200 lbs/sec really puts the brakes on, and 0 lbs/sec is free fall. Ignition occurs a 8 lbs/sec, so _do not_ use burn rates between 1 and 7 lbs/sec. To make the landing more of a challenge, but more closely approximate the real Apollo LEM capsule, you should make the available fuel at the start (N) equal to 16,000 lbs, and the weight of the capsule (M) equal to 32,500 lbs.
+
+#### LEM
+This is the most comprehensive of the three versions and permits you to control the time interval of firing, the thrust, and the attitude angle. It also allows you to work in the metric or English system of measurement. The instructions in the program dialog are very complete, so you shouldn’t have any trouble.
+
+#### ROCKET
+In this version, you start 500 feet above the lunar surface and control the burn rate in 1-second bursts. Each unit of fuel slows your descent by 1 ft/sec. The maximum thrust of your engine is 30 ft/sec/sec.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=106)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=121)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
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/60_Mastermind/README.md b/60_Mastermind/README.md
index d78cf527..e9bf1c3c 100644
--- a/60_Mastermind/README.md
+++ b/60_Mastermind/README.md
@@ -1,7 +1,29 @@
 ### MasterMind
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=110
+In that Match-April 1976 issue of _Creative_ we published a computerized version of Master Mind, a logic game. Master Mind is played by two people—one is called the code-maker; the other, the code-breaker. At the beginning of the game the code-maker forms a code, or combination of colored pegs. He hides these from the code-breaker. The code-breaker then attempts to deduce the code, by placing his own guesses, one at a time, on the board. After he makes a guess (by placing a combination of colored pegs on the board) the code-maker then gives the code-breaker clues to indicate how close the guess was to the code. For every peg in the guess that’s the right color but not in the right position, the code-breaker gets a white peg. Note that these black and white pegs do not indicate _which_ pegs in the guess are correct, but merely that they exist. For example, if the code was:
+```
+Yellow Red Red Green
+```
+
+and my guess was
+```
+Red Red Yellow Black
+```
+I would receive two white pegs and one black peg for the guess. I wouldn’t know (except by comparing previous guesses) which one of the pegs in my guess was the right color in the right position.
+
+Many people have written computer programs to play Master Mind in the passive role, i.e., the computer is the code maker and the human is the code-breaker. This is relatively trivial; the challenge is writing a program that can also play actively as a code-breaker.
+
+Actually, the task of getting the computer to deduce the correct combination is not at all difficult. Imagine, for instance, that you made a list of all possible codes. To begin, you select a guess from your list at random. Then, as you receive clues, you cross off from the list those combinations which you know are impossible. For example if your guess is Red Red Green Green and you receive no pegs, then you know that any combination containing either a red or a green peg is impossible and may be crossed of the list. The process is continued until the correct solution is reached or there are no more combinations left on the list (in which case you know that the code-maker made a mistake in giving you the clues somewhere).
+
+Note that in this particular implementation, we never actually create a list of the combinations, but merely keep track of which ones (in sequential order) may be correct. Using this system, we can easily say that the 523rd combination may be correct, but to actually produce the 523rd combination we have to count all the way from the first combination (or the previous one, if it was lower than 523). Actually, this problem could be simplified to a conversion from base 10 to base (number of colors) and then adjusting the values used in the MID$ function so as not to take a zeroth character from a string if you want to experiment. We did try a version that kept an actual list of all possible combinations (as a string array), which was significantly faster than this version, but which ate tremendous amounts of memory.
+
+At the beginning of this game, you input the number of colors and number of positions you wish to use (which will directly affect the number of combinations) and the number of rounds you wish to play. While you are playing as the code-breaker, you may type BOARD at any time to get a list of your previous guesses and clues, and QUIT to end the game. Note that this version uses string arrays, but this is merely for convenience and can easily be converted for a BASIC that has no string arrays as long as it has a MID$ function. This is because the string arrays are one-dimensional, never exceed a length greater than the number of positions and the elements never contain more than one character.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=110)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=125)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/61_Math_Dice/README.md b/61_Math_Dice/README.md
index f6285a11..a24c9dbe 100644
--- a/61_Math_Dice/README.md
+++ b/61_Math_Dice/README.md
@@ -1,7 +1,14 @@
 ### Math Dice
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=113
+The program presents pictorial drill on addition facts using printed dice with no reading involved. It is good for beginning addition, since the answer can be derived from counting spots on the dice as well as by memorizing math facts or awareness of number concepts. It is especially effective run on a CRT terminal.
+
+It was originally written by Jim Gerrish, a teacher at the Bernice A. Ray School in Hanover, New Hampshire.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=113)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=128)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
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/62_Mugwump/README.md b/62_Mugwump/README.md
index 421f0112..c67037f2 100644
--- a/62_Mugwump/README.md
+++ b/62_Mugwump/README.md
@@ -1,7 +1,18 @@
 ### Mugwump
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=114
+Your objective in this game is to find the four Mugwumps hiding on various squares of a 10 by 10 grid. Homebase (lower left) is position (0,0) and a guess is a pair of whole numbers (0 to 9), separated by commas. The first number is the number of units to the right of homebase and the second number is the distance above homebase.
+
+You get ten guesses to locate the four Mugwumps; after each guess, the computer tells you how close you are to each Mugwump. Playing the game with the aid of graph paper and a compass should allow you to find all the Mugwumps in six or seven moves using triangulation similar to Loran radio navigation.
+
+If you want to make the game somewhat more difficult, you can print the distance to each Mugwump either rounded or truncated to the nearest integer.
+
+This program was modified slightly by Bob Albrecht of People’s Computer Company. It was originally written by students of Bud Valenti of Project SOLO in Pittsburg, Pennsylvania.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=114)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=129)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/63_Name/README.md b/63_Name/README.md
index daa110c1..45bf4e9a 100644
--- a/63_Name/README.md
+++ b/63_Name/README.md
@@ -1,7 +1,14 @@
 ### Name
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=116
+NAME is a silly little ice-breaker to get a relationship going between a computer and a shy human. The sorting algorithm used is highly inefficient — as any reader of _Creative Computing_ will recognize, this is the worst possible sort for speed. But the program is good fun and that’s what counts here.
+
+NAME was originally written by Geoffrey Chase of the Abbey, Portsmouth, Rhode Island.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=116)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=131)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/64_Nicomachus/README.md b/64_Nicomachus/README.md
index f54f600e..bbe911fc 100644
--- a/64_Nicomachus/README.md
+++ b/64_Nicomachus/README.md
@@ -1,7 +1,18 @@
 ### Nicomachus
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=117
+One of the most ancient forms of arithmetic puzzle is sometimes referred to as a “boomerang.” At some time, everyone has been asked to “think of a number,” and, after going through some process of private calculation, to state the result, after which the questioner promptly tells you the number you originally thought of. There are hundreds of varieties of this puzzle.
+
+The oldest recorded example appears to be that given in _Arithmetica_ of Nicomachus, who died about the year 120. He tells you to think of any whole number between 1 and 100 and divide it successfully by 3, 5, and 7, telling him the remainder in each case. On receiving this information, he promptly discloses the number you thought of.
+
+Can you discover a simple method of mentally performing this feat? If not, you can see how the ancient mathematician did it by looking at this program.
+
+Nicomachus was written by David Ahl.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=117)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=132)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/65_Nim/README.md b/65_Nim/README.md
index a47d44e4..867f641d 100644
--- a/65_Nim/README.md
+++ b/65_Nim/README.md
@@ -1,7 +1,27 @@
 ### Nim
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=118
+NIM is one of the oldest two-person games known to man; it is believed to have originated in ancient China. The name, which was coined by the first mathematician to analyze it, comes from an archaic English verb which means to steal or to take away. Objects are arranged in rows between the two opponents as in the following example:
+|         |       |           |
+|---------|-------|-----------|
+| XXXXXXX | Row 1 | 7 Objects |
+| XXXXX   | Row 2 | 5 Objects |
+| XXX     | Row 3 | 3 Objects |
+| X       | Row 4 | 1 Object  |
+
+Opponents take turns removing objects until there are none left. The one who picks up the last object wins. The moves are made according to the following rules:
+1. On any given turn only objects from one row may be removed. There is no restriction on which row or on how many objects you remove. Of course, you cannot remove more than are in the row.
+2. You cannot skip a move or remove zero objects.
+
+The winning strategy can be mathematically defined, however, rather than presenting it here, we’d rather let you find it on your own. HINT: Play a few games with the computer and mark down on a piece of paper the number of objects in each stack (in binary!) after each move. Do you see a pattern emerging?
+
+This game of NIM is from Dartmouth College and allows you to specify any starting size for the four piles and also a win option. To play traditional NIM, you would simply specify 7,5,3 and 1, and win option 1.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=118)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=133)
+
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/66_Number/README.md b/66_Number/README.md
index 8eb2e9b5..47a9b5cb 100644
--- a/66_Number/README.md
+++ b/66_Number/README.md
@@ -1,7 +1,14 @@
 ### Number
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=121
+In contrast to other number guessing games where you keep guessing until you get the random number selected by the computer (GUESS, TRAP, STARS, etc.), in this game you only get one guess per play and you gain or lose points depending upon how close your guess is to the random number selected by the computer. You occasionally get a jackpot which will double your point count. You win when you get 500 points.
+
+Tom Adametx wrote this program while a student at Curtis Junior High School in Sudbury, Massachusetts.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=121)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=136)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/67_One_Check/README.md b/67_One_Check/README.md
index 0e703692..b3522e15 100644
--- a/67_One_Check/README.md
+++ b/67_One_Check/README.md
@@ -1,7 +1,29 @@
 ### One Check
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=122
+In this game or puzzle, 48 checkers are placed on the two outside spaces of a standard 64-square checkerboard as shown:
+
+|   |   |   |   |   |   |   |   |
+|---|---|---|---|---|---|---|---|
+| ● | ● | ● | ● | ● | ● | ● | ● |
+| ● | ● | ● | ● | ● | ● | ● | ● |
+| ● | ● |   |   |   |   | ● | ● |
+| ● | ● |   |   |   |   | ● | ● |
+| ● | ● |   |   |   |   | ● | ● |
+| ● | ● |   |   |   |   | ● | ● |
+| ● | ● | ● | ● | ● | ● | ● | ● |
+| ● | ● | ● | ● | ● | ● | ● | ● |
+
+The object is to remove as many checkers as possible by diagonal jumps (as in standard checkers).
+
+It is easy to remove 30 to 39 checkers, a challenge to remove 40 to 44, and a substantial feat to remove 45 to 47.
+
+The program was created and written by David Ahl.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=122)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=137)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/68_Orbit/README.md b/68_Orbit/README.md
index 3d7a04cb..df49852b 100644
--- a/68_Orbit/README.md
+++ b/68_Orbit/README.md
@@ -1,7 +1,57 @@
 ### Orbit
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=124
+ORBIT challenges you to visualize spacial positions in polar coordinates. The object is to detonate a Photon explosive within a certain distance of a germ laden Romulan spaceship. This ship is orbiting a planet at a constant altitude and orbital rate (degrees/hour). The location of the ship is hidden by a device that renders the ship invisible, but after each bomb you are told how close to the enemy ship your bomb exploded. The challenge is to hit an invisible moving target with a limited number of shots.
+
+The planet can be replaced by a point at its center (called the origin); then the ship’s position can be given as a distance form the origin and an angle between its position and the eastern edge of the planet.
+
+```
+direction
+of orbit    <       ^ ship
+              \     ╱
+                \  ╱ <
+                 |╱   \
+                 ╱      \
+                ╱         \
+               ╱           | angle
+              ╱           /
+             ╱          /
+            ╱         /
+           ╱——————————————————— E
+
+```
+
+The distance of the bomb from the ship is computed using the law of consines. The law of cosines states:
+
+```
+D = SQUAREROOT( R**2+D1**2+R*D1*COS(A-A1) )
+```
+
+Where D is the distance between the ship and the bomb, R is the altitude of the ship, D1 is the altitude of the bomb, and A-A1 is the angle between the ship and the bomb.
+
+
+```
+                 bomb  <
+                        ╲                   ^ ship
+                         ╲                  ╱
+                          ╲                ╱ <
+                           ╲              ╱   \
+                        D1  ╲            ╱      \
+                             ╲        R ╱         \
+                              ╲   A1   ╱           | A
+                               ╲⌄——— ◝╱           /
+                                ╲    ╱ \        /
+                                 ╲  ╱   \      /
+                                  ╲╱───────────────────── E
+
+```
+
+ORBIT was originally called SPACE WAR and was written by Jeff Lederer of Project SOLO Pittsburgh, Pennsylvania.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=124)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=139)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/69_Pizza/README.md b/69_Pizza/README.md
index fc83503a..671e7237 100644
--- a/69_Pizza/README.md
+++ b/69_Pizza/README.md
@@ -1,7 +1,16 @@
 ### Pizza
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=126
+In this game, you take orders for pizzas from people living in Hyattsville. Armed with a map of the city, you must then tell your delivery boy the address where the pizza is to be delivered. If the pizza is delivered to the correct address, the customer phones you and thanks you; if not, you must give the driver the correct address until the pizza gets delivered.
+
+Some interesting modifications suggest themselves for this program such as pizzas getting cold after two incorrect delivery attempts or taking three or more orders at a time and figuring out the shortest delivery route. Send us your modifications!
+
+This program seems to have surfaced originally at the University of Georgia in Athens, Georgia. The author is unknown.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=126)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=141)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/70_Poetry/README.md b/70_Poetry/README.md
index fe3c0ea8..b04bb915 100644
--- a/70_Poetry/README.md
+++ b/70_Poetry/README.md
@@ -1,7 +1,29 @@
 ### Poetry
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=128
+This program produces random verse which might loosely be considered in the Japanese Haiku style. It uses 20 phrases in four groups of five phrases each and generally cycles through the groups in order. It inserts commas (random — 19% of the time), indentation (random — 22% of the time), and starts new paragraphs (18% probability but at least once every 20 phrases).
+
+The phrases in POETRY are somewhat suggestive of Edgar Allen Poe. Try it with phrases from computer technology, from love and romance, from four-year-old children, or from some other project. Send us the output.
+
+Here are some phrases from nature to try:
+```
+Carpet of ferns     Mighty Oaks
+Morning dew         Grace and beauty
+Tang of dawn        Silently singing
+Swaying pines       Nature speaking
+
+Entrances me        Untouched, unspoiled
+Soothing me         Shades of green
+Rustling leaves     Tranquility
+Radiates calm       …so peaceful
+```
+
+The original author of this program is unknown. It was modified and reworked by Jim Bailey, Peggy Ewing, and Dave Ahl at DEC.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=128)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=143)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/71_Poker/README.md b/71_Poker/README.md
index b5f7bc83..1bad96d6 100644
--- a/71_Poker/README.md
+++ b/71_Poker/README.md
@@ -1,7 +1,16 @@
 ### Poker
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=129
+You and the computer are opponents in this game of draw poker. At the start of the game, each player is given $200. The game ends when either player runs out of money, although if you go broke the computer will offer to buy back your wristwatch or diamond tie tack.
+
+The computer opens the betting before the draw; you open the betting after the draw. If you don’t have a hand that’s worth anything and you want to fold, bet 0. Prior to the draw, to check the draw, you may bet .5. Of course, if the computer has made a bet, you must match it in order to draw or, if you have a good hand, you may raise the bet at any time.
+
+The author is A. Christopher Hall of Trinity College, Hartford, Connecticut.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=129)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=144)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/72_Queen/README.md b/72_Queen/README.md
index 44ec90bb..dc62cb59 100644
--- a/72_Queen/README.md
+++ b/72_Queen/README.md
@@ -1,7 +1,16 @@
 ### Queen
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=133
+This game is based on the permissible moves of the chess queen — i.e., along any vertical, horizontal, or diagonal. In this game, the queen can only move to the left, down, and diagonally down to the left.
+
+The object of the game is to place the queen (one only) in the lower left-hand square (no. 158), by alternating moves between you and the computer. The one to place the queen there wins.
+
+You go first and place the queen in any one of the squares on the top row or the right-hand column. That is your first move. The computer is beatable, but it takes some figuring. See if you can devise a winning strategy.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=133)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=148)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/73_Reverse/README.md b/73_Reverse/README.md
index 68d83b47..a753e8a8 100644
--- a/73_Reverse/README.md
+++ b/73_Reverse/README.md
@@ -1,7 +1,31 @@
 ### Reverse
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=135
+The game of REVERSE requires you to arrange a list of numbers in numerical order from left to right. To move, you tell the computer how many numbers (counting from the left) to reverse. For example, if the current list is:
+```
+    2 3 4 5 1 6 7 8 9
+```
+
+and you reverse 4, the result will be:
+```
+    5 4 3 2 1 6 7 8 9
+```
+Now if you reverse 5, you win!
+
+There are many ways to beat the game, but approaches tend to be either algorithmic or heuristic. The game thus offers the player a chance to play with these concepts in a practical (rather than theoretical) context.
+
+An algorithmic approach guarantees a solution in a predictable number of moves, given the number of items in the list. For example, one method guarantees a solution in 2N - 3 moves when teh list contains N numbers. The essence of an algorithmic approach is that you know in advance what your next move will be. Once could easily program a computer to do this.
+
+A heuristic approach takes advantage of “partial orderings” in the list at any moment. Using this type of approach, your next move is dependent on the way the list currently appears. This way of solving the problem does not guarantee a solution in a predictable number of moves, but if you are lucky and clever, you may come out ahead of the algorithmic solutions. One could not so easily program this method.
+
+In practice, many players adopt a “mixed” strategy, with both algorithmic and heuristic features. Is this better than either “pure” strategy?
+
+The program was created by Peter Sessions of People’s Computer Company and the notes above adapted from his original write-up.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=135)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=150)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/73_Reverse/perl/reverse.pl b/73_Reverse/perl/reverse.pl
new file mode 100755
index 00000000..94ef40e6
--- /dev/null
+++ b/73_Reverse/perl/reverse.pl
@@ -0,0 +1,234 @@
+#!/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 List::Util qw{ shuffle };   # Shuffle an array.
+use Term::ReadLine;     # Prompt and return user input
+
+our $VERSION = '0.000_01';
+
+# Manifest constant for size of list.
+use constant NUMBER_OF_NUMBERS  => 9;
+
+print <<'EOD';
+                                REVERSE
+               Creative Computing  Morristown, New Jersey
+
+
+
+Reverse -- a game of skill
+
+EOD
+
+# Display the rules if desired. There is no straightforward way to
+# interpolate a manifest constant into a string, but @{[ ... ]} will
+# interpolate any expression.
+print <<"EOD" if get_yes_no( 'Do you want the rules' );
+
+This is the game of 'Reverse'.  To win, all you have
+to do is arrange a list of numbers (1 through @{[ NUMBER_OF_NUMBERS ]})
+in numerical order from left to right.  To move, you
+tell me how many numbers (counting from the left) to
+reverse.  For example, if the current list is:
+
+2 3 4 5 1 6 7 8 9
+
+and you reverse 4, the result will be:
+
+5 4 3 2 1 6 7 8 9
+
+Now if you reverse 5, you win!
+
+1 2 3 4 5 6 7 8 9
+
+No doubt you will like this game, but
+if you want to quit, reverse 0 (zero).
+
+EOD
+
+while ( 1 ) {   # Iterate until something interrupts us.
+
+    # Populate the list with the integers from 1, shuffled. If we
+    # accidentally generate a winning list, just redo the loop.
+    my @list = shuffle( 1 .. NUMBER_OF_NUMBERS );
+    redo if is_win( \@list );
+
+    print <<"EOD";
+
+Here we go ... The list is:
+EOD
+
+    my $moves = 0;  # Move counter
+
+    while ( 1 ) {   # Iterate until something interrupts us.
+        print <<"EOD";
+
+@list
+
+EOD
+
+        # Read the number of values to reverse. Zero is special-cased to
+        # take us out of this loop.
+        last unless my $max_index = get_input(
+            'How many shall I reverse (0 to quit)? ',
+            sub {
+                return m/ \A [0-9]+ \z /smx &&
+                    $ARG <= NUMBER_OF_NUMBERS;
+            },
+            "Oops! Too many! I can reverse at most " .
+                NUMBER_OF_NUMBERS,
+        );
+
+        --$max_index;   # Convert number to reverse to upper index
+
+        # Use a Perl array slice and the reverse() built-in to reverse
+        # the beginning of the list.
+        @list[ 0 .. $max_index ] = reverse @list[ 0 .. $max_index ];
+
+        $moves++;   # Count a move
+
+        # If we have not won, iterate again.
+        next unless is_win( \@list );
+
+        # Announce the win, and drop out of the loop.
+        print <<"EOD";
+
+You won it in $moves moves!!!
+EOD
+        last;
+    }
+
+    # Drop out of this loop unless the player wants to play again.
+    say '';
+    last unless get_yes_no( 'Try again' );
+}
+
+print <<'EOD';
+
+O.K. Hope you had fun!!
+EOD
+
+# Get input from the user. The arguments are:
+# * The prompt
+# * A reference to validation code. This code receives the response in
+#   $ARG and returns true for a valid response.
+# * A warning to print if the response is not valid. This must end in a
+#   return.
+# The first valid response is returned. An end-of-file terminates the
+# script.
+sub get_input {
+    my ( $prompt, $validate, $warning ) = @ARG;
+
+    # If no validator is passed, default to one that always returns
+    # true.
+    $validate ||= sub { 1 };
+
+    # Create the readline object. The 'state' causes the variable to be
+    # initialized only once, no matter how many times this subroutine is
+    # called. The do { ... } is a compound statement used because we
+    # need to tweak the created object before we store it.
+    state $term = do {
+        my $obj = Term::ReadLine->new( 'reverse' );
+        $obj->ornaments( 0 );
+        $obj;
+    };
+
+    while ( 1 ) {   # Iterate indefinitely
+
+        # Read the input into the topic variable, localized to prevent
+        # Spooky Action at a Distance. We exit on undef, which signals
+        # end-of-file.
+        exit unless defined( local $ARG = $term->readline( $prompt ) );
+
+        # Return the input if it is valid.
+        return $ARG if $validate->();
+
+        # Issue the warning, and go around the merry-go-round again.
+        warn $warning;
+    }
+}
+
+# Get a yes-or-no answer. The argument is the prompt, which will have
+# '? [y/n]: ' appended. The donkey work is done by get_input(), which is
+# requested to validate the response as beginning with 'y' or 'n',
+# case-insensitive. The return is a true value for 'y' and a false value
+# for 'n'.
+sub get_yes_no {
+    my ( $prompt ) = @ARG;
+    state $map_answer = {
+        n   => 0,
+        y   => 1,
+    };
+    my $resp = lc get_input(
+        "$prompt? [y/n]: ",
+        sub { m/ \A [yn] /smxi },
+        "Please respond 'y' or 'n'\n",
+    );
+    return $map_answer->{ substr $resp, 0, 1 };
+}
+
+# Determine if a given list represents a win. The argument is a
+# reference to the array containing the list. We return a true value for
+# a win, or a false value otherwise.
+sub is_win {
+    my ( $list ) = @_;
+    my $expect = 1; # We expect the first element to be 1;
+
+    # Iterate over the array.
+    foreach my $element ( @{ $list } ) {
+
+        # If the element does not have the expected value, we return
+        # false. We post-increment the expected value en passant.
+        $element == $expect++
+            or return 0;
+    }
+
+    # All elements had the expected value, so we won. Return a true
+    # value.
+    return 1;
+}
+
+__END__
+
+=head1 TITLE
+
+reverse.pl - Play the game 'reverse' from Basic Computer Games
+
+=head1 SYNOPSIS
+
+ reverse.pl
+
+=head1 DETAILS
+
+This Perl script is a port of C, which is the 73rd entry in
+Basic Computer Games.
+
+The cool thing about this port is the fact that, in a language with
+array slices, list assignments, and a C built-in, the
+reversal is a single assignment statement.
+
+=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/74_Rock_Scissors_Paper/README.md b/74_Rock_Scissors_Paper/README.md
index 602dd15b..1b9fa865 100644
--- a/74_Rock_Scissors_Paper/README.md
+++ b/74_Rock_Scissors_Paper/README.md
@@ -1,7 +1,21 @@
 ### Rock, Scissors, Paper
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=137
+Remember the game of rock-scissors-paper. You and your opponent make a motion three times with your fists and then either show:
+- a flat hand (paper)
+- fist (rock)
+- two fingers (scissors)
+
+Depending upon what is shown, the game is a tie (both show the same) or one person wins. Paper wraps up rock, so it wins. Scissors cut paper, so they win. And rock breaks scissors, so it wins.
+
+In this computerized version of rock-scissors-paper, you can play up to ten games vs. the computer.
+
+Charles Lund wrote this game while at the American School in The Hague, Netherlands.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=137)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=152)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/74_Rock_Scissors_Paper/ruby/rockscissors.rb b/74_Rock_Scissors_Paper/ruby/rockscissors.rb
new file mode 100644
index 00000000..9b24ac9a
--- /dev/null
+++ b/74_Rock_Scissors_Paper/ruby/rockscissors.rb
@@ -0,0 +1,80 @@
+SCREEN_WIDTH = 72
+
+MOVE_WORDS = {
+  1 => 'PAPER',
+  2 => 'SCISSORS',
+  3 => 'ROCK'
+}
+
+WIN_TABLE = {
+  1 => 3,
+  2 => 1,
+  3 => 2
+}
+
+def center_text(text)
+  text.rjust((SCREEN_WIDTH / 2) + (text.size / 2))
+end
+
+def ask_for_number_of_games
+  loop do
+    puts "HOW MANY GAMES" 
+    response = STDIN.gets.to_i
+    return response if response > 0 and response < 11
+    puts "SORRY, BUT WE AREN'T ALLOWED TO PLAY THAT MANY."
+  end
+end
+
+def ask_for_human_move
+  loop do
+    puts "3=ROCK...2=SCISSORS...1=PAPER"
+    puts "1...2...3...WHAT'S YOUR CHOICE"
+    response = STDIN.gets.to_i
+    return response if [1,2,3].include?(response)
+    puts "INVALID"
+  end
+end
+
+def calculate_result(human_move, computer_move)
+  return 'TIE' if human_move == computer_move
+  return 'WIN' if WIN_TABLE[human_move] == computer_move
+  'LOSE'
+end
+
+puts center_text('GAME OF ROCK, SCISSORS, PAPER')
+puts center_text('CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY')
+puts
+puts
+puts
+
+number_of_games = ask_for_number_of_games
+games_won = 0
+games_lost = 0
+
+number_of_games.times do |game_number|
+  puts
+  puts "GAME NUMBER #{game_number + 1}"
+  computer_move = rand(3) + 1
+  human_move = ask_for_human_move
+  puts "THIS IS MY CHOICE..."
+  puts "...#{MOVE_WORDS[computer_move]}"
+  
+  case calculate_result(human_move, computer_move)
+  when 'WIN'
+    puts "YOU WIN!!!"
+    games_won += 1
+  when 'TIE'
+    puts "TIE GAME.  NO WINNER."
+  when 'LOSE'
+    puts "WOW!  I WIN!!!"
+    games_lost = games_lost += 1
+  end
+end
+
+puts
+puts "HERE IS THE FINAL GAME SCORE:"
+puts "I HAVE WON #{games_lost} GAME(S)."
+puts "YOU HAVE WON #{games_won} GAME(S)."
+puts "AND #{number_of_games - (games_lost + games_won)} GAME(S) ENDED IN A TIE."
+puts "THANKS FOR PLAYING!!"
+
diff --git a/75_Roulette/README.md b/75_Roulette/README.md
index f652ee9e..51719248 100644
--- a/75_Roulette/README.md
+++ b/75_Roulette/README.md
@@ -1,7 +1,16 @@
 ### Roulette
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=138
+This game simulates an American Roulette wheel; “American” because it has 38 number compartments (1 to 36, 0 and 00). The European wheel has 37 numbers (1 to 36 and 0). The Bahamas, Puerto Rico, and South American countries are slowly switching to the American wheel because it gives the house a bigger percentage. Odd and even numbers alternate around the wheel, as do red and black. The layout of the wheel insures a highly random number pattern. In fact, roulette wheels are sometimes used to generate tables of random numbers.
+
+In this game, you may bet from $5 to $500 and you may bet on red or black, odd or even, first or second 18 numbers, a column, or single number. You may place any number of bets on each spin of the wheel.
+
+There is no long-range winning strategy for playing roulette. However, a good strategy is that of “doubling.” First spin, bet $1 on an even/odds bet (odd, even, red, or black). If you lose, double your bet again to $2. If you lose again, double to $4. Continue to double until you win (i.e, you break even on a losing sequence). As soon as you win, bet $1 again, and after every win, bet $1. Do not ever bet more than $1 unless you are recuperating losses by doubling. Do not ever bet anything but the even odds bets. Good luck!
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=138)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=153)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/76_Russian_Roulette/README.md b/76_Russian_Roulette/README.md
index 3385adc8..cf1b64fc 100644
--- a/76_Russian_Roulette/README.md
+++ b/76_Russian_Roulette/README.md
@@ -1,7 +1,14 @@
 ### Russian Roulette
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=141
+In this game, you are given by the computer a revolver loaded with one bullet and five empty chambers. You spin the chamber and pull the trigger by inputting a “1,” or, if you want to quit, input a “2.” You win if you play ten times and are still alive.
+
+Tom Adametx wrote this program while a student at Curtis Jr. High School in Sudbury, Massachusetts.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=141)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=153)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/77_Salvo/README.md b/77_Salvo/README.md
index 82f8a5e2..fbf938ca 100644
--- a/77_Salvo/README.md
+++ b/77_Salvo/README.md
@@ -1,7 +1,25 @@
 ### Salvo
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=142
+The rules are _not_ explained by the program, so read carefully this description by Larry Siegel, the program author.
+
+SALVO is played on a 10x10 grid or board using an x,y coordinate system. The player has 4 ships:
+- battleship (5 squares)
+- cruiser (3 squares)
+- two destroyers (2 squares each)
+
+The ships may be placed horizontally, vertically, or diagonally and must not overlap. The ships do not move during the game.
+
+As long as any square of a battleship still survives, the player is allowed three shots, for a cruiser 2 shots, and for each destroyer 1 shot. Thus, at the beginning of the game the player has 3+2+1+1=7 shots. The players enters all of his shots and the computer tells what was hit. A shot is entered by its grid coordinates, x,y. The winner is the one who sinks all of the opponents ships.
+
+Important note: Your ships are located and the computer’s ships are located on 2 _separate_ 10x10 boards.
+
+Author of the program is Lawrence Siegel of Shaker Heights, Ohio.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=142)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=157)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/78_Sine_Wave/README.md b/78_Sine_Wave/README.md
index f78ccccf..c38234a2 100644
--- a/78_Sine_Wave/README.md
+++ b/78_Sine_Wave/README.md
@@ -1,7 +1,12 @@
 ### Sine Wave
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=146
+Did you ever go to a computer show and see a bunch of CRT terminals just sitting there waiting forlornly for someone to give a demo on them. It was one of those moments when I was at DEC that I decided there should be a little bit of background activity. And why not plot with words instead of the usual X’s? Thus SINE WAVE was born and lives on in dozens on different versions. At least those CRTs don’t look so lifeless anymore.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=146)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=161)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/78_Sine_Wave/java/src/SineWave.java b/78_Sine_Wave/java/src/SineWave.java
index f3b3c7eb..0908791e 100644
--- a/78_Sine_Wave/java/src/SineWave.java
+++ b/78_Sine_Wave/java/src/SineWave.java
@@ -1,5 +1,3 @@
-import java.util.Arrays;
-
 /**
  * Sine Wave
  *
@@ -12,24 +10,19 @@ import java.util.Arrays;
 public class SineWave {
 
     public static void main(String[] args) {
-
-        System.out.println("SINE WAVE");
-        System.out.println("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
-        System.out.println();
-
-        int toggle = 0;
-        for(double t = 0; t<40; t += .25) {
-            int a = 26 + (int) (25 * Math.sin(t));
-            char[] repeat = new char[a];
-            Arrays.fill(repeat,' ');
-            System.out.print(new String(repeat));
-            if (toggle == 1) {
-                System.out.println("COMPUTING");
-                toggle = 0;
-            } else {
-                System.out.println("CREATIVE");
-                toggle = 1;
-            }
+        System.out.println("""
+           SINE WAVE
+           CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY
+           """);
+        var isCreative = true;
+        for(var t = 0d; t<40; t += .25) {
+            //Indent output
+            var indentations = 26 + (int) (25 * Math.sin(t));
+            System.out.print(" ".repeat(indentations));
+            //Change output every iteration
+            var word = isCreative ? "CREATIVE" : "COMPUTING";
+            System.out.println(word);
+            isCreative = !isCreative ;
         }
     }
 }
diff --git a/79_Slalom/README.md b/79_Slalom/README.md
index 49935ea1..7e46e3a5 100644
--- a/79_Slalom/README.md
+++ b/79_Slalom/README.md
@@ -1,7 +1,16 @@
 ### Slalom
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=147
+This game simulates a slalom run down a course with one to 25 gates. The user picks the number of gates and has some control over his speed down the course.
+
+If you’re not a skier, here’s your golden opportunity to try it with minimal risk. If you are a skier, here’s something to do while your leg is in a cast.
+
+SLALOM was written by J. Panek while a student at Dartmouth College.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=147)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=162)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/80_Slots/README.md b/80_Slots/README.md
index 1b4a1987..952fa03e 100644
--- a/80_Slots/README.md
+++ b/80_Slots/README.md
@@ -1,7 +1,18 @@
 ### Slots
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=149
+The slot machine or one-arm bandit is a mechanical device that will absorb coins just about as fast as you can feed it. After inserting a coin, you pull a handle that sets three independent reels spinning. If the reels stop with certain symbols appearing in the pay line, you get a certain payoff. The original slot machine, called the Liberty Bell, was invented in 1895 by Charles Fey in San Francisco. Fey refused to sell or lease the manufacturing rights, so H.S. Mills in Chicago built a similar, but much improved machine called the Operators Bell. This has survived nearly unchanged to today.
+
+On the Operators Bell and other standard slot machines, there are 20 symbols on each wheel but they are not distributed evenly among the objects (cherries, bar, apples, etc.). Of the 8,000 passible combinations, the expected payoff (to the player) is 7,049 or $89.11 for every $100.00 put in, one of the lowest expected payoffs in all casino games.
+
+In the program here, the payoff is considerably more liberal; indeed it appears to favor the player by 11% — i.e., an expected payoff of $111 for each $100 bet.
+
+The program was originally written by Fred Mirabella and Bob Harper.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=149)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=164)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
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/81_Splat/README.md b/81_Splat/README.md
index 4b4161ef..24631217 100644
--- a/81_Splat/README.md
+++ b/81_Splat/README.md
@@ -1,7 +1,16 @@
 ### Splat
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=151
+SPLAT simulates a parachute jump in which you try to open your parachute at the last possible moment without going splat! You may select your own terminal velocity or let the computer do it for you. You many also select the acceleration due to gravity or, again, let the computer do it in which case you might wind up on any of eight planets (out to Neptune), the moon, or the sun.
+
+The computer then tells you the height you’re jumping from and asks for the seconds of free fall. It then divides your free fall time into eight intervals and gives you progress reports on your way down. The computer also keeps track of all prior jumps in the array A and lets you know how you compared with previous successful jumps. If you want to recall information from previous runs, then you should store array A in a disk or take file and read it before each run.
+
+John Yegge created this program while at the Oak Ridge Associated Universities.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=151)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=166)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/82_Stars/README.md b/82_Stars/README.md
index cc6c3171..ff067975 100644
--- a/82_Stars/README.md
+++ b/82_Stars/README.md
@@ -1,7 +1,16 @@
 ### Stars
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=153
+In this game, the computer selects a random number from 1 to 100 (or any value you set). You try to guess the number and the computer gives you clues to tell you how close you’re getting. One star (\*) means you’re far away from the number; seven stars (\*\*\*\*\*\*\*) means you’re really close. You get 7 guesses.
+
+On the surface this game is similar to GUESS; however, the guessing strategy is quite different. See if you can come up with one or more approaches to finding the mystery number.
+
+Bob Albrecht of People’s Computer Company created this game.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=153)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=166)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
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/83_Stock_Market/README.md b/83_Stock_Market/README.md
index 6b963c2f..65447e1f 100644
--- a/83_Stock_Market/README.md
+++ b/83_Stock_Market/README.md
@@ -1,7 +1,16 @@
 ### Stock Market
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=154
+This program “plays” the stock market. You will be given $10,000 and may buy or sell stocks. Stock prices and trends are generated randomly; therefore, this model does not represent exactly what happens on the exchange. (Depending upon your point of view, you may feel this is quite a good representation!)
+
+Every trading day, a table of stocks, their prices, and number of shares in your portfolio is printed. Following this, the initials of each stock are printed followed by a question mark. You indicate your transaction in number of shares — a positive number to buy, negative to sell, or 0 to do no trading. A brokerage fee of 1% is charges on all transactions (a bargain!). Note: Even if the value of a stock drops to zero, it may rebound again — then again, it may not.
+
+This program was created by D. Pessel, L. Braun, and C. Losik of the Huntington Computer Project at SUNY, Stony Brook, N.Y.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=154)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=166)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/84_Super_Star_Trek/README.md b/84_Super_Star_Trek/README.md
index 495ce9ae..4a96f528 100644
--- a/84_Super_Star_Trek/README.md
+++ b/84_Super_Star_Trek/README.md
@@ -1,7 +1,98 @@
 ### Super Star Trek
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=157
+#### Brief History
+Many versions of Star Trek have been kicking around various college campuses since the late sixties. I recall playing one at Carnegie-Mellon Univ. in 1967 or 68, and a very different one at Berkeley. However, these were a far cry from the one written by Mike Mayfield of Centerline Engineering and/or Custom Data. This was written for an HP2000C and completed in October 1972. It became the “standard” Star Trek in February 1973 when it was put in the HP contributed program library and onto a number of HP Data Center machines.
+
+In the summer of 1973, I converted the HP version to BASIC-PLUS for DEC’s RSTS-11 compiler and added a few bits and pieces while I was at it. Mary Cole at DEC contributed enormously to this task too. Later that year I published it under the name SPACWE (Space War — in retrospect, an incorrect name) in my book _101 Basic Computer Games_.It is difficult today to find an interactive computer installation that does not have one of these versions of Star Trek available.
+
+#### Quadrant Nomenclature
+Recently, certain critics have professed confusion as to the origin on the “quadrant” nomenclature used on all standard CG (Cartesian Galactic) maps. Naturally, for anyone with the remotest knowledge of history, no explanation is necessary; however, the following synopsis should suffice for the critics:
+
+As everybody schoolboy knows, most of the intelligent civilizations in the Milky Way had originated galactic designations of their own choosing well before the Third Magellanic Conference, at which the so-called “2⁶ Agreement” was reached. In that historic document, the participant cultures agreed, in all two-dimensional representations of the galaxy, to specify 64 major subdivisions, ordered as an 8 x 8 matrix. This was partially in deference to the Earth culture (which had done much in the initial organization of the Federation), whose century-old galactic maps had landmarks divided into four “quadrants,” designated by ancient “Roman Numerals” (the origin of which has been lost).
+
+To this day, the official logs of starships originating on near-Earth starbases still refer to the major galactic areas as “quadrants.”
+
+The relation between the Historical and Standard nomenclatures is shown in the simplified CG map below.
+
+|   | 1            | 2  | 3   | 4  | 5          | 6  | 7   | 8  |
+|---|--------------|----|-----|----|------------|----|-----|----|
+| 1 |    ANTARES   |    |     |    |   SIRIUS   |    |     |    |
+|   | I            | II | III | IV | I          |    | III | IV |
+| 2 |     RIGEL    |    |     |    |    DENEB   |    |     |    |
+|   | I            | II | III | IV | I          | II | III | IV |
+| 3 |    PROCYON   |    |     |    |   CAPELLA  |    |     |    |
+|   | I            | II | III | IV | I          | II | III | IV |
+| 4 | VEGA         |    |     |    | BETELGUESE |    |     |    |
+|   | I            | II | III | IV | I          | II | III | IV |
+| 5 |    CANOPUS   |    |     |    |  ALDEBARA  |    |     |    |
+|   | I            | II | III | IV | I          | II | III | IV |
+| 6 |    ALTAIR    |    |     |    |   REGULUS  |    |     |    |
+|   | I            | II | III | IV | I          | II | III | IV |
+| 7 | SAGITTARIOUS |    |     |    |  ARCTURUS  |    |     |    |
+|   | I            | II | III | IV | I          | II | III | IV |
+| 8 |    POLLUX    |    |     |    |    SPICA   |    |     |    |
+|   | I            | II | III | IV | I          | II | III | IV |
+
+#### Super Star Trek† Rules and Notes
+1. OBJECTIVE: You are Captain of the starship “Enterprise”† with a mission to seek and destroy a fleet of Klingon† warships (usually about 17) which are menacing the United Federation of Planets.† You have a specified number of stardates in which to complete your mission. You also have two or three Federation Starbases† for resupplying your ship.
+
+2. You will be assigned a starting position somewhere in the galaxy. The galaxy is divided into an 8 x 8 quadrant grid. The astronomical name of a quadrant is called out upon entry into a new region. (See “Quadrant Nomenclature.”) Each quadrant is further divided into an 8 x 8 section grid.
+
+3. On a section diagram, the following symbols are used:
+    - `<*>` Enterprise
+    - `†††` Klingon
+    - `>!<` Starbase
+    - `*`   Star
+
+4. You have eight commands available to you (A detailed description of each command is given in the program instructions.)
+    - `NAV` Navigate the Starship by setting course and warp engine speed.
+    - `SRS` Short-range sensor scan (one quadrant)
+    - `LRS` Long-range sensor scan (9 quadrants)
+    - `PHA` Phaser† control (energy gun)
+    - `TOR` Photon torpedo control
+    - `SHE` Shield control (protects against phaser fire)
+    - `DAM` Damage and state-of-repair report
+    - `COM` Call library computer
+
+5. Library computer options are as follows (more complete descriptions are in program instructions):
+    - `0` Cumulative galactic report
+    - `1` Status report
+    - `2` Photon torpedo course data
+    - `3` Starbase navigation data
+    - `4` Direction/distance calculator
+    - `5` Quadrant nomenclature map
+
+6. Certain reports on the ship’s status are made by officers of the Enterprise who appears on the original TV Show—Spock,† Scott,† Uhura,† Chekov,† etc.
+
+7. Klingons are non-stationary within their quadrants. If you try to maneuver on them, they will move and fire on you.
+
+8. Firing and damage notes:
+    - Phaser fire diminishes with increased distance between combatants.
+    - If a Klingon zaps you hard enough (relative to your shield strength) he will generally cause damage to some part of your ship with an appropriate “Damage Control” report resulting.
+    - If you don’t zap a Klingon hard enough (relative to his shield strength) you won’t damage him at all. Your sensors will tell the story.
+    - Damage control will let you know when out-of-commission devices have been completely repaired.
+
+9. Your engines will automatically shit down if you should attempt to leave the galaxy, or if you should try to maneuver through a star, or Starbase, or—heaven help you—a Klingon warship.
+
+10. In a pinch, or if you should miscalculate slightly, some shield control energy will be automatically diverted to warp engine control (if your shield are operational!).
+
+11. While you’re docked at a Starbase, a team of technicians can repair your ship (if you’re willing for them to spend the time required—and the repairmen _always_ underestimate…)
+
+12. If, to same maneuvering time toward the end of the game, you should cold-bloodedly destroy a Starbase, you get a nasty note from Starfleet Command. If you destroy your _last_ Starbase, you lose the game! (For those who think this is too a harsh penalty, delete line 5360-5390, and you’ll just get a “you dumdum!”-type message on all future status reports.)
+
+13. End game logic has been “cleaned up” in several spots, and it is possible to get a new command after successfully completing your mission (or, after resigning your old one).
+
+14. For those of you with certain types of CRT/keyboards setups (e.g. Westinghouse 1600), a “bell” character is inserted at appropriate spots to cause the following items to flash on and off on the screen:
+    - The Phrase “\*RED\*” (as in Condition: Red)
+    - The character representing your present quadrant in the cumulative galactic record printout.
+
+15. This version of Star Trek was created for a Data General Nova 800 system with 32K or core. So that it would fit, the instructions are separated from the main program via a CHAIN. For conversion to DEC BASIC-PLUS, Statement 160 (Randomize) should be moved after the return from the chained instructions, say to Statement 245. For Altair BASIC, Randomize and the chain instructions should be eliminated.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=157)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=166)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
@@ -11,4 +102,3 @@ instructions.txt
 
 #### External Links
  - Super Star Trek in C++ : https://www.codeproject.com/Articles/28399/The-Object-Oriented-Text-Star-Trek-Game-in-C
-
diff --git a/85_Synonym/README.md b/85_Synonym/README.md
index b9a5b79f..66c3c4c3 100644
--- a/85_Synonym/README.md
+++ b/85_Synonym/README.md
@@ -1,7 +1,20 @@
 ### Synonym
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=164
+A synonym of a word is another word (in the English language) which has the same, or very nearly the same, meaning. This program tests your knowledge of synonyms of a few common words.
+
+The computer chooses a word and asks you for a synonym. The computer then tells you whether you’re right or wrong. If you can’t think of a synonym, type “HELP” which causes a synonym to be printed.
+
+You may put in words of your choice in the data statements. The number following DATA in Statement 500 is the total number of data statements. In each data statement, the first number is the number of words in that statement.
+
+Can you think of a way to make this into a more general kind of CAI program for any subject?
+
+Walt Koetke of Lexington High School, Massachusetts created this program.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=164)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=179)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
@@ -14,4 +27,4 @@ http://www.vintage-basic.net/games.html
    all of the help.
 
  - The player can ask for HELP and then submit that answer. Is it
-   meant to be a clue, or just giving a correct answer to the player?
\ No newline at end of file
+   meant to be a clue, or just giving a correct answer to the player?
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/86_Target/README.md b/86_Target/README.md
index ca5f9177..29e14e64 100644
--- a/86_Target/README.md
+++ b/86_Target/README.md
@@ -1,7 +1,16 @@
 ### Target
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=165
+In this program, you are firing a weapon from a spaceship in 3-dimensional space. Your ship, the Starship Enterprise, is located at the origin (0,0,0) of a set of x,y,z coordinates. You will be told the approximate location of the target in 3-dimensional rectangular coordinates, the approximate angular deviation from the x and z axes in both radians and degrees, and the approximate distance to the target.
+
+Given this information, you then proceed to shoot at the target. A shot within 20 kilometers of the target destroys it. After each shot, you are given information as to the position of the explosion of your shot and a somewhat improved estimate of the location of the target. Fortunately, this is just practice and the target doesn’t shoot back. After you have attained proficiency, you ought to be able to destroy a target in 3 or 4 shots. However, attaining proficiency might take a while!
+
+The author is H. David Crockett of Fort Worth, Texas.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=165)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=180)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/87_3-D_Plot/README.md b/87_3-D_Plot/README.md
index e7cea9b4..3361976a 100644
--- a/87_3-D_Plot/README.md
+++ b/87_3-D_Plot/README.md
@@ -1,7 +1,16 @@
 ### 3-D Plot
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=167
+3-D PLOT will plot the family of curves of any function. The function Z is plotted as “rising” out of the x-y plane with x and y inside a circle of radius 30. The resultant plot looks almost 3-dimensional.
+
+You set the function you want plotted in line 5. As with any mathematical plot, some functions come out “prettier” than others.
+
+The author of this amazingly clever program is Mark Bramhall of DEC.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=167)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=182)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/88_3-D_Tic-Tac-Toe/README.md b/88_3-D_Tic-Tac-Toe/README.md
index e06c4e40..df9cabf4 100644
--- a/88_3-D_Tic-Tac-Toe/README.md
+++ b/88_3-D_Tic-Tac-Toe/README.md
@@ -1,7 +1,17 @@
 ### 3-D Tic-Tac-Toe
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=168
+3-D TIC-TAC-TOE is a game of tic-tac-toe in a 4x4x4 cube. You must get 4 markers in a row or diagonal along any 3-dimensional plane in order to win.
+
+Each move is indicated by a 3-digit number (digits not separated by commas), with each digit between 1 and 4 inclusive. The digits indicate the level, column, and row, respectively, of the move. You can win if you play correctly; although, it is considerably more difficult than standard, two-dimensional 3x3 tic-tac-toe.
+
+This version of 3-D TIC-TAC-TOE is from Dartmouth College.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=168)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=183)
+
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/89_Tic-Tac-Toe/README.md b/89_Tic-Tac-Toe/README.md
index b4d04d06..311322fa 100644
--- a/89_Tic-Tac-Toe/README.md
+++ b/89_Tic-Tac-Toe/README.md
@@ -1,7 +1,25 @@
 ### Tic-Tac-Toe
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=171
+The game of tic-tac-toe hardly needs any introduction. In this one, you play versus the computer. Moves are entered by number:
+```
+1   2   3
+
+4   5   6
+
+7   8   9
+```
+
+If you make any bad moves, the computer will win; if the computer makes a bad move, you can win; otherwise, the game ends in a tie.
+
+A second version of the game is included which prints out the board after each move. This is ideally suited to a CRT terminal, particularly if you modify it to not print out a new board after each move, but rather use the cursor to make the move.
+
+The first program was written by Tom Koos while a student researcher at the Oregon Museum of Science and Industry; it was extensively modified by Steve North of Creative Computing. The author of the second game is Curt Flick of Akron, Ohio.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=171)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=186)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
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/README.md b/90_Tower/README.md
index 6d452f7f..cbd05830 100644
--- a/90_Tower/README.md
+++ b/90_Tower/README.md
@@ -1,7 +1,20 @@
 ### Tower
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=173
+This is a simulation of a game of logic that originated in the middle East. It is sometimes called Pharaoh's Needles, but its most common name is the Towers of Hanoi.
+
+Legend has it that a secret society of monks live beneath the city of Hanoi. They possess three large towers or needles on which different size gold disks may be placed. Moving one at a time and never placing a large on a smaller disk, the monks endeavor to move the tower of disks from the left needle to the right needle. Legend says when they have finished moving this 64-disk tower, the world will end. How many moves will they have to make to accomplish this? If they can move 1 disk per minute and work 24 hours per day, how many years will it take?
+
+In the computer puzzle you are faced with three upright needles. On the leftmost needle are placed from two to seven graduated disks, the largest being on bottom and smallest on top. Your object is to move the entire stack of disks to the rightmost needle. However, you many only move one disk at a time and you may never place a larger disk on top of a smaller one.
+
+In this computer game, the disks are referred to by their size — i.e., the smallest is 3, next 5, 7, 9, 11, 13, and 15. If you play with fewer than 7 disks always use the largest, i.e. with 2 disks you would use nos. 13 and 15. The program instructions are self-explanatory. Good luck!
+
+Charles Lund wrote this program while at the American School in the Hague, Netherlands.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=173)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=188)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
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/perl/tower.pl b/90_Tower/perl/tower.pl
new file mode 100755
index 00000000..f9258eea
--- /dev/null
+++ b/90_Tower/perl/tower.pl
@@ -0,0 +1,381 @@
+#!/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';
+
+# Manifest constant representing the maximum number of disks. We can
+# change this within limits. It needs to be at least 3 or the
+# explanatory text will contain negative numbers. There is no known
+# upper limit, though if it is more than 10 the output lines can be more
+# than 80 columns, and if it is more than 49 the disk numbers will be
+# more than two digits, which will cause output lines not to align
+# properly.
+use constant MAX_DISKS  => 7;
+
+print <<'EOD';
+                                 TOWERS
+               Creative Computing  Morristown, New Jersey
+
+
+EOD
+
+while ( 1 ) {   # Iterate until something makes us stop.
+
+    print <<'EOD';
+Towers of Hanoi Puzzle.
+
+You must transfer the disks from the left to the right
+Tower, one at a time, never putting a larger disk on a
+smaller disk.
+
+EOD
+
+    # Get the desired number of disks to work with.
+    my $size = get_input(
+
+        # Manifest constants do not interpolate into strings. This can
+        # be worked around using the @{[ ... ]} construction, which
+        # interpolates any expression.
+        "How many disks do you want to move (@{[ MAX_DISKS ]} is max)? ",
+
+        sub {
+
+            # Accept any response which is an integer greater than zero
+            # and less than or equal to the maximum number of disks.
+            # NOTE that 'and' performs the same operation as &&, but
+            # binds much more loosely.
+            return m/ \A [0-9]+ \z /smx &&
+                $ARG > 0 &&
+                $ARG <= MAX_DISKS;
+        },
+
+        3,
+        "Sorry, but I can't do that job for you.\n",    # Warning
+        <<'EOD',
+All right, wise guy, if you can't play the game right, I'll
+just take my puzzle and go home.  So long.
+EOD
+    );
+
+    # Expressions can be interpolated using @{[ ... ]}
+    print <<"EOD";
+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 @{[ MAX_DISKS * 2 + 1 ]}.  If you do the puzzle with
+2 disks, their code names would be @{[ MAX_DISKS * 2 - 1 ]} and @{[ MAX_DISKS * 2 + 1 ]}.  With 3 disks
+the code names would be @{[ MAX_DISKS * 2 - 3 ]}, @{[ MAX_DISKS * 2 - 1 ]} and @{[ MAX_DISKS * 2 + 1 ]}, 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!
+
+EOD
+
+    # Compute the legal disk numbers for this puzzle. The expression
+    # reads right to left thus:
+    #   * The .. operator generates the integers between and including
+    #     its end points, that is, from MAX_DISKS + 1 - $size to
+    #     MAX_DISKS, inclusive.
+    #   * map { .. } calls the block once for each of its arguments.
+    #     The value of the argument appears in the topic variable $ARG,
+    #     and the result of the block is returned.
+    #   * The list generated by the map {} is assigned to array
+    #     @legal_disks.
+    my @legal_disks = map { $ARG * 2 + 1 }
+        MAX_DISKS + 1 - $size ..  MAX_DISKS;
+
+    # Generate the board. This is an array of needles, indexed from
+    # zero. Each needle is represented by a reference to an array
+    # containing the disks on that needle, bottom to top.
+    my @board = (
+        [ reverse @legal_disks ],
+        [],
+        []
+    );
+
+    display( \@board ); # Display the initial board.
+
+    my $moves = 0;  # Move counter.
+
+    while ( 1 ) {   # Iterate until something makes us stop.
+        my $disk = get_input(
+            'Which disk would you like to move? ',
+            sub {
+                # Accept any odd integer in the required range.
+                # NOTE that 'and' performs the same operation as &&, but
+                # binds much more loosely.
+                return m/ \A [0-9]+ \z /smx &&
+                    $ARG % 2 &&
+                    $ARG >= ( MAX_DISKS + 1 - $size ) * 2 + 1 &&
+                    $ARG <= MAX_DISKS * 2 + 1;
+            },
+            3,
+            do {    # Compound statement and scope for 'local'
+
+                # We want to interpolate @legal_disks into our warning.
+                # Interpolation of an array places $LIST_SEPARATOR
+                # between the elements of the array. The default is ' ',
+                # but we want ', '. We use 'local' to restrict the
+                # change to the current block and code called by it.
+                # Failure to localize the change can cause Spooky Action
+                # at a Distance.
+                local $LIST_SEPARATOR = ', ';
+
+                "Illegal entry... You may only type @legal_disks\n";
+            },
+            "Stop wasting my time.  Go bother someone else.\n",
+        );
+
+        # Return the number (from zero) of the needle which has the
+        # desired disk on top. If the desired disk is not found, we got
+        # undef back. In this case we redo the innermost loop.
+        redo unless defined( my $from = find_disk( $disk, \@board ) );
+
+        # Find out where the chosen disk goes.
+        # NOTE that unlike the BASIC implementation, we require the
+        # needle to be moved.
+        my $to = get_input(
+            'Place disk on which needle? ',
+            sub {
+                # Accept integers 1 through 3, but not the current
+                # location of the disk
+                return m/ \A [0-9]+ \z /smx &&
+                    $ARG > 0 &&
+                    $ARG <= 3 &&
+                    $ARG != $from + 1;
+            },
+            2,
+            <<'EOD',
+I'll assume you hit the wrong key this time.  But watch it,
+I only allow one mistake.
+EOD
+            <<'EOD',
+I tried to warn you, but you wouldn't listen.
+Bye bye, big shot.
+EOD
+        ) - 1;
+
+        # Check for placing a larger disk on a smaller one. The check is
+        # that the destination needle has something on it (an empty
+        # array is false in Boolean context) and that the destination
+        # needle's top disk ([-1] selects the last element of an array)
+        # is smaller than the source needle's disk.
+        if ( @{ $board[$to] } && $board[$to][-1] < $board[$from][-1] ) {
+            warn <<'EOD';
+You can't place a larger disk on top of a smaller one,
+It might crush it!
+EOD
+            redo;
+        }
+
+        # Remove the selected disk from its needle, and place it on the
+        # destination needle.
+        push @{ $board[$to] }, pop @{ $board[$from] };
+
+        $moves++;   # Count another move.
+
+        display( \@board ); # Display the current board.
+
+        # If all the disks are on the last needle, we are done.
+        if ( @{ $board[2] } == $size ) {
+
+            # Print a success message
+            print <<"EOD";
+Congratulations!
+
+You have performed the task in $moves moves.
+
+EOD
+            last;   # Exit the innermost loop.
+
+        # If the maximum allowed moves have been exceeded
+        } elsif ( $moves >= 2 ** MAX_DISKS ) {
+
+            # Warn
+            warn <<"EOD";
+Sorry, but I have orders to stop if you make more than
+$moves moves.
+EOD
+
+            last;   # Exit the innermost loop.
+        }
+    }
+
+    say '';
+    get_input(
+        'Try again? [y/N]: ',
+        sub {
+            exit if $ARG eq '' || m/ \A n /smxi;
+            return m/ \A y /smxi;
+        },
+        ~0, # The 1's complement of 0 = largest possible integer
+        "Please respond 'y' or 'n'\n",
+    );
+}
+
+# Display the board, which is passed in as a reference.
+sub display {
+    my ( $board ) = @_;
+    say '';
+
+    # Use a manifest constant for an empty needle. This is global
+    # despite its appearing to be nested in the subroutine. Perl uses
+    # 'x' as its string replication operator. The initial 4 blanks
+    # accommodate the disk number and spacing between needles.
+    use constant EMPTY_NEEDLE   => ' ' x 4 . ' ' x MAX_DISKS . '|' .
+        ' ' x MAX_DISKS;
+
+    # Iterate over the rows to be printed.
+    foreach my $inx ( reverse 0 .. MAX_DISKS ) {
+
+        my $line;   # Line buffer.
+
+        # Iterate over needles.
+        foreach my $col ( 0 .. 2 ) {
+
+            # If this position on the needle is occupied
+            if ( my $disk_num = $board->[$col][$inx] ) {
+
+                # Compute the width of a half disk
+                my $half_width = ( $disk_num - 1 ) / 2;
+
+                # Compute the graphic for the half disk. Perl uses 'x'
+                # as its string replication operator.
+                my $half_disk = '*' x $half_width;
+
+                # Append the disk to the line. The inner sprintf() does
+                # most of the work; the outer simply pads the graphic to
+                # the required total width.
+                $line .= sprintf( '%*s', -( MAX_DISKS * 2 + 5 ),
+                    sprintf( '%*d %s|%s', MAX_DISKS + 3 - $half_width,
+                        $disk_num, $half_disk, $half_disk ) );
+
+            # Else this position is not occupied
+            } else {
+
+                # So just append the empty needle.
+                $line .= EMPTY_NEEDLE;
+            }
+        }
+
+        # Remove white space at the end of the line
+        $line =~ s/ \s+ \z //smx;
+
+        # Display the line
+        say $line;
+    }
+    {   # Display the needle numbers
+        my $line;
+        foreach my $col ( 0 .. 2 ) {
+            $line .= sprintf '%*d%*s', MAX_DISKS + 5, $col + 1,
+            MAX_DISKS, ' ';
+        }
+        $line =~ s/ \s+ \z //smx;
+        say $line;
+    }
+
+    say ''; # Empty line
+
+    return;
+}
+
+# Find the named disk. The arguments are the disk number (which is
+# assumed valid) and a reference to the board. If the disk is found on
+# the top of a needle, the needle's index (from zero) is returned.
+# Otherwise a warning is issued and undef is returned.
+sub find_disk {
+    my ( $disk, $board ) = @_;
+    foreach my $inx ( 0 .. 2 ) {
+        @{ $board->[$inx] }                 # If the needle is occupied
+            and $disk == $board->[$inx][-1] # and we want its topmost
+            and return $inx;                # return needle index
+    }
+
+    # Since we assume the disk number is valid but we did not find it,
+    # it must not be the topmost disk.
+    warn "That disk is below another one.  Make another choice.\n";
+
+    return undef;
+}
+
+# Input subroutine. The arguments are:
+# * The prompt.
+# * Validation code. This recieves the input in the topic variable $ARG,
+#   and returns a true value if the validation passed, and a false value
+#   if it failed.
+# * The maximum number of tries before dying.
+# * The warning message for a validation failure, with trailing "\n".
+# * The error message when the number of tries is exceeded, with
+#   trailing "\n".
+# The return is the valid input. We exit if end-of-file is reached,
+sub get_input {
+    my ( $prompt, $validate, $tries, $warning, $error ) = @_;
+
+    # Instantiate the readline object. A state variable is only
+    # initialized once.
+    state $term = Term::ReadLine->new( 'tower' );
+
+    while ( 1 ) {   # Iterate until something makes us stop.
+
+        # The input gets read into the localized topic variable. If it
+        # is undefined, it signals end-of-file, so we exit.
+        exit unless defined( local $ARG = $term->readline( $prompt ) );
+
+        # Call the validation code. If it returns a true value, we
+        # return our input.
+        return $ARG if $validate->();
+
+        # Die if we are out of retries. In Perl, 0 is false and all
+        # other integers are true.
+        die $error unless --$tries;
+
+        # Warn.
+        warn $warning;
+    }
+}
+
+__END__
+
+=head1 TITLE
+
+tower.pl - Play the game 'tower' from Basic Computer Games
+
+=head1 SYNOPSIS
+
+ tower.pl
+
+=head1 DETAILS
+
+This Perl script is a port of C, which is the 90th 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/90_Tower/python/tower.py b/90_Tower/python/tower.py
new file mode 100644
index 00000000..5e4a25b7
--- /dev/null
+++ b/90_Tower/python/tower.py
@@ -0,0 +1,162 @@
+import sys
+
+class Disk:
+    def __init__(self, size):
+        self.__size = size
+
+    def size(self):
+        return self.__size
+
+    def print(self):
+        print("[ %s ]" % self.size())
+
+class Tower:
+    def __init__(self):
+        self.__disks = []
+
+    def empty(self):
+        return len(self.__disks) == 0
+
+    def top(self):
+        if self.empty():
+            return None
+        else:
+            return self.__disks[-1]
+
+    def add(self, disk):
+        if not self.empty():
+            t = self.top()
+            if disk.size() > t.size():
+                raise Exception("YOU CAN'T PLACE A LARGER DISK ON TOP OF A SMALLER ONE, IT MIGHT CRUSH IT!")
+        self.__disks.append(disk)
+
+    def pop(self):
+        if self.empty():
+            raise Exception("empty pop")
+        return self.__disks.pop()
+
+    def print(self):
+        r = "Needle: [%s]" % (", ".join([str(x.size()) for x in self.__disks]))
+        print(r)
+
+
+
+print("""
+IN THIS PROGRAM, WE SHALL REFER TO DISKS BY NUMERICAL CODE.
+3 WILL REPRESENT THE SMALLEST DISK, 5 THE NEXT SIZE,
+7 THE NEXT, AND SO ON, UP TO 15.  IF YOU DO THE PUZZLE WITH
+2 DISKS, THEIR CODE NAMES WOULD BE 13 AND 15.  WITH 3 DISKS
+THE CODE NAMES WOULD BE 11, 13 AND 15, ETC.  THE NEEDLES
+ARE NUMBERED FROM LEFT TO RIGHT, 1 TO 3.  WE WILL
+START WITH THE DISKS ON NEEDLE 1, AND ATTEMPT TO MOVE THEM
+TO NEEDLE 3.
+
+GOOD LUCK!
+
+""")
+
+
+
+
+class Game:
+    def __init__(self):
+        # use fewer sizes to make debugging easier
+        # self.__sizes = [3, 5, 7]  # ,9,11,13,15]
+        self.__sizes = [3, 5, 7, 9, 11, 13, 15]
+        
+        self.__sizes.sort()
+
+        self.__towers = []
+        self.__moves = 0
+        self.__towers = [Tower(), Tower(), Tower()]
+        self.__sizes.reverse()
+        for size in self.__sizes:
+            disk = Disk(size)
+            self.__towers[0].add(disk)
+
+    def winner(self):
+        return self.__towers[0].empty() and self.__towers[1].empty()
+
+    def print(self):
+        for t in self.__towers:
+            t.print()
+
+    def moves(self):
+        return self.__moves
+
+    def which_disk(self):
+        w = int(input("WHICH DISK WOULD YOU LIKE TO MOVE\n"))
+        if w in self.__sizes:
+            return w
+        else:
+            raise Exception()
+
+    def pick_disk(self):
+        which = None
+        while which is None:
+            try:
+                which = self.which_disk()
+            except:
+                print("ILLEGAL ENTRY... YOU MAY ONLY TYPE 3,5,7,9,11,13, OR 15.\n")
+
+        valids = [t for t in self.__towers if t.top() and t.top().size() == which]
+        assert len(valids) in (0, 1)
+        if not valids:
+            print("THAT DISK IS BELOW ANOTHER ONE.  MAKE ANOTHER CHOICE.\n")
+            return None
+        else:
+            assert valids[0].top().size() == which
+            return valids[0]
+
+    def which_tower(self):
+        try:
+            needle = int(input("PLACE DISK ON WHICH NEEDLE\n"))
+            tower = self.__towers[needle - 1]
+        except:
+            print("I'LL ASSUME YOU HIT THE WRONG KEY THIS TIME.  BUT WATCH IT,\nI ONLY ALLOW ONE MISTAKE.\n")
+            return None
+        else:
+            return tower
+
+    def take_turn(self):
+        from_tower = None
+        while from_tower is None:
+            from_tower = self.pick_disk()
+
+        to_tower = self.which_tower()
+        if not to_tower:
+            to_tower = self.which_tower()
+
+        if not to_tower:
+            print("I TRIED TO WARN YOU, BUT YOU WOULDN'T LISTEN.\nBYE BYE, BIG SHOT.\n")
+            sys.exit(0)
+
+        disk = from_tower.pop()
+        try:
+            to_tower.add( disk )
+            self.__moves += 1
+        except Exception as err:
+            print(err)
+            from_tower.add(disk)
+
+game = Game()
+while True:
+    game.print()
+
+    game.take_turn()
+
+    if game.winner():
+        print("CONGRATULATIONS!!\nYOU HAVE PERFORMED THE TASK IN %s MOVES.\n" % game.moves())
+        while True:
+            yesno = input("TRY AGAIN (YES OR NO)\n")
+            if yesno.upper() == "YES":
+                game = Game()
+                break
+            elif yesno.upper() == "NO":
+                print("THANKS FOR THE GAME!\n")
+                sys.exit(0)
+            else:
+                print("'YES' OR 'NO' PLEASE\n")
+    elif game.moves() > 128:
+        print("SORRY, BUT I HAVE ORDERS TO STOP IF YOU MAKE MORE THAN 128 MOVES.")
+        sys.exit(0)
diff --git a/90_Tower/python/tower_test.py b/90_Tower/python/tower_test.py
new file mode 100644
index 00000000..a7023a0f
--- /dev/null
+++ b/90_Tower/python/tower_test.py
@@ -0,0 +1,49 @@
+import unittest
+import tower
+
+class MyTestCase(unittest.TestCase):
+    def test_something(self):
+        t = tower.Tower()
+        self.assertTrue(t.empty())
+
+        d = tower.Disk(3)
+        t.add(d)
+        self.assertFalse(t.empty())
+
+        d5 = tower.Disk(5)
+        self.assertRaises(Exception, t.add, d5)
+        self.assertFalse(t.empty())
+
+    def test_oksize(self):
+        t = tower.Tower()
+        self.assertTrue(t.empty())
+
+        d5 = tower.Disk(5)
+        t.add(d5)
+        self.assertFalse(t.empty())
+
+        d3 = tower.Disk(3)
+        t.add(d3)
+        self.assertFalse(t.empty())
+
+        self.assertEqual(t.top(), d3)
+        self.assertEqual(t.pop(), d3)
+        self.assertEqual(t.pop(), d5)
+
+    def test_game(self):
+        g = tower.Game()
+        self.assertEqual(g.moves(), 0)
+        self.assertFalse(g.winner())
+
+    def test_format(self):
+        t = tower.Tower()
+        d3 = tower.Disk(3)
+        d5 = tower.Disk(5)
+        t.add(d5)
+        t.add(d3)
+
+        f = t.vertical_format(6, 3)
+        self.assertEqual(f, ['      ', '[ 3 ] ', '[ 5 ] '])
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/91_Train/README.md b/91_Train/README.md
index c5a249fb..50e7cf6e 100644
--- a/91_Train/README.md
+++ b/91_Train/README.md
@@ -1,7 +1,16 @@
 ### Train
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=175
+TRAIN is a program which uses the computer to generate problems with random initial conditions to teach about the time-speed-distance relationship (distance = rate x time). You then input your answer and the computer verifies your response.
+
+TRAIN is merely an example of a student-generated problem. Maximum fun (and benefit) comes more from _writing_ programs like this as opposed to solving the specific problem posed. Exchange your program with others—you solve their problem and let them solve yours.
+
+TRAIN was originally written in FOCAL by one student for use by others in his class. It was submitted to us by Walt Koetke, Lexington High School, Lexington, Mass.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=175)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=190)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/91_Train/csharp/Train/Train.sln b/91_Train/csharp/Train/Train.sln
new file mode 100644
index 00000000..7735a737
--- /dev/null
+++ b/91_Train/csharp/Train/Train.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.31129.286
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TrainGame", "Train\TrainGame.csproj", "{42617537-4E7C-4082-A17B-7F18DFA04C35}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TrainTests", "..\TrainTests\TrainTests\TrainTests.csproj", "{7C740A47-99C6-44E1-BDEE-140086BCFE8B}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{42617537-4E7C-4082-A17B-7F18DFA04C35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{42617537-4E7C-4082-A17B-7F18DFA04C35}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{42617537-4E7C-4082-A17B-7F18DFA04C35}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{42617537-4E7C-4082-A17B-7F18DFA04C35}.Release|Any CPU.Build.0 = Release|Any CPU
+		{7C740A47-99C6-44E1-BDEE-140086BCFE8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{7C740A47-99C6-44E1-BDEE-140086BCFE8B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{7C740A47-99C6-44E1-BDEE-140086BCFE8B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{7C740A47-99C6-44E1-BDEE-140086BCFE8B}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {919F73B8-DE34-4992-9B05-E1FEC2D2F7C6}
+	EndGlobalSection
+EndGlobal
diff --git a/91_Train/csharp/Train/Train/TrainGame.cs b/91_Train/csharp/Train/Train/TrainGame.cs
new file mode 100644
index 00000000..98e0db68
--- /dev/null
+++ b/91_Train/csharp/Train/Train/TrainGame.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Linq;
+
+namespace Train
+{
+    public class TrainGame
+    {
+        private Random Rnd { get; } = new Random();
+        private readonly int ALLOWED_PERCENTAGE_DIFFERENCE = 5;
+
+        static void Main()
+        {
+            TrainGame train = new TrainGame();
+            train.GameLoop();
+        }
+
+        public void GameLoop()
+        {
+            DisplayIntroText();
+
+            do
+            {
+                PlayGame();
+            } while (TryAgain());
+        }
+
+        private void PlayGame()
+        {
+            int carSpeed = (int)GenerateRandomNumber(40, 25);
+            int timeDifference = (int)GenerateRandomNumber(5, 15);
+            int trainSpeed = (int)GenerateRandomNumber(20, 19);
+
+            Console.WriteLine($"A CAR TRAVELING {carSpeed} MPH CAN MAKE A CERTAIN TRIP IN");
+            Console.WriteLine($"{timeDifference} HOURS LESS THAN A TRAIN TRAVELING AT {trainSpeed} MPH");
+            Console.WriteLine("HOW LONG DOES THE TRIP TAKE BY CAR?");
+
+            double userInputCarJourneyDuration = double.Parse(Console.ReadLine());
+            double actualCarJourneyDuration = CalculateCarJourneyDuration(carSpeed, timeDifference, trainSpeed);
+            int percentageDifference = CalculatePercentageDifference(userInputCarJourneyDuration, actualCarJourneyDuration);
+
+            if (IsWithinAllowedDifference(percentageDifference, ALLOWED_PERCENTAGE_DIFFERENCE))
+            {
+                Console.WriteLine($"GOOD! ANSWER WITHIN {percentageDifference} PERCENT.");
+            }
+            else
+            {
+                Console.WriteLine($"SORRY.  YOU WERE OFF BY {percentageDifference} PERCENT.");
+            }
+            Console.WriteLine($"CORRECT ANSWER IS {actualCarJourneyDuration} HOURS.");
+        }
+
+        public static bool IsWithinAllowedDifference(int percentageDifference, int allowedDifference)
+        {
+            return percentageDifference <= allowedDifference;
+        }
+
+        private static int CalculatePercentageDifference(double userInputCarJourneyDuration, double carJourneyDuration)
+        {
+            return (int)(Math.Abs((carJourneyDuration - userInputCarJourneyDuration) * 100 / userInputCarJourneyDuration) + .5);
+        }
+
+        public static double CalculateCarJourneyDuration(double carSpeed, double timeDifference, double trainSpeed)
+        {
+            return timeDifference * trainSpeed / (carSpeed - trainSpeed);
+        }
+
+        public double GenerateRandomNumber(int baseSpeed, int multiplier)
+        {
+            return multiplier * Rnd.NextDouble() + baseSpeed;
+        }
+
+        private bool TryAgain()
+        {
+            Console.WriteLine("ANOTHER PROBLEM (YES OR NO)? ");
+            return IsInputYes(Console.ReadLine());
+        }
+
+        public static bool IsInputYes(string consoleInput)
+        {
+            var options = new string[] { "Y", "YES" };
+            return options.Any(o => o.Equals(consoleInput, StringComparison.CurrentCultureIgnoreCase));
+        }
+
+        private void DisplayIntroText()
+        {
+            Console.WriteLine("TRAIN");
+            Console.WriteLine("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
+            Console.WriteLine();
+            Console.WriteLine("TIME - SPEED DISTANCE EXERCISE");
+            Console.WriteLine();
+        }
+    }
+}
diff --git a/91_Train/csharp/Train/Train/TrainGame.csproj b/91_Train/csharp/Train/Train/TrainGame.csproj
new file mode 100644
index 00000000..c73e0d16
--- /dev/null
+++ b/91_Train/csharp/Train/Train/TrainGame.csproj
@@ -0,0 +1,8 @@
+
+
+  
+    Exe
+    netcoreapp3.1
+  
+
+
diff --git a/91_Train/csharp/TrainTests/TrainTests/TrainGameTests.cs b/91_Train/csharp/TrainTests/TrainTests/TrainGameTests.cs
new file mode 100644
index 00000000..a9804283
--- /dev/null
+++ b/91_Train/csharp/TrainTests/TrainTests/TrainGameTests.cs
@@ -0,0 +1,53 @@
+using Train;
+using Xunit;
+
+namespace TrainTests
+{
+    public class TrainGameTests
+    {
+        [Fact]
+        public void MiniumRandomNumber()
+        {
+            TrainGame game = new TrainGame();
+            Assert.True(game.GenerateRandomNumber(10, 10) >= 10);
+        }
+
+        [Fact]
+        public void MaximumRandomNumber()
+        {
+            TrainGame game = new TrainGame();
+            Assert.True(game.GenerateRandomNumber(10, 10) <= 110);
+        }
+
+        [Fact]
+        public void IsInputYesWhenY()
+        {
+            Assert.True(TrainGame.IsInputYes("y"));
+        }
+
+        [Fact]
+        public void IsInputYesWhenNotY()
+        {
+            Assert.False(TrainGame.IsInputYes("a"));
+        }
+
+        [Fact]
+        public void CarDurationTest()
+        {
+            Assert.Equal(1, TrainGame.CalculateCarJourneyDuration(30, 1, 15) );
+        }
+
+        [Fact]
+        public void IsWithinAllowedDifference()
+        {
+            Assert.True(TrainGame.IsWithinAllowedDifference(5,5));
+        }
+
+
+        [Fact]
+        public void IsNotWithinAllowedDifference()
+        {
+            Assert.False(TrainGame.IsWithinAllowedDifference(6, 5));
+        }
+    }
+}
diff --git a/91_Train/csharp/TrainTests/TrainTests/TrainTests.csproj b/91_Train/csharp/TrainTests/TrainTests/TrainTests.csproj
new file mode 100644
index 00000000..a8de6dde
--- /dev/null
+++ b/91_Train/csharp/TrainTests/TrainTests/TrainTests.csproj
@@ -0,0 +1,26 @@
+
+
+  
+    netcoreapp3.1
+
+    false
+  
+
+  
+    
+    
+    
+      runtime; build; native; contentfiles; analyzers; buildtransitive
+      all
+    
+    
+      runtime; build; native; contentfiles; analyzers; buildtransitive
+      all
+    
+  
+
+  
+    
+  
+
+
diff --git a/92_Trap/README.md b/92_Trap/README.md
index 7778ecb5..18d5db48 100644
--- a/92_Trap/README.md
+++ b/92_Trap/README.md
@@ -1,7 +1,19 @@
 ### Trap
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=176
+This is another in the family of “guess the mystery number” games. In TRAP the computer selects a random number between 1 and 100 (or other limit set). Your object is to find the number. On each guess, you enter 2 numbers trying to trap the mystery number between your two trap numbers. The computer will tell you if you have trapped the number.
+
+To win the game, you must guess the mystery number by entering it as the same value for both of your trap numbers. You get 6 guesses (this should be changed if you change the guessing limit).
+
+After you have played GUESS, STARS, and TRAP, compare the guessing strategy you have found best for each game. Do you notice any similarities? What are the differences? Can you write a new guessing game with still another approach?
+
+TRAP was suggested by a 10-year-old when he was playing GUESS. It was originally programmed by Steve Ullman and extensively modified into its final form by Bob Albrecht of People’s Computer Co.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=176)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=191)
+
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/93_23_Matches/README.md b/93_23_Matches/README.md
index eb368c88..3e48212e 100644
--- a/93_23_Matches/README.md
+++ b/93_23_Matches/README.md
@@ -1,7 +1,18 @@
 ### 23 Matches
 
-As published in Basic Computer Games (1978)
-https://www.atariarchives.org/basicgames/showpage.php?page=177
+In the game of twenty-three matches, you start with 23 matches lying on a table. On each turn, you may take 1, 2, or 3 matches. You alternate moves with the computer and the one who has to take the last match loses.
+
+The easiest way to devise a winning strategy is to start at the end of the game. Since your wish to leave the last match to your opponent, you would like to have either 4, 3, or 2 on your last turn you so can take away 3, 2, or 1 and leave 1. Consequently, you would like to leave your opponent with 5 on his next to last turn so, no matter what his move, you are left with 4, 3, or 2. Work this backwards to the beginning and you’ll find the game can effectively be won on the first move. Fortunately, the computer gives you the first move, so if you play wisely, you can win.
+
+After you’ve mastered 23 Matches, move on to BATNUM and then to NUM.
+
+This version of 23 Matches was originally written by Bob Albrecht of People’s Computer Company.
+
+---
+
+As published in Basic Computer Games (1978):
+- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=177)
+- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=192)
 
 Downloaded from Vintage Basic at
 http://www.vintage-basic.net/games.html
diff --git a/93_23_Matches/java/CoinSide.java b/93_23_Matches/java/CoinSide.java
new file mode 100644
index 00000000..7126e9b4
--- /dev/null
+++ b/93_23_Matches/java/CoinSide.java
@@ -0,0 +1,4 @@
+public enum CoinSide {
+    HEADS,
+    TAILS
+}
diff --git a/93_23_Matches/java/Messages.java b/93_23_Matches/java/Messages.java
new file mode 100644
index 00000000..c0e52b7c
--- /dev/null
+++ b/93_23_Matches/java/Messages.java
@@ -0,0 +1,70 @@
+public class Messages {
+
+    // This is a utility class and contains only static members.
+    // Utility classes are not meant to be instantiated.
+    private Messages() {
+        throw new IllegalStateException("Utility class");
+    }
+
+    public static final String INTRO = """
+                                          23 MATCHES
+                          CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY
+                        
+                        
+                        
+             THIS IS A GAME CALLED '23 MATCHES'.
+                        
+            WHEN IT IS YOUR TURN, YOU MAY TAKE ONE, TWO, OR THREE
+            MATCHES. THE OBJECT OF THE GAME IS NOT TO HAVE TO TAKE
+            THE LAST MATCH.
+                        
+            LET'S FLIP A COIN TO SEE WHO GOES FIRST.
+            IF IT COMES UP HEADS, I WILL WIN THE TOSS.
+            """;
+
+    public static final String HEADS = """
+            HEADS! I WIN! HA! HA!
+            PREPARE TO LOSE, MEATBALL-NOSE!!
+                      
+            I TAKE 2 MATCHES
+            """;
+
+    public static final String TAILS = """
+            TAILS! YOU GO FIRST.
+            """;
+
+    public static final String MATCHES_LEFT = """
+            THE NUMBER OF MATCHES IS NOW %d
+            
+            YOUR TURN -- YOU MAY TAKE 1, 2 OR 3 MATCHES.
+            """;
+
+    public static final String REMOVE_MATCHES_QUESTION = "HOW MANY DO YOU WISH TO REMOVE? ";
+
+    public static final String REMAINING_MATCHES = """
+            THERE ARE NOW %d MATCHES REMAINING.
+            """;
+
+    public static final String INVALID = """
+            VERY FUNNY! DUMMY!
+            DO YOU WANT TO PLAY OR GOOF AROUND?
+            NOW, HOW MANY MATCHES DO YOU WANT?
+            """;
+
+    public static final String WIN = """
+            YOU WON, FLOPPY EARS !
+            THINK YOU'RE PRETTY SMART !
+            LETS PLAY AGAIN AND I'LL BLOW YOUR SHOES OFF !!
+            """;
+
+    public static final String CPU_TURN = """
+            MY TURN ! I REMOVE %d MATCHES.
+            """;
+
+    public static final String LOSE = """
+            YOU POOR BOOB! YOU TOOK THE LAST MATCH! I GOTCHA!!
+            HA ! HA ! I BEAT YOU !!!
+                      
+            GOOD BYE LOSER!
+            """;
+}
diff --git a/93_23_Matches/java/TwentyThreeMatches.java b/93_23_Matches/java/TwentyThreeMatches.java
index 46b12793..f0c9836a 100644
--- a/93_23_Matches/java/TwentyThreeMatches.java
+++ b/93_23_Matches/java/TwentyThreeMatches.java
@@ -1,163 +1,79 @@
+import java.util.Random;
 import java.util.Scanner;
-import java.lang.Math;
 
-/**
- * Game of 23 Matches
- * 

- * Based on the BASIC game of 23 Matches here - * https://github.com/coding-horror/basic-computer-games/blob/main/93%2023%20Matches/23matches.bas - *

- * Note: The idea was to create a version of the 1970's BASIC game in Java, without introducing - * new features - no additional text, error checking, etc has been added. - * - * Converted from BASIC to Java by Darren Cardenas. - */ - -public class TwentyThreeMatches { +public class TwentyThreeMatches { - private static final int MATCH_COUNT_START = 23; + private static final int MATCH_COUNT_START = 23; + private static final Random RAND = new Random(); + private final Scanner scan = new Scanner(System.in); - private static final int HEADS = 1; - - private final Scanner scan; // For user input - - public TwentyThreeMatches() { - - scan = new Scanner(System.in); - - } // End of constructor TwentyThreeMatches + public void startGame() { + //Initialize values + int cpuRemoves = 0; + int matchesLeft = MATCH_COUNT_START; + int playerRemoves = 0; - public void play() { - - showIntro(); - startGame(); - - } // End of method play - - private static void showIntro() { - - System.out.println(" ".repeat(30) + "23 MATCHES"); - System.out.println(" ".repeat(14) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); - System.out.println("\n\n"); + //Flip coin and decide who goes first. + CoinSide coinSide = flipCoin(); + if (coinSide == CoinSide.HEADS) { + System.out.println(Messages.HEADS); + matchesLeft -= 2; + } else { + System.out.println(Messages.TAILS); + } - System.out.println(" THIS IS A GAME CALLED '23 MATCHES'."); - System.out.println(""); - System.out.println("WHEN IT IS YOUR TURN, YOU MAY TAKE ONE, TWO, OR THREE"); - System.out.println("MATCHES. THE OBJECT OF THE GAME IS NOT TO HAVE TO TAKE"); - System.out.println("THE LAST MATCH."); - System.out.println(""); - System.out.println("LET'S FLIP A COIN TO SEE WHO GOES FIRST."); - System.out.println("IF IT COMES UP HEADS, I WILL WIN THE TOSS."); - System.out.println(""); - - } // End of method showIntro - - private void startGame() { + // Game loop + while (true) { + //Show matches left if CPU went first or Player already removed matches + if (coinSide == CoinSide.HEADS) { + System.out.format(Messages.MATCHES_LEFT, matchesLeft); + } + coinSide = CoinSide.HEADS; - int coinSide = (int) (3 * Math.random()); - int cpuRemoves = 0; - int matchesLeft = MATCH_COUNT_START; - int playerRemoves = 0; - - if (coinSide == HEADS) { - - System.out.println("HEADS! I WIN! HA! HA!"); - System.out.println("PREPARE TO LOSE, MEATBALL-NOSE!!"); - System.out.println(""); - System.out.println("I TAKE 2 MATCHES"); - - matchesLeft -= 2; - - } else { - - System.out.println("TAILS! YOU GO FIRST. "); - System.out.println(""); - + // Player removes matches + System.out.println(Messages.REMOVE_MATCHES_QUESTION); + playerRemoves = turnOfPlayer(); + matchesLeft -= playerRemoves; + System.out.format(Messages.REMAINING_MATCHES, matchesLeft); + + // If 1 match is left, the CPU has to take it. You win! + if (matchesLeft <= 1) { + System.out.println(Messages.WIN); + return; + } + + // CPU removes matches + // At least two matches are left, because win condition above was not triggered. + if (matchesLeft <= 4) { + cpuRemoves = matchesLeft - 1; + } else { + cpuRemoves = 4 - playerRemoves; + } + System.out.format(Messages.CPU_TURN, cpuRemoves); + matchesLeft -= cpuRemoves; + + // If 1 match is left, the Player has to take it. You lose! + if (matchesLeft <= 1) { + System.out.println(Messages.LOSE); + return; + } + } } - // Begin outer while loop - while (true) { - - if (coinSide == HEADS) { - - System.out.println("THE NUMBER OF MATCHES IS NOW " + matchesLeft); - System.out.println(""); - System.out.println("YOUR TURN -- YOU MAY TAKE 1, 2 OR 3 MATCHES."); - - } - - coinSide = HEADS; + private CoinSide flipCoin() { + return RAND.nextBoolean() ? CoinSide.HEADS : CoinSide.TAILS; + } - System.out.print("HOW MANY DO YOU WISH TO REMOVE? "); - - // Begin match removal while loop - while (true) { - - playerRemoves = scan.nextInt(); - - // Handle invalid entries - if ((playerRemoves > 3) || (playerRemoves <= 0)) { - - System.out.println("VERY FUNNY! DUMMY!"); - System.out.println("DO YOU WANT TO PLAY OR GOOF AROUND?"); - System.out.print("NOW, HOW MANY MATCHES DO YOU WANT? "); - continue; - + private int turnOfPlayer() { + while (true) { + int playerRemoves = scan.nextInt(); + // Handle invalid entries + if ((playerRemoves > 3) || (playerRemoves <= 0)) { + System.out.println(Messages.INVALID); + continue; + } + return playerRemoves; } - - break; - - } // End match removal while loop - - matchesLeft -= playerRemoves; - - System.out.println("THERE ARE NOW " + matchesLeft + " MATCHES REMAINING."); - - // Win condition - if (matchesLeft <= 1) { - - // Win condition - System.out.println("YOU WON, FLOPPY EARS !"); - System.out.println("THINK YOU'RE PRETTY SMART !"); - System.out.println("LETS PLAY AGAIN AND I'LL BLOW YOUR SHOES OFF !!"); - System.out.println(""); - return; - - } else if ((matchesLeft >= 2) && (matchesLeft <= 4)) { - - cpuRemoves = matchesLeft - 1; - - } else { - - cpuRemoves = 4 - playerRemoves; - - } - - System.out.println("MY TURN ! I REMOVE " + cpuRemoves + " MATCHES"); - - matchesLeft -= cpuRemoves; + } - // Lose condition - if (matchesLeft <= 1) { - - System.out.println(""); - System.out.println("YOU POOR BOOB! YOU TOOK THE LAST MATCH! I GOTCHA!!"); - System.out.println("HA ! HA ! I BEAT YOU !!!"); - System.out.println(""); - System.out.println("GOOD BYE LOSER!"); - return; - - } - - } // End outer while loop - - } // End of method startGame - - public static void main(String[] args) { - - TwentyThreeMatches game = new TwentyThreeMatches(); - game.play(); - - } // End of method main - -} // End of class TwentyThreeMatches +} diff --git a/93_23_Matches/java/TwentyThreeMatchesGame.java b/93_23_Matches/java/TwentyThreeMatchesGame.java new file mode 100644 index 00000000..01735a91 --- /dev/null +++ b/93_23_Matches/java/TwentyThreeMatchesGame.java @@ -0,0 +1,24 @@ +/** + * Game of 23 Matches + *

+ * Based on the BASIC game of 23 Matches here + * https://github.com/coding-horror/basic-computer-games/blob/main/93%2023%20Matches/23matches.bas + *

+ * Note: The idea was to create a version of the 1970's BASIC game in Java, without introducing + * new features - no additional text, error checking, etc has been added. + *

+ * Converted from BASIC to Java by Darren Cardenas. + */ +public class TwentyThreeMatchesGame { + + public static void main(String[] args) { + showIntro(); + TwentyThreeMatches game = new TwentyThreeMatches(); + game.startGame(); + } + + private static void showIntro() { + System.out.println(Messages.INTRO); + } + +} diff --git a/93_23_Matches/perl/23matches.pl b/93_23_Matches/perl/23matches.pl new file mode 100644 index 00000000..c575d9cb --- /dev/null +++ b/93_23_Matches/perl/23matches.pl @@ -0,0 +1,86 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +print ' ' x 31 . "23 MATCHES\n"; +print ' ' x 15 . "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n"; +print "\n\n\n"; + +print " THIS IS A GAME CALLED '23 MATCHES'.\n\n"; + +print "WHEN IT IS YOUR TURN, YOU MAY TAKE ONE, TWO, OR THREE\n"; +print "MATCHES. THE OBJECT OF THE GAME IS NOT TO HAVE TO TAKE\n"; +print "THE LAST MATCH.\n\n"; + +print "LET'S FLIP A COIN TO SEE WHO GOES FIRST.\n"; +print "IF IT COMES UP HEADS, I WILL WIN THE TOSS.\n\n"; + +my $N = 23; +my $Q = int( 2 * rand(5) ); + +if ( $Q == 1 ) { + print "HEADS! I WIN! HA! HA!\n"; + print "PREPARE TO LOSE, MEATBALL-NOSE!!\n\n"; + + print "I TAKE 2 MATCHES\n"; + $N -= 2; + + print "THE NUMBER OF MATCHES IS NOW $N\n\n"; + + print "YOUR TURN -- YOU MAY TAKE 1, 2 OR 3 MATCHES.\n"; +} +else { + print "TAILS! YOU GO FIRST.\n\n"; +} + +print "HOW MANY DO YOU WISH TO REMOVE?\n"; + +INPUT: +{ + chomp( my $K = ); + + if ( $K > 3 or $K <= 0 ) { + print "VERY FUNNY! DUMMY!\n"; + print "DO YOU WANT TO PLAY OR GOOF AROUND?\n"; + print "NOW, HOW MANY MATCHES DO YOU WANT?\n"; + redo INPUT; + } + + $N -= $K; + + print "THERE ARE NOW $N MATCHES REMAINING.\n"; + + my $Z; + + if ( $N <= 1 ) { + print "YOU WON, FLOPPY EARS!\n"; + print "THINK YOU'RE PRETTY SMART!\n"; + print "LET'S PLAY AGAIN AND I'LL BLOW YOUR SHOES OFF!!\n"; + exit; + } + elsif ( $N > 4 ) { + $Z = 4 - $K; + } + else { + $Z = $N - 1; + } + + print "MY TURN! I REMOVE $Z MATCHES\n"; + + $N -= $Z; + + if ( $N <= 1 ) { + print "\nYOU POOR BOOB! YOU TOOK THE LAST MATCH! I GOTCHA!!\n"; + print "HA! HA! I BEAT YOU!!!\n\n"; + + print "GOOD BYE LOSER!\n"; + } + else { + print "THE NUMBER OF MATCHES IS NOW $N\n\n"; + + print "YOUR TURN -- YOU MAY TAKE 1, 2 OR 3 MATCHES.\n"; + print "HOW MANY DO YOU WISH TO REMOVE?\n"; + redo INPUT; + } +} diff --git a/94_War/README.md b/94_War/README.md index 4345c4ac..a850f73e 100644 --- a/94_War/README.md +++ b/94_War/README.md @@ -1,7 +1,14 @@ ### War -As published in Basic Computer Games (1978) -https://www.atariarchives.org/basicgames/showpage.php?page=178 +This program plays the card game of War. In War, the card deck is shuffled, then two cards are dealt, one to each player. Players compare cards and the higher card (numerically) wins. In case of a tie, no one wins. The game ends when you have gone through the whole deck (52 cards, 26 games) or when you decide to quit. + +The computer gives cards by suit and number, for example, S-7 is the 7 of spades. + +--- + +As published in Basic Computer Games (1978): +- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=178) +- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=193) Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html diff --git a/94_War/kotlin/README.md b/94_War/kotlin/README.md new file mode 100644 index 00000000..f43a5b70 --- /dev/null +++ b/94_War/kotlin/README.md @@ -0,0 +1,3 @@ +Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) + +Conversion to [Kotlin](https://kotlinlang.org/) diff --git a/94_War/kotlin/War.kt b/94_War/kotlin/War.kt new file mode 100644 index 00000000..0b8b9ee3 --- /dev/null +++ b/94_War/kotlin/War.kt @@ -0,0 +1,108 @@ +/** + * Converted FROM BASIC to Kotlin by John Long, with hints from the Java by Nahid Mondol. + * + */ + +val suits = listOf("S", "H", "C", "D") +val ranks = listOf("2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A") + +// Create the deck programmatically +val fullDeck = suits.flatMap { suit -> + ranks.map { rank -> + Card(suit, rank) + } +} + +class Card(private val suit: String, private val rank: String) { + // Allow comparison of cards to each other + operator fun compareTo(other: Card): Int = this.rankValue.compareTo(other.rankValue) + // We can figure relative rank by the order in the ranks value + private val rankValue: Int = ranks.indexOf(rank) + override fun toString(): String = "$suit-$rank" +} + +fun main() { + introMessage() + showDirectionsBasedOnInput() + playGame() + println("THANKS FOR PLAYING. IT WAS FUN.") +} + +private fun introMessage() { + println("\t WAR") + println("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") + println("THIS IS THE CARD GAME OF WAR. EACH CARD IS GIVEN BY SUIT-#") + print("AS S-7 FOR SPADE 7. DO YOU WANT DIRECTIONS? ") +} + +private fun showDirectionsBasedOnInput() { + if (getYesOrNo()) { + println("THE COMPUTER GIVES YOU AND IT A 'CARD'. THE HIGHER CARD") + println("(NUMERICALLY) WINS. THE GAME ENDS WHEN YOU CHOOSE NOT TO ") + println("CONTINUE OR WHEN YOU HAVE FINISHED THE PACK.\n") + } +} + +// Stay in loop until player chooses an option, then return "true" for yes or "false" for no +private fun getYesOrNo() = generateSequence { readln() }.firstNotNullOf { it.asYesOrNo } + +// Since this returns null for an incorrect value, above firstNotNullOf will keep looping until +// we get something valid +private val String.asYesOrNo: Boolean? + get() = + when (this.lowercase()) { + "yes" -> true + "no" -> false + else -> { + println("YES OR NO, PLEASE. ") + null + } + } + + +private fun playGame() { + // Shuffle the deck than break it into 26 pairs + val pairs = fullDeck.shuffled().chunked(2) + val score = Score(0, 0) + val lastPlayerCard = pairs.last().first() + // We use "destructuring" to extract the pair of cards directly to a variable here + pairs.forEach { (playerCard, computerCard) -> + println("YOU: $playerCard\tCOMPUTER: $computerCard") + when { + playerCard > computerCard -> score.playerWins() + computerCard > playerCard -> score.computerWins() + else -> println("TIE. NO SCORE CHANGE.") + } + // Doesn't make sense to ask to continue if we have no more cards left to deal + if (playerCard != lastPlayerCard) { + println("DO YOU WANT TO CONTINUE") + if (!getYesOrNo()) { + return + } + } + } + score.printFinalScore() + return +} + + +class Score(private var player: Int, private var computer: Int) { + fun playerWins() { + player++ + printScore("YOU WIN.") + } + + fun computerWins() { + computer++ + printScore("THE COMPUTER WINS!!!") + } + + private fun printScore(text: String) { + println("$text YOU HAVE $player AND THE COMPUTER HAS $computer") + } + + // Only print if you go through the whole deck + fun printFinalScore() { + println("WE HAVE RUN OUT OF CARDS. FINAL SCORE: YOU: $player THE COMPUTER:$computer") + } +} diff --git a/94_War/perl/war.pl b/94_War/perl/war.pl new file mode 100755 index 00000000..367a1185 --- /dev/null +++ b/94_War/perl/war.pl @@ -0,0 +1,153 @@ +#!/usr/bin/env perl + +use 5.010; # To get 'say' + +use strict; # Require explicit declaration of variables +use warnings; # Enable optional compiler warnings + +use English; # Use more friendly names for Perl's magic variables +use Getopt::Long 2.33 qw{ :config auto_version }; +use List::Util qw{ shuffle }; +use Pod::Usage; +use Term::ReadLine; # Prompt and return user input + +our $VERSION = '0.000_01'; + +my %opt; + +GetOptions( \%opt, + qw{ unicode! }, + help => sub { pod2usage( { -verbose => 2 } ) }, +) or pod2usage( { -verbose => 0 } ); + +my @cards; +my $current_rank_value = 1; +my %rank_value; +my @suits = $opt{unicode} ? + ( map { chr } 0x2660 .. 0x2663 ) : + ( qw{ S H D C } ); +foreach my $rank ( ( 2 .. 10 ), qw{ J Q K A } ) { + $rank_value{$rank} = $current_rank_value++; + foreach my $suit ( @suits ) { + push @cards, "$suit-$rank"; + } +} + +$opt{unicode} + and binmode STDOUT, ':encoding(utf-8)'; + +@cards = shuffle( @cards ); + +print <<'EOD'; + WAR + Creative Computing Morristown, New Jersey + + + +This is the card game of War. Each card is given by suit-# +EOD + +# Create the readline object. +state $term = Term::ReadLine->new( 'word' ); + +my $resp = $term->readline( + "as $suits[0]-7 for Spade 7. Do you want directions? [y/N]: " ); +exit unless defined $resp; +if ( $resp =~ m/ \A y /smxi ) { + print <<'EOD'; +The computer gives you and it a 'card'. The higher card +(numerically) wins. The game ends when you choose not to +continue or when you have finished the pack. + +EOD +} + +my $your_score = my $computer_score = 0; + +while ( 1 ) { + my ( $you, $computer ) = splice @cards, 0, 2; + say ''; + say "You: $you; computer: $computer"; + my $result = $rank_value{ substr $you, 2 } <=> + $rank_value{ substr $computer, 2 }; + if ( $result < 0 ) { + $computer_score++; + say "The computer wins!!! ", + "You have $your_score and the computer has $computer_score"; + } elsif ( $result > 0 ) { + $your_score++; + say "You win. ", + "You have $your_score and the computer has $computer_score"; + } else { + say 'Tie. No score change.'; + } + + last unless @cards; + + $resp = $term->readline( 'Do you want to continue? [Y/n]: ' ); + last unless defined $resp; + last if $resp =~ m/ \A n /smxi; +} + +say "We have run out of cards. ", + "Final score: you: $your_score; the computer: $computer_score" + unless @cards; +say ''; +say 'Thanks for playing. It was fun.'; +__END__ + +=head1 TITLE + +war.pl - Play the game 'War' from Basic Computer Games + +=head1 SYNOPSIS + + war.pl + war.pl --help + war.pl --version + +=head1 OPTIONS + +=head2 --help + +This option displays the documentation for this script. The script then +exits. + +=head2 --unicode + +If this Boolean option is asserted, the suits are designated by their +Unicode glyphs rather than by ASCII letters. For these to display +properly your terminal must properly interpret Unicode. + +The default is C<--no-unicode>. + +=head2 --version + +This option displays the version of this script. The script then exits. + +=head1 DETAILS + +This Perl script is a port of C, which is the 94th entry in Basic +Computer Games. + +=head1 PORTED BY + +Thomas R. Wyant, III F + +=head1 COPYRIGHT AND LICENSE + +Copyright (C) 2022 by Thomas R. Wyant, III + +This program is free software; you can redistribute it and/or modify it +under the same terms as Perl 5.10.0. For more details, see the Artistic +License 1.0 at +L, and/or the +Gnu GPL at L. + +This program is distributed in the hope that it will be useful, but +without any warranty; without even the implied warranty of +merchantability or fitness for a particular purpose. + +=cut + +# ex: set expandtab tabstop=4 textwidth=72 : diff --git a/95_Weekday/README.md b/95_Weekday/README.md index 16f825a3..445c2c4b 100644 --- a/95_Weekday/README.md +++ b/95_Weekday/README.md @@ -1,7 +1,16 @@ ### Weekday -As published in Basic Computer Games (1978) -https://www.atariarchives.org/basicgames/showpage.php?page=179 +This program gives facts about your date of birth (or some other day of interest). It is not prepared to give information on people born before the use of the current type of calendar, i.e. year 1582. + +You merely enter today’s date in the form—month, day, year and your date of birth in the same form. The computer then tells you the day of the week of your birth date, your age, and how much time you have spent sleeping, eating, working, and relaxing. + +This program was adapted from a GE timesharing program by Tom Kloos at the Oregon Museum of Science and Industry. + +--- + +As published in Basic Computer Games (1978): +- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=179) +- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=194) Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html diff --git a/96_Word/README.md b/96_Word/README.md index 404135e8..8db22c6c 100644 --- a/96_Word/README.md +++ b/96_Word/README.md @@ -1,7 +1,16 @@ ### Word -As published in Basic Computer Games (1978) -https://www.atariarchives.org/basicgames/showpage.php?page=181 +WORD is a combination of HANGMAN and BAGELS. In this game, the player must guess a word with clues as to a letter position furnished by the computer. However, instead of guessing one letter at a time, in WORD you guess an entire word (or group of 5 letters, such as ABCDE). The computer will tell you if any letters that you have guessed are in the mystery word and if any of them are in the correct position. Armed with these clues, you go on guessing until you get the word or, if you can’t get it, input a “?” and the computer will tell you the mystery word. + +You may change the words in Data Statements, but they must be 5-letter words. + +The author of this program is Charles Reid of Lexington High School, Lexington, Massachusetts. + +--- + +As published in Basic Computer Games (1978): +- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=181) +- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=194) Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html diff --git a/96_Word/javascript/word.js b/96_Word/javascript/word.js index 6e5acf50..4df5d3fd 100644 --- a/96_Word/javascript/word.js +++ b/96_Word/javascript/word.js @@ -12,10 +12,10 @@ function input() { var input_element; var input_str; - + return new Promise(function (resolve) { input_element = document.createElement("INPUT"); - + print("? "); input_element.setAttribute("type", "text"); input_element.setAttribute("length", "50"); @@ -61,7 +61,7 @@ async function main() print("\n"); print("\n"); print("I AM THINKING OF A WORD -- YOU GUESS IT. I WILL GIVE YOU\n"); - print("CLUE TO HELP YO GET IT. GOOD LUCK!!\n"); + print("CLUES TO HELP YOU GET IT. GOOD LUCK!!\n"); print("\n"); print("\n"); while (1) { diff --git a/96_Word/perl/word.pl b/96_Word/perl/word.pl new file mode 100755 index 00000000..5369cc71 --- /dev/null +++ b/96_Word/perl/word.pl @@ -0,0 +1,169 @@ +#!/usr/bin/env perl + +use 5.010; # To get 'state' and 'say' + +use strict; # Require explicit declaration of variables +use warnings; # Enable optional compiler warnings + +use English; # Use more friendly names for Perl's magic variables +use Term::ReadLine; # Prompt and return user input + +our $VERSION = '0.000_01'; + +print <<'EOD'; + WORD + Creative Computing Morristown, New Jersey + + + +I am thinking of a word -- you guess it. I will give you +clues to help you get it. Good luck!! + + +EOD + +# Read the content of __DATA__, remove the trailing newlines, and store +# each line into @words. Stop at __END__, since Perl does not see this +# as an end-of-file. +my @words; +while ( ) { + chomp; + last if $ARG eq '__END__'; + push @words, lc $ARG; # Normalize case to lower. +} + +# This loop represents an actual game. We execute it until the player +# does something that makes us explicitly break out. +while ( 1 ) { + print <<'EOD'; + + +You are starting a new game ... +EOD + + # Choose a random target word. The rand() function returns a number + # from 0 to its argument, and coerces its argument to a scalar. In + # scalar context, an array evaluates to the number of elements it + # contains. + my $target = $words[ rand @words ]; + + # We generalize the code by using the actual length of the target. + my $target_length = length $target; + + my $count = 0; # Number of guesses + + # Make an array of the individual letters in the target. We will + # iterate over this to determine matching letters. + my @target_array = split qr<>, $target; + + # Make a hash of those letters. We will use this to determine common + # letters. Any true value will do for the value of the hash. By + # making use of this hash we avoid the nested loops of the original + # BASIC program. + my %target_hash = map { $ARG => 1 } @target_array; + + # We keep prompting the player until we get a response that causes + # us to break out of the loop. + while ( 1 ) { + + # Create the readline object. The state keyword means the + # variable is only initialized once, no matter how many times + # execution passes this point. + state $term = Term::ReadLine->new( 'word' ); + + # Read the next guess. A return of undef means end-of-file. + my $guess = $term->readline( "Guess a $target_length letter word: " ); + exit unless defined $guess; + + last if $guess eq '?'; # A question mark means we give up + if ( length( $guess ) != $target_length ) { + # Wrong length. Ask again. + say "You must guess a $target_length letter word. Try again."; + redo; # Redo the innermost loop + } + + $guess = lc $guess; # Lower-case the guess + $count++; # Count another guess + + if ( $guess eq $target ) { + # We guessed the word. + say "You have guessed the word. It took $count guesses!"; + my $answer = $term->readline( 'Want to play again? [y/N]: '); + exit unless defined $guess; # End of file + exit unless $guess =~ m/ \A y /smxi; + last; # Exit the innermost loop. + } + + my @common_letters; # Letters common to guess and target + my $match = '-' x length $target; # Assume no matches + my $inx = 0; # Iterator + foreach my $letter ( split qr<>, $guess ) { + if ( $target_hash{$letter} ) { + # If the letter is in the hash, it occurs in the target + push @common_letters, $letter; + # If it is at the current position in the target, it is + # an actual match. + $target_array[$inx] eq $letter + and substr $match, $inx, 1, $letter; + } + $inx++; + } + + say 'There were ', scalar @common_letters, + ' matches and the common letters were... ', @common_letters; + say "From the exact letter matches, you know................ $match"; + say ''; + say q; + redo; + } + +} +__DATA__ +dinky +smoke +water +grass +train +might +first +candy +champ +would +clump +dopey +__END__ + +=head1 TITLE + +word.pl - Play the game 'word' from Basic Computer Games + +=head1 SYNOPSIS + + word.pl + +=head1 DETAILS + +This Perl script is a port of C, which is the 96th entry in Basic +Computer Games. + +=head1 PORTED BY + +Thomas R. Wyant, III F + +=head1 COPYRIGHT AND LICENSE + +Copyright (C) 2022 by Thomas R. Wyant, III + +This program is free software; you can redistribute it and/or modify it +under the same terms as Perl 5.10.0. For more details, see the Artistic +License 1.0 at +L, and/or the +Gnu GPL at L. + +This program is distributed in the hope that it will be useful, but +without any warranty; without even the implied warranty of +merchantability or fitness for a particular purpose. + +=cut + +# ex: set expandtab tabstop=4 textwidth=72 : diff --git a/README.md b/README.md index d481611c..266aaac8 100644 --- a/README.md +++ b/README.md @@ -42,3 +42,5 @@ Feel free to begin converting these classic games into the above list of modern, ### Have fun! Thank you for taking part in this project to update a classic programming book -- one of the most influential programming books in computing history -- for 2022 and beyond! + +NOTE: per [the official blog post announcement](https://blog.codinghorror.com/updating-the-single-most-influential-book-of-the-basic-era/), I will be **donating $5 for each contributed program in the 8 agreed upon languages to [Girls Who Code](https://girlswhocode.com/)**.