From 5439ece5317c9ca33a0e2b65c3a4d51458fc6d61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torben=20M=C3=B6ller?= Date: Sat, 1 Jan 2022 13:57:11 +0100 Subject: [PATCH 01/18] feat: Implement 78 Sine_Wave in Java --- 78_Sine_Wave/java/src/SineWave.java | 33 ++++++++++++----------------- 1 file changed, 13 insertions(+), 20 deletions(-) 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 ; } } } From f0f5c38c024269fa11008b091887780f6d33c417 Mon Sep 17 00:00:00 2001 From: Scott Halgrim Date: Sat, 1 Jan 2022 10:53:17 -0800 Subject: [PATCH 02/18] fix typo --- 01_Acey_Ducey/python/acey_ducey.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 32e6b885ce3d8e6c05f3722dba8633a0e59c3af7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torben=20M=C3=B6ller?= Date: Sat, 1 Jan 2022 20:17:09 +0100 Subject: [PATCH 03/18] feat: Implement 93 23_Matches in Java --- 93_23_Matches/java/CoinSide.java | 4 + 93_23_Matches/java/Messages.java | 70 ++++++ 93_23_Matches/java/TwentyThreeMatches.java | 218 ++++++------------ .../java/TwentyThreeMatchesGame.java | 24 ++ 4 files changed, 165 insertions(+), 151 deletions(-) create mode 100644 93_23_Matches/java/CoinSide.java create mode 100644 93_23_Matches/java/Messages.java create mode 100644 93_23_Matches/java/TwentyThreeMatchesGame.java 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); + } + +} From 14b66215e446025740ca65fda3f7ab03013357ff Mon Sep 17 00:00:00 2001 From: Moritz Hamann Date: Sat, 1 Jan 2022 19:20:35 +0000 Subject: [PATCH 04/18] Python solution for 50: Horserace Avoided any dictionary types, since those are not available in the original BASIC version. This makes it slightly less idiomatic. Tried my best to keep it as close to the original as possible. Also horses are enumerated from 0, rather than from 1 as in the original implementation (if needed this can easily be adjusted). --- 50_Horserace/python/horserace.py | 278 +++++++++++++++++++++++++++++++ 1 file changed, 278 insertions(+) create mode 100644 50_Horserace/python/horserace.py 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 From 88b964b9ae25b8fa604c0ad5ae55a19c1314e081 Mon Sep 17 00:00:00 2001 From: Domenic Denicola Date: Sat, 1 Jan 2022 16:03:26 -0500 Subject: [PATCH 05/18] Improve Acey Ducey JavaScript port Notable changes: * Use informative variable names instead of q, a, b, m, and c * Use a lookup table instead of chained ifs for face cards * Refactor I/O helpers into a separate file * Refactor random card logic into its own function * Support any case for "yes" input * Add a few comments to non-obvious parts of the game logic * Insert "HERE IS THE CARD WE DREW:" into the output since just printing the card was pretty confusing * Make indentation uniform * Make bracing style uniform * Use conventional JavaScript camelCasing * Use top-level await in a module instead of a main() function * Clean up the HTML shell page for modern best practices (including a mobile-friendly viewport) --- 01_Acey_Ducey/javascript/aceyducey.html | 12 +- 01_Acey_Ducey/javascript/aceyducey.js | 207 ++++++++++-------------- 01_Acey_Ducey/javascript/io.js | 29 ++++ 3 files changed, 120 insertions(+), 128 deletions(-) create mode 100644 01_Acey_Ducey/javascript/io.js diff --git a/01_Acey_Ducey/javascript/aceyducey.html b/01_Acey_Ducey/javascript/aceyducey.html index 991e3b5c..e526c3df 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..6c31d77c 100644
--- a/01_Acey_Ducey/javascript/aceyducey.js
+++ b/01_Acey_Ducey/javascript/aceyducey.js
@@ -1,135 +1,100 @@
-// ACEY DUCEY
-//
-// Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
-//
+import { readLine, print, spaces } from "./io.js";
 
-function print(str)
-{
-    document.getElementById("output").appendChild(document.createTextNode(str));
+const minFaceCard = 11;
+const faceCards = {
+  11: "JACK",
+  12: "QUEEN",
+  13: "KING",
+  14: "ACE"
+};
+
+function randomCard() {
+  return Math.floor(Math.random() * 13 + 2);
 }
 
-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 printCard(card) {
+  if (card < minFaceCard) {
+    print(card);
+  } else {
+    print(faceCards[card]);
+  }
+  print("\n");
 }
 
-function tab(space)
-{
-    var str = "";
-    while (space-- > 0)
-        str += " ";
-    return str;
-}
-
-print(tab(26) + "ACEY DUCEY CARD GAME\n");
-print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
-print("\n");
-print("\n");
+print(spaces(26) + "ACEY DUCEY CARD GAME\n");
+print(spaces(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n\n\n");
 print("ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER\n");
 print("THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP\n");
 print("YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING\n");
 print("ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE\n");
 print("A VALUE BETWEEN THE FIRST TWO.\n");
-print("IF YOU DO NOT WANT TO BET, INPUT A 0\n");
+print("IF YOU DO NOT WANT TO BET, INPUT '0'\n");
 
-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");
-}
+let currentMoney = 100;
+while (true) {
+  print(`YOU NOW HAVE ${currentMoney} DOLLARS.\n\n`);
 
-// 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;
-                } else {
-                    print("O.K., HOPE YOU HAD FUN!");
-                    break;
-                }
-            } else {
-                q = q - m;
-            }
+  let card1, card2, currentBet;
+  do {
+    print("HERE ARE YOUR NEXT TWO CARDS: \n");
+    [card1, card2] = [randomCard(), randomCard()];
+
+    // Ensure we always show cards in order of lowest to highest, and we never
+    // get two of the same card.
+    do {
+      card1 = randomCard();
+      card2 = randomCard();
+    } while (card1 >= card2);
+
+    printCard(card1);
+    printCard(card2);
+    print("\n");
+
+    while (true) {
+      print("\nWHAT IS YOUR BET? ");
+      currentBet = parseInt(await readLine(), 10);
+
+      if (currentBet > 0) {
+        if (currentBet > currentMoney) {
+          print("SORRY, MY FRIEND, BUT YOU BET TOO MUCH.\n");
+          print(`YOU HAVE ONLY ${currentMoney} DOLLARS TO BET.\n`);
+          continue;
         }
-    }
-}
+        break;
+      }
 
-main();
+      // Invalid bet value. Output an error message and reset to undefined to
+      // restart the loop with new cards.
+      currentBet = undefined;
+      print("CHICKEN!!\n");
+      print("\n");
+      break;
+    }
+  } while (currentBet === undefined);
+
+  const actualCard = randomCard();
+  print("\n\nHERE IS THE CARD WE DREW:\n")
+  printCard(actualCard);
+  print("\n\n");
+
+  if (actualCard > card1 && actualCard < card2) {
+    print("YOU WIN!!!\n");
+    currentMoney += currentBet;
+  } else {
+    print("SORRY, YOU LOSE\n");
+    if (currentBet < currentMoney) {
+      currentMoney -= currentBet;
+    } else {
+      print("\n\nSORRY, FRIEND, BUT YOU BLEW YOUR WAD.\n\n\n");
+      print("TRY AGAIN (YES OR NO)");
+      const tryAgain = await readLine();
+      print("\n\n");
+      if (tryAgain.toLowerCase() === "yes") {
+        currentMoney = 100;
+      } else {
+        print("O.K., HOPE YOU HAD FUN!");
+        break;
+      }
+    }
+  }
+}
diff --git a/01_Acey_Ducey/javascript/io.js b/01_Acey_Ducey/javascript/io.js
new file mode 100644
index 00000000..a9211e9c
--- /dev/null
+++ b/01_Acey_Ducey/javascript/io.js
@@ -0,0 +1,29 @@
+const outputEl = document.querySelector("#output");
+
+export function print(string) {
+  outputEl.append(string);
+}
+
+export function readLine() {
+  return new Promise(resolve => {
+    const inputEl = document.createElement("input");
+    outputEl.append(inputEl);
+    inputEl.focus();
+
+    inputEl.addEventListener("keydown", event => {
+      if (event.key === "Enter") {
+        const result = inputEl.value;
+        inputEl.remove();
+
+        print(result);
+        print("\n");
+
+        resolve(result);
+      }
+    });
+  });
+}
+
+export function spaces(numberOfSpaces) {
+  return " ".repeat(numberOfSpaces);
+}

From 73e3b4be2d550cdaa7ae11e6b1a589149a78acdd Mon Sep 17 00:00:00 2001
From: Jeff Atwood 
Date: Sat, 1 Jan 2022 13:20:35 -0800
Subject: [PATCH 06/18] Update README.md

---
 README.md | 2 ++
 1 file changed, 2 insertions(+)

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/)**.

From 5e0f7cf9ad8bde9b91c07791617645096b6dfcff Mon Sep 17 00:00:00 2001
From: bmshipe 
Date: Sat, 1 Jan 2022 17:05:51 -0600
Subject: [PATCH 07/18] C# implementation of Bullseye

---
 18_Bullseye/csharp/Bullseye.csproj |  10 ++
 18_Bullseye/csharp/Bullseye.sln    |  22 +++
 18_Bullseye/csharp/BullseyeGame.cs | 237 +++++++++++++++++++++++++++++
 18_Bullseye/csharp/Player.cs       |  28 ++++
 18_Bullseye/csharp/Program.cs      |  14 ++
 5 files changed, 311 insertions(+)
 create mode 100644 18_Bullseye/csharp/Bullseye.csproj
 create mode 100644 18_Bullseye/csharp/Bullseye.sln
 create mode 100644 18_Bullseye/csharp/BullseyeGame.cs
 create mode 100644 18_Bullseye/csharp/Player.cs
 create mode 100644 18_Bullseye/csharp/Program.cs

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();
+        }
+    }
+}

From 6935e24ae38d9faff890e4c2b83317360795aa3e Mon Sep 17 00:00:00 2001
From: doctorgraphics <10650289+doctorgraphics@users.noreply.github.com>
Date: Sat, 1 Jan 2022 15:20:20 -0800
Subject: [PATCH 08/18] Update aceyducey.py

Correct misspelled word "assement" to "assessment"  on line 177
---
 01_Acey_Ducey/python/aceyducey.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

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

From d6f9b8c68a077d9cb604ea6dbbbe4766bb80e17b Mon Sep 17 00:00:00 2001
From: ericries 
Date: Sat, 1 Jan 2022 17:04:12 -0800
Subject: [PATCH 09/18] Add files via upload

simple Python implementation of Tower - apologies for any errors, I only had a minute to dash this off
---
 tower.py | 159 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 159 insertions(+)
 create mode 100644 tower.py

diff --git a/tower.py b/tower.py
new file mode 100644
index 00000000..72c67a61
--- /dev/null
+++ b/tower.py
@@ -0,0 +1,159 @@
+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):
+        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)
\ No newline at end of file

From 5e2647682f1fde87702de7ea4f9786143172e47f Mon Sep 17 00:00:00 2001
From: ericries 
Date: Sat, 1 Jan 2022 17:05:56 -0800
Subject: [PATCH 10/18] Add files via upload

Simple python implementation. Apologies for any errors, I only had a minute to whip this up
---
 90_Tower/python/tower.py | 159 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 159 insertions(+)
 create mode 100644 90_Tower/python/tower.py

diff --git a/90_Tower/python/tower.py b/90_Tower/python/tower.py
new file mode 100644
index 00000000..72c67a61
--- /dev/null
+++ b/90_Tower/python/tower.py
@@ -0,0 +1,159 @@
+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):
+        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)
\ No newline at end of file

From fb7b7ffe0e7eea768951e31a3e876149f86803bf Mon Sep 17 00:00:00 2001
From: ericries 
Date: Sat, 1 Jan 2022 17:08:10 -0800
Subject: [PATCH 11/18] Delete tower.py

---
 tower.py | 159 -------------------------------------------------------
 1 file changed, 159 deletions(-)
 delete mode 100644 tower.py

diff --git a/tower.py b/tower.py
deleted file mode 100644
index 72c67a61..00000000
--- a/tower.py
+++ /dev/null
@@ -1,159 +0,0 @@
-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):
-        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)
\ No newline at end of file

From c2dc4c8b38977849ac3c933e1946c8c5ca09f628 Mon Sep 17 00:00:00 2001
From: LukasMurdock 
Date: Sat, 1 Jan 2022 20:09:24 -0500
Subject: [PATCH 12/18] update acey_ducey

---
 01_Acey_Ducey/javascript/.prettierrc.json |   6 +
 01_Acey_Ducey/javascript/aceyducey.html   |   8 +-
 01_Acey_Ducey/javascript/aceyducey.js     | 284 +++++++++++++++-------
 01_Acey_Ducey/javascript/io.js            |  29 ---
 01_Acey_Ducey/javascript/util.js          |  67 +++++
 5 files changed, 277 insertions(+), 117 deletions(-)
 create mode 100644 01_Acey_Ducey/javascript/.prettierrc.json
 delete mode 100644 01_Acey_Ducey/javascript/io.js
 create mode 100644 01_Acey_Ducey/javascript/util.js

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

-
+

+
diff --git a/01_Acey_Ducey/javascript/aceyducey.js b/01_Acey_Ducey/javascript/aceyducey.js
index 6c31d77c..61525a3d 100644
--- a/01_Acey_Ducey/javascript/aceyducey.js
+++ b/01_Acey_Ducey/javascript/aceyducey.js
@@ -1,100 +1,216 @@
-import { readLine, print, spaces } from "./io.js";
+// UTILITY VARIABLES
 
-const minFaceCard = 11;
-const faceCards = {
-  11: "JACK",
-  12: "QUEEN",
-  13: "KING",
-  14: "ACE"
-};
+// By default:
+// — Browsers have a window object
+// — Node.js does not
+// Checking for an undefined window object is a loose check
+// to enable browser and Node.js support
+const isRunningInBrowser = typeof window !== 'undefined';
 
-function randomCard() {
-  return Math.floor(Math.random() * 13 + 2);
+// To easily validate input strings with utility functions
+const validLowerCaseYesStrings = ['yes', 'y'];
+const validLowerCaseNoStrings = ['no', 'n'];
+const validLowerCaseYesAndNoStrings = [
+    ...validLowerCaseYesStrings,
+    ...validLowerCaseNoStrings,
+];
+// UTILITY VARIABLES
+
+// Function to get a random number (card) 2-14 (ACE is 14)
+function getRandomCard() {
+    // In our game, the value of ACE is greater than face cards;
+    // instead of having the value of ACE be 1, we’ll have it be 14.
+    // So, we want to shift the range of random numbers from 1-13 to 2-14
+    let min = 2;
+    let max = 14;
+    // Return random integer between two values, inclusive
+    return Math.floor(Math.random() * (max - min + 1) + min);
 }
 
-function printCard(card) {
-  if (card < minFaceCard) {
-    print(card);
-  } else {
-    print(faceCards[card]);
-  }
-  print("\n");
+function getGameCards() {
+    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 two cardTwo
+    // we will continue to generate random cards.
+    while (cardOne >= cardTwo) {
+        cardOne = getRandomCard();
+        cardTwo = getRandomCard();
+    }
+    return [cardOne, cardTwo, cardThree];
 }
 
-print(spaces(26) + "ACEY DUCEY CARD GAME\n");
-print(spaces(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n\n\n");
-print("ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER\n");
-print("THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP\n");
-print("YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING\n");
-print("ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE\n");
-print("A VALUE BETWEEN THE FIRST TWO.\n");
-print("IF YOU DO NOT WANT TO BET, INPUT '0'\n");
+// Function to get card value
+function getCardValue(card) {
+    let faceOrAce = {
+        11: 'JACK',
+        12: 'QUEEN',
+        13: 'KING',
+        14: 'ACE',
+    };
+    // If card value matches a key in faceOrAce, use faceOrAce value;
+    // Else, return undefined and handle with the Nullish Coalescing Operator (??)
+    // and default to card value.
+    let cardValue = faceOrAce[card] ?? card;
+    return cardValue;
+}
 
-let currentMoney = 100;
-while (true) {
-  print(`YOU NOW HAVE ${currentMoney} DOLLARS.\n\n`);
+print(spaces(26) + 'ACEY DUCEY CARD GAME');
+print(spaces(15) + 'CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n\n');
+print('ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER');
+print('THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP');
+print('YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING');
+print('ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE');
+print('A VALUE BETWEEN THE FIRST TWO.');
+print("IF YOU DO NOT WANT TO BET, INPUT '0'");
 
-  let card1, card2, currentBet;
-  do {
-    print("HERE ARE YOUR NEXT TWO CARDS: \n");
-    [card1, card2] = [randomCard(), randomCard()];
+main();
 
-    // Ensure we always show cards in order of lowest to highest, and we never
-    // get two of the same card.
-    do {
-      card1 = randomCard();
-      card2 = randomCard();
-    } while (card1 >= card2);
-
-    printCard(card1);
-    printCard(card2);
-    print("\n");
+async function main() {
+    let bet;
+    let availableDollars = 100;
 
+    // Loop game forever
     while (true) {
-      print("\nWHAT IS YOUR BET? ");
-      currentBet = parseInt(await readLine(), 10);
+        let [cardOne, cardTwo, cardThree] = getGameCards();
 
-      if (currentBet > 0) {
-        if (currentBet > currentMoney) {
-          print("SORRY, MY FRIEND, BUT YOU BET TOO MUCH.\n");
-          print(`YOU HAVE ONLY ${currentMoney} DOLLARS TO BET.\n`);
-          continue;
+        print(`YOU NOW HAVE ${availableDollars} DOLLARS.\n`);
+
+        print('HERE ARE YOUR NEXT TWO CARDS: ');
+        print(getCardValue(cardOne));
+        print(getCardValue(cardTwo));
+        print('');
+
+        // Loop until receiving a valid bet
+        let validBet = false;
+        while (!validBet) {
+            print('\nWHAT IS YOUR BET? ');
+            bet = parseInt(await input(), 10);
+            let minimumRequiredBet = 0;
+            if (bet > minimumRequiredBet) {
+                if (bet > availableDollars) {
+                    print('SORRY, MY FRIEND, BUT YOU BET TOO MUCH.');
+                    print(`YOU HAVE ONLY ${availableDollars} DOLLARS TO BET.`);
+                } else {
+                    validBet = true;
+                }
+            } else {
+                // Does not meet minimum required bet
+                print('CHICKEN!!');
+                print('');
+            }
         }
-        break;
-      }
 
-      // Invalid bet value. Output an error message and reset to undefined to
-      // restart the loop with new cards.
-      currentBet = undefined;
-      print("CHICKEN!!\n");
-      print("\n");
-      break;
+        print('\n\nHERE IS THE CARD WE DREW: ');
+        print(getCardValue(cardThree));
+
+        // Determine if player won or lost
+        if (cardThree > cardOne && cardThree < cardTwo) {
+            print('YOU WIN!!!');
+            availableDollars = availableDollars + bet;
+        } else {
+            print('SORRY, YOU LOSE');
+
+            if (bet >= availableDollars) {
+                print('');
+                print('');
+                print('SORRY, FRIEND, BUT YOU BLEW YOUR WAD.');
+                print('');
+                print('');
+                print('TRY AGAIN (YES OR NO)');
+
+                let tryAgainInput = await input();
+
+                print('');
+                print('');
+
+                if (isValidYesNoString(tryAgainInput)) {
+                    availableDollars = 100;
+                } else {
+                    print('O.K., HOPE YOU HAD FUN!');
+                    break;
+                }
+            } else {
+                availableDollars = availableDollars - bet;
+            }
+        }
     }
-  } while (currentBet === undefined);
-
-  const actualCard = randomCard();
-  print("\n\nHERE IS THE CARD WE DREW:\n")
-  printCard(actualCard);
-  print("\n\n");
-
-  if (actualCard > card1 && actualCard < card2) {
-    print("YOU WIN!!!\n");
-    currentMoney += currentBet;
-  } else {
-    print("SORRY, YOU LOSE\n");
-    if (currentBet < currentMoney) {
-      currentMoney -= currentBet;
-    } else {
-      print("\n\nSORRY, FRIEND, BUT YOU BLEW YOUR WAD.\n\n\n");
-      print("TRY AGAIN (YES OR NO)");
-      const tryAgain = await readLine();
-      print("\n\n");
-      if (tryAgain.toLowerCase() === "yes") {
-        currentMoney = 100;
-      } else {
-        print("O.K., HOPE YOU HAD FUN!");
-        break;
-      }
-    }
-  }
 }
+
+// UTILITY FUNCTIONS
+function isValidYesNoString(string) {
+    return validLowerCaseYesAndNoStrings.includes(string.toLowerCase());
+}
+
+function isValidYesString(string) {
+    return validLowerCaseYesStrings.includes(string.toLowerCase());
+}
+
+function isValidNoString(string) {
+    return validLowerCaseNoStrings.includes(string.toLowerCase());
+}
+
+function print(string) {
+    if (isRunningInBrowser) {
+        // Adds trailing newline to match console.log behavior
+        document
+            .getElementById('output')
+            .appendChild(document.createTextNode(string + '\n'));
+    } else {
+        console.log(string);
+    }
+}
+
+function input() {
+    if (isRunningInBrowser) {
+        // Accept input from the browser DOM input
+        return new Promise((resolve) => {
+            const outputElement = document.querySelector('#output');
+            const inputElement = document.createElement('input');
+            outputElement.append(inputElement);
+            inputElement.focus();
+
+            inputElement.addEventListener('keydown', (event) => {
+                if (event.key === 'Enter') {
+                    const result = inputElement.value;
+                    inputElement.remove();
+                    print(result);
+                    print('');
+                    resolve(result);
+                }
+            });
+        });
+    } else {
+        // Accept input from the command line in Node.js
+        // See: https://nodejs.dev/learn/accept-input-from-the-command-line-in-nodejs
+        return new Promise(function (resolve) {
+            const readline = require('readline').createInterface({
+                input: process.stdin,
+                output: process.stdout,
+            });
+            readline.question('', function (input) {
+                resolve(input);
+                readline.close();
+            });
+        });
+    }
+}
+
+function printInline(string) {
+    if (isRunningInBrowser) {
+        document
+            .getElementById('output')
+            .appendChild(document.createTextNode(string));
+    } else {
+        process.stdout.write(string);
+    }
+}
+
+function spaces(numberOfSpaces) {
+    return ' '.repeat(numberOfSpaces);
+}
+
+// UTILITY FUNCTIONS
diff --git a/01_Acey_Ducey/javascript/io.js b/01_Acey_Ducey/javascript/io.js
deleted file mode 100644
index a9211e9c..00000000
--- a/01_Acey_Ducey/javascript/io.js
+++ /dev/null
@@ -1,29 +0,0 @@
-const outputEl = document.querySelector("#output");
-
-export function print(string) {
-  outputEl.append(string);
-}
-
-export function readLine() {
-  return new Promise(resolve => {
-    const inputEl = document.createElement("input");
-    outputEl.append(inputEl);
-    inputEl.focus();
-
-    inputEl.addEventListener("keydown", event => {
-      if (event.key === "Enter") {
-        const result = inputEl.value;
-        inputEl.remove();
-
-        print(result);
-        print("\n");
-
-        resolve(result);
-      }
-    });
-  });
-}
-
-export function spaces(numberOfSpaces) {
-  return " ".repeat(numberOfSpaces);
-}
diff --git a/01_Acey_Ducey/javascript/util.js b/01_Acey_Ducey/javascript/util.js
new file mode 100644
index 00000000..418eff40
--- /dev/null
+++ b/01_Acey_Ducey/javascript/util.js
@@ -0,0 +1,67 @@
+// 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';
+
+const outputElement = document.querySelector('#output');
+
+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 inputEl = document.createElement('input');
+            outputElement.append(inputEl);
+            inputEl.focus();
+
+            inputEl.addEventListener('keydown', (event) => {
+                if (event.key === 'Enter') {
+                    const result = inputEl.value;
+                    inputEl.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);
+}

From 437b4c29205c2a5eb5a338215eeb9a5207130e1d Mon Sep 17 00:00:00 2001
From: ericries 
Date: Sat, 1 Jan 2022 17:26:04 -0800
Subject: [PATCH 13/18] Update tower.py

easier debugging with fewer disks
---
 90_Tower/python/tower.py | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/90_Tower/python/tower.py b/90_Tower/python/tower.py
index 72c67a61..5e4a25b7 100644
--- a/90_Tower/python/tower.py
+++ b/90_Tower/python/tower.py
@@ -60,7 +60,10 @@ GOOD LUCK!
 
 class Game:
     def __init__(self):
-        self.__sizes = [3, 5, 7]  # ,9,11,13,15]
+        # 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 = []
@@ -156,4 +159,4 @@ while True:
                 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)
\ No newline at end of file
+        sys.exit(0)

From 5c994e95621220a4caf31a22e8a33cf1bbccc934 Mon Sep 17 00:00:00 2001
From: LukasMurdock 
Date: Sat, 1 Jan 2022 20:45:19 -0500
Subject: [PATCH 14/18] typos

---
 01_Acey_Ducey/javascript/aceyducey.js |  6 +--
 01_Acey_Ducey/javascript/util.js      | 67 ---------------------------
 2 files changed, 3 insertions(+), 70 deletions(-)
 delete mode 100644 01_Acey_Ducey/javascript/util.js

diff --git a/01_Acey_Ducey/javascript/aceyducey.js b/01_Acey_Ducey/javascript/aceyducey.js
index 61525a3d..8bf5e9e1 100644
--- a/01_Acey_Ducey/javascript/aceyducey.js
+++ b/01_Acey_Ducey/javascript/aceyducey.js
@@ -27,14 +27,14 @@ function getRandomCard() {
     return Math.floor(Math.random() * (max - min + 1) + min);
 }
 
-function getGameCards() {
+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 two cardTwo
+    // So, while cardOne is greater than or equal too cardTwo
     // we will continue to generate random cards.
     while (cardOne >= cardTwo) {
         cardOne = getRandomCard();
@@ -75,7 +75,7 @@ async function main() {
 
     // Loop game forever
     while (true) {
-        let [cardOne, cardTwo, cardThree] = getGameCards();
+        let [cardOne, cardTwo, cardThree] = newGameCards();
 
         print(`YOU NOW HAVE ${availableDollars} DOLLARS.\n`);
 
diff --git a/01_Acey_Ducey/javascript/util.js b/01_Acey_Ducey/javascript/util.js
deleted file mode 100644
index 418eff40..00000000
--- a/01_Acey_Ducey/javascript/util.js
+++ /dev/null
@@ -1,67 +0,0 @@
-// 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';
-
-const outputElement = document.querySelector('#output');
-
-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 inputEl = document.createElement('input');
-            outputElement.append(inputEl);
-            inputEl.focus();
-
-            inputEl.addEventListener('keydown', (event) => {
-                if (event.key === 'Enter') {
-                    const result = inputEl.value;
-                    inputEl.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);
-}

From f0327b7615a5ceacee962b0b99ecc24fe99bbd29 Mon Sep 17 00:00:00 2001
From: Matthew Kuehn 
Date: Sat, 1 Jan 2022 20:02:28 -0600
Subject: [PATCH 15/18] Fix spelling and grammar.

---
 59_Lunar_LEM_Rocket/javascript/lem.js | 8 ++++----
 96_Word/javascript/word.js            | 6 +++---
 2 files changed, 7 insertions(+), 7 deletions(-)

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/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) {

From 355ac8ec8e3a356c59289e335a08d6fadd5a7c2c Mon Sep 17 00:00:00 2001
From: John Long 
Date: Sat, 1 Jan 2022 20:45:20 -0800
Subject: [PATCH 16/18] Add Kotlin for Tic-Tac-Toe

---
 89_Tic-Tac-Toe/kotlin/Board.kt      | 80 ++++++++++++++++++++++++
 89_Tic-Tac-Toe/kotlin/README.md     |  3 +
 89_Tic-Tac-Toe/kotlin/TicTacToe2.kt | 96 +++++++++++++++++++++++++++++
 3 files changed, 179 insertions(+)
 create mode 100644 89_Tic-Tac-Toe/kotlin/Board.kt
 create mode 100644 89_Tic-Tac-Toe/kotlin/README.md
 create mode 100644 89_Tic-Tac-Toe/kotlin/TicTacToe2.kt

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")
+        }
+    }
+}

From 39ba8f21a6a26b1c3f6f43d3e2c83f05eef32b46 Mon Sep 17 00:00:00 2001
From: John Long 
Date: Sat, 1 Jan 2022 21:21:39 -0800
Subject: [PATCH 17/18] Kotlin version of HiLow

---
 47_Hi-Lo/kotlin/HiLo.kt   | 50 +++++++++++++++++++++++++++++++++++++++
 47_Hi-Lo/kotlin/README.md |  3 +++
 2 files changed, 53 insertions(+)
 create mode 100644 47_Hi-Lo/kotlin/HiLo.kt
 create mode 100644 47_Hi-Lo/kotlin/README.md

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/)

From 07aab1b4913dd2f18c41db7bda0d1d36dfbef09f Mon Sep 17 00:00:00 2001
From: Pat Ludwig 
Date: Sat, 1 Jan 2022 23:33:18 -0600
Subject: [PATCH 18/18] Add perl version of Change

Used my own "perlified" logic instead of the original.
Added proper handling of plurals, other than that the
text should be identical
---
 22_Change/perl/change.pl | 68 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 68 insertions(+)
 create mode 100755 22_Change/perl/change.pl

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";
+  }
+}