diff --git a/.gitignore b/.gitignore index 2243de053..878190440 100644 --- a/.gitignore +++ b/.gitignore @@ -34,7 +34,9 @@ venv/ .DS_Store .vs/ **/target/ -Cargo.lock **/*.rs.bk /target todo.md + +.fake +.fake \ No newline at end of file diff --git a/00_Alternate_Languages/01_Acey_Ducey/MiniScript/README.md b/00_Alternate_Languages/01_Acey_Ducey/MiniScript/README.md new file mode 100644 index 000000000..06640e03b --- /dev/null +++ b/00_Alternate_Languages/01_Acey_Ducey/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript aceyducey.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "aceyducey" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/01_Acey_Ducey/MiniScript/aceyducey.ms b/00_Alternate_Languages/01_Acey_Ducey/MiniScript/aceyducey.ms new file mode 100644 index 000000000..c00684ce7 --- /dev/null +++ b/00_Alternate_Languages/01_Acey_Ducey/MiniScript/aceyducey.ms @@ -0,0 +1,59 @@ +print " "*26 + "Acey Ducey Card Game" +print " "*15 + "Creative Computing Morristown, New Jersey" +print +print +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 a 0." + +cards = range(2,10) + ["Jack", "Queen", "King", "Ace"] + +while true + money = 100 + + while true + print "You now have " + money + " dollars." + print + print "Here are your next two cards:" + while true + A = floor(rnd * cards.len) + B = floor(rnd * cards.len) + if B > A then break + end while + print cards[A] + print cards[B] + bet = input("What is your bet? ").val + while bet > money + print "Sorry, my friend, but you bet too much." + print "You have only " + money + " dollars to bet." + bet = input("What is your bet? ").val + end while + if bet == 0 then + print "Chicken!!" + continue + end if + C = floor(rnd * cards.len) + print cards[C] + + if C <= A or C >= B then + print "Sorry, you lose." + money -= bet + if money <= 0 then break + else + print "You win!!!" + money += bet + end if + end while + + print + print + print "Sorry, friend, but you blew your wad." + print; print + again = input("Try again (yes or no)? ").lower + if again and again[0] == "n" then break +end while + +print "O.K., hope you had fun!" \ No newline at end of file diff --git a/00_Alternate_Languages/01_Acey_Ducey/c++/build/x64/Debug/aceyducey.exe b/00_Alternate_Languages/01_Acey_Ducey/c++/build/x64/Debug/aceyducey.exe deleted file mode 100644 index a68757afd..000000000 Binary files a/00_Alternate_Languages/01_Acey_Ducey/c++/build/x64/Debug/aceyducey.exe and /dev/null differ diff --git a/00_Alternate_Languages/01_Acey_Ducey/c++/build/x64/Debug/aceyducey.pdb b/00_Alternate_Languages/01_Acey_Ducey/c++/build/x64/Debug/aceyducey.pdb deleted file mode 100644 index d33fbd15a..000000000 Binary files a/00_Alternate_Languages/01_Acey_Ducey/c++/build/x64/Debug/aceyducey.pdb and /dev/null differ diff --git a/00_Alternate_Languages/01_Acey_Ducey/c++/build/x64/Release/aceyducey.exe b/00_Alternate_Languages/01_Acey_Ducey/c++/build/x64/Release/aceyducey.exe deleted file mode 100644 index 6314b08fa..000000000 Binary files a/00_Alternate_Languages/01_Acey_Ducey/c++/build/x64/Release/aceyducey.exe and /dev/null differ diff --git a/00_Alternate_Languages/01_Acey_Ducey/c++/build/x64/Release/aceyducey.pdb b/00_Alternate_Languages/01_Acey_Ducey/c++/build/x64/Release/aceyducey.pdb deleted file mode 100644 index 569e58188..000000000 Binary files a/00_Alternate_Languages/01_Acey_Ducey/c++/build/x64/Release/aceyducey.pdb and /dev/null differ diff --git a/00_Alternate_Languages/01_Acey_Ducey/c++/build/x86/Debug/aceyducey.exe b/00_Alternate_Languages/01_Acey_Ducey/c++/build/x86/Debug/aceyducey.exe deleted file mode 100644 index 34d54fafc..000000000 Binary files a/00_Alternate_Languages/01_Acey_Ducey/c++/build/x86/Debug/aceyducey.exe and /dev/null differ diff --git a/00_Alternate_Languages/01_Acey_Ducey/c++/build/x86/Debug/aceyducey.pdb b/00_Alternate_Languages/01_Acey_Ducey/c++/build/x86/Debug/aceyducey.pdb deleted file mode 100644 index 100d8fbc3..000000000 Binary files a/00_Alternate_Languages/01_Acey_Ducey/c++/build/x86/Debug/aceyducey.pdb and /dev/null differ diff --git a/00_Alternate_Languages/01_Acey_Ducey/c++/build/x86/Release/aceyducey.exe b/00_Alternate_Languages/01_Acey_Ducey/c++/build/x86/Release/aceyducey.exe deleted file mode 100644 index 565711757..000000000 Binary files a/00_Alternate_Languages/01_Acey_Ducey/c++/build/x86/Release/aceyducey.exe and /dev/null differ diff --git a/00_Alternate_Languages/01_Acey_Ducey/c++/build/x86/Release/aceyducey.pdb b/00_Alternate_Languages/01_Acey_Ducey/c++/build/x86/Release/aceyducey.pdb deleted file mode 100644 index bba540221..000000000 Binary files a/00_Alternate_Languages/01_Acey_Ducey/c++/build/x86/Release/aceyducey.pdb and /dev/null differ diff --git a/00_Alternate_Languages/01_Acey_Ducey/nim/aceyducey.nim b/00_Alternate_Languages/01_Acey_Ducey/nim/aceyducey.nim new file mode 100644 index 000000000..abc03defc --- /dev/null +++ b/00_Alternate_Languages/01_Acey_Ducey/nim/aceyducey.nim @@ -0,0 +1,89 @@ +import std/[random,strutils] + +var + bet, cardA, cardB, cardC, stash: int + retry: bool = true + +randomize() # Seed the random number generator + +proc printGreeting() = + echo spaces(26),"ACEY DUCEY CARD GAME" + echo spaces(15),"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" + echo """ + +ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER +THE DEALER (COMPUTER) DEALS TWO CARDS FACE 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 + +""" + +proc printBalance() = + echo "YOU NOW HAVE ", stash," DOLLARS." + echo "" + +proc printCard(aCard: int) = + case aCard: + of 11: echo "=== JACK ===" + of 12: echo "=== QUEEN ===" + of 13: echo "=== KING ===" + of 14: echo "=== ACE ===" + else: echo "=== ", aCard, " ===" + +proc drawDealerCards() = + echo "HERE ARE YOUR NEXT TWO CARDS: " + cardA = rand 2..14 + cardB = cardA # Copy cardA, so we can test cardB to be different + while cardB == cardA: + cardB = rand 2..14 + if cardA > cardB: # Make sure cardA is the smaller card + swap cardA, cardB + echo "" + printCard cardA + echo "" + printCard cardB + echo "" + +proc drawPlayerCard() = + cardC = rand 2..14 + printCard cardC + echo "" + +proc getBet(): int = + result = stash + 1 #ensure we enter the loop + while (result < 0) or (result > stash): + echo "WHAT IS YOUR BET: " + result = readLine(stdin).parseInt() + if result > stash: + echo "SORRY, MY FRIEND, BUT YOU BET TOO MUCH." + echo "YOU HAVE ONLY ", stash, " DOLLARS TO BET." + if result == 0: + echo "CHICKEN!!" + +proc tryAgain(): bool = + echo "TRY AGAIN (YES OR NO)" + var answer = readLine(stdin).normalize() + result = (answer == "y") or (answer == "yes") + +printGreeting() +while retry: + stash = 100 + while stash > 0: + printBalance() + drawDealerCards() + bet = getBet() + echo "" + drawPlayerCard() + if (cardC >= cardA) and (cardC <= cardB): + echo "YOU WIN!!!" + stash += bet + else: + if bet > 0: + echo "SORRY, YOU LOSE" + stash -= bet + echo "SORRY, FRIEND, BUT YOU BLEW YOUR WAD." + echo "" + retry = tryAgain() +echo "O.K., HOPE YOU HAD FUN!" diff --git a/00_Alternate_Languages/02_Amazing/MiniScript/README.md b/00_Alternate_Languages/02_Amazing/MiniScript/README.md new file mode 100644 index 000000000..35d818ef5 --- /dev/null +++ b/00_Alternate_Languages/02_Amazing/MiniScript/README.md @@ -0,0 +1,21 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript amazing.ms +``` +Note that because this program imports "listUtil", you will need to have a the standard MiniScript libraries somewhere in your import path. + +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "amazing" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/02_Amazing/MiniScript/amazing.ms b/00_Alternate_Languages/02_Amazing/MiniScript/amazing.ms new file mode 100644 index 000000000..3c3589791 --- /dev/null +++ b/00_Alternate_Languages/02_Amazing/MiniScript/amazing.ms @@ -0,0 +1,108 @@ +import "listUtil" +print " "*28 + "Amazing Program" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print; print +while true + inp = input("What and your width and length? ") + inp = inp.replace(",", " ") + fields = inp.split + h = fields[0].val; v = fields[-1].val + if h > 1 and v > 1 then break + print "Meaningless dimensions. Try again." +end while + +// order: keeps track of the order in which each cell was +// visited as we built the maze. 0 means not explored yet. Indexed in [column][row] order. +// (This is W in the original BASIC program.) +order = list.init2d(h,v, 0) + +// walls: keeps track of the walls below and to the right of each cell: +// 0: walls below and to the right +// 1: wall to the right +// 2: wall below +// 3: neither wall +// (This is V in the original BASIC program.) +// Note that a wall to the right can be removed from a +// valid entry by adding 2; a wall below can be removed +// by adding 1. +walls = list.init2d(h,v, 0) +print +print +print +print + +// pick an exit at the top of the maze, +// and print the maze top +x = floor(rnd * h) +for i in range(0, h-1) + if i == x then print ". ","" else print ".--","" +end for +print "." + +// walk from our starting position (by the exit) around +// the maze, clearing a wall on each step +c = 1 // current step number +order[x][0] = c; c += 1 +r = x; s = 0 // [r][s] is our current position in the maze +while true + // collect the set of directions we can move in + dirs = [] + if r > 0 and order[r-1][s] == 0 then dirs.push "left" + if s > 0 and order[r][s-1] == 0 then dirs.push "up" + if r+1 < h and order[r+1][s] == 0 then dirs.push "right" + if s+1 < v and order[r][s+1] == 0 then dirs.push "down" + if not dirs then + //print "Uh-oh, I'm stuck at " + r + "," + s + // couldn't find any directions for this cell; + // find the next already-explored cell + while true + r += 1 + if r >= h then + r = 0 + s += 1 + if s >= v then s = 0 + end if + if order[r][s] != 0 then break + end while + continue + end if + + // pick a random available direction; move there, + // clearing the wall in between and updating order + d = dirs.any + if d == "left" then + walls[r-1][s] += 2 + r = r-1 + else if d == "up" then + walls[r][s-1] += 1 + s = s-1 + else if d == "right" then + walls[r][s] += 2 + r = r+1 + else if d == "down" then + walls[r][s] += 1 + s = s+1 + end if + + //print "At step " + c + ", at " + r + "," + s + order[r][s] = c + c += 1 + if c > h*v then break +end while + +// pick an exit at the bottom of the maze +x = floor(rnd * h) +walls[x][v-1] += 1 + +// print the (rest of the) maze +for j in range(0, v-1) + print "I", "" + for i in range(0, h-1) + if walls[i][j] < 2 then print " I", "" else print " ", "" + end for + print + for i in range(0, h-1) + if walls[i][j] % 2 == 0 then print ":--", "" else print ": ", "" + end for + print "." +end for diff --git a/00_Alternate_Languages/03_Animal/MiniScript/README.md b/00_Alternate_Languages/03_Animal/MiniScript/README.md new file mode 100644 index 000000000..1e48ea49c --- /dev/null +++ b/00_Alternate_Languages/03_Animal/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript animal.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "animal" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/03_Animal/MiniScript/animal.ms b/00_Alternate_Languages/03_Animal/MiniScript/animal.ms new file mode 100644 index 000000000..5aafdddb4 --- /dev/null +++ b/00_Alternate_Languages/03_Animal/MiniScript/animal.ms @@ -0,0 +1,90 @@ +print " "*32 + "Animal" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print +print "Play 'Guess the Animal'" +print +print "Think of an animal and the computer will try to guess it." +print + +// Ask a yes/no question, and return "Y" or "N". +getYesNo = function(prompt) + while true + inp = input(prompt + "? ").upper + if inp and (inp[0] == "Y" or inp[0] == "N") then return inp[0] + print "Please answer Yes or No." + end while +end function + +// Our data is stored as a list of little maps. +// Answers have only an "answer" key. +// Questions have a "question" key, plus "ifYes" and "ifNo" +// keys which map to the index of the next question or answer. +data = [ + {"question":"Does it swim", "ifYes":1, "ifNo":2}, + {"answer":"fish"}, + {"answer":"bird"}] + +// List all known animals. +listKnown = function + print; print "Animals I already know are:" + for item in data + if item.hasIndex("answer") then print (item.answer + " "*17)[:17], "" + end for + print; print +end function + +// Ask the question at curIndex, and handle the user's response. +doQuestion = function + q = data[curIndex] + if getYesNo(q.question) == "Y" then + globals.curIndex = q.ifYes + else + globals.curIndex = q.ifNo + end if +end function + +// Check the answer at curIndex. If incorrect, get a new question +// to put at that point in our data. +checkAnswer = function + node = data[curIndex] + inp = getYesNo("Is it a " + node.answer) + if inp == "Y" then + print "Why not try another animal?" + else + actual = input("The animal you were thinking of was a? ").lower + print "Please type in a question that would distinguish a" + print actual + " from a " + node.answer + q = {} + q.question = input + q.question = q.question[0].upper + q.question[1:] - "?" + data[curIndex] = q + k = data.len + data.push node // old answer at index k + data.push {"answer":actual} // new answer at index k+1 + if getYesNo("For a " + actual + " the answer would be") == "Y" then + data[curIndex].ifYes = k+1 + data[curIndex].ifNo = k + else + data[curIndex].ifNo = k+1 + data[curIndex].ifYes = k + end if + end if +end function + +// Main loop. (Press Control-C to break.) +while true + while true + inp = input("Are you thinking of an animal? ").upper + if inp == "LIST" then listKnown + if inp and inp[0] == "Y" then break + end while + curIndex = 0 + while true + if data[curIndex].hasIndex("question") then + doQuestion + else + checkAnswer + break + end if + end while +end while diff --git a/00_Alternate_Languages/04_Awari/MiniScript/README.md b/00_Alternate_Languages/04_Awari/MiniScript/README.md new file mode 100644 index 000000000..f2b6dfe50 --- /dev/null +++ b/00_Alternate_Languages/04_Awari/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript awari.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "awari" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/04_Awari/MiniScript/awari.ms b/00_Alternate_Languages/04_Awari/MiniScript/awari.ms new file mode 100644 index 000000000..e01c2d6cc --- /dev/null +++ b/00_Alternate_Languages/04_Awari/MiniScript/awari.ms @@ -0,0 +1,146 @@ +print " "*34 + "Awari" +print " "*15 + "Creative Computing Morristown, New Jersey" + +// Keep a list of the opening moves (up to 8) of previously lost or drawn games. +// These will be used to penalize repeating the same moves again. +badGames = [] + +printQty = function(qty) + print (" " + qty)[-2:], " " +end function + +printBoard = function + print; print " ", "" + for i in range(12, 7); printQty board[i]; end for + print; printQty board[13] + print " " * 6, ""; printQty board[6] + print; print " ", "" + for i in range(0, 5); printQty board[i]; end for + print; print +end function + +// Redistribute the stones starting at position. +// If the last one ends by itself, opposite some nonzero +// stones on the other side, then capture them into homePos. +// Return true if the last stone ended at homePos, false otherwise. +moveStones = function(board, position, homePos) + p = board[position]; board[position] = 0 + while p + position = (position + 1) % 14 + board[position] += 1 + p -= 1 + end while + if board[position] == 1 and position != 6 and position != 13 and board[12-position] then + board[homePos] += board[12-position] + 1 + board[position] = 0 + board[12-position] = 0 + end if + globals.gameOver = board[0:6].sum == 0 or board[7:13].sum == 0 + return position == homePos +end function + +// Get the player move. Note that the player inputs their move as a number +// from 1-6, but we return it as an actual board position index 0-6. +getPlayerMove = function(prompt="Your move") + while true + pos = input(prompt + "? ").val + if 0 < pos < 7 and board[pos-1] then return pos - 1 + print "Illegal move." + end while +end function + +getComputerMove = function + // Copy the board for safekeeping + boardCopy = board[:] + bestScore = -99; bestMove = 0 + for j in range(7, 12) + if board[j] == 0 then continue // can't move from an empty spot + // suppose we move at position j... + moveStones board, j, 13 + // consider each possible response the player could make + bestPlayerScore = 0 + for i in range(0, 5) + if board[i] == 0 then continue + landPos = board[i] + i // figure the landing position + score = floor(landPos / 14); landPos %= 14 + if board[landPos] == 0 and landPos != 6 and landPos != 13 then + score += board[12 - landPos] // points for capturing stones + end if + if score > bestPlayerScore then bestPlayerScore = score + end for + // figure our own score as our points, minus player points, minus best player score + ourScore = board[13] - board[6] - bestPlayerScore + if gameMoves.len < 8 then + // subtract 2 points if current series of moves is in our bad-games list + proposed = gameMoves + [j] + for badGame in badGames + if badGame[:proposed.len] == proposed then ourScore -= 2 + end for + end if + if ourScore > bestScore then + bestScore = ourScore + bestMove = j + end if + // restore the board + globals.board = boardCopy[:] + end for + print char(42+bestMove) // (labels computer spots as 1-6 from right to left) + return bestMove +end function + +// The game is over when either side has 0 stones left. +isGameOver = function + return board[0:6].sum == 0 or board[7:13].sum == 0 +end function + +// Play one game to completion. +playOneGame = function + // The board is represented as a list of 13 numbers. + // Position 6 is the player's home; 13 is the computer's home. + globals.board = [3]*14; board[13] = 0; board[6] = 0 + // Also keep a list of the moves in the current game + globals.gameMoves = [] + print; print + while true + // Player's turn + printBoard + pos = getPlayerMove + gameMoves.push pos + if moveStones(board, pos, 6) then + if gameOver then break + printBoard + pos = getPlayerMove("Again") + gameMoves.push pos + moveStones board, pos, 6 + end if + if gameOver then break + // Computer's turn + printBoard; print "My move is ", "" + pos = getComputerMove + gameMoves.push pos + if moveStones(board, pos, 13) then + if gameOver then break + printBoard; print "...followed by ", "" + pos = getComputerMove + gameMoves.push pos + moveStones board, pos, 13 + end if + if gameOver then break + end while + printBoard + print; print "GAME OVER" + delta = board[6] - board[13] + if delta < 0 then + print "I win by " + (-delta) + " points" + return + end if + if delta == 0 then print "Drawn game" else print "You win by " + delta + " points" + if gameMoves.len > 8 then gameMoves = gameMoves[:8] + if badGames.indexOf(gameMoves) == null then badGames.push gameMoves +end function + +// Main loop +while true + playOneGame + print; print +end while diff --git a/00_Alternate_Languages/05_Bagels/MiniScript/README.md b/00_Alternate_Languages/05_Bagels/MiniScript/README.md new file mode 100644 index 000000000..743d5160b --- /dev/null +++ b/00_Alternate_Languages/05_Bagels/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript bagels.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "bagels" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/05_Bagels/MiniScript/bagels.ms b/00_Alternate_Languages/05_Bagels/MiniScript/bagels.ms new file mode 100644 index 000000000..93b33b010 --- /dev/null +++ b/00_Alternate_Languages/05_Bagels/MiniScript/bagels.ms @@ -0,0 +1,83 @@ +print " "*33 + "BAGELS" +print " "*15 + "Creative Computing Morristown, New Jersey"; print; print +// *** BAGELS Number Guessing Game +// *** Original source unknown but suspected to be +// *** Lawrence Hall of Science, U.C. Berkely + +print; print; print +inp = input("Would you like the rules (yes or no)? ") +if not inp or inp[0].lower != "n" then + print; print "I am thinking of a three-digit number. Try to guess" + print "my number and I will give you clues as follows:" + print " PICO - one digit correct but in the wrong position" + print " FERMI - one digit correct and in the right position" + print " BAGELS - no digits correct" +end if + +pickNumber = function + // pick three unique random digits + while true + actual = [floor(10*rnd), floor(10*rnd), floor(10*rnd)] + if actual[0] != actual[1] and actual[0] != actual[2] and actual[1] != actual[2] then break + end while + //print "DEBUG: actual=" + actual + print; print "O.K. I have a number in mind." + return actual +end function + +getGuess = function(guessNum) + isNotDigit = function(c); return c < "0" or c > "9"; end function + while true + inp = input("Guess #" + guessNum + "? ") + if inp.len != 3 then + print "Try guessing a three-digit number." + else if inp[0] == inp[1] or inp[0] == inp[2] or inp[1] == inp[2] then + print "Oh, I forgot to tell you that the number I have in mind" + print "has no two digits the same." + else if isNotDigit(inp[0]) or isNotDigit(inp[1]) or isNotDigit(inp[2]) then + print "What?" + else + return [inp[0].val, inp[1].val, inp[2].val] + end if + end while +end function + +doOneGame = function + actual = pickNumber + for guessNum in range(1, 20) + guess = getGuess(guessNum) + picos = 0; fermis = 0 + for i in [0,1,2] + if guess[i] == actual[i] then + fermis += 1 + else if actual.indexOf(guess[i]) != null then + picos += 1 + end if + end for + if fermis == 3 then + print "YOU GOT IT!!!" + globals.score += 1 + return + else if picos or fermis then + print "PICO " * picos + "FERMI " * fermis + else + print "BAGELS" + end if + end for + print "Oh well." + print "That's twenty guesses. My number was " + actual.join("") +end function + +// main loop +score = 0 +while true + doOneGame + print + inp = input("Play again (yes or no)? ") + if not inp or inp[0].upper != "Y" then break +end while +if score then + print; print "A " + score + " point BAGELS buff!!" +end if +print "Hope you had fun. Bye." + diff --git a/00_Alternate_Languages/05_Bagels/nim/bagels.nim b/00_Alternate_Languages/05_Bagels/nim/bagels.nim new file mode 100644 index 000000000..f302b375d --- /dev/null +++ b/00_Alternate_Languages/05_Bagels/nim/bagels.nim @@ -0,0 +1,97 @@ +import std/[random,strutils] + +# BAGLES NUMBER GUESSING GAME +# ORIGINAL SOURCE UNKNOWN BUT SUSPECTED TO BE +# LAWRENCE HALL OF SCIENCE, U.C. BERKELY + +var + a, b: array[1..3, int] + wincount: int = 0 + prompt: string + stillplaying: bool = true + +randomize() # Seed the random number generator + +# Seed 3 unique random numbers; indicate if they're all unique +proc genSeed(): bool = + for i in 1..3: + a[i] = rand(0..9) + if (a[1] == a[2]) or (a[2] == a[3]) or (a[3] == a[1]): + return false + return true + +# Primary game logic +proc playGame() = + var youwin, unique: bool = false + # We want 3 unique random numbers: loop until we get them! + while unique == false: + unique = genSeed() + echo "O.K. I HAVE A NUMBER IN MIND." + for i in 1..20: + var c, d: int = 0 + echo "GUESS #", i + prompt = readLine(stdin).normalize() + if (prompt.len() != 3): + echo "TRY GUESSING A THREE-DIGIT NUMBER." + continue + for z in 1..3: + b[z] = prompt.substr(z-1, z-1).parseInt() # Convert string digits to array ints + if (b[1] == b[2]) or (b[2] == b[3]) or (b[3] == b[1]): + echo "OH, I FORGOT TO TELL YOU THAT THE NUMBER I HAVE IN MIND" + echo "HAS NO TWO DIGITS THE SAME." + # Figure out the PICOs + if (a[1] == b[2]): c += 1 + if (a[1] == b[3]): c += 1 + if (a[2] == b[1]): c += 1 + if (a[2] == b[3]): c += 1 + if (a[3] == b[1]): c += 1 + if (a[3] == b[2]): c += 1 + # Determine FERMIs + for j in 1..3: + if (a[j] == b[j]): d += 1 + # Reveal clues + if (d != 3): + if (c != 0): + for j in 1..c: + echo "PICO" + if (d != 0): + for j in 1..d: + echo "FERMI" + if (c == 0) and (d == 0): + echo "BAGELS" + # If we have 3 FERMIs, we win! + else: + echo "YOU GOT IT!!!" + echo "" + wincount += 1 + youwin = true + break + # Only invoke if we've tried 20 guesses without winning + if not youwin: + echo "OH WELL." + echo "THAT'S TWENTY GUESSES. MY NUMBER WAS ", a[1], a[2], a[3] + +# main program +echo spaces(33), "BAGELS" +echo spaces(15), "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" +echo "\n\n" +echo "WOULD YOU LIKE THE RULES (YES OR NO)" +prompt = readLine(stdin).normalize() +if prompt.substr(0, 0) == "y": + echo "I AM THINKING OF A THREE-DIGIT NUMBER. TRY TO GUESS" + echo "MY NUMBER AND I WILL GIVE YOU CLUES AS FOLLOWS:" + echo " PICO - ONE DIGIT CORRECT BUT IN THE WRONG POSITION" + echo " FERMI - ONE DIGIT CORRECT AND IN THE RIGHT POSITION" + echo " BAGELS - NO DIGITS CORRECT" + echo "" +while(stillplaying == true): + playGame() + echo "PLAY AGAIN (YES OR NO)" + prompt = readLine(stdin).normalize() + if prompt.substr(0, 0) != "y": + stillplaying = false +if wincount > 0: + echo "" + echo "A ", wincount, " POINT BAGELS BUFF!!" +echo "" +echo "HOPE YOU HAD FUN. BYE." diff --git a/00_Alternate_Languages/06_Banner/MiniScript/README.md b/00_Alternate_Languages/06_Banner/MiniScript/README.md new file mode 100644 index 000000000..25a241af9 --- /dev/null +++ b/00_Alternate_Languages/06_Banner/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript banner.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "banner" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/06_Banner/MiniScript/banner.ms b/00_Alternate_Languages/06_Banner/MiniScript/banner.ms new file mode 100644 index 000000000..d5d57e187 --- /dev/null +++ b/00_Alternate_Languages/06_Banner/MiniScript/banner.ms @@ -0,0 +1,89 @@ +blockWidth = input("Horizontal? ").val +if blockWidth <= 1 then blockWidth = 3 + +blockHeight = input("Vertical? ").val +if blockHeight <= 1 then blockHeight = 5 + +inp = input("Centered? ").upper +centered = inp and inp[0] > "P" + +printChar = input("Character (type 'all' if you want character being printed)? ") + +statement = input("Statement: ").upper + +//input("Set page") // <-- opportunity to set your pin-feed printer before proceeding! + +// Define the character data. For each character, we have 7 numbers +// which are the 9-bit binary representation of each row, plus one. +data = {} +data[" "] = [0,0,0,0,0,0,0] +data["!"] = [1,1,1,384,1,1,1] +data["?"] = [5,3,2,354,18,11,5] +data["."] = [1,1,129,449,129,1,1] +data["*"] = [69,41,17,512,17,41,69] +data["="] = [41,41,41,41,41,41,41] +data["0"] = [57,69,131,258,131,69,57] +data["1"] = [0,0,261,259,512,257,257] +data["2"] = [261,387,322,290,274,267,261] +data["3"] = [66,130,258,274,266,150,100] +data["4"] = [33,49,41,37,35,512,33] +data["5"] = [160,274,274,274,274,274,226] +data["6"] = [194,291,293,297,305,289,193] +data["7"] = [258,130,66,34,18,10,8] +data["8"] = [69,171,274,274,274,171,69] +data["9"] = [263,138,74,42,26,10,7] +data["A"] = [505,37,35,34,35,37,505] +data["B"] = [512,274,274,274,274,274,239] +data["C"] = [125,131,258,258,258,131,69] +data["D"] = [512,258,258,258,258,131,125] +data["E"] = [512,274,274,274,274,258,258] +data["F"] = [512,18,18,18,18,2,2] +data["G"] = [125,131,258,258,290,163,101] +data["H"] = [512,17,17,17,17,17,512] +data["I"] = [258,258,258,512,258,258,258] +data["J"] = [65,129,257,257,257,129,128] +data["K"] = [512,17,17,41,69,131,258] +data["L"] = [512,257,257,257,257,257,257] +data["M"] = [512,7,13,25,13,7,512] +data["N"] = [512,7,9,17,33,193,512] +data["O"] = [125,131,258,258,258,131,125] +data["P"] = [512,18,18,18,18,18,15] +data["Q"] = [125,131,258,258,322,131,381] +data["R"] = [512,18,18,50,82,146,271] +data["S"] = [69,139,274,274,274,163,69] +data["T"] = [2,2,2,512,2,2,2] +data["U"] = [128,129,257,257,257,129,128] +data["V"] = [64,65,129,257,129,65,64] +data["W"] = [256,257,129,65,129,257,256] +data["X"] = [388,69,41,17,41,69,388] +data["Y"] = [8,9,17,481,17,9,8] +data["Z"] = [386,322,290,274,266,262,260] + +for c in statement + if not data.hasIndex(c) then continue + + // Print character c in giant sideways banner-style! + for datum in data[c] + if datum then datum -= 1 // remove spurious +1 + if printChar.upper != "ALL" then c = printChar + + for lineRepeat in range(blockWidth-1) + if centered then print " " * (34 - 4.5*blockHeight), "" + + for bitPos in range(9,0) + if bitAnd(datum, 2^bitPos) then charToPrint=c else charToPrint=" " + print charToPrint * blockHeight, "" + end for // next bitPos + + print + wait 0.01 // put in a small pause so it's not too fast to see! + end for // next lineRepeat (repeating line according to entered Y value) + + end for // next datum (row of this character) + + // Add a little space after each character + for i in range(1, 2 * blockWidth) + print + wait 0.01 + end for +end for // next character in the message diff --git a/00_Alternate_Languages/07_Basketball/MiniScript/README.md b/00_Alternate_Languages/07_Basketball/MiniScript/README.md new file mode 100644 index 000000000..5958590e2 --- /dev/null +++ b/00_Alternate_Languages/07_Basketball/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript basketball.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "basketball" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/07_Basketball/MiniScript/basketball.ms b/00_Alternate_Languages/07_Basketball/MiniScript/basketball.ms new file mode 100644 index 000000000..d152cb105 --- /dev/null +++ b/00_Alternate_Languages/07_Basketball/MiniScript/basketball.ms @@ -0,0 +1,299 @@ +print " "*31 + "Basketball" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print +print "This is Dartmouth College basketball. You will be Dartmouth" +print " captain and playmaker. Call shots as follows: 1. Long" +print " (30 ft.) jump shot; 2. Short (15 ft.) jump shot; 3. Lay" +print " up; 4. Set shot." +print "Both teams will use the same defense. Call defense as" +print "follows: 6. Press; 6.5 Man-to-Man; 7. Zone; 7.5 None." +print "To change defense, just type 0 as your next shot." + +inputDefense = function(prompt) + while true + globals.defense = input(prompt).val + if defense >= 6 then break + end while +end function + +// Do the center jump; return US or THEM who gets the ball. +centerJump = function + print "Center Jump" + if rnd < 0.6 then + print opponent + " controls the tap." + return THEM + else + print "Dartmouth controls the tap." + return US + end if +end function + +inputShot = function + while true + globals.shotType = input("Your shot (0-4): ") + if shotType == "0" then + inputDefense "Your new defensive alignment is? " + continue + end if + globals.shotType = shotType.val + if 1 <= shotType <= 4 then return + end while +end function + +endFirstHalf = function + print " ***** End of first half *****"; print + print "Score: Dartmouth: " + score[US] + " " + opponent + ": " + score[THEM] + print; print + globals.inControl = centerJump +end function + +checkGameOver = function + print + if score[0] != score[1] then + print " ***** END OF GAME *****" + print "Final score: Dartmouth: " + score[US] + " " + opponent + ": " + score[THEM] + print + return true + else + print " ***** End of Second Half *****" + print "Score at end of regulation time:" + print " Dartmouth: " + score[US] + " " + opponent + ": " + score[THEM] + print + print "Begin two minute overtime period" + return false + end if +end function + +scoreBasket = function(who = null) + if who == null then who = inControl + score[who] += 2 + printScore +end function + +printScore = function + print "Score: " + score[1] + " to " + score[0] + print "Time: " + timer +end function + +// Logic for a Dartmouth jump shot. Return true to continue as Dartmouth, +// false to pass the ball to the opponent. +dartmouthJumpShot = function + if rnd <= 0.341 * defense / 8 then + print "Shot is good." + scoreBasket + return false + end if + + if rnd < 0.682 * defense / 8 then + print "Shot is off target." + if defense/6 * rnd > 0.45 then + print "Rebound to " + opponent + return false + end if + print "Dartmouth controls the rebound." + if rnd <= 0.4 then + globals.shotType = 3 + (rnd > 0.5) + return true + end if + if defense == 6 and rnd < 0.6 then + print "Pass stolen by " + opponent + ", easy layup." + scoreBasket THEM + return true + end if + print "Ball passed back to you." + return true + end if + + if rnd < 0.782 * defense/8 then + print "Shot is blocked. Ball controlled by ", "" + if rnd > 0.5 then + print opponent + "." + return false + else + print "Dartmouth." + return true + end if + end if + + if rnd > 0.843 * defense/8 then + print "Charging foul. Dartmouth loses ball." + else + print "Shooter is fouled. Two shots." + doFoulShots US + end if + return false +end function + +// Logic for an opponent jump shot. Return true to continue as opponent, +// false to pass the ball to Dartmouth. +opponentJumpShot = function + if rnd <= 0.35 * defense / 8 then + print "Shot is good." + scoreBasket + return false + end if + + if 8 / defense * rnd <= 0.75 then + print "Shot is off rim." + return opponentMissed + end if + + if 8 / defense * rnd <= 0.9 then + print "Player fouled. Two shots." + doFoulShots THEM + return false + end if + print "Offensive foul. Dartmouth's ball." + return false +end function + +// Do a Dartmouth set shot or lay-up. Return true to continue as Dartmouth, +// false to pass the ball to the opponent. +dartmouthSetOrLay = function + if 7 / defense * rnd <= 0.4 then + print "Shot is good. Two points." + scoreBasket + else if 7 / defense * rnd <= 0.7 then + print "Shot is off the rim." + if rnd < 0.667 then + print opponent + " controls the rebound." + return false + end if + print "Dartmouth controls the rebound." + if rnd < 0.4 then return true + print "Ball passed back to you." + return true + else if 7 / defense * rnd < 0.875 then + print "Shooter fouled. Two shots." + doFoulShots US + else if 7 / defense * rnd < 0.925 then + print "Shot blocked. " + opponent + "'s ball." + else + print "Charging foul. Dartmouth loses the ball." + end if + return false +end function + +// Do an opponent set shot or lay-up. Return true to continue as opponent, +// false to pass the ball to Dartmouth. +opponentSetOrLay = function + if 7 / defense * rnd <= 0.413 then + print "Shot is missed." + return opponentMissed + else + print "Shot is good." + scoreBasket + return false + end if +end function + +// Handle opponent missing a shot -- return true to continue as opponent, +// false to pass the ball to Dartmouth. +opponentMissed = function + if defense / 6 * rnd <= 0.5 then + print "Dartmouth controls the rebound." + return false + else + print opponent + " controls the rebound." + if defense == 6 and rnd <= 0.75 then + print "Ball stolen. Easy lay up for Dartmouth." + scoreBasket US + return true + end if + if rnd <= 0.5 then + print "Pass back to " + opponent + " guard." + return true + end if + globals.shotType = 4 - (rnd > 0.5) + return true + end if +end function + +playOneSide = function + print + while true + globals.timer += 1 + if timer == 50 then return endFirstHalf + if time == 92 then + print " *** Two minutes left in the game ***"; print + end if + if shotType == 1 or shotType == 2 then + print "Jump Shot" + if inControl == US then + if dartmouthJumpShot then continue else break + else + if opponentJumpShot then continue else break + end if + else // (if shot type >= 3) + if shotType > 3 then print "Set shot." else print "Lay up." + if inControl == US then + if dartmouthSetOrLay then continue else break + else + if opponentSetOrLay then continue else break + end if + end if + end while + globals.inControl = 1 - globals.inControl +end function + +doFoulShots = function(who) + if rnd > 0.49 then + if rnd > 0.75 then + print "Both shots missed." + else + print "Shooter makes one shot and misses one." + score[who] += 1 + end if + else + print "Shooter makes both shots." + score[who] += 2 + end if + printScore +end function + +opponentPlay = function + print + while true + globals.timer += 1 + if timer == 50 then return endFirstHalf + if time == 92 then + print " *** Two minutes left in the game ***"; print + end if + if shotType == 1 or shotType == 2 then + print "Jump Shot" + if opponentJumpShot then continue else break + else // (if shot type >= 3) + if shotType > 3 then print "Set shot." else print "Lay up." + if opponentSetOrLay then continue else break + end if + end while + globals.inControl = US +end function + +// Constants +THEM = 0 +US = 1 + +// Main program +inputDefense "Your starting defense will be? " +print +opponent = input("Choose your opponent? ") +score = [0,0] // score for each team (US and THEM) +gameOver = false +inControl = centerJump +timer = 0 +while not gameOver + print + if inControl == US then + inputShot + playOneSide + else + shotType = ceil(10 / 4 * rnd + 1) + playOneSide + end if + if timer >= 100 then + if checkGameOver then break + timer = 93 + dartmouthHasBall = centerJump + end if +end while diff --git a/00_Alternate_Languages/08_Batnum/MiniScript/README.md b/00_Alternate_Languages/08_Batnum/MiniScript/README.md new file mode 100644 index 000000000..b33a3f84d --- /dev/null +++ b/00_Alternate_Languages/08_Batnum/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript batnum.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "batnum" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/08_Batnum/MiniScript/batnum.ms b/00_Alternate_Languages/08_Batnum/MiniScript/batnum.ms new file mode 100644 index 000000000..d1019717b --- /dev/null +++ b/00_Alternate_Languages/08_Batnum/MiniScript/batnum.ms @@ -0,0 +1,109 @@ +print " "*33 + "Batnum" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print +print "This program is a 'Battle of Numbers' game, where the" +print "computer is your opponent." +print +print "The game starts with an assumed pile of objects. You" +print "and your opponent alternately remove objects from the pile." +print "Winning is defined in advance as taking the last object or" +print "not. You can also specify some other beginning conditions." +print "Don't use zero, however, in playing the game." +print "Enter a negative number for new pile size to stop playing." +print + +options = {} +getOptions = function + while true + options.pileSize = input("Enter pile size? ").val + if options.pileSize != 0 and options.pileSize == floor(options.pileSize) then break + end while + if options.pileSize < 0 then return + + while true + winOption = input("Enter win option - 1 to take last, 2 to avoid last: ") + if winOption == "1" or winOption == "2" then break + end while + options.takeLast = (winOption == "1") + + while true + minMax = input("Enter min and max? ").replace(",", " ").split + if minMax.len < 2 then continue + options.minTake = minMax[0].val + options.maxTake = minMax[-1].val + if options.minTake >= 1 and options.minTake < options.maxTake then break + end while + + while true + startOpt = input("Enter start option - 1 computer first, 2 you first: ") + if startOpt == "1" or startOpt == "2" then break + end while + options.computerFirst = (startOpt == "1") +end function + +computerTurn = function + // Random computer play (not in original program): + take = options.minTake + floor(rnd * (options.maxTake - options.minTake)) + + // Proper (smart) computer play + q = pile + if not options.takeLast then q -= 1 + take = q % (options.minTake + options.maxTake) + if take < options.minTake then take = options.minTake + if take > options.maxTake then take = options.maxTake + + if take >= pile then + if options.takeLast then + print "Computer takes " + pile + " and wins." + else + print "Computer takes " + pile + " and loses." + end if + globals.gameOver = true + else + globals.pile -= take + print "Computer takes " + take + " and leaves " + pile + end if +end function + +playerTurn = function + while true + print; take = input("Your move? ").val + if take == 0 then + print "I told you not to use zero! Computer wins by forfeit." + globals.gameOver = true + return + end if + if options.minTake <= take <= options.maxTake and take <= pile then break + print "Illegal move, reenter it" + end while + if take >= pile then + if options.takeLast then + print "Congratulations, you win." + else + print "Tough luck, you lose." + end if + globals.gameOver = true + else + globals.pile -= take + //print "You take " + take + ", leaving " + pile // (not in original program) + end if +end function + +while true + getOptions + if options.pileSize < 0 then break + pile = options.pileSize + gameOver = false + + print; print + + if options.computerFirst then computerTurn + while not gameOver + playerTurn + if gameOver then break + computerTurn + end while + + for i in range(1,10); print; end for +end while +print "OK, bye!" \ No newline at end of file diff --git a/00_Alternate_Languages/09_Battle/MiniScript/README.md b/00_Alternate_Languages/09_Battle/MiniScript/README.md new file mode 100644 index 000000000..07cd98272 --- /dev/null +++ b/00_Alternate_Languages/09_Battle/MiniScript/README.md @@ -0,0 +1,21 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +NOTE: One feature has been added to the original game. At the "??" prompt, instead of entering coordinates, you can enter "?" (a question mark) to reprint the fleet disposition code. + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript battle.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "battle" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/09_Battle/MiniScript/battle.ms b/00_Alternate_Languages/09_Battle/MiniScript/battle.ms new file mode 100644 index 000000000..e082b25b5 --- /dev/null +++ b/00_Alternate_Languages/09_Battle/MiniScript/battle.ms @@ -0,0 +1,157 @@ +print " "*33 + "Battle" +print " "*15 + "Creative Computing Morristown, New Jersey" +// -- BATTLE WRITTEN BY RAY WESTERGARD 10/70 +// COPYRIGHT 1971 BY THE REGENTS OF THE UNIV. OF CALIF. +// PRODUCED AT THE LAWRENCE HALL OF SCIENCE, BERKELEY +// Converted to MiniScript by Joe Strout, July 2023. + +import "listUtil" + +setup = function + // prepare the shield with the bad guys' ships + globals.field = list.init2d(6,6, 0) // matrix of enemy ships -- 0th row/column unused + for shipType in [1,2,3] + for ship in [1,2] + while not addShip(shipType, ship) + // keep trying until we successfully add it! + end while + end for + end for + + // prepare another matrix to keep track of where we have hit + globals.hits = list.init2d(6,6, 0) + + // and some some info per ship (ID) and ship type + globals.shipHitsLeft = [null, 2, 2, 3, 3, 4, 4] // hits left till each ship is sunk + globals.sunkPerType = [0] * 3 // counts how many of each type were sunk + + // finally, keep track of hits and splashes + globals.splashCount = 0 + globals.hitCount = 0 +end function + +// Try to add the given ship to field. +// Return true on success, false on failure. +addShip = function(shipType, shipNum) + size = 5 - shipType + x = floor(rnd * 6) + y = floor(rnd * 6) + direction = floor(rnd * 4) + if direction == 0 then + dx = 1; dy = 0 + else if direction == 1 then + dx = 1; dy = 1 + else if direction == 2 then + dx = 0; dy = 1 + else + dx = -1; dy = 1 + end if + // First make sure our placement doesn't go out of bounds + if not (0 <= x + dx * size <= 5) then return false + if not (0 <= y + dy * size <= 5) then return false + // Then, make sure it's not blocked by another ship + for i in range(0, size-1) + px = x + dx * i // (point under consideration) + py = y + dy * i + // make sure where we want to place the ship is still empty + if field[px][py] then return false + // if placing a ship diagonally, don't allow it to cross + // another ship on the other diagonal + if i > 0 and dx and dy then + adjacent1 = field[px-dx][py] + adjacent2 = field[px][py-dy] + if adjacent1 and adjacent1 == adjacent2 then return false + end if + end for + // Looks like it's all clear, so fill it in! + id = 9 - 2 * shipType - shipNum; + for i in range(0, size-1) + field[x + dx * i][y + dy * i] = id + end for + return true +end function + +// Print the "encoded" fleet disposition. This is just the regular field, +// but rotated and flipped. +printField = function + print + print "The following code of the bad guys' fleet disposition" + print "has been captured but not decoded:" + print + for i in range(0,5) + for j in range(0,5) + print field[5-j][i], " " + end for + print + end for +end function + +doOneTurn = function + while true + xy = input("??").replace(",", " ") + if xy == "?" then; printField; continue; end if // (not in original game) + x = xy[0].val; y = xy[-1].val + if xy.len < 2 or x != floor(x) or x < 1 or x > 6 or y != floor(y) or y < 1 or y > 6 then + print "Invalid input. Try again." + continue + end if + break + end while + row = x - 1 // (minus one since our matrix is 0-based instead of 1-based) + col = y - 1 + shipId = field[row][col] + if shipId == 0 then + // fall through to 'end if' below + else if shipHitsLeft[shipId] == 0 then + print "There used to be a ship at that point, but you sank it." + else if hits[row][col] then + print "You already put a hole in ship number " + shipId + " at that point." + else + hits[row][col] = shipId + print "A direct hit on ship number " + shipId + globals.hitCount += 1 + shipHitsLeft[shipId] -= 1 + if shipHitsLeft[shipId] == 0 then + shipType = floor((shipId-1) / 2) // get ship type, 0-2 + sunkPerType[shipType] += 1 + print "And you sunk it. Hurray for the good guys." + print "So far, the bad guys have lost" + print sunkPerType[0] + " destroyer(s), " + sunkPerType[1] + + " cruiser(s), and " + sunkPerType[2] + " aircraft carrier(s)." + print "Your current splash/hit ratio is " + (splashCount / hitCount) + end if + return + end if + print "Splash! Try again." + globals.splashCount += 1 +end function + +playOneGame = function + setup + printField + print + print "De-code it and use it if you can" + print "but keep the de-coding method a secret." + print + print "START GAME" + + while true + doOneTurn + if sunkPerType.sum == 6 then break + end while + + print + print "You have totally wiped out the bad guys' fleet" + print "with a final splash/hit ratio of " + (splashCount / hitCount) + if splashCount == 0 then + print "Congratulations -- a direct hit every time." + end if +end function + +// Main loop +while true + playOneGame + print + print "****************************" + print +end while diff --git a/00_Alternate_Languages/10_Blackjack/MiniScript/README.md b/00_Alternate_Languages/10_Blackjack/MiniScript/README.md new file mode 100644 index 000000000..2f68bb794 --- /dev/null +++ b/00_Alternate_Languages/10_Blackjack/MiniScript/README.md @@ -0,0 +1,21 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript blackjack.ms +``` +But note that the current release (1.2.1) of command-line MiniScript does not properly flush the output buffer when line breaks are suppressed, as this program does when prompting for your next action after a Hit. So, method 2 (below) is recommended for now. + +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "blackjack" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/10_Blackjack/MiniScript/blackjack.ms b/00_Alternate_Languages/10_Blackjack/MiniScript/blackjack.ms new file mode 100644 index 000000000..f6d7959b7 --- /dev/null +++ b/00_Alternate_Languages/10_Blackjack/MiniScript/blackjack.ms @@ -0,0 +1,285 @@ +import "listUtil" +print " "*31 + "Black Jack" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print + +fna = function(q); return q - 11*(q >= 22); end function +hands = [[]]*15 // hands(i,j) is the jth card in hand i (P) +handValue = [0]*15 // total value of each hand (Q) +deck = [] // the deck being dealt from (C) +playerMoney = [0]*8 // total $ for each player (T) +roundWinnings = [0]*7 // total $ won/lost this hand for each player (S) +betPerHand = [0]*15 // bet for each hand (B) + +cardNames = " A 2 3 4 5 6 7 8 9 10 J Q K".split + +reshuffle = function + print "Reshuffling" + globals.deck = range(1,13)*4 + deck.shuffle +end function + +// Function to draw a card from the deck (reshuffling if needed) +getCard = function + if not deck then reshuffle + return deck.pop +end function + +// Function to get a name for the given card, preceded by "a" or "an" +a = function(cardNum) + article = "a" + "n" * (cardNum == 1 or cardNum == 8) + return article + " " + cardNames[cardNum] +end function + +// Function to evaluate the given hand. Total is usually put into +// handValue(handNum). Totals have the following meaning: +// 2-10...Hard 2-10 +// 11-21...Soft 11-21 +// 22-32...Hard 11-21 +// 33+ or -1...Busted +evalHand = function(handNum) + result = 0 + for card in hands[handNum] + result = addCardToTotal(result, card) + end for + return result +end function + +// Function to add a card into a total hand value. +addCardToTotal = function(total, card) + if total < 0 then return total // (already busted) + x1 = card; if x1 > 10 then x1 = 10 + q1 = total + x1 + if total < 11 then + if card == 1 then return total + 11 // (ace) + return q1 + 11 * (q1 >= 11) + end if + total = q1 + (total <= 21 and q1 > 21) + if total >= 33 then total = -1 + return total +end function + +// Print one of those totals as it should appear to the user. +displayTotal = function(total) + return total - 11 * (total >= 22) +end function + +// Get a yes/no response from the user +getYesNo = function(prompt) + while true + inp = input(prompt).upper + if inp and (inp[0] == "Y" or inp[0] == "N") then return inp[0] + end while +end function + +// Get a number, within a given range, from the user +getNumber = function(prompt, minVal=0, maxVal=500) + while true + result = input(prompt).val + if result == floor(result) and minVal <= result <= maxVal then return result + print "Please enter a number from " + minVal + " to " + maxVal + end while +end function + +// Get one of a list of one-letter (uppercase) options. +getOption = function(prompt, options) + while true + result = input(prompt).upper + if result and options.indexOf(result[0]) != null then return result[0] + print "Type " + options[:-1].join(", ") + " or " + options[-1] + " please" + end while +end function + +getBets = function + print "Bets:" + for i in range(0, numPlayers-1) + betPerHand[i] = getNumber("# " + (i+1) + "? ", 1, 500) + end for +end function + +playOneRound = function + globals.roundWinnings = [0] * numPlayers + if deck.len < (numPlayers+1) * 2 then reshuffle + getBets + print "PLAYER ", "" + for i in range(0, numPlayers-1) + print i+1, " " + hands[i] = [] + end for + print "DEALER" + hands[numPlayers] = [] + for row in [1,2] + print " ", "" + for i in range(0, numPlayers) + hands[i].push getCard + if row == 1 or i < numPlayers then + print " " + (cardNames[hands[i][-1]] + " ")[:2], " " + end if + end for + print + end for + dealerCard0 = hands[numPlayers][0] + dealerCard1 = hands[numPlayers][1] + // Test for insurance + if dealerCard0 == 1 and getYesNo("Any insurance? ") == "Y" then + print "Insurance Bets" + for i in range(0, numPlayers-1) + insurance = getNumber("# " + (i+1) + "? ", 0, betPerHand[i]/2) + roundWinnings[i] = insurance * (3 * (dealerCard1 >= 10) - 1) + end for + end if + // Test for dealer blackjack + if (dealerCard0==1 and dealerCard1 > 9) or + (dealerCard0 > 9 and dealerCard1==1) then + print; print "Dealer has " + a(dealerCard1) + " in the hole for Blackjack" + for i in range(0, numPlayers) + handValue[i] = evalHand(i) + end for + else + // no dealer blackjack + if dealerCard0 == 1 or dealerCard0 >= 10 then + print; print "No dealer Blackjack." + end if + // now play the hands + for i in range(0, numPlayers-1) + playHand i + end for + handValue[numPlayers] = evalHand(numPlayers) // (evaluate dealer hand) + // Test for playing the dealer's hand... we only do so if + // there are any player hands with cards left. + anyLeft = false + for i in range(0, numPlayers-1) + if hands[i] or hands[i+8] then anyLeft = true + end for + if not anyLeft then + print "Dealer had " + a(hands[numPlayers][1]) + " concealed." + else + // Play dealer's hand. + dispTotal = displayTotal(handValue[numPlayers]) + print "Dealer has " + a(hands[numPlayers][1]) + " concealed" + + " for a total of " + dispTotal + "." + while handValue[numPlayers] > 0 and dispTotal <= 16 + card = getCard + if hands[numPlayers].len == 2 then print "Draws ", "" + print cardNames[card], " " + hands[numPlayers].push card + handValue[numPlayers] = evalHand(numPlayers) + dispTotal = displayTotal(handValue[numPlayers]) + end while + if hands[numPlayers].len > 2 then + if handValue[numPlayers] < 0 then print " ---Busted" else print " ---Total is " + dispTotal + end if + print + end if + end if + tallyResults +end function + +playHand = function(handNum, prompt=null, allowSplit=true) + if not prompt then prompt = "Player " + (handNum % 8 + 1) + while hands[handNum] + options = ["H", "S", "D"] + ["/"] * allowSplit + choice = getOption(prompt + "? ", options) + if choice == "S" then // player wants to stand + handValue[handNum] = evalHand(handNum) + if handValue[handNum] == 21 and hands[handNum].len == 2 then + print "Blackjack" + roundWinnings[handNum] += 1.5 * betPerHand[handNum] + betPerHand[handNum] = 0 + discardHand handNum + else + print "Total is " + displayTotal(handValue[handNum]) + end if + break + else if choice == "D" or choice == "H" then // hit or double down + handValue[handNum] = evalHand(handNum) + if choice == "D" then betPerHand[handNum] *= 2 + card = getCard + print "Received " + a(card), " " + hands[handNum].push card + handValue[handNum] = evalHand(handNum) + if handValue[handNum] < 0 then + print "...Busted" + discardHand handNum + roundWinnings[handNum] = -betPerHand[handNum] + betPerHand[handNum] = 0 + end if + prompt = "Hit" + if choice == "D" then; print; break; end if + else if choice == "/" then // split + card1 = hands[handNum][0]; if card1 > 10 then card1 = 10 + card2 = hands[handNum][1]; if card2 > 10 then card2 = 10 + if card1 != card2 then + print "Splitting not allowed." + continue + end if + hand2 = handNum + 8 + hands[hand2] = [hands[handNum].pop] + betPerHand[hand2] = betPerHand[handNum] + card = getCard + print "First hand receives " + a(card) + hands[handNum].push card + card = getCard + print "Second hand receives " + a(card) + hands[hand2].push card + if card1 != 1 then + // Now play the two hands + playHand handNum, "Hand 1", false + playHand hand2, "Hand 2", false + end if + break + end if + allowSplit = false + end while +end function + +discardHand = function(handNum) + hands[handNum] = [] + handValue[handNum] = 0 +end function + +tallyResults = function + dealerTotal = displayTotal(evalHand(numPlayers)) + for i in range(0, numPlayers-1) + playerHandATotal = displayTotal(evalHand(i)) + playerHandBTotal = displayTotal(evalHand(i+8)) + // calculate roundWinnings[i], which is the $ won/lost for player i + roundWinnings[i] = roundWinnings[i] + betPerHand[i]*sign(playerHandATotal - dealerTotal) + betPerHand[i+8]*sign(playerHandBTotal - dealerTotal) + betPerHand[i+8] = 0 + s = "Player " + (i+1) + " " + s += ["loses", "pushes", "wins"][sign(roundWinnings[i])+1] + if roundWinnings[i] != 0 then s += " " + abs(roundWinnings[i]) + playerMoney[i] += roundWinnings[i] + playerMoney[numPlayers] -= roundWinnings[i] + s = (s + " "*25)[:25] + "Total = " + playerMoney[i] + print s + discardHand i + discardHand i+8 + end for + print "Dealer's total = " + playerMoney[numPlayers] + print +end function + +// Main program starts here + +if getYesNo("Do you want instructions? ") == "Y" then + print "This is the game of 21. As many as 7 players may play the" + print "game. On each deal, bets will be asked for, and the" + print "players' bets should be typed in. The cards will then be" + print "dealt, and each player in turn plays his hand. The" + print "first response should be either 'D', indicating that the" + print "player is doubling down, 'S', indicating that he is" + print "standing, 'H', indicating he wants another card, or '/'," + print "indicating that he wants to split his cards. After the" + print "initial response, all further responses should be 'S' or" + print "'H', unless the cards were split, in which case doubling" + print "down is again permitted. In order to collect for" + print "blackjack, the initial response should be 'S'." + print +end if +numPlayers = getNumber("Number of players? ", 1, 7) +print +// main loop! +while true + playOneRound +end while diff --git a/00_Alternate_Languages/11_Bombardment/MiniScript/README.md b/00_Alternate_Languages/11_Bombardment/MiniScript/README.md new file mode 100644 index 000000000..4a399a7bd --- /dev/null +++ b/00_Alternate_Languages/11_Bombardment/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript bombardment.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "bombardment" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/11_Bombardment/MiniScript/bombardment.ms b/00_Alternate_Languages/11_Bombardment/MiniScript/bombardment.ms new file mode 100644 index 000000000..a6f75009b --- /dev/null +++ b/00_Alternate_Languages/11_Bombardment/MiniScript/bombardment.ms @@ -0,0 +1,101 @@ +print " "*33 + "Bombardment" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print +print "You are on a battlefield with 4 platoons and you" +print "have 25 outposts available where they may be placed." +print "You can only place one platoon at any one outpost." +print "The computer does the same with its four platoons." +print +print "The object of the game is to fire missiles at the" +print "outposts of the computer. It will do the same to you." +print "The one who destroys all four of the enemy's platoons" +print "first is the winner." +print +print "Good luck... and tell us where you want the bodies sent!" +print +input "(Press Return.)" // (so user can read the above) +print +print "Tear off matrix and use it to check off the numbers." +for i in range(1,5); print; end for +memory = [] // records computer's guesses +for row in range(1,5) + for i in range(row*5-4, row*5) + print (" " + i)[-6:], "" + end for + print +end for +print + +// Define a helper function to pick a random position (1-25) +// that is not already in the given list. +pickOutpost = function(excludingThese) + while true + pick = floor(rnd * 25) + 1 + if excludingThese.indexOf(pick) == null then return pick + end while +end function + +// Choose the computer's four positions. +computerOutposts = [] +for i in range(1,4) + computerOutposts.push pickOutpost(computerOutposts) +end for + +playerOutposts = [] +while playerOutposts.len != 4 + inp = input("What are your four positions? ") + inp = inp.replace(", ", " ").replace(",", " ") + inp = inp.split + for pos in inp + pos = pos.val + playerOutposts.push pos + if pos < 1 or pos > 25 then playerOutposts=[] + end for +end while + +// Main loop. +while true + // player's attack + pos = input("Where do you wish to fire your missile? ").val + if computerOutposts.indexOf(pos) == null then + print "Ha, ha you missed. My turn now:" + else + print "You got one of my outposts!" + computerOutposts.remove computerOutposts.indexOf(pos) + left = computerOutposts.len + if left == 3 then + print "One down, three to go." + else if left == 2 then + print "Two down, two to go." + else if left == 3 then + print "Three down, one to go." + else + print "You got me, I'm going fast. ButI'll get you when" + print "My transisto&s recup%ra*e!" + break + end if + end if + + // computer's attack + pos = pickOutpost(memory) + memory.push pos + if playerOutposts.indexOf(pos) == null then + print "I missed you, you dirty rat. I picked " + pos + ". Your turn:" + else + playerOutposts.remove playerOutposts.indexOf(pos) + left = playerOutposts.len + if left == 0 then + print "You're dead. Your last outpost was at " + pos + ". Ha, ha, ha." + print "Better luck next time." + break + end if + print "I got you. It won't be long now. Post " + pos + " was hit." + if left == 3 then + print "You have only three outposts left." + else if left == 2 then + print "You have only two outposts left." + else if left == 1 then + print "You have only one outpost left." + end if + end if +end while diff --git a/00_Alternate_Languages/12_Bombs_Away/MiniScript/README.md b/00_Alternate_Languages/12_Bombs_Away/MiniScript/README.md new file mode 100644 index 000000000..22bcc0b1a --- /dev/null +++ b/00_Alternate_Languages/12_Bombs_Away/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript bombsaway.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "bombsaway" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/12_Bombs_Away/MiniScript/bombsaway.ms b/00_Alternate_Languages/12_Bombs_Away/MiniScript/bombsaway.ms new file mode 100644 index 000000000..ea06ea48d --- /dev/null +++ b/00_Alternate_Languages/12_Bombs_Away/MiniScript/bombsaway.ms @@ -0,0 +1,112 @@ + +getNum = function(prompt, maxVal=5) + while true + num = floor(input(prompt + "? ").val) + if num > 0 and num <= maxVal then return num + print "Try again..." + end while +end function + +showReturn = function + print "You made it through tremendous flak!!" +end function + +showShotDown = function + print "* * * * boom * * * *" + print "You have been shot down....." + print "Dearly beloved, we are gathered here today to pay our" + print "last tribute..." +end function + +showSuccess = function + print "Direct hit!!!! " + floor(100*rnd) + " killed." + print "Mission successful." +end function + +// Function to calculate the mission result for all nations except Japan. +doNonJapanResult = function + print + d = input("How many missions have you flown? ").val + while d >= 160 + print "Missions, not miles..." + print "150 missions is high even for old-timers." + d = input("Now then, how many missions have you flown? ").val + end while + print + if d >= 100 then print "That's pushing the odds!" + if d < 25 then print "Fresh out of training, eh?" + print + if d >= 160 * rnd then + showSuccess + else + print "Missed target by " + floor(2+30*rnd) + " miles!" + print "Now you're really in for it !!"; print + r = getNum("Does the enemy have guns(1), missiles(2), or both(3)") + print + if r != 2 then + s = input("What's the percent hit rate of enemy gunners (10 to 50)? ").val + if s<10 then + print "You lie, but you'll pay..." + showShotDown + return + end if + end if + print + print + if r > 1 then t = 35 else t = 0 + if s + t > 100 * rnd then + showShotDown + else + showReturn + end if + end if +end function + +s = 0 // hit rate of enemy gunners +r = 0 // whether enemy has guns(1), missiles(2), or both(3) + +// Main Loop +while true + print "You are a pilot in a World War II bomber." + a = getNum("What side -- Italy(1), Allies(2), Japan(3), Germany(4)", 4) + + if a == 1 then // Italy + b = getNum("Your target -- Albania(1), Greece(2), North Africa(3)") + print + print ["Should be easy -- you're flying a nazi-made plane.", + "Be careful!!!", "You're going for the oil, eh?"][b-1] + doNonJapanResult + + else if a == 2 then // Allies + g = getNum("Aircraft -- Liberator(1), B-29(2), B-17(3), Lancaster(4)", 4) + print ["You've got 2 tons of bombs flying for Ploesti.", + "You're dumping the A-bomb on Hiroshima.", + "You're chasing the Aismark in the North Sea.", + "You're busting a German heavy water plant in the Ruhr."][g-1] + doNonJapanResult + + else if a == 3 then // Japan (different logic than all others) + print "You're flying a kamikaze mission over the USS Lexington." + isFirst = input("Your first kamikaze mission(y or n)? ").lower + if isFirst and isFirst[0] == "n" then + s = 0 + showReturn + else + print + if rnd > 0.65 then showSuccess else showShotDown + end if + + else // Germany + m = getNum("A nazi, eh? Oh well. Are you going for Russia(1)," + + char(13) + "England(2), or France(3)") + print ["You're nearing Stalingrad.", + "Nearing London. Be careful, they've got radar.", + "Nearing Versailles. Duck soup. They're nearly defenseless."][m-1] + doNonJapanResult + end if + + print; print; print; another = input("Another mission (y or n)? ").lower + if not another or another[0] != "y" then + print "Chicken !!!" ; print ; break + end if +end while diff --git a/00_Alternate_Languages/13_Bounce/MiniScript/README.md b/00_Alternate_Languages/13_Bounce/MiniScript/README.md new file mode 100644 index 000000000..e7e8f23a7 --- /dev/null +++ b/00_Alternate_Languages/13_Bounce/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript bounce.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "bounce" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/13_Bounce/MiniScript/bounce.ms b/00_Alternate_Languages/13_Bounce/MiniScript/bounce.ms new file mode 100644 index 000000000..64174be62 --- /dev/null +++ b/00_Alternate_Languages/13_Bounce/MiniScript/bounce.ms @@ -0,0 +1,55 @@ +print " "*33 + "Bounce" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print +t = [0]*21 +print "This simulation lets you specify the initial velocity" +print "of a ball thrown straight up, and the coefficient of" +print "elasticity of the ball. Please use a decimal fraction" +print "coefficiency (less than 1)." +print +print "You also specify the time increment to be used in" +print "'strobing' the ball's flight (try .1 initially)." +print + +addToLine = function(line, tabPos, textToAdd) + return line + " " * (floor(tabPos) - line.len) + textToAdd +end function + +while true + s2 = input("Time increment (sec)? ").val + print + v = input("Velocity (fps)? ").val + print + c = input("Coefficient? ").val + print + print "feet" + print + s1 = floor(70/(v/(16*s2))) + for i in range(1, s1) + t[i]=v*c^(i-1)/16 + end for + for h in range(floor(-16*(v/32)^2+v^2/32+.5), 0, -0.5) + line = "" + if floor(h)==h then line = str(h) + l=0 + for i in range(1, s1) + for time in range(0, t[i], s2) + l=l+s2 + if abs(h-(.5*(-32)*time^2+v*c^(i-1)*time))<=.25 then + line = addToLine(line, l/s2, "0") + end if + end for + time = t[i+1]/2 + if -16*time^2+v*c^(i-1)*time < h then break + end for + print line + end for + print " " + "." * floor((l+1)/s2+1) + line = " 0" + for i in range(1, l+.9995) + line = addToLine(line, i/s2, i) + end for + print line + print " " * floor((l+1)/(2*s2)-2) + "seconds" + print +end while diff --git a/00_Alternate_Languages/14_Bowling/MiniScript/README.md b/00_Alternate_Languages/14_Bowling/MiniScript/README.md new file mode 100644 index 000000000..bfb33e6b5 --- /dev/null +++ b/00_Alternate_Languages/14_Bowling/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript bowling.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "bowling" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/14_Bowling/MiniScript/bowling.ms b/00_Alternate_Languages/14_Bowling/MiniScript/bowling.ms new file mode 100644 index 000000000..139ec225e --- /dev/null +++ b/00_Alternate_Languages/14_Bowling/MiniScript/bowling.ms @@ -0,0 +1,138 @@ +import "listUtil" + +print " "*34 + "Bowl" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print + +pinDown = [0]*10 // state of each pin: 1=down, 0=standing +player = 1 +frame = 1 +ball = 1 +scores = list.init3d(10, 4, 3, 0) // index by [frame][player][ball], all 0-based + +printInstructions = function + print "The game of bowling takes mind and skill. During the game" + print "the computer will keep score. You may compete with" + print "other players [up to four]. You will be playing ten frames." + print "On the pin diagram 'O' means the pin is down...'+' means the" + print "pin is standing. After the game the computer will show your" + print "scores." +end function + +printPinDiagram = function + print "Player: " + (player+1) + " Frame: " + (frame+1) + " Ball: " + (ball+1) + print + k = 0 + for row in range (0, 3) + line = " " * row + for j in range(1, 4-row) + line += "+O"[pinDown[k]] + " " + k += 1 + end for + print line + end for +end function + +printAnalysis = function(previousDown=0) + pinsLeft = 10 - pinDown.sum + if pinDown.sum == previousDown then print "Gutter!!" + if ball == 0 and pinsLeft == 0 then + print "Strike!!!!!" + char(7)*4 + globals.status = 3 + else if ball == 1 and pinsLeft == 0 then + print "Spare!!!!" + globals.status = 2 + else if ball == 1 and pinsLeft > 0 then + print "Error!!!" // (i.e., didn't clear all the pins in 2 balls) + globals.status = 1 + end if +end function + +rollOneBall = function + print "Type roll to get the ball going." + input // (response ignored) + for i in range(1, 20) + // Generate a random number from 0-99, then take this mod 15. + // This gives us a slightly higher chance of hitting a non-existent + // pin than one of the actual 10. + x = floor(rnd*100) + if x % 15 < 10 then pinDown[x % 15] = 1 + end for + printPinDiagram +end function + +doOneFrame = function + globals.pinDown = [0]*10 + globals.ball = 0 + rollOneBall + printAnalysis + hitOnBall0 = pinDown.sum + scores[frame][player][ball] = hitOnBall0 + + globals.ball = 1 + if hitOnBall0 < 10 then + print "Roll your 2nd ball" + print + rollOneBall + printAnalysis hitOnBall0 + end if + // Note: scoring in this program is not like real bowling. + // It just stores the number of pins down at the end of each ball, + // and a status code (1, 2, or 3). + scores[frame][player][ball] = pinDown.sum + scores[frame][player][2] = status +end function + +pad = function(n, width=3) + return (" "*width + n)[-width:] +end function + +printFinalScores = function + print "FRAMES" + for i in range(1,10) + print pad(i), "" + end for + print + for player in range(0, numPlayers-1) + for i in range(0, 2) + for frame in range(0, 9) + print pad(scores[frame][player][i]), "" + end for + print + end for + print + end for +end function + +playOneGame = function + for f in range(0, 9) + globals.frame = f + for p in range(0, numPlayers-1) + globals.player = p + doOneFrame + end for + end for + print + printFinalScores +end function + +// Main program +print "Welcome to the alley" +print "Bring your friends" +print "Okay let's first get acquainted" +print +ans = input("The instructions (Y/N)? ").upper +if not ans or ans[0] != "N" then printInstructions +while true + numPlayers = input("First of all...How many are playing? ").val + if 0 < numPlayers < 5 then break + print "Please enter a number from 1 to 4." +end while +print +print "Very good..." +while true + playOneGame + print + ans = input("Do you want another game? ").upper + if not ans or ans[0] != "Y" then break +end while diff --git a/00_Alternate_Languages/15_Boxing/MiniScript/README.md b/00_Alternate_Languages/15_Boxing/MiniScript/README.md new file mode 100644 index 000000000..9c0ae8fc2 --- /dev/null +++ b/00_Alternate_Languages/15_Boxing/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript boxing.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "boxing" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/15_Boxing/MiniScript/boxing.ms b/00_Alternate_Languages/15_Boxing/MiniScript/boxing.ms new file mode 100644 index 000000000..948a3bdde --- /dev/null +++ b/00_Alternate_Languages/15_Boxing/MiniScript/boxing.ms @@ -0,0 +1,174 @@ +print " "*33 + "Boxing" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print +print "Boxing Olympic Style (3 Rounds -- 2 out of 3 Wins)" + +playerWins = 0 +opponentWins = 0 +print +opponentName = input("What is your opponent's name? ") +playerName = input("Input your man's name? ") +print "Different punches are: (1) full swing; (2) hook; (3) uppercut; (4) jab." +playerBest = input("What is your man's best? ").val +playerWeakness = input("What is his vulnerability? " ).val +while true + opponentBest = floor(4 * rnd + 1) + opponentWeakness = floor(4 * rnd + 1) + if opponentBest != opponentWeakness then break +end while +print opponentName + "'s advantage is " + opponentBest + " and vulnerability is secret." +print + +playerConnects = function + print "He connects!" + if playerPoints > 35 then + print opponentName + " is knocked cold and " + playerName + " is the winner and champ!" + globals.done = true + return + end if + globals.playerPoints += 15 +end function + +doPlayerPunch = function + p = input(playerName + "'s punch? ").val + if p == playerBest then globals.playerPoints += 2 + if p == 1 then // Full Swing + print playerName + " swings and ", "" + if opponentWeakness == 4 then // (probably a bug in original code) + playerConnects + else + x3 = floor(30 * rnd+1) + if x3 < 10 then + playerConnects + else + print "he misses " + if playerPoints != 1 then + print + print + end if + end if + end if + else if p == 2 then // Hook + print playerName + " gives the hook... ", "" + if opponentWeakness == 2 then + globals.playerPoints += 7 + else + h1 = floor(2 * rnd + 1) + if h1 == 1 then + print "But it's blocked!!!!!!!!!!!!!" + else + print "Connects..." + globals.playerPoints += 7 + end if + end if + else if p == 3 then // Uppercut + print playerName + " tries an uppercut ", "" + if opponentWeakness == 3 or floor(100 * rnd + 1) < 51 then + print "and he connects!" + globals.playerPoints += 4 + else + print "and it's blocked (lucky block!)" + end if + else // Jab + print playerName + " jabs at " + opponentName + "'s head ", "" + if opponentWeakness != 4 and floor(8 * rnd + 1) >= 4 then + print "It's blocked." + else + globals.playerPoints += 3 + end if + end if +end function + +playerKnockedOut = function + print playerName + " is knocked cold and " + opponentName + " is the winner and champ!" + globals.done = true +end function + +doOpponentPunch = function + j7 = floor(4 * rnd + 1) + if j7 == playerBest then globals.opponentPoints += 2 + if j7 == 1 then // Full swing + print opponentName + " takes a full swing and ", "" + if playerWeakness == 1 or floor(60 * rnd + 1) < 30 then + print "POW!!!!! He hits him right in the face!" + if opponentPoints > 35 then + playerKnockedOut + else + globals.opponentPoints += 15 + end if + else + print "it's blocked!" + end if + end if + if j7 == 2 then // Hook + print opponentName + " gets " + playerName + " in the jaw (ouch!)" + globals.playerPoints += 7 + print "....and again!" + globals.playerPoints += 5 + if opponentPoints > 35 then + playerKnockedOut + return + end if + print + // continue below as if an Uppercut (probably a bug in the original code) + end if + if j7 == 2 or j7 == 3 then // Uppercut, or Hook + print playerName + " is attacked by an uppercut (oh,oh)..." + if playerWeakness == 3 or floor(200*rnd+1) <= 75 then + print "and " + opponentName + " connects..." + globals.opponentPoints += 8 + else + print " blocks and hits " + opponentName + " with a hook." + globals.playerPoints += 5 + end if + end if + if j7 == 4 then // Jab + print opponentName + " jabs and ", "" + if playerWeakness == 4 or floor(7 * rnd + 1) > 4 then + print "blood spills !!!" + globals.opponentPoints += 5 + else + print "It's blocked!" + end if + end if +end function + +playOneRound = function + globals.playerPoints = 0 + globals.opponentPoints = 0 + print "Round " + round + " begins..." + for r1 in range(1, 7) + i = floor(10 * rnd + 1) + if i <= 5 then + doPlayerPunch + else + doOpponentPunch + end if + if done then return + end for // next R1 (sub-round) + if playerPoints > opponentPoints then + print; print playerName + " wins round " + round + globals.playerWins += 1 + else + print; print opponentName + " wins round " + round + globals.opponentWins += 1 + end if +end function + +done = false +for round in range(1,3) + playOneRound + if done then break + if opponentWins >= 2 then + print opponentName + " wins (nice going, " + opponentName + ")." + break + else if playerWins >= 2 then + print playerName + " amazingly wins!!" + break + end if +end for // next round + +print +print +print "and now goodbye from the Olympic arena." +print diff --git a/00_Alternate_Languages/16_Bug/MiniScript/README.md b/00_Alternate_Languages/16_Bug/MiniScript/README.md new file mode 100644 index 000000000..554d5e529 --- /dev/null +++ b/00_Alternate_Languages/16_Bug/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript bug.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "bug" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/16_Bug/MiniScript/bug.ms b/00_Alternate_Languages/16_Bug/MiniScript/bug.ms new file mode 100644 index 000000000..29349b2a2 --- /dev/null +++ b/00_Alternate_Languages/16_Bug/MiniScript/bug.ms @@ -0,0 +1,192 @@ +print " "*34 + "Bug" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print +print "The game Bug" +print "I hope you enjoy this game." +print +ans = input("Do you want instructions? ").lower +if not ans or ans[0] != "n" then + print "The object of bug is to finish your bug before i finish" + print "mine. Each number stands for a part of the bug body." + print "I will roll the die for you, tell you what i rolled for you" + print "what the number stands for, and if you can get the part." + print "If you can get the part I will give it to you." + print "The same will happen on my turn." + print "If there is a change in either bug I will give you the" + print "option of seeing the pictures of the bugs." + print "Ihe numbers stand for parts as follows:" + print "Number Part Number of part needed" + print "1 body 1" + print "2 neck 1" + print "3 head 1" + print "4 feelers 2" + print "5 tail 1" + print "6 legs 6" + print + input "(Press Return.)" // (wait before starting the game) + print +end if + +// define a class to represent a bug (with all its body parts) +Bug = {} +Bug.body = false +Bug.neck = false +Bug.head = false +Bug.feelers = 0 +Bug.tail = false +Bug.legs = 0 +Bug.feelerLetter = "F" +Bug.pronoun = "I" + +// add a method to determine if the bug is complete +Bug.complete = function + return self.tail and self.feelers >= 2 and self.legs >= 6 +end function + +// add a method to draw the bug using print +Bug.draw = function + if self.feelers then + for row in range(1,4) + print " "*10 + (self.feelerLetter + " ") * self.feelers + end for + end if + if self.head then + print " HHHHHHH" + print " H H" + print " H O O H" + print " H H" + print " H V H" + print " HHHHHHH" + end if + if self.neck then + print " N N" + print " N N" + end if + if self.body then + print " BBBBBBBBBBBB" + print " B B" + print " B B" + if self.tail then print "TTTTTB B" + print " BBBBBBBBBBBB" + end if + if self.legs then + for row in [1,2] + print " "*5 + "L " * self.legs + end for + end if +end function + +// add a method to add a part, if possible; return true if bug changed +Bug.addPart = function(partNum) + if partNum == 1 then + print "1=Body" + if self.body then + print self.pronoun + " do not need a body." + else + print self.pronoun + " now have a body." + self.body = true + return true + end if + else if partNum == 2 then + print "2=neck" + if self.neck then + print self.pronoun + " do not need a neck." + else if not self.body then + print self.pronoun + " do not have a body." + else + print self.pronoun + " now have a neck." + self.neck = true + return true + end if + else if partNum == 3 then + print "3=head" + if self.head then + print self.pronoun + " have a head." + else if not self.neck then + print self.pronoun + " do not have a neck." + else + print self.pronoun + " needed a head." + self.head = true + return true + end if + else if partNum == 4 then + print "4=feelers" + if self.feelers >= 2 then + print self.pronoun + " have two feelers already." + else if not self.head then + print self.pronoun + " do not have a head." + else + if self.pronoun == "You" then + print "I now give you a feeler." + else + print "I get a feeler." + end if + self.feelers += 1 + return true + end if + else if partNum == 5 then + print "5=tail" + if self.tail then + print self.pronoun + " already have a tail." + else if not self.body then + print self.pronoun + " do not have a body." + else + if self.pronoun == "You" then + print "I now give you a tail." + else + print "I now have a tail." + end if + self.tail = true + return true + end if + else if partNum == 6 then + print "6=legs" + if self.legs >= 6 then + print self.pronoun + " have 6 feet." + else if not self.body then + print self.pronoun + " do not have a body." + else + self.legs += 1 + print self.pronoun + " now have " + self.legs + " leg" + "s"*(self.legs>1) + "." + return true + end if + end if + return 0 +end function + + +// ...then, instantiate a bug for You (human player) and Me (computer) +you = new Bug +you.feelerLetter = "A" // (don't ask me why) +you.pronoun = "You" +me = new Bug + +// Main loop +while not you.complete and not me.complete + anyChange = false + die = floor(6 * rnd + 1) + print; print "You rolled a " + die + if you.addPart(die) then anyChange = true + wait 2 + die = floor(6 * rnd + 1) + print; print "I rolled a " + die + if me.addPart(die) then anyChange = true + if you.complete then print "Your bug is finished." + if me.complete then print "My bug is finished." + if anyChange then + ans = input("Do you want the pictures? ").lower + if not ans or ans[0] != "n" then + print "*****Your Bug*****" + print; print + you.draw + wait 2 + print + print "*****My Bug*****" + print; print + me.draw + wait 2 + end if + end if +end while +print "I hope you enjoyed the game, play it again soon!!" + diff --git a/00_Alternate_Languages/17_Bullfight/MiniScript/README.md b/00_Alternate_Languages/17_Bullfight/MiniScript/README.md new file mode 100644 index 000000000..2290e6e12 --- /dev/null +++ b/00_Alternate_Languages/17_Bullfight/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript bull.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "bull" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/17_Bullfight/MiniScript/bull.ms b/00_Alternate_Languages/17_Bullfight/MiniScript/bull.ms new file mode 100644 index 000000000..1259d651f --- /dev/null +++ b/00_Alternate_Languages/17_Bullfight/MiniScript/bull.ms @@ -0,0 +1,188 @@ +print " "*34 + "Bull" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print + +getYesNo = function(prompt) + while true + ans = input(prompt + "? ").lower + if ans and (ans[0] == "y" or ans[0] == "n") then return ans[0] + print "Incorrect answer - - please type 'yes' or 'no'." + end while +end function + +if getYesNo("Do you want instructions") == "y" then + print "Hello, all you bloodlovers and aficionados." + print "Here is your big chance to kill a bull." + print + print "On each pass of the bull, you may try" + print "0 - Veronica (dangerous inside move of the cape)" + print "1 - Less dangerous outside move of the cape" + print "2 - Ordinary swirl of the cape." + print + print "Instead of the above, you may try to kill the bull" + print "on any turn: 4 (over the horns), 5 (in the chest)." + print "But if I were you," + print "I wouldn't try it before the seventh pass." + print + print "The crowd will determine what award you deserve" + print "(posthumously if necessary)." + print "The braver you are, the better the award you receive." + print + print "The better the job the picadores and toreadores do," + print "the better your chances are." + print; input "(Press return.)" +end if +print; print +bravery = 1 +outcome = 1 +qualities = [null, "superb", "good", "fair", "poor", "awful"] + +// Select a bull (level 1-5, lower numbers are tougher) +bullLevel = floor(rnd*5+1) +print "You have drawn a " + qualities[bullLevel] + " bull." +if bullLevel > 4 then print "You're lucky." +if bullLevel < 2 then print "Good luck. You'll need it." +print + +// Simulate one of the preliminary types of bullfighters +// (picodores or toreadores). Return their effect, 0.1 - 0.5. +simPreliminary = function(fighterType) + effect = 0.1 + temp = 3 / bullLevel * rnd + if temp < 0.87 then effect = 0.2 + if temp < 0.63 then effect = 0.3 + if temp < 0.5 then effect = 0.4 + if temp < 0.37 then effect = 0.5 + t = floor(10 * effect + 0.2) // (get quality in range 1 - 5) + print "The " + fighterType + " did a " + qualities[t] + " job." + if t == 5 then + if fighterType == "picadores" then + print floor(rnd*2+1) + " of the horses of the picadores killed." + end if + print floor(rnd*2+1) + " of the " + fighterType + " killed." + else if t == 4 then + if rnd > 0.5 then + print "One of the " + fighterType + " killed." + else + print "No " + fighterType + " were killed." + end if + end if + print + return effect +end function + +picaEffect = simPreliminary("picadores") +toreEffect = simPreliminary("toreadores") + +getGored = function + while not done + if rnd > 0.5 then + print "You are dead." + globals.bravery = 1.5 + globals.done = true + else + print "You are still alive."; print + if getYesNo("Do you run from the ring") == "y" then + print "Coward" + globals.bravery = 0 + globals.done = true + else + print "You are brave. Stupid, but brave." + if rnd > 0.5 then + globals.bravery = 2 + break + else + print "You are gored again!" + end if + end if + end if + end while +end function + +pass = 0 +courage = 1 // cumulative effect of cape choices +bravery = 1 // set mainly by outcomes after getting gored +victory = false // true if we kill the bull +done = false + +while not done + pass += 1 + print + print "Pass number " + pass + if pass < 3 then + print "The bull is charging at you! You are the matador--" + tryKill = (getYesNo("do you want to kill the bull") == "y") + else + tryKill = (getYesNo("Here comes the bull. Try for a kill") == "y") + end if + if tryKill then + print; print "It is the moment of truth."; print + h = input("How do you try to kill the bull? " ).val + if h != 4 and h != 5 then + print "You panicked. The bull gored you." + getGored + break + end if + k = (6-bullLevel) * 10 * rnd / ((picaEffect + toreEffect) * 5 * pass) + if h == 4 then + victory = (k <= 0.8) + else + victory = (k <= 0.2) + end if + if victory then + print "You killed the bull!" + else + print "The bull has gored you!" + getGored + end if + done = true + else + if pass < 3 then + capeMove = input("What move do you make with the cape? ").val + else + capeMove = input("Cape move? ").val + end if + while capeMove < 0 or capeMove > 2 or capeMove != floor(capeMove) + print "Don't panic, you idiot! Put down a correct number" + capeMove = input.val + end while + m = [3, 2, 0.5][capeMove] + courage += m + f = (6-bullLevel+m/10)*rnd / ((picaEffect+toreEffect+pass/10)*5) + if f >= 0.51 then + print "The bull has gored you!" + getGored + end if + end if +end while + +// Final outcome +if bravery == 0 then + print "The crowd boos for ten minutes. If you ever dare to show" + print "your face in a ring again, they swear they will kill you--" + print "unless the bull does first." +else + fnd = (4.5+courage/6-(picaEffect+toreEffect)*2.5+4*bravery+2*(victory+1)-pass^2/120-bullLevel) + fnc = function; return fnd * rnd; end function + if bravery == 2 then + print "The crowd cheers wildly!" + else if victory then + print "The crowd cheers!"; print + end if + print "The crowd awards you" + if fnc < 2.4 then + print "nothing at all." + else if fnc < 4.9 then + print "one ear of the bull." + else if fnc < 7.4 then + print "Both ears of the bull!" + print "Ole!" + else + print "Ole! You are 'Muy Hombre!"" Ole! Ole!" + end if +end if +print +print "Adios"; print; print; print + + + diff --git a/00_Alternate_Languages/18_Bullseye/MiniScript/README.md b/00_Alternate_Languages/18_Bullseye/MiniScript/README.md new file mode 100644 index 000000000..73a1c749a --- /dev/null +++ b/00_Alternate_Languages/18_Bullseye/MiniScript/README.md @@ -0,0 +1,22 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +0. Try-It! Page: +Go to https://miniscript.org/tryit/, clear the sample code from the code editor, and paste in the contents of bullseye.ms. Then click the "Run Script" button. Program output (and input) will appear in the green-on-black terminal display to the right of or below the code editor. + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript bullseye.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "bullseye" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/18_Bullseye/MiniScript/bullseye.ms b/00_Alternate_Languages/18_Bullseye/MiniScript/bullseye.ms new file mode 100644 index 000000000..10dc95cac --- /dev/null +++ b/00_Alternate_Languages/18_Bullseye/MiniScript/bullseye.ms @@ -0,0 +1,59 @@ +print " "*32 + "Bullseye" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print + +print "In this game, up to 20 players throw darts at a target" +print "with 10, 20, 30, and 40 point zones. the objective is" +print "to get 200 points."; print +print "throw description probable score" +print " 1 fast overarm bullseye or complete miss" +print " 2 controlled overarm 10, 20 or 30 points" +print " 3 underarm anything";print +names = [] +n = input("How many players? ").val; print +for i in range(0, n-1) + names.push input("Name of player #" + (i+1) + "? ") +end for +scores = [0] * n + +round = 0 +while true + round += 1; print; print "round " + round; print "---------" + for i in range(0, n-1) + while true + print; t = input(names[i] + "'s throw? ").val + if 1 <= t <= 3 then break + print "Input 1, 2, or 3!" + end while + if t == 1 then + p1=.65; p2=.55; p3=.5; p4=.5 + else if t == 2 then + p1=.99; p2=.77; p3=.43; p4=.01 + else + p1=.95; p2=.75; p3=.45; p4=.05 + end if + u = rnd + if u>=p1 then + print "Bullseye!! 40 points!"; b=40 + else if u>=p2 then + print "30-point zone!"; b=30 + else if u>=p3 then + print "20-point zone"; b=20 + else if u>=p4 then + print "Whew! 10 points."; b=10 + else + print "Missed the target! too bad."; b=0 + end if + scores[i] += b; print "Total score = " + scores[i] + end for + winners = [] + for i in range(0, n-1) + if scores[i] >= 200 then winners.push i + end for + if winners then break +end while + +print; print "We have a winner!!"; print +for i in winners; print names[i] + " scored " + scores[i] + " points."; end for +print; print "Thanks for the game." + diff --git a/00_Alternate_Languages/19_Bunny/MiniScript/README.md b/00_Alternate_Languages/19_Bunny/MiniScript/README.md new file mode 100644 index 000000000..90a054330 --- /dev/null +++ b/00_Alternate_Languages/19_Bunny/MiniScript/README.md @@ -0,0 +1,22 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +0. Try-It! Page: +Go to https://miniscript.org/tryit/, clear the sample code from the code editor, and paste in the contents of bunny.ms. Then click the "Run Script" button. Program output (and input) will appear in the green-on-black terminal display to the right of or below the code editor. + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript bunny.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "bunny" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/19_Bunny/MiniScript/bunny.ms b/00_Alternate_Languages/19_Bunny/MiniScript/bunny.ms new file mode 100644 index 000000000..176e1e6cf --- /dev/null +++ b/00_Alternate_Languages/19_Bunny/MiniScript/bunny.ms @@ -0,0 +1,43 @@ +print " "*33 + "Bunny" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print + +data = [] +data += [1,2,-1,0,2,45,50,-1,0,5,43,52,-1,0,7,41,52,-1] +data += [1,9,37,50,-1,2,11,36,50,-1,3,13,34,49,-1,4,14,32,48,-1] +data += [5,15,31,47,-1,6,16,30,45,-1,7,17,29,44,-1,8,19,28,43,-1] +data += [9,20,27,41,-1,10,21,26,40,-1,11,22,25,38,-1,12,22,24,36,-1] +data += [13,34,-1,14,33,-1,15,31,-1,17,29,-1,18,27,-1] +data += [19,26,-1,16,28,-1,13,30,-1,11,31,-1,10,32,-1] +data += [8,33,-1,7,34,-1,6,13,16,34,-1,5,12,16,35,-1] +data += [4,12,16,35,-1,3,12,15,35,-1,2,35,-1,1,35,-1] +data += [2,34,-1,3,34,-1,4,33,-1,6,33,-1,10,32,34,34,-1] +data += [14,17,19,25,28,31,35,35,-1,15,19,23,30,36,36,-1] +data += [14,18,21,21,24,30,37,37,-1,13,18,23,29,33,38,-1] +data += [12,29,31,33,-1,11,13,17,17,19,19,22,22,24,31,-1] +data += [10,11,17,18,22,22,24,24,29,29,-1] +data += [22,23,26,29,-1,27,29,-1,28,29,-1,4096] + +string.pad = function(w) + return self + " " * (w - self.len) +end function + +for i in range(5); print; end for + +line = "" +while true + x = data.pull + if x > 128 then break + if x >= 0 then + line = line.pad(x) + y = data.pull + for i in range(x, y) + line += "BUNNY"[i % 5] + end for + else + print line + line = "" + wait 0.1 // optional delay to make printing more visible + end if +end while + diff --git a/00_Alternate_Languages/20_Buzzword/MiniScript/README.md b/00_Alternate_Languages/20_Buzzword/MiniScript/README.md new file mode 100644 index 000000000..90a054330 --- /dev/null +++ b/00_Alternate_Languages/20_Buzzword/MiniScript/README.md @@ -0,0 +1,22 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +0. Try-It! Page: +Go to https://miniscript.org/tryit/, clear the sample code from the code editor, and paste in the contents of bunny.ms. Then click the "Run Script" button. Program output (and input) will appear in the green-on-black terminal display to the right of or below the code editor. + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript bunny.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "bunny" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/20_Buzzword/MiniScript/buzzword.ms b/00_Alternate_Languages/20_Buzzword/MiniScript/buzzword.ms new file mode 100644 index 000000000..26a377755 --- /dev/null +++ b/00_Alternate_Languages/20_Buzzword/MiniScript/buzzword.ms @@ -0,0 +1,38 @@ +print " "*26 + "Buzzword Generator" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print + +print "This program prints highly acceptable phrases in" +print "'educator-speak' that you can work into reports" +print "and speeches. Whenever a question mark is printed," +print "type a 'y' for another phrase or 'n' to quit." + +words1 = ["ability","basal","behavioral","child-centered", + "differentiated","discovery","flexible","heterogeneous", + "homogeneous","manipulative","modular","tavistock", + "individualized"] + +words2 = ["learning", "evaluative","objective", + "cognitive","enrichment","scheduling","humanistic", + "integrated","non-graded","training","vertical age", + "motivational","creative"] + +words3 = ["grouping","modification", + "accountability","process","core curriculum","algorithm", + "performance","reinforcement","open classroom","resource", + "structure","facility","environment"] + +list.any = function + return self[self.len * rnd] +end function + +print; print; print "Here's the first phrase:" + +while true + print [words1.any, words2.any, words3.any].join + print + yn = input("?").lower + if yn != "y" then break +end while + +print "Come back when you need help with another report!" diff --git a/00_Alternate_Languages/20_Buzzword/nim/buzzword.nim b/00_Alternate_Languages/20_Buzzword/nim/buzzword.nim new file mode 100644 index 000000000..dfba31c9d --- /dev/null +++ b/00_Alternate_Languages/20_Buzzword/nim/buzzword.nim @@ -0,0 +1,34 @@ +import std/[random,strutils] + +randomize() + +const + words1 = ["ABILITY","BASAL","BEHAVIORAL","CHILD-CENTERED","DIFFERENTIATED","DISCOVERY","FLEXIBLE", + "HETEROGENEOUS","HOMOGENEOUS","MANIPULATIVE","MODULAR","TAVISTOCK","INDIVIDUALIZED"] + words2 = ["LEARNING","EVALUATIVE","OBJECTIVE","COGNITIVE","ENRICHMENT","SCHEDULING","HUMANISTIC", + "INTEGRATED","NON-GRADED","TRAINING","VERTICAL AGE","MOTIVATIONAL","CREATIVE"] + words3 = ["GROUPING","MODIFICATION","ACCOUNTABILITY","PROCESS","CORE CURRICULUM","ALGORITHM", "PERFORMANCE", + "REINFORCEMENT","OPEN CLASSROOM","RESOURCE","STRUCTURE","FACILITY","ENVIRONMENT"] + +var + stillplaying: bool = true + prompt: string + +echo spaces(26), "BUZZWORD GENERATOR" +echo spaces(15), "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" +echo "\n" +echo "THIS PROGRAM PRINTS HIGHLY ACCEPTABLE PHRASES IN" +echo "'EDUCATOR-SPEAK' THAT YOU CAN WORK INTO REPORTS" +echo "AND SPEECHES. AFTER EACH PHRASE, HIT 'ENTER' FOR" +echo "ANOTHER PHRASE, OR TYPE 'N' TO QUIT." +echo "\n" +echo "HERE'S THE FIRST PHRASE..." + +while stillplaying: + echo "" + echo words1[rand(0..12)], " ", words2[rand(0..12)], " ", words3[rand(0..12)] + prompt = readLine(stdin).normalize() + if prompt.substr(0, 0) == "n": + stillplaying = false + +echo "COME BACK WHEN YOU NEED HELP WITH ANOTHER REPORT!" diff --git a/00_Alternate_Languages/21_Calendar/MiniScript/README.md b/00_Alternate_Languages/21_Calendar/MiniScript/README.md new file mode 100644 index 000000000..fea2264e6 --- /dev/null +++ b/00_Alternate_Languages/21_Calendar/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript calendar.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "calendar" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/21_Calendar/MiniScript/calendar.ms b/00_Alternate_Languages/21_Calendar/MiniScript/calendar.ms new file mode 100644 index 000000000..27691bdb9 --- /dev/null +++ b/00_Alternate_Languages/21_Calendar/MiniScript/calendar.ms @@ -0,0 +1,89 @@ +import "stringUtil" + +print " "*32 + "Calendar" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print + +startingDOW = 0 +leapYear = false + +// Note: while the original program required changes to the code to configure +// it for the current year, in this port we choose to ask the user. +// Here's the function to do that. +getParameters = function + days = "sunday monday tuesday wednesday thursday friday saturday".split + globals.startingDOW = 999 + while startingDOW == 999 + ans = input("What is the first day of the week of the year? ").lower + if not ans then continue + for i in days.indexes + if days[i].startsWith(ans) then + globals.startingDOW = -i + break + end if + end for + end while + + while true + ans = input("Is it a leap year? ").lower + if ans and (ans[0] == "y" or ans[0] == "n") then break + end while + globals.leapYear = (ans[0] == "y") + + while true + ans = input("Pause after each month? ").lower + if ans and (ans[0] == "y" or ans[0] == "n") then break + end while + globals.pause = (ans[0] == "y") +end function + +getParameters +monthNames = [ + " JANUARY ", + " FEBRUARY", + " MARCH ", + " APRIL ", + " MAY ", + " JUNE ", + " JULY ", + " AUGUST ", + "SEPTEMBER", + " OCTOBER ", + " NOVEMBER", + " DECEMBER", + ] +monthDays = [31, 28 + leapYear, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] + +// Function to print one month calendar. +// month: numeric month number, 0-based (0-11) +printMonth = function(month) + daysSoFar = monthDays[:month].sum + daysLeft = monthDays[month:].sum + print "** " + str(daysSoFar).pad(4) + "*"*18 + " " + monthNames[month] + + " " + "*"*18 + " " + str(daysLeft).pad(4) + "**" + print + print " S M T W T F S" + print + print "*" * 61 + // calculate the day of the week, from 0=Sunday to 6=Saturday + dow = (daysSoFar - startingDOW) % 7 + print " " * 5 + " " * (8*dow), "" + for i in range(1, monthDays[month]) + print str(i).pad(8), "" + dow += 1 + if dow == 7 then + dow = 0 + print + if i == monthDays[month] then break + print; print " " * 5, "" + end if + end for + print +end function + +// Main loop. +for month in range(0, 11) + printMonth month + print + if month < 11 and pause then input +end for diff --git a/00_Alternate_Languages/22_Change/MiniScript/README.md b/00_Alternate_Languages/22_Change/MiniScript/README.md new file mode 100644 index 000000000..5708156d8 --- /dev/null +++ b/00_Alternate_Languages/22_Change/MiniScript/README.md @@ -0,0 +1,22 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +0. Try-It! Page: +Go to https://miniscript.org/tryit/, clear the sample code from the code editor, and paste in the contents of change.ms. Then click the "Run Script" button. Program output (and input) will appear in the green-on-black terminal display to the right of or below the code editor. + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript change.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "change" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/22_Change/MiniScript/change.ms b/00_Alternate_Languages/22_Change/MiniScript/change.ms new file mode 100644 index 000000000..675a179aa --- /dev/null +++ b/00_Alternate_Languages/22_Change/MiniScript/change.ms @@ -0,0 +1,57 @@ +print " "*33 + "Change" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print + +print "I, your friendly microcomputer, will determine" +print "the correct change for items costing up to $100." +print; print +while true + itemCost = input("Cost of item? ").val + if itemCost == 0 then break + payment = input("Amount of payment? ").val + change = payment - itemCost + if change < 0 then + print "Sorry, you have short-changed me $" + (itemCost - payment) + continue + else if change == 0 then + print "Correct amount, thank you." + continue + end if + + print "Your change, $" + change + + dollars = floor(change/10) + if dollars then print dollars + " ten dollar bill(s)" + change -= dollars * 10 + + fivers = floor(change/5) + if fivers then print fivers + " five dollar bill(s)" + change -= fivers * 5 + + ones = floor(change) + if ones then print ones + " one dollar bill(s)" + change -= ones + + change *= 100 // (now working in cents) + + halfs = floor(change / 50) + if halfs then print halfs + " one half dollar(s)" + change -= halfs * 50 + + quarters = floor(change / 25) + if quarters then print quarters + " quarter(s)" + change -= quarters * 25 + + dimes = floor(change / 10) + if dimes then print dimes + " dime(s)" + change -= dimes * 10 + + nickels = floor(change / 5) + if nickels then print nickels + " nickel(s)" + change -= nickels * 5 + + pennies = round(change) + if pennies then print pennies + " penny(s)" + print "Thank you, come again." + print; print +end while \ No newline at end of file diff --git a/00_Alternate_Languages/23_Checkers/MiniScript/README.md b/00_Alternate_Languages/23_Checkers/MiniScript/README.md new file mode 100644 index 000000000..9cb5fd6ee --- /dev/null +++ b/00_Alternate_Languages/23_Checkers/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript checkers.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "checkers" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/23_Checkers/MiniScript/checkers.ms b/00_Alternate_Languages/23_Checkers/MiniScript/checkers.ms new file mode 100644 index 000000000..dc7527128 --- /dev/null +++ b/00_Alternate_Languages/23_Checkers/MiniScript/checkers.ms @@ -0,0 +1,243 @@ +print " "*32 + "Checkers" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print + +print "This is the game of Checkers. The computer is X," +print "and you are O. The computer will move first." +print "Squares are referred to by a coordinate system." +print "(0,0) is the lower left corner" +print "(0,7) is the upper left corner" +print "(7,0) is the lower right corner" +print "(7,7) is the upper right corner" +print "The computer will type '+TO' when you have another" +print "jump. Type two negative numbers if you cannot jump." +print; print; print + +input "(Press Return.)"; print // (give player a chance to read) + +// The board. Pieces are represented by numeric values: +// - 0 = empty square +// - -1,-2 = computer (X) (-1 for regular piece, -2 for king) +// - 1,2 = human (O) (1 for regular piece, 2 for king) +// Board is indexed by [x][y], so we have to initialize it sideways: +board = [ + [ 1, 0, 1, 0, 0, 0, -1, 0], + [ 0, 1, 0, 0, 0, -1, 0, -1], + [ 1, 0, 1, 0, 0, 0, -1, 0], + [ 0, 1, 0, 0, 0, -1, 0, -1], + [ 1, 0, 1, 0, 0, 0, -1, 0], + [ 0, 1, 0, 0, 0, -1, 0, -1], + [ 1, 0, 1, 0, 0, 0, -1, 0], + [ 0, 1, 0, 0, 0, -1, 0, -1]] + +// Function to print the board +printBoard = function + for y in range(7, 0) + for x in range(0, 7) + // We print by indexing with the board entry (-2 to 2) into a list + // of possible representations. Remember that in MiniScript, a + // negative index counts from the end. + print [". ", "O ", "O*", "X*", "X "][board[x][y]], " " + end for + print; print + end for +end function + +// Function to get x,y coordinates from the player. +// This is written to allow the two numbers to be +// separated by any combination of ',' and ' '. +// Returns input as [x,y]. +inputPosition = function(prompt, requiredPieceSign, allowCancel=false) + while true + ans = input(prompt + "? ").replace(",", " ").split + if ans.len < 2 then + print "Enter two coordinates, for example: 4 0" + continue + end if + x = val(ans[0]) + y = val(ans[-1]) + if x < 0 and y < 0 and allowCancel then + return [x, y] + else if x < 0 or x > 7 or y < 0 or y > 7 then + print "Coordinates must be in the range 0-7" + else if x%2 != y%2 then + print "Invalid coordinates (both must be odd, or both even)" + else if sign(board[x][y]) != requiredPieceSign then + print "Invalid coordinates" + else + return [x, y] + end if + end while +end function + +// Evaluate a potential (computer) move. +evalMove = function(fromX, fromY, toX, toY) + score = 0 + + // +2 if it promotes this piece + if toY == 0 and board[fromX][fromY] == -1 then score += 2 + + // +5 if it jumps an opponent's piece + if abs(fromY-toY) == 2 then score += 5 + + // -2 if the piece is moving away from the top boundary + if fromY == 7 then score -= 2 + + // +1 for putting the piece against a vertical boundary + if toX == 0 or toX == 7 then score += 1 + + // check neighboring pieces of the target position + for c in [-1, 1] + if toX+c < 0 or toX+c > 7 or toY-1 < 0 then continue + // +1 for each adjacent friendly piece + if board[toX+c][toY-1] < 0 then score += 1 + + // -1 for each opponent piece that could now jump this one + if toX-c >= 0 and toX-c <= 7 and toY+1 <= 7 and board[toX+c][toY-1] > 0 and ( + board[toX-c][toY+1] == 0 or (toX-c == fromX and toY+1 == fromY)) then score -= 2 + end for + return score +end function + +// Consider a possible (computer) move, including whether it's even valid. +// Return it or the previous best, whichever is better. +consider = function(fromX, fromY, toX, toY, previousBest) + + // make sure it's within the bounds of the board + if toX < 0 or toX > 7 or toY < 0 or toY > 7 then return previousBest + + // if it's an opponent's piece, consider jumping it instead + dx = toX - fromX + if board[toX][toY] > 0 and abs(dx) == 1 then + dy = toY - fromY + return consider(fromX, fromY, fromX + dx*2, fromY + dy*2, previousBest) + end if + + // if it's a jump, make sure it's over an opponent piece + if abs(dx) == 2 then + midX = (fromX + toX)/2; midY = (fromY + toY)/2 + if board[midX][midY] < 1 then return previousBest + end if + + // make sure the destination is empty + if board[toX][toY] then return previousBest + + // all checks passed; score it, and return whichever is better + rating = evalMove(fromX, fromY, toX, toY) + if not previousBest or rating > previousBest.rating then + newBest = {} + newBest.fromX = fromX; newBest.fromY = fromY + newBest.toX = toX; newBest.toY = toY + newBest.rating = rating + return newBest + else + return previousBest + end if +end function + +// Do the computer's turn +doComputerTurn = function + // For each square on the board containing one of my pieces, consider + // possible moves and keep the best one so far. Start with a step + // size of 1 (ordinary move), but if we jump, then keep jumping. + stepSize = 1 + while true + bestMove = null + for x in range(0, 7) + for y in range(0, 7) + if board[x][y] >= 0 then continue // not my piece + move = {} + move.fromPos = [x,y] + + // Consider forward moves; if it's a king, also consider backward moves + for dx in [-stepSize, stepSize] + move.toPos = [dx, -stepSize] + bestMove = consider(x, y, x+dx, y-stepSize, bestMove) + if board[x][y] == -2 then bestMove = consider(x, y, x+dx, y+stepSize, bestMove) + end for + end for + end for + + if not bestMove then + // No valid move -- if step size is still 1, this means we + // couldn't find ANY move on our turn, and we have lost. + // Otherwise, we're done. + if stepSize == 1 then + globals.gameOver = true + globals.winner = 1 + end if + break + end if + + // Do the move, and stop if we did not jump. + if stepSize == 1 then + print "From " + bestMove.fromX + " " + bestMove.fromY, "" + end if + print " to " + bestMove.toX + " " + bestMove.toY, "" + + movePiece bestMove.fromX, bestMove.fromY, bestMove.toX, bestMove.toY + if abs(bestMove.toX - bestMove.fromX) == 1 then break + stepSize = 2 + end while + print +end function + +// Move one piece (including captures and crowning +movePiece = function(fromX, fromY, toX, toY) + piece = board[fromX][fromY] + board[toX][toY] = piece + board[fromX][fromY] = 0 + // capture piece jumped over + if abs(toX - fromX) == 2 then + board[(fromX+toX)/2][(fromY+toY)/2] = 0 + end if + // crown (make into a king) a piece that reaches the back row + if (toY == 7 and piece > 0) or (toY == 0 and piece < 0) then + board[toX][toY] = 2 * sign(piece) + end if +end function + +// Handle the player's move. +doPlayerTurn = function + fromPos = inputPosition("From", 1, true) + fromX = fromPos[0]; fromY = fromPos[1] + if fromX < 0 then + // Player concedes the game. + globals.gameOver = true + globals.winner = -1 + return + end if + while true + toPos = inputPosition("To", 0) + toX = toPos[0]; toY = toPos[1] + dist = abs(toX - fromX) + if dist <= 2 and abs(toY - fromY) == dist then break + end while + while true + // Make the move, and continue as long as we have a jump + movePiece fromX, fromY, toX, toY + if dist != 2 then break + + // Prompt for another move, allowing a cancel (negative input). + fromX = toX; fromY = toX + toPos = inputPosition("+To", 0, true) + if toPos[0] < 0 or toPos[1] < 0 then break + toX = toPos[0]; toY = toPos[1] + end while + // If piece has reached the end of the board, crown this piece + if toY == 7 then board[toX][toY] = 2 +end function + +// Main loop. +gameOver = false +while not gameOver + doComputerTurn + if gameOver then break + printBoard + doPlayerTurn + if gameOver then break + printBoard +end while + +print +if winner > 0 then print "You win." else print "I win." diff --git a/00_Alternate_Languages/24_Chemist/MiniScript/README.md b/00_Alternate_Languages/24_Chemist/MiniScript/README.md new file mode 100644 index 000000000..82983edc2 --- /dev/null +++ b/00_Alternate_Languages/24_Chemist/MiniScript/README.md @@ -0,0 +1,22 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +0. Try-It! Page: +Go to https://miniscript.org/tryit/, clear the sample code from the code editor, and paste in the contents of chemist.ms. Then click the "Run Script" button. Program output (and input) will appear in the green-on-black terminal display to the right of or below the code editor. + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript chemist.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "chemist" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/24_Chemist/MiniScript/chemist.ms b/00_Alternate_Languages/24_Chemist/MiniScript/chemist.ms new file mode 100644 index 000000000..712792ce9 --- /dev/null +++ b/00_Alternate_Languages/24_Chemist/MiniScript/chemist.ms @@ -0,0 +1,30 @@ +print " "*33 + "Chemist" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print +print "The fictitious chemical kryptocyanic acid can only be" +print "diluted by the ratio of 7 parts water to 3 parts acid." +print "If any other ratio is attempted, the acid becomes unstable" +print "and soon explodes. Given the amount of acid, you must" +print "decide how much water to add for dilution. If you miss" +print "you face the consequences." + +deaths = 0 +while deaths < 9 + acid = floor(rnd * 50) + water = 7 * acid/3 + response = input(acid +" liters of kryptocyanic acid. How much water? ").val + diff = abs(water - response) + if diff > water/20 then + print " SIZZLE! You have just been desalinated into a blob" + print " of quivering protoplasm!" + deaths += 1 + if deaths < 9 then + print " However, you may try again with another life." + end if + else + print " Good job! You may breathe now, but don't inhale the fumes!" + print + end if +end while +print " Your 9 lives are used, but you will be long remembered for" +print " your contributions to the field of comic book chemistry." diff --git a/00_Alternate_Languages/25_Chief/MiniScript/README.md b/00_Alternate_Languages/25_Chief/MiniScript/README.md new file mode 100644 index 000000000..fa28aa67b --- /dev/null +++ b/00_Alternate_Languages/25_Chief/MiniScript/README.md @@ -0,0 +1,24 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +NOTE: I have added `wait` statements before and while printing the lightning bolt, without which it appears too quickly to be properly dramatic. + +Ways to play: + +0. Try-It! Page: +Go to https://miniscript.org/tryit/, clear the sample code from the code editor, and paste in the contents of chief.ms. Then click the "Run Script" button. Program output (and input) will appear in the green-on-black terminal display to the right of or below the code editor. + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript chief.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "chief" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/25_Chief/MiniScript/chief.ms b/00_Alternate_Languages/25_Chief/MiniScript/chief.ms new file mode 100644 index 000000000..272e6699e --- /dev/null +++ b/00_Alternate_Languages/25_Chief/MiniScript/chief.ms @@ -0,0 +1,50 @@ +print " "*30 + "Chief" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print +print "I am chief Numbers Freek, the great Indian math god." +yn = input("Are you ready to take the test you called me out for? ").lower +if not yn or yn[0] != "y" then + print "Shut up, pale face with wise tongue." +end if +print " Take a number and add 3. Divide this number by 5 and" +print "multiply by 8. Divide by 5 and add the same. Subtract 1." +b = input(" What do you have? ").val +c = (b+1-5)*5/8*5-3 +yn = input("I bet your number was " + c + ". Am I right? ").lower +if yn and yn[0] == "y" then + print "Bye!!!" +else + k = input("What was your original number? ").val + f=k+3 + g=f/5 + h=g*8 + i=h/5+5 + j=i-1 + print "So you think you're so smart, eh?" + print "Now watch." + print k + " plus 3 equals " + f +". This divided by 5 equals " + g + ";" + print "this times 8 equals " + h + ". If we divide by 5 and add 5," + print "we get " + i + ", which, minus 1, equals " + j + "." + yn = input("Now do you believe me? ").lower + if yn and yn[0] == "y" then + print "Bye!!!" + else + print "You have made me mad!!!" + print "There must be a great lightning bolt!" + print; print; wait 2 + for x in range(30, 22) + print " "*x + "x x"; wait 0.1 + end for + print " "*21 + "x xxx"; wait 0.1 + print " "*20 + "x x"; wait 0.1 + print " "*19 + "xx x"; wait 0.1 + for y in range(20, 13) + print " "*y + "x x"; wait 0.1 + end for + print " "*12 + "xx"; wait 0.1 + print " "*11 + "x"; wait 0.1 + print " "*10 + "*"; wait 0.1 + print; print"#########################"; print + print "I hope you believe me now, for your sake!!" + end if +end if diff --git a/00_Alternate_Languages/26_Chomp/MiniScript/README.md b/00_Alternate_Languages/26_Chomp/MiniScript/README.md new file mode 100644 index 000000000..2fce3ec72 --- /dev/null +++ b/00_Alternate_Languages/26_Chomp/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript chomp.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "chomp" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/26_Chomp/MiniScript/chomp.ms b/00_Alternate_Languages/26_Chomp/MiniScript/chomp.ms new file mode 100644 index 000000000..01b12f815 --- /dev/null +++ b/00_Alternate_Languages/26_Chomp/MiniScript/chomp.ms @@ -0,0 +1,102 @@ +print " "*33 + "Chomp" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print +// *** THE GAME OF CHOMP *** COPYRIGHT PCC 1973 *** + +initBoard = function(rows, columns) + globals.rows = rows + globals.columns = columns + globals.board = [[null]] // indexed as [row][column], 1-based + for i in range(1, rows) + board.push ["."] + ["*"] * columns + end for + board[1][1] = "P" +end function + +printBoard = function + print + print " "*7 + "1 2 3 4 5 6 7 8 9" + for i in range(1, rows) + print i + " "*6 + board[i][1:].join + end for + print +end function + +introduction = function + print + print "This is the game of chomp (Scientific American, Jan 1973)" + r = input("Do you want the rules (1=yes, 0=no!)? ").val + if r == 0 then return + print "Chomp is for 1 or more players (humans only)." + print + print "Here's how a board looks (this one is 5 by 7):" + initBoard 5, 7 + printBoard + print + print "The board is a big cookie - R rows high and C columns" + print "wide. You input R and C at the start. In the upper left" + print "corner of the cookie is a poison square (P). The one who" + print "chomps the poison square loses. To take a chomp, type the" + print "row and column of one of the squares on the cookie." + print "All of the squares below and to the right of that square" + print "(including that square, too) disappear -- chomp!!" + print "No fair chomping squares that have already been chomped," + print "or that are outside the original dimensions of the cookie." + print +end function + +setup = function + print + globals.numPlayers = input("How many players? ").val + rows = input("How many rows? ").val + while rows > 9 + rows = input("Too many rows (9 is maximum). Now, how many rows? ").val + end while + columns = input("How many columns? ").val + while rows > 9 + columns = input("Too many columns (9 is maximum). Now, how many columns? ").val + end while + print + initBoard rows, columns +end function + +doOneTurn = function(player) + printBoard + print "Player " + player + while true + inp = input("Coordinates of chomp (row,column)? ") + inp = inp.replace(",", " ").split + if inp.len < 2 then continue + r = inp[0].val + c = inp[-1].val + if 1 <= r <= rows and 1 <= c <= columns and board[r][c] != " " then break + print "No fair. You're trying to chomp on empty space!" + end while + if board[r][c] == "P" then + print "You lose, player " + player + globals.gameOver = true + else + + end if + for row in range(r, rows) + for col in range(c, columns) + board[row][col] = " " + end for + end for +end function + +// Main program +introduction +while true + setup + gameOver = false + player = 0 + while not gameOver + player += 1 + if player > numPlayers then player = 1 + doOneTurn player + end while + print + r = input("Again (1=yes, 0=no!)? ").val + if r != 1 then break +end while diff --git a/00_Alternate_Languages/27_Civil_War/MiniScript/README.md b/00_Alternate_Languages/27_Civil_War/MiniScript/README.md new file mode 100644 index 000000000..ad8e06ed8 --- /dev/null +++ b/00_Alternate_Languages/27_Civil_War/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript civilwar.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "civilwar" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/27_Civil_War/MiniScript/civilwar.ms b/00_Alternate_Languages/27_Civil_War/MiniScript/civilwar.ms new file mode 100644 index 000000000..30b1ce829 --- /dev/null +++ b/00_Alternate_Languages/27_Civil_War/MiniScript/civilwar.ms @@ -0,0 +1,437 @@ +print " "*26 + "Civil War" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print +// ORIGINAL GAME DESIGN: CRAM, GOODIE, HIBBARD LEXINGTON H.S. +// MODIFICATIONS: G. PAUL, R. HESS (TIES), 1973 +// Conversion to MiniScript: J. Strout, 2023 + +sides = ["Confederate", "Union"] + +getYesNo = function(prompt) + while true + yn = input(prompt + "? ").lower + if yn and yn[0] == "y" then return "YES" + if yn and yn[0] == "n" then return "NO" + print "Yes or no -- " + end while +end function + +instructions = function + print; print; print; print + print "This is a civil war simulation." + print "To play type a response when the computer asks." + print "Remember that all factors are interrelated and that your" + print "responses could change history. Facts and figures used are" + print "based on the actual occurrence. Most battles tend to result" + print "as they did in the civil war, but it all depends on you!!" + print + print "The object of the game is to win as many battles as "; + print "possible." + print + print "Your choices for defensive strategy are:" + print " (1) artillery attack" + print " (2) fortification against frontal attack" + print " (3) fortification against flanking maneuvers" + print " (4) falling back" + print " Your choices for offensive strategy are:" + print " (1) artillery attack" + print " (2) frontal attack" + print " (3) flanking maneuvers" + print " (4) encirclement" + print "You may surrender by typing a '5' for your strategy." +end function + +historicalBattles = [] +addBattle = function(name, men0, men1, cas0, cas1, offDef, desc) + battle = {} + battle.name = name + battle.men = [men0, men1] + battle.casualties = [cas0, cas1] + battle.offDef = offDef // 1=Confederate Defense, 2=both Offense, 3=Confederate Offense + battle.description = desc + historicalBattles.push battle +end function + +loadData = function + // Historical data + addBattle "Bull Run",18000,18500,1967,2708,1, [ +"July 21, 1861. Gen. Beauregard, commanding the South, met", +"Union forces with Gen. Mcdowell in a premature battle at", +"Bull Run. Gen. Jackson helped push back the Union attack."] + + addBattle "Shiloh",40000.,44894.,10699,13047,3, [ +"April 6-7, 1862. The Confederate surprise attack at", +"Shiloh failed due to poor organization."] + + addBattle "Seven Days",95000.,115000.,20614,15849,3, [ +"June 25-july 1, 1862. General Lee (CSA) upheld the", +"offensive throughout the battle and forced Gen. McClellan", +"and the Union forces away from Richmond."] + + addBattle "Second Bull Run",54000.,63000.,10000,14000,2, [ +"Aug 29-30, 1862. The combined Confederate forces under Lee", +"and Jackson drove the Union forces back into washington."] + + addBattle "Antietam",40000.,50000.,10000,12000,3, [ +"Sept 17, 1862. The South failed to incorporate Maryland", +"into the Confederacy."] + + addBattle "Fredericksburg",75000.,120000.,5377,12653,1, [ +"Dec 13, 1862. The Confederacy under Lee successfully", +"repulsed an attack by the Union under Gen. Burnside."] + + addBattle "Murfreesboro",38000.,45000.,11000,12000,1, [ +"Dec 31, 1862. The South under Gen. Bragg won a close battle."] + + addBattle "Chancellorsville",32000,90000.,13000,17197,2, [ +"May 1-6, 1863. The South had a costly victory and lost", +"one of their outstanding generals, 'Stonewall' Jackson."] + + addBattle "Vicksburg",50000.,70000.,12000,19000,1, [ +"July 4, 1863. Vicksburg was a costly defeat for the South", +"because it gave the Union access to the Mississippi."] + + addBattle "Gettysburg",72500.,85000.,20000,23000,3, [ +"July 1-3, 1863. A Southern mistake by Gen. Lee at Gettysburg", +"cost them one of the most crucial battles of the war."] + + addBattle "Chickamauga",66000.,60000.,18000,16000,2, [ +"Sept. 15, 1863. Confusion in a forest near Chickamauga led", +"to a costly Southern victory."] + + addBattle "Chattanooga",37000.,60000.,36700.,5800,2, [ +"Nov. 25, 1863. After the South had sieged Gen. Rosencrans'", +"army for three months, Gen. Grant broke the siege."] + + addBattle "Spotsylvania",62000.,110000.,17723,18000,2, [ +"May 5, 1864. Grant's plan to keep Lee isolated began to", +"fail here, and continued at Cold Harbor and Petersburg."] + + addBattle "Atlanta",65000.,100000.,8500,3700,1, [ +"August, 1864. Sherman and three veteran armies converged", +"on Atlanta and dealt the death blow to the Confederacy."] + +end function + +setup = function + print; print; print + if getYesNo("Are there two generals present") == "YES" then + globals.numPlayers = 2 + else + globals.numPlayers = 1 + print; print "You are the Confederacy. Good luck!" + print + end if + print "Select a battle by typing a number from 1 to 14 on" + print "request. Type any other number to end the simulation." + print "But '0' brings back exact previous battle situation" + print "allowing you to replay it." + print + print "Note: a negative food$ entry causes the program to " + print "use the entries from the previous battle." + print + yn = getYesNo("After requesting a battle, do you wish battle descriptions") + globals.battleDesc = (yn == "YES") +end function + +// Game variables -- the 2-element arrays below represent the stats for +// player 0 (Confederacy) and 1 (Union) +D = [0,0] // money +F = [0,0] // food budget +H = [0,0] // salary budget +B = [0,0] // ammo budget +men = [0,0] // men at start of battle? (M1, M2) +menAdded = [0,0] // men added (M3, M4) +menAvail = [0,0] // men available (M5, M6) +P = [0,0] // casualties? +T = [0,0] // total losses? +resources = [0,0] // total resources (money) available to each side (R1, R2) +spending = [0,0] // total expenditures for each side (Q1, Q2) +morale = [0,0] // troop morale (O) +strategy = [0,0] // strategy choice (Y1, Y2) +inflation = [0,0] // inflation (I1, I2) + +R = 0 // previous battle +losses = 0 // Confederate losses +wins = 0 // Confederate wins +unresolved = 0 // battles where neither side clearly won + +printInColumns = function(a, b, c) + print (a + " "*20)[:20] + (b + " "*16)[:16] + c +end function + +pickBattle = function + print; print; print + globals.replay = false + while true + num = input("Which battle do you wish to simulate? ").val + if num == 0 and R > 0 then + num = R + globals.replay = true + end if + if 0 < num <= historicalBattles.len then break + end while + globals.bat = historicalBattles[num - 1] + if not replay then + men[0] = bat.men[0] + men[1] = bat.men[1] + + // inflation calc + inflation[0] = 10 + (losses - wins) * 2 + inflation[1] = 10 + (wins - losses) * 2 + + // money available + D[0] = 100 * floor((men[0]*(100-inflation[0])/2000) * (1+(resources[0]-spending[0])/(resources[0]+1))+0.5) + D[1] = 100 * floor(men[1]*(100-inflation[1])/2000 + 0.5) + if numPlayers == 2 then + D[1] = 100 * floor((men[1]*(100-inflation[1])/2000) * (1+(resources[1]-spending[1])/(resources[1]+1))+0.5) + end if + + // men available + menAvail[0] = floor(men[0]*(1 + (P[0]-T[0])/(menAdded[0]+1))) + menAvail[1] = floor(men[1]*(1 + (P[1]-T[1])/(menAdded[1]+1))) + globals.F1 = 5/6 * men[0] // ?!? + print; print; print; print; print +print "P:" + P + "; T:" + T + "; menAdded:" + menAdded +print "men[0]:" + men[0] + " ...F1:" + F1 + + print "This is the battle of " + bat.name + if battleDesc then + for line in bat.description + print line + end for + end if + else + print bat.name + " Instant Replay" + end if + print + printInColumns " ", "CONFEDERACY", " UNION" + printInColumns "MEN", " "+menAvail[0], " "+menAvail[1] + printInColumns "MONEY", "$ "+D[0], "$ "+D[1] + printInColumns "INFLATION", " "+(inflation[0]+15)+" %", " " +inflation[1]+" %" + // (Note: printout lies and shows confederate inflation 15% higher than actual) + print +end function + +getBudget = function(player) + print sides[player] + " General---How much do you wish to spend for" + getNum = function(prompt, allowNegative) + while true + n = input(prompt) + if not n then continue + if n[-1] == "k" then n = n[:-1] + "000" // (allow entries like "60k") + if n.val >= 0 or allowNegative then return n.val + print "Negative values are not allowed." + end while + end function + + while true + f = getNum(" - Food......? ", true) + if f < 0 then + if resources[player] == 0 then + print "No previous entries." + print "How much do you wish to spend for" + continue + end if + break // keep all previous entries + end if + F[player] = f + H[player] = getNum(" - Salaries..? ", false) + B[player] = getNum(" - Ammunition? ", false) + if F[player] + H[player] + B[player] <= D[player] then break + print "Think again! You have only $" + D[player] + end while +end function + +calcMorale = function(player) + m = ((2*F[player]^2 + H[player]^2) / F1^2 + 1) + print (" "*11 + sides[player])[-11:], " " + if m >= 10 then + print "morale is high" + else if m >= 5 then + print "morale is fair" + else + print "morale is poor" + end if + morale[player] = m +end function + +getStrategy = function(player) + if numPlayers == 1 then prompt = "Your strategy" else prompt = sides[player] + " strategy" + while true + strat = input(prompt + "? ").val + if 0 < strat < 6 then break + print "Strategy " + strat + " not allowed." + end while + strategy[player] = strat + if strat == 5 then + print "The " + ["Confederacy", "Union"][player] + " has surrendered." + globals.gameOver = true + end if +end function + +calcComputerStrategy = function + // Union strategy is computer chosen + print "Union strategy is ", "" + s0 = 0 + r = 100 * rnd + for i in range(0, 3) + s0 += unionStrats[i] + if r < s0 then + strategy[1] = i+1 + break + end if + end for + print strategy[1] +end function + +learnStrategy = function + // Learn present strategy, start forgetting old ones. + // - Present strategy of south gains 3*s, others lose s + // probability points, unless a strategy falls below 5%. + s = 3; s0 = 0 + for i in range(0,3) + if unionStrats[i] < 5 then continue + unionStrats[i] -= s + s0 += s + end for + unionStrats[strategy[1]-1] += s0 +end function + +doBattle = function + U = 0; U2 = 0 + // simulated losses -- North + C6 = (2 * bat.casualties[1]/5) * (1+1/(2*(abs(strategy[1]-strategy[0])+1))) + C6 = C6 * (1.28 + (5*men[1]/6) / (B[1]+1)) + C6 = floor(C6 * (1+1/morale[1]) + .5) + // - IF LOSS > MEN PRESENT, RESCALE LOSSES + E2 = 100/morale[1] // desertions (Union) + if floor(C6 + E2) >= menAvail[1] then + C6 = floor(13*menAvail[1]/20) + E2 = 7 * C6/13 + U2=1 // Union loss + end if + // simulated losses -- South + C5 = (2 * bat.casualties[0]/5) * (1+1/(2*(abs(strategy[1]-strategy[0])+1))) + print "step A:" + C5 + C5 = floor(C5 * (1+1/morale[0])*(1.28+F1/(B[0]+1))+.5) + print "step B:" + C5 + print "based on morale[0]:"+morale[0]+", F1:"+F1+", B[0]:"+B[0] + E=100/morale[0] + if C5+100/morale[0] >= men[0]*(1+(P1-T1)/(menAdded[0]+1)) then + C5 = floor(13*men[0]/20 * (1+(P1-T1)/(menAdded[0]+1))) + print "step C:" + C5 + print "men: " + men; print "menAdded: " + menAdded; print "P1,T1:" + P1 + "," + T1 + E=7*C5/13 // desertions (Confed) + U=1 // Confederate loss + end if + print + print; print; printInColumns "", "CONFEDERACY", "UNION" + if numPlayers == 1 then + C6 = floor(17 * bat.casualties[1] * bat.casualties[0] / (C5*20)) + E2 = 5 * morale[0] + end if + printInColumns "CASUALTIES", C5, C6 + printInColumns "DESERTIONS", floor(E), floor(E2) + print + if numPlayers == 2 then + print "Compared to the actual casualties at " + bat.name + print "Confederate: " + round(100*C5/bat.casualties[0]) + "% of the original" + print "Union: " + round(100*C6/bat.casualties[1]) + "% of the original" + print + // Who won? + print "U:"+U+ " U2:"+U2 + if (U == 1 and U2 != 1) or (U == U2 and C5+E > C6+E2) then + print "The Union wins " + bat.name + globals.losses += 1 + else if (U2 == 1 and U != 1) or (U == U2 and C5+E < C6+E2) then + print "The confederacy wins " + bat.name + globals.wins += 1 + else + print "Battle outcome unresolved" + globals.unresolved += 1 + end if + else + print "Your casualties were " + round(100*C5/bat.casualties[0]) + "% of" + print "the actual casualties at " + bat.name + print + if U == 1 or C5+E >= 17*bat.casualties[1]*bat.casualties[0]/(C5*20)+5*morale[0] then + print "You lose " + bat.name + if not replay then globals.losses += 1 + else + print "You win " + bat.name + if not replay then globals.wins += 1 + end if + end if + if not replay then + // Cumulative battle factors which alter historical + // resources availeble. (If a replay, don't update.) + T[0] += C5 + E + T[1] += C6 + E2 + P[0] += bat.casualties[0] + P[1] += bat.casualties[1] + spending[0] += F[0] + H[0] + B[0] + spending[1] += F[1] + H[1] + B[1] + resources[0] += men[0]*(100-inflation[0])/20 + resources[1] += men[1]*(100-inflation[1])/20 + menAdded[0] += men[0] + menAdded[1] += men[1] + + learnStrategy + end if + print "---------------" +end function + +// Main Program +loadData +unionStrats = [25, 25, 25, 25] +P1=0; P2=0; T1=0; T2=0 // cumulative stat thingies +print +if getYesNo("Do you want instructions") == "YES" then instructions +setup +gameOver = false +while not gameOver + pickBattle + if gameOver then break + for i in range(0, numPlayers-1) + getBudget i + end for + for i in range(0, numPlayers-1) + calcMorale i + end for + print "Confederate General---", "" + if bat.offDef == 3 then + print "you are on the offensive" + else if bat.offDef == 1 then + print "you are on the defensive" + else + print "both sides are on the offensive " + end if + print + getStrategy 0 + if numPlayers == 2 then getStrategy 1 else calcComputerStrategy + if gameOver then break + doBattle +end while + +// Finish off +print; print; print; print; print; print +print "The Confederacy has won " + wins + " battles and lost " + losses +if strategy[0] == 5 or (strategy[1] != 5 and losses > wins) then + print "The Union has won the war" +else + print "The Confederacy has won the war" +end if +print "For the " + (wins + losses + unresolved) + " battles fought (excluding reruns)" +print +printInColumns "", "Confederacy", "Union" +printInColumns "Historical Losses", round(P[0]), round(P[1]) +printInColumns "Simulated Losses", round(T[0]), round(T[1]) +print +printInColumns " % of Original", round(100*T[0]/P[0]), round(100*T[1]/P[1]) +if numPlayers == 1 then + print + print "Union intelligence suggsets that the South used " + print "strategies 1, 2, 3, 4 in the following percentages" + print unionStrats.join +end if diff --git a/00_Alternate_Languages/28_Combat/MiniScript/README.md b/00_Alternate_Languages/28_Combat/MiniScript/README.md new file mode 100644 index 000000000..0963185f3 --- /dev/null +++ b/00_Alternate_Languages/28_Combat/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript combat.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "combat" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/28_Combat/MiniScript/combat.ms b/00_Alternate_Languages/28_Combat/MiniScript/combat.ms new file mode 100644 index 000000000..c5f6596af --- /dev/null +++ b/00_Alternate_Languages/28_Combat/MiniScript/combat.ms @@ -0,0 +1,165 @@ +print " "*33 + "Combat" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print + +printInColumns2 = function(a, b, lineBreak=true) + print (a+" "*16)[:16] + (b+" "*16)[:16], "" + if lineBreak then print +end function + +printInColumns3 = function(a, b, c, lineBreak=true) + print (a+" "*16)[:16] + (b+" "*16)[:16] + (c+" "*16)[:16], "" + if lineBreak then print +end function + +// computer forces: +d = 30000 // army +e = 20000 // navy +f = 22000 // air force + +print "I am at war with you."; print "We have 72000 soldiers apiece." +while true + print; print "Distribute your forces." + printInColumns3 "", "ME", "YOU" + printInColumns2 "army", d, false + a = input("?").val + printInColumns2 "navy", e, false + b = input("?").val + printInColumns2 "a. f.", f, false + c = input("?").val + if a+b+c <= 72000 then break +end while + + +print "You attack first. Type (1) for army; (2) for navy;" +print "and (3) for air force." +y = input.val +while true + x = input("How many men? ").val + if x < 0 then continue + if y <= 1 or y > 3 then + // Army attack + if x > a then continue + if x < a/3 then + print "You lost "+x+" men from your army." + a=floor(a-x) + else if x < 2*a/3 then + print "You lost " + floor(x/3) + " men, but I lost " + floor(2*d/3) + a=floor(a-x/3) + d=0 // (message above lied!) + else + print "You sunk one of my patrol boats, but I wiped out two" + print "of your air force bases and 3 army bases." + a=floor(a/3) + c=floor(c/3) + e=floor(2*e/3) + end if + else if y == 2 then + // Naval attack + if x > b then continue + if x < e/3 then + print "Your attack was stopped!" + b = floor(b-x) + else if x < 2*e/3 then + print "You destroyed " + floor(2*e/3) + "of my army." + e=floor(e/3) + else + print "You sunk one of my patrol boats, but I wiped out two" + print "of your air force bases and 3 army bases." + a=floor(a/3) + c=floor(c/3) + e=floor(2*e/3) + end if + else + // Air force attack + if x > c then continue + if x < c/3 then + print "Your attack was wiped out." + c = floor(c-x) + else if x < 2*c/3 then + print "We had a dogfight. You won - and finished your mission." + d=floor(2*d/3) + e=floor(e/3) + f=floor(f/3) + else + print "You wiped out one of my army patrols, but I destroyed" + print "two navy bases and bombed three army bases." + a=floor(a/4) + b=floor(b/3) + d=floor(2*d/3) + end if + end if + break +end while + +result = null // 1 you win, -1 you lose, 0 tie (treaty) +print +printInColumns3 "", "YOU", "ME" +printInColumns3 "army", a, d +printInColumns3 "navy", b, e +printInColumns3 "a. f.", c, f +print "What is your next move?" +print "army=1 navy=2 air force=3" +g = input.val +while true + t = input("How many men? ").val + if t < 0 then continue + if g <= 1 or g > 3 then + // Army attack + if t > a then continue + if t < d/2 then + print "I wiped out your attack!" + a = a-t + else + print "You destroyed my army!" + d=0 + end if + else if g == 2 then + // Naval attack + if t > b then continue + if t < e/2 then + print "I sunk two of your battleships, and my air force" + print "wiped out your ungaurded capitol." // (sic) + a = a/4 + b = b/2 + else + print "Your navy shot down three of my xiii planes," + print "and sunk three battleships." + f = 2*f/3 + e = (e/2) + end if + else + // Air Force attack + if t > c then continue + if t > f/2 then + print "My navy and air force in a combined attack left" + print "your country in shambles." + a = a/3 + b = b/3 + c = c/3 + else + print "One of your planes crashed into my house. I am dead." + print "My country fell apart." + result = 1 + end if + end if + break +end while + +if result == null then + print + print "From the results of both of your attacks," + result = 0 + if a+b+c > 3/2*(d+e+f) then result = 1 + if a+b+c < 2/3*(d+e+f) then result = -1 +end if + +if result == 0 then + print "the treaty of paris concluded that we take our" + print "respective countries and live in peace." +else if result == 1 then + print "You won, oh! shucks!!!!" +else + print "You lost-I conquered your country. It serves you" + print "right for playing this stupid game!!!" +end if diff --git a/00_Alternate_Languages/29_Craps/MiniScript/README.md b/00_Alternate_Languages/29_Craps/MiniScript/README.md new file mode 100644 index 000000000..e500f4c36 --- /dev/null +++ b/00_Alternate_Languages/29_Craps/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript craps.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "craps" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/29_Craps/MiniScript/craps.ms b/00_Alternate_Languages/29_Craps/MiniScript/craps.ms new file mode 100644 index 000000000..900ba5d43 --- /dev/null +++ b/00_Alternate_Languages/29_Craps/MiniScript/craps.ms @@ -0,0 +1,87 @@ +// Craps +// Ported from BASIC to MiniScript by Joe Strout (2023) + +print " "*33 + "CRAPS" +print " "*15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" +print; print; print + +print "2,3,12 are losers; 4,5,6,8,9,10 are points; 7,11 are natural winners." +winnings = 0 + +rollDice = function + wait 0.5 // (a small delay makes the game more dramatic) + return ceil(rnd*6) + ceil(rnd*6) +end function + +doOneBet = function + while true + wager = input("Input the amount of your wager: ").val + if wager > 0 then break + end while + print "I will now throw the dice." + result = 0 // 1 if we win, 2 if win double, -1 if we lose + roll = rollDice + if roll == 7 or roll == 11 then + print roll + "- natural....a winner!!!!" + print roll + " pays even money, you win " + wager + " dollars" + result = 1 + else if roll == 2 then + print "2- snake eyes....you lose." + print "You lose " + wager + " dollars." + result = -1 + else if roll == 3 or roll == 12 then + print roll + " - craps...you lose." + print "You lose " + wager + " dollars." + result = -1 + else + point = roll + print point + " is the point. I will roll again" + while true + roll = rollDice + if roll == 7 then + print "7 - craps. You lose." + print "You lose $" + wager + result = -1 + break + else if roll == point then + print roll + "- a winner.........CONGRATS!!!!!!!!" + print roll + " at 2 to 1 odds pays you...let me see..." + + (2*wager) + " dollars" + result = 2 + break + else + print roll + " - no point. I will roll again" + end if + end while + end if + globals.winnings += wager * result +end function + +// Main loop. +while true + doOneBet + + while true + // Why you have to enter 5 to continue is anyone's guess, + // but that's what the original program did. + again = input(" If you want to play again print 5 if not print 2: ") + if again == "5" or again == "2" then break + end while + + if winnings < 0 then + print "You are now under $" + (-winnings) + else if winnings > 0 then + print "You are now ahead $" + winnings + else + print "You are now even at 0" + end if + if again != "5" then break +end while + +if winnings < 0 then + print "Too bad, you are in the hole. Come again." +else if winnings > 0 then + print "Congratulations---You came out a winner. Come again!" +else + print "Congrtulations---You came out even, not bad for an amateur" +end if diff --git a/00_Alternate_Languages/29_Craps/nim/craps.nim b/00_Alternate_Languages/29_Craps/nim/craps.nim new file mode 100644 index 000000000..770b6c0e1 --- /dev/null +++ b/00_Alternate_Languages/29_Craps/nim/craps.nim @@ -0,0 +1,68 @@ +import std/[random,strutils] + +var + wager, winnings, rollResult: int + stillplaying: bool = true + +randomize() # Seed the random number generator + +proc tryAgain(): bool = + echo "WANT TO PLAY AGAIN? (YES OR NO)" + var answer = readLine(stdin).normalize() + result = (answer == "y") or (answer == "yes") + +proc takePoint(point: int) = + var flag = true + while flag: + var pointRoll: int = (rand 1..6) + (rand 1..6) # roll dice, then add the sum + if pointRoll == 7: + echo pointRoll, "- CRAPS. YOU LOSE." + echo "YOU LOSE ", wager, " DOLLARS." + winnings -= wager + flag = false + if pointRoll == point: + echo point, "- A WINNER.........CONGRATS!!!!!!!!" + echo "AT 2 TO 1 ODDS PAYS YOU...LET ME SEE... ", 2*wager, " DOLLARS" + winnings += (2*wager) + flag = false + if flag: + echo pointRoll, " - NO POINT. I WILL ROLL AGAIN" + +echo spaces(33), "CRAPS" +echo spaces(15), "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" +echo "\n" +echo "2,3,12 ARE LOSERS; 4,5,6,8,9,10 ARE POINTS; 7,11 ARE NATURAL WINNERS." +winnings = 0 + +# play the game +while stillplaying: + echo "" + echo "INPUT THE AMOUNT OF YOUR WAGER." + wager = readline(stdin).parseInt() + echo "I WILL NOW THROW THE DICE" + rollResult = (rand 1..6) + (rand 1..6) # roll dice, then add the sum + case rollResult: + of 7, 11: + echo rollResult, "- NATURAL....A WINNER!!!!" + echo rollResult, " PAYS EVEN MONEY, YOU WIN ", wager, " DOLLARS" + winnings += wager + of 2: + echo rollResult, "- SNAKE EYES....YOU LOSE." + echo "YOU LOSE ", wager, " DOLLARS." + winnings -= wager + of 3, 12: + echo rollResult, "- CRAPS...YOU LOSE." + echo "YOU LOSE ", wager, " DOLLARS." + winnings -= wager + else: + echo rollResult, " IS THE POINT. I WILL ROLL AGAIN" + takePoint(rollResult) + if winnings < 0: echo "YOU ARE NOW UNDER $", winnings + if winnings > 0: echo "YOU ARE NOW AHEAD $", winnings + if winnings == 0: echo "YOU ARE NOW EVEN AT 0" + stillplaying = tryAgain() + +# done playing +if winnings < 0: echo "TOO BAD, YOU ARE IN THE HOLE. COME AGAIN." +if winnings > 0: echo "CONGRATULATIONS---YOU CAME OUT A WINNER. COME AGAIN!" +if winnings == 0: echo "CONGRATULATIONS---YOU CAME OUT EVEN, NOT BAD FOR AN AMATEUR" diff --git a/00_Alternate_Languages/30_Cube/MiniScript/README.md b/00_Alternate_Languages/30_Cube/MiniScript/README.md new file mode 100644 index 000000000..17eeb20b7 --- /dev/null +++ b/00_Alternate_Languages/30_Cube/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript cube.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "cube" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/30_Cube/MiniScript/cube.ms b/00_Alternate_Languages/30_Cube/MiniScript/cube.ms new file mode 100644 index 000000000..1639ce43f --- /dev/null +++ b/00_Alternate_Languages/30_Cube/MiniScript/cube.ms @@ -0,0 +1,100 @@ +print " "*34 + "Bullseye" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print + +if input("Do you want to see the instructions? (yes--1,no--0) ").val then + print "This is a game in which you will be playing against the" + print "random decision of the computer. The field of play is a" + print "cube of size 3. Any of the locations can be designated" + print "by inputing three numbers such as 2,3,1. At the start," + print "you are automatically at location 1,1,1. The object of" + print "the game is to get to location 3,3,3. One minor detail:" + print "the computer will pick, at random, locations at which" + print "it will plant land mines. If you hit one of these locations" + print "you lose. One other detail: you may move only one space " + print "in one direction each move. For example: from 1,1,2 you" + print "may move to 2,1,2 or 1,1,3. You may not change" + print "two of the numbers on the same move. If you make an illegal" + print "move, you lose and the computer takes the money you may" + print "have bet on that round." + print + print + print "All yes or no questions will be answered by a 1 for yes" + print "or a 0 (zero) for no." + print + print "When stating the amount of a wager, print only the number" + print "of dollars (example: 250). You are automatically started with" + print "500 dollars in your account." + print + print "Good luck!" +end if + +money = 500 + +while money >= 0 + mineLocs = [] + // Note: unlike the original BASIC program, which doesn't actually pick random + // numbers unless you manually set X to a positive value before running the + // program, we do pick five random mine locations here. + // But like that program, we make no attempt to avoid 1,1,1 or 3,3,3, or to + // ensure that we have picked five DIFFERENT locations. + for i in range(1,5) + mineLocs.push [floor(3 * rnd + 1), floor(3 * rnd + 1), floor(3 * rnd + 1)] + end for + wager = 0 + if input("Want to make a wager? ").val then + wager = input("How much? ").val + while money < wager + wager = input("Tried to fool me; bet again? ").val + end while + end if + position = [1,1,1] + print + inp = input("It's your move: ") + won = 0 + while true + inp = inp.replace(",", " ").replace(" ", " ").split + newPos = [] + if inp.len == 3 then newPos = [inp[0].val, inp[1].val, inp[2].val] + legal = newPos.len == 3 + totalChange = 0 + for i in newPos.indexes + // The original game allowed you to walk outside the 1-3 range, + // thus safely avoiding all the mines. To disallow this exploit, + // uncomment the following line. + //if newPos[i] < 1 or newPos[i] > 3 then legal = false + totalChange += abs(newPos[i] - position[i]) + end for + if totalChange > 1 then legal = false + if not legal then + print; print "Illegal move. You lose." + won = -wager + break + end if + if newPos == [3,3,3] then + print "Congratulations!" + won = wager + break + else if mineLocs.indexOf(newPos) != null then + print "******BANG******" + print "You lose!" + print + print + won = -wager + break + end if + position = newPos + inp = input("Next move: ") + end while + if won != 0 then + globals.money += won + if money <= 0 then print "You bust." + end if + if wager then + print " You now have " + money + " dollars." + end if + if not input("Do you want to try again? ").val then break +end while +print "Tough luck!" +print +print "Goodbye." diff --git a/00_Alternate_Languages/31_Depth_Charge/MiniScript/README.md b/00_Alternate_Languages/31_Depth_Charge/MiniScript/README.md new file mode 100644 index 000000000..0c20ea36f --- /dev/null +++ b/00_Alternate_Languages/31_Depth_Charge/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript depthcharge.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "depthcharge.ms" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/31_Depth_Charge/MiniScript/depthcharge.ms b/00_Alternate_Languages/31_Depth_Charge/MiniScript/depthcharge.ms new file mode 100644 index 000000000..3607cb805 --- /dev/null +++ b/00_Alternate_Languages/31_Depth_Charge/MiniScript/depthcharge.ms @@ -0,0 +1,116 @@ +// Depth Charge +// Originally by Dana Noftle (1978) +// Translated from BASIC to MiniScript by Ryushinaka and Joe Strout (2023) + +askYesNo = function(prompt) + while true + answer = input(prompt).lower[:1] + if answer == "y" or answer == "n" then return answer + end while +end function + +showWelcome = function + print + print " "*34 + "Depth Charge" + print " "*15 + "Creative Computing Morristown, New Jersey" + print +end function + +setup = function + while true + globals.size = input("Dimension of search area? ").val + if size > 0 then break + end while + + globals.numCharges = ceil(log(size,2)) + if numCharges == 0 then globals.numCharges = 1 // ensure we have at least 1 shot + + print "You are the captain of the destroyer USS Computer." + print "An enemy sub has been causing you trouble. Your" + print "mission is to destroy it. You have " + numCharges + " shots." + print "Specify depth charge explosion point with a" + print "trio of numbers -- the first two are the" + print "surface coordinates; the third is the depth." +end function + +showShotResult = function(shot,location) + result = "Sonar reports shot was " + if shot[1] > location[1] then + result = result + "north" + else if shot[1] < location[1] then + result = result + "south" + end if + + if shot[0] > location[0] then + result = result + "east" + else if shot[0] < location[0] then + result = result + "west" + end if + + if shot[1] != location[1] or shot[0] != location[0] then + result = result + " and " + end if + + if shot[2] > location[2] then + result = result + "too low." + else if shot[2] < location[2] then + result = result + "too high." + else + result = result + "depth OK." + end if + + print result +end function + +getShot = function + shotPos = [0,0,0] + while true + rawGuess = input("Enter coordinates: ").split(" ") + if rawGuess.len == 3 then + shotPos[0] = rawGuess[0].val + shotPos[1] = rawGuess[1].val + shotPos[2] = rawGuess[2].val + return shotPos + else + print "Please enter coordinates separated by spaces" + print "Example: 3 2 1" + end if + end while +end function + + +playGame = function + print "Good luck!" + print + + subPos = [floor(rnd*size), floor(rnd*size), floor(rnd*size)] + + // For debugging, you can give away the answer: + //print "(Sub is hidden at: " + subPos.join(" ") + ")" + + for c in range(1, numCharges) + print "Trial " + c + + shot = getShot + + if shot[0] == subPos[0] and shot[1] == subPos[1] and shot[2] == subPos[2] then + print "B O O M ! ! You found it in " + c + " tries!" + return + else + showShotResult(shot,subPos) + end if + end for + + print "You have been torpedoed! Abandon ship!" + print "The submarine was at " + subPos.join(" ") + "." +end function + +showWelcome +setup +while true + playGame + if askYesNo("Another game (Y or N): ") == "n" then + print "OK. Hope you enjoyed yourself." + break + end if +end while diff --git a/00_Alternate_Languages/32_Diamond/MiniScript/README.md b/00_Alternate_Languages/32_Diamond/MiniScript/README.md new file mode 100644 index 000000000..3a8249a0d --- /dev/null +++ b/00_Alternate_Languages/32_Diamond/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript diamond.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "diamond" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/32_Diamond/MiniScript/diamond.ms b/00_Alternate_Languages/32_Diamond/MiniScript/diamond.ms new file mode 100644 index 000000000..8166b570a --- /dev/null +++ b/00_Alternate_Languages/32_Diamond/MiniScript/diamond.ms @@ -0,0 +1,23 @@ +// Diamond +// +// Ported from BASIC to MiniScript by Joe Strout + +print " "*33 + "DIAMOND" +print " "*15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" +print; print; print +print "For a pretty diamond pattern," +maxw = input("type in an odd number between 5 and 21: ").val +s = "CC" + "!" * maxw +columns = floor(68/maxw) +for row in range(1, columns) + for w in range(1, maxw, 2) + range(maxw-2, 1, -2) + print " "*(maxw-w)/2, "" + for column in range(1, columns) + print s[:w], "" + if column < columns then print " "*(maxw-w), "" + end for + print + wait 0.01 + end for +end for + diff --git a/00_Alternate_Languages/33_Dice/MiniScript/README.md b/00_Alternate_Languages/33_Dice/MiniScript/README.md new file mode 100644 index 000000000..b670e30da --- /dev/null +++ b/00_Alternate_Languages/33_Dice/MiniScript/README.md @@ -0,0 +1,21 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript dice.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "dice" + run +``` +3. "Try-It!" page on the web: +Go to https://miniscript.org/tryit/, clear the default program from the source code editor, paste in the contents of dice.ms, and click the "Run Script" button. diff --git a/00_Alternate_Languages/33_Dice/MiniScript/dice.ms b/00_Alternate_Languages/33_Dice/MiniScript/dice.ms new file mode 100644 index 000000000..38b41ef34 --- /dev/null +++ b/00_Alternate_Languages/33_Dice/MiniScript/dice.ms @@ -0,0 +1,53 @@ +// Dice +// +// Danny Freidus +// Ported from BASIC to MiniScript by Joe Strout + +print " "*34 + "DICE" +print " "*15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" +print; print; print +print "This program simulates the rolling of a" +print "pair of dice." +print "You enter the number of times you want the computer to" +print "'roll' the dice. Watch out, very large numbers take" +print "a long time. In particular, numbers over 5000." + +// Function to do one run of the simulation. +runOnce = function + // Clear the array we'll use to hold the counts + counts = [0] * 13 + // Loop the desired number of times + x = input("How many rolls? ").val + for s in range(1, x) + // roll two dice and find the sum + die1 = ceil(6 * rnd) + die2 = ceil(6 * rnd) + sum = die1 + die2 + // update the count for that sum + counts[sum] += 1 + end for + print + + // print a table showing how many times each sum was rolled + print "Total Spots Number of Times" + for v in range(2, 12) + // (the [-6:] trick below right-aligns the number) + print (" " + v)[-6:] + " "*10 + counts[v] + end for + print; print +end function + +// Get a yes/no (or at least y/n) response from the user. +askYesNo = function(prompt) + while true + answer = input(prompt).lower[:1] + if answer == "y" or answer == "n" then return answer + end while +end function + +// main loop +while true + print + runOnce + if askYesNo("Try again? ") == "n" then break +end while diff --git a/00_Alternate_Languages/33_Dice/nim/dice.nim b/00_Alternate_Languages/33_Dice/nim/dice.nim new file mode 100644 index 000000000..6564ae9ba --- /dev/null +++ b/00_Alternate_Languages/33_Dice/nim/dice.nim @@ -0,0 +1,37 @@ +import std/[random,strutils] + +var + a,b,r,x: int + f: array[2..12, int] + z: string + retry: bool = true + +randomize() # Seed the random number generator + +echo spaces(34), "DICE" +echo spaces(15), "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" +echo "\n" +echo "THIS PROGRAM SIMULATES THE ROLLING OF A PAIR OF DICE." +echo "YOU ENTER THE NUMBER OF TIMES YOU WANT THE COMPUTER TO" +echo "'ROLL' THE DICE. WATCH OUT, VERY LARGE NUMBERS TAKE" +echo "A LONG TIME. IN PARTICULAR, NUMBERS OVER 5000." + +while(retry): + echo "\n" + echo "HOW MANY ROLLS" + x = readLine(stdin).parseInt() + for v in 2..12: + f[v] = 0 # Initialize array to 0 + for s in 1..x: + a = rand(1..6) # Die 1 + b = rand(1..6) # Die 2 + r = a + b # Sum of dice + f[r] += 1 # Increment array count of dice sum result + echo "" + echo "TOTAL SPOTS: ", "NUMBER OF TIMES" + for v in 2..12: + echo v, ": ", f[v] # Print out counts for each possible result + echo "\n" + echo "TRY AGAIN?" + z = readLine(stdin).normalize() + retry = (z=="yes") or (z=="y") diff --git a/00_Alternate_Languages/34_Digits/MiniScript/README.md b/00_Alternate_Languages/34_Digits/MiniScript/README.md new file mode 100644 index 000000000..280be246e --- /dev/null +++ b/00_Alternate_Languages/34_Digits/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript digits.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "digits" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/34_Digits/MiniScript/digits.ms b/00_Alternate_Languages/34_Digits/MiniScript/digits.ms new file mode 100644 index 000000000..2abde907d --- /dev/null +++ b/00_Alternate_Languages/34_Digits/MiniScript/digits.ms @@ -0,0 +1,99 @@ +import "listUtil" + +print " "*33 + "Digits" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print + +printInColumns = function(a, b, c, d) + print (a+" "*16)[:16] + (b+" "*16)[:16] + (c+" "*16)[:16] + (d+" "*16)[:16] +end function + +print "This is a game of guessing." +print "For instructions, type '1', else type '0'" +if input != "0" then + print + print "Please take a piece of paper and write down" + print "the digits '0', '1', or '2' thirty times at random." + print "Arrange them in three lines of ten digits each." + print "I will ask for then ten at a time." + print "I will always guess them first and then look at your" + print "next number to see if i was right. By pure luck," + print "I ought to be right ten times. But i hope to do better" + print "than that *****" + print; print +end if + +a = 0; b = 1; c = 3 + +while true + m = list.init2d(27, 3, 1) + k = list.init2d(3, 3, 9) + l = list.init2d(9, 3, 3) + l[0][0] = 2; l[4][1] = 2; l[8][2] = 2 + z=26; z1=8; z2=2 + qtyRight = 0 + guess = 0 + for t in range(1,3) + while true + print + print "Ten numbers, please"; + n = input.replace(",", " ").replace(" ", "").split + if n.len != 10 then continue + valid = true + for i in n.indexes + n[i] = n[i].val + if n[i] < 0 or n[i] > 2 then + print "Only use the digits '0', '1', or '2'." + print "Let's try again." + valid = false + break + end if + end for + if valid then break + end while + + print; printInColumns "My guess","Your no.","Result","No. right"; print + for u in range(0, 9) + yourNum = n[u]; s=0 + for j in range(0,2) + s1 = a*k[z2][j] + b*l[z1][j] + c*m[z][j] + if s > s1 then continue + if s < s1 or rnd >= 0.5 then + s = s1; guess = j + end if + end for + if guess == yourNum then + outcome = " right" + qtyRight += 1 + else + outcome = " wrong" + end if + printInColumns " "+guess, " " + yourNum, outcome, qtyRight + + m[z][yourNum] += 1 + l[z1][yourNum] += 1 + k[z2][yourNum] += 1 + z -= floor(z/9)*9 + z = 3*z + yourNum + z1 = z - floor(z/9)*9 + z2 = yourNum + end for + end for + + print + if qtyRight > 10 then + print "I guessed more than 1/3 of your numbers." + print "I win." + print char(7) * 10 + else if qtyRight < 10 then + print "I guessed less than 1/3 of your numbers." + print "You beat me. Congratulations *****" + else + print "I guessed exactly 1/3 of your numbers." + print "It's a tie game." + end if + print "Do you want to try again (1 for yes, 0 for no)"; + if input != "1" then break +end while + +print; print "Thanks for the game." diff --git a/00_Alternate_Languages/35_Even_Wins/MiniScript/README.md b/00_Alternate_Languages/35_Even_Wins/MiniScript/README.md new file mode 100644 index 000000000..d52819302 --- /dev/null +++ b/00_Alternate_Languages/35_Even_Wins/MiniScript/README.md @@ -0,0 +1,33 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Note that this folder (like the original BASIC programs) contains TWO different programs based on the same idea. evenwins.ms plays deterministically; gameofevenwins.ms learns from its failures over multiple games. + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript evenwins.ms +``` +or + +``` + miniscript gameofevenwins.ms +``` + +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "evenwins" + run +``` +or + +``` + load "gameofevenwins" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/35_Even_Wins/MiniScript/evenwins.ms b/00_Alternate_Languages/35_Even_Wins/MiniScript/evenwins.ms new file mode 100644 index 000000000..0712b830f --- /dev/null +++ b/00_Alternate_Languages/35_Even_Wins/MiniScript/evenwins.ms @@ -0,0 +1,140 @@ +print " "*31 + "Digits" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print + +y1 = 0 +m1 = 0 +print " This is a two person game called 'Even Wins.'" +print "To play the game, the players need 27 marbles or" +print "other objects on a table." +print +print +print " The 2 players alternate turns, with each player" +print "removing from 1 to 4 marbles on each move. The game" +print "ends when there are no marbles left, and the winner" +print "is the one with an even number of marbles." +print +print +print " The only rules are that (1) you must alternate turns," +print "(2) you must take between 1 and 4 marbles each turn," +print "and (3) you cannot skip a turn." +print +print +print +while true + print " Type a '1' if you want to go first, and type" + print "a '0' if you want me to go first." + c = input.val + print + if c != 0 then + t = 27 + print + print + print + print "Total=" + t + print + print + print "What is your first move?" + m = 0 + else + t = 27 + m = 2 + print + print "Total= " + t + print + m1 += m + t -= m + end if + while true + if m then + print "I pick up " + m + " marbles." + if t == 0 then break + print + print "Total=" + t + print + print " And what is your next move, my total is " + m1 + end if + while true + y = input.val + print + if y < 1 or y > 4 then + print + print "The number of marbles you must take be a positive" + print "integer between 1 and 4." + print + print " What is your next move?" + print + else if y > t then + print " You have tried to take more marbles than there are" + print "left. Try again." + else + break + end if + end while + + y1 += y + t -= y + if t == 0 then break + print "Total=" + t + print + print "Your total is " + y1 + if t < 0.5 then break + r = t % 6 + if y1 % 2 != 0 then + if t >= 4.2 then + if r <= 3.4 then + m = r + 1 + m1 += m + t -= m + else if r < 4.7 or r > 3.5 then + m = 4 + m1 += m + t -= m + else + m = 1 + m1 += m + t -= m + end if + else + m = t + t -= m + print "I pick up " + m + " marbles." + print + print "Total = 0" + m1 += m + break + end if + else + if r < 1.5 or r > 5.3 then + m = 1 + m1 += m + t -= m + else + m = r - 1 + m1 += m + t -= m + if t < 0.2 then + print "I pick up " + m + " marbles." + print + break + end if + end if + end if + end while + print "That is all of the marbles." + print + print " My total is " + m1 + ", your total is " + y1 + print + if m1 % 2 then + print " You won. Do you want to play" + else + print " I won. Do you want to play" + end if + print "again? Type 1 for yes and 0 for no." + a1 = input.val + if a1 == 0 then break + m1 = 0 + y1 = 0 +end while +print +print "OK. See you later" diff --git a/00_Alternate_Languages/35_Even_Wins/MiniScript/gameofevenwins.ms b/00_Alternate_Languages/35_Even_Wins/MiniScript/gameofevenwins.ms new file mode 100644 index 000000000..e533550d1 --- /dev/null +++ b/00_Alternate_Languages/35_Even_Wins/MiniScript/gameofevenwins.ms @@ -0,0 +1,90 @@ +print " "*28 + "Game Of Even Wins" +print " "*15 + "Creative Computing Morristown, New Jersey" +print +print +yesNo = input("Do you want instructions (yes or no)? ").lower +if not yesNo or yesNo[0] != "n" then + print "The game is played as follows:" + print + print "At the beginning of the game, a random number of chips are" + print "placed on the board. The number of chips always starts" + print "as an odd number. On each turn, a player must take one," + print "two, three, or four chips. The winner is the player who" + print "finishes with a total number of chips that is even." + print "The computer starts out knowing only the rules of the" + print "game. It gradually learns to play well. It should be" + print "difficult to beat the computer after twenty games in a row." + print "Try it!!!!" + print + print "To quit at any time, type a '0' as your move." + print +end if + +l = 0 +b = 0 +r = [[4]*6, [4]*6] + +while true + a = 0 + b = 0 + e = 0 + l = 0 + p = floor((13 * rnd + 9) / 2) * 2 + 1; + while true + if p == 1 then + print "There is 1 chip on the board." + else + print "There are " + p + " chips on the board." + end if + e1 = e + l1 = l + e = a % 2 + l = p % 6 + if r[e][l] < p then + m = r[e][l] + if m <= 0 then + m = 1 + b = 1 + break + end if + p -= m + if m == 1 then + prompt = "Computer takes 1 chip leaving " + p + "... Your move? " + else + prompt = "Computer takes " + m + " chips leaving " + p + "... Your move? " + end if + b += m + while true + m = input(prompt).val + if m == 0 then break + if 1 <= m <= p and m <= 4 then break + prompt = m + " is an illegal move ... Your move? " + end while + if m == 0 then break + if m == p then break // <-- Really? Before we've done the math? + p -= m + a += m + else + if p == 1 then + print "Computer takes 1 chip." + else + print "Computer takes " + p + " chips." + end if + r[e][l] = p + b += p + break + end if + end while + if m == 0 then break + if b % 2 != 0 then + print "Game over ... you win!!!" + if r[e][l] != 1 then + r[e][l] -= 1 + else if (r[e1][l1] != 1) then + r[e1][l1] -= 1 + end if + else + print "Game over ... I win!!!" + end if + print +end while diff --git a/00_Alternate_Languages/36_Flip_Flop/MiniScript/README.md b/00_Alternate_Languages/36_Flip_Flop/MiniScript/README.md new file mode 100644 index 000000000..e012477dd --- /dev/null +++ b/00_Alternate_Languages/36_Flip_Flop/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript flipflop.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "flipflop" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/36_Flip_Flop/MiniScript/flipflop.ms b/00_Alternate_Languages/36_Flip_Flop/MiniScript/flipflop.ms new file mode 100644 index 000000000..6fb07cc0a --- /dev/null +++ b/00_Alternate_Languages/36_Flip_Flop/MiniScript/flipflop.ms @@ -0,0 +1,97 @@ +print " "*32 + "FlipFlop" +print " "*15 + "Creative Computing Morristown, New Jersey" +print +// Created by Michael Cass (1978) +// Ported to MiniScript by Joe Strout (2023) + +print "The object of this puzzle is to change this:" +print +print "X X X X X X X X X X" +print +print "to this:" +print +print "O O O O O O O O O O" +print +print "By typing the 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)." +print + +startNewGame = function + globals.q = rnd + globals.guesses = 0 + print "Here is the starting line of X's." + print + print "1 2 3 4 5 6 7 8 9 10" + print "X X X X X X X X X X" + print +end function + +getInput = function + while true + n = input("Input the number: ").val + if n == floor(n) and 0 <= n <= 11 then break + print "Illegal entry--try again." + end while + return n +end function + +startNewGame +while true + A = [""] + ["X"] * 10 // (include empty 0th element so we can index 1-based + m = 0 // (previous input) + while true + n = getInput + if n == 11 then + startNewGame + continue + else if n == 0 then + A = ["X"] * 10 + continue + end if + + if n != m then + // when user enters a different number from previous time + m = n + if A[n] == "O" then A[n] = "X" else A[n] = "O" + while m == n + r = tan(q + n/q - n) - sin(q/n) + 336*sin(8*n) + n = floor(10 * (r - floor(r))) + if A[n] != "O" then + A[n] = "O" + break + end if + A[n] = "X" + end while + else + // when n == m, i.e., user entered the same number twice in a row + while m == n + if A[n] == "O" then A[n] = "X" else A[n] = "O" + r = 0.592 * (1 / tan(q/n + q)) / sin(n*2 + q) - cos(n) + n = floor(10 * (r - floor(r))) + if A[n] != "O" then + A[n] = "O" + break + end if + A[n] = "X" + end while + end if + + print "1 2 3 4 5 6 7 8 9 10" + print A[1:].join + guesses += 1 + if A[1:] == ["O"]*10 then break + end while + + if guesses <= 12 then + print "Very good. You guessed it in only " + guesses + " guesses." + else + print "Try harder next time. It took you " + guesses + " guesses." + end if + + yesNo = input("Do you want to try another puzzle? ").lower + if not yesNo or yesNo[0] == "n" then break + print +end while diff --git a/00_Alternate_Languages/37_Football/MiniScript/README.md b/00_Alternate_Languages/37_Football/MiniScript/README.md new file mode 100644 index 000000000..4d8c33066 --- /dev/null +++ b/00_Alternate_Languages/37_Football/MiniScript/README.md @@ -0,0 +1,34 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript football.ms +```or +``` + miniscript ftball.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "football" + run +```or +``` + load "ftball" + run +``` + +#### Apology from the Translator + +These MiniScript programs were actually ported from the JavaScript ports of the original BASIC programs. I did that because the BASIC code (of both programs) was incomprehensible spaghetti. The JavaScript port, however, was essentially the same — and so are the MiniScript ports. The very structure of these programs makes them near-impossible to untangle. + +If I were going to write a football simulation from scratch, I would approach it very differently. But in that case I would have either a detailed specification of how the program should behave, or at least enough understanding of American football to design it myself as I go. Neither is the case here (and we're supposed to be porting the original programs, not making up our own). + +So, I'm sorry. Please take these programs as proof that you can write bad code even in the most simple, elegant languages. And I promise to try harder on future translations! diff --git a/00_Alternate_Languages/37_Football/MiniScript/football.ms b/00_Alternate_Languages/37_Football/MiniScript/football.ms new file mode 100644 index 000000000..e2b4583f6 --- /dev/null +++ b/00_Alternate_Languages/37_Football/MiniScript/football.ms @@ -0,0 +1,402 @@ +player_data = [17,8,4,14,19,3,10,1,7,11,15,9,5,20,13,18,16,2,12,6, + 20,2,17,5,8,18,12,11,1,4,19,14,10,7,9,15,6,13,16,3] +aa = [0]*21 +ba = [0]*21 +ca = [0]*41 +ha = [0]*3 +ta = [0]*3 +wa = [0]*3 +xa = [0]*3 +ya = [0]*3 +za = [0]*3 +ms = [null, "",""] +da = [0]*3 +ps = ["", "PITCHOUT","TRIPLE REVERSE","DRAW","QB SNEAK","END AROUND", + "DOUBLE REVERSE","LEFT SWEEP","RIGHT SWEEP","OFF TACKLE", + "WISHBONE OPTION","FLARE PASS","SCREEN PASS", + "ROLL OUT OPTION","RIGHT CURL","LEFT CURL","WISHBONE OPTION", + "SIDELINE PASS","HALF-BACK OPTION","RAZZLE-DAZZLE","BOMB!!!!"] +globals.p = 0 +globals.t = 0 + +printFieldHeaders = function + print "TEAM 1 [0 10 20 30 40 50 60 70 80 90 100] TEAM 2" + print +end function + +printSeparator = function + print "+" * 67 +end function + +showBall = function + print " " * (da[t] + 5 + p / 2) + ms[t] + printFieldHeaders +end function + +showScores = function + print + print "TEAM 1 SCORE IS " + ha[1] + print "TEAM 2 SCORE IS " + ha[2] + print + if ha[t] >= e then + print "TEAM " + t + " WINS*******************" + return true + end if + return false +end function + +losePossession = function + print + print "** LOSS OF POSSESSION FROM TEAM " + t + " TO TEAM " + ta[t] + print + printSeparator + print + globals.t = ta[t] +end function + +touchdown = function + print + print "TOUCHDOWN BY TEAM " + t + " *********************YEA TEAM" + q = 7 + if rnd <= 0.1 then + q = 6 + print "EXTRA POINT NO GOOD" + else + print "EXTRA POINT GOOD" + end if + ha[t] += q +end function + +askYesNo = function(prompt) + while true + yn = input(prompt + "? ").lower + if not yn then continue + if yn[0] == "y" then return "YES" + if yn[0] == "n" then return "NO" + end while +end function + + +print " "*32 + "FOOTBALL" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print +print "Presenting N.F.U. Football (No FORTRAN Used)" +print; print +if askYesNo("Do you want instructions") == "YES" then + print "This is a football game for two teams in which players must" + print "prepare a tape with a data statement (1770 for team 1," + print "1780 for team 2) in which each team scrambles nos. 1-20" + print "These numbers are then assigned to twenty given plays." + print "A list of nos. and their plays is provided with" + print "both teams having the same plays. The more similar the" + print "plays the less yardage gained. Scores are given" + print "whenever scores are made. Scores may also be obtained" + print "by inputting 99,99 for play nos. To punt or attempt a" + print "field goal, input 77,77 for play numbers. Questions will be" + print "asked then. On 4th down, you will also be asked whether" + print "you want to punt or attempt a field goal. If the answer to" + print "both questions is no it will be assumed you want to" + print "try and gain yardage. Answer all questions Yes or No." + print "The game is played until players terminate (control-c)." + print "Please prepare a tape and run." +end if +print +e = input("Please input score limit on game: ").val +for i in range(1, 40) + if i <= 20 then + aa[player_data[i - 1]] = i + else + ba[player_data[i - 1]] = i - 20 + end if + ca[i] = player_data[i - 1] +end for +l = 0 +globals.t = 1 +while true + print "TEAM " + t + " PLAY CHART" + print "NO. PLAY" + for i in range(1, 20) + print (ca[i+1] + " "*6)[:6] + ps[i] + end for + l += 20 + globals.t = 2 + print + print "TEAR OFF HERE----------------------------------------------" + for x in range(1, 11); print; end for + wait 3 + if l != 20 then break +end while + +playGame = function + da[1] = 0 + da[2] = 3 + ms[1] = "--->" + ms[2] = "<---" + ha[1] = 0 + ha[2] = 0 + ta[1] = 2 + ta[2] = 1 + wa[1] = -1 + wa[2] = 1 + xa[1] = 100 + xa[2] = 0 + ya[1] = 1 + ya[2] = -1 + za[1] = 0 + za[2] = 100 + globals.p = 0 + printFieldHeaders + print "TEAM 1 defend 0 YD goal -- TEAM 2 defends 100 YD goal." + globals.t = floor(2 * rnd + 1) + print + print "The coin is flipped" + routine = 1 + while true + if routine <= 1 then + globals.p = xa[t] - ya[t] * 40 + printSeparator + print + print "Team " + t + " receives kick-off" + k = floor(26 * rnd + 40) + end if + if routine <= 2 then + globals.p = p - ya[t] * k + end if + if routine <= 3 then + if wa[t] * p >= za[t] + 10 then + print + print "Ball went out of endzone --automatic touchback--" + globals.p = za[t] - wa[t] * 20 + if routine <= 4 then routine = 5 + else + print "Ball went " + k + " yards. Now on " + p + showBall + end if + end if + if routine <= 4 then + if askYesNo("Team " + t + " do you want to runback") == "YES" then + k = floor(9 * rnd + 1) + r = floor(((xa[t] - ya[t] * p + 25) * rnd - 15) / k) + globals.p = p - wa[t] * r + print + print "Runback team " + t + " " + r + " yards" + g = rnd + if g < 0.25 then + losePossession + routine = 4 + continue + else if ya[t] * p >= xa[t] then + touchdown + if showScores then return + globals.t = ta[t] + routine = 1 + continue + else if wa[t] * p >= za[t] then + print + print "Safety against team " + t + " **********************OH-OH" + ha[ta[t]] += 2 + if showScores then return + globals.p = za[t] - wa[t] * 20 + if askYesNo("Team " + t + " do you want to punt instead of a kickoff") == "YES" then + print + print "Team " + t + " will punt" + g = rnd + if g < 0.25 then + losePossession + routine = 4 + continue + end if + print + printSeparator + k = floor(25 * rnd + 35) + globals.t = ta[t] + routine = 2 + continue + end if + touchdown + if showScores then return + globals.t = ta[t] + routine = 1 + continue + else + routine = 5 + continue + end if + else // player does not want to runback + if wa[t] * p >= za[t] then globals.p = za[t] - wa[t] * 20 + end if + end if + if routine <= 5 then + d = 1 + s = p + end if + if routine <= 6 then + print "=" * 67 + print "TEAM " + t + " DOWN " + d + " ON " + p + if d == 1 then + if ya[t] * (p + ya[t] * 10) >= xa[t] then + c = 8 + else + c = 4 + end if + end if + if c != 8 then + print " "*27 + (10 - (ya[t] * p - ya[t] * s)) + " yards to 1st down" + else + print " "*27 + (xa[t] - ya[t] * p) + " yards" + end if + showBall + if d == 4 then routine = 8 + end if + if routine <= 7 then + u = floor(3 * rnd - 1) + while true + str = input("Input offensive play, defensive play: ") + str = str.replace(",", " ").replace(" ", " ").split + if t == 1 then + p1 = str[0].val + p2 = str[1].val + else + p2 = str[0].val + p1 = str[1].val + end if + if p1 == 99 then + if showScores then return + continue + end if + if 1 <= p1 <= 20 and 1 <= p2 <= 20 then break + print "Illegal play number, check and" + end while + end if + if d == 4 or p1 == 77 then + if askYesNo("Does team " + t + " want to punt") == "YES" then + print + print "Team " + t + " will punt" + if rnd < 0.25 then + losePossession + routine = 4 + continue + end if + print + printSeparator + k = floor(25 * rnd + 35) + globals.t = ta[t] + routine = 2 + continue + end if + if askYesNo("Does team " + t + " want to attempt a field goal") == "YES" then + print + print "Team " + t + " will attempt a field goal" + if rnd < 0.025 then + losePossession + routine = 4 + continue + else + f = floor(35 * rnd + 20) + print + print "Kick is " + f + " yards long" + globals.p = p - wa[t] * f + if rnd < 0.35 then + print "Ball went wide" + else if ya[t] * p >= xa[t] then + print "FIELD GOLD GOOD FOR TEAM " + t + " *********************YEA" + q = 3 + ha[t] = ha[t] + q + if showScores then return + globals.t = ta[t] + routine = 1 + continue + end if + print "Field goal unsuccesful team " + t + "-----------------too bad" + print + printSeparator + if ya[t] * p < xa[t] + 10 then + print + print "Ball now on " + p + globals.t = ta[t] + showBall + routine = 4 + continue + else + globals.t = ta[t] + routine = 3 + continue + end if + end if + else + routine = 7 + continue + end if + end if + y = floor(abs(aa[p1] - ba[p2]) / 19 * ((xa[t] - ya[t] * p + 25) * rnd - 15)) + print + if t == 1 and aa[p1] < 11 or t == 2 and ba[p2] < 11 then + print "The ball was run" + else if u == 0 then + print "Pass incomplete team " + t + y = 0 + else + if rnd <= 0.025 and y > 2 then + print "Pass completed" + else + print "Quarterback scrambled" + end if + end if + globals.p = p - wa[t] * y + print + print "Net yards gained on down " + d + " are " + y + + if rnd <= 0.025 then + losePossession + routine = 4 + continue + else if ya[t] * p >= xa[t] then + touchdown + if showScores then return + globals.t = ta[t] + routine = 1 + continue + else if wa[t] * p >= za[t] then + print + print "SAFETY AGAINST TEAM " + t + " **********************OH-OH" + ha[ta[t]] = ha[ta[t]] + 2 + if showScores then return + globals.p = za[t] - wa[t] * 20 + if askYesNo("Team " + t + " do you want to punt instead of a kickoff") == "YES" then + print + print "Team " + t + " will punt" + if rnd < 0.25 then + losePossession + routine = 4 + continue + end if + print + printSeparator + k = floor(25 * rnd + 35) + globals.t = ta[t] + routine = 2 + continue + end if + touchdown + if showScores then return + globals.t = ta[t] + routine = 1 + else if ya[t] * p - ya[t] * s >= 10 then + routine = 5 + else + d += 1 + if d != 5 then + routine = 6 + else + print + print "Conversion unsuccessful team " + t + globals.t = ta[t] + print + printSeparator + routine = 5 + end if + end if + end while +end function + +playGame diff --git a/00_Alternate_Languages/37_Football/MiniScript/ftball.ms b/00_Alternate_Languages/37_Football/MiniScript/ftball.ms new file mode 100644 index 000000000..31d3f8299 --- /dev/null +++ b/00_Alternate_Languages/37_Football/MiniScript/ftball.ms @@ -0,0 +1,431 @@ +os = ["Dartmouth", ""] +sa = [0, 1] +ls = ["", "Kick","receive"," yard ","run back for ","ball on ", + "yard line"," simple run"," tricky run"," short pass", + " long pass","punt"," quick kick "," place kick"," loss ", + " no gain","gain "," touchdown "," touchback ","safety***", + "junk"] +p = 0 +x = 0 +x1 = 0 + +fnf = function(x); return 1 - 2 * p; end function +fng = function(z); return p * (x1 - x) + (1 - p) * (x - x1); end function + +show_score = function + print + print "SCORE: " + sa[0] + " TO " + sa[1] + print + print +end function + +show_position = function + if x <= 50 then + print ls[5] + os[0] + " " + x + " " + ls[6] + else + print ls[5] + os[1] + " " + (100 - x) + " " + ls[6] + end if +end function + +offensive_td = function + print ls[17] + "***" + if rnd <= 0.8 then + sa[p] += 7 + print "Kick is good." + else + print "Kick is off to the side" + sa[p] += 6 + end if + show_score + print os[p] + " kicks off" + globals.p = 1 - p +end function + +// Main program +main = function + print " "*33 + "FTBALL" + print " "*15 + "Creative Computing Morristown, New Jersey" + print + print + print "This is Dartmouth championship football." + print + print "You will quarterback Dartmouth. Call plays as follows:" + print "1= simple run; 2= tricky run; 3= short pass;" + print "4= long pass; 5= punt; 6= quick kick; 7= place kick." + print + os[1] = input("Choose your opponent: ").upper + os[0] = "DARMOUTH" + print + sa[0] = 0 + sa[1] = 0 + globals.p = floor(rnd * 2) + print os[p] + " won the toss" + if p != 0 then + print os[1] + " Elects to receive." + print + else + print "Do you elect to kick or receive? " + while true + str = input.lower + if str and (str[0] == "k" or str[0] == "r") then break + print "Incorrect answer. Please type 'kick' or 'receive'" + end while + if str[0] == "k" then e = 1 else e = 2 + if e == 1 then globals.p = 1 + end if + globals.t = 0 + start = 1 + while true + if start <= 1 then + x = 40 + (1 - p) * 20 + end if + if start <= 2 then + y = floor(200 * ((rnd - 0.5))^3 + 55) + print " " + y + " " + ls[3] + " kickoff" + x = x - fnf(1) * y + if abs(x - 50) >= 50 then + print "Touchback for " + os[p] + "." + x = 20 + p * 60 + start = 4 + else + start = 3 + end if + end if + if start <= 3 then + y = floor(50 * (rnd)^2) + (1 - p) * floor(50 * (rnd)^4) + x = x + fnf(1) * y + if abs(x - 50) < 50 then + print " " + y + " " + ls[3] + " runback" + else + print ls[4] + offensive_td + start = 1 + continue + end if + end if + if start <= 4 then + // First down + show_position + end if + if start <= 5 then + x1 = x + d = 1 + print + print "First down " + os[p] + "***" + print + print + end if + // New play + globals.t += 1 + if t == 30 then + if rnd <= 1.3 then + print "Game delayed. Dog on field." + print + end if + end if + if t >= 50 and rnd <= 0.2 then break + if p != 1 then + // Opponent's play + if d <= 1 then + if rnd > 1/3 then z = 1 else z = 3 + else if d != 4 then + if 10 + x - x1 < 5 or x < 5 then + if rnd > 1/3 then z = 1 else z = 3 + else if x <= 10 then + a = floor(2 * rnd) + z = 2 + a + else if x <= x1 or d < 3 or x < 45 then + a = floor(2 * rnd) + z = 2 + a * 2 + else + if (rnd > 1 / 4) then + z = 4 + else + z = 6 + end if + end if + else + if x <= 30 then + z = 5 + else if 10 + x - x1 < 3 or x < 3 then + if rnd > 1/3 then z = 1 else z = 3 + else + z = 7 + end if + end if + else + while true + z = input("Next play? ").val + if 1 <= z <= 7 then break + print "Illegal play number, retype" + end while + end if + f = 0 + print ls[z + 6] + ". " + r = rnd * (0.98 + fnf(1) * 0.02) + r1 = rnd + if z == 1 or z == 2 then // Simple Run or Tricky Run + done = false + if z == 1 then + y = floor(24 * (r - 0.5)^3 + 3) + if rnd >= 0.05 then + routine = 1 + done = true + end if + else + y = floor(20 * r - 5) + if rnd > 0.1 then + routine = 1 + done = true + end if + end if + if not done then + f = -1 + x3 = x + x = x + fnf(1) * y + if abs(x - 50) < 50 then + print "*** Fumble after " + routine = 2 + else + print "*** Fumble." + routine = 4 + end if + end if + else if z == 3 or z == 4 then // Short Pass or Long Pass + if z == 3 then + y = floor(60 * (r1 - 0.5)^3 + 10) + else + y = floor(160 * ((r1 - 0.5))^3 + 30) + end if + if z == 3 and r < 0.05 or z == 4 and r < 0.1 then + if d != 4 then + print "Intercepted." + f = -1 + x = x + fnf(1) * y + if abs(x - 50) >= 50 then + routine = 4 + else + routine = 3 + end if + else + y = 0 + if rnd < 0.3 then + print "Batted down. ", "" + else + print "Incomplete. ", "" + end if + routine = 1 + end if + else if z == 4 and r < 0.3 then + print "Passer tackled. ", "" + y = -floor(15 * r1 + 3) + routine = 1 + else if z == 3 and r < 0.15 then + print "Passer taclked. ", "" + y = -floor(10 * r1) + routine = 1 + else if z == 3 and r < 0.55 or z == 4 and r < 0.75 then + y = 0 + if rnd < 0.3 then + print "Batted down. ", "" + else + print "Incomplete. ", "" + end if + routine = 1 + else + print "Complete. ", "" + routine = 1 + end if + else if z == 5 or z == 6 then // Punt or Quick Kick + y = floor(100 * ((r - 0.5))^3 + 35) + if (d != 4) then y = floor(y * 1.3) + print " " + y + " " + ls[3] + " punt" + if abs(x + y * fnf(1) - 50) < 50 and d >= 4 then + y1 = floor((r1)^2 * 20) + print " " + y1 + " " + ls[3] + " Run back" + y = y - y1 + end if + f = -1 + x = x + fnf(1) * y + if abs(x - 50) >= 50 then routine = 4 else routine = 3 + else if z == 7 then // Place kick + y = floor(100 * ((r - 0.5))^3 + 35) + if r1 <= 0.15 then + print "Kick is blocked ***" + x = x - 5 * fnf(1) + globals.p = 1 - p + start = 4 + continue + end if + x = x + fnf(1) * y + if abs(x - 50) >= 60 then + if r1 <= 0.5 then + print "Kick is off to the side." + print ls[18] + globals.p = 1 - p + x = 20 + p * 60 + start = 4 + continue + else + print "Field goal ***" + sa[p] = sa[p] + 3 + show_score + print os[p] + " kicks off" + globals.p = 1 - p + start = 1 + continue + end if + else + print "Kick is short." + if abs(x - 50) >= 50 then + // Touchback + print ls[18] + globals.p = 1 - p + x = 20 + p * 60 + start = 4 + continue + end if + globals.p = 1 - p + start = 3 + continue + end if + end if + // Gain or loss + if routine <= 1 then + x3 = x + x = x + fnf(1) * y + if abs(x - 50) >= 50 then + routine = 4 + end if + end if + if routine <= 2 then + if y != 0 then + print " " + abs(y) + " " + ls[3] + if (y < 0) then + yt = -1 + else if y > 0 then + yt = 1 + else + yt = 0 + end if + print ls[15 + yt] + if abs(x3 - 50) <= 40 and rnd < 0.1 then + // Penalty + p3 = floor(2 * rnd) + print os[p3] + " offsides -- penalty of 5 yards." + print + print + if p3 != 0 then + print "Do you accept the penalty?" + while true + str = input.lower + if str and (str[0] == "y" or str[0] == "n") then break + print "Yype 'yes' or 'no'" + end while + if str[0] == "y" then + f = 0 + d = d - 1 + if (p != p3) then + x = x3 + fnf(1) * 5 + else + x = x3 - fnf(1) * 5 + end if + end if + else + // opponent's strategy on penalty + if ((p != 1 and (y <= 0 or f < 0 or fng(1) < 3 * d - 2)) or + (p == 1 and ((y > 5 and f >= 0) or d < 4 or fng(1) >= 10))) then + print "penalty refused." + else + print "penalty accepted." + f = 0 + d = d - 1 + if (p != p3) then + x = x3 + fnf(1) * 5 + else + x = x3 - fnf(1) * 5 + end if + end if + end if + routine = 3 + end if + end if + end if + if routine <= 3 then + show_position + if f != 0 then + globals.p = 1 - p + start = 5 + continue + else if fng(1) >= 10 then + start = 5 + continue + else if d == 4 then + globals.p = 1 - p + start = 5 + continue + else + d += 1 + print "DOWN: " + d + " " + if (x1 - 50) * fnf(1) >= 40 then + print "Goal to go" + else + print "Yards to go: " + (10 - fng(1)) + end if + print + print + start = 6 + continue + end if + end if + if routine <= 4 then + // Ball in end-zone + e = (x >= 100) + case = 1 + e - f * 2 + p * 4 + if case == 1 or case == 5 then + // Safety + sa[1 - p] = sa[1 - p] + 2 + print ls[19] + show_score + print os[p] + " kicks off from its 20 yard line." + x = 20 + p * 60 + globals.p = 1 - p + start = 2 + continue + end if + if case == 3 or case == 6 then + // defensive td + print ls[17] + "for " + os[1 - p] + "***" + globals.p = 1 - p + end if + if case == 3 or case == 6 or case == 2 or case == 8 then + // offensive td + print ls[17] + "***" + if rnd <= 0.8 then + sa[p] = sa[p] + 7 + print "kick is good." + else + print "kick is off to the side" + sa[p] = sa[p] + 6 + end if + show_score + print os[p] + " kicks off" + globals.p = 1 - p + start = 1 + continue + end if + if case == 4 or case == 7 then + // Touchback + print ls[18] + globals.p = 1 - p + x = 20 + p * 60 + start = 4 + continue + end if + end if + end while + print "END OF GAME ***" + print "FINAL SCORE: " + os[0] + ": " + sa[0] + " " + os[1] + ": " + sa[1] +end function + +main diff --git a/00_Alternate_Languages/38_Fur_Trader/MiniScript/README.md b/00_Alternate_Languages/38_Fur_Trader/MiniScript/README.md new file mode 100644 index 000000000..3b15b9926 --- /dev/null +++ b/00_Alternate_Languages/38_Fur_Trader/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript furtrader.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "furtrader" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/38_Fur_Trader/MiniScript/furtrader.ms b/00_Alternate_Languages/38_Fur_Trader/MiniScript/furtrader.ms new file mode 100644 index 000000000..6fb85e1a2 --- /dev/null +++ b/00_Alternate_Languages/38_Fur_Trader/MiniScript/furtrader.ms @@ -0,0 +1,171 @@ +print " "*31 + "Fur Trader" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print + +furs = [0,0,0,0] // how many of each type of fur you have +value = [0,0,0,0] // value of each type of fur +furNames = "mink beaver ermine fox".split + +getYesNo = function + while true + ans = input("Answer yes or no: ").upper + if not ans then continue + if ans[0] == "Y" then return "YES" + if ans[0] == "N" then return "NO" + end while +end function + +printInstructions = function + print "You are the leader of a French fur trading expedition in " + print "1776 leaving the Lake Ontario area to sell furs and get" + print "supplies for the next year. You have a choice of three" + print "forts at which you may trade. The cost of supplies" + print "and the amount you receive for your furs will depend" + print "on the fort that you choose." + print +end function + +pickFort = function + print + while true + print "You may trade your furs at fort 1, fort 2," + print "or fort 3. Fort 1 is Fort Hochelaga (Montreal)" + print "and is under the protection of the French army." + print "Fort 2 is Fort Stadacona (Quebec) and is under the" + print "protection of the French Army. However, you must" + print "make a portage and cross the Lachine rapids." + print "Fort 3 is Fort New York and is under Dutch control." + print "You must cross through Iroquois land." + b = input("Answer 1, 2, or 3: ").val + if b == 1 then + print "You have chosen the easiest route. However, the fort" + print "is far from any seaport. The value" + print "you receive for your furs will be low and the cost" + print "of supplies higher than at Forts Stadacona or New York." + else if b == 2 then + print "You have chosen a hard route. It is, in comparsion," + print "harder than the route to Hochelaga but easier than" + print "the route to New York. You will receive an average value" + print "for your furs and the cost of your supplies will be average." + else if b == 3 then + print "You have chosen the most difficult route. At" + print "Fort New York you will receive the highest value" + print "for your furs. The cost of your supplies" + print "will be lower than at all the other forts." + else + continue + end if + print "Do you want to trade at another fort?" + if getYesNo == "NO" then return b + end while +end function + +visitFort = function(fort) + print + if fort == 1 then + value[0] = floor((.2*rnd+.7)*100+.5)/100 + value[2] = floor((.2*rnd+.65)*100+.5)/100 + value[1] = floor((.2*rnd+.75)*100+.5)/100 + value[3] = floor((.2*rnd+.8)*100+.5)/100 + print "Supplies at Fort Hochelaga cost $150.00." + print "Your travel expenses to Hochelaga were $10.00." + globals.money -= 150 + 10 + else if fort == 2 then + value[0] = floor((.3*rnd+.85)*100+.5)/100 + value[2] = floor((.15*rnd+.8)*100+.5)/100 + value[1] = floor((.2*rnd+.9)*100+.5)/100 + p = floor(10*rnd)+1 + if p <= 2 then + furs[1] = 0 + print "Your beaver were too heavy to carry across" + print "the portage. You had to leave the pelts, but found" + print "them stolen when you returned." + else if p <= 6 then + print "You arrived safely at Fort Stadacona." + else if p <= 8 then + for j in range(0,3); furs[j] = 0; end for + print "Your canoe upset in the Lachine rapids. You" + print "lost all your furs." + else if furs[3] then + furs[3] = 0 + print "Your fox pelts were not cured properly." + print "No one will buy them." + end if + print "Supplies at Fort Stadacona cost $125.00." + print "Your travel expenses to Stadacona were $15.00." + globals.money -= 125 + 15 + else + value[0] = floor((.15*rnd+1.05)*100+.5)/100 + value[3] = floor((.25*rnd+1.1)*100+.5)/100 + p = floor(10*rnd)+1 + if p <= 2 then + print "You were attacked by a party of Iroquois." + print "All people in your trading group were" + print "killed. This ends the game." + globals.gameOver = true + return + else if p<=6 then + print "You were lucky. You arrived safely" + print "at Fort New York." + else if p<=8 then + for j in range(0,3); furs[j] = 0; end for + print "You narrowly escaped an iroquois raiding party." + print "However, you had to leave all your furs behind." + else + value[1] /= 2 + value[0] /= 2 + print "Your mink and beaver were damaged on your trip." + print "You receive only half the current price for these furs." + end if + print "Supplies at New York cost $80.00." + print "Your travel expenses to New York were $25.00." + globals.money -= 80 + 25 + end if +end function + +printInstructions + +gameOver = false +money=600 +while not gameOver + print "Do you wish to trade furs?" + if getYesNo == "NO" then break + + value[2]=floor((.15*rnd+.95)*100+.5)/100 // ermine value + value[1]=floor((.25*rnd+1.00)*100+.5)/100 // beaver value + + print + print "You have $" + money + " savings." + print "And 190 furs to begin the expedition." + print + print "Your 190 furs are distributed among the following" + print "kinds of pelts: mink, beaver, ermine and fox." + print + furs = [0,0,0,0] + for j in range(0, 3) + furs[j] = input("How many " + furNames[j] + " do you have? ").val + if furs.sum >= 190 then break + end for + if furs.sum > 190 then + print "You may not have that many furs." + print "Do not try to cheat. I can add." + print "You must start again." + continue + end if + + fort = pickFort + visitFort fort + if gameOver then break + + print + for j in [1, 3, 2, 0] + if not furs[j] then continue + revenue = value[j] * furs[j] + print "Your " + furNames[j] + " sold for $" + revenue + "." + money += revenue + end for + print + print "You now have $" + money + " including your previous savings." +end while + + diff --git a/00_Alternate_Languages/39_Golf/MiniScript/README.md b/00_Alternate_Languages/39_Golf/MiniScript/README.md new file mode 100644 index 000000000..8a7e5325d --- /dev/null +++ b/00_Alternate_Languages/39_Golf/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript golf.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "golf" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/39_Golf/MiniScript/golf.ms b/00_Alternate_Languages/39_Golf/MiniScript/golf.ms new file mode 100644 index 000000000..634ef0905 --- /dev/null +++ b/00_Alternate_Languages/39_Golf/MiniScript/golf.ms @@ -0,0 +1,303 @@ +print " "*34 + "Golf" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print + + print "Welcome to the creative computing country club," + print "an eighteen hole championship layout located a short" + print "distance from scenic downtown Morristown. The" + print "commentator will explain the game as you play." + print "Enjoy your game; see you at the 19th hole..." + print;print + l = [0] * 11 + holesInCourse=18 + totalScore=0 + totalPar=0 + dubChance=.8 + s2=0 + curHole=1 + + +getHoleData = function(hole) + // data for all the holes: distance, par, locOnLeft, and locOnRight for each one + data = [ + 361,4,4,2,389,4,3,3,206,3,4,2,500,5,7,2, + 408,4,2,4,359,4,6,4,424,4,4,2,388,4,4,4, + 196,3,7,2,400,4,7,2,560,5,7,2,132,3,2,2, + 357,4,4,4,294,4,2,4,475,5,2,3,375,4,4,2, + 180,3,6,2,550,5,6,6] + i = (hole-1) * 4 + globals.distance = data[i] + globals.par = data[i+1] + globals.locOnRight = data[i+2] +globals.locOnLeft = data[i+3] +end function + +startHole = function(hole) + getHoleData hole + print + print "You are at the tee off hole " + hole + " distance " + distance + " yards, par " + par + globals.totalPar += par + print "On your right is ", "" + printLocation locOnRight + print "On your left is ", "" + printLocation locOnLeft +end function + +// Get player's handicap +while true + handicap = input("What is your handicap? ").val + if 0 <= handicap <= 30 then break + print "PGA handicaps range from 0 to 30." +end while + +// Get player's weak point +while true + print "Difficulties at golf include:" + print "0=hook, 1=slice, 2=poor distance, 3=trap shots, 4=putting" + weakness = input("Which one (only one) is your worst? ").val + if 0 <= weakness <= 4 then break +end while + +// End a sentence by printing the name of the given location +printLocation = function(locIdx) + if locIdx < 1 or locIdx > 6 then + print "out of bounds." + else + print ["fairway.", "rough.", "trees.", "adjacent fairway.", + "trap.", "water."][locIdx-1] + end if +end function + +// Print score for one hole (plus total), and some praise or advice. +printScore = function(hole, score, par, totalScore, totalPar) + print "Your score on hole " + hole + " was " + score + print "Total par for " + hole + " holes is " + totalPar + " Your total is " + totalScore + if hole == holesInCourse then return + if score > par+2 then + print "Keep your head down." + else if score == par then + print "A par. Nice going." + else if score == par-1 then + print "A birdie." + else if score == 1 then + print "A hole in one." + else if score == par-2 then + print "A great big eagle." + end if +end function + +// Print club advice -- but only once. +clubAdviceGiven = false +printClubAdvice = function + if clubAdviceGiven then return // (already done) + globals.clubAdviceGiven = true + print "Selection of clubs" + print "yardage desired suggested clubs" + print "200 to 280 yards 1 to 4" + print "100 to 200 yards 19 to 13" + print " 0 to 100 yards 29 to 23" +end function + +doPenalty = function + print "Penalty stroke assessed. Hit from previous location." + globals.score += 1 + globals.j += 1 + globals.curLoc = 1 + globals.distance = prevDistance +end function + +// Try to get out of a trap. Return true if succeeded, false if failed. +doTrapShot = function + if weakness == 3 then + if rnd <= dubChance then + globals.dubChance *= 0.2 + print "Shot dubbed, still in trap." + return false + end if + globals.dubChance = 0.8 + end if + globals.distToPin = 1 + (3*floor((80/(40-handicap))*rnd)) + return true +end function + +getClub = function + //print "DEBUG: getClub, with curLoc=" + curLoc + while true + club = input("What club do you choose? ").val + print + if club < 1 or club > 29 then continue + if club > 4 and club < 12 then + print "That club is not in the bag." + continue + end if + if club >= 12 then club -= 6 + if curLoc <= 5 or club == 14 or club == 23 then break + print "That club is not in the bag." + print + continue + end while + return club +end function + +getSwing = function(club) + if club <= 13 then return 1 // (full swing) + while true + print "Now gauge your distance by a percentage (1 to 100)" + swing = input("of a full swing? ").val / 100 + print + if 0 <= swing <= 1 then return swing + // Given an invalid swing input, the original BASIC code would + // print "That club is not in the bag" and go back to choosing a club. + // But that is convoluted spaghetti, and I'm not doing it. + end while +end function + +playOneHole = function + q = 0 // counts certain kinds of shots on every third hole (?) + distanceHit = 0 + offLine = 0 + + // shot loop -- take as many shots as you need for this hole + while true + if curLoc < 1 then curLoc = 1 + if curLoc > 6 then + print "Your shot went out of bounds." + doPenalty + distanceHit = 0 + else if curLoc > 5 then + print "Your shot went into the water." + doPenalty + distanceHit = 0 + end if + + if score > 0 and distanceHit then + print "Shot went " + distanceHit + " yards. It's " + distToPin + " yards from the cup." + print "Ball is " + floor(offLine) + " yards off line... in ", "" + printLocation curLoc + end if + + printClubAdvice + + club = getClub + swing = getSwing(club) + globals.score += 1 + if curLoc == 5 and not doTrapShot then continue + if club > 14 then club -= 10 + + //print "DEBUG Club:"+club + " Swing:"+swing + " Weakness:"+weakness + + if curHole % 3 == 0 then + if s2 + q + (10*(curHole-1)/18) < (curHole-1)*(72+((handicap+1)/.85))/18 then + q += 1 + if score % 2 and distance >= 95 then + globals.distance -= 75 + distanceHit = 0 + print "Ball hit tree - bounced into rough " + distance + " yards from hole." + continue + end if + end if + end if + + if club >= 4 and curLoc == 2 then + print "You dubbed it." + distanceHit = 35 + else if score > 7 and distance < 200 then + // user is really sucking, let's cut them a break + putt 1 + (3 * floor((80/(40-handicap))*rnd)) + return + else + //print "DEBUG: SWING with handicap:" + handicap + " club:" + club + distanceHit = floor(((30-handicap)*2.5+187-((30-handicap)*.25+15)*club/2)+25*rnd) + distanceHit = floor(distanceHit*swing) + if weakness == 2 then distanceHit = floor(.85*distanceHit) + end if + offLine = (rnd/.8)*(2*handicap+16)*abs(tan(distanceHit*.0035)) + distToPin = floor(sqrt(offLine^2+abs(distance-distanceHit)^2)) + //print "DEBUG distance:"+distance+"; distanceHit:"+distanceHit+"; distToPin:"+distToPin+"; offLine:"+offLine + if distanceHit > distance and distToPin >= 20 then print "Too much club. You're past the hole." + + globals.prevDistance = distance + globals.distance = distToPin + if distToPin > 27 then + if offLine < 30 or j > 0 then + curLoc = 1 + continue + end if + // hook or slice + s9 = (s2+1)/15 + if weakness == 0 then + isSlice = floor(s9) == s9 + else + isSlice = not floor(s9) == s9 + end if + if isSlice then + print "You sliced- " + curLoc = locOnRight + else + print "You hooked- " + curLoc = locOnLeft + end if + if offLine > 45 then print "badly." + + else if distToPin > 20 then + curLoc = 5 + else if distToPin > .5 then + globals.curLoc = 8 // on the green! + putt distToPin * 3 // (convert yards to feet, and putt) + return + else + curLoc = 9 + print "You holed it." + print + globals.curHole += 1 + return + end if + end while +end function + +putt = function(distToPin) + puttAttempts = 0 + while true + distToPin = abs(floor(distToPin)) + print "On green, " + distToPin + " feet from the pin." + i = input("Choose your putt potency (1 to 13): ").val + globals.score += 1 + if score+1 - par > handicap*0.072 + 2 or puttAttempts > 2 then break + puttAttempts += 1 + if weakness == 4 then + distToPin -= i*(4+1*rnd)+1 + else + distToPin -= i*(4+2*rnd)+1.5 + end if + if -2 <= distToPin <= 2 then break + if distToPin < 0 then + print "Passed by cup." + else + print "Putt short." + end if + end while + print "You holed it." + print + return +end function + +// main loop +while true + curLoc = 0 + j = 0 + s2 += 1 + if curHole > 1 then + end if + + print + + score = 0 + startHole curHole + playOneHole + + totalScore += score + printScore curHole, score, par, totalScore, totalPar + if curHole == holesInCourse then break + + curHole += 1 +end while diff --git a/00_Alternate_Languages/40_Gomoko/MiniScript/README.md b/00_Alternate_Languages/40_Gomoko/MiniScript/README.md new file mode 100644 index 000000000..5613d133a --- /dev/null +++ b/00_Alternate_Languages/40_Gomoko/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript gomoko.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "gomoko" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/40_Gomoko/MiniScript/gomoko.ms b/00_Alternate_Languages/40_Gomoko/MiniScript/gomoko.ms new file mode 100644 index 000000000..3188e22a1 --- /dev/null +++ b/00_Alternate_Languages/40_Gomoko/MiniScript/gomoko.ms @@ -0,0 +1,94 @@ +print " "*33 + "Gomoku" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print +print "Welcome to the Oriental game of Gomoko." +print; print "The game is played on an N by N grid of a size" +print "that you specify During your play, you may cover one grid" +print "intersection with a marker. The object of the game is to get" +print "5 adjacent markers in a row -- horizontally, vertically, or" +print "diagonally. On the board diagram, your moves are marked" +print "with a '1' and the computer moves with a '2'." +print; print "The computer does not keep track of who has won." +print "To end the game, type -1,-1 for your move."; print + +inBounds = function(x,y) + return 0 < x <= n and 0 < y <= n +end function + +empty = function(x,y) + return A[x][y] == 0 +end function + +printBoard = function + for y in range(1,n) + print A[y][1:n+1].join + end for + print +end function + +doPlayerMove = function + while true + inp = input("Your play (i,j)? ").replace(" ", "").split(",") + print + if inp.len != 2 then continue + x = inp[0].val; y = inp[1].val + if x == -1 then return false + if not inBounds(x,y) then + print "Illegal move. Try again..." + else if not empty(x,y) then + print "Square occupied. Try again..." + else + break + end if + end while + A[x][y] = 1 + globals.lastPlayerMove = [x,y] + return true +end function + +doComputerMove = function + // Computer tries a move near the player's last move + for e in range(-1,1) + for f in range(-1,1) + if e==0 and f==0 then continue + x = lastPlayerMove[0] + e; y = lastPlayerMove[1] + f + if inBounds(x,y) and empty(x,y) then + A[x][y] = 2 + return + end if + end for + end for + + // Computer tries a random move + while true + x = floor(n * rnd + 1); y = floor(n * rnd + 1) + if empty(x,y) then break + end while + A[x][y] = 2 +end function + +playGame = function + while true + globals.n = input("What is your board size (min 7/ max 19)? ").val + if 7 <= n <= 19 then break + print "I said, the minimum is 7, the maximum is 19." + end while + globals.A = [] + for i in range(0,19) + A.push [0]*20 + end for + print; print "We alternate moves. You go first..."; print + while true + if not doPlayerMove then return + doComputerMove + printBoard + end while +end function + +// Main loop +while true + playGame + print; print "Thanks for the game!!" + q = input("Play again (1 for Yes, 0 for No)? ").val + if q != 1 then break +end while \ No newline at end of file diff --git a/00_Alternate_Languages/41_Guess/MiniScript/README.md b/00_Alternate_Languages/41_Guess/MiniScript/README.md new file mode 100644 index 000000000..bf19c7599 --- /dev/null +++ b/00_Alternate_Languages/41_Guess/MiniScript/README.md @@ -0,0 +1,21 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript guess.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "guess" + run +``` +3. "Try-It!" page on the web: +Go to https://miniscript.org/tryit/, clear the default program from the source code editor, paste in the contents of guess.ms, and click the "Run Script" button. diff --git a/00_Alternate_Languages/41_Guess/MiniScript/guess.ms b/00_Alternate_Languages/41_Guess/MiniScript/guess.ms new file mode 100644 index 000000000..496426ce4 --- /dev/null +++ b/00_Alternate_Languages/41_Guess/MiniScript/guess.ms @@ -0,0 +1,59 @@ +setup = function + print " "*33 + "GUESS" + print " "*15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" + print; print; print + print "This is a number guessing game. I'll think" + print "of a number between 1 and any limit you want." + print "Then you have to guess what it is." + print + while true + globals.limit = input("What limit do you want? ").val + if limit > 1 then break + print "Please enter a number greater than 1." + end while + globals.par = floor(log(limit, 2)) + 1 +end function + +printGap = function + for i in range(1, 5) + print + end for +end function + +doOneGame = function + rightAnswer = ceil(rnd * limit) + print "I'm thinking of a number between 1 and " + limit + print "Now you try to guess what it is." + guess = 0 + while true + guess = guess + 1 + num = input("Your guess: ").val + if num <= 0 then + printGap + setup + return + end if + if num == rightAnswer then + print "That's it! You got it in " + guess + " tries." + if guess < par then + print "Very good." + else if guess == par then + print "Good." + else + print "You should have been able to get it in only " + par + "." + end if + printGap + return + end if + if num > rightAnswer then + print "Too high. Try a smaller answer." + else + print "Too low. Try a bigger answer." + end if + end while +end function + +setup +while true + doOneGame +end while \ No newline at end of file diff --git a/00_Alternate_Languages/42_Gunner/MiniScript/README.md b/00_Alternate_Languages/42_Gunner/MiniScript/README.md new file mode 100644 index 000000000..323499176 --- /dev/null +++ b/00_Alternate_Languages/42_Gunner/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript gunner.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "gunner" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/42_Gunner/MiniScript/gunner.ms b/00_Alternate_Languages/42_Gunner/MiniScript/gunner.ms new file mode 100644 index 000000000..5c52a89ea --- /dev/null +++ b/00_Alternate_Languages/42_Gunner/MiniScript/gunner.ms @@ -0,0 +1,78 @@ +print " "*30 + "Gunner" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print +print "You are the officer-in-charge, giving orders to a gun" +print "crew, telling them the degrees of elevation you estimate" +print "will place a projectile on target. A hit within 100 yards" +print "of the target will destroy it."; print + +// Select a target and give the player up to 5 tries to hit it. +// Return the number of shots taken, or set globals.gameOver to true. +playOneTarget = function(maxRange) + globals.gameOver = false + targetDist = floor(maxRange * (.1 + .8 * rnd)) + shot = 0 + print "Distance to the target is " + targetDist + " yards." + print + while true + print + degrees = input("Elevation? ").val + if degrees > 89 then + print "Maximum elevation is 89 degrees." + continue + else if degrees < 1 then + print "Minimum elevation is one degree." + continue + end if + shot += 1 + if shot >= 6 then + globals.gameOver = true + return + end if + radiansX2 = 2 * degrees * pi/180 + throw = maxRange * sin(radiansX2) + diff = floor(targetDist - throw) + if abs(diff) < 100 then + print "*** TARGET DESTROYED *** " + shot + " rounds of ammunition expended." + return shot + end if + if diff > 0 then + print "Short of target by " + diff + " yards." + else + print "Over target by " + abs(diff) + " yards." + end if + end while +end function + +playOneGame = function + maxRange = floor(40000*rnd + 20000) + print "Maximum range of your gun is " + maxRange + " yards." + shots = 0 + for targetNum in range(1,4) + shots += playOneTarget(maxRange) + if gameOver then break + if targetNum < 4 then + print + print "The forward observer has sighted more enemy activity..." + end if + end for + if gameOver then + print; print "Boom !!!! You have just been destroyed" + print "by the enemy."; print; print; print + else + print; print; print "Total rounds expended were: " + shots + end if + if shots > 18 or gameOver then + print "Better go back to font sill for refresher training!" + else + print "Nice shooting !!" + end if +end function + +// Main loop +while true + playOneGame + print; yn = input("Try again (Y or N)? ").upper + if not yn or yn[0] != "Y" then break +end while +print; print "OK. Return to base camp." diff --git a/00_Alternate_Languages/43_Hammurabi/MiniScript/README.md b/00_Alternate_Languages/43_Hammurabi/MiniScript/README.md new file mode 100644 index 000000000..bb4411bbf --- /dev/null +++ b/00_Alternate_Languages/43_Hammurabi/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript hammurabi.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "hammurabi" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/43_Hammurabi/MiniScript/hammurabi.ms b/00_Alternate_Languages/43_Hammurabi/MiniScript/hammurabi.ms new file mode 100644 index 000000000..f81a1e708 --- /dev/null +++ b/00_Alternate_Languages/43_Hammurabi/MiniScript/hammurabi.ms @@ -0,0 +1,190 @@ +print " "*32 + "Hamurabi" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print +print "Try your hand at governing ancient Sumeria" +print "for a ten-year term of office."; print + +eol = char(10) + +game = {} +game.z = 0 // year +game.p = 95 +game.s = 2800 // bushels in store +game.h = 3000 +game.e = game.h - game.s // bushels eaten by rats +game.food = 0 // bushels given to people to eat +game.y = 3 // value (in bushels) per acre +game.a = game.h / game.y // acres owned +game.i = 5 // immigration/births +game.d = 0 // how many starved this year +game.d1 = 0 // total starved over the whole game +game.p1 = 0 // average % of population starved per year +game.q = 1 // if negative, then a plague strikes + +startYear = function + print; print; print "Hamurabi: I beg to report to you," + game.z += 1 + print "In year " + game.z + ", " + + game.d + " people starved, " + + game.i + " came to the city," + game.p += game.i + if game.q < 0 then + game.p = floor(game.p / 2) + print "A horrible plague struck! Half the people died." + end if + print "Population is now " + game.p + "." + print "The city now owns " + game.a + " acres." + print "You harvested " + game.y + " bushels per acre." + print "The rats ate " + game.e + " bushels." + print "You now have " + game.s + " bushels in store."; print +end function + +exitGame = function + print; print char(7)*10 + print "So long for now."; print + exit +end function + +impeach = function + print "Due to this extreme mismanagement you have not only" + print "been impeached and thrown out of office but you have" + print "also been declared national fink!!!!" + exitGame +end function + +getNumber = function(prompt, max, maxMsg) + while true + value = input(prompt + "? ").val + if value < 0 then + print; print "Hamurabi: I cannot do what you wish." + print "Get yourself another steward!" + exitGame + end if + if value <= max then return value + print "Hamurabi: Think again. " + maxMsg + " Now then," + end while +end function + +hint = function(msg) + // This was not in the original program. But if you want to make + // the game easier, uncomment this line: + //print msg +end function + +min = function(a, b, c) + m = [a, b, c] + m.sort + return m[0] +end function + +getDecisions = function + // buy/sell land + c = floor(10 * rnd); game.y = c + 17 + print "Land is trading at " + game.y + " bushels per acre." + qty = getNumber("How many acres do you wish to buy", + floor(game.s / game.y), "You have only" + eol + game.s + " bushels of grain.") + if qty > 0 then + game.a += qty + game.s -= game.y * qty + else + qty = getNumber("How many acres do you wish to sell", + game.a, "You own only" + eol + game.a + " acres.") + game.a -= qty + game.s += game.y * qty + end if + + // feed the people + hint "Your people want " + (game.p * 20) + " bushels of food." + game.food = getNumber("How many bushels do you wish to feed your people", + game.s, "You have only" + eol + game.s + " bushels of grain.") + game.s -= game.food + + // planting (a little more complicate because there are THREE limits) + hint "You can plant up to " + + min(game.a, floor(game.s * 2), floor(game.p*10-1)) + " acres." + game.d = 0 + while game.a > 0 and game.s > 2 + game.d = getNumber("How many acres do you wish to plant with seed", + game.a, "You own only " + game.a + " acres.") + // enough grain for seed? (each bushel can plant 2 acres) + if floor(game.d / 2) > game.s then + print "Hamurabi: Think again. You have only" + eol + game.s + + " bushels of grain. Now then," + continue + end if + // enough people to tend the crops? (each person can tend 10 acres) + if game.d >= game.p * 10 then + print "But you have only " + game.p + " people to tend the fields! Now then," + continue + end if + break + end while + game.s -= floor(game.d / 2) +end function + +simulateYear = function + // A bountiful harvest! + c = floor(rnd * 5) + 1 + game.y = c; game.h = game.d * game.y; game.e = 0 + c = floor(rnd * 5) + 1 + if c % 2 == 0 then + // rats are running wild!! + game.e = floor(game.s / c) + end if + game.s += game.h - game.e + + // Let's have some babies + c = floor(rnd * 5) + 1 + game.i = floor(c * (20 * game.a + game.s) / game.p / 100 + 1) + // How many people had full tummies? + c = floor(game.food / 20) + // Horros, a 15% chance of plague + game.q = floor(10 * (2 * rnd - 0.3)) + + if game.p < c then + game.d = 0 + else + // starve enough for impeachment? + game.d = game.p - c + if game.d > 0.45 * game.p then + print; print "You starved " + game.d + " people in one year!!!" + impeach + end if + game.p1 = ((game.z - 1) * game.p1 + game.d * 100 / game.p) / game.z + game.p = c + game.d1 += game.d + end if +end function + +printFinalResult = function + print "In your 10-year term of office, " + game.p1 + " percent of the" + print "population starved per year on the average, i.e., a total of" + print game.d1 + " people died!!" + acresPer = game.a / game.p + print "You started with 10 acres per person and ended with" + print acresPer + " acres per person."; print + if game.p1 > 33 or acresPer < 7 then impeach + if game.p1 > 10 or acresPer < 9 then + print "Your heavy-handed performance smacks of Nero and Ivan IV." + print "The people (remaining) find you an unpleasant ruler, and," + print "frankly, hate your guts!!" + else if game.p1 > 3 or acresPer < 10 then + print "Your performance could have been somewhat better, but" + print "really wasn't too bad at all. " + floor(game.p * 0.8 * rnd) + " people" + print "would dearly like to see you assassinated but we all have our" + print "trivial problems." + else + print "A fantastic performance!! Charlemange, Disraeli, and" + print "Jefferson combined could not have done better!" + end if +end function + +// Main loop +while true + startYear + if game.z == 11 then break + getDecisions + simulateYear +end while +printFinalResult +exitGame diff --git a/00_Alternate_Languages/43_Hammurabi/hammurabi.bas b/00_Alternate_Languages/43_Hammurabi/hammurabi.bas index e41fe4fbd..71afd1c51 100644 --- a/00_Alternate_Languages/43_Hammurabi/hammurabi.bas +++ b/00_Alternate_Languages/43_Hammurabi/hammurabi.bas @@ -1,119 +1,119 @@ -10 PRINT TAB(32);"HAMURABI" -20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" -30 PRINT:PRINT:PRINT +10 PRINT TAB(32); "HAMURABI" +20 PRINT TAB(15); "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" +30 PRINT : PRINT : PRINT 80 PRINT "TRY YOUR HAND AT GOVERNING ANCIENT SUMERIA" -90 PRINT "FOR A TEN-YEAR TERM OF OFFICE.":PRINT -95 D1=0: P1=0 -100 Z=0: P=95:S=2800: H=3000: E=H-S -110 Y=3: A=H/Y: I=5: Q=1 -210 D=0 -215 PRINT:PRINT:PRINT "HAMURABI: I BEG TO REPORT TO YOU,": Z=Z+1 -217 PRINT "IN YEAR";Z;",";D;"PEOPLE STARVED,";I;"CAME TO THE CITY," -218 P=P+I -227 IF Q>0 THEN 230 -228 P=INT(P/2) +90 PRINT "FOR A TEN-YEAR TERM OF OFFICE." : PRINT +95 D1 = 0 : P1 = 0 +100 Z = 0 : P = 95 : S = 2800 : H = 3000 : E = H - S +110 Y = 3 : A = H / Y : I = 5 : Q = 1 +210 D = 0 +215 PRINT : PRINT : PRINT "HAMURABI: I BEG TO REPORT TO YOU," : Z = Z + 1 +217 PRINT "IN YEAR "; Z; ","; D; " PEOPLE STARVED, "; I; " CAME TO THE CITY," +218 P = P + I +227 IF Q > 0 THEN 230 +228 P = INT(P / 2) 229 PRINT "A HORRIBLE PLAGUE STRUCK! HALF THE PEOPLE DIED." -230 PRINT "POPULATION IS NOW";P -232 PRINT "THE CITY NOW OWNS ";A;"ACRES." -235 PRINT "YOU HARVESTED";Y;"BUSHELS PER ACRE." -250 PRINT "THE RATS ATE";E;"BUSHELS." -260 PRINT "YOU NOW HAVE ";S;"BUSHELS IN STORE.": PRINT -270 IF Z=11 THEN 860 -310 C=INT(10*RND(1)): Y=C+17 -312 PRINT "LAND IS TRADING AT";Y;"BUSHELS PER ACRE." +230 PRINT "POPULATION IS NOW "; P +232 PRINT "THE CITY NOW OWNS "; A; " ACRES." +235 PRINT "YOU HARVESTED "; Y; " BUSHELS PER ACRE." +250 PRINT "THE RATS ATE "; E; " BUSHELS." +260 PRINT "YOU NOW HAVE "; S; " BUSHELS IN STORE." : PRINT +270 IF Z = 11 THEN 860 +310 C = INT(10 * RND(1)) : Y = C + 17 +312 PRINT "LAND IS TRADING AT "; Y; " BUSHELS PER ACRE." 320 PRINT "HOW MANY ACRES DO YOU WISH TO BUY"; -321 INPUT Q: IF Q<0 THEN 850 -322 IF Y*Q<=S THEN 330 +321 INPUT Q : IF Q < 0 THEN 850 +322 IF Y * Q <= S THEN 330 323 GOSUB 710 324 GOTO 320 -330 IF Q=0 THEN 340 -331 A=A+Q: S=S-Y*Q: C=0 +330 IF Q = 0 THEN 340 +331 A = A + Q : S = S - Y * Q : C = 0 334 GOTO 400 340 PRINT "HOW MANY ACRES DO YOU WISH TO SELL"; -341 INPUT Q: IF Q<0 THEN 850 -342 IF QC/2 THEN 530 +522 IF INT(C / 2) <> C / 2 THEN 530 523 REM *** RATS ARE RUNNING WILD!! -525 E=INT(S/C) -530 S=S-E+H +525 E = INT(S / C) +530 S = S - E + H 531 GOSUB 800 532 REM *** LET'S HAVE SOME BABIES -533 I=INT(C*(20*A+S)/P/100+1) +533 I = INT(C *(20 * A + S) / P / 100 + 1) 539 REM *** HOW MANY PEOPLE HAD FULL TUMMIES? -540 C=INT(Q/20) +540 C = INT(Q / 20) 541 REM *** HORROS, A 15% CHANCE OF PLAGUE -542 Q=INT(10*(2*RND(1)-.3)) -550 IF P.45*P THEN 560 -553 P1=((Z-1)*P1+D*100/P)/Z -555 P=C: D1=D1+D: GOTO 215 -560 PRINT: PRINT "YOU STARVED";D;"PEOPLE IN ONE YEAR!!!" +552 D = P - C : IF D > 0.45 * P THEN 560 +553 P1 =((Z - 1) * P1 + D * 100 / P) / Z +555 P = C : D1 = D1 + D : GOTO 215 +560 PRINT : PRINT "YOU STARVED "; D; " PEOPLE IN ONE YEAR!!!" 565 PRINT "DUE TO THIS EXTREME MISMANAGEMENT YOU HAVE NOT ONLY" 566 PRINT "BEEN IMPEACHED AND THROWN OUT OF OFFICE BUT YOU HAVE" -567 PRINT "ALSO BEEN DECLARED NATIONAL FINK!!!!": GOTO 990 +567 PRINT "ALSO BEEN DECLARED NATIONAL FINK!!!!" : GOTO 990 710 PRINT "HAMURABI: THINK AGAIN. YOU HAVE ONLY" -711 PRINT S;"BUSHELS OF GRAIN. NOW THEN," -712 RETURN -720 PRINT "HAMURABI: THINK AGAIN. YOU OWN ONLY";A;"ACRES. NOW THEN," -730 RETURN -800 C=INT(RND(1)*5)+1 -801 RETURN -850 PRINT: PRINT "HAMURABI: I CANNOT DO WHAT YOU WISH." +711 PRINT S; "BUSHELS OF GRAIN. NOW THEN," +712 RETURN +720 PRINT "HAMURABI: THINK AGAIN. YOU OWN ONLY "; A; " ACRES. NOW THEN," +730 RETURN +800 C = INT(RND(1) * 5) + 1 +801 RETURN +850 PRINT : PRINT "HAMURABI: I CANNOT DO WHAT YOU WISH." 855 PRINT "GET YOURSELF ANOTHER STEWARD!!!!!" 857 GOTO 990 -860 PRINT "IN YOUR 10-YEAR TERM OF OFFICE,";P1;"PERCENT OF THE" +860 PRINT "IN YOUR 10-YEAR TERM OF OFFICE,"; P1; "PERCENT OF THE" 862 PRINT "POPULATION STARVED PER YEAR ON THE AVERAGE, I.E. A TOTAL OF" -865 PRINT D1;"PEOPLE DIED!!": L=A/P +865 PRINT D1; "PEOPLE DIED!!" : L = A / P 870 PRINT "YOU STARTED WITH 10 ACRES PER PERSON AND ENDED WITH" -875 PRINT L;"ACRES PER PERSON.": PRINT -880 IF P1>33 THEN 565 -885 IF L<7 THEN 565 -890 IF P1>10 THEN 940 -892 IF L<9 THEN 940 -895 IF P1>3 THEN 960 -896 IF L<10 THEN 960 +875 PRINT L; "ACRES PER PERSON." : PRINT +880 IF P1 > 33 THEN 565 +885 IF L < 7 THEN 565 +890 IF P1 > 10 THEN 940 +892 IF L < 9 THEN 940 +895 IF P1 > 3 THEN 960 +896 IF L < 10 THEN 960 900 PRINT "A FANTASTIC PERFORMANCE!!! CHARLEMANGE, DISRAELI, AND" -905 PRINT "JEFFERSON COMBINED COULD NOT HAVE DONE BETTER!":GOTO 990 +905 PRINT "JEFFERSON COMBINED COULD NOT HAVE DONE BETTER!" : GOTO 990 940 PRINT "YOUR HEAVY-HANDED PERFORMANCE SMACKS OF NERO AND IVAN IV." 945 PRINT "THE PEOPLE (REMAINING) FIND YOU AN UNPLEASANT RULER, AND," -950 PRINT "FRANKLY, HATE YOUR GUTS!!":GOTO 990 +950 PRINT "FRANKLY, HATE YOUR GUTS!!" : GOTO 990 960 PRINT "YOUR PERFORMANCE COULD HAVE BEEN SOMEWHAT BETTER, BUT" -965 PRINT "REALLY WASN'T TOO BAD AT ALL. ";INT(P*.8*RND(1));"PEOPLE" +965 PRINT "REALLY WASN'T TOO BAD AT ALL. "; INT(P * 0.8 * RND(1)); "PEOPLE" 970 PRINT "WOULD DEARLY LIKE TO SEE YOU ASSASSINATED BUT WE ALL HAVE OUR" 975 PRINT "TRIVIAL PROBLEMS." -990 PRINT: FOR N=1 TO 10: PRINT CHR$(7);: NEXT N -995 PRINT "SO LONG FOR NOW.": PRINT -999 END +990 PRINT : FOR N = 1 TO 10 : PRINT CHR$(7); : NEXT N +995 PRINT "SO LONG FOR NOW." : PRINT +999 END \ No newline at end of file diff --git a/00_Alternate_Languages/44_Hangman/MiniScript/README.md b/00_Alternate_Languages/44_Hangman/MiniScript/README.md new file mode 100644 index 000000000..cf8b20021 --- /dev/null +++ b/00_Alternate_Languages/44_Hangman/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript hangman.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "hangman" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/44_Hangman/MiniScript/hangman.ms b/00_Alternate_Languages/44_Hangman/MiniScript/hangman.ms new file mode 100644 index 000000000..53966d45e --- /dev/null +++ b/00_Alternate_Languages/44_Hangman/MiniScript/hangman.ms @@ -0,0 +1,127 @@ +print " "*32 + "Hangman" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print + +words = [] +words += ["gum","sin","for","cry","lug","bye","fly"] +words += ["ugly","each","from","work","talk","with","self"] +words += ["pizza","thing","feign","fiend","elbow","fault","dirty"] +words += ["budget","spirit","quaint","maiden","escort","pickax"] +words += ["example","tension","quinine","kidney","replica","sleeper"] +words += ["triangle","kangaroo","mahogany","sergeant","sequence"] +words += ["moustache","dangerous","scientist","different","quiescent"] +words += ["magistrate","erroneously","loudspeaker","phytotoxic"] +words += ["matrimonial","parasympathomimetic","thigmotropism"] +// Note: on Mini Micro, you could instead do: +// words = file.readLines("/sys/data/englishWords.txt") + +words.shuffle + +addToPic = function(guessCount) + if guessCount == 1 then + print "First, we draw a head" + ps[3][6]="-"; ps[3][7]="-"; ps[3][8]="-"; ps[4][5]="("; ps[4][6]="." + ps[4][8]="."; ps[4][9]=")"; ps[5][6]="-"; ps[5][7]="-"; ps[5][8]="-" + else if guessCount == 2 then + print "Now we draw a body." + for i in range(6, 9); ps[i][7]="x"; end for + else if guessCount == 3 then + print "Next we draw an arm." + for i in range(4, 7); ps[i][i-1]="\"; end for + else if guessCount == 4 then + print "This time it's the other arm." + ps[4][11]="/"; ps[5][10]="/"; ps[6][9]="/"; ps[7][8]="/" + else if guessCount == 5 then + print "Now, let's draw the right leg." + ps[10][6]="/"; ps[11][5]="/" + else if guessCount == 6 then + print "This time we draw the left leg." + ps[10][8]="\"; ps[11][9]="\" + else if guessCount == 7 then + print "Now we put up a hand." + ps[3][11]="\" + else if guessCount == 8 then + print "Next the other hand." + ps[3][3]="/" + else if guessCount == 9 then + print "Now we draw one foot" + ps[12][10]="\"; ps[12][11]="-" + else if guessCount == 10 then + print "Here's the other foot -- you're hung!!" + ps[12][3]="-"; ps[12][4]="/" + end if + for i in range(1, 12) + print ps[i].join("") + end for + print +end function + +doOneGame = function + usedLetters = [] + globals.ps = [] + for i in range(0, 12); ps.push [" "]*12; end for + for i in range(1,12); ps[i][1] = "X"; end for + for i in range(1, 7); ps[1][i] = "X"; end for; ps[2][7] = "X" + secretWord = words.pull.upper + //print "(Secret word: " + secretWord + ")" + visibleWord = ["-"] * secretWord.len + wrongGuesses = 0 + while true + print "Here are the letters you used:" + print usedLetters.join(",") + print + print visibleWord.join("") + print + guess = input("What is your guess? ").upper + guess = (guess + " ")[0] + if guess < "A" or guess > "Z" then continue + if usedLetters.indexOf(guess) != null then + print "You guessed that letter before!" + continue + end if + usedLetters.push guess + for i in visibleWord.indexes + if secretWord[i] == guess then visibleWord[i] = guess + end for + if visibleWord.indexOf("-") == null then + print "You found the word!" + return true + else if secretWord.indexOf(guess) != null then + print + print visibleWord.join("") + print + guess = input("What is your guess for the word? ").upper + if guess == secretWord then + print "Right!! It took you " + usedLetters.len + " guesses!" + return true + else + print "Wrong. Try another letter." + end if + print + else + print "Sorry, that letter isn't in the word." + wrongGuesses += 1 + addToPic wrongGuesses + if wrongGuesses > 9 then + print "Sorry, you lose. The word was " + secretWord + return false + end if + end if + end while +end function + +while true + if not words then + print "You did all the words!!" + break + end if + won = doOneGame + if won then + yn = input("Want another word? ").upper + else + yn = input("You missed that one. Do you want another word? ").upper + end if + if not yn or yn[0] != "Y" then break +end while +print +print "It's been fun! Bye for now." diff --git a/00_Alternate_Languages/45_Hello/ANSI_C/hello.c b/00_Alternate_Languages/45_Hello/ANSI_C/hello.c new file mode 100644 index 000000000..74b0275f2 --- /dev/null +++ b/00_Alternate_Languages/45_Hello/ANSI_C/hello.c @@ -0,0 +1,166 @@ +#include +#include + +#define TRUE 1 +#define FALSE 0 +#define MAX_INPUT_LENGTH 80 + +void tab(int number_of_spaces); +void get_input(char *input_buffer); +int strings_match(char *string1, char *string2); + +int main() { + int done = FALSE; + int paid = FALSE; + int maybe_more, sure; + + char name[MAX_INPUT_LENGTH]; + char reply[MAX_INPUT_LENGTH]; + + tab(33); + printf("HELLO\n"); + tab(15); + printf("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n"); + printf("\n\n\n"); + printf("HELLO. MY NAME IS CREATIVE COMPUTER.\n"); + printf("\n\nWHAT'S YOUR NAME "); + get_input(name); + printf("\nHI THERE, %s, ARE YOU ENJOYING YOURSELF HERE ", name); + + get_input(reply); + while (!strings_match(reply, "YES") && !strings_match(reply, "NO")) { + printf("%s, I DON'T UNDERSTAND YOUR ANSWER OF '%s'.\n", name, reply); + printf("PLEASE ANSWER 'YES' OR 'NO'. DO YOU LIKE IT HERE "); + get_input(reply); + } + + if (strings_match(reply, "YES")) { + printf("I'M GLAD TO HEAR THAT, %s.\n", name); + } + else { + printf("OH, I'M SORRY TO HEAR THAT, %s. MAYBE WE CAN " + "BRIGHTEN UP YOUR VISIT A BIT.\n", name); + } + + printf("\nSAY, %s, I CAN SOLVE ALL KINDS OF PROBLEMS EXCEPT " + "THOSE DEALING WITH GREECE. WHAT KIND OF PROBLEMS DO " + "YOU HAVE (ANSWER SEX, HEALTH, MONEY, OR JOB) ", name); + + while (!done) { + get_input(reply); + + if (strings_match(reply, "JOB")) { + printf("I CAN SYMPATHIZE WITH YOU %s. I HAVE TO WORK " + "VERY LONG HOURS FOR NO PAY -- AND SOME OF MY BOSSES " + "REALLY BEAT ON MY KEYBOARD. MY ADVICE TO YOU, %s, IS TO " + "OPEN A RETAIL COMPUTER STORE. IT'S GREAT FUN.\n\n", name, name); + } + + else if (strings_match(reply, "MONEY")) { + printf("SORRY, %s, I'M BROKE TOO. WHY DON'T YOU SELL " + "ENCYCLOPEADIAS OR MARRY SOMEONE RICH OR STOP EATING " + "SO YOU WON'T NEED SO MUCH MONEY?\n\n", name); + } + + else if (strings_match(reply, "HEALTH")) { + printf("MY ADVICE TO YOU %s IS:\n", name); + printf(" 1. TAKE TWO ASPRIN\n"); + printf(" 2. DRINK PLENTY OF FLUIDS (ORANGE JUICE, NOT BEER!)\n"); + printf(" 3. GO TO BED (ALONE)\n\n"); + } + + else if (strings_match(reply, "SEX")) { + printf("IS YOUR PROBLEM TOO MUCH OR TOO LITTLE "); + + sure = FALSE; + while (!sure) { + get_input(reply); + if (strings_match(reply, "TOO MUCH")) { + printf("YOU CALL THAT A PROBLEM?!! I SHOULD HAVE SUCH PROBLEMS!\n"); + printf("IF IT BOTHERS YOU, %s, TAKE A COLD SHOWER.\n\n", name); + sure = TRUE; + } + else if (strings_match(reply, "TOO LITTLE")) { + printf("WHY ARE YOU HERE IN SUFFERN, %s? YOU SHOULD BE " + "IN TOKYO OR NEW YORK OR AMSTERDAM OR SOMEPLACE WITH SOME " + "REAL ACTION.\n\n", name); + sure = TRUE; + } + else { + printf("DON'T GET ALL SHOOK, %s, JUST ANSWER THE QUESTION " + "WITH 'TOO MUCH' OR 'TOO LITTLE'. WHICH IS IT ", name); + } + } + } + + else { // not one of the prescribed categories + printf("OH, %s, YOUR ANSWER OF '%s' IS GREEK TO ME.\n\n", name, reply); + } + + printf("ANY MORE PROBLEMS YOU WANT SOLVED, %s ", name); + + maybe_more = TRUE; + while (maybe_more) { + get_input(reply); + if (strings_match(reply, "NO")) { + done = TRUE; + maybe_more = FALSE; + } + else if (strings_match(reply, "YES")) { + printf("WHAT KIND (SEX, MONEY, HEALTH, JOB) "); + maybe_more = FALSE; + } + else { + printf("JUST A SIMPLE 'YES' OR 'NO' PLEASE, %s. ", name); + } + } // no further questions + } // end of 'not done' loop + + printf("\nTHAT WILL BE $5.00 FOR THE ADVICE, %s.\n", name); + printf("PLEASE LEAVE THE MONEY ON THE TERMINAL.\n"); + // pause a few seconds + printf("\n\n\nDID YOU LEAVE THE MONEY "); + get_input(reply); + while (!paid) { + if (strings_match(reply, "YES")) { + printf("HEY, %s??? YOU LEFT NO MONEY AT ALL!\n", name); + printf("YOU ARE CHEATING ME OUT OF MY HARD-EARNED LIVING.\n"); + printf("\nWHAT A RIP OFF, %s!!!\n", name); + printf("TAKE A WALK, %s.\n\n", name); + paid = TRUE; + } + else if (strings_match(reply, "NO")) { + printf("THAT'S HONEST, %s, BUT HOW DO YOU EXPECT " + "ME TO GO ON WITH MY PSYCHOLOGY STUDIES IF MY PATIENTS " + "DON'T PAY THEIR BILLS?\n\n", name); + printf("NICE MEETING YOU, %s, HAVE A NICE DAY.\n", name); + paid = TRUE; + } + else { + printf("YOUR ANSWER OF '%s' CONFUSES ME, %s.\n", reply, name); + printf("PLEASE RESPOND WITH 'YES' OR 'NO'.\n"); + } + } +} + + +void tab(int number_of_spaces) { + for (int i=0; i < number_of_spaces; i++) + putchar(' '); +} + + +void get_input(char *input_buffer) { + fgets(input_buffer, MAX_INPUT_LENGTH - 1, stdin); + input_buffer[strcspn(input_buffer, "\n")] = '\0'; // trim the trailing line break +} + + +int strings_match(char *string1, char *string2) { + if (strncasecmp(string1, string2, MAX_INPUT_LENGTH - 1) != 0) + return FALSE; + else // strings match, at least within maximum input line length + return TRUE; +} + + diff --git a/00_Alternate_Languages/45_Hello/MiniScript/README.md b/00_Alternate_Languages/45_Hello/MiniScript/README.md new file mode 100644 index 000000000..c3e534cbf --- /dev/null +++ b/00_Alternate_Languages/45_Hello/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript hello.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "hello" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/45_Hello/MiniScript/hello.ms b/00_Alternate_Languages/45_Hello/MiniScript/hello.ms new file mode 100644 index 000000000..a82344ef2 --- /dev/null +++ b/00_Alternate_Languages/45_Hello/MiniScript/hello.ms @@ -0,0 +1,108 @@ +print " "*33 + "Hello" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print + +print "Hello. My name is Creative Computer." +print +print +ns = input("What's your name? ") +print +print "Hi there, " + ns + ", are you enjoying yourself here?" +while true + bs = input.lower + print + if bs == "yes" then + print "I'm glad to hear that, " + ns + "." + print + break + else if bs == "no" then + print "Oh, I'm sorry to hear that, " + ns + ". Maybe we can" + print "brighten up your visit a bit." + break + else + print "Please answer 'yes' or 'no'. Do you like it here?" + end if +end while +print +print "Say, " + ns + ", I can solve all kinds of problems except" +print "those dealing with Greece. What kind of problems do" +print "you have (answer sex, health, money, or job)?" +while true + cs = input + print + if cs != "sex" and cs != "health" and cs != "money" and cs != "job" then + print "Oh, " + ns + ", your answer of " + cs + " is Greek to me." + else if cs == "job" then + print "I can sympathize with you " + ns + ". I have to work" + print "very long hours for no pay -- and some of my bosses" + print "really beat on my keyboard. My advice to you, " + ns + "," + print "is to open a retail computer store. It's great fun." + else if cs == "money" then + print "Sorry, " + ns + ", I'm broke too. Why don't you sell" + print "encyclopeadias or marry someone rich or stop eating" + print "so you won't need so much money?" + else if cs == "health" then + print "My advice to you " + ns + " is:" + print " 1. Take two asprin" + print " 2. Drink plenty of fluids (orange juice, not beer!)" + print " 3. Go to bed (alone)" + else + print "Is your problem too much or too little?" + while true + ds = input.lower + print + if ds == "too much" then + print "You call that a problem?!! I should have such problems!" + print "If it bothers you, " + ns + ", take a cold shower." + break + else if ds == "too little" then + print "Why are you here in suffern, " + ns + "? You should be" + print "in Tokyo or New York or Amsterdam or someplace with some" + print "real action." + break + else + print "Don't get all shook, " + ns + ", just answer the question" + print "with 'too much' or 'too little'. Which is it?" + end if + end while + end if + print + print "Any more problems you want solved, " + ns + "?" + es = input.lower + print + if es == "yes" then + print "What kind (sex, money, health, job)?" + else if es == "no" then + print "That will be $5.00 for the advice, " + ns + "." + print "Please leave the money on the terminal." + print + wait 2 + print + print + while true + gs = input("Did you leave the money? ").lower + print + if gs == "yes" then + print "Hey, " + ns + "??? You left no money at all!" + print "You are cheating me out of my hard-earned living." + print + print "What a rip off, " + ns + "!!!" + print + break + else if gs == "no" then + print "That's honest, " + ns + ", but how do you expect" + print "me to go on with my psychology studies if my patient" + print "don't pay their bills?" + break + else + print "Your answer of '" + gs + "' confuses me, " + ns + "." + print "Please respond with 'yes' or 'no'." + end if + end while + break + end if +end while +print +print "Take a walk, " + ns + "." +print +print diff --git a/00_Alternate_Languages/45_Hello/Swift/hello.swift b/00_Alternate_Languages/45_Hello/Swift/hello.swift new file mode 100644 index 000000000..0d882f755 --- /dev/null +++ b/00_Alternate_Languages/45_Hello/Swift/hello.swift @@ -0,0 +1,157 @@ + +func tab(_ number_Of_Spaces: Int) { + var spaces = "" + + for _ in 1...number_Of_Spaces { + spaces += " " + } + print(spaces, terminator:"") +} + + +func get_Input() -> String { + let input = readLine() + return (input == nil ? "" : input!.uppercased()) +} + + +func main() +{ + var done = false, + answered = false, + maybe_More = false, + paid = false + var reply = "" + var name = "STRANGER" + + tab (33) + print("HELLO") + tab (15) + print("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n") + + print("HELLO. MY NAME IS CREATIVE COMPUTER.") + print("WHAT'S YOUR NAME? ") + let input = readLine() + if (input != nil && input != "") { + name = input!.uppercased() + } + + print("\nHI THERE, \(name), ARE YOU ENJOYING YOURSELF HERE?") + + reply = get_Input() + while (reply != "YES" && reply != "NO") { + print("\(name), I DON'T UNDERSTAND YOUR ANSWER OF '\(reply)'.") + print("PLEASE ANSWER 'YES' OR 'NO'. DO YOU LIKE IT HERE?") + reply = get_Input() + } + + if (reply == "YES") { + print("\nI'M GLAD TO HEAR THAT, \(name).\n") + } + else { + print("\nOH, I'M SORRY TO HEAR THAT, \(name). MAYBE WE CAN " + + "BRIGHTEN UP YOUR VISIT A BIT.\n") + } + + print("SAY, \(name), I CAN SOLVE ALL KINDS OF PROBLEMS EXCEPT " + + "THOSE DEALING WITH GREECE. WHAT KIND OF PROBLEMS DO " + + "YOU HAVE (ANSWER SEX, HEALTH, MONEY, OR JOB)?") + + while (!done) { + reply = get_Input() + + if (reply == "JOB") { + print("\nI CAN SYMPATHIZE WITH YOU \(name). I HAVE TO WORK " + + "VERY LONG HOURS FOR NO PAY -- AND SOME OF MY BOSSES " + + "REALLY BEAT ON MY KEYBOARD. MY ADVICE TO YOU, \(name), IS TO " + + "OPEN A RETAIL COMPUTER STORE. IT'S GREAT FUN.\n") + } + + else if (reply == "MONEY") { + print("\nSORRY, \(name), I'M BROKE TOO. WHY DON'T YOU SELL " + + "ENCYCLOPEADIAS OR MARRY SOMEONE RICH OR STOP EATING " + + "SO YOU WON'T NEED SO MUCH MONEY?\n") + } + + else if (reply == "HEALTH") { + print("\nMY ADVICE TO YOU \(name) IS:") + print(" 1. TAKE TWO ASPRIN") + print(" 2. DRINK PLENTY OF FLUIDS (ORANGE JUICE, NOT BEER!)") + print(" 3. GO TO BED (ALONE)\n") + } + + else if (reply == "SEX") { + print("\nIS YOUR PROBLEM TOO MUCH OR TOO LITTLE?") + + answered = false + while (!answered) { + reply = get_Input() + if (reply == "TOO MUCH") { + print("\nYOU CALL THAT A PROBLEM?!! I SHOULD HAVE SUCH PROBLEMS!") + print("IF IT BOTHERS YOU, \(name), TAKE A COLD SHOWER.\n") + answered = true + } + else if (reply == "TOO LITTLE") { + print("\nWHY ARE YOU HERE IN SUFFERN, \(name)? YOU SHOULD BE " + + "IN TOKYO OR NEW YORK OR AMSTERDAM OR SOMEPLACE WITH SOME " + + "REAL ACTION.\n") + answered = true + } + else { + print("\nDON'T GET ALL SHOOK, \(name), JUST ANSWER THE QUESTION " + + "WITH 'TOO MUCH' OR 'TOO LITTLE'. WHICH IS IT?") + } + } + } + + else { // not one of the prescribed categories + print("\nOH, \(name), YOUR ANSWER OF '\(reply)' IS GREEK TO ME.\n") + } + + print("\nANY MORE PROBLEMS YOU WANT SOLVED, \(name)? ") + + maybe_More = true + while (maybe_More) { + reply = get_Input() + if (reply == "NO") { + done = true + maybe_More = false + } + else if (reply == "YES") { + print("\nWHAT KIND (SEX, MONEY, HEALTH, JOB) ") + maybe_More = false + } + else { + print("\nJUST A SIMPLE 'YES' OR 'NO' PLEASE, \(name). ") + } + } // no further questions + } // end of 'not done' loop + + print("\nTHAT WILL BE $5.00 FOR THE ADVICE, \(name).") + print("PLEASE LEAVE THE MONEY ON THE TERMINAL.") + // pause a few seconds + print("\n\n\nDID YOU LEAVE THE MONEY? ") + reply = get_Input() + while (!paid) { + if (reply == "YES") { + print("\nHEY, \(name)??? YOU LEFT NO MONEY AT ALL!") + print("YOU ARE CHEATING ME OUT OF MY HARD-EARNED LIVING.\n") + print("WHAT A RIP OFF, \(name)!!!\n") + print("TAKE A WALK, \(name).") + paid = true + } + else if (reply == "NO") { + print("THAT'S HONEST, \(name), BUT HOW DO YOU EXPECT " + + "ME TO GO ON WITH MY PSYCHOLOGY STUDIES IF MY PATIENTS " + + "DON'T PAY THEIR BILLS?\n") + print("NICE MEETING YOU, \(name), HAVE A NICE DAY.") + paid = true + } + else { + print("YOUR ANSWER OF '\(reply)' CONFUSES ME, \(name).") + print("PLEASE RESPOND WITH 'YES' OR 'NO'.") + } + } +} + +main() diff --git a/00_Alternate_Languages/45_Hello/hello.c b/00_Alternate_Languages/45_Hello/hello.c new file mode 100644 index 000000000..ac70709ef --- /dev/null +++ b/00_Alternate_Languages/45_Hello/hello.c @@ -0,0 +1,164 @@ +#include +#include + +#define TRUE 1 +#define FALSE 0 +#define MAX_INPUT_LENGTH 80 + +void tab(int number_of_spaces); +void get_input(char *input_buffer); +int strings_match(char *string1, char *string2); + +int main() { + int done = FALSE; + int paid = FALSE; + int maybe_more, sure; + + char name[MAX_INPUT_LENGTH]; + char reply[MAX_INPUT_LENGTH]; + + tab(33); + printf("HELLO\n"); + tab(15); + printf("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n"); + printf("\n\n\n"); + printf("HELLO. MY NAME IS CREATIVE COMPUTER.\n"); + printf("\n\nWHAT'S YOUR NAME "); + get_input(name); + printf("\nHI THERE, %s, ARE YOU ENJOYING YOURSELF HERE ", name); + + get_input(reply); + while (!strings_match(reply, "YES") && !strings_match(reply, "NO")) { + printf("%s, I DON'T UNDERSTAND YOUR ANSWER OF '%s'.\n", name, reply); + printf("PLEASE ANSWER 'YES' OR 'NO'. DO YOU LIKE IT HERE "); + get_input(reply); + } + + if (strings_match(reply, "YES")) { + printf("I'M GLAD TO HEAR THAT, %s.\n", name); + } + else { + printf("OH, I'M SORRY TO HEAR THAT, %s. MAYBE WE CAN " + "BRIGHTEN UP YOUR VISIT A BIT.\n", name); + } + + printf("\nSAY, %s, I CAN SOLVE ALL KINDS OF PROBLEMS EXCEPT " + "THOSE DEALING WITH GREECE. WHAT KIND OF PROBLEMS DO " + "YOU HAVE (ANSWER SEX, HEALTH, MONEY, OR JOB) ", name); + + while (!done) { + get_input(reply); + + if (strings_match(reply, "JOB")) { + printf("I CAN SYMPATHIZE WITH YOU %s. I HAVE TO WORK " + "VERY LONG HOURS FOR NO PAY -- AND SOME OF MY BOSSES " + "REALLY BEAT ON MY KEYBOARD. MY ADVICE TO YOU, %s, IS TO " + "OPEN A RETAIL COMPUTER STORE. IT'S GREAT FUN.\n\n", name, name); + } + + else if (strings_match(reply, "MONEY")) { + printf("SORRY, %s, I'M BROKE TOO. WHY DON'T YOU SELL " + "ENCYCLOPEADIAS OR MARRY SOMEONE RICH OR STOP EATING " + "SO YOU WON'T NEED SO MUCH MONEY?\n\n", name); + } + + else if (strings_match(reply, "HEALTH")) { + printf("MY ADVICE TO YOU %s IS:\n", name); + printf(" 1. TAKE TWO ASPRIN\n"); + printf(" 2. DRINK PLENTY OF FLUIDS (ORANGE JUICE, NOT BEER!)\n"); + printf(" 3. GO TO BED (ALONE)\n\n"); + } + + else if (strings_match(reply, "SEX")) { + printf("IS YOUR PROBLEM TOO MUCH OR TOO LITTLE "); + + sure = FALSE; + while (!sure) { + get_input(reply); + if (strings_match(reply, "TOO MUCH")) { + printf("YOU CALL THAT A PROBLEM?!! I SHOULD HAVE SUCH PROBLEMS!\n"); + printf("IF IT BOTHERS YOU, %s, TAKE A COLD SHOWER.\n\n", name); + sure = TRUE; + } + else if (strings_match(reply, "TOO LITTLE")) { + printf("WHY ARE YOU HERE IN SUFFERN, %s? YOU SHOULD BE " + "IN TOKYO OR NEW YORK OR AMSTERDAM OR SOMEPLACE WITH SOME " + "REAL ACTION.\n\n", name); + sure = TRUE; + } + else { + printf("DON'T GET ALL SHOOK, %s, JUST ANSWER THE QUESTION " + "WITH 'TOO MUCH' OR 'TOO LITTLE'. WHICH IS IT ", name); + } + } + } + + else { // not one of the prescribed categories + printf("OH, %s, YOUR ANSWER OF '%s' IS GREEK TO ME.\n\n", name, reply); + } + + printf("ANY MORE PROBLEMS YOU WANT SOLVED, %s ", name); + + maybe_more = TRUE; + while (maybe_more) { + get_input(reply); + if (strings_match(reply, "NO")) { + done = TRUE; + maybe_more = FALSE; + } + else if (strings_match(reply, "YES")) { + printf("WHAT KIND (SEX, MONEY, HEALTH, JOB) "); + maybe_more = FALSE; + } + else { + printf("JUST A SIMPLE 'YES' OR 'NO' PLEASE, %s. ", name); + } + } // no further questions + } // end of 'not done' loop + + printf("\nTHAT WILL BE $5.00 FOR THE ADVICE, %s.\n", name); + printf("PLEASE LEAVE THE MONEY ON THE TERMINAL.\n"); + // pause a few seconds + printf("\n\n\nDID YOU LEAVE THE MONEY "); + get_input(reply); + while (!paid) { + if (strings_match(reply, "YES")) { + printf("HEY, %s??? YOU LEFT NO MONEY AT ALL!\n", name); + printf("YOU ARE CHEATING ME OUT OF MY HARD-EARNED LIVING.\n"); + printf("\nWHAT A RIP OFF, %s!!!\n", name); + printf("TAKE A WALK, %s.\n\n", name); + paid = TRUE; + } + else if (strings_match(reply, "NO")) { + printf("THAT'S HONEST, %s, BUT HOW DO YOU EXPECT " + "ME TO GO ON WITH MY PSYCHOLOGY STUDIES IF MY PATIENTS " + "DON'T PAY THEIR BILLS?\n\n", name); + printf("NICE MEETING YOU, %s, HAVE A NICE DAY.\n", name); + paid = TRUE; + } + else { + printf("YOUR ANSWER OF '%s' CONFUSES ME, %s.\n", reply, name); + printf("PLEASE RESPOND WITH 'YES' OR 'NO'.\n"); + } + } +} + + +void tab(int number_of_spaces) { + for (int i=0; i < number_of_spaces; i++) + putchar(' '); +} + + +void get_input(char *input_buffer) { + fgets(input_buffer, MAX_INPUT_LENGTH - 1, stdin); + input_buffer[strcspn(input_buffer, "\n")] = '\0'; // trim the trailing line break +} + + +int strings_match(char *string1, char *string2) { + if (strncmp(string1, string2, MAX_INPUT_LENGTH - 1) != 0) + return FALSE; + else // strings do not match within maximum input line length + return TRUE; +} diff --git a/00_Alternate_Languages/46_Hexapawn/MiniScript/README.md b/00_Alternate_Languages/46_Hexapawn/MiniScript/README.md new file mode 100644 index 000000000..e2cd714ad --- /dev/null +++ b/00_Alternate_Languages/46_Hexapawn/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript hexapawn.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "hexapawn" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/46_Hexapawn/MiniScript/hexapawn.ms b/00_Alternate_Languages/46_Hexapawn/MiniScript/hexapawn.ms new file mode 100644 index 000000000..f2505ac4e --- /dev/null +++ b/00_Alternate_Languages/46_Hexapawn/MiniScript/hexapawn.ms @@ -0,0 +1,266 @@ +print " "*32 + "Hexapawn" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print + +// Hexapawn: interpretation of hexapawn game as presented in +// Martin Gardner's "The Unexpected Hanging and Other Mathematic- +// al Diversions", chapter eight: A Matchbox Game-Learning Machine +// Original version for H-P timeshare system by reversed.A. Kaapke 5/5/76 +// Instructions by jeff dalton +// Conversion to MITS BASIC by Steve North +// Conversion to MiniScript by Joe Strout + +// All 19 possible board positions: +ba = [null, + [null,-1,-1,-1,1,0,0,0,1,1], + [null,-1,-1,-1,0,1,0,1,0,1], + [null,-1,0,-1,-1,1,0,0,0,1], + [null,0,-1,-1,1,-1,0,0,0,1], + [null,-1,0,-1,1,1,0,0,1,0], + [null,-1,-1,0,1,0,1,0,0,1], + [null,0,-1,-1,0,-1,1,1,0,0], + [null,0,-1,-1,-1,1,1,1,0,0], + [null,-1,0,-1,-1,0,1,0,1,0], + [null,0,-1,-1,0,1,0,0,0,1], + [null,0,-1,-1,0,1,0,1,0,0], + [null,-1,0,-1,1,0,0,0,0,1], + [null,0,0,-1,-1,-1,1,0,0,0], + [null,-1,0,0,1,1,1,0,0,0], + [null,0,-1,0,-1,1,1,0,0,0], + [null,-1,0,0,-1,-1,1,0,0,0], + [null,0,0,-1,-1,1,0,0,0,0], + [null,0,-1,0,1,-1,0,0,0,0], + [null,-1,0,0,-1,1,0,0,0,0]] + +// Possible responses for each board position (move from x to y, +// represented as x*10 + y): +ma = [null, + [null,24,25,36,0], + [null,14,15,36,0], + [null,15,35,36,47], + [null,36,58,59,0], + [null,15,35,36,0], + [null,24,25,26,0], + [null,26,57,58,0], + [null,26,35,0,0], + [null,47,48,0,0], + [null,35,36,0,0], + [null,35,36,0,0], + [null,36,0,0,0], + [null,47,58,0,0], + [null,15,0,0,0], + [null,26,47,0,0], + [null,47,58,0,0], + [null,35,36,47,0], + [null,28,58,0,0], + [null,15,47,0,0]] +s = [0]*10 +t = [0]*10 + +showBoard = function + print + for i in [1,2,3] + print " "*10, "" + for j in [1,2,3] + print "X.O"[s[(i - 1) * 3 + j] + 1], "" + end for + print + end for +end function + +mirror = function(x) + return [null, 3,2,1, 6,5,4, 9,8,7][x] +end function + +mirrorBoard = function(b) + return [null, b[3],b[2],b[1], b[6],b[5],b[4], b[9],b[8],b[7]] +end function + +intro = function + while true + s = input("Instructions (Y-N)? ").lower + if s then s = s[0] + if s == "n" then return + if s == "y" then break + end while + print + print "This program plays the game of Hexapawn." + print "Hexapawn is played with Chess pawns on a 3 by 3 board." + print "The pawns are moved as in Chess - one space forward to" + print "an empty space or one space forward and diagonally to" + print "capture an opposing man. On the board, your pawns" + print "are 'O', the computer's pawns are 'X', and empty " + print "squares are '.'. To enter a move, type the number of" + print "the square you are moving from, followed by the number" + print "of the square you will move to. The numbers must be" + print "seperated by a comma." + print + input "(Press Return.)" + print + print "The computer starts a series of games knowing only when" + print "the game is won (a draw is impossible) and how to move." + print "It has no strategy at first and just moves randomly." + print "However, it learns from each game. Thus, winning becomes" + print "more and more difficult. Also, to help offset your" + print "initial advantage, you will not be told how to win the" + print "game but must learn this by playing." + print + print "The numbering of the board is as follows:" + print " "*10 + "123" + print " "*10 + "456" + print " "*10 + "789" + print + print "For example, to move your rightmost pawn forward," + print "you would type 9,6 in response to the question" + print "'Your move?'. Since I'm a good sport, you'll always" + print "go first." + print +end function + +getMove = function + while true + inp = input("Your move? ").replace(",", " ").split + if inp.len > 1 then + m1 = inp[0].val + m2 = inp[-1].val + if 0 < m1 < 10 and 0 < m2 < 10 then + if s[m1] != 1 or s[m2] == 1 or + (m2 - m1 != -3 and s[m2] != -1) or + (m2 > m1) or (m2 - m1 == -3 and s[m2] != 0) or + (m2 - m1 < -4) or (m1 == 7 and m2 == 3) then + print "Illegal move." + continue + end if + return [m1, m2] + end if + end if + print "Illegal co-ordinates." + end while +end function + +// Find the current board number (1-19) and whether it is mirrored. +findBoardNum = function + idx = ba.indexOf(s) + if idx != null then return [idx, false] + idx = ba.indexOf(mirrorBoard(s)) + if idx != null then return [idx, true] + return null +end function + +// Main program +intro +wins = 0 +losses = 0 +while true + s = [null, -1,-1,-1, 0,0,0, 1,1,1] + computerWins = false + showBoard + while true + // Input player's move + userMove = getMove + m1 = userMove[0]; m2 = userMove[1] + + // Move player's pawn + s[m1] = 0 + s[m2] = 1 + showBoard + + // If no computer pawns, or player reached top, then computer loses + if s.indexOf(-1) == null or s[1] == 1 or s[2] == 1 or s[3] == 1 then + break + end if + // Ensure at least one computer pawn with valid move. + // (Note: original BASIC code for this had several bugs; the code + // below should be more correct.) + anyValidMove = false + for i in range(1, 6) // (no sense checking position 7-9) + if s[i] != -1 then continue + // check for a straight-ahead move + if s[i + 3] == 0 then anyValidMove = true + // check for a capture + if i == 2 or i == 5 then + if s[i+2] == 1 or s[i+4] == 1 then anyValidMove = true + else if i == 1 or i == 4 then + if s[i+4] == 1 then anyValidMove = true + else + if s[i+2] == 1 then anyValidMove = true + end if + end for + if not anyValidMove then break + + boardAndReversed = findBoardNum + if boardAndReversed == null then + print "Illegal board pattern" // (should never happen in normal play) + break + end if + x = boardAndReversed[0]; reversed = boardAndReversed[1] + + // Select a random move for board X, as permitted by our memory + possibilities = [] + for i in range(1, 4) + if ma[x][i] != 0 then possibilities.push i + end for + + // For more insight into how the computer learns, uncomment this line: + //print "Considering for board " + x + ": " + possibilities + " (reversed=" + reversed + ")" + if not possibilities then + print "I resign." + break + end if + possibilities.shuffle + y = possibilities[0] + + m1 = floor(ma[x][y] / 10) + m2 = ma[x][y] % 10 + if reversed then + m1 = mirror(m1) + m2 = mirror(m2) + end if + + // Announce move + print "I move from " + m1 + " to " + m2 + s[m1] = 0 + s[m2] = -1 + showBoard + + // Finish if computer reaches bottom, or no player pawns are left + if s[7] == -1 or s[8] == -1 or s[9] == -1 or s.indexOf(1) == null then + computerWins = true + break + end if + + // Finish if player cannot move + playerCanMove = false + for i in range(1, 9) + if s[i] != 1 then continue + if i > 3 and s[i - 3] == 0 then playerCanMove = true + if mirror(i) != i then + if i >= 7 then + if s[5] == -1 then playerCanMove = true + else + if s[2] == -1 then playerCanMove = true + end if + else + if s[i - 2] == -1 or s[i - 4] == -1 then playerCanMove = true + end if + end for + if not playerCanMove then + print "You can't move, so ", "" + computerWins = true + break + end if + end while + if computerWins then + print "I win." + wins += 1 + else + print "You win" + // Because we lost, clear out the last response used, so that we don't + // make the same mistake again. This is how the computer learns! + ma[x][y] = 0 + losses += 1 + end if + print "I have won " + wins + " and you " + losses + " out of " + (losses + wins) + " games." + print + wait 2 +end while \ No newline at end of file diff --git a/00_Alternate_Languages/47_Hi-Lo/MiniScript/README.md b/00_Alternate_Languages/47_Hi-Lo/MiniScript/README.md new file mode 100644 index 000000000..dc20e7c83 --- /dev/null +++ b/00_Alternate_Languages/47_Hi-Lo/MiniScript/README.md @@ -0,0 +1,22 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +0. Try-It! Page: +Go to https://miniscript.org/tryit/, clear the sample code from the code editor, and paste in the contents of hi-lo.ms. Then click the "Run Script" button. Program output (and input) will appear in the green-on-black terminal display to the right of or below the code editor. + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript hi-lo.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "hi-lo" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/47_Hi-Lo/MiniScript/hi-lo.ms b/00_Alternate_Languages/47_Hi-Lo/MiniScript/hi-lo.ms new file mode 100644 index 000000000..bd0801eed --- /dev/null +++ b/00_Alternate_Languages/47_Hi-Lo/MiniScript/hi-lo.ms @@ -0,0 +1,41 @@ +print " "*34 + "Hi Lo" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print +print "This is the game of hi lo."; print +print "You will have 6 tries to guess the amount of money in the" +print "hi lo jackpot, which is between 1 and 100 dollars. If you" +print "guess the amount, you win all the money in the jackpot!" +print "Then you get another chance to win more money. However," +print "if you do not guess the amount, the game ends."; print +total = 0 +while true + guesses=0 + print + number=floor(100*rnd) + while true + guess = input("Your guess? ").val + guesses += 1 + if guess < number then + print "Your guess is too low." + else if guess > number then + print "Your guess is too high." + else + print "Got it!!!!!!!!!! You win " + number + " dollars." + total += number + print "Your total winnings are now " + total + " dollars." + break + end if + if guesses >= 6 then + print "You blew it...too bad...the number was " + number + total = 0 + break + end if + end while + + print + yn = input("Play again (yes or no)?").lower + if not yn or yn[0] != "y" then break +end while + +print +print "So long. Hope you enjoyed yourself!!!" diff --git a/00_Alternate_Languages/48_High_IQ/MiniScript/README.md b/00_Alternate_Languages/48_High_IQ/MiniScript/README.md new file mode 100644 index 000000000..794d1c57f --- /dev/null +++ b/00_Alternate_Languages/48_High_IQ/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript highiq.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "highiq" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/48_High_IQ/MiniScript/highiq.ms b/00_Alternate_Languages/48_High_IQ/MiniScript/highiq.ms new file mode 100644 index 000000000..5d2ab966f --- /dev/null +++ b/00_Alternate_Languages/48_High_IQ/MiniScript/highiq.ms @@ -0,0 +1,163 @@ +print " "*33 + "H-I-Q" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print +print "Here is the board:"; print + +print " ! ! !" +print " 13 14 15"; print +print " ! ! !" +print " 22 23 24"; print +print "! ! ! ! ! ! !" +print "29 30 31 32 33 34 35"; print +print "! ! ! ! ! ! !" +print "38 39 40 41 42 43 44"; print +print "! ! ! ! ! ! !" +print "47 48 49 50 51 52 53"; print +print " ! ! !" +print " 58 59 60"; print +print " ! ! !" +print " 67 68 69"; print +print "To save typing time, a compressed version of the game board" +print "will be used during play. Refer to the above one for peg" +input "numbers. Press Return to begin." +print + +// Prepare the board (t): a 9x9 2D array, with 5 for pins, +// -5 for invalid (no hole) positions, and 0 for empty holes. +// Also prepare the pinToPos map, which maps pin numbers to +// [row, column] positions. +setupBoard = function + globals.t = [[-5]*10] + globals.pinToPos = {} + pinNums = [13,14,15,22,23,24,29,30,31,32,33,34,35,38,39,40,41, + 42,43,44,47,48,49,50,51,52,53,58,59,60,67,68,69] + for row in range(1,9) + t.push [-5]*10 + for col in range(1,9) + if row < 2 or row > 8 or col < 2 or col > 8 or + ((row < 4 or row > 6) and (col < 4 or col > 6)) then + t[row][col] = -5 + else + t[row][col] = 5 + pinToPos[pinNums.pull] = [row,col] + end if + end for + end for + t[5][5] = 0 +end function + +printBoard = function + for x in range(1,9) + s = "" + for y in range(1,9) + if t[x][y] < 0 then + s += " " + else if t[x][y] == 0 then + s += " o" + else + s += " !" + end if + end for + print s + end for +end function + +isPin = function(pinNum) + if not pinToPos.hasIndex(pinNum) then return false + pos = pinToPos[pinNum] + return t[pos[0]][pos[1]] == 5 +end function + +isHole = function(pinNum) + if not pinToPos.hasIndex(pinNum) then return false + pos = pinToPos[pinNum] + return t[pos[0]][pos[1]] == 0 +end function + +isValidJump = function(pinFrom, pinTo) + if not pinToPos.hasIndex(pinFrom) then return false + posFrom = pinToPos[pinFrom] + if not isHole(pinTo) then return false + posTo = pinToPos[pinTo] + // check that the Manhattan distance is exactly 2 + dist = abs(posFrom[0] - posTo[0]) + abs(posFrom[1] - posTo[1]) + if dist != 2 then return false + // and check that the intervening position contains a pin + if t[(posFrom[0]+posTo[0])/2][(posFrom[1]+posTo[1])/2] != 5 then return false + return true +end function + +// Check if the game is over (player has no legal moves). +// Return true if over, false if there are legal moves yet. +checkGameOver = function + for row in range(2,8) + for col in range(2,8) + fromPin = pinToPos.indexOf([row,col]) + if fromPin == null or not isPin(fromPin) then continue + for r2 in [row-2, row+2] + toPin = pinToPos.indexOf([r2,col]) + if toPin == null then continue + if isValidJump(fromPin, toPin) then return false + end for + for c2 in [col-2, col+2] + toPin = pinToPos.indexOf([row,c2]) + if toPin == null then continue + if isValidJump(fromPin, toPin) then return false + end for + end for + end for + return true // no legal moves found, so game over +end function + +// Get the user's move, returning [[fromRow,fromCol], [toRow,toCol]]. +// (Check legality and return only legal moves.) +getMove = function + print + while true + fromNum = input("Move which piece? ").val + if isPin(fromNum) then toNum = input("To where? ").val else toNum = 0 + if isHole(toNum) and isValidJump(fromNum, toNum) then break + print "Illegal move, try again..." + end while + return [pinToPos[fromNum], pinToPos[toNum]] +end function + +// Get the user's move, and update the board accordingly. +doOneMove = function + move = getMove + fromRow = move[0][0]; fromCol = move[0][1] + toRow = move[1][0]; toCol = move[1][1] + t[fromRow][fromCol] = 0 + t[toRow][toCol] = 5 + t[(fromRow+toRow)/2][(fromCol+toCol)/2] = 0 +end function + +// Main program +while true + setupBoard + printBoard + while true + doOneMove + print + printBoard + if checkGameOver then break + end while + print; print "The game is over." + pinsLeft = 0 + for a in t + for b in a + if b == 5 then pinsLeft += 1 + end for + end for + print "You had " + pinsLeft + " pieces remaining." + if pinsLeft == 1 then + print "Bravo! You made a perfect score!" + print "Save this paper as a record of your accomplishment!" + end if + print + yn = input("Play again (yes or no)? ").lower + if yn and yn[0] == "n" then break +end while +print; print "So long for now."; print + + \ No newline at end of file diff --git a/00_Alternate_Languages/49_Hockey/MiniScript/README.md b/00_Alternate_Languages/49_Hockey/MiniScript/README.md new file mode 100644 index 000000000..8c92997dc --- /dev/null +++ b/00_Alternate_Languages/49_Hockey/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript hockey.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "hockey" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/49_Hockey/MiniScript/hockey.ms b/00_Alternate_Languages/49_Hockey/MiniScript/hockey.ms new file mode 100644 index 000000000..450736f70 --- /dev/null +++ b/00_Alternate_Languages/49_Hockey/MiniScript/hockey.ms @@ -0,0 +1,396 @@ +print " "*33 + "Hockey" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print +// ROBERT PUOPOLO ALG. 1 140 MCCOWAN 6/7/73 HOCKEY + +getYesNo = function(prompt) + while true + yn = input(prompt + "? ").lower + if yn and yn[0] == "y" then return "yes" + if yn and yn[0] == "n" then return "no" + print "Answer yes or no!!" + end while +end function + +getTwoStrings = function(prompt) + s = input(prompt + "? ").replace(", ", ",").split(",") + answer1 = s[0] + if s.len < 2 then + answer2 = input("?? ") + else + answer2 = s[1] + end if + return [answer1, answer2] +end function + +getNumber = function(prompt, minVal = 1, maxVal = 999) + while true + num = input(prompt + "? ").val + if minVal <= num <= maxVal then return num + end while +end function + +printWithTab = function(s) + print s.replace("\t", char(9)) +end function + +ha = [0] * 21 +ta = [0] * 6 +t1 = [0] * 6 +t2 = [0] * 6 +t3 = [0] * 6 +aNames = [""] * 8 // Team 1 player names and team name +bNames = [""] * 8 // Team 2 player names and team name +x = 1 +print; print; print +if getYesNo("Would you like the instructions") == "yes" then + print + print "This is a simulated hockey game." + print "Question Response" + print "pass Type in the number of passes you would" + print " like to make, from 0 to 3." + print "shot Type the number corresponding to the shot" + print " you want to make. Enter:" + print " 1 for a slapshot" + print " 2 for a wristshot" + print " 3 for a backhand" + print " 4 for a snap shot" + print "area Type in the number corresponding to" + print " the area you are aiming at. Enter:" + print " 1 for upper left hand corner" + print " 2 for upper right hand corner" + print " 3 for lower left hand corner" + print " 4 for lower right hand corner" + print + print "At the start of the game, you will be asked for the names" + print "of your players. They are entered in the order: " + print "left wing, center, right wing, left defense," + print "right defense, goalkeeper. Any other input required will" + print "have explanatory instructions." +end if + +setup = function + teamNames = getTwoStrings("Enter the two teams") + aNames[7] = teamNames[0] + bNames[7] = teamNames[1] + print + globals.roundsInGame = getNumber("Enter the number of minutes in a game") + print + print "Would the " + aNames[7] + " coach enter his team" + print + for i in range(1, 6) + aNames[i] = input("Player " + i + "? ") + end for + print + print "Would the " + bNames[7] + " coach do the same" + print + for t in range(1, 6) + bNames[t] = input("Player " + t + "? ") + end for + print + rs = input("Input the referee for this game? ") + print + print " "*10 + aNames[7] + " starting lineup" + for t in range(1, 6) + print aNames[t] + end for + print + print " "*10 + bNames[7] + " starting lineup" + for t in range(1, 6) + print bNames[t] + end for + print + print "We're ready for tonights opening face-off." + print rs + " will drop the puck between " + aNames[2] + " and " + bNames[2] +end function + +shootAndScore = function(shootingTeam, shooter, asst1, asst2, z) + while true + ha[20] = floor(100 * rnd) + 1 + if ha[20] % z != 0 then break + a2 = floor(100 * rnd) + 1 + if a2 % 4 == 0 then + if shootingTeam == 1 then + print "Save " + bNames[6] + " -= 1 rebound" + else + print "Save " + aNames[6] + " -= 1 follow up" + end if + continue + end if + end while + + if shootingTeam == 1 then + print "Goal " + aNames[7] + ha[9] += 1 + else + print "Score " + bNames[7] + ha[8] += 1 + end if + print char(7) * 25 + print "Score: " + if ha[8] <= ha[9] then + printWithTab aNames[7] + ": " + ha[9] + "\t" + bNames[7] + ": " + ha[8] + else + printWithTab bNames[7] + ": " + ha[8] + "\t" + aNames[7] + ": " + ha[9] + end if + if shootingTeam == 1 then + print "Goal scored by: " + aNames[shooter] + if asst1 then + if asst2 then + print " assisted by: " + aNames[asst1] + " and " + aNames[asst2] + else + print " assisted by: " + aNames[asst1] + end if + else + print " unassisted." + end if + ta[shooter] += 1 + t1[asst1] += 1 + t1[asst2] += 1 + else + print "Goal scored by: " + bNames[shooter] + if asst1 then + if asst2 then + print " assisted by: " + bNames[asst1] + " and " + bNames[asst2] + else + print " assisted by: " + bNames[asst1] + end if + else + print " unassisted." + end if + t2[shooter] += 1 + t3[asst1] += 1 + t3[asst2] += 1 + end if +end function + +shootBlocked = function(shootingTeam, shooter) + s1 = floor(6 * rnd) + 1 + if shootingTeam == 1 then + if s1 == 1 then + print "Kick save and a beauty by " + bNames[6] + print "cleared out by " + bNames[3] + return false + else if s1 == 2 then + print "what a spectacular glove save by " + bNames[6] + print "and " + bNames[6] + " golfs it into the crowd" + else if s1 == 3 then + print "skate save on a low steamer by " + bNames[6] + return false + else if s1 == 4 then + print "pad save by " + bNames[6] + " off the stick" + print "of " + aNames[shooter] + " and " + bNames[6] + " covers up" + else if s1 == 5 then + print "whistles one over the head of " + bNames[6] + return false + else if s1 == 6 then + print bNames[6] + " makes a face save!! and he is hurt" + print "the defenseman " + bNames[5] + " covers up for him" + end if + else + if s1 == 1 then + print "stick save by " + aNames[6] +"" + print "and cleared out by " + aNames[4] + return false + else if s1 == 2 then + print "Oh my god!! " + bNames[shooter] + " rattles one off the post" + print "to the right of " + aNames[6] + " and " + aNames[6] + " covers " + print "on the loose puck!" + else if s1 == 3 then + print "Skate save by " + aNames[6] + print aNames[6] + " whacks the loose puck into the stands" + else if s1 == 4 then + print "Stick save by " + aNames[6] + " and he clears it out himself" + return false + else if s1 == 5 then + print "Kicked out by " + aNames[6] + print "and it rebounds all the way to center ice" + return false + else if s1 == 6 then + print "Glove save " + aNames[6] + " and he hangs on" + end if + end if +end function + +doOneRound = function + control = floor(2 * rnd) + 1 + if control == 1 then + print aNames[7] + " has control of the puck" + else + print bNames[7] + " has control." + end if + p = getNumber("Pass", 0, 3) + for n in range(1, 3) + ha[n] = 0 + end for + while true + for j in range(1, p + 2) + ha[j] = floor(5 * rnd) + 1 + end for + if not (ha[j - 1] == ha[j - 2] or (p + 2 >= 3 and (ha[j - 1] == ha[j - 3] or ha[j - 2] == ha[j - 3]))) then break + end while + + if p == 0 then + s = getNumber("Shot", 1, 4) + if control == 1 then + print aNames[ha[j - 1]], "" + g = ha[j - 1] + g1 = 0 + g2 = 0 + else + print bNames[ha[j - 1]], "" + g2 = 0 + g2 = 0 + g = ha[j - 1] + end if + if s == 1 then + print " lets a boomer go from the red line!!" + z = 10 + else if s == 2 then + print " flips a wristshot down the ice" + // BUG: missing line 430 in the original caused it to fall through + // to the s == 3 case. We'll instead just do: + z = 2 + else if s == 3 then + print " backhands one in on the goaltender" + z = 25 + else + print " snaps a long flip shot" + z = 17 + end if + else + if control == 1 then + if p == 1 then + print aNames[ha[j - 2]] + " leads " + aNames[ha[j - 1]] + " with a perfect pass." + print aNames[ha[j - 1]] + " cutting in!!!" + g = ha[j - 1] + g1 = ha[j - 2] + g2 = 0 + z1 = 3 + else if p == 2 then + print aNames[ha[j - 2]] + " gives to a streaking " + aNames[ha[j - 1]] + print aNames[ha[j - 3]] + " comes down on " + bNames[5] + " and " + bNames[4] + g = ha[j - 3] + g1 = ha[j - 1] + g2 = ha[j - 2] + z1 = 2 + else if p == 3 then + print "oh my god!! a ' 4 on 2 ' situation" + print aNames[ha[j - 3]] + " leads " + aNames[ha[j - 2]] + print aNames[ha[j - 2]] + " is wheeling through center." + print aNames[ha[j - 2]] + " gives and goest with " + aNames[ha[j - 1]] + print "pretty passing!" + print aNames[ha[j - 1]] + " drops it to " + aNames[ha[j - 4]] + g = ha[j - 4] + g1 = ha[j - 1] + g2 = ha[j - 2] + z1 = 1 + end if + else + if p == 1 then + print bNames[ha[j - 1]] + " hits " + bNames[ha[j - 2]] + " flying down the left side" + g = ha[j - 2] + g1 = ha[j - 1] + g2 = 0 + z1 = 3 + else if p == 2 then + print "it's a ' 3 on 2 '!" + print "only " + aNames[4] + " and " + aNames[5] + " are back." + print bNames[ha[j - 2]] + " gives off to " + bNames[ha[j - 1]] + print bNames[ha[j - 1]] + " drops to " + bNames[ha[j - 3]] + g = ha[j - 3] + g1 = ha[j - 1] + g2 = ha[j - 2] + z1 = 2 + else if p == 3 then + print " a '3 on 2 ' with a ' trailer '!" + print bNames[ha[j - 4]] + " gives to " + bNames[ha[j - 2]] + " who shuffles it off to" + print bNames[ha[j - 1]] + " who fires a wing to wing pass to " + print bNames[ha[j - 3]] + " aNames he cuts in alone!!" + g = ha[j - 3] + g1 = ha[j - 1] + g2 = ha[j - 2] + z1 = 1 + end if + end if + s = getNumber("Shot", 1, 4) + if control == 1 then + print aNames[g], "" + else + print bNames[g], "" + end if + if s == 1 then + print " lets a big slap shot go!!" + z = 4 + z += z1 + else if s == 2 then + print " rips a wrist shot off" + z = 2 + z += z1 + else if s == 3 then + print " gets a backhand off" + z = 3 + z += z1 + else + print " snaps off a snap shot" + z = 2 + z += z1 + end if + end if + + a = getNumber("Area", 1, 4) // area shot in + a1 = floor(4 * rnd) + 1 // area vulnerable + + if control == 1 then + globals.teamAShotsOnNet += 1 + else + globals.teamBShotsOnNet += 1 + end if + if a == a1 then + shootAndScore control, g, g1, g2 + else + shootBlocked control, g + end if + return true +end function + +// Main program +setup +teamAShotsOnNet = 0 +teamBShotsOnNet = 0 +for l in range(1, roundsInGame) + while not doOneRound; end while // (repeat until it returns true) + if l < roundsInGame then print "And we're ready for the face-off" +end for + + + +print char(7)*30 +print "That's the Siren" +print +print " "*15 + "Final Score:" +if ha[8] <= ha[9] then + printWithTab aNames[7] + ": " + ha[9] + "\t" + bNames[7] + ": " + ha[8] +else + printWithTab bNames[7] + ": " + ha[8] + "\t" + aNames[7] + ": " + ha[9] +end if +print +print " "*10 + "Scoring Summary" +print +print " "*25 + aNames[7] +printWithTab "\tName\tGoals\tAssists" +printWithTab "\t -= 1 -= 1\t -= 1 -= 1-\t -= 1 -= 1 -= 1-" +for i in range(1, 5) + printWithTab "\t" + aNames[i] + "\t" + ta[i] + "\t" + t1[i] +end for +print +print " "*25 + bNames[7] +printWithTab "\tName\tGoals\tAssists" +printWithTab "\t -= 1 -= 1\t -= 1 -= 1-\t -= 1 -= 1 -= 1-" +for t in range(1, 5) + printWithTab "\t" + bNames[t] + "\t" + t2[t] + "\t" + t3[t] +end for +print +print "Shots on net" +print aNames[7] + ": " + teamAShotsOnNet +print bNames[7] + ": " + teamBShotsOnNet diff --git a/00_Alternate_Languages/50_Horserace/MiniScript/README.md b/00_Alternate_Languages/50_Horserace/MiniScript/README.md new file mode 100644 index 000000000..8f5703a2c --- /dev/null +++ b/00_Alternate_Languages/50_Horserace/MiniScript/README.md @@ -0,0 +1,25 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript horserace.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "horserace" + run +``` + +## Porting Notes + +- The original program, designed to be played directly on a printer, drew a track 27 rows long. To fit better on modern screens, I've shortened the track to 23 rows. This is adjustable via the "trackLen" value assigned on line 72. + +- Also because we're playing on a screen instead of a printer, I'm clearing the screen and pausing briefly before each new update of the track. This is done via the `clear` API when running in Mini Micro, or by using a VT100 escape sequence in other contexts. diff --git a/00_Alternate_Languages/50_Horserace/MiniScript/horserace.ms b/00_Alternate_Languages/50_Horserace/MiniScript/horserace.ms new file mode 100644 index 000000000..a8750f5ed --- /dev/null +++ b/00_Alternate_Languages/50_Horserace/MiniScript/horserace.ms @@ -0,0 +1,149 @@ +print " "*31 + "Horserace" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print + +print "Welcome to South Portland High Racetrack" +print " ...owned by Laurie Chevalier" + +// show directions, if wanted +x = input("Do you want directions? ").lower +if not x or x[0] != "n" then + print "Up to 10 may play. A table of odds will be printed. You" + print "may bet any + amount under 100000 on one horse." + print "During the race, a horse will be shown by its" + print "number. The horses race down the paper!" + print +end if + +// get player names +qtyPlayers = input("How many want to bet? ").val +if qtyPlayers < 1 then exit +print "When ? appears, type name" +playerNames = [null] +for i in range(1, qtyPlayers) + playerNames.push input("?") +end for +print +pick = [null] + [0]*qtyPlayers +bet = [null] + [0]*qtyPlayers + +d = [0] * 9 // odds denominator +r = 0 // odds numerator + +printInColumns = function(col0, col1, col2) + print (col0+" "*16)[16] + (col1+" "*10)[:10] + (col2+" "*10)[:10] +end function + +setup = function + // initialize the horses + globals.names = [null] + + "Joe Maw,L.B.J.,Mr.Washburn,Miss Karen,Jolly,Horse,Jelly Do Not,Midnight".split(",") + for i in range(1,8) + d[i] = floor(10*rnd+1) + end for + globals.r = d.sum + + // print odds table + printInColumns "Horse", "Number", "Odds" + for i in range(1,8) + printInColumns names[i], i, round(r/d[i],2) + ":1" + end for + + // get the players' bets + print "--------------------------------------------------" + print "Place your bets...Horse # then Amount" + for j in range(1, qtyPlayers) + while true + s = input(playerNames[j] + "? ").replace(",", " ").split + if s.len < 2 then s.push -1 + pick[j] = s[0].val + bet[j] = s[-1].val + if pick[j] < 1 or pick[j] > 8 or bet[j] < 1 or bet[j] >=100000 then + print "You can't do that!" + else + break + end if + end while + end for +end function + +// Draw a racetrack, with each horse the given number +// of lines down from START to FINISH. +trackLen = 23 +drawTrack = function(horsePositions) + print + if version.hostName == "Mini Micro" then clear else print char(27)+"c" + if horsePositions[1] == 0 then print "1 2 3 4 5 6 7 8" else print + print "XXXXSTARTXXXX" + for row in range(1, trackLen) + for h in range(1,8) + p = horsePositions[h] + if p > trackLen then p = trackLen + if p == row then print h, " " + end for + print + end for + print "XXXXFINISHXXXX", "" + if version.hostName != "Mini Micro" then print +end function + +runRace = function + pos = [0]*9 + maxPos = 0 + while true + drawTrack pos + wait 1 + if maxPos >= trackLen then break + for i in range(1,8) + q = floor(100*rnd+1) + x = floor(r/d[i]+0.5) + if q < 10 then + speed = 1 + else if q < x+17 then + speed = 2 + else if q < x+37 then + speed = 3 + else if q < x+57 then + speed = 4 + else if q < x+77 then + speed = 5 + else if q < x+92 then + speed = 6 + else + speed = 7 + end if + pos[i] += speed + if pos[i] > maxPos then maxPos = pos[i] + end for + end while + + print + print "---------------------------------------------" + print + print "The race results are:" + results = [] + for i in range(1,8) + results.push {"num":i, "pos":pos[i]} + end for + results.sort "pos", false + for place in range(1, 8) + h = results[place-1].num + print " " + place + " Place Horse No. " + h + " at " + round(r/d[h],2) + ":1" + print + end for + for p in range(1, qtyPlayers) + if pick[p] == results[0].num then + print playerNames[p] + " wins $" + round((r/d[pick[p]])*bet[p], 2) + end if + end for +end function + +// Main loop +while true + setup + runRace + print "Do you want to bet on the next race ?" + yn = input("Yes or no? ").lower + if not yn or yn[0] != "y" then break +end while + diff --git a/00_Alternate_Languages/51_Hurkle/MiniScript/README.md b/00_Alternate_Languages/51_Hurkle/MiniScript/README.md new file mode 100644 index 000000000..2f774e452 --- /dev/null +++ b/00_Alternate_Languages/51_Hurkle/MiniScript/README.md @@ -0,0 +1,22 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +0. Try-It! Page: +Go to https://miniscript.org/tryit/, clear the sample code from the code editor, and paste in the contents of hurkle.ms. Then click the "Run Script" button. Program output (and input) will appear in the green-on-black terminal display to the right of or below the code editor. + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript hurkle.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "hurkle" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/51_Hurkle/MiniScript/hurkle.ms b/00_Alternate_Languages/51_Hurkle/MiniScript/hurkle.ms new file mode 100644 index 000000000..a1b228222 --- /dev/null +++ b/00_Alternate_Languages/51_Hurkle/MiniScript/hurkle.ms @@ -0,0 +1,56 @@ +print " "*33 + "Hurcle" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print +n = 5 // number of guesses allowed +g = 10 // grid size +print +print "A hurkle is hiding on a " + g + " by " + g + " grid. Homebase" +print "on the grid is point 0,0 in the southwest corner," +print "and any point on the grid is designated by a" +print "pair of whole numbers seperated by a comma. The first" +print "number is the horizontal position and the second number" +print "is the vertical position. You must try to" +print "guess the hurkle's gridpoint. You get " + n + "tries." +print "After each try, I will tell you the approximate" +print "direction to go to look for the hurkle." +print + +playOneGame = function + a = floor(g*rnd) + b = floor(g*rnd) + for k in range(1, n) + s = input("Guess #" + k + "? ").replace(",", " ").split + x = s[0].val + y = s[-1].val + if x == a and y == b then + print + print "You found him in " + k + " guesses!" + return + end if + if y == b then + if x == a then + print + print "You found him in " + k + " guesses!" + return + else if x < a then + dir = "east" + else + dir = "west" + end if + else if y < b then + dir = "north" + else + dir = "south" + end if + print "Go " + dir + end for + print "Sorry, that's " + n + " guesses." + print "The hurkle is at " + a + "," + b +end function + +while true + playOneGame + print + print "Let's play again, hurkle is hiding." + print +end while diff --git a/00_Alternate_Languages/52_Kinema/MiniScript/README.md b/00_Alternate_Languages/52_Kinema/MiniScript/README.md new file mode 100644 index 000000000..173e59ad5 --- /dev/null +++ b/00_Alternate_Languages/52_Kinema/MiniScript/README.md @@ -0,0 +1,21 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript kinema.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "kinema" + run +``` +3. "Try-It!" page on the web: +Go to https://miniscript.org/tryit/, clear the default program from the source code editor, paste in the contents of kinema.ms, and click the "Run Script" button. diff --git a/00_Alternate_Languages/52_Kinema/MiniScript/kinema.ms b/00_Alternate_Languages/52_Kinema/MiniScript/kinema.ms new file mode 100644 index 000000000..9fb27071a --- /dev/null +++ b/00_Alternate_Languages/52_Kinema/MiniScript/kinema.ms @@ -0,0 +1,40 @@ +// Kinema +// +// Ported from BASIC to MiniScript by Joe Strout + +print " "*33 + "KINEMA" +print " "*15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" +print; print; print + +checkAnswer = function(prompt, correctValue) + answer = input(prompt).val + right = abs((answer - correctValue)/answer) < 0.15 + if right then + print "Close enough!" + else + print "Not even close...." + end if + print "Correct answer is " + correctValue + return right +end function + +doOneRun = function + print; print + rightCount = 0 + V = 5 + floor(35*rnd) + print "A ball is thrown upwards at " + V + " meters per second." + print + rightCount += checkAnswer("How high will it go (in meters)? ", 0.05 * V^2) + rightCount += checkAnswer("How long until it returns (in seconds)? ", V/5) + t = 1 + floor(2*V*rnd)/10 + rightCount += checkAnswer("What will its velocity be after " + t + + " seconds? ", V-10*t) + print + print rightCount + " right out of 3." + if rightCount >= 2 then print " Not bad." +end function + +// main loop (press control-C to break out) +while true + doOneRun +end while diff --git a/00_Alternate_Languages/53_King/MiniScript/README.md b/00_Alternate_Languages/53_King/MiniScript/README.md new file mode 100644 index 000000000..34818a09c --- /dev/null +++ b/00_Alternate_Languages/53_King/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript king.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "king" + run +``` diff --git a/00_Alternate_Languages/53_King/MiniScript/king.ms b/00_Alternate_Languages/53_King/MiniScript/king.ms new file mode 100644 index 000000000..8d4f106c9 --- /dev/null +++ b/00_Alternate_Languages/53_King/MiniScript/king.ms @@ -0,0 +1,403 @@ +print " "*34 + "KING" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print + +yearsRequired = 8 +rallods = floor(60000+(1000*rnd)-(1000*rnd)) +countrymen = floor(500+(10*rnd)-(10*rnd)) +landArea = 2000 +foreignWorkers = 0 +sellableLandExplained = false +tourism = 0 +year = 0 + +zs = input("Do you want instructions? ").lower +if zs == "again" then + while true + year = input("How many years had you been in office when interrupted? ").val + if 0 <= year < yearsRequired then break + print " Come on, your term in office is only " + yearsRequired + " years." + end while + rallods = input("How much did you have in the treasury? ").val + countrymen = input("How many countrymen? ").val + foreignWorkers = input("How many foreign workers? ").val + while true + landArea = input("How many square miles of land? ").val + if 1000 <= landArea <= 2000 then break + print " Come on, you started with 1000 sq. miles of farm land" + print " and 1000 sq. miles of forest land." + end while +else if not zs or zs[0] != "n" then + print; print; print + print "Congratulations! You've just been elected premier of Setats" + print "Detinu, a small communist island 30 by 70 miles long. Your" + print "job is to decide upon the contry's budget and distribute" + print "money to your countrymen from the communal treasury." + print "The money system is rallods, and each person needs 100" + print "rallods per year to survive. Your country's income comes" + print "from farm produce and tourists visiting your magnificent" + print "forests, hunting, fishing, etc. Half your land is farm land" + print "which also has an excellent mineral content and may be sold" + print "to foreign industry (strip mining) who import and support" + print "their own workers. Crops cost between 10 and 15 rallods per" + print "square mile to plant." + print "Your goal is to complete your " + yearsRequired + " year term of office." + print "Good luck!" +end if + +print + + +// Bonus little feature when running in Mini Micro: display a status bar with +// key game stats at the top of the screen. +updateStatusBar = function + if version.hostName != "Mini Micro" then return + display(2).mode = displayMode.text; td = display(2) + s = " Rallods: " + (rallods + " "*8)[:8] + s += " Land: " + (landArea + " "*6)[:6] + s += " Countrymen: " + (countrymen + " "*6)[:6] + s += " Foreigners: " + (foreignWorkers + " "*5)[:5] + td.color = color.black; td.backColor = text.color + td.row = 25; td.column = 0; td.print s +end function + +min0 = function(x) + if x < 0 then return 0 else return x +end function +floorMin0 = function(x) + return min0(floor(x)) +end function +stop = function + updateStatusBar + print; print; exit +end function + +while true + landPrice = floor(10*rnd+95) + plantingArea = 0 + pollutionControl = 0 + deaths = 0 + + print + print "You now have " + rallods + " rallods in the treasury." + print floor(countrymen) + " countrymen, ", "" + costToPlant = floor(((rnd/2)*10+10)) + if foreignWorkers != 0 then + print floor(foreignWorkers) + " foreign workers, ", "" + end if + print "and " + floor(landArea) + " sq. miles of land." + print "This year industry will buy land for " + landPrice, "" + print " rallods per square mile." + print "Land currently costs " + costToPlant + " rallods per square mile to plant." + print + + updateStatusBar + while true + sellToIndustry = input("How many square miles do you wish to sell to industry? ").val + if sellToIndustry < 0 then continue + if sellToIndustry <= landArea-1000 then break + print "*** Think again. You only have " + (landArea-1000) + " square miles of farm land." + if not sellableLandExplained then + print;print "(Foreign industry will only buy farm land because" + print "forest land is uneconomical to strip mine due to trees," + print "thicker top soil, etc.)" + sellableLandExplained = true + end if + end while + landArea = floor(landArea-sellToIndustry) + rallods = floor(rallods+(sellToIndustry*landPrice)) + + updateStatusBar + while true + welfare = input("How many rallods will you distribute among your countrymen? ").val + if welfare < 0 then continue + if welfare <= rallods then break + print " Think again. You've only " + rallods + " rallods in the treasury" + end while + rallods = floor(rallods-welfare) + + updateStatusBar + while rallods > 0 + plantingArea = input("How many square miles do you wish to plant? ").val + if plantingArea < 0 then continue + if plantingArea > countrymen*2 then + print " Sorry, but each countryman can only plant 2 sq. miles." + continue + end if + if plantingArea > landArea-1000 then + print " Sorry, but you've only " + (landArea-1000) + " sq. miles of farm land." + continue + end if + plantingCost = floor(plantingArea * costToPlant) + if plantingCost <= rallods then break + print " Think again. You've only " + rallods + " rallods left in the treasury." + end while + rallods -= plantingCost + + updateStatusBar + while rallods > 0 + pollutionControl = input("How many rallods do you wish to spend on pollution control? ").val + if pollutionControl < 0 then continue + if pollutionControl <= rallods then break + print " Think again. You only have " + rallods + " rallods remaining." + end while + + if sellToIndustry == 0 and welfare == 0 and plantingArea == 0 and pollutionControl == 0 then + print + print "Goodbye." + print "(If you wish to continue this game at a later date, answer" + print "'again' when asked if you want instructions at the start" + print "of the game.)" + exit + end if + + print + print + + rallods = floor(rallods-pollutionControl) + updateStatusBar + + original_rallods = rallods + + starvationDeaths = floorMin0(countrymen - welfare/100) + if starvationDeaths then + if welfare/100 < 50 then + print + print + print "Over one third of the popultation has died since you" + print "were elected to office. The people (remaining)" + print "hate your guts." + if rnd > .5 then + print "You have been thrown out of office and are now" + print "residing in prison." + else + print "You have been assassinated." + end if + countrymen -= starvationDeaths + stop + end if + print starvationDeaths + " countrymen died of starvation" + end if + + pollutionDeaths = floorMin0(rnd*(2000-landArea)) + if pollutionControl >= 25 then + pollutionDeaths = floor(pollutionDeaths/(pollutionControl/25)) + end if + if pollutionDeaths > 0 then + print pollutionDeaths + " countrymen died of carbon-monoxide and dust inhalation" + end if + + deaths = starvationDeaths + pollutionDeaths + if deaths then + print " You were forced to spend " + floor(deaths*9), "" + print " rallods on funeral expenses" + rallods -= deaths * 9 + end if + + if rallods < 0 then + print " Insufficient reserves to cover cost - land was sold" + landArea = floorMin0(landArea+(rallods/landPrice)) + rallods = 0 + end if + + countrymen = min0(countrymen - deaths) + + if sellToIndustry then + newForeigners = floor(sellToIndustry+(rnd*10)-(rnd*20)) + if foreignWorkers == 0 then newForeigners += 20 + foreignWorkers += newForeigners + print newForeigners + " workers came to the country and ", "" + end if + + immigration = floor(((welfare/100-countrymen)/10)+(pollutionControl/25)-((2000-landArea)/50)-(pollutionDeaths/2)) + print abs(immigration) + " countrymen ", "" + if immigration < 0 then print "came to", "" else print "left", "" + print " the island." + countrymen = floorMin0(countrymen + immigration) + + cropLoss = floor(((2000-landArea)*((rnd+1.5)/2))) + if cropLoss > plantingArea then cropLoss = plantingArea + if foreignWorkers > 0 then print "Of " + floor(plantingArea) + " sq. miles planted,", "" + print " you harvested " + floor(plantingArea-cropLoss) + " sq. miles of crops." + if cropLoss then + print " (Due to air and water pollution from foreign industry.)" + end if + agriculturalIncome = floor((plantingArea-cropLoss)*(landPrice/2)) + print "Making " + agriculturalIncome + " rallods." + rallods += agriculturalIncome + + v1 = floor(((countrymen-immigration)*22)+(rnd*500)) + v2 = floor((2000-landArea)*15) + prevTourism = tourism + tourism = abs(floor(v1-v2)) + print " You made " + tourism + " rallods from tourist trade." + if v2 > 2 and tourism < prevTourism then + print " Decrease because ", "" + g1 = 10*rnd + if g1 <= 2 then + print "fish population has dwindled due to water pollution." + else if g1 <= 4 then + print "air pollution is killing game bird population." + else if g1 <= 6 then + print "mineral baths are being ruined by water pollution." + else if g1 <= 8 then + print "unpleasant smog is discouraging sun bathers." + else + print "hotels are looking shabby due to smog grit." + end if + end if + rallods += tourism + updateStatusBar + + if deaths > 200 then + print + print + print deaths + "countrymen died in one year!!!!!" + print "due to this extreme mismanagement, you have not only" + print "been impeached and thrown out of office, but you" + m6 = floor(rnd*10) + if m6 <= 3 then 1670 + if m6 <= 6 then 1680 + if m6 <= 10 then 1690 + print "also had your left eye gouged out!" + goto 1590 + print "have also gained a very bad reputation." + goto 1590 + print "have also been declared national fink." + stop + else if countrymen < 343 then + print + print + print "Over one third of the popultation has died since you" + print "were elected to office. The people (remaining)" + print "hate your guts." + if rnd > .5 then + print "You have been thrown out of office and are now" + print "residing in prison." + else + print "You have been assassinated." + end if + stop + else if (original_rallods/100) > 5 and deaths - pollutionDeaths >= 2 then + print + print "Money was left over in the treasury which you did" + print "not spend. As a result, some of your countrymen died" + print "of starvation. The public is enraged and you have" + print "been forced to either resign or commit suicide." + print "The choice is yours." + print "If you choose the latter, please turn off your computer" + print "before proceeding." + stop + else if foreignWorkers > countrymen then + print + print + print "The number of foreign workers has exceeded the number" + print "of countrymen. As a minority, they have revolted and" + print "taken over the country." + if rnd > .5 then + print "You have been thrown out of office and are now" + print "residing in prison." + else + print "You have been assassinated." + end if + stop + else if year == yearsRequired-1 then + print + print + print "Congratulations!!!!!!!!!!!!!!!!!!" + print "You have succesfully completed your " + yearsRequired + " year term" + print "of office. You were, of course, extremely lucky, but" + print "nevertheless, it's quite an achievement. Goodbye and good" + print "luck - you'll probably need it if you're the type that" + print "plays this game." + stop + end if + + updateStatusBar + wait + year += 1 +end while + +//print +//print +//print "the number of foreign workers has exceeded the number" +//print "of countrymen. as a minority, they have revolted and" +//print "taken over the country." +//if rnd<=.5 then 1580 +//print "you have been thrown out of office and are now" +//print "residing in prison." +//goto 1590 +//print "you have been assassinated." +//print +//print +//exit +//print +//print +//print deaths + "countrymen died in one year!!!!!" +//print "due to this extreme mismanagement, you have not only" +//print "been impeached and thrown out of office, but you" +//m6 = floor(rnd*10) +//if m6 <= 3 then 1670 +//if m6 <= 6 then 1680 +//if m6 <= 10 then 1690 +//print "also had your left eye gouged out!" +//goto 1590 +//print "have also gained a very bad reputation." +//goto 1590 +//print "have also been declared national fink." +//goto 1590 +// +//print +//print +//print "over one third of the popultation has died since you" +//print "were elected to office. the people (remaining)" +//print "hate your guts." +//goto 1570 +//if deaths-pollutionDeaths < 2 then 1515 +//print +//print "money was left over in the treasury which you did" +//print "not spend. as a result, some of your countrymen died" +//print "of starvation. the public is enraged and you have" +//print "been forced to either resign or commit suicide." +//print "the choice is yours." +//print "if you choose the latter, please turn off your computer" +//print "before proceeding." +//goto 1590 +//print +//print +//print "congratulations!!!!!!!!!!!!!!!!!!" +//print "you have succesfully completed your" + yearsRequired + "year term" +//print "of office. you were, of course, extremely lucky, but" +//print "nevertheless, it's quite an achievement. goodbye and good" +//print "luck - you'll probably need it if you're the type that" +//print "plays this game." +//goto 1590 +// +//print "how many years had you been in office when interrupted"; +//input year +//if year < 0 then 1590 +//if year < 8 then 1969 +//print " come on, your term in office is only" + yearsRequired + "years." +//goto 1960 +//print "how much did you have in the treasury"; +//input rallods +//if rallods < 0 then 1590 +//print "how many countrymen"; +//input countrymen +//if countrymen < 0 then 1590 +//print "how many workers"; +//input foreignWorkers +//if foreignWorkers < 0 then 1590 +//print "how many square miles of land"; +//input landArea +//if landArea < 0 then 1590 +//if landArea > 2000 then 1996 +//if landArea > 1000 then 100 +//print " come on, you started with 1000 sq. miles of farm land" +//print " and 10,000 sq. miles of forest land." +//goto 1990 +// +//year = year+1 +//deaths = 0 +//goto 100 +//end diff --git a/00_Alternate_Languages/53_King/king_variable_update.bas b/00_Alternate_Languages/53_King/king_variable_update.bas index 7f88e1014..86c3828c3 100644 --- a/00_Alternate_Languages/53_King/king_variable_update.bas +++ b/00_Alternate_Languages/53_King/king_variable_update.bas @@ -8,7 +8,7 @@ 11 IF Z$="AGAIN" THEN 1960 12 PRINT:PRINT:PRINT 20 PRINT "CONGRATULATIONS! YOU'VE JUST BEEN ELECTED PREMIER OF SETATS" - 22 PRINT "DETINU, RALLODS SMALL COMMUNIST ISLAND 30 BY 70 MILES LONG. YOUR" + 22 PRINT "DETINU, A SMALL COMMUNIST ISLAND 30 BY 70 MILES LONG. YOUR" 24 PRINT "JOB IS TO DECIDE UPON THE CONTRY'S BUDGET AND DISTRIBUTE" 26 PRINT "MONEY TO YOUR COUNTRYMEN FROM THE COMMUNAL TREASURY." 28 PRINT "THE MONEY SYSTEM IS RALLODS, AND EACH PERSON NEEDS 100" @@ -219,7 +219,7 @@ REM I think tourism calculations are actually wrong in the original code! 1505 IF COUNTRYMEN<343 THEN 1700 1510 IF (ORIGINAL_RALLODS/100)>5 THEN 1800 1515 IF FOREIGN_WORKERS>COUNTRYMEN THEN 1550 -1520 IF YEARS_REQUIRED-1=X5 THEN 1900 +1520 IF YEARS_REQUIRED-1=YEAR THEN 1900 1545 GOTO 2000 1550 PRINT 1552 PRINT @@ -277,9 +277,9 @@ REM I think tourism calculations are actually wrong in the original code! 1950 GOTO 1590 1960 PRINT "HOW MANY YEARS HAD YOU BEEN IN OFFICE WHEN INTERRUPTED"; -1961 INPUT X5 -1962 IF X5<0 THEN 1590 -1963 IF X5<8 THEN 1969 +1961 INPUT YEAR +1962 IF YEAR<0 THEN 1590 +1963 IF YEAR<8 THEN 1969 1965 PRINT " COME ON, YOUR TERM IN OFFICE IS ONLY";YEARS_REQUIRED;"YEARS." 1967 GOTO 1960 1969 PRINT "HOW MUCH DID YOU HAVE IN THE TREASURY"; @@ -300,7 +300,7 @@ REM I think tourism calculations are actually wrong in the original code! 1997 PRINT " AND 10,000 SQ. MILES OF FOREST LAND." 1998 GOTO 1990 -2000 X5=X5+1 +2000 YEAR=YEAR+1 2020 DEATHS=0 2040 GOTO 100 2046 END diff --git a/00_Alternate_Languages/54_Letter/MiniScript/README.md b/00_Alternate_Languages/54_Letter/MiniScript/README.md new file mode 100644 index 000000000..dfc5cf386 --- /dev/null +++ b/00_Alternate_Languages/54_Letter/MiniScript/README.md @@ -0,0 +1,21 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript letter.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "letter" + run +``` +3. "Try-It!" page on the web: +Go to https://miniscript.org/tryit/, clear the default program from the source code editor, paste in the contents of letter.ms, and click the "Run Script" button. diff --git a/00_Alternate_Languages/54_Letter/MiniScript/letter.ms b/00_Alternate_Languages/54_Letter/MiniScript/letter.ms new file mode 100644 index 000000000..78a32b3bc --- /dev/null +++ b/00_Alternate_Languages/54_Letter/MiniScript/letter.ms @@ -0,0 +1,44 @@ +// Letter Guessing Game +// Ported to MiniScript by Joe Strout, 2023 + +print " "*33 + "LETTER" +print " "*15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" +print; print; print + +print "Letter Guessing Game"; print +print "I'll think of a letter of the alphabet, A to Z." +print "Try to guess my letter and I'll give you clues" +print "as to how close you're getting to my letter." + +// Function to play one round of the game +playOneGame = function + letter = char(65 + floor(rnd * 26)) + guesses = 0 + print; print "OK, I have a letter. Start guessing." + while true + print; guess = input("What is your guess? ")[:1].upper + guesses = guesses + 1 + if guess == letter then + print; print "You got it in " + guesses + " guesses!!" + if guesses > 5 then + print "But it shouldn't take more than 5 guesses!" + else + print "Good job !!!!!" + print char(7) * 15 + end if + return + else if guess < letter then + print "Too low. Try a higher letter." + else + print "Too high. Try a lower letter." + end if + end while +end function + +// main loop -- press Control-C to exit +while true + print + playOneGame + print + print "Let's play again....." +end while diff --git a/00_Alternate_Languages/55_Life/MiniScript/README.md b/00_Alternate_Languages/55_Life/MiniScript/README.md new file mode 100644 index 000000000..4cfd557b0 --- /dev/null +++ b/00_Alternate_Languages/55_Life/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript life.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "life" + run +``` diff --git a/00_Alternate_Languages/55_Life/MiniScript/life.ms b/00_Alternate_Languages/55_Life/MiniScript/life.ms new file mode 100644 index 000000000..69f3bafc1 --- /dev/null +++ b/00_Alternate_Languages/55_Life/MiniScript/life.ms @@ -0,0 +1,132 @@ +print " "*34 + "LIFE" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print +import "listUtil" // (needed for list.init2d) + +maxx = 66 // (size adjusted for Mini Micro display) +maxy = 23 +A = list.init2d(maxx+1, maxy+1, 0) + +// Stuff the given pattern into the center of the cell array. +// Return the number of live cells. +stuffIntoCenter = function(pattern) + maxLen = 0 + for p in pattern + if p.len > maxLen then maxLen = p.len + end for + + population = 0 + y = floor(maxy/2 - pattern.len/2) + for row in pattern + x = floor(maxx/2 - maxLen/2) + for c in row + if c != " " then + A[x][y] = 1 + population += 1 + end if + x += 1 + end for + y += 1 + end for + return population +end function + +// Get the initial pattern from the user +initToUserPattern = function + print "Enter your pattern (enter DONE when done):" + userPattern = [] + while true + p = input("?") + if p.upper == "DONE" then break + if p and p[0] == "." then p = " " + p[1:] + userPattern.push p + end while + return stuffIntoCenter(userPattern) +end function + +// For testing purposes, skip asking the user and just use a hard-coded pattern. +initToStandardPattern = function + pattern = [ + " **", + " * *", + " *"] + return stuffIntoCenter(pattern) +end function + +// Or, just for fun, initialize to a random pattern of junk in the center. +initRandom = function + for x in range(ceil(maxx*0.3), floor(maxx*0.7)) + for y in range(ceil(maxy*0.3), floor(maxy*0.7)) + A[x][y] = rnd > 0.5 + end for + end for +end function + +// Define a function to draw the current game state. +// This also changes 2 (dying) to 0 (dead), and 3 (being born) to 1 (alive). +drawGameState = function(generation=0, population=0, invalid=false) + if version.hostName == "Mini Micro" then text.row = 26 else print + print "Generation: " + generation + " Population: " + population + + " " + "INVALID!" * invalid + for y in range(0, maxy) + s = "" + for x in range(0, maxx) + if A[x][y] == 2 then + A[x][y] = 0 + else if A[x][y] == 3 then + A[x][y] = 1 + end if + if A[x][y] then s += "*" else s += " " + end for + print s + end for +end function + +// Update the game state, setting cells that should be born to 3 and +// cells that should die to 2. Return the number of cells that will +// be alive after this update. Also, set globals.invalid if any live +// cells are found on the edge of the map. +updateGameState = function + population = 0 + for x in range(0, maxx) + for y in range(0, maxy) + c = A[x][y] == 1 or A[x][y] == 2 // center state + n = -c // number of neighbors + for nx in range(x-1, x+1) + if nx < 0 or nx > maxx then continue + for ny in range(y-1, y+1) + if ny < 0 or ny > maxy then continue + n += A[nx][ny] == 1 or A[nx][ny] == 2 + end for + end for + if c and n != 2 and n != 3 then // live cell with < 2 or > 3 neighbors... + A[x][y] = 2 // dies + else if not c and n == 3 then // dead cell with 3 neighbors... + A[x][y] = 3 // comes to life + if x == 0 or x == maxx or y == 0 or y == maxy then + globals.invalid = true + end if + end if + population += (A[x][y] == 1 or A[x][y] == 3) + end for + end for + return population +end function + + +// Initialize the game state (uncomment one of the following three lines) +population = initToUserPattern +//population = initToStandardPattern +//population = initRandom + +// Main loop +if version.hostName == "Mini Micro" then clear +invalid = false +generation = 0 +while population > 0 + drawGameState generation, population, invalid + population = updateGameState + generation += 1 + //key.get // <-- Uncomment this to single-step with each keypress! +end while +drawGameState generation, population, invalid diff --git a/00_Alternate_Languages/56_Life_for_Two/MiniScript/README.md b/00_Alternate_Languages/56_Life_for_Two/MiniScript/README.md new file mode 100644 index 000000000..93b25b999 --- /dev/null +++ b/00_Alternate_Languages/56_Life_for_Two/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). The only liberty I took with the original design is that, when prompting each player for their turn, I include a reminder of what symbol (* or #) represents their pieces on the board. + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript lifefortwo.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "lifefortwo" + run +``` diff --git a/00_Alternate_Languages/56_Life_for_Two/MiniScript/lifefortwo.ms b/00_Alternate_Languages/56_Life_for_Two/MiniScript/lifefortwo.ms new file mode 100644 index 000000000..49ac531ed --- /dev/null +++ b/00_Alternate_Languages/56_Life_for_Two/MiniScript/lifefortwo.ms @@ -0,0 +1,129 @@ +print " "*33 + "LIFE2" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print +print " "*10 + "U.B. LIFE GAME" + +// N: counts neighbors and game state, as follows: +// 1's digit: player 1 neighbors +// 10's digit: player 2 neighbors +// 100's digit: player 1 current live cell +// 1000's digit: player 2 current live cell +N = [] +for i in range(0,6); N.push [0]*7; end for + +// K: encodes the rule for what cells come to life, based on +// the value in N. The first 9 entries mean new life for Player 1; +// the second 9 entries mean new life for Player 2. +K = [3,102,103,120,130,121,112,111,12, + 21,30,1020,1030,1011,1021,1003,1002,1012] + +// population: how many live cells each player (1-2) has +population = [null, 0, 0] + +// Function to get input coordinates from the player, for any empty space. +// Where possible, hide the input so the other player can't see it. +getCoords = function + while true + print "X,Y" + inp = input.replace(",", " ").replace(" "," ") + if version.hostName == "Mini Micro" then + text.row = text.row + 1; print " "*60 + end if + parts = inp.split + if parts.len == 2 then + x = parts[0].val + y = parts[1].val + if 0 < x <= 5 and 0 < y <= 5 and N[x][y] == 0 then break + end if + print "Illegal coords. Retype" + end while + return [x, y] +end function + +// Function to print the board. At the same time, it replaces +// any player 1 value (as judged by list K) with 100, and any +// player 2 value with 1000. Also update population[] with the +// number of pieces of each player. +printBoard = function + population[1] = 0 + population[2] = 0 + for y in range(0, 6) + if y == 0 or y == 6 then + print " 0 1 2 3 4 5 0" + else + print " " + y, " " + for x in range(1, 5) + kIndex = K.indexOf(N[x][y]) + if kIndex == null then + print " ", " " + N[x][y] = 0 + else if kIndex < 9 then + print "*", " " + N[x][y] = 100 + population[1] += 1 + else + print "#", " " + N[x][y] = 1000 + population[2] += 1 + end if + end for + print y + end if + end for + print +end function + +// Function to update the board (N). +updateBoard = function + for j in range(1,5) + for k in range(1,5) + if N[j][k] < 100 then continue // not a live cell + if N[j][k] > 999 then value = 10 else value = 1 + for x in range(j-1, j+1) + for y in range(k-1, k+1) + if x == j and y == k then continue + N[x][y] += value + //if [x,y] == [2,1] then print "adding " + value + " from " + j+","+k + " to " + x+","+y + ", --> " + N[x][y] + end for + end for + end for + end for +end function + + +// Get initial player positions. +for player in [1,2] + print; print "Player " + player + " - 3 live pieces." + if player == 2 then value = 30 else value = 3 + for k in range(1,3) + pos = getCoords + N[pos[0]][pos[1]] = value + end for +end for + +printBoard +while true + updateBoard + printBoard + if population[1] == 0 and population[2] == 0 then + print "A DRAW" + break + else if population[1] == 0 then + print "PLAYER 2 IS THE WINNER" + break + else if population[2] == 0 then + print "PLAYER 1 IS THE WINNER" + break + end if + + print; print "Player 1 (*)" + p1pos = getCoords + print; print "Player 2 (#)" + p2pos = getCoords + if p1pos == p2pos then + print "Same coord. Set to 0" + else + N[p1pos[0]][p1pos[1]] = 100 + N[p2pos[0]][p2pos[1]] = 1000 + end if +end while \ No newline at end of file diff --git a/00_Alternate_Languages/57_Literature_Quiz/MiniScript/README.md b/00_Alternate_Languages/57_Literature_Quiz/MiniScript/README.md new file mode 100644 index 000000000..e3f12eb47 --- /dev/null +++ b/00_Alternate_Languages/57_Literature_Quiz/MiniScript/README.md @@ -0,0 +1,22 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +0. Try-It! Page: +Go to https://miniscript.org/tryit/, clear the sample code from the code editor, and paste in the contents of litquiz.ms. Then click the "Run Script" button. Program output (and input) will appear in the green-on-black terminal display to the right of or below the code editor. + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript litquiz.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "litquiz" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/57_Literature_Quiz/MiniScript/litquiz.ms b/00_Alternate_Languages/57_Literature_Quiz/MiniScript/litquiz.ms new file mode 100644 index 000000000..e465cc6bb --- /dev/null +++ b/00_Alternate_Languages/57_Literature_Quiz/MiniScript/litquiz.ms @@ -0,0 +1,66 @@ +print " "*24 + "Literature Quiz" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print + +r=0 +print "Test your knowledge of children's literature." +print; print "This is a multiple-choice quiz." +print "Type a 1, 2, 3, or 4 after the question mark." +print; print "Good luck!"; + +print; print +print "In pinocchio, what was the name of the cat?" +print "1)Tigger, 2)Cicero, 3)Figaro, 4)Fuipetto"; +a = input("?").val +if a!=3 then + print "Sorry...Figaro was his name." +else + print "Very good! Here's another." + r += 1 +end if + +print; print +print "From whose garden did Bugs Bunny steal the carrots?" +print "1)Mr. Nixon's, 2)Elmer Fudd's, 3)Clem Judd's, 4)Stromboli's"; +a = input("?").val +if a != 2 then + print "Too bad...it was elmer fudd's garden." +else + print "Pretty good!" + r += 1 +end if + +print; print +print "In the Wizard of Oz, Dorothy's dog was named" +print "1)Cicero, 2)Trixia, 3)King, 4)Toto"; +a = input("?").val +if a != 4 then + print "Back to the books,...Toto was his name." +else + print "Yea! You're a real literature giant." + r += 1 +end if + +print;print +print "Who was the fair maiden who ate the poison apple?" +print "1)Sleeping Beauty, 2)Cinderella, 3)Snow White, 4)Wendy"; +a = input("?").val +if a != 3 then + print "Oh, come on now...it was Snow White." +else + print "Good memory!" + r += 1 +end if + +print;print +if r == 4 then + print "Wow! That's super! You really know your nursery" + print "Your next quiz will be on 2nd century Chinese" + print "literature (ha, ha, ha)" +else if r<2 then + print "Ugh. That was definitely not too swift. Back to" + print "nursery school for you, my friend." +else + print "Not bad, but you might spend a little more time" + print "reading the nursery greats." +end if diff --git a/00_Alternate_Languages/58_Love/MiniScript/README.md b/00_Alternate_Languages/58_Love/MiniScript/README.md new file mode 100644 index 000000000..1186963df --- /dev/null +++ b/00_Alternate_Languages/58_Love/MiniScript/README.md @@ -0,0 +1,22 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +0. Try-It! Page: +Go to https://miniscript.org/tryit/, clear the sample code from the code editor, and paste in the contents of love.ms. Then click the "Run Script" button. Program output (and input) will appear in the green-on-black terminal display to the right of or below the code editor. + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript love.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "love" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/58_Love/MiniScript/love.ms b/00_Alternate_Languages/58_Love/MiniScript/love.ms new file mode 100644 index 000000000..64693f127 --- /dev/null +++ b/00_Alternate_Languages/58_Love/MiniScript/love.ms @@ -0,0 +1,41 @@ +print " "*33 + "LOVE" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print + +print "A tribute to the great american artist, Robert Indiana." +print "His greatest work will be reproduced with a message of" +print "your choice up to 60 characters. If you can't think of" +print "a message, simple type the word 'LOVE'"; print +msg = input("Your message, please? ") +for i in range(1, 10); print; end for + +repeatedMsg = msg * ceil(60 / msg.len) + +data = [] +data += [60,1,12,26,9,12,3,8,24,17,8,4,6,23,21,6,4,6,22,12,5,6,5] +data += [4,6,21,11,8,6,4,4,6,21,10,10,5,4,4,6,21,9,11,5,4] +data += [4,6,21,8,11,6,4,4,6,21,7,11,7,4,4,6,21,6,11,8,4] +data += [4,6,19,1,1,5,11,9,4,4,6,19,1,1,5,10,10,4,4,6,18,2,1,6,8,11,4] +data += [4,6,17,3,1,7,5,13,4,4,6,15,5,2,23,5,1,29,5,17,8] +data += [1,29,9,9,12,1,13,5,40,1,1,13,5,40,1,4,6,13,3,10,6,12,5,1] +data += [5,6,11,3,11,6,14,3,1,5,6,11,3,11,6,15,2,1] +data += [6,6,9,3,12,6,16,1,1,6,6,9,3,12,6,7,1,10] +data += [7,6,7,3,13,6,6,2,10,7,6,7,3,13,14,10,8,6,5,3,14,6,6,2,10] +data += [8,6,5,3,14,6,7,1,10,9,6,3,3,15,6,16,1,1] +data += [9,6,3,3,15,6,15,2,1,10,6,1,3,16,6,14,3,1,10,10,16,6,12,5,1] +data += [11,8,13,27,1,11,8,13,27,1,60] + +for row in range(0, 35) + s = [] + a1 = 0; p = true + while a1 < 60 + a = data.pull + a1 += a + for i in range(a1-a, a1-1) + s.push repeatedMsg[i] * p + " " * (not p) + end for + p = not p + end while + print s.join("") + wait 0.1 // OPTIONAL; slows printing down so you can see it all +end for diff --git a/00_Alternate_Languages/59_Lunar_LEM_Rocket/MiniScript/README.md b/00_Alternate_Languages/59_Lunar_LEM_Rocket/MiniScript/README.md new file mode 100644 index 000000000..6ba18136c --- /dev/null +++ b/00_Alternate_Languages/59_Lunar_LEM_Rocket/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). Note that there are three different programs in this folder, all variations on the "land the LEM on the Moon" idea. + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the desired program with a command such as: + +``` + miniscript lem.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "lem" // (or "lunar" or "rocket") + run +``` diff --git a/00_Alternate_Languages/59_Lunar_LEM_Rocket/MiniScript/lem.ms b/00_Alternate_Languages/59_Lunar_LEM_Rocket/MiniScript/lem.ms new file mode 100644 index 000000000..92a263e26 --- /dev/null +++ b/00_Alternate_Languages/59_Lunar_LEM_Rocket/MiniScript/lem.ms @@ -0,0 +1,324 @@ +print " "*34 + "LEM" +print " "*15 + "Creative Computing Morristown, New Jersey" +// rockt2 is an interactive game that simulates a lunar +// landing is similar to that of the apollo program. +// There is absolutely no chance involved + + +printIntro = function + print + print " You are on a lunar landing mission. as the pilot of" + print "the lunar excursion module, you will be expected to" + print "give certain commands to the module navigation system." + print "The on-board computer will give a running account" + print "of information needed to navigate the ship." + print + input "(Press Return.)" + print + print "The attitude angle called for is described as follows." + print "+ or -180 degrees is directly away from the moon" + print "-90 degrees is on a tangent in the direction of orbit" + print "+90 degrees is on a tangent from the direction of orbit" + print "0 (zero) degrees is directly toward the moon" + print + print " "*30 + "-180|+180" + print " "*34 + "^" + print " "*27 + "-90 < -+- > +90" + print " "*34 + "!" + print " "*34 + "0" + print " "*21 + "<<<< direction of orbit <<<<" + print + print " "*20 + "------ surface of moon ------" + print + input + print + print "All angles between -180 and +180 degrees are accepted." + print + print "1 fuel unit = 1 sec. at max thrust" + print "Any discrepancies are accounted for in the use of fuel" + print "for an attitude change." + print "Available engine power: 0 (zero) and any value between" + print "10 and 100 percent." + print + print "Negative thrust or time is prohibited." + print + input +end function + +printInOutInfo = function(withExample = true) + print + print "Input: time interval in seconds ------ (T)" + print " percentage of thrust ---------- (P)" + print " attitude angle in degrees ----- (A)" + print + if withExample then + print "For example:" + print "T,P,A? 10,65,-60" + print "To abort the mission at any time, enter 0,0,0" + print + end if + print "Output: total time in elapsed seconds" + print " height in " + ms + print " distance from landing site in " + ms + print " vertical velocity in " + ms + "/second" + print " horizontal velocity in " + ms + "/second" + print " fuel units remaining" + print +end function + +initState = function + globals.m = 17.95 + globals.f1 = 5.25 + globals.n = 7.5 + globals.r0 = 926 + globals.v0 = 1.29 + globals.t = 0 + globals.h0 = 60 + globals.r = r0+h0 + globals.a = -3.425 + globals.r1 = 0 + globals.a1 = 8.84361e-04 + globals.r3 = 0 + globals.a3 = 0 + globals.m1 = 7.45 + globals.m0 = m1 + globals.b = 750 + globals.t1 = 0 + globals.f = 0 + globals.p = 0 + globals.n = 1 + globals.m2 = 0 + globals.s = 0 + globals.c = 0 +end function + +getUnits = function(moreHelp=true) + print + while true + print "Input measurement option number? ", "" + if moreHelp then + print + print "Which system of measurement do you prefer?" + print " 1 = metric 0 = english" + print "Enter the appropriate number? ", "" + end if + k = input.val + if k == 0 then + globals.z = 6080 + globals.ms = "feet" + globals.g3 = .592 + globals.ns = "n.miles" + globals.g5 = z + break + else if k == 1 then + globals.z = 1852.8 + globals.ms="meters" + globals.g3 = 3.6 + globals.ns=" kilometers" + globals.g5 = 1000 + break + end if + moreHelp = true + end while +end function + +startFirstGame = function + initState + print + print "Lunar Landing Simulation" + print + print "Have you flown an Apollo/LEM mission before", "" + while true + qs = input(" (yes or no)? ").lower + if qs and (qs[0] == "y" or qs[0] == "n") then break + print "Just answer the question, please, ", "" + end while + getUnits (qs[0] == "n") + if qs[0] == "n" then printIntro + printInOutInfo +end function + +startSubsequentGame = function + initState + print + print "OK, do you want the complete instructions or the input -" + print "output statements?" + while true + print "1 = complete instructions" + print "2 = input-output statements" + print "3 = neither" + b1 = input.val + if 1 <= b1 <= 3 then break + end while + if b1 == 1 then printIntro + if b1 < 3 then printInOutInfo +end function + +getTurnInputs = function + while true + print + inp = input("T,P,A? ").replace(",", " ").replace(" ", " ").split + if inp.len != 3 then continue + globals.t1 = inp[0].val // NOTE: though we prompt for T, P, A, + globals.f = inp[1].val // internally these are t1, f, and p respectively. + globals.p = inp[2].val + globals.f = f/100 + if t1 < 0 then + print + print "This spacecraft is not able to violate the space-"; + print "time continuum." + continue + else if t1 == 0 then + return // abort mission + end if + if f < 0 or f > 1.05 or abs(f-.05) < .05 then + print + print "Impossible thrust value: ", "" + if f < 0 then + print "negative" + else if f < 0.5 then + print "too small" + else + print "too large" + end if + continue + end if + if abs(p) > 180 then + print + print "If you want to spin around, go outside the module" + print "for an E.V.A." + continue + end if + return + end while +end function + +pad = function(num, width=10) + anum = abs(num) + if anum >= 10000 then + s = round(num) + else if anum >= 10000 then + s = round(num, 1) + else if anum > 100 then + s = round(num, 2) + else + s = round(num, 3) + end if + return (s + " " * width)[:width] +end function + +integrate = function + n = 20 + if t1 >= 400 then n = t1/20 + globals.t1 = t1/n + globals.p = p*3.14159/180 + s = sin(p) + c = cos(p) + globals.m2 = m0*t1*f/b + globals.r3 = -.5*r0*((v0/r)^2)+r*a1*a1 + globals.a3 = -2*r1*a1/r + + for i in range(1, n) + if m1 != 0 then + globals.m1 = m1-m2 + if m1<=0 then + globals.f = f*(1+m1/m2) + globals.m2 = m1+m2 + print "You are out of fuel." + globals.m1 = 0 + end if + else + globals.f = 0 + globals.m2 = 0 + end if + globals.m = m-.5*m2 + globals.r4 = r3 + globals.r3 = -.5*r0*((v0/r)^2)+r*a1*a1 + globals.r2 = (3*r3-r4)/2+.00526*f1*f*c/m + globals.a4 = a3 + globals.a3 = -2*r1*a1/r + globals.a2 = (3*a3-a4)/2+.0056*f1*f*s/(m*r) + globals.x = r1*t1+.5*r2*t1*t1 + globals.r = r+x + globals.h0 = h0+x + globals.r1 = r1+r2*t1 + globals.a = a+a1*t1+.5*a2*t1*t1 + globals.a1 = a1+a2*t1 + globals.m = m-.5*m2 + globals.t = t+t1 + if h0<3.287828e-04 then break + end for + + globals.h = h0*z + globals.h1 = r1*z + globals.d = r0*a*z + globals.d1 = r*a1*z + globals.t2 = m1*b/m0 + + print " " + [pad(t, 10), pad(h, 10), pad(d, 10), pad(h1, 10), pad(d1, 10), pad(t2, 10)].join +end function + +// Do one turn of the game. Return true if game still in progress, +// or false when game is over (aborted, crashed, or landed). +doOneTurn = function + if m1 == 0 then + // out of fuel! + globals.t1 = 20 + globals.f = 0 + globals.p = 0 + else + getTurnInputs + end if + if t1 == 0 then + print "Mission abended" + return false + end if + integrate + + if h0 < 3.287828e-04 then + if r1 < -8.21957e-04 or abs(r*a1) > 4.93174e-04 or h0 < -3.287828e-04 then + print + print "Crash !!!!!!!!!!!!!!!!" + print "Your impact created a crater " + abs(h) + " " + ms + " deep." + x1 = sqr(d1*d1+h1*h1)*g3 + print "At contact you were traveling " + x1 + " " + ns + "/hr" + else if abs(d)>10*z then + print "You are down safely - " + print + print "But missed the landing site by" + abs(d/g5) + " " + ns + "." + else + print + print "Tranquility Base here -- the Eagle has landed." + print "Congratulations -- there was no spacecraft damage." + print "You may now proceed with surface exploration." + end if + return false + end if + + if r0*a>164.474 then + print + print "You have been lost in space with no hope of recovery." + return false + end if + return true +end function + +startFirstGame +while true + t1 = 0; f = 0; p = 0 + integrate + while doOneTurn + end while + print + while true + qs = input("Do you want to try it again (yes/no)? ").lower + if qs and (qs[0] == "y" or qs[0] == "n") then break + end while + if qs[0] == "n" then + print + print "Too bad, the space program hates to lose experienced" + print "astronauts." + break + end if + startSubsequentGame +end while diff --git a/00_Alternate_Languages/59_Lunar_LEM_Rocket/MiniScript/lunar.ms b/00_Alternate_Languages/59_Lunar_LEM_Rocket/MiniScript/lunar.ms new file mode 100644 index 000000000..e8da8d72c --- /dev/null +++ b/00_Alternate_Languages/59_Lunar_LEM_Rocket/MiniScript/lunar.ms @@ -0,0 +1,114 @@ +print " "*33 + "Lunar" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print +print "This is a computer simulation of an Apollo lunar" +print "landing capsule."; print; print +print "The on-board computer has failed (it was made by" +print "Xerox) so you have to land the capsule manually." + +printCols = function(fields, delimiter=null) + if delimiter == null then delimiter = text.delimiter + line = "" + for s in fields + line += (s + " "*12)[:12] + end for + print line, delimiter +end function + +doOneGame = function + print; print "Set burn rate of retro rockets to any value between" + print "0 (free fall) and 200 (maximum burn) pounds per second." + print "Set new burn rate every 10 seconds."; print + print "Capsule weight 32,500 lbs; fuel weight 16,500 lbs." + print; print; print; print "Good luck" + l = 0 + + print + printCols ["sec","mi + ft","mph","lb fuel","burn rate"] + print + a=120; v = 1; m=33000; n=16500; g=1e-03; z = 1.8 + + formulaSet1 = function // (subroutine 330) + outer.l += s + outer.t -= s + outer.m -= s * k + outer.a = i + outer.v = j + end function + + formulaSet2 = function // (subroutine 420) + outer.q = s * k / m + outer.j = v + g*s + z*(-q-q*q/2-q^3/3-q^4/4-q^5/5) + outer.i = a - g*s*s/2 - v*s+z*s*(q/2+q^2/6+q^3/12+q^4/20+q^5/30) + end function + + formulaSet3 = function // (loop 340-360) + while s >= 5e-3 + outer.d = v + sqrt(v * v + 2 * a * (g - z * k / m)) + outer.s = 2 * a / d + formulaSet2 + formulaSet1 + end while + end function + + while true + printCols [l, floor(a) + " " + floor(5280*(a-floor(a))), 3600*v, m-n], "" + k = input.val + t=10 + shouldExit = false + + while true + if m-n < 1e-03 then break + if t < 1e-03 then break + s = t; if m < n+s*k then s = (m-n)/k + formulaSet2 + if i <= 0 then + formulaSet3 + shouldExit = true + break + end if + if v > 0 and j < 0 then + while v > 0 and j <= 0 + w = (1 - m*g/(z*k))/2 + s = m*v / (z*k*(w + sqrt(w*w + v/z))) + 0.05 + formulaSet2 + if i <= 0 then + formulaSet3 + shouldExit = true + break + end if + formulaSet1 + end while + if shouldExit then break + continue + end if + formulaSet1 + end while + if shouldExit then break + if m-n < 1e-03 then + print "Fuel out at " + round(l) + " seconds" + s = (-v+sqrt(v*v+2*a*g))/g + v = v+g*s; l = l+s + break + end if + end while + + w = 3600*v + print "On moon at " + l + " seconds - impact velocity " + round(w,1) + " mph" + if w <= 1.2 then + print "Perfect landing!" + else if w <= 10 then + print "Good landing (could be better)" + else if w <= 60 then + print "Craft damage... you're stranded here until a rescue" + print "party arrives. Hope you have enough oxygen!" + else + print "Sorry there were no survivors. You blew it!" + print "In fact, you blasted a new lunar crater " + round(w*.227,1) + " feet deep!" + end if +end function + +while true + doOneGame + print; print; print; print "Try again??" +end while diff --git a/00_Alternate_Languages/59_Lunar_LEM_Rocket/MiniScript/rocket.ms b/00_Alternate_Languages/59_Lunar_LEM_Rocket/MiniScript/rocket.ms new file mode 100644 index 000000000..4131290b5 --- /dev/null +++ b/00_Alternate_Languages/59_Lunar_LEM_Rocket/MiniScript/rocket.ms @@ -0,0 +1,102 @@ +if version.hostName == "Mini Micro" then clear +print " "*30 + "Rocket" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print +print "Lunar Landing Simulation" +print "----- ------- ----------"; print +yn = input("Do you want instructions (yes or no)? ").lower +if not yn or yn[0] != "n" then + print + print "You are landing on the moon and and have taken over manual" + print "control 1000 feet above a good landing spot. You have a down-" + print "ward velocity of 50 feet/sec. 150 units of fuel remain." + print + print "Here are the rules that govern your Apollo space-craft" + print "(Press Return after each one):"; print + print "(1) After each second the height, velocity, and remaining fuel" + print " will be reported via Digby your on-board computer." + input + print "(2) After the report a '?' will appear. Enter the number" + print " of units of fuel you wish to burn during the next" + print " second. Each unit of fuel will slow your descent by" + print " 1 foot/sec." + input + print "(3) The maximum thrust of your engine is 30 feet/sec/sec" + print " or 30 units of fuel per second." + input + print "(4) When you contact the lunar surface, your descent engine" + print " will automatically shut down and you will be given a" + print " report of your landing speed and remaining fuel." + input + print "(5) If you run out of fuel the '?' will no longer appear" + print " but your second by second report will continue until" + print " you contact the lunar surface."; print + input +end if + +pad = function(s, width=10) + return (s + " "*width)[:width] +end function + +// Bonus little feature when running in Mini Micro: display a header bar +// always at the top of the screen. +drawHeaders = function + if version.hostName != "Mini Micro" then return + display(2).mode = displayMode.text; td = display(2) + td.color = text.color; td.backColor = color.black + td.row = 25; td.column = 0 + td.print "sec feet speed fuel plot of distance" + " "*21 +end function + +while true + print "Beginning landing procedure.........."; print + print "G O O D L U C K ! ! !" + print; print + print "sec feet speed fuel plot of distance" + drawHeaders + print + t=0; h=1000; v=50; fuel=150 + while true + print pad(t,5) + pad(h,7) + pad(v, 10) + pad(fuel,8) + "|" + " "*floor(h/30) + "*" + if fuel <= 0 then + burn = 0 + wait 0.5 // (slight pause for drama and legibility) + else + burn = input("?").val + if burn < 0 then burn = 0 + if burn > 30 then burn=30 + if burn > fuel then + burn = fuel + print "**** out of fuel ****" + end if + end if + v1 = v - burn + 5 + fuel -= burn + h -= .5*(v+v1) + if h <= 0 then break + t += 1 + v = v1 + end while + print "***** CONTACT *****" + h = h+ .5*(v1+v) + if burn == 5 then + d = h/v + else + d = (-v+sqrt(v*v+h*(10-2*burn)))/(5-burn) + end if + v1 = v + (5-burn)*d + print "Touchdown at " + (t+d) + " seconds." + print "Landing velocity = " + round(v1,1) + " feet/sec." + print fuel + " units of fuel remaining." + if v1 == 0 then + print "Congratulations! a perfect landing!!" + print "Your license will be renewed.......later." + else if abs(v1) >= 2 then + print "***** Sorry, but you blew it!!!!" + print "Appropriate condolences will be sent to your next of kin." + end if + print; print; print + yn = input("Another mission? ").lower + if not yn or yn[0] != "y" then break +end while +print; print "Control out."; print diff --git a/00_Alternate_Languages/60_Mastermind/MiniScript/README.md b/00_Alternate_Languages/60_Mastermind/MiniScript/README.md new file mode 100644 index 000000000..088a33011 --- /dev/null +++ b/00_Alternate_Languages/60_Mastermind/MiniScript/README.md @@ -0,0 +1,17 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: +``` + miniscript mastermind.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: +``` + load "mastermind" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/60_Mastermind/MiniScript/mastermind.ms b/00_Alternate_Languages/60_Mastermind/MiniScript/mastermind.ms new file mode 100644 index 000000000..93ea30820 --- /dev/null +++ b/00_Alternate_Languages/60_Mastermind/MiniScript/mastermind.ms @@ -0,0 +1,260 @@ +// +// MASTERMIND II +// STEVE NORTH +// CREATIVE COMPUTING +// PO BOX 789-M MORRISTOWN NEW JERSEY 07960 +// +// Converted to MiniScript by Joe Strout (Sep 2023) + +// Advance the given combination to the next possible combination. +// Note that (unlike the BASIC program) our values are 0-based, not 1-based. +incrementCombo = function(combo) + idx = 0 + while true + combo[idx] += 1 + if combo[idx] < colorCount then return + combo[idx] = 0 + idx += 1 + end while +end function + +// Convert a numeric combination like [2, 0, 1] into a color string like "RBW". +comboToColorString = function(combo) + result = "" + for q in combo + result += colorCodes[q] + end for + return result +end function + +// Get a random color code like "RBW". +getRandomCode = function + // We do this by starting with a numeric combo of all 0's (first color), + // and then stepping through subsequent combos a random number of times. + combo = [0] * posCount + steps = floor(possibilities * rnd) + while steps + incrementCombo combo + steps -= 1 + end while + return comboToColorString(combo) +end function + +// Return [blackPins, whitePins] for the given guess and actual code. +// blackPins is how many guess entries are the correct color AND position; +// whitePins is how many guess entries have the right color, but wrong position. +// This works with either color strings or numeric combos. +calcResult = function(guess, actual) + if guess isa string then guess = guess.split("") else guess = guess[:] + if actual isa string then actual = actual.split("") else actual = actual[:] + black = 0; white = 0 + for i in guess.indexes + if guess[i] == actual[i] then + black += 1 + actual[i] = null + else + for j in actual.indexes + if guess[i] == actual[j] and guess[j] != actual[j] then + white += 1 + actual[j] = null + break + end if + end for + end if + guess[i] = null + end for + return [black, white] +end function + +// Pad a string with spaces, to the given width. +pad = function(s, width=12) + return (s + " "*width)[:width] +end function + +// Print the history of guesses and results. +printBoard = function + print + print "BOARD" + print "Move Guess Black White" + for i in guessHistory.indexes + print pad(i+1, 9) + pad(guessHistory[i], 15) + + pad(guessResult[i][0], 10) + guessResult[i][1] + end for + print +end function + +// Quit the game (after reporting the computer's secret code). +quit = function(computerCode) + print "Quitter! My combination was: " + computerCode + print + print "Good bye" + exit +end function + +// Prompt the user for a guess (e.g. "RBW"). +// Also handle "BOARD" and "QUIT" commands. +inputGuess = function(guessNum, secretCode) + while true + guess = input("Move #" + guessNum + " Guess? ").upper + if guess == "BOARD" then + printBoard + else if guess == "QUIT" then + quit secretCode + else if guess.len != posCount then + print "Bad number of positions." + else + ok = true + for c in guess + if colorCodes.indexOf(c) == null then + print "'" + c + "' is unrecognized." + ok = false + break + end if + end for + if ok then return guess + end if + end while +end function + +// Play one half-round where the computer picks a code, +// and the human tries to guess it. +doHumanGuesses = function + print "Guess my combination." + print + secretCode = getRandomCode + //print "My secret combo is: " + secretCode // (for debugging purposes) + + globals.guessHistory = [] // list of guesses, e.g. "RBW" + globals.guessResult = [] // result of each guess, as [BlackPins, WhitePins] + + for guessNum in range(1, 10) + guess = inputGuess(guessNum, secretCode) + result = calcResult(guess, secretCode) + if result[0] == posCount then + print "You guessed it in " + guessNum + " moves!" + break + end if + // Tell human results + print "You have " + result[0] + " blacks and " + result[1] + " whites." + // Save all this stuff for board printout later + guessHistory.push guess + guessResult.push result + end for + if guess != secretCode then + print "You ran out of moves! That's all you get!" + print "The actual combination was: " + secretCode + end if + globals.humanScore += guessNum +end function + +// Play one half-round where the human picks a code, +// and the computer tries to guess it. +// Return true if this goes OK, and false if we need a do-over. +doComputerGuesses = function + print "Now I guess. Think of a combination." + input "Hit Return when ready:" + + // Make a list of possible combination *numbers*, from 0 to possibilities-1. + // We'll remove entries from this list as we eliminate them with our guesses. + possible = range(0, possibilities-1) + + gotIt = false + for guessNum in range(1, 10) + if not possible then + print "You have given me inconsistent information." + print "Try again, and this time please be more careful." + return false + end if + guessIdx = possible[rnd * possible.len] + guessCombo = [0] * posCount + if guessIdx > 0 then + for x in range(0, guessIdx-1) + incrementCombo guessCombo + end for + end if + + print "My guess is: " + comboToColorString(guessCombo) + + while true + s = input(" Blacks, Whites? ").replace(",", " ").replace(" ", " ").split + if s.len == 2 then break + end while + actualResult = [s[0].val, s[1].val] + + if actualResult[0] == posCount then + print "I got it in " + guessNum + " moves!" + gotIt = true + break + end if + + // Now zip through all possibilities, and if it's still in our + // possible list but doesn't match the given result, remove it. + combo = [0] * posCount + for x in range(0, possibilities-1) + if x > 0 then incrementCombo combo + idx = possible.indexOf(x) + if idx == null then continue // (already eliminated) + result = calcResult(combo, guessCombo) + if result != actualResult then + //print "Eliminating #" + x + ", " + comboToColorString(combo) + possible.remove idx + end if + end for + end for + + if not gotIt then + print "I used up all my moves!" + print "I guess my CPU is just having an off day." + end if + globals.computerScore += guessNum + return true +end function + +// Show the score (with the given header). +showScore = function(header="Score") + print header + ":" + print " COMPUTER " + computerScore + print " HUMAN " + humanScore + print +end function + +// Initialization of global variables +colorCodes = "BWRGOYPT" +colorNames = "Black,White,Red,Green,Orange,Yellow,Purple,Tan".split(",") +computerScore = 0 +humanScore = 0 + +// Main program +print " "*30 + "Mastermind" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print +while true + colorCount = input("Number of colors? ").val + if 0 < colorCount <= 8 then break + print "No more than 8, please!" +end while +posCount = input("Number of positions? ").val +roundCount = input("Number of rounds? ").val +possibilities = colorCount ^ posCount +print "Total possibilities = " + possibilities + +print; print +print "Color Letter" +print "===== ======" +for x in range(0, colorCount-1) + print pad(colorNames[x], 9) + colorCodes[x] +end for +print + +for round in range(1, roundCount) + print + print "Round number " + round + " ----" + print + doHumanGuesses + showScore + while not doComputerGuesses; end while + showScore +end for + +print "GAME OVER" +showScore "Final score" diff --git a/00_Alternate_Languages/61_Math_Dice/MiniScript/README.md b/00_Alternate_Languages/61_Math_Dice/MiniScript/README.md new file mode 100644 index 000000000..ac7b913c2 --- /dev/null +++ b/00_Alternate_Languages/61_Math_Dice/MiniScript/README.md @@ -0,0 +1,21 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +0. Try-It! Page: +Go to https://miniscript.org/tryit/, clear the sample code from the code editor, and paste in the contents of mathdice.ms. Then click the "Run Script" button. Program output (and input) will appear in the green-on-black terminal display to the right of or below the code editor. + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: +``` + miniscript mathdice.ms +``` + +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: +``` + load "mathdice" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/61_Math_Dice/MiniScript/mathdice.ms b/00_Alternate_Languages/61_Math_Dice/MiniScript/mathdice.ms new file mode 100644 index 000000000..74ed63496 --- /dev/null +++ b/00_Alternate_Languages/61_Math_Dice/MiniScript/mathdice.ms @@ -0,0 +1,58 @@ +print " "*31 + "Math Dice" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print +print "This program generates successive pictures of two dice." +print "When two dice and an equal sign followed by a question" +print "mark have been printed, type your answer and the return key." +print "To conclude the lesson, type Control-C as your answer." +print + +printDie = function(d) + print + print " -----" + if d == 1 then + print "| |" + else if d == 2 or d == 3 then + print "| * |" + else + print "| * * |" + end if + if d == 2 or d == 4 then + print "| |" + else if d == 6 then + print "| * * |" + else + print "| * |" + end if + if d == 1 then + print "| |" + else if d == 2 or d == 3 then + print "| * |" + else + print "| * * |" + end if + print " -----" + print +end function + +while true + d1 = floor(6 * rnd + 1) + printDie d1 + print " +" + d2 = floor(6 * rnd + 1) + printDie d2 + total = d1 + d2 + answer = input(" =? ").val + if answer != total then + print "No, count the spots and give another answer." + answer = input(" =? ").val + end if + if answer == total then + print "Right!" + else + print "No, the answer is " + total + end if + print + print "The dice roll again..." +end while + diff --git a/00_Alternate_Languages/62_Mugwump/MiniScript/README.md b/00_Alternate_Languages/62_Mugwump/MiniScript/README.md new file mode 100644 index 000000000..ac7b913c2 --- /dev/null +++ b/00_Alternate_Languages/62_Mugwump/MiniScript/README.md @@ -0,0 +1,21 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +0. Try-It! Page: +Go to https://miniscript.org/tryit/, clear the sample code from the code editor, and paste in the contents of mathdice.ms. Then click the "Run Script" button. Program output (and input) will appear in the green-on-black terminal display to the right of or below the code editor. + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: +``` + miniscript mathdice.ms +``` + +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: +``` + load "mathdice" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/62_Mugwump/MiniScript/mugwump.ms b/00_Alternate_Languages/62_Mugwump/MiniScript/mugwump.ms new file mode 100644 index 000000000..8ba5f7fb2 --- /dev/null +++ b/00_Alternate_Languages/62_Mugwump/MiniScript/mugwump.ms @@ -0,0 +1,65 @@ +print " "*33 + "Mugwump" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print +// Courtesy People's Computer Company +print "The object of this game is to find four mugwumps" +print "hidden on a 10 by 10 grid. Homebase is position 0,0." +print "Any guess you make must be two numbers with each" +print "number between 0 and 9, inclusive. First number" +print "is distance to right of homebase and second number" +print "is distance above homebase." +print +print "You get 10 tries. After each try, I will tell" +print "you how far you are from each mugwump." +print + +playOneGame = function + mugwump = {} // key: number 1-4; value: [x,y] position + for i in range(1, 4) + mugwump[i] = [floor(10*rnd), floor(10*rnd)] + end for + + found = 0 + for turn in range(1, 10) + print + print + while true + inp = input("Turn no. " + turn + " -- what is your guess? ") + inp = inp.replace(",", " ").replace(" ", " ").split + if inp.len == 2 then break + end while + x = inp[0].val; y = inp[1].val + for i in range(1, 4) + pos = mugwump[i] + if pos == null then continue // (already found) + if pos == [x,y] then + print "You have found mugwump " + i + mugwump[i] = null + found += 1 + else + d = sqrt( (pos[0] - x)^2 + (pos[1] - y)^2 ) + print "You are " + round(d, 1) + " units from mugwump " + i + end if + end for + if found == 4 then + print + print "You got them all in " + turn + " turns!" + return + end if + end for + print + print "Sorry, that's 10 tries. Here is where they're hiding:" + for i in range(1, 4) + pos = mugwump[i] + if pos == null then continue + print "Mugwump " + i + " is at (" + pos[0] + "," + pos[1] + ")" + end for +end function + +// Main loop +while true + playOneGame + print + print "That was fun! Let's play again......." + print "Four more mugwumps are now in hiding." +end while diff --git a/00_Alternate_Languages/63_Name/MiniScript/README.md b/00_Alternate_Languages/63_Name/MiniScript/README.md new file mode 100644 index 000000000..819ecf501 --- /dev/null +++ b/00_Alternate_Languages/63_Name/MiniScript/README.md @@ -0,0 +1,21 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +0. Try-It! Page: +Go to https://miniscript.org/tryit/, clear the sample code from the code editor, and paste in the contents of name.ms. Then click the "Run Script" button. Program output (and input) will appear in the green-on-black terminal display to the right of or below the code editor. + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: +``` + miniscript name.ms +``` + +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: +``` + load "name" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/63_Name/MiniScript/name.ms b/00_Alternate_Languages/63_Name/MiniScript/name.ms new file mode 100644 index 000000000..bb3d2da23 --- /dev/null +++ b/00_Alternate_Languages/63_Name/MiniScript/name.ms @@ -0,0 +1,25 @@ +print " "*34 + "Name" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print +print "Hello."; print "My name is Creative Computer." +name = input("What's your name (first and last)? ") + +s = "" +for i in range(name.len - 1) + s += name[i] +end for +print; print "Thank you, " + s + "." +print "Oops! I guess I got it backwards. A smart" +print "computer like me shouldn't make a mistake like that!"; print +print "But i just noticed your letters are out of order." +s = name.split("").sort.join("") +print "Let's put them in order like this: " + s +yn = input("Don't you like that better? ").lower +print +if yn and yn[0] == "y" then + print "I knew you'd agree!!" +else + print "I'm sorry you don't like it that way." +end if +print; print "I really enjoyed meeting you " + name + "." +print "Have a nice day!" diff --git a/00_Alternate_Languages/64_Nicomachus/MiniScript/README.md b/00_Alternate_Languages/64_Nicomachus/MiniScript/README.md new file mode 100644 index 000000000..5ea2cf130 --- /dev/null +++ b/00_Alternate_Languages/64_Nicomachus/MiniScript/README.md @@ -0,0 +1,21 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript nicomachus.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "nicomachus" + run +``` +3. "Try-It!" page on the web: +Go to https://miniscript.org/tryit/, clear the default program from the source code editor, paste in the contents of nicomachus.ms, and click the "Run Script" button. diff --git a/00_Alternate_Languages/64_Nicomachus/MiniScript/nicomachus.ms b/00_Alternate_Languages/64_Nicomachus/MiniScript/nicomachus.ms new file mode 100644 index 000000000..ebbc394b7 --- /dev/null +++ b/00_Alternate_Languages/64_Nicomachus/MiniScript/nicomachus.ms @@ -0,0 +1,45 @@ +// Nicomachus +// originally by David Ahl +// Ported from BASIC to MiniScript by Joe Strout, 2023 + +print " "*33 + "NICOMA" +print " "*15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" +print; print; print +print "Boomerang puzzle from Arithmetica of Nicomachus -- A.D. 90!" + +// Get a yes/no (or at least y/n) response from the user. +askYesNo = function(prompt) + while true + answer = input(prompt) + a1 = answer.lower[:1] + if a1 == "y" or a1 == "n" then return a1 + print "Eh? I don't understand '" + answer + "' Try 'yes' or 'no'." + end while +end function + +doOne = function + print + print "Please think of a number between 1 and 100." + A = input("Your number divided by 3 has a remainder of: ").val + B = input("Your number divided by 5 has a remainder of: ").val + C = input("Your number divided by 7 has a remainder of: ").val + print + print "Let me think a moment..." + print + wait 1.5 + D = 70*A + 21*B + 15*C + D = D % 105 // gets the remainder after dividing by 105 + yesNo = askYesNo("Your number was " + D + ", right? ") + if yesNo == "y" then + print "How about that!" + else + print "I feel your arithmetic is in error." + end if +end function + +// Main loop -- press Control-C to break +while true + doOne + print + print "Let's try another." +end while diff --git a/00_Alternate_Languages/65_Nim/MiniScript/README.md b/00_Alternate_Languages/65_Nim/MiniScript/README.md new file mode 100644 index 000000000..0265ff51f --- /dev/null +++ b/00_Alternate_Languages/65_Nim/MiniScript/README.md @@ -0,0 +1,18 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: +``` + miniscript nim.ms +``` + +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: +``` + load "nim" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/65_Nim/MiniScript/nim.ms b/00_Alternate_Languages/65_Nim/MiniScript/nim.ms new file mode 100644 index 000000000..f241c6964 --- /dev/null +++ b/00_Alternate_Languages/65_Nim/MiniScript/nim.ms @@ -0,0 +1,194 @@ +print " "*33 + "Nim" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print + +askYesNo = function(prompt) + while true + answer = input(prompt + "? ").lower + if answer and answer[0] == "y" then return "yes" + if answer and answer[0] == "n" then return "no" + print "Please answer yes or no." + end while +end function + +print "This is the game of Nim." +if askYesNo("Do you want instructions") == "yes" then + print "The game is played with a number of piles of objects." + print "Any number of objects are removed from one pile by you and" + print "the machine alternately. On your turn, you may take" + print "all the objects that remain in any pile, but you must" + print "take at least one object, and you may take objects from" + print "only one pile on a single turn. You must specify whether" + print "winning is defined as taking or not taking the last object," + print "the number of piles in the game, and how many objects are" + print "originally in each pile. Each pile may contain a" + print "different number of objects." + print "The machine will show its move by listing each pile and the" + print "number of objects remaining in the piles after each of its" + print "moves." +end if + +allEmpty = function(piles) + for i in range(1, piles.len-1) + if piles[i] then return false + end for + return true +end function + +// Do the computer's turn; return true if game over, false otherwise. +doComputerTurn = function(pile, winCondition) + n = pile.len - 1 + d = [0,0,0] + b = [null] + for i in range(1, n) + b.push [0]*11 + end for + + if winCondition == 2 then + c = 0 + broke = false + for i in range(1, n) + if pile[i] == 0 then continue + c += 1 + if c == 3 then; broke = true; break; end if + d[c] = i + end for + if not broke then + if c == 2 and (pile[d[1]] == 1 or pile[d[2]] == 1) then + print "Machine wins" + return true + end if + if pile[d[1]] > 1 then + print "Machine wins" + return true + end if + print "Machine loses" + return true + end if + c = 0 + broke = false + for i in range(1, n) + if pile[i] > 1 then; broke = true; break; end if + if pile[i] == 0 then continue + c += 1 + end for + if not broke and c/2 != floor(c/2) then + print "Machine loses" + return true + end if + end if + + for i in range(1, n) + e = pile[i] + for j in range(0, 10) + f = e/2 + b[i][j] = 2*(f-floor(f)) + e = floor(f) + end for + end for + broke = false + for j in range(10, 0) + c = 0 + h = 0 + for i in range(1, n) + if b[i][j] == 0 then continue + c += 1 + if pile[i] <= h then continue + h = pile[i] + g = i + end for + if c/2 != floor(c/2) then; broke = true; break; end if + end for + if not broke then + while true + e = floor(n*rnd+1) + if pile[e] != 0 then break + end while + f = floor(pile[e]*rnd+1) + pile[e] -= f + else + pile[g] = 0 + for j in range(0, 10) + b[g][j] = 0 + c=0 + for i in range(1,n) + if b[i][j] == 0 then continue + c += 1 + end for + pile[g] += 2*(c/2-floor(c/2))*2^j + end for + if winCondition != 1 then + c = 0 + broke = false + for i in range(1, n) + if pile[i]>1 then; broke = true; break; end if + if pile[i] == 0 then continue + c += 1 + end for + if not broke and c/2 == floor(c/2) then pile[g]= 1 - pile[g] + end if + end if + + print "Pile Size" + for i in range(1, n) + print i + " " + pile[i] + end for + if winCondition == 1 and allEmpty(pile) then + print "Machine wins" + return true + end if + return false +end function + +// Do the human player's turn; return true if game over, false otherwise. +doPlayerTurn = function(pile, winCondition) + n = pile.len - 1 + while true + inp = input("Your move - pile, number to be removed? ") + inp = inp.replace(",", " ").replace(" ", " ").split + if inp.len != 2 then continue + x = inp[0].val; y = inp[1].val + if x == floor(x) and y == floor(y) and 1 <= x <= n and 1 <= y <= pile[x] then break + end while + + pile[x] -= y + if allEmpty(pile) then + if winCondition == 1 then print "Machine loses" else print "Machine wins" + return true + end if + return false +end function + +playOneGame = function + print + while true + w = input("Enter win option - 1 to take last, 2 to avoid last? ").val + if w == 1 or w == 2 then break + end while + while true + n = input("Enter number of piles? ").val + if n == floor(n) and 1 <= n <= 100 then break + end while + + print "Enter pile sizes" + pile = [null] + [0]*n // (null at element 0 to make our array 1-based) + for i in range(1, n) + while true + pile[i] = input(i + "? ").val + if pile[i] == floor(pile[i]) and 1 <= pile[i] <= 2000 then break + end while + end for + + if askYesNo("Do you want to move first") == "yes" then + if doPlayerTurn(pile, w) then return + end if + while true + if doComputerTurn(pile, w) then return + if doPlayerTurn(pile, w) then return + end while +end function + +while true + playOneGame + if askYesNo("Do you want to play another game") == "no" then break +end while diff --git a/00_Alternate_Languages/66_Number/MiniScript/README.md b/00_Alternate_Languages/66_Number/MiniScript/README.md new file mode 100644 index 000000000..e3c8143ac --- /dev/null +++ b/00_Alternate_Languages/66_Number/MiniScript/README.md @@ -0,0 +1,21 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript number.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "number" + run +``` +3. "Try-It!" page on the web: +Go to https://miniscript.org/tryit/, clear the default program from the source code editor, paste in the contents of number.ms, and click the "Run Script" button. diff --git a/00_Alternate_Languages/66_Number/MiniScript/number.ms b/00_Alternate_Languages/66_Number/MiniScript/number.ms new file mode 100644 index 000000000..0f242d682 --- /dev/null +++ b/00_Alternate_Languages/66_Number/MiniScript/number.ms @@ -0,0 +1,44 @@ +// Number Game +// originally by Tom Adametx +// Ported from BASIC to MiniScript by Joe Strout, 2023 + +print " "*33 + "NUMBER" +print " "*15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" +print; print; print + +print "You have 100 points. By guessing numbers from 1 to 5, you" +print "can gain or lose points depending on how close you get to" +print "a random number selected by the computer."; print +print "You occasionally will get a jackpot which will double(!)" +print "your point count. You win when you get to 500 points." +print + +P = 100 +fnr = function; return ceil(5*rnd); end function +while true + guess = input("Guess a number from 1 to 5: ").val + R = fnr + S = fnr + T = fnr + U = fnr + V = fnr + if guess == R then + P = P - 5 + else if guess == S then + P = P + 5 + else if guess == T then + P = P+P + print "You hit the jackpot!!!" + else if guess == U then + P = P + 1 + else if guess == V then + P = P - floor(P*0.5) + else if guess > 5 then + continue + end if + if P > 500 then + print "!!!!You win!!!! with " + P + " points." + break + end if + print "You have " + P + " points."; print +end while diff --git a/00_Alternate_Languages/67_One_Check/MiniScript/README.md b/00_Alternate_Languages/67_One_Check/MiniScript/README.md new file mode 100644 index 000000000..d79b401fb --- /dev/null +++ b/00_Alternate_Languages/67_One_Check/MiniScript/README.md @@ -0,0 +1,18 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: +``` + miniscript onecheck.ms +``` + +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: +``` + load "onecheck" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/67_One_Check/MiniScript/onecheck.ms b/00_Alternate_Languages/67_One_Check/MiniScript/onecheck.ms new file mode 100644 index 000000000..fee41c8c4 --- /dev/null +++ b/00_Alternate_Languages/67_One_Check/MiniScript/onecheck.ms @@ -0,0 +1,101 @@ +print "Solitaire Checker Puzzle by David Ahl" +print +print "48 checkers are placed on the 2 outside spaces of a" +print "standard 64-square checkerboard. The object is to" +print "remove as many checkers as possible by diagonal jumps" +print "(as in standard checkers). Use the numbered board to" +print "indicate the square you wish to jump from and to. On" +print "the board printed out on each turn '1' indicates a" +print "checker and '0' an empty square. When you have no" +print "possible jumps remaining, input a '0' in response to" +print "question 'Jump from?'" +print +input "(Press Return.)" +print + +pad4 = function(n) + return (" " + n)[-4:] +end function + +printBoard = function + // Idea: This program could be greatly improved by printing the board + // as the index numbers (1-64), indicating which of those positions + // contain checkers via color or punctuation, e.g. "(42)" vs " 42 ". + // This would make it much easier for the user to figure out what + // numbers correspond to the positions they have in mind. + print + for j in range(1, 57, 8) + print " " + board[j:j+8].join(" ") + end for +end function + +initBoard = function + globals.board = [null] + [1] * 64 // treat this as 1-based array + for j in range(19, 43, 8) + for i in range(j, j+3) + board[i] = 0 + end for + end for +end function + +isLegal = function(from, to) + if board[from] == 0 then return false + if board[to] == 1 then return false + if board[(to+from)/2] == 0 then return false + fromRow = floor((from-1) / 8) // row in range 0-7 + fromCol = from - fromRow*8 // column in range 1-8 + toRow = floor((to-1) / 8) + toCol = to - toRow*8 + if fromRow > 7 or toRow > 7 or fromCol > 8 or toCol > 8 then return false + if abs(fromRow-toRow) != 2 or abs(fromCol-toCol) != 2 then return false + return true +end function + +askYesNo = function(prompt) + while true + answer = input(prompt + "? ").lower + if answer and answer[0] == "y" then return "yes" + if answer and answer[0] == "n" then return "no" + print "Please answer 'yes' or 'no'." + end while +end function + +print "Here is the numerical board:" +print +for j in range(1, 57, 8) + print pad4(j) + pad4(j+1) + pad4(j+2) + pad4(j+3) + + pad4(j+4) + pad4(j+5) + pad4(j+6) + pad4(j+7) +end for + +while true + initBoard + print + print "And here is the opening position of the checkers." + printBoard + jumps = 0 + while true + fromPos = input("Jump from? ").val + if fromPos == 0 then break + toPos = input("To? ").val + print + if not isLegal(fromPos, toPos) then + print "Illegal move. Try again..." + continue + end if + board[fromPos] = 0 + board[toPos] = 1 + board[(toPos+fromPos)/2] = 0 + jumps += 1 + printBoard + end while + // End game summary + sum = board[1:].sum + print "You made " + jumps + " jumps and had " + sum + " pieces" + print "remaining on the board." + print + if askYesNo("Try again") == "no" then break +end while +print +print "O.K. Hope you had fun!!" + + \ No newline at end of file diff --git a/00_Alternate_Languages/68_Orbit/MiniScript/README.md b/00_Alternate_Languages/68_Orbit/MiniScript/README.md new file mode 100644 index 000000000..cc0c29669 --- /dev/null +++ b/00_Alternate_Languages/68_Orbit/MiniScript/README.md @@ -0,0 +1,18 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: +``` + miniscript orbit.ms +``` + +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: +``` + load "orbit" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/68_Orbit/MiniScript/orbit.ms b/00_Alternate_Languages/68_Orbit/MiniScript/orbit.ms new file mode 100644 index 000000000..5c624c42b --- /dev/null +++ b/00_Alternate_Languages/68_Orbit/MiniScript/orbit.ms @@ -0,0 +1,95 @@ +print " "*33 + "Orbit" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print +print "Somewhere above your planet is a Romulan ship." +print +print "The ship is in a constant polar orbit. Its" +print "distance from the center of your planet is from" +print "10,000 to 30,000 miles and at its present velocity can" +print "circle your planet once every 12 to 36 hours." +print +print "Unfortunately, they are using a cloaking device so" +print "you are unable to see them, but with a special" +print "instrument you can tell how near their ship your" +print "photon bomb exploded. You have seven hours until they" +print "have built up sufficient power in order to escape" +print "your planet's gravity." +print +print "Your planet has enough power to fire one bomb an hour." +print +print "At the beginning of each hour you will be asked to give an" +print "angle (between 0 and 360) and a distance in units of" +print "100 miles (between 100 and 300), after which your bomb's" +print "distance from the enemy ship will be given." +print +print "An explosion within 5,000 miles of the romulan ship" +print "will destroy it." +print; input "(Press Return.)" +print +print "Below is a diagram to help you visualize your plight." +print +print +print " 90" +print " 0000000000000" +print " 0000000000000000000" +print " 000000 000000" +print " 00000 00000" +print " 00000 xxxxxxxxxxx 00000" +print " 00000 xxxxxxxxxxxxx 00000" +print " 0000 xxxxxxxxxxxxxxx 0000" +print " 0000 xxxxxxxxxxxxxxxxx 0000" +print " 0000 xxxxxxxxxxxxxxxxxxx 0000" +print "180<== 00000 xxxxxxxxxxxxxxxxxxx 00000 ==>0" +print " 0000 xxxxxxxxxxxxxxxxxxx 0000" +print " 0000 xxxxxxxxxxxxxxxxx 0000" +print " 0000 xxxxxxxxxxxxxxx 0000" +print " 00000 xxxxxxxxxxxxx 00000" +print " 00000 xxxxxxxxxxx 00000" +print " 00000 00000" +print " 000000 000000" +print " 0000000000000000000" +print " 0000000000000" +print " 270" +print +print "x - your planet" +print "o - the orbit of the romulan ship" +print; input "(Press Return.)" +print +print "On the above diagram, the romulan ship is circling" +print "counterclockwise around your planet. Don't forget that" +print "without sufficient power the romulan ship's altitude" +print "and orbital rate will remain constant." +print +print "Good luck. The federation is counting on you." + +while true + a=floor(360*rnd) + d=floor(200*rnd + 200) + r=floor(20*rnd + 10) + for h in range(1,7) + print + print + print "This is hour " + h + ", at what angle do you wish to send" + a1 = input("your photon bomb? ").val + d1 = input("How far out do you wish to detonate it? ").val + print + print + a += r + if a >= 360 then a -= 360 + t = abs(a-a1) + if t >= 180 then t = 360 - t + c = sqrt(d*d + d1*d1 - 2*d*d1*cos(t*pi/180)) + print "Your photon bomb exploded " + round(c) + " * 10^2 miles from the" + print "Romulan ship." + if c<=50 then break + end for + if c <= 50 then + print "You have succesfully completed your mission." + else + print "You have allowed the Romulans to escape." + end if + print "Another romulan ship has gone into orbit." + yn = input("Do you wish to try to destroy it? ").lower + " " + if yn[0] != "y" then break +end while +print "good bye." diff --git a/00_Alternate_Languages/69_Pizza/MiniScript/README.md b/00_Alternate_Languages/69_Pizza/MiniScript/README.md new file mode 100644 index 000000000..993c4056c --- /dev/null +++ b/00_Alternate_Languages/69_Pizza/MiniScript/README.md @@ -0,0 +1,18 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: +``` + miniscript pizza.ms +``` + +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: +``` + load "pizza" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/69_Pizza/MiniScript/pizza.ms b/00_Alternate_Languages/69_Pizza/MiniScript/pizza.ms new file mode 100644 index 000000000..84c33bb63 --- /dev/null +++ b/00_Alternate_Languages/69_Pizza/MiniScript/pizza.ms @@ -0,0 +1,98 @@ +print " "*33 + "Pizza" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print +print "Pizza Delivery Game"; print +name = input("What is your first name? "); print +print "Hi, " + name + ". In this game you are to take orders" +print "for pizzas. Then you are to tell a delivery boy" +print "where to deliver the ordered pizzas."; print; print + +// Convert a house name like "G" into coordinates like [3,2]. +nameToCoords = function(name) + idx = name.code - "A".code + row = floor(idx / 4) + col = idx % 4 + return [col+1, row+1] +end function + +// Convert house coordinates like [3,2] into a house name like "G". +coordsToName = function(coords) + idx = (coords[1]-1)*4 + (coords[0]-1) + return char("A".code + idx) +end function + +askYesNo = function(prompt) + while true + yn = input(prompt + "? ").lower + " " + if yn[0] == "y" then return "yes" + if yn[0] == "n" then return "no" + print "'Yes' or 'no' please, now then," + end while +end function + +input "(Press Return.)"; print + +print "Map of the city of Hyattsville"; print +print " -----1-----2-----3-----4-----" +for row in range(4, 1) + print "-"; print "-"; print "-" + s = row + " " + for col in range(1, 4) + s += coordsToName([col, row]) + " " + end for + s += row + print s +end for +print "-"; print "-"; print "-" +print " -----1-----2-----3-----4-----" + +input +print "The output is a map of the homes where" +print "you are to send pizzas."; print +print "Your job is to give a truck driver" +print "the location or coordinates of the" +print "home ordering the pizza."; print +if askYesNo("Do you need more directions") == "yes" then + print; print "Somebody will ask for a pizza to be" + print "delivered. Then a delivery boy will" + print "ask you for the location."; + print " Example:" + print "This is J. Please send a pizza." + print "Driver to " + name + ". Where does j live?" + print "Your answer would be 2,3"; print + if askYesNo("Understand") == "no" then + print "This job is definitely too difficult for you. Thanks anyway" + exit + end if + print "Good. you are now ready to start taking orders."; print + print "Good luck!!"; print +end if + +while true + for turn in range(1,5) + coords = [floor(rnd*4+1), floor(rnd*4+1)] + buyer = coordsToName(coords) + print "Hello " + name + "'s Pizza. This is " + buyer + + ". Please send a pizza." + while true + while true + inp = input(" Driver to " + name + ": Where does " + buyer + " live? ") + inp = inp.replace(",", " ").replace(" ", " ") + inp = inp.split + ["0","0"] + guess = [inp[0].val, inp[1].val] + if 1 <= guess[0] <= 4 and 1 <= guess[1] <= 4 then break + end while + if guess == coords then + print "Hello " + name + ". This is " + buyer + ", thanks for the pizza." + break + else + print "This is " + coordsToName(guess) + ". I did not order a pizza." + print "I live at " + guess.join(",") + end if + end while + print + end for + if askYesNo("Do you want to deliver more pizzas") == "no" then break +end while + +print; print "O.K. " + name + ", see you later!"; print diff --git a/00_Alternate_Languages/70_Poetry/MiniScript/README.md b/00_Alternate_Languages/70_Poetry/MiniScript/README.md new file mode 100644 index 000000000..52effbcc2 --- /dev/null +++ b/00_Alternate_Languages/70_Poetry/MiniScript/README.md @@ -0,0 +1,18 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: +``` + miniscript poetry.ms +``` + +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: +``` + load "poetry" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/70_Poetry/MiniScript/poetry.ms b/00_Alternate_Languages/70_Poetry/MiniScript/poetry.ms new file mode 100644 index 000000000..f55d32901 --- /dev/null +++ b/00_Alternate_Languages/70_Poetry/MiniScript/poetry.ms @@ -0,0 +1,51 @@ +print " "*30 + "POETRY" +print " "*15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" +print; print; print + +I = 0; J = 0; K = 0; U = 0 + +// Note: infinite loop. Press control-C to break. +while true + if J == 1 then + print ["MIDNIGHT DREARY", "FIERY EYES", + "BIRD OR FIEND", "THING OF EVIL", "PROPHET"][I-1], "" + else if J == 2 then + if I == 1 or I == 4 then U = 2 + if I == 3 then U = 0 + print ["BEGUILING ME", "THRILLED ME", + "STILL SITTING....", "NEVER FLITTING", "BURNED"][I-1], "" + else if J == 3 then + if U != 0 or I < 5 then + print ["AND MY SOUL", "DARKNESS THERE", + "SHALL BE LIFTED", "QUOTH THE RAVEN", "SIGN OF PARTING"][I-1], "" + end if + else if J == 4 then + print ["NOTHING MORE", "YET AGAIN", + "SLOWLY CREEPING", "...EVERMORE", "NEVERMORE"][I-1], "" + end if + if U != 0 and rnd <= 0.19 then + print ",", "" + U = 2 + end if + if rnd <= 0.65 then + print " ", "" + U += 1 + else + print + U = 0 + end if + while true + I =floor(floor(10*rnd)/2)+1 + J += 1 + K += 1 + if U == 0 and floor(J/2) == J/2 then print " ", "" + if J <= 5 then break + J = 0 + print + if K <= 20 then continue + print + U = 0 + K = 0 + break + end while +end while diff --git a/00_Alternate_Languages/71_Poker/MiniScript/README.md b/00_Alternate_Languages/71_Poker/MiniScript/README.md new file mode 100644 index 000000000..95144dcfd --- /dev/null +++ b/00_Alternate_Languages/71_Poker/MiniScript/README.md @@ -0,0 +1,18 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: +``` + miniscript poker.ms +``` + +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: +``` + load "poker" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/71_Poker/MiniScript/poker.ms b/00_Alternate_Languages/71_Poker/MiniScript/poker.ms new file mode 100644 index 000000000..b367db031 --- /dev/null +++ b/00_Alternate_Languages/71_Poker/MiniScript/poker.ms @@ -0,0 +1,557 @@ +print " "*33 + "POKER" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print + +print "Welcome to the casino. We each have $200." +print "I will open the betting before the draw; you open after." +print "To fold bet 0; to check bet .5." +print "Enough talk -- let's get down to business." +print + +askYesNo = function(prompt) + while true + yn = input(prompt + "? ").lower + " " + if yn[0] == "y" then return "yes" + if yn[0] == "n" then return "no" + print "Answer yes or no, please." + end while +end function +askNumber = function(prompt, maxQty=3, minQty=1, maxErr=null, minErr=null) + while true + value = input(prompt + "? ").val + if minQty <= value <= maxQty then return value + if value < minQty and minErr != null then + print minErr + else if maxErr != null then + print maxErr + else + print "Enter a value between " + minQty + " and " + maxQty + ", please." + end if + end while +end function +random = function(n) + return floor(n * rnd) +end function +rand10 = function + return floor(10 * rnd) +end function +pad = function(s, width) + return s + " " * (width - s.len) +end function + +// Bonus little feature when running in Mini Micro: display a header bar +// always at the top of the screen, showing current balances. Does nothing +// on other platforms. +drawHeaders = function + if version.hostName != "Mini Micro" then return + display(2).mode = displayMode.text; td = display(2) + td.color = color.black; td.backColor = text.color + td.row = 25; td.column = 0 + td.print pad("Computer: $" + computer.balance, 25) + + pad("Table: $" + Table.pot, 25) + + pad("Player: $" + human.balance, 18) +end function + + +// Card class: represents a single playing card +Card = {} +Card.rank = 0 // from 2 to 14 (Ace) +Card.suit = "Clubs" +Card.keep = false // temp flag, used to note which cards to keep vs. discard +Card.make = function(rank, suit) + result = new Card + result.rank = rank + result.suit = suit + return result +end function +Card.rankStr = function(plural=false) + if self.rank > 10 then + return ["Jack", "Queen", "King", "Ace"][self.rank-11] + "s"*plural + else + return str(self.rank) + "'s" * plural + end if +end function +Card.str = function + return self.rankStr + " of " + self.suit +end function + +// Prepare a standard deck of 52 cards, and functions to draw and discard +deck = [] +for suit in ["Clubs", "Diamonds", "Hearts", "Spades"] + for rank in range(2, 14) + deck.push Card.make(rank, suit) + end for +end for +deck.shuffle +discardPile = [] +drawCard = function + if not deck then + globals.deck = discardPile + deck.shuffle + globals.discardPile = [] + end if + return deck.pop +end function +discard = function(cardOrCards) + if cardOrCards isa Hand then + globals.discardPile += cardOrCards.cards + else if cardOrCards isa list then + globals.discardPile += cardOrCards + else + discardPile.push cardOrCards + end if +end function + +// Hand ranks: how we compare Poker hands +HandRank = {} +HandRank.value = 0 +HandRank.str = function(highCard); return ""; end function +HandRank.make = function(value) + result = new HandRank + result.value = value + return result +end function + +HandRank.None = new HandRank +HandRank.Schmaltz = HandRank.make(1) +HandRank.Schmaltz.str = function(c); return "schmaltz, " + c.rankStr + " high"; end function +HandRank.PartialStraight = HandRank.make(2) +HandRank.PartialStraight.str = function(c); return ""; end function // (no display string; this is used only internally) +HandRank.Pair = HandRank.make(3) +HandRank.Pair.str = function(c); return "a pair of " + c.rankStr(true); end function +HandRank.TwoPair = HandRank.make(4) +HandRank.TwoPair.str = function(c); return "two pair, " + c.rankStr(true); end function +HandRank.Three = HandRank.make(5) +HandRank.Three.str = function(c); return "three " + c.rankStr(true); end function +HandRank.Straight = HandRank.make(6) +HandRank.Straight.str = function(c); return "straight, " + c.rankStr + " high"; end function +HandRank.Flush = HandRank.make(7) +HandRank.Flush.str = function(c); return "a flush in " + c.suit; end function +HandRank.FullHouse = HandRank.make(8) +HandRank.FullHouse.str = function(c); return "full house, " + c.rankStr(true); end function +HandRank.Four = HandRank.make(9) +HandRank.Four.str = function(c); return "four " + c.rankStr(true); end function +// Note: original code does not detect a straight flush or royal flush. + +// Hand: represents a set of cards in the hand of one player. +Hand = {} +Hand.cards = null // list of Card +Hand.rank = null // HandRank instance +Hand.highCard = null // reference to which (of self.cards) determines relative value +Hand.afterDraw = false // true if we've already had a chance to draw cards +Hand.make = function(cards) + result = new Hand + result.cards = cards + result.analyze + return result +end function +Hand.deal = function + return Hand.make([drawCard, drawCard, drawCard, drawCard, drawCard]) +end function +Hand.replaceCard = function(index) + discard self.cards[index] + self.cards[index] = drawCard +end function +Hand.rankStr = function + return self.rank.str(self.highCard) +end function +Hand.isWeak = function + return self.rank.value < HandRank.PartialStraight.value or + (self.rank.value == HandRank.PartialStraight.value and self.afterDraw) or + (self.rank <= HandRank.TwoPair.value and self.highCard.rank <= 6) +end function +Hand.beats = function(other) + if self.rank.value > other.rank.value then return true + if self.rank.value < other.rank.value then return false + return self.highCard.rank > other.highCard.rank +end function +Hand.print = function(startingNumber=1) + num = startingNumber + for c in self.cards + s = " " * (num < 10) + num + " -- " + c.str + print " " + s, "" + if num % 2 == 0 then + print + else + print " " * (28-s.len), "" + end if + num += 1 + end for + if num % 2 == 0 then print +end function +Hand.analyze = function + allSameSuit = true + for i in range(1, self.cards.len-1) + if self.cards[i].suit != self.cards[0].suit then allSameSuit = false + end for + if allSameSuit then + self.rank = HandRank.Flush + self.highCard = self.cards[0] + return + end if + + sortedCards = self.cards[:] + sortedCards.sort "rank" + self.rank = HandRank.Schmaltz + for c in sortedCards; c.keep = false; end for + keepAny = false + + for i in range(0, sortedCards.len-2) + matchesNextCard = (sortedCards[i].rank == sortedCards[i+1].rank) + if matchesNextCard then + self.highCard = sortedCards[i] + matchesPrevCard = (i > 0 and sortedCards[i].rank == sortedCards[i-1].rank) + sortedCards[i].keep = true + sortedCards[i+1].keep = true + keepAny = true + if self.rank.value < HandRank.Pair.value then + self.rank = HandRank.Pair + else if matchesPrevCard and self.rank == HandRank.Pair then + self.rank = HandRank.Three + else if self.rank == HandRank.Pair then + self.rank = HandRank.TwoPair + else if self.rank == HandRank.TwoPair then + self.rank = HandRank.FullHouse + else if matchesPrevCard then + self.rank = HandRank.Four + else + self.rank = HandRank.FullHouse + end if + end if + end for + if not keepAny then + if sortedCards[3].rank - sortedCards[0].rank == 3 then + for i in range(0,3); sortedCards[i].keep = true; end for + self.rank = HandRank.PartialStraight + end if + if sortedCards[4].rank - sortedCards[1].rank == 3 then + if self.rank == HandRank.PartialStraight then + self.rank = HandRank.Straight + sortedCards[4].keep = true + self.highCard = sortedCards[4] + else + self.rank = HandRank.PartialStraight + for i in range(1,4); sortedCards[i].keep = true; end for + end if + end if + end if + if self.rank == HandRank.Schmaltz then + self.highCard = sortedCards[4] + sortedCards[4].keep = true + sortedCards[3].keep = true + end if + +end function + +// Some global constants, just to make the code more understandable +Ante = 5 + +// Player -- base class for computer and human +Player = {} +Player.balance = 200 +Player.hand = null +Player.totalBet = 0 +Player.anteUp = function + self.balance -= Ante + return Ante +end function +Player.newHand = function + if self.hand then discard self.hand + self.hand = Hand.deal + self.totalBet = 0 +end function +Player.addToPot = function(amount) + self.balance -= amount + Table.pot += amount + drawHeaders +end function +Player.win = function + self.balance += Table.pot + Table.pot = 0 + drawHeaders +end function + +// strategies the computer player might employ +Strategy = {} +Strategy.make = function(name, value=2, drawCount=null) + result = new Strategy + result.name = name + result.value = value + result.drawCount = drawCount + return result +end function +Strategy.fold = Strategy.make("FOLD") +Strategy.check = Strategy.make("CHECK") +Strategy.raise = Strategy.make("RAISE", 2) +Strategy.bluff = function(value, drawCount); return Strategy.make("BLUFF", value, drawCount); end function +Strategy.bet = function(value); return Strategy.make("BET", value); end function + +// computer player +computer = new Player +computer.strategy = null +computer.newHand = function + super.newHand + if self.hand.isWeak then + if rand10 < 2 then + self.strategy = Strategy.bluff(23, 2) + else if rand10 < 2 then + self.strategy = Strategy.bluff(23, 1) + else if rand10 < 1 then + self.strategy = Strategy.bluff(23, 0) + else + self.strategy = Strategy.fold + end if + else if self.hand.rank.value < HandRank.Three.value then + if rand10 < 2 then self.strategy = Strategy.bluff(23, null) else self.strategy = Strategy.check + else if self.hand.rank.value < HandRank.FullHouse.value then + self.strategy = Strategy.bet(35) + else + if rand10 < 1 then self.strategy = Strategy.bet(35) else self.strategy = Strategy.raise + end if +end function +computer.bet = function(minBet=1, openingBet=false) + //print "My hand: "; self.hand.print; print "Strategy: " + self.strategy + if self.balance < minBet then + print "I fold." + return 0 + end if + if openingBet and (self.strategy == Strategy.check or self.strategy == Strategy.fold) then + print "I check." + return 0.5 + else if self.strategy == Strategy.fold and human.totalBet > 5 then + print "I fold." + return 0 + else if self.strategy == Strategy.check then + print "I'll see you." + return minBet + else if openingBet then + result = self.strategy.value + rand10 + if result > self.balance then result = self.balance + if result == 0 then + print "I check." + return 0.5 + end if + print "I'll open with $" + result + return result + else + bet = self.strategy.value + rand10 + if self.strategy == Strategy.raise then bet += minBet + if bet > self.balance then bet = self.balance + raise = bet - minBet + if raise <= 0 then + print "I'll see you." + return minBet + else + print "I'll see you, and raise you " + raise + return bet + end if + end if +end function +computer.drawCards = function + //print "My hand:"; self.hand.print + drawCount = 0 + for c in self.hand.cards; if not c.keep then drawCount += 1; end for + print "I am taking " + drawCount + " card" + "s" * (drawCount != 1) + for i in self.hand.cards.indexes + if not self.hand.cards[i].keep then self.hand.replaceCard i + end for + self.hand.analyze + //print "My new hand: "; self.hand.print + if self.strategy.name == "BLUFF" then + self.strategy = Strategy.bluff(28) + else if self.hand.isWeak then + self.strategy = Strategy.fold + else if self.hand.rank.value < HandRank.Three.value then + if rand10 == 0 then self.strategy = Strategy.bet(19) else self.strategy = Strategy.raise + else if self.hand.rank.value < HandRank.FullHouse.value then + if rand10 == 0 then self.strategy = Strategy.bet(11) else self.strategy = Strategy.bet(19) + else + self.strategy = Strategy.raise + end if +end function +computer.win = function + print; print "I win." + super.win +end function +computer.checkFunds = function + if self.balance >= Ante then return + if human.balance < 50 then return // BUGFIX + if human.hasWatch or askYesNo("Would you like to buy back your watch for $50") == "no" then + print "I'm busted. Conglatulations!" + exit + end if + self.balance += 50 + // Note: original BASIC code does not take money from the player, but let's fix that: + human.balance -= 50 // BUGFIX + human.hasWatch = true + drawHeaders +end function + + +human = new Player +human.hasWatch = true +human.bet = function(minBet=1, openingBet=false) + while true + betStr = input("What is your bet? ") + bet = betStr.val + if bet == 0 and betStr != "0" then + print "Enter 0 to fold, 0.5 to check, or a value of 1 or more to bet." + continue + else if bet == 0.5 and openingBet then + return bet // (check) + else if 0 < bet < 1 then + print "No small change, please." + continue + else if bet < minBet then + print "If you can't see my bet, then fold." + continue + else if bet > self.balance then + print "You can't bet with what you haven't got." + self.trySellWatch + continue + end if + return bet + end while +end function +human.drawCards = function + qty = askNumber("How many cards do you want", 3, 0, "You can't draw more than three cards.") + if qty == 0 then return + print "What are their numbers: " + for i in range(1, qty) + num = askNumber("", 5, 1) + self.hand.replaceCard num - 1 + end for + print "Your new hand:" + self.hand.print + self.hand.analyze +end function +human.win = function + print; print "You win." + super.win +end function +human.checkFunds = function + if self.balance < Ante then self.trySellWatch + if self.balance < Ante then + print "Your wad is shot. So long, sucker!" + exit + end if +end function +human.trySellWatch = function + if not self.hasWatch then return + if rand10 < 7 then + value = 75 + msg = "I'll give you $" + value + " for it." + else + value = 25 + msg = "That's a pretty crummy watch - I'll give you $" + value + "." + end if + if computer.balance < value then return // BUGFIX + if askYesNo("Would you like to sell your watch") == "no" then return + print msg + self.balance += value + self.hasWatch = false + // Note: the original BASIC program does not actually take any money from the computer. + // But let's do it right here: + computer.balance -= value // BUGFIX + drawHeaders +end function + +Table = {} +Table.pot = 0 + + +deal = function + Table.pot += computer.anteUp + human.anteUp + drawHeaders + computer.newHand + human.newHand +end function + +// Do a round of betting. Return true to continue, or false +// if either player folds (ending the hand). +takeBets = function(computerFirst) + if computerFirst then + bet = computer.bet(1, true) + if bet == 0 then + human.win + return false + end if + if bet == 0.5 then bet = 0 // "check" (no bet, but stay in the hand) + computer.addToPot bet + else + bet = 0 + end if + raise = bet + canCheck = (bet == 0) + while true + bet = human.bet(raise, canCheck) + if bet == 0 then + computer.win + return false + end if + if bet == 0.5 then // (checked) + if computerFirst then return true + bet = 0 + else + human.addToPot bet + raise = bet - raise + if raise == 0 then return true + end if + if computerFirst or bet > 0 then canCheck = false + + bet = computer.bet(raise, canCheck) + if bet == 0 then + human.win + return false + end if + if bet == 0.5 then return true // (checked) + canCheck = false + computer.addToPot bet + raise = bet - raise + if raise == 0 then return true + end while +end function + +playHand = function + print + computer.checkFunds + human.checkFunds + print "The ante is " + Ante + ". I will deal:" + print + + deal + + print "Your hand:" + human.hand.print + + print + if not takeBets(true) then return + + print; print "Now we draw -- ", "" + human.drawCards + computer.drawCards + + if not takeBets(false) then return + + print; print "Now we compare hands:" + print "My hand:" + computer.hand.print 6 + print + print "You have " + human.hand.rankStr + print "I have " + computer.hand.rankStr + if computer.hand.beats(human.hand) then + computer.win + else if human.hand.beats(computer.hand) then + human.win + else + print "The hand is drawn." + print "All $" + Table.pot + " remains in the pot." + end if +end function + +drawHeaders +while true + playHand + print "Now I have $" + computer.balance + " and you have $" + human.balance + if askYesNo("Do you wish to continue") == "no" then break +end while \ No newline at end of file diff --git a/00_Alternate_Languages/72_Queen/MiniScript/README.md b/00_Alternate_Languages/72_Queen/MiniScript/README.md new file mode 100644 index 000000000..cf8463343 --- /dev/null +++ b/00_Alternate_Languages/72_Queen/MiniScript/README.md @@ -0,0 +1,18 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: +``` + miniscript queen.ms +``` + +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: +``` + load "queen" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/72_Queen/MiniScript/queen.ms b/00_Alternate_Languages/72_Queen/MiniScript/queen.ms new file mode 100644 index 000000000..d3e882b91 --- /dev/null +++ b/00_Alternate_Languages/72_Queen/MiniScript/queen.ms @@ -0,0 +1,151 @@ +print " "*33 + "QUEEN" +print " "*15 + "Creative Computing Morristown New Jersey" +print; print; print + +getYesNo = function(prompt) + while true + inp = input(prompt + "? ").lower + " " + if inp[0] == "y" then return "yes" + if inp[0] == "n" then return "no" + print "Please answer 'yes' or 'no'." + end while +end function + +printDirections = function + print "We are going to play a game based on one of the chess" + print "moves. Our queen will be able to move only to the left " + print "down or diagonally down and to the left." + print + print "The object of the game is to place the queen in the lower" + print "left hand square by alternating moves between you and the" + print "computer. The first one to place the queen there wins." + print + print "You go first and place the queen in any one of the squares" + print "on the top row or right hand column." + print "That will be your first move." + print "We alternate moves." + print "You may forfeit by typing '0' as your move." + print "Be sure to press the return key after each response." + print + input "(Press Return to continue.)" + print +end function + +printCoordinates = function + // Porting note: I cannot imagine what possessed the original author to + // use such a crazy numbering scheme, which is not easy for the human + // player OR the code. But assumptions about it are scattered throughout + // the whole program, so we're stuck with it. + print + print " 81 71 61 51 41 31 21 11" + print " 92 82 72 62 52 42 32 22" + print " 103 93 83 73 63 53 43 33" + print " 114 104 94 84 74 64 54 44" + print " 125 115 105 95 85 75 65 55" + print " 136 126 116 106 96 86 76 66" + print " 147 137 127 117 107 97 87 77" + print " 158 148 138 128 118 108 98 88" + print +end function + +getStartPos = function + while true + m1 = input("Where would you like to start? ").val + tens = floor(m1/10) + ones = m1 % 10 + if ones == 1 or tens == ones or m1 == 0 then return m1 + print "Please read the directions again." + print "You have begun illegally." + print + end while +end function + +isGoodMove = function(ones, tens) + pos = 10 * tens + ones + return [158, 127, 126, 75, 73].indexOf(pos) != null +end function + +getRandomMove = function(queenPos) + tens = floor(queenPos/10) + ones = queenPos % 10 + z = rnd + if z > 0.6 then + return 10 * (tens+1) + ones + else if z > 0.3 then + return 10 * (tens+2) + (ones+1) + else + return 10 * (tens+1) + ones + end if +end function + +getComputerMove = function(queenPos) + tens = floor(queenPos/10) + ones = queenPos % 10 + if [41, 44, 73, 75, 126, 127].indexOf(queenPos) != null then return getRandomMove(queenPos) + for k in range(7, 1) + if isGoodMove(ones, tens+k) then return 10 * (tens+k) + ones // left + if isGoodMove(ones+k, tens+k) then return 10 * (tens+k) + (ones+k) // down + if isGoodMove(ones+k, tens+k*2) then return 10 * (tens+k*2) + (ones+k) // down-left + end for + return getRandomMove(queenPos) +end function + +getHumanMove = function(queenPos) + tens = floor(queenPos/10) + ones = queenPos % 10 + while true + pos = input("What is your move? ").val + if pos == 0 then return 0 + dTens = floor(pos/10) - tens + dOnes = pos % 10 - ones + ok = false + if dOnes == 0 and dTens > 0 then ok = true // moving left + if dOnes == dTens and dOnes > 0 then ok = true // moving down + if dTens == dOnes*2 and dOnes > 0 then ok = true // moving down-left + if ok then return pos + print + print "Y O U C H E A T . . . Try again"; + end while +end function + +playGame = function + queenPos = getStartPos + while true + if queenPos == 0 then + print "It looks like I have won by forfeit." + return + end if + + // computer move + queenPos = getComputerMove(queenPos) + print "Computer moves to square " + queenPos + if queenPos == 158 then + print + print "Nice try, but it looks like I have won." + return + end if + + // human move + queenPos = getHumanMove(queenPos) + if queenPos == 158 then + print + print "C O N G R A T U L A T I O N S . . ." + print + print "You have won--very well played." + print "It looks like I have met my match." + print "Thanks for playing---I can't win all the time." + return + end if + end while +end function + +// Main program +if getYesNo("Do you want instructions") == "yes" then printDirections +while true + printCoordinates + playGame + print + if getYesNo("Anyone else care to try") == "no" then break +end while +print +print "OK --- Thanks again." diff --git a/00_Alternate_Languages/73_Reverse/MiniScript/README.md b/00_Alternate_Languages/73_Reverse/MiniScript/README.md new file mode 100644 index 000000000..18812f302 --- /dev/null +++ b/00_Alternate_Languages/73_Reverse/MiniScript/README.md @@ -0,0 +1,16 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + + miniscript reverse.ms + +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + + load "reverse" + run diff --git a/00_Alternate_Languages/73_Reverse/MiniScript/reverse.ms b/00_Alternate_Languages/73_Reverse/MiniScript/reverse.ms new file mode 100644 index 000000000..9d7396cf3 --- /dev/null +++ b/00_Alternate_Languages/73_Reverse/MiniScript/reverse.ms @@ -0,0 +1,71 @@ +num = 9 + +reverse = function(i) + if i == null then return i + ret = [] + for item in i + ret.insert(0,item) + end for + return ret +end function + +showRules = function + print + print "This is the game of 'Reverse'. To win, all you have" + print "to do is arrange a list of numbers (1 through " + num + ")" + print "in numerical order from left to right. To move, you" + print "tell me how many numbers (counting from the left) to" + print "reverse. For example, if the current list is:" + print; print "2 3 4 5 1 6 7 8 9" + print; print "and you reverse 4, the result will be:" + print; print "5 4 3 2 1 6 7 8 9" + print; print "Now if reverse 5, you win!" + print; print "1 2 3 4 5 6 7 8 9" + print + print "No doubt you will like this game, but" + print "if you want to quit, reverse 0 (zero)." + print + return +end function + +printState = function + print;print digits.join(" "); print +end function + +print " " * 32 + "Reverse" +print " " * 15 + "Creative Computing Morristown, New Jersey" +print; print; print +print "Reverse -- a game of skill" +print + +ans = input("Do you want the rules? ") + " " +if ans != null and ans[0].lower == "y" then showRules + +while true + turns = 0 + digits = range(1, num) + digits.shuffle + print;print "Here we go ... the list is:" + while true + printState + amt = input("How many shall I reverse? ").val + if amt == null or amt == 0 then break + + if amt > num then + print "OOPS! Too many! I can reverse at most " + num + else + turns += 1 + digits = reverse(digits[:amt]) + digits[amt:] + end if + if digits == range(1,num) then + printState + print "You won it in " + turns + " moves!!" + break + end if + end while + print + ans = input("Try again (YES or NO)? ") + " " + print + if ans == null or ans[0].lower != "y" then break +end while +print "O.K. Hope you had fun!!" diff --git a/00_Alternate_Languages/74_Rock_Scissors_Paper/MiniScript/README.md b/00_Alternate_Languages/74_Rock_Scissors_Paper/MiniScript/README.md new file mode 100644 index 000000000..bfec2da94 --- /dev/null +++ b/00_Alternate_Languages/74_Rock_Scissors_Paper/MiniScript/README.md @@ -0,0 +1,21 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +0. Try-It! Page: +Go to https://miniscript.org/tryit/, clear the sample code from the code editor, and paste in the contents of rockscissors.ms. Then click the "Run Script" button. Program output (and input) will appear in the green-on-black terminal display to the right of or below the code editor. + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: +``` + miniscript rockscissors.ms +``` + +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: +``` + load "rockscissors" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/74_Rock_Scissors_Paper/MiniScript/rockscissors.ms b/00_Alternate_Languages/74_Rock_Scissors_Paper/MiniScript/rockscissors.ms new file mode 100644 index 000000000..322e354b6 --- /dev/null +++ b/00_Alternate_Languages/74_Rock_Scissors_Paper/MiniScript/rockscissors.ms @@ -0,0 +1,40 @@ +print " "*21 + "Game of Rock, Scissors, Paper" +print " "*15 + "Creative Computing Morristown New Jersey" +print; print; print + +while true + numGames = input("How many games? ").val + if 0 < numGames < 11 then break + print "Sorry, but we aren't allowed to play that many." +end while + +computerWins = 0 +playerWins = 0 +for game in range(1, numGames) + print; print "Game number " + game + myChoice = floor(rnd*3 + 1) + while true + print "3=Rock...2=Scissors...1=Paper" + playerChoice = input("1...2...3...What's your choice? ").val + if [1,2,3].indexOf(playerChoice) != null then break + print "Invalid." + end while + print "This is my choice..." + print ["...Paper", "...Scissors", "...Rock"][myChoice-1] + diff = myChoice - playerChoice + if diff == 0 then + print "Tie game. No winner." + else if diff == 1 or diff == -2 then + print "Wow! I win!!!" + computerWins += 1 + else + print "You win!!!" + playerWins += 1 + end if +end for + +print; print "Here is the final game score:" +print "I have won " + computerWins + " game(s)." +print "You have won " + playerWins + " game(s)." +print "And " + (numGames - computerWins - playerWins) + " game(s) ended in a tie." +print; print "Thanks for playing!!" diff --git a/00_Alternate_Languages/74_Rock_Scissors_Paper/nim/rockscissors.nim b/00_Alternate_Languages/74_Rock_Scissors_Paper/nim/rockscissors.nim new file mode 100644 index 000000000..3c0cda0c5 --- /dev/null +++ b/00_Alternate_Languages/74_Rock_Scissors_Paper/nim/rockscissors.nim @@ -0,0 +1,68 @@ +import std/[random,strformat,strutils] + +type + symbol = enum + PAPER = 1, SCISSORS = 2, ROCK = 3 + +var + cpuChoice, playerChoice, turns: int + cpuWins, playerWins, ties: int = 0 + +randomize() + +# Function: player makes a choice +proc choose(): int = + echo "3=ROCK...2=SCISSORS...1=PAPER...WHAT'S YOUR CHOICE?" + result = readLine(stdin).parseInt() + +# Function: determine the outcome +proc outcome(p: symbol, c: symbol): string = + if p == c: + ties += 1 + result = "TIE GAME. NO WINNER." + else: + const + winTable = [ + PAPER: (ROCK, "COVERS"), + SCISSORS: (PAPER, "CUTS"), + ROCK: (SCISSORS, "CRUSHES") + ] + let (winCond, winVerb) = winTable[p] + if winCond == c: + playerWins += 1 + result = fmt"{p} {winVerb} {c}. YOU WIN." + else: + let (_, winVerb) = winTable[c] + cpuWins += 1 + result = fmt"{c} {winVerb} {p}. I WIN." + +# Start the game +echo spaces(21), "GAME OF ROCK, SCISSORS, PAPER" +echo spaces(15), "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" +echo "\n" +echo "HOW MANY GAMES?" +turns = readLine(stdin).parseInt() +while turns > 10: + echo "SORRY, BUT WE AREN'T ALLOWED TO PLAY THAT MANY." + turns = readLine(stdin).parseInt() + +# Play the game +for i in 1..turns: + echo "" + echo "GAME NUMBER ", i + playerChoice = choose() + while playerChoice != 1 and playerChoice != 2 and playerChoice != 3: + echo "INVALID" + playerChoice = choose() + cpuChoice = rand(1..3) # match against range in symbol + echo "THIS IS MY CHOICE... ", symbol(cpuChoice) + echo outcome(symbol(playerChoice), symbol(cpuChoice)) + +# Results +echo "" +echo "HERE IS THE FINAL GAME SCORE:" +echo "I HAVE WON ", cpuWins," GAME(S)." +echo "YOU HAVE WON ", playerWins," GAME(S)." +echo "AND ", ties," GAME(S) ENDED IN A TIE." +echo "" +echo "THANKS FOR PLAYING!!" diff --git a/00_Alternate_Languages/75_Roulette/MiniScript/README.md b/00_Alternate_Languages/75_Roulette/MiniScript/README.md new file mode 100644 index 000000000..4a2e49289 --- /dev/null +++ b/00_Alternate_Languages/75_Roulette/MiniScript/README.md @@ -0,0 +1,18 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: +``` + miniscript roulette.ms +``` + +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: +``` + load "roulette" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/75_Roulette/MiniScript/roulette.ms b/00_Alternate_Languages/75_Roulette/MiniScript/roulette.ms new file mode 100644 index 000000000..60e7087bc --- /dev/null +++ b/00_Alternate_Languages/75_Roulette/MiniScript/roulette.ms @@ -0,0 +1,200 @@ +print " "*32 + "Roulette" +print " "*15 + "Creative Computing Morristown New Jersey" +print; print; print + +if version.hostName == "Mini Micro" then + import "dateTime" + globals.date = dateTime.str(dateTime.now, "MMM d, yyyy") +else + globals.date = input("Enter the current date (as in 'Jan 23, 1979') - ") +end if + +yn = input("Do you want instructions? ").lower + " " +if yn[0] != "n" then + print + print "This is the betting layout" + print " (*=Red)" + print + print " 1* 2 3*" + print " 4 5* 6 " + print " 7* 8 9*" + print "10 11 12*" + print "---------------" + print "13 14* 15 " + print "16* 17 18*" + print "19* 20 21*" + print "22 23* 24 " + print "---------------" + print "25* 26 27*" + print "28 29 30*" + print "31 32* 33 " + print "34* 35 36*" + print "---------------" + print " 00 0 " + print + input "(Press Return at each pause.)" + print + print "Types of Bets" + print + print "The numbers 1 to 36 signify a straight bet" + print "on that number." + print "These pay off 35:1" + print + print "The 2:1 bets are:" + print " 37) 1-12 40) first column" + print " 38) 13-24 41) second column" + print " 39) 25-36 42) third column" + print + print "The even money bets are:" + print " 43) 1-18 46) odd" + print " 44) 19-36 47) red" + print " 45) even 48) black" + print + print " 49)0 and 50)00 pay off 35:1" + print " NOTE: 0 and 00 do not count under any" + print " bets except their own." + input + print "When I ask for each bet, type the number" + print "and the amount, separated by a comma." + print "For example: to bet $500 on black, type 48,500" + print "when I ask for a bet." + print + print "The minimum bet is $5, the maximum is $500." + print +end if + +redNumbers = [1,3,5,7,9,12,14,16,18,19,21,23,25,27,30,32,34,36] + +// function to convert a number 1-38 to a number/description, like "00" +// or "7 RED" +numDesc = function(number) + if number == 37 then return "0" + if number == 38 then return "00" + s = str(number) + if redNumbers.indexOf(number) == null then + return s + " BLACK" + else + return s + " RED" + end if +end function + +// function to calculate the payout factor (positive if player wins, +// or -1 if player loses) for the given bet and actual spin. +payoutFactor = function(bet, spin) + if bet <= 36 then // straight bet, pays 35:1 + if bet == spin then return 35 else return -1 + else if bet == 49 then // 0, pays 35:1 + if spin == 37 then return 35 else return -1 + else if bet == 50 then // 00, pays 35:1 + if spin == 38 then return 35 else return -1 + else if bet == 37 then // 1-12, pays 2:1 + if 1 <= spin <= 12 then return 2 else return -1 + else if bet == 38 then // 13-24, pays 2:1 + if 13 <= spin <= 24 then return 2 else return -1 + else if bet == 39 then // 25-36, pays 2:1 + if 25 <= spin <= 36 then return 2 else return -1 + else if bet == 40 then // first column, pays 2:1 + if spin % 3 == 1 then return 2 else return -1 + else if bet == 41 then // second column, pays 2:1 + if spin % 3 == 2 then return 2 else return -1 + else if bet == 42 then // third column, pays 2:1 + if spin % 3 == 0 then return 2 else return -1 + else if bet == 43 then // 1-18, even money + if 1 <= spin <= 18 then return 1 else return -1 + else if bet == 44 then // 19-36, even money + if 19 <= spin <= 36 then return 1 else return -1 + else if bet == 45 then // even number, even money + if spin % 2 == 0 then return 1 else return -1 + else if bet == 46 then // odd number, even money + if spin % 2 == 1 then return 1 else return -1 + else if bet == 47 then // red, even money + if redNumbers.indexOf(spin) != null then return 1 else return -1 + else if bet == 48 then // black, even money + if redNumbers.indexOf(spin) == null then return 1 else return -1 + end if + print "Invalid bet " + bet + " in payoutFactor" +end function + +playerCash = 1000 +houseCash = 100000 +x = [0] * 38 // (keeps track of how often each number comes up) +while playerCash > 0 + // Get the player's bets + numBets = input("How many bets? ").val + if numBets < 1 then continue + bets = []; amounts = [] + for i in range(1, numBets) + while true + s = input("Number " + i + "? ").replace(",", " ").replace(" ", " ").split + if s.len != 2 then continue + bet = s[0].val; amount = s[1].val + if bets.indexOf(bet) != null then + print "You made that bet once already,dum-dum" + continue + end if + if 1 <= bet <= 50 and 5 <= amount <= 500 then + bets.push bet + amounts.push amount + break + end if + end while + end for + + // Spin the wheel! + print "Spinning" + print + print + spin = floor(38 * rnd + 1) + x[spin] += 1 + print numDesc(spin) + print + + // Now, pay out the bets + for i in bets.indexes + f = payoutFactor(bets[i], spin) + if f > 0 then + print "You win " + f*amounts[i] + " on bet " + i + else + print "You lose " + (-f)*amounts[i] + " on bet " + i + end if + playerCash += f * amounts[i] + houseCash -= f * amounts[i] + end for + print + print "Totals: ME YOU" + print " " + (houseCash+" "*12)[:12] + playerCash + if playerCash > 0 and houseCash > 0 then + yn = input("Again? ").lower + " " + if yn[0] != "y" then break + end if +end while + +if houseCash < 1 then + print "You broke the house!" + playerCash = 101000 +end if +if playerCash < 1 then + print "Oops! You just spent your last dollar!" + print "Thanks for your money." + print "I'll use it to buy a solid gold roulette wheel" + print +else + name = input("To whom shall I make the check? ") + print + print "-"*68 + print " "*55 + "Check No. " + floor(rnd*100) + print + print " "*(67 - date.len) + date + print + print + print "Pay to the order of-----" + name + "-----$ " + playerCash + print + print + print " "*10 + "The Memory Bank of New York" + print + print " "*35 + "The Computer" + print " "*35 + "----------X-----" + print + print "-"*68 + print "Come back soon!" +end if diff --git a/00_Alternate_Languages/76_Russian_Roulette/MiniScript/README.md b/00_Alternate_Languages/76_Russian_Roulette/MiniScript/README.md new file mode 100644 index 000000000..79d028a92 --- /dev/null +++ b/00_Alternate_Languages/76_Russian_Roulette/MiniScript/README.md @@ -0,0 +1,21 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +0. Try-It! Page: +Go to https://miniscript.org/tryit/, clear the sample code from the code editor, and paste in the contents of russianroulette.ms. Then click the "Run Script" button. Program output (and input) will appear in the green-on-black terminal display to the right of or below the code editor. + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: +``` + miniscript russianroulette.ms +``` + +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: +``` + load "russianroulette" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/76_Russian_Roulette/MiniScript/russianroulette.ms b/00_Alternate_Languages/76_Russian_Roulette/MiniScript/russianroulette.ms new file mode 100644 index 000000000..3eb896f94 --- /dev/null +++ b/00_Alternate_Languages/76_Russian_Roulette/MiniScript/russianroulette.ms @@ -0,0 +1,37 @@ +print " "*28 + "Russian Roulette" +print " "*15 + "Creative Computing Morristown New Jersey" +print; print; print + +print "This is a game of >>>>>>>>>>Russian Roulette." + +while true + print; print "Here is a revolver." + print "Type '1' to spin chamber and pull trigger." + print "Type '2' to give up." + print "GO" + + n = 0 + while n < 10 + inp = input("? ").val + if inp == 2 then + print " CHICKEN!!!!!" + break + else if rnd > 0.833333 then + print " BANG!!!!! You're dead!" + print "Condolences will be sent to your relatives." + break + else + n += 1 + print "- CLICK -" + print + end if + end while + + if n >= 10 then + print "You win!!!!!" + print "Let someone else blow his brains out." + else + print; print; print + print "...Next victim..." + end if +end while diff --git a/00_Alternate_Languages/77_Salvo/MiniScript/README.md b/00_Alternate_Languages/77_Salvo/MiniScript/README.md new file mode 100644 index 000000000..d31694466 --- /dev/null +++ b/00_Alternate_Languages/77_Salvo/MiniScript/README.md @@ -0,0 +1,18 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: +``` + miniscript salvo.ms +``` + +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: +``` + load "salvo" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/77_Salvo/MiniScript/salvo.ms b/00_Alternate_Languages/77_Salvo/MiniScript/salvo.ms new file mode 100644 index 000000000..e7c2796e7 --- /dev/null +++ b/00_Alternate_Languages/77_Salvo/MiniScript/salvo.ms @@ -0,0 +1,240 @@ +print " "*33 + "Salvo" +print " "*15 + "Creative Computing Morristown New Jersey" +print; print; print + +import "listUtil" + +Ship = {} +Ship.name = "Ship" +Ship.positions = null // list of [x,y] coordinates for this ship +Ship.shots = 1 // how many shots this ship provides +Ship.hitPoints = 1 // how many hits this ship can take before sinking +Ship.make = function(name, shots=1, size=2) + result = new Ship + result.name = name + result.shots = shots + result.positions = [] + result.hitPoints = size + return result +end function + +Board = {} +Board.ships = null // list of Ship instances +Board.splash = null // 2D array of: none, or turn on which it was hit +Board.shipAt = function(xy) + for ship in self.ships + if ship.positions.contains(xy) then return ship + end for +end function +Board.isEmptySpot = function(xy) + return 0 < xy[0] < 11 and 0 < xy[1] < 11 and self.shipAt(xy) == null +end function +Board.make = function + result = new Board + result.ships = [] + result.splash = list.init2d(11, 11) + return result +end function +Board.totalShots = function + sum = 0 + for ship in self.ships + sum += ship.shots + end for + return sum +end function + +computerBoard = Board.make +playerBoard = Board.make + +directions = [[-1,-1], [-1,0], [-1,1], [0,-1], [0,1], [1,-1], [1,0], [1,1]] + +randomPosition = function + return [1 + floor(rnd*10), 1 + floor(rnd*10)] +end function + +inputCoords = function(prompt="?") + while true + inp = input(prompt + "? ").replace(",", " ").replace(" ", " ").split + if inp.len != 2 then + print "Please enter coordinates such as: 5,3" + else + x = inp[0].val + y = inp[1].val + if 0 < x < 11 and 0 < y < 11 then return [x,y] + print "X and Y coordinates must be in the range 1-10." + end if + end while +end function + +inBounds = function(pos) + return 0 < pos[0] < 11 and 0 < pos[1] < 11 +end function + +placeComputerShips = function + placeOne = function(ship) + while true + pos = randomPosition + dir = directions.any + ok = true + p = pos[:] + for i in range(0, ship.hitPoints - 1) + if not computerBoard.isEmptySpot(p) then ok = false + p.add dir + end for + if ok then break + end while + for i in range(0, ship.hitPoints - 1) + ship.positions.push pos[:] + pos.add dir + end for + computerBoard.ships.push ship + end function + placeOne Ship.make("Battleship", 3, 5) + placeOne Ship.make("Cruiser", 2, 3) + placeOne Ship.make("Destroyer", 1, 2) + placeOne Ship.make("Destroyer", 1, 2) +end function + +placePlayerShips = function + placeOne = function(ship) + print ship.name + playerBoard.ships.push ship + // Note: like the original BASIC program, we do no validation on + // the input other than making sure it is in range. So you can + // have a ship scattered all over the map, have ships overlap, etc. + for i in range(1, ship.hitPoints) + ship.positions.push inputCoords + end for + end function + print "Enter coordinates for..." + placeOne Ship.make("Battleship", 3, 5) + placeOne Ship.make("Cruiser", 2, 3) + placeOne Ship.make("Destroyer", 1, 2) + placeOne Ship.make("Destroyer", 1, 2) +end function + +printComputerShips = function + for ship in computerBoard.ships + print ship.name + for pos in ship.positions + print " " + pos.join(" ") + end for + end for +end function + +doPlayerTurn = function(turnNum = 1) + shots = playerBoard.totalShots + print "You have " + shots + " shot" + "s"*(shots!=1) + "." + if shots < 1 then + print "I have won." + exit + end if + hits = [] + for i in range(1, shots) + while true + pos = inputCoords + prevHitOnTurn = computerBoard.splash[pos[0]][pos[1]] + if prevHitOnTurn == null then break + print "You shot there before on turn " + prevHitOnTurn + end while + computerBoard.splash[pos[0]][pos[1]] = turnNum + hit = computerBoard.shipAt(pos) + if hit then hits.push hit + end for + for hit in hits + print "You hit my " + hit.name + "." + hit.hitPoints -= 1 + if hit.hitPoints == 0 then + print "...and sank it!" // (not in original BASIC program) + computerBoard.ships.removeVal hit + end if + end for +end function + +pickShot = function + // Pick a spot for the computer to shoot at. We'll do this by + // computing a "neighbor score" for each spot: the number of + // neighboring spots that (1) we have previously hit, and (2) + // contain an enemy ship. Then we'll pick randomly from the + // set of spots with the highest neighbor score. + bestScore = 0 + spots = [] + for i in range(1,10) + for j in range(1,10) + pos = [i,j] + if playerBoard.splash[pos[0]][pos[1]] then continue + score = 0 + for dir in directions + n = pos.plus(dir) + if inBounds(n) and playerBoard.splash[n[0]][n[1]] and playerBoard.shipAt(n) then + score += 1 + end if + end for + if score > bestScore then + bestScore = score + spots = [pos] + else if score == bestScore then + spots.push pos + end if + end for + end for + return spots.any +end function + +doComputerTurn = function(turnNum = 1) + shots = computerBoard.totalShots + print "I have " + shots + " shot" + "s"*(shots!=1) + "." + if shots < 1 then + print "You have won." + exit + end if + hits = [] + for i in range(1, shots) + pos = pickShot + playerBoard.splash[pos[0]][pos[1]] = turnNum + hit = playerBoard.shipAt(pos) + if hit then hits.push hit + if seeComputerShots then print " " + pos.join(" ") + end for + for hit in hits + print "I hit your " + hit.name + "." + hit.hitPoints -= 1 + if hit.hitPoints == 0 then + print "...and sank it!" // (not in original BASIC program) + playerBoard.ships.removeVal hit + end if + end for +end function + +// Main Program + +placeComputerShips +placePlayerShips +while true + yn = input("Do you want to start? ").lower + if yn == "where are your ships?" then + printComputerShips + else if yn and (yn[0] == "y" or yn[0] == "n") then + break + end if +end while +startWithPlayer = (yn[0] == "y") +while true + yn = input("Do you want to see my shots? ").lower + if yn and (yn[0] == "y" or yn[0] == "n") then break +end while +seeComputerShots = (yn[0] == "y") + +turnNumber = 1 +while true + print + print "Turn " + turnNumber + if startWithPlayer then + doPlayerTurn turnNumber + doComputerTurn turnNumber + else + doComputerTurn turnNumber + doPlayerTurn turnNumber + end if + turnNumber += 1 +end while \ No newline at end of file diff --git a/00_Alternate_Languages/78_Sine_Wave/MiniScript/README.md b/00_Alternate_Languages/78_Sine_Wave/MiniScript/README.md new file mode 100644 index 000000000..a17e5cf14 --- /dev/null +++ b/00_Alternate_Languages/78_Sine_Wave/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript sinewave.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "sinewave" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/78_Sine_Wave/MiniScript/sinewave.ms b/00_Alternate_Languages/78_Sine_Wave/MiniScript/sinewave.ms new file mode 100644 index 000000000..da3e99a77 --- /dev/null +++ b/00_Alternate_Languages/78_Sine_Wave/MiniScript/sinewave.ms @@ -0,0 +1,15 @@ +print " "*30 + "SINE WAVE" +print " "*15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" +print; print; print; print; print +// Remarkable program by David Ahl, ported +// from BASIC to MiniScript by Joe Strout + +B = 0 +// start long loop +for t in range(0, 40, 0.25) + A = floor(26 + 25*sin(t)) + print " "*A, "" + if not B then print "CREATIVE" else print "COMPUTING" + B = not B + wait 0.01 +end for diff --git a/00_Alternate_Languages/79_Slalom/MiniScript/README.md b/00_Alternate_Languages/79_Slalom/MiniScript/README.md new file mode 100644 index 000000000..c954f090c --- /dev/null +++ b/00_Alternate_Languages/79_Slalom/MiniScript/README.md @@ -0,0 +1,18 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: +``` + miniscript slalom.ms +``` + +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: +``` + load "slalom" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/79_Slalom/MiniScript/slalom.ms b/00_Alternate_Languages/79_Slalom/MiniScript/slalom.ms new file mode 100644 index 000000000..41bb0b829 --- /dev/null +++ b/00_Alternate_Languages/79_Slalom/MiniScript/slalom.ms @@ -0,0 +1,170 @@ +print " "*33 + "Slalom" +print " "*15 + "Creative Computing Morristown New Jersey" +print; print; print + +gateSpeeds = [14,18,26,29,18,25,28,32,29,20,29,29,25,21,26,29,20,21,20, + 18,26,25,33,31,22] + +medals = {} +medals.gold = 0 +medals.silver = 0 +medals.bronze = 0 + +while true + qtyGates = input("How many gates does this course have (1 to 25)? ").val + if qtyGates > 25 then + print "25 is the limit." + qtyGates = 25 + end if + if qtyGates >= 1 then break + print "Try again," +end while + +print "Type ""ins"" for instructions" +print "Type ""max"" for approximate maximum speeds" +print "Type ""run"" for the beginning of the race" +while true + cmd = input("Command--").lower + if cmd == "ins" then + print + print "*** Slalom: This is the 1976 Winter Olympic Giant Slalom. You are" + print " the American team's only hope of a gold medal." + print + print " 0 -- type this if you want to see how long you've taken." + print " 1 -- type this if you want to speed up a lot." + print " 2 -- type this if you want to speed up a little." + print " 3 -- type this if you want to speed up a teensy." + print " 4 -- type this if you want to keep going the same speed." + print " 5 -- type this if you want to check a teensy." + print " 6 -- type this if you want to check a little." + print " 7 -- type this if you want to check a lot." + print " 8 -- type this if you want to cheat and try to skip a gate." + print + print " The place to use these options is when the computer asks:" + print + print "Option?" + print + print " Good luck!" + print + else if cmd == "max" then + print "GATE MAX" + print " # M.P.H." + print "-----------" + for i in range(1, qtyGates) + print " " + i + " "*(i<10) + " " + gateSpeeds[i-1] + end for + else if cmd == "run" then + break + end if +end while + +while true + skill = input("Rate yourself as a skier, (1=worst, 3=best)? ").val + if 1 <= skill <= 3 then break + print "The bounds are 1-3" +end while + +doOneRace = function + print "The starter counts down...5...4...3...2...1..GO!" + time = 0 + speed = floor(rnd * 9 + 9) + print + print "You're off!" + gate = 0 + while gate+1 <= qtyGates + gate += 1 + gateSpeed = gateSpeeds[(gate-1) % gateSpeeds.len] + print + print "Here comes gate #" + gate + ":" + print speed + " M.P.H." + prevSpeed = speed + while true + opt = input("Option? ").val + if opt == 0 then + print "You've taken " + time + " seconds." + else if opt < 1 or opt > 8 then + print "What?" + else + break + end if + end while + if opt == 1 then + speed += floor(rnd*(10-5)+5) + else if opt == 2 then + speed += floor(rnd*(5-3)+3) + else if opt == 3 then + speed += floor(rnd*(4-1)+1) + else if opt == 4 then + // (no change) + else if opt == 5 then + speed -= floor(rnd*(4-1)+1) + else if opt == 6 then + speed -= floor(rnd*(5-3)+3) + else if opt == 7 then + speed -= floor(rnd*(10-5)+5) + else if opt == 8 then + print "***CHEAT" + if rnd < 0.7 then + print "An official caught you!" + print "You took " + round(time+rnd, 3) + " seconds." + break + else + print "You made it!" + char(7) + time += 1.5 + continue + end if + end if + print speed + " M.P.H." + if speed > gateSpeed then + if rnd < (speed - gateSpeed)*0.1 + 0.2 then + msg = "You went over the maximum speed and " + if rnd < 0.5 then msg += "snagged a flag!" else msg += "wiped out!" + print msg + print "You took " + round(time+rnd, 3) + " seconds." + else + print "You went over the maximum speed and made it!" + end if + else if speed > gateSpeed - 1 then + print "Close one!" + end if + if speed < 7 then + print "Let's be realistic, OK? Let's go back and try again..." + speed = prevSpeed + gate -= 1 + continue + end if + time += gateSpeed - speed + 1 + if speed > gateSpeed then time += 0.5 + end while + + print + print "You took " + round(time+rnd, 3) + " seconds." + avg = time / qtyGates + if avg < 1.5 - (skill * 0.1) then + print "You won a gold medal!" + medals.gold += 1 + else if avg < 2.9 - (skill * 0.1) then + print "You won a silver medal" + medals.silver += 1 + else if avg < 4.4 - (skill * 0.01) then + print "You won a bronze medal" + medals.bronze += 1 + end if +end function + +while true + doOneRace + while true + yesno = input("Do you want to race again? ").lower + " " + if yesno[0] == "y" or yesno[0] == "n" then break + print "Please type 'yes' or 'no'" + end while + if yesno[0] == "n" then break + print +end while + +print +print "Thanks for the race" +if medals.gold then print "Gold medals: " + medals.gold +if medals.silver then print "Silver medals: " + medals.silver +if medals.bronze then print "Bronze medals: " + medals.bronze diff --git a/00_Alternate_Languages/79_Slalom/slalom.bas b/00_Alternate_Languages/79_Slalom/slalom.bas index b46cc39be..47c6d8010 100644 --- a/00_Alternate_Languages/79_Slalom/slalom.bas +++ b/00_Alternate_Languages/79_Slalom/slalom.bas @@ -58,7 +58,7 @@ 825 PRINT "*** SLALOM: THIS IS THE 1976 WINTER OLYMPIC GIANT SLALOM. YOU ARE" 830 PRINT " THE AMERICAN TEAM'S ONLY HOPE OF A GOLD MEDAL." 840 PRINT -845 PRINT " 0 -- TYPE THIS IS YOU WANT TO SEE HOW LONG YOU'VE TAKEN." +845 PRINT " 0 -- TYPE THIS IF YOU WANT TO SEE HOW LONG YOU'VE TAKEN." 850 PRINT " 1 -- TYPE THIS IF YOU WANT TO SPEED UP A LOT." 860 PRINT " 2 -- TYPE THIS IF YOU WANT TO SPEED UP A LITTLE." 870 PRINT " 3 -- TYPE THIS IF YOU WANT TO SPEED UP A TEENSY." diff --git a/00_Alternate_Languages/80_Slots/MiniScript/README.md b/00_Alternate_Languages/80_Slots/MiniScript/README.md new file mode 100644 index 000000000..a8b56b3d6 --- /dev/null +++ b/00_Alternate_Languages/80_Slots/MiniScript/README.md @@ -0,0 +1,18 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: +``` + miniscript slots.ms +``` + +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: +``` + load "slots" + run +``` diff --git a/00_Alternate_Languages/80_Slots/MiniScript/slots.ms b/00_Alternate_Languages/80_Slots/MiniScript/slots.ms new file mode 100644 index 000000000..310ff95e7 --- /dev/null +++ b/00_Alternate_Languages/80_Slots/MiniScript/slots.ms @@ -0,0 +1,108 @@ +print " "*30 + "Slots" +print " "*15 + "Creative Computing Morristown New Jersey" +print; print; print + +// PRODUCED BY FRED MIRABELLE AND BOB HARPER ON JAN 29, 1973 +// IT SIMULATES THE SLOT MACHINE. +// (Ported to MiniScript by Joe Strout on Oct 04, 2023) + +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." + +symbols = ["BAR", "BELL", "ORANGE", "LEMON", "PLUM", "CHERRY"] + + +winTriple = function(symbol, bet) + if symbol == "BAR" then + print "***JACKPOT***" + globals.profit += 101 * bet + else + print "**TOP DOLLAR**" + globals.profit += 11 * bet + end if + print "You won!" +end function + +winDouble = function(symbol, bet) + if symbol == "BAR" then + print "*DOUBLE BAR*" + globals.profit += 6 * bet + else + print "Double!" + globals.profit += 3 * bet + end if + print "You won!" +end function + +lose = function(bet) + print "You lost." + globals.profit -= bet +end function + +calcWinLoss = function(spun, bet) + if spun[0] == spun[1] then + if spun[0] == spun[2] then + winTriple spun[0], bet + else + winDouble spun[0], bet + end if + else if spun[0] == spun[2] then + winDouble spun[0], bet + else if spun[1] == spun[2] then + winDouble spun[1], bet + else + lose bet + end if +end function + +ringBells = function(qty=5) + // I believe all the obnoxious beeping was to slow down the game + // and build suspense as each "wheel" appears. Our version: + wait 0.1 + for i in range(1, qty) + print char(7), "" + wait 0.05 + end for +end function + +// Main program +profit = 0 +while true + print + + // Get bet + while true + bet = input("Your bet? ").val + if 1 <= bet <= 100 then break + if bet < 1 then print "Minimum bet is $1" else print "House limits are $100" + end while + + // Spin 3 wheels (randomly picking a symbol for each one) + spun = [] + spun.push symbols[rnd * symbols.len] + spun.push symbols[rnd * symbols.len] + spun.push symbols[rnd * symbols.len] + print + ringBells 10; print spun[0], " " + ringBells 5; print spun[1], " " + ringBells 5; print spun[2] + print + + // Calculate and display win/loss + wait 0.5 + calcWinLoss spun, bet + + // Show new state, and maybe play again + print "Your standings are $ " + profit + yn = input("Again? ").lower + " " + if yn[0] != "y" then break +end while + +if profit == 0 then + print "Hey, you broke even." +else if profit > 0 then + print "Collect your winnings from the H&M cashier." +else + print "Pay up! Please leave your money on the terminal." +end if diff --git a/00_Alternate_Languages/81_Splat/MiniScript/README.md b/00_Alternate_Languages/81_Splat/MiniScript/README.md new file mode 100644 index 000000000..2d133f871 --- /dev/null +++ b/00_Alternate_Languages/81_Splat/MiniScript/README.md @@ -0,0 +1,18 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: +``` + miniscript splat.ms +``` + +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: +``` + load "splat" + run +``` diff --git a/00_Alternate_Languages/81_Splat/MiniScript/splat.ms b/00_Alternate_Languages/81_Splat/MiniScript/splat.ms new file mode 100644 index 000000000..d38ddd237 --- /dev/null +++ b/00_Alternate_Languages/81_Splat/MiniScript/splat.ms @@ -0,0 +1,133 @@ +within5pct = function(n) + return n * (1 + rnd / 20 - rnd / 20) +end function + +splatMsg = ["Requiescat in pace.", "May the Angel of Heaven lead you into Paradise.", + "Rest in piece.", "Son-Of-A-Gun.", "#$%&&%!$", + "A kick in the pants is a boost if you're headed right.", + "Hmm. Should have picked a shorter time.", "Mutter. Mutter. Mutter.", + "Pushing up daisies.", "Easy come, easy go."] + +history = [] +//clear // (works on Mini Micro only) +print " " * 33 + "Splat" +print " " * 15 + "Creative Computing Morristown, New Jersey" +print;print;print +print "Welcome to 'Splat' -- the game that simulates a parachute" +print "jump. Try to open your chute at the last possible" +print "moment without going splat." +ans = "y" +while ans == "y" + print;print + + distance = floor(9001 * rnd) + 1000 + + ans = input("Select your own terminal velocity (Yes or No)? ") + " " + if ans[0].lower == "y" then + terminalVel = input("What terminal velocity (mi/hr)? ").val + else + terminalVel = floor(1000 * rnd) + print "Ok. Terminal velocity = " + terminalVel + "mi/hr" + end if + + terminalVel = terminalVel * 5280/3600 + velocity = within5pct(terminalVel) + + ans = input("Want to select acceleration due to gravity (Yes or No)? ") + " " + if ans[0].lower == "y" then + acceleration = input("What acceleration (ft/sec/sec)? ").val + acceleration = within5pct(acceleration) + else + bodies = ["Mercury", "Venus", "Earth", "the moon", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune", "the Sun"] + gravity = [12.2, 28.3,32.16,5.15,12.5,85.2,37.6,33.8,39.6,896] + + initialStmt = ["Fine. You're on ", "All right. You're on ", "Then you're on "] + + i = floor(rnd * 10) + acceleration = gravity[i] + stmt = initialStmt[i%3] + bodies[i] + ". Acceleration=" + acceleration + " ft/sec/sec." + print stmt + end if + + actAccel = within5pct(acceleration) + print + print " Altitude = " + distance + " ft" + print " Term. Velocity = " + terminalVel + " ft/sec +/-5%" + print " Acceleration = " + acceleration + " ft/sec/sec +/-5%" + print "Set the timer for your freefall." + sec = input("How many seconds? ").val + + print "Here we go."; print + + print "Time (sec)" + char(9) + "Dist to Fall (ft)" + print "==========" + char(9) + "=================" + termVelHit = false + for i in range(0, sec, sec/8) + sec = velocity/actAccel + if i <= sec and termVelHit == false then + dist = distance - ((actAccel/2)*i^2) + else if termVelHit == false then + termVelHit = true + print "Terminal velocity reached a T plus " + velocity/actAccel + " seconds." + end if + if termVelHit then + dist = distance - ((velocity^2/(2*actAccel))+(velocity*(i-sec))) + end if + + if dist <= 0 then break + print (" " + i + " " * 9)[:9] + char(9) + " " + dist + end for + + if dist > 0 then + print "Chute open" + history.push(dist) + numJumps = history.len + numLowerJumps = 0 + for d in history + numLowerJumps += (dist <= d) + end for + + jumpDiff = numJumps - numLowerJumps + if numJumps < 4 then + ordinal = ["1st", "2nd", "3rd"] + print "Amazing!! Not bad for your " + ordinal[numJumps-1] + " successful jump!!!" + else if jumpDiff <= numJumps * 0.10 then + print "Wow! That's some jumping. Of the " + numJumps + " successful jumps" + print "before yours, only " + jumpDiff + " opened their chutes lower than" + print "you did." + else if jumpDiff <= numJumps * 0.25 then + print "Pretty good! " + numJumps + " successful jumps preceded yours and only" + print jumpDiff + " of them got lower than you did before their chute" + print "opened." + else if jumpDiff <= numJumps * 0.50 then + print "Not bad. There have been " + numJumps + " successful jumps before yours." + print "You were beaten out by " + jumpDiff + " of them." + else if jumpDiff <= numJumps * 0.75 then + print "Conservative, aren't you? You ranked only " + jumpDiff + " in the" + print numJumps + " successful jumps before yours." + else if jumpDiff <= numJumps * 0.90 then + print "Humph! Don't you have any sporting blood? There were" + print numJumps + " successful jumps before yours and you came in " + numLowerJumps + " jumps" + print "better than the worst. Shape up!!!" + else + print "Hey! You pulled the rip code much too soon. " + numJumps + " successful" + print "jumps before yours and you came in number " + jumpDiff + "! Get with it." + end if + else if dist <= 0 and not termVelHit then + print (2 * distance / actAccel) ^ .5 + " " * 5 + "Splat" + else if dist <= 0 and termVelHit then + print velocity/actAccel + ((distance - (velocity^2/(2*actAccel)))/velocity) + " " *5 + "Splat" + end if + + if dist <=0 then + splatMsg.shuffle + print splatMsg[0] + print "I'll give you another chance." + end if + + ans = input("Do you want to play again? ") + " " + ans = ans[0].lower +end while + +print "S" * 10 +print \ No newline at end of file diff --git a/00_Alternate_Languages/82_Stars/MiniScript/README.md b/00_Alternate_Languages/82_Stars/MiniScript/README.md new file mode 100644 index 000000000..2e4b6d024 --- /dev/null +++ b/00_Alternate_Languages/82_Stars/MiniScript/README.md @@ -0,0 +1,18 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: +``` + miniscript stars.ms +``` + +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: +``` + load "stars" + run +``` diff --git a/00_Alternate_Languages/82_Stars/MiniScript/stars.ms b/00_Alternate_Languages/82_Stars/MiniScript/stars.ms new file mode 100644 index 000000000..d83d75b74 --- /dev/null +++ b/00_Alternate_Languages/82_Stars/MiniScript/stars.ms @@ -0,0 +1,48 @@ +kMaxNum = 100 +kTries = 7 + +instructions = function + print "I am thinking of a whole number from 1 to " + kMaxNum + print "Try to guess my number. After you guess, I" + print "will output one or more stars (*). The more" + print "stars I type, the closer you are to my number." + print "One star (*) means far away, seven stars (*******)" + print "means really close! You get " + kTries + " guesses." + print +end function + +print " " * 34 + "Stars" +print " " * 15 + "Creative Computing Morristown, New Jersey" +print; print; print + +ans = input("Do you want instructions? ").lower + " " +if ans[0] == "y" then + instructions +end if + +while true + print + print "OK, I am thinking of a number, start guessing." + starNum = floor(rnd * kMaxNum) + 1 + try = 0 + while try < kTries + print + guess = input("Your guess: ").val + + if guess == starNum then + break + else + d = abs(guess - starNum) + print "*" * (7 - floor(log(d,2))) + end if + try += 1 + end while + + if try < kTries then + print "*" * 59 + print "You got it in " + (try + 1) + " guesses! Let's play again." + else + print "Sorry, that's " + try + " guesses. The number was " + starNum + end if + print +end while diff --git a/00_Alternate_Languages/83_Stock_Market/MiniScript/README.md b/00_Alternate_Languages/83_Stock_Market/MiniScript/README.md new file mode 100644 index 000000000..ac3893185 --- /dev/null +++ b/00_Alternate_Languages/83_Stock_Market/MiniScript/README.md @@ -0,0 +1,18 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: +``` + miniscript stockmarket.ms +``` + +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: +``` + load "stockmarket" + run +``` diff --git a/00_Alternate_Languages/83_Stock_Market/MiniScript/stockmarket.ms b/00_Alternate_Languages/83_Stock_Market/MiniScript/stockmarket.ms new file mode 100644 index 000000000..850cb725f --- /dev/null +++ b/00_Alternate_Languages/83_Stock_Market/MiniScript/stockmarket.ms @@ -0,0 +1,199 @@ +print " "*30 + "Stock Market" +print " "*15 + "Creative Computing Morristown New Jersey" +print; print; print + +// Stock Market Simulation -STOCK- +// Revised 8/18/70 (D. Pessel, L. Braun, C. Losik) +// Ported to MiniScript 10/07/23 (J. Strout) + + +// Ask a yes/no question; return true if yes, false if no +yes = function(prompt) + while true + response = input(prompt + " (YES-Type 1, NO-Type 0)? ") + if not response then continue + if response == "1" or response[0].lower == "y" then return true + if response == "0" or response[0].lower == "n" then return false + end while +end function + +printInstructions = function + print; print + print "This program plays the stock market. You will be given" + print "$10,000 and may buy or sell stocks. The stock prices will" + print "be generated randomly and therefore this model does not" + print "represent exactly what happens on the exchange. A table" + print "of available stocks, their prices, and the number of shares" + print "in your portfolio will be printed. Following this, the" + print "initials of each stock will be printed with a question" + print "mark. Here you indicate a transaction. To buy a stock" + print "type +NNN, to sell a stock type -NNN, where NNN is the" + print "number of shares. A brokerage fee of 1% will be charged" + print "on all transactions. Note that if a stock's value drops" + print "to zero it may rebound to a positive value again. You" + print "have $10,000 to invest. Use integers for all your inputs." + print "(Note: To get a 'feel' for the market run for at least" + print "10 days)" + print "-----Good luck!-----" + print + input "(Press Return to continue.)" +end function + +randomIndex = function; return floor(rnd * stockPrices.len); end function + +// Randomly produce new stock values based on previous day's values. +// N1,N2 are random numbers of days which respectively determine +// when a stock will increase or decrease 10 points. +adjustStockPrices = function + for i in changePerShare.indexes + changePerShare[i] = 0 + end for + // if N1 days have passed, increase a random stock by 10 + if N1 < 1 then + changePerShare[randomIndex] = 10 + globals.N1 = floor(4.99 * rnd + 1) + end if + // if N2 days have passed, decrease a random stock by 10 + if N2 < 1 then + changePerShare[randomIndex] = -10 + globals.N2 = floor(4.99 * rnd + 1) + end if + // Deduct one day from N1 and N2 + globals.N1 -= 1 + globals.N2 -= 1 + // update all stocks + for i in stockPrices.indexes + smallChange = rnd + if smallChange < 0.25 then smallChange = 0.25 + if smallChange > 0.5 then smallChange = 0.5 + changePerShare[i] += marketTrendSlope * stockPrices[i] + smallChange + floor(3 - 6*rnd + 0.5) + changePerShare[i] = round(changePerShare[i], 2) + stockPrices[i] += changePerShare[i] + if stockPrices[i] < 0 then + changePerShare[i] = 0 + stockPrices[i] = 0 + end if + stockPrices[i] = round(stockPrices[i], 2) + end for + // After trendDaysLeft, randomly change trend sign and slope + globals.trendDaysLeft -= 1 + if trendDaysLeft < 1 then + globals.trendDaysLeft = floor(4.99 * rnd + 1) + globals.marketTrendSlope = floor(rnd*10 + 0.5)/100 + if rnd < 0.5 then globals.marketTrendSlope = -marketTrendSlope + end if +end function + +pad = function(s, width=20) + s = str(s) + return s + " "*(width - s.len) +end function + +printInitialPortfolio = function + print pad("Stock", 30) + pad("Initials", 12) + "Price/Share" + for i in stockSymbols.indexes + print pad(stockNames[i], 32) + pad(stockSymbols[i], 14) + stockPrices[i] + end for +end function + +printMarketResults = function + print + print "********** END OF DAY'S TRADING **********" + print + print + print pad("Stock", 8) + pad("Price/Share", 14) + pad("Holdings", 12) + + pad("Value", 10) + "Net Price Change" + for i in stockSymbols.indexes + value = round(stockPrices[i] + sharesOwned[i], 2) + print pad(stockSymbols[i], 9) + pad(stockPrices[i], 14) + + pad(sharesOwned[i], 12) + pad(value, 10) + changePerShare[i] + end for + print +end function + +printStatus = function + average = stockPrices.sum / stockPrices.len + print + print "New York Stock Exchange Average: " + round(average, 2), "" + if prevAverage != null then + print " Net Change " + round(average - prevAverage, 2) + else + print + end if + globals.prevAverage = average + print + stockValue = 0 + for i in stockPrices.indexes + stockValue += stockPrices[i] * sharesOwned[i] + end for + print "Total stock assets are $ " + round(stockValue, 2) + print "Total cash assets are $ " + round(cash, 2) + print "Total assets are $ " + round(stockValue + cash, 2) + print + // Uncomment the following line to cheat/debug: + //print "marketTrendSlope: " + marketTrendSlope + "; trendDaysLeft: " + trendDaysLeft +end function + +// Calculate total cost, including brokerage fee, to buy the given +// stock (or if qtyToBuy < 0, negative cash gains minus the fee). +totalCost = function(stockIndex, qtyToBuy) + baseVal = stockPrices[stockIndex] * qtyToBuy + fee = round(abs(baseVal) * 0.01, 2) + return baseVal + fee +end function + +buySell = function + print "What is your transaction in" + i = 0 + while i < stockSymbols.len + while true + qty = input(stockSymbols[i] + "? ") + if qty == "" or qty == "0" or qty.val != 0 then break + print "Enter quantity to buy/sell like +10 or -2." + end while + qty = qty.val + if qty < 0 and -qty > sharesOwned[i] then + print "You have only " + sharesOwned[i] + " of " + stockSymbols[i] + "; try again." + continue + end if + if totalCost(i, qty) > cash then + print "This would cost " + (totalCost(i, qty) - cash) + " more than you have." + continue + end if + sharesOwned[i] += qty + globals.cash -= totalCost(i, qty) + i += 1 + end while +end function + +// Introduction +if yes("Do you want the instructions") then printInstructions +print; print + +// Initial stock values and trends +stockSymbols = ["IBM", "RCA", "LBJ", "ABC", "CBS"] +stockNames = ["Int. Ballistic Missiles", "Red Cross of America", + "Lichtenstein, Bumrap & Joke", "American Bankrupt Co.", "Censured Books Store"] +sharesOwned = [0] * stockSymbols.len +cash = 10000 +stockPrices = [100, 85, 150, 140, 110] +changePerShare = [0] * stockSymbols.len +trendDaysLeft = floor(4.99 * rnd + 1) +marketTrendSlope = floor(rnd*10 + 0.5)/100 +if rnd > 0.5 then marketTrendSlope = -marketTrendSlope +N1 = 0 // days until a big positive jump in a random price +N2 = 0 // days until a big negative jump in a random price +adjustStockPrices +prevAverage = null + +printInitialPortfolio +printStatus +while true + buySell + adjustStockPrices + printMarketResults + printStatus + if not yes("Do you wish to continue") then break +end while +print "Hope you had fun!!" + diff --git a/00_Alternate_Languages/84_Super_Star_Trek/MiniScript/README.md b/00_Alternate_Languages/84_Super_Star_Trek/MiniScript/README.md new file mode 100644 index 000000000..0b383f4b5 --- /dev/null +++ b/00_Alternate_Languages/84_Super_Star_Trek/MiniScript/README.md @@ -0,0 +1,18 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). Note that while the original game separated the instructions into a separate BASIC program (probably due to memory limitations), we have chosen here to combine them into one MiniScript program with both instructions and gameplay. The instructions are available at the start of the game, and later at any time via the HLP command. + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: +``` + miniscript superstartrek.ms +``` + +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: +``` + load "superstartrek" + run +``` diff --git a/00_Alternate_Languages/84_Super_Star_Trek/MiniScript/superstartrek.ms b/00_Alternate_Languages/84_Super_Star_Trek/MiniScript/superstartrek.ms new file mode 100644 index 000000000..7b7a247c9 --- /dev/null +++ b/00_Alternate_Languages/84_Super_Star_Trek/MiniScript/superstartrek.ms @@ -0,0 +1,1267 @@ +// SUPER STARTREK - MAY 16,1978 - REQUIRES 24K MEMORY +// +// **** **** STAR TREK **** **** +// **** SIMULATION OF A MISSION OF THE STARSHIP ENTERPRISE, +// **** AS SEEN ON THE STAR TREK TV SHOW. +// **** ORIGIONAL PROGRAM BY MIKE MAYFIELD, MODIFIED VERSION +// **** PUBLISHED IN DEC'S "101 BASIC GAMES", BY DAVE AHL. +// **** MODIFICATIONS TO THE LATTER (PLUS DEBUGGING) BY BOB +// *** LEEDOM - APRIL & DECEMBER 1974, +// *** WITH A LITTLE HELP FROM HIS FRIENDS . . . +// *** COMMENTS, EPITHETS, AND SUGGESTIONS SOLICITED -- +// *** SEND TO; R. C. LEEDOM +// *** WESTINGHOUSE DEFENSE & ELECTRONICS SYSTEMS CNTR. +// *** BOX 746, M.S. 338 +// *** BALTIMORE, MD 21203 +// *** +// *** CONVERTED TO MICROSOFT 8 K BASIC 3/16/78 BY JOHN GORDERS +// *** Convertod te MiniScript 10/10/23 by Joe Strout +// *** (based heavily on the Python port by @jkboyce) + +import "listUtil" +import "stringUtil" +import "mathUtil" + +//================================================================= +// Instructions +// (This was originally in a separate program, superstartreckins.bas, +// for memory reasons. I've chosen to incorporate it into the main +// program, as that's more convenient for everybody.) +//================================================================= +Instructions = {} +Instructions.print = function + print + print "At each pause, press Return to continue." + input + + print " Instructions for 'Super Star Trek'" + print + print "1. When you see \Command?\ printed, enter one of the legal" + print " commands (NAV,SRS,LRS,PHA,TOR,SHE,DAM,COM, OR XXX)." + print "2. If you should type in an illegal command, you'll get a short" + print " list of the legal commands printed out." + print "3. Some commands require you to enter data (for example, the" + print " 'NAV' command comes back with 'Course (1-9)?'.) If you" + print " type in illegal data (like negative numbers), that command" + print " will be aborted" + print + print " The galaxy is divided into an 8 x 8 quadrant grid," + print "and each quadrant is further divided into an 8 x 8 sector grid." + print + print " You will be assigned a starting point somewhere in the" + print "galaxy to begin a tour of duty as comander of the starship" + print "\Enterprise\; your mission: to seek and destroy the fleet of" + print "klingon warwhips which are menacing the United Federation of" + print "Planets." + input + + print + print " You have the following commands available to you as captain" + print "of the starship Enterprise:" + print + print "\NAV\ command = warp engine control --" + print " course is in a circular numerical 4 3 2" + print " vector arrangement as shown . | ." + print " integer and real values may be .|." + print " used. (Thus course 1.5 is half- 5 ---*--- 1" + print " way between 1 and 2.) .|." + print " . | ." + print " Values may approach 9.0, which 6 7 8" + print " itself is equivalent to 1.0" + print " course" + print " One warp factor is the size of " + print " one quadrant. Therefore, to get" + print " from quadrant 6,5 to 5,5, you would" + print " use course 3, warp factor 1." + input + + print + print "\SRS\ command = short range sensor scan" + print " Shows you a scan of your present quadrant." + print + print " Symbology on your sensor screen is as follows:" + print " <*> = your starship's position" + print " +K+ = klingon battle cruiser" + print " >!< = federation starbase (refuel/repair/re-arm here!)" + print " * = star" + print + print " A condensed 'status report' will also be presented." + input + + print + print "\LRS\ command = long range sensor scan" + print " Shows conditions in space for one quadrant on each side" + print " of the enterprise (which is in the middle of the scan)." + print " The scan is coded in the form \###\, where the units digit" + print " is the number of stars, the tens digit is the number of" + print " starbases, and the hundresds digit is the number of" + print " klingons." + print + print " Example - 207 = 2 klingons, no starbases, & 7 stars." + input + + print + print "\PHA\ command = phaser control." + print " Allows you to destroy the klingon battle cruisers by " + print " zapping them with suitably large units of energy to" + print " deplete their shield power. (Remember, klingons have" + print " phasers too!)" + print + print "\TOR\ command = photon torpedo control" + print " Torpedo course is the same as used in warp engine control." + print " If you hit the klingon vessel, he is destroyed and" + print " cannot fire back at you. If you miss, you are subject to" + print " his phaser fire. In either case, you are also subject to " + print " the phaser fire of all other klingons in the quadrant." + print + print " The library-computer (\COM\ command) has an option to " + print " compute torpedo trajectory for you (option 2)." + input + + print + print "\SHE\ command = shield control" + print " Defines the number of energy units to be assigned to the" + print " shields. Energy is taken from total ship's energy. Note" + print " that the status display total energy includes shield energy." + print + print "\DAM\ command = dammage control report" + print " Gives the state of repair of all devices, where a negative" + print " 'state of repair' shows that the device is temporarily" + print " damaged." + input + + print + print "\COM\ command = library-computer" + print " The library-computer contains six options:" + print " option 0 = cumulative galactic record" + print " This option showes computer memory of the results of all" + print " previous short and long range sensor scans." + print " option 1 = status report" + print " This option shows the number of klingons, stardates," + print " and starbases remaining in the game." + print " option 2 = photon torpedo data" + print " Which gives directions and distance from the enterprise" + print " to all klingons in your quadrant." + print " option 3 = starbase nav data" + print " This option gives direction and distance to any " + print " starbase within your quadrant." + print " option 4 = direction/distance calculator" + print " This option allows you to enter coordinates for" + print " direction/distance calculations." + print " option 5 = galactic /region name/ map" + print " This option prints the names of the sixteen major " + print " galactic regions referred to in the game." + input + print +end function + +//================================================================= +// Constants (tweak these to make the game easier or harder!) +//================================================================= +Constant = {} +Constant.klingonShieldStrength = 200 +Constant.maxEnergy = 3000 +Constant.maxTorpedos = 10 +Constant.minDays = 25 +Constant.maxDays = 34 + +// Directions. NOTE: this game has an unusual coordinate +// system, where x is the row (higher X values are further +// down on the screen), and y is the column (higher Y values +// are further to the right). +dirs = [ // (down, right) + [0, 1], // 1: go right (same as #9) + [-1, 1], // 2: go up-right + [-1, 0], // 3: go up (lower x-coordines; north) + [-1, -1], // 4: go up-left (north-west) + [0, -1], // 5: go left (west) + [1, -1], // 6: go down-left (south-west) + [1, 0], // 7: go down (higher x-coordines; south) + [1, 1], // 8: go down-right + [0, 1]] // 9: go right (east) + + + +//================================================================= +// Global Utility Functions +//================================================================= + +// Generate a random integer from 0 to 7 inclusive. +fnr = function + return floor(rnd * 8) +end function + +// Generate a random integer between any two limits (inclusive). +randInt = function(minVal, maxVal) + return floor(rnd * (maxVal - minVal + 1)) + minVal +end function + +// Print distance and direction between two points on a grid. +printDirection = function(source, dest) + delta1 = -(dest.x - source.x) + delta2 = dest.y - source.y + + if delta2 > 0 then + if delta1 < 0 then + base = 7 + else + base = 1 + temp = delta1; delta1 = delta2; delta2 = temp + end if + else + if delta1 > 0 then + base = 3 + else + base = 5 + temp = delta1; delta1 = delta2; delta2 = temp + end if + end if + + delta1 = abs(delta1) + delta2 = abs(delta2) + if delta1 or delta2 then // bug in original: failed when source == dest + if delta1 >= delta2 then + print "Direction = " + round(base + delta2 / delta1, 6) + else + print "Direction = " + round(base + 2 - delta1 / delta2, 6) + end if + end if + print "Distance = " + round(sqrt(delta1 ^ 2 + delta2 ^ 2), 6) +end function + +getYesNo = function(prompt) + while true + ans = input(prompt + "? ").lower + " " + if ans[0] == "y" then return "yes" + if ans[0] == "n" then return "no" + print "Please answer Yes or No." + end while +end function + +getXY = function(prompt) + while true + ans = input(prompt + "? ").split(",") + if ans.len == 2 then return {"x":ans[0].val, "y":ans[1].val} + print "Please enter two numbers separated by a comma. Example: 5,2" + end while +end function + +string.padBoth = function(length, padChar=" ") + // Pad a string on BOTH sides, so that it ends up centered. + if self.len > length then return self.len[:length] + extra = length - self.len + leftExtra = floor(extra/2) + rightExtra = extra - leftExtra + return padChar * leftExtra + self + padChar * rightExtra +end function + +//================================================================= +// Entity: map of strings used both as unique identifiers for +// things that can be at any position in a quadrant, AND also used +// as display strings in the short-range scan. +//================================================================= +Entity = {} +Entity.klingon = "+K+" +Entity.ship = "<*>" +Entity.empty = " " +Entity.starbase = ">!<" +Entity.star = " * " + +//================================================================= +// Point class: represents an X,Y position. Note that because +// the original game used 1-based coordinates, we have a conversion +// to string method that adds 1 to X and Y. But internally, these +// are assumed to be 0-based. +//================================================================= +Point = {} +Point.make = function(x,y) + p = new Point + p.x = x + p.y = y + return p +end function +Point.random = function + return Point.make(fnr, fnr) +end function +Point.oneBasedStr = function + return (self.x + 1) + " , " + (self.y + 1) +end function + +//================================================================= +// Position: combination of quadrant and sector +//================================================================= +Position = {} +Position.make = function(quadX, quadY, secX, secY) + pos = new Position + pos.quadrant = Point.make(quadX, quadY) + pos.sector = Point.make(secX, secY) + return pos +end function +Position.random = function + return Position.make(fnr, fnr, fnr, fnr) +end function + +//================================================================= +// Quadrant: represents one area of the map +//================================================================= +Quadrant = {} +Quadrant.make = function(quadrantX, quadrantY, klingons, bases, stars) + q = new Quadrant + q.point = Point.make(quadrantX, quadrantY) + q.klingons = klingons + q.bases = bases + q.stars = stars + q.extraRepairTime = rnd * 0.5 + q.klingonShips = [] // (list of KlingonShip) + q.starbase = null // (Point of starbase, if any) + q.charted = false + q.data = null + return q +end function + +// Get the name of the region of this quadrant (e.g. "Rigel"). +Quadrant.name = function + region1 = [ + "Antares", + "Rigel", + "Procyon", + "Vega", + "Canopus", + "Altair", + "Sagittarius", + "Pollux"] + region2 = [ + "Sirius", + "Deneb", + "Capella", + "Betelgeuse", + "Aldebaran", + "Regulus", + "Arcturus", + "Spica"] + if self.point.y < 4 then return region1[self.point.x] + return region2[self.point.x] +end function + +// Get the full name of this quadrant (e.g. "Rigel IV") +Quadrant.fullName = function + return self.name + " " + ["I", "II", "III", "IV"][self.point.y % 4] +end function + +// Store the given entity for the given cell in this quadrant +// (rounding to the nearest integer coordinates). +Quadrant.setValue = function(x, y, entity) + self.data[round(x)][round(y)] = entity +end function + +// Get the entity in the given cell of this quadrant +// (again rounding to the nearest integers). +Quadrant.value = function(x, y) + return self.data[round(x)][round(y)] +end function + +// Find an empty position within this quadrant. +Quadrant.findEmptyPoint = function + while true + p = Point.random + if self.value(p.x, p.y) == Entity.empty then return p + end while +end function + +Quadrant.addStarbase = function + pos = self.findEmptyPoint + self.setValue pos.x, pos.y, Entity.starbase + self.starbase = pos // (note: assumes 0 or 1 starbases) +end function + +// Fill out the contents of this quadrant, given the position of +// the ship and our number of klingons, bases, and stars. +Quadrant.populate = function(shipPos) + self.data = list.init2d(8, 8, Entity.empty) + if shipPos != null then self.setValue shipPos.x, shipPos.y, Entity.ship + for i in range(0, self.klingons-1, 1) + pos = self.findEmptyPoint + self.setValue pos.x, pos.y, Entity.klingon + self.klingonShips.push KlingonShip.make(pos.x, pos.y) + end for + if self.bases > 0 then self.addStarbase + for i in range(0, self.stars-1, 1) + pos = self.findEmptyPoint + self.setValue pos.x, pos.y, Entity.star + end for +end function + +// Return the three-digit number that represents the contents of +// this quadrant in a long-range scan: +Quadrant.lrsValue = function + return str(self.klingons) + str(self.bases) + str(self.stars) +end function + +// Return a list of lines representing the short-range scan +// of this quadrant (i.e., a representation of what entities +// it contains). +Quadrant.scanLines = function + result = [] + for row in self.data + result.push row.join + end for + return result +end function + +//================================================================= +// KlingonShip: represents an enemy ship. These are very simple; +// they need only their position (sector) within the quadrant, +// and their current shield level. +//================================================================= +KlingonShip = {} +KlingonShip.make = function(x, y) + k = new KlingonShip + k.sector = Point.make(x,y) + k.shield = Constant.klingonShieldStrength * (rnd + 0.5) + return k +end function +KlingonShip.distance = function(ship) + // Find distance, in sector units, between this ship and the given + // (player) ship. Assumes both are in the same quadrant. + return mathUtil.distance(self.sector, ship.position.sector) +end function + +//================================================================= +// Ship: represents the starship Enterprise and all its systems. +//================================================================= +Ship = {} +Ship.maxEnergy = Constant.maxEnergy +Ship.maxTorpedos = Constant.maxTorpedos +Ship.init = function + self.position = Position.random + self.devices = [ + "Warp Engines", + "Short Range Sensors", + "Long Range Sensors", + "Phaser Control", + "Photon Tubes", + "Damage Control", + "Shield Control", + "Library-Computer", + ] + self.deviceStatus = [0] * self.devices.len // > 0 means working; < 0 means broken + self.docked = false + self.shields = 0 + self.refill +end function + +Ship.useWarpEnergy = function(warpRounds) + // Note: movement costs 10 energy per sub-step (sector change). + self.energy -= warpRounds * 10 + if self.energy < 0 then + print "Shield control supplies energy to complete the maneuver." + self.shields += self.energy + self.energy = 0 + if self.shields < 0 then self.shields = 0 + end if +end function + +Ship.refill = function + self.energy = self.maxEnergy + self.torpedos = self.maxTorpedos +end function + +Ship.stranded = function + return self.shields + self.energy <= 10 or + (self.energy <= 10 and self.deviceStatus[6] < 0) +end function + +//================================================================= +// Galaxy: all 64 quadrants, plus global information about how many +// klingons there are, the current game time, etc. Basically +// represents the state of the game (but not the flow; that happens +// in the Game class). +//================================================================= +Galaxy = {} +Galaxy.init = function(minDuration=20) + self.qtyKlingons = 0 + self.qtyBases = 0 + self.startDate = 100 * randInt(20, 39) + self.endDate = self.startDate + randInt(Constant.minDays, Constant.maxDays) + self.stardate = self.startDate + self.ship = new Ship + self.ship.init + self.quadrants = list.init2d(8, 8) + for x in range(0, 7) + for y in range(0, 7) + klingons = 0 + r1 = rnd + if r1 > 0.80 then klingons = 1 + if r1 > 0.95 then klingons = 2 + if r1 > 0.98 then klingons = 3 + bases = (rnd > 0.96) + self.quadrants[x][y] = Quadrant.make(x, y, klingons, bases, fnr+1) + self.qtyKlingons += klingons + self.qtyBases += bases + end for + end for + self.missionDuration = minDuration + if self.qtyKlingons > self.missionDuration then + self.missionDuration = self.qtyKlingons + 1 + end if + here = self.localQuadrant + if self.qtyBases == 0 then self.qtyBases = 1 + self.ship.position.sector = Point.random +end function + +Galaxy.daysElapsed = function; return self.stardate - self.startDate; end function +Galaxy.daysLeft = function; return self.endDate - self.stardate; end function +Galaxy.missionOver = function; return self.stardate > self.endDate; end function + +Galaxy.localQuadrant = function + qp = self.ship.position.quadrant + return self.quadrants[qp.x][qp.y] +end function + +Galaxy.printLongRangeScan = function(point) + sep = "-" * 19 + for x in range(point.x - 1, point.x + 1) + print sep + entries = [] + for y in range(point.y - 1, point.y + 1) + if not (0 <= x <= 7 and 0 <= y <= 7) then + entries.push "***" + else + q = self.quadrants[x][y] + entries.push q.lrsValue + q.charted = true + end if + end for + print ": " + entries.join(" : ") + " :" + end for + print sep +end function + +//================================================================= +// Game: represents the flow of the game, and handles user actions. +//================================================================= +Game = {} +Game.init = function + self.galaxy = new Galaxy + self.galaxy.init + self.gameOver = false +end function + +Game.printIntro = function + print;print;print;print;print;print;print;print;print;print;print + print " ,------*------," + print " ,------------- '--- ------'" + print " '-------- --' / /" + print " ,---' '-------/ /--," + print " '----------------'";print + print " THE USS ENTERPRISE --- NCC-1701" + print;print;print;print;print + k = self.galaxy.qtyKlingons + sb = self.galaxy.qtyBases + if sb == 1 then isAre = "is" else isAre = "are" + days = self.galaxy.endDate - self.galaxy.startDate + orders = ["Your orders are as follows:"] + orders.push " Destroy the " + k + " Klingon warships which have invaded" + orders.push " the galaxy before they can attack federation headquarters" + orders.push " on stardate " + self.galaxy.endDate + ". This gives you " + + days + " days. There " + isAre + orders.push " " + sb + " starbase" + "s"*(sb!=1) + " in the galaxy for resupplying your ship." + for line in orders + print line // for extra drama, add a `wait 0.5` line here! + end for + print + input "Press Return when ready to accept command" + print + self.enterQuadrant +end function + +Game.enterQuadrant = function + // Populate the local quadrant and print a short-range scan. + ship = self.galaxy.ship + here = self.galaxy.localQuadrant + here.charted = true + here.populate ship.position.sector + + if self.galaxy.daysElapsed == 0 then + print "Your mission begins with your ship located" + print "in the galactic quadrant, '" + here.fullName + "'" + else + print "Now entering " + here.fullName + " quadrant . . ." + end if + print + + if here.klingons > 0 then + print "COMBAT AREA CONDITION RED" + if ship.shields <= 200 then print " SHIELDS DANGEROUSLY LOW" + end if + self.shortRangeScan +end function + +Game.shortRangeScan = function + ship = self.galaxy.ship + ship.docked = false + quad = self.galaxy.localQuadrant + cs = null + for x in range(ship.position.sector.x - 1, ship.position.sector.x + 1) + for y in range(ship.position.sector.y - 1, ship.position.sector.y + 1) + if 0 <= x <= 7 and 0 <= y <= 7 and quad.value(x, y) == Entity.starbase then + ship.docked = true + cs = "DOCKED" + ship.refill + print "Shields dropped for docking purposes" + ship.shields = 0 + end if + end for + end for + if not cs then + if quad.klingons then + cs = "*RED*" + else if ship.energy < ship.maxEnergy * 0.1 then + cs = "*YELLOW*" + else + cs = "GREEN" + end if + end if + if ship.deviceStatus[1] < 0 then + print + print "*** Short range sensors are out ***" + return + end if + print "-"*33 + for x in range(0, 7) + line = quad.data[x].join + if x == 0 then + line += " STARDATE " + round(self.galaxy.stardate, 1) + else if x == 1 then + line += " CONDITION " + cs + else if x == 2 then + line += " QUADRANT " + ship.position.quadrant.oneBasedStr + else if x == 3 then + line += " SECTOR " + ship.position.sector.oneBasedStr + else if x == 4 then + line += " PHOTON TORPEDOES " + ship.torpedos + else if x == 5 then + line += " TOTAL ENERGY " + floor(ship.energy + ship.shields) + else if x == 6 then + line += " SHIELDS " + floor(ship.shields) + else if x == 7 then + line += " KLINGONS REMAINING " + self.galaxy.qtyKlingons + end if + print line + end for + print "-"*33 +end function + +Game.longRangeScan = function + ship = self.galaxy.ship + if ship.deviceStatus[2] < 0 then + print "Long range sensors are inoperable" + return + end if + print "Long range scan for quadrant " + ship.position.quadrant.oneBasedStr + self.galaxy.printLongRangeScan ship.position.quadrant +end function + +Game.updateKlingons = function + // Move klingons around randomly within the sector + here = self.galaxy.localQuadrant + if not here.klingonShips then return + + for klingonShip in here.klingonShips + if klingonShip.shield <= 0 then continue // (already disabled/destroyed) + here.setValue klingonShip.sector.x, klingonShip.sector.y, Entity.empty + klingonShip.sector = here.findEmptyPoint + here.setValue klingonShip.sector.x, klingonShip.sector.y, Entity.klingon + end for + + self.klingonsFire +end function + +Game.klingonsFire = function + // Nearby klingons fire on the player ship. + here = self.galaxy.localQuadrant + if here.klingons <= 0 then return + ship = self.galaxy.ship + if ship.docked then + print "Starbase shields protect the Enterprise" + return + end if + for klingonShip in here.klingonShips + if klingonShip.shield <= 0 then continue // (already disabled/destroyed) + hit = floor((klingonShip.shield / klingonShip.distance(ship)) * (rnd + 2)) + ship.shields -= hit + print " " + hit + " unit hit on Enterprise from sector " + klingonShip.sector.oneBasedStr + if ship.shields <= 0 then + self.endGame false, false, true + return + end if + print " " + if hit >= 20 and rnd < 0.60 and hit / ship.shields > 0.02 then + deviceNum = floor(rnd * ship.devices.len) + ship.deviceStatus[deviceNum] -= hit / ship.shields + 0.5 * rnd + print "Damage control reports '" + ship.devices[deviceNum] + " damaged by the hit'" + end if + end for +end function + +Game.damControlWhileUnderway = function(improvement=1) + // Work on repairing damaged devices, and 20% of the time, do additional work on + // some random device (which may make it better or worse). + ship = self.galaxy.ship + first = true + for i in range(0, ship.devices.len-1) + if ship.deviceStatus[i] >= 0 then continue + ship.deviceStatus[i] += improvement + if -0.1 < ship.deviceStatus[i] < 0 then ship.deviceStatus[i] = -0.1 + if ship.deviceStatus[i] >= 0 then + if first then s = "Damage control report:" else s = "" + s += " " + ship.devices[i] + " repair completed" + print s + first = false + end if + end for + + if rnd > 0.2 then return + deviceNum = floor(rnd * ship.devices.len) + if rnd < 0.6 then + ship.deviceStatus[deviceNum] -= rnd * 5 + 1 + print "Damage control report: " + ship.devices[deviceNum] + " damaged" + else + ship.deviceStatus[deviceNum] += rnd * 3 + 1 + print "Damage control report: " + ship.devices[deviceNum] + " state of repair improved" + end if +end function + +Game.navigate = function + galaxy = self.galaxy + ship = galaxy.ship + cd = input("Course (1-9)? ").val - 1 // (convert input to 0-8) + if cd == dirs.len - 1 then cd == 0 + if cd < 0 or cd >= dirs.len then + print " Lt. Sulu reports, 'Incorrect course data, sir!'" + return + end if + + if ship.deviceStatus[0] < 0 then maxWarp = 0.2 else maxWarp = 8 + warp = input("Warp factor (0-" + maxWarp + ")? ").val + if warp > maxWarp and maxWarp < 1 then + print "Warp engines are damaged. Maximum speed = warp " + maxWarp + return + end if + if warp == 0 then return + if warp < 0 or warp > 8 then + print " Chief engineer Scott reports 'The engines won't take warp " + warp + "!'" + return + end if + + warpRounds = round(warp * 8) + // Note that we check for sufficient energy based on 1/10th of what it + // will actually cost. This is apparently intentional. + if ship.energy < warpRounds then + print "Engineering reports 'Insufficient energy available" + print " for maneuvering at warp " + warp + "!'" + if ship.shields >= warpRounds - ship.energy and ship.deviceStatus[6] >= 0 then + print "Deflector control room acknowledges " + ship.shields + " units of energy" + print " presently deployed to shields." + end if + return + end if + + self.updateKlingons + self.damControlWhileUnderway + + here = galaxy.localQuadrant + here.setValue ship.position.sector.x, ship.position.sector.y, Entity.empty + startQuad = Point.make(here.point.x, here.point.y) +// print "DEBUG set value at " + ship.position.sector.x + "," + ship.position.sector.y + " to Entity.empty" + // interpolate the direction + cdi = floor(cd) + dx = mathUtil.lerp(dirs[cdi][0], dirs[cdi+1][0], cd - cdi) + dy = mathUtil.lerp(dirs[cdi][1], dirs[cdi+1][1], cd - cdi) + finalGlobalX = ship.position.quadrant.x * 8 + ship.position.sector.x + dx * warpRounds + finalGlobalY = ship.position.quadrant.y * 8 + ship.position.sector.y + dy * warpRounds + for i in range(1, warpRounds) + ship.position.sector.x += dx + ship.position.sector.y += dy + if not (0 <= ship.position.sector.x <= 7 and 0 <= ship.position.sector.y <= 7) then + // Exceeded quadrant limits; calculate final position. + // Note that we randomly re-generate all the stuff in a quadrant every + // time we enter it. So we can jump right to the final position now; + // We only check for collisions when moving *within* your starting sector. + // your starting sector. + ship.position.quadrant.x = floor(finalGlobalX / 8) + ship.position.quadrant.y = floor(finalGlobalY / 8) + ship.position.sector.x = finalGlobalX - ship.position.quadrant.x * 8 + ship.position.sector.y = finalGlobalY - ship.position.quadrant.y * 8 + hitEdge = false + if ship.position.quadrant.x < 0 then + hitEdge = true + ship.position.quadrant.x = 0 + ship.position.sector.x = 0 + else if ship.position.quadrant.x > 7 then + hitEdge = true + ship.position.quadrant.x = 7 + ship.position.sector.x = 7 + end if + if ship.position.quadrant.y < 0 then + hitEdge = true + ship.position.quadrant.y = 0 + ship.position.sector.y = 0 + else if ship.position.quadrant.y > 7 then + hitEdge = true + ship.position.quadrant.y = 7 + ship.position.sector.y = 7 + end if + if hitEdge then + print "Lt. Uhura reports message from Starfleet Command:" + print " 'Permission to attempt crossing of galactic perimeter" + print " is hereby *denied*. Shut down your engines.'" + print "Chief Engineer Scott reports 'Warp engines shut down" + print " at sector " + ship.position.sector.oneBasedStr + + " of quadrant " + ship.position.quadrant.oneBasedStr + ".'" + end if + break + end if + x = floor(ship.position.sector.x) + y = floor(ship.position.sector.y) + if here.value(x, y) != Entity.empty then + ship.position.sector.x = floor(x - dx) + ship.position.sector.y = floor(y - dy) + print "Warp engines shut down at sector " + ship.position.sector.oneBasedStr + + " due to bad navigation" + break + end if + end for + ship.position.sector.x = floor(ship.position.sector.x) + ship.position.sector.y = floor(ship.position.sector.y) + doScan = true + if ship.position.quadrant != startQuad then + self.enterQuadrant + doScan = false + else + here.setValue ship.position.sector.x, ship.position.sector.y, Entity.ship +// print "DEBUG set value at " + ship.position.sector.x + "," + ship.position.sector.y + " to Entity.ship" + end if + ship.useWarpEnergy warpRounds + if warp < 1 then galaxy.stardate += round(warp, 1) else galaxy.stardate += 1 + if galaxy.missionOver then + self.endGame false, false, false + else if doScan then + self.shortRangeScan + end if +end function + +Game.damageControl = function + ship = self.galaxy.ship + if ship.deviceStatus[5] < 0 then + print "Damage control report not available." + else + print + print "DEVICE STATE OF REPAIR" + for i in range(0, ship.devices.len-1) + print ship.devices[i].pad(26) + round(ship.deviceStatus[i], 2) + end for + print + end if + + if not ship.docked then return + + repairTime = 0 + for status in ship.deviceStatus + if status < 0 then repairTime += 0.1 + end for + if repairTime == 0 then return + + repairTime += self.galaxy.localQuadrant.extraRepairTime + if repairTime >= 1 then repairTime = 0.9 + print + print "Technicians standing by to effect repairs to your ship;" + print "estimated time to repair: " + round(repairTime, 2) + " stardates" + if getYesNo("Will you authorize the repair order (Y/N)") == "no" then return + + for i in range(0, ship.devices.len-1) + if ship.deviceStatus[i] < 0 then ship.deviceStatus[i] = 0 + end for + self.galaxy.stardate += repairTime + 0.1 +end function + +Game.shieldControl = function + ship = self.galaxy.ship + if ship.deviceStatus[6] < 0 then + print "Shield control inoperable" + return + end if + totalEnergy = ship.energy + ship.shields + shieldEnergy = input("Energy available = " + totalEnergy + " Number of units to shields? ") + if shieldEnergy == "" then shieldEnergy = ship.shields else shieldEnergy = shieldEnergy.val + if shieldEnergy > totalEnergy then + print "Shield control reports 'This is not the federation treasury.'" + print + shieldEnergy = -1 + end if + if shieldEnergy < 0 or shieldEnergy == ship.shields then + print "" + return + end if + ship.energy += ship.shields - shieldEnergy + ship.shields = shieldEnergy + print "Deflector control room report:" + print " 'Shields now at " + ship.shields + " units per your command.'" +end function + +Game.computer = function + galaxy = self.galaxy + ship = galaxy.ship + if ship.deviceStatus[7] < 0 then + print "Computer disabled" + return + end if + + while true + command = input("Computer active and awaiting command? ") + print + if command == "0" then // Cumulative Galactic Record + print + print " COMPUTER RECORD OF GALAXY FOR QUADRANT " + + ship.position.quadrant.oneBasedStr + print " 1 2 3 4 5 6 7 8" + sep = " ----- ----- ----- ----- ----- ----- ----- -----" + print sep + for i in range(0,7) + line = " " + (i+1) + " " + for j in range(0, 7) + line += " " + if galaxy.quadrants[i][j].charted then + line += galaxy.quadrants[i][j].lrsValue + else + line += "***" + end if + end for + print line + print sep + end for + else if command == "1" then // Status Report + print " STATUS REPORT:" + print "Klingon" + "s" * (galaxy.qtyKlingons != 1) + " left: " + galaxy.qtyKlingons + print "Mission must be completed in " + round(galaxy.daysLeft, 1) + " stardates" + + if galaxy.qtyBases == 0 then + print "Your stupidity has left you on your own in" + print " the galaxy -- you have no starbases left!" + else + print "The Federation is maintaining " + galaxy.qtyBases + + " starbase" + "s" * (galaxy.qtyBases != 1) + " in the galaxy" + end if + + self.damageControl + else if command == "2" then // Photon Torpedo Data + here = galaxy.localQuadrant + if here.klingons <= 0 then + print "Science officer Spock reports 'Sensors show no enemy ships" + print " in this quadrant'" + else + print "FROM ENTERPRISE TO KLINGON BATTLE CRUISER" + "S" * (here.klingons != 1) + for klingonShip in here.klingonShips + if klingonShip.shield <= 0 then continue + printDirection ship.position.sector, klingonShip.sector + end for + end if + else if command == "3" then // Starbase Nav Data + here = galaxy.localQuadrant + if not here.starbase then + print "Science officer Spock reports, 'Sensors show no starbases" + print " in this quadrant.'" + else + print "FROM ENTERPRISE TO STARBASE:" + printDirection ship.position.sector, here.starbase + end if + else if command == "4" then // Direction/Distance Calculator + print "DIRECTION/DISTANCE CALCULATOR:" + print "You are at quadrant " + ship.position.quadrant.oneBasedStr + + " SECTOR " + ship.position.sector.oneBasedStr + print "Please enter" + fromXY = getXY(" Initial coordinates (X,Y)") + toXY = getXY(" Final coordinates (X,Y)") + printDirection fromXY, toXY + else if command == "5" then // Galaxy 'Region Name' Map + print + print " THE GALAXY" + print " 1 2 3 4 5 6 7 8" + sep = " ----- ----- ----- ----- ----- ----- ----- -----" + print sep + for i in range(0,7) + line = " " + (i+1) + " " + line += galaxy.quadrants[i][0].name.padBoth(25) + line += galaxy.quadrants[i][4].name.padBoth(25) + print line + print sep + end for + else + print "Functions available from Library-Computer:" + print " 0 = Cumulative Galactic Record" + print " 1 = Status Report" + print " 2 = Photon Torpedo Data" + print " 3 = Starbase Nav Data" + print " 4 = Direction/Distance Calculator" + print " 5 = Galaxy 'Region Name' Map" + continue + end if + print + break // break out after any valid command + end while +end function + +Game.destroyKlingon = function(x, y) + // Destroy the Klingon in the local quadrant at sector x,y. + // Return true if the game is now won (last klingon destroyed), + // or false otherwise. + galaxy = self.galaxy + here = galaxy.localQuadrant + print "*** KLINGON DESTROYED ***" + galaxy.qtyKlingons -= 1 + here.klingons -= 1 + here.setValue x, y, Entity.empty + for i in here.klingonShips.indexes + ks = here.klingonShips[i] + if ks.sector.x == x and ks.sector.y == y then + ks.shield = 0 + here.klingonShips.remove i + break + end if + end for + + if galaxy.qtyKlingons <= 0 then + self.endGame true, false, false + return true + end if + return false +end function + + +Game.phasers = function + galaxy = self.galaxy + ship = galaxy.ship + here = galaxy.localQuadrant + klingonShips = here.klingonShips + + if ship.deviceStatus[3] < 0 then + print "Phasers inoperative" + return + end if + + if here.klingons <= 0 then + print "Science officer Spock reports 'Sensors show no enemy ships" + print " in this quadrant'" + return + end if + + if ship.deviceStatus[7] < 0 then + print "Computer failure hampers accuracy" + end if + + print "Phasers locked on target; energy available = " + ship.energy + " units" + while true + phaserPower = input("Number of units to fire? ").val + if phaserPower <= 0 then return + if phaserPower <= ship.energy then break + print "Energy available = " + ship.energy + " units" + end while + + ship.energy -= phaserPower + if ship.deviceStatus[7] < 0 then // (bug in original; was d(6) + phaserPower *= rnd + end if + + phaserPerKlingon = floor(phaserPower / here.klingons) + for i in range(klingonShips.len - 1) + ks = klingonShips[i] + if ks.shield <= 0 then continue + distance = mathUtil.distance(ks.sector, ship.position.sector) + hit = floor((phaserPerKlingon / distance) * (rnd + 2)) + if hit <= 0.15 * ks.shield then + print "Sensors show no damage to enemy at " + ks.sector.oneBasedStr + else + ks.shield -= hit + print " " + hit + " unit hit on Klingon at sector " + ks.sector.oneBasedStr + if ks.shield <= 0 then + if self.destroyKlingon(ks.sector.x, ks.sector.y) then return + else + print " (Sensors show " + round(ks.shield, 6) + " units remaining)" + end if + end if + end for + + self.klingonsFire +end function + +Game.torpedos = function + galaxy = self.galaxy + ship = galaxy.ship + here = galaxy.localQuadrant + klingonShips = here.klingonShips + + if ship.torpedos <= 0 then + print "All photon torpedos expended" + return + else if ship.deviceStatus[4] < 0 then + print "Photon tubes are not operational" + return + end if + + cd = input("Course (1-9)? ").val - 1 // (convert input to 0-8) + if cd == dirs.len - 1 then cd == 0 + if cd < 0 or cd >= dirs.len then + print " Lt. Sulu reports, 'Incorrect course data, sir!'" + return + end if + + cdi = floor(cd) + dx = mathUtil.lerp(dirs[cdi][0], dirs[cdi+1][0], cd - cdi) + dy = mathUtil.lerp(dirs[cdi][1], dirs[cdi+1][1], cd - cdi) + + ship.energy -= 2 + ship.torpedos -= 1 + + x = ship.position.sector.x + y = ship.position.sector.y + print "Torpedo track:" + while true + x += dx + y += dy + roundX = round(x); roundY = round(y) + if not (0 <= roundX <= 7) or not (0 <= roundY <= 7) then + print "Torpedo missed" + self.klingonsFire + return + end if + print " " + (roundX+1) + " , " + (roundY+1) + wait 0.5 // (added for dramatic effect) + entityHit = here.value(roundX, roundY) + if entityHit != Entity.empty then break + end while + + if entityHit == Entity.klingon then + if self.destroyKlingon(roundX, roundY) then return + else if entityHit == Entity.star then + print "Star at " + (roundX+1) + " , " + (roundY+1) + " absorbed torpedo energy." + else if entityHit == Entity.starbase then + print "*** STARBASE DESTROYED ***" + here.bases -= 1 + here.setValue roundX, roundY, Entity.empty + galaxy.qtyBases -= 1 + if galaxy.qtyBases == 0 and galaxy.qtyKlingons <= galaxy.daysLeft then + // (Note: bug in original code, compared qtyKlingons to + // quantity daysLeft - missionDuration, which would always be a negative + // number. So this hard-labor message could never appear. I've chosen + // a different comparison which at least can be true sometimes.) + print "That does it, captain!! You are hereby relieved of command" + print "and sentenced to 99 stardates at hard labor on Cygnus 12!!" + self.endGame false, false, false + return + end if + print "Starfleet Command reviewing your record to consider" + print "court martial!" + ship.docked = false + end if + + self.klingonsFire +end function + +Game.endGame = function(won=false, quit=true, shipDestroyed=false) + if won then + print "Congratulations, captain! The last klingon battle cruiser" + print "menacing the federation has been destroyed." + efficiency = round(1000 * self.galaxy.qtyKlingons / self.galaxy.daysElapsed^2, 4) + print "Your efficiency rating is " + efficiency + print + else + if not quit then + if shipDestroyed then + print + print "The enterprise has been destroyed. The Federation " + print "will be conquered." + end if + print "It is stardate " + round(self.galaxy.stardate, 1) + end if + + print "There were " + self.galaxy.qtyKlingons + " klingon battle cruisers left at" + print "the end of your mission." + print + end if + if self.galaxy.qtyBases == 0 then exit + print "The Federation is in need of a new starship commander" + print "for a similar mission -- if there is a volunteer," + if input("let him step forward and enter 'aye'? ").lower().trim != "aye" then exit + self.gameOver = true +end function + +notImplemented = function + print + print "Not implemented yet." + print +end function + +Game.commands = [] // each entry is [command, summary, function] +Game.addCommand = function(command, summary, func) + self.commands.push [command, summary, @func] +end function + +Game.addCommand "NAV", "to set course", @Game.navigate +Game.addCommand "SRS", "for short range sensor scan", @Game.shortRangeScan +Game.addCommand "LRS", "for long range sensor scan", @Game.longRangeScan +Game.addCommand "PHA", "to fire phasers", @Game.phasers +Game.addCommand "TOR", "to fire photon torpedos", @Game.torpedos +Game.addCommand "SHE", "to raise or lower shields", @Game.shieldControl +Game.addCommand "DAM", "for damage control reports", @Game.damageControl +Game.addCommand "COM", "to call on library-computer", @Game.computer +Game.addCommand "HLP", "for help, i.e. instructions", @Instructions.print // (not in original game) +Game.addCommand "XXX", "to resign your command", @Game.endGame + + +Game.doOneCommand = function + cmd = input("Command? ").upper + for entry in self.commands + if cmd == entry[0] then + self.curCmdFunc = entry[2] + self.curCmdFunc + return + end if + end for + print "Enter one of the following:" + for entry in self.commands + print " " + entry[0] + " (" + entry[1] + ")" + end for + print +end function + +Game.mainLoop = function + while not self.gameOver + self.doOneCommand + if self.galaxy.ship.stranded then + print + print "** FATAL ERROR ** You've just stranded your ship in space." + print "You have insufficient maneuvering energy, and shield control" + print "is presently incapable of cross-circuiting to engine room!!" + end if + end while +end function + +for i in range(1,12); print; end for +print " "*10 + "*************************************" +print " "*10 + "* *" +print " "*10 + "* *" +print " "*10 + "* * * SUPER STAR TREK * * *" +print " "*10 + "* *" +print " "*10 + "* *" +print " "*10 + "*************************************" +for i in range(1,8); print; end for +if getYesNo("Do you need instructions (y/n)") == "yes" then + Instructions.print +end if + +while true + game = new Game + game.init + game.printIntro + game.mainLoop +end while \ No newline at end of file diff --git a/00_Alternate_Languages/84_Super_Star_Trek/instructions.txt b/00_Alternate_Languages/84_Super_Star_Trek/instructions.txt index 59124d9d6..837558347 100644 --- a/00_Alternate_Languages/84_Super_Star_Trek/instructions.txt +++ b/00_Alternate_Languages/84_Super_Star_Trek/instructions.txt @@ -57,7 +57,7 @@ OF THE STARSHIP ENTERPRISE: ITSELF IS EQUIVALENT TO 1.0" COURSE ONE WARP FACTOR IS THE SIZE OF - ONE QUADTANT. THEREFORE, TO GET + ONE QUADRANT. THEREFORE, TO GET FROM QUADRANT 6,5 TO 5,5, YOU WOULD USE COURSE 3, WARP FACTOR 1. @@ -77,7 +77,7 @@ OF THE STARSHIP ENTERPRISE: OF THE ENTERPRISE (WHICH IS IN THE MIDDLE OF THE SCAN) THE SCAN IS CODED IN THE FORM \###\, WHERE TH UNITS DIGIT IS THE NUMBER OF STARS, THE TENS DIGIT IS THE NUMBER OF - STARBASES, AND THE HUNDRESDS DIGIT IS THE NUMBER OF + STARBASES, AND THE HUNDREDS DIGIT IS THE NUMBER OF KLINGONS. EXAMPLE - 207 = 2 KLINGONS, NO STARBASES, & 7 STARS. diff --git a/00_Alternate_Languages/85_Synonym/MiniScript/README.md b/00_Alternate_Languages/85_Synonym/MiniScript/README.md new file mode 100644 index 000000000..f887013c7 --- /dev/null +++ b/00_Alternate_Languages/85_Synonym/MiniScript/README.md @@ -0,0 +1,17 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: +``` + miniscript synonym.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: +``` + load "synonym" + run +``` diff --git a/00_Alternate_Languages/85_Synonym/MiniScript/synonym.ms b/00_Alternate_Languages/85_Synonym/MiniScript/synonym.ms new file mode 100644 index 000000000..c64c4f52e --- /dev/null +++ b/00_Alternate_Languages/85_Synonym/MiniScript/synonym.ms @@ -0,0 +1,47 @@ +words = [["first", "start", "beginning", "onset", "initial"], +["similar", "alike", "same", "like", "resembling"], +["model", "pattern", "prototype", "standard", "criterion"], +["small", "insignificant", "little", "tiny", "minute"], +["stop", "halt", "stay", "arrest", "check", "standstill"], +["house", "dwelling", "residence", "domicile", "lodging", "habitation"], +["pit", "hole", "hollow", "well", "gulf", "chasm", "abyss"], +["push", "shove", "thrust", "prod","poke","butt", "press"], +["red", "rouge", "scarlet", "crimson", "flame", "ruby"], +["pain", "suffering", "hurt", "misery", "distress", "ache", "discomfort"]] + +words.shuffle + +responses = ["Right","Correct","Fine","Good!","Check"] + +print " " * 33 + "SYNONYM" +print " " * 15 + "Creative Computing Morristown, New Jersey" +print; print; print +print "A synonym of a word means another word in the English" +print "language which has the same or very nearly the same meaning." +print "I choose a word -- you type a synonym." +print "If you can't think a synonym, type the word 'HELP'" +print "and I will tell you a synonym." +print + +for synonyms in words + word = synonyms[0] + synonyms = synonyms[1:] + responses.shuffle + + print + while 1 + guess = input(" What is a synonym of " + word + "? ").lower + if guess == "help" then + synonyms.shuffle + print "**** A synonym of " + word + " is " + synonyms[0] + "." + print + else if guess == word or synonyms.indexOf(guess) == null then + print " Try again." + else + print responses[0] + break + end if + end while +end for +print +print "Synonym drill completed." diff --git a/00_Alternate_Languages/86_Target/MiniScript/README.md b/00_Alternate_Languages/86_Target/MiniScript/README.md new file mode 100644 index 000000000..c06194764 --- /dev/null +++ b/00_Alternate_Languages/86_Target/MiniScript/README.md @@ -0,0 +1,17 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: +``` + miniscript target.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: +``` + load "target" + run +``` diff --git a/00_Alternate_Languages/86_Target/MiniScript/target.ms b/00_Alternate_Languages/86_Target/MiniScript/target.ms new file mode 100644 index 000000000..125fe2ff5 --- /dev/null +++ b/00_Alternate_Languages/86_Target/MiniScript/target.ms @@ -0,0 +1,114 @@ +degToRad = function(n) + return n * pi / 180 +end function + +radToDeg = function(n) + return n * 180 / pi +end function + +roundDown = function(n, r) + return floor(n / r) * r +end function + +getCoord = function(distance, radX, radZ) + xc = sin(radZ)*cos(radX)*distance + yc = sin(radZ)*sin(radX)*distance + zc = cos(radZ)*distance + return [xc,yc,zc] +end function + +distanceBetween = function (d1,d2) + return ((d1[0]-d2[0])^2 + (d1[1]-d2[1])^2 + (d1[2]-d2[2])^2)^.5 +end function + +coordStr = function(coords) + return "X = " + round(coords[0]) + + " Y = " + round(coords[1]) + " Z = " + round(coords[2]) +end function + +print " " * 33 + "TARGET" +print " " * 15 + "Creative Computing Morristown, New Jersey" +print; print; print + +print "You are the weapons officer on the Starship Enterprise" +print "and this is a test to see how accurae a shot you" +print "are in a 3-dimensional range. You will be told" +print "the radian offset for the X and Z axes, the location" +print "of the target in 3-dimensional rectangular coordinates," +print "the approximate number of degrees from the X and Z" +print "axes, and the approximate distance to the target." +print "You will then proceed to shoot at the target until it is" +print "destroyed!" +print; print +print "Good luck!" +roundToList = [20,10,2,1] +ready = true +while ready + turns = -1 + radX = rnd * 2 * pi + radZ = rnd * 2 * pi + print "Radians from X axis = " + radX + " from Z axis = " + radZ + + distance = 100000 * rnd * rnd + coords = getCoord(distance, radX, radZ) + + print "Target sighted: Approx Coordinates: " + coordStr(coords) + + gameRunning = true + while gameRunning + turns += 1 + if turns >=4 then + estDistance = distance + else + estDistance = roundDown(distance, roundToList[turns]) + end if + + print " Estimated Distance: " + estDistance + print + tx = input("Input angle deviation from X in degrees: ").val + tz = input("Input angle deviation from Z in degrees: ").val + tdist = input("Input distance: ").val + print + if tdist < 20 then + print "You blew yourself up!!" + gameRunning = false + else + tx = degToRad(tx) + tz = degToRad(tz) + + print "Radians from X-axis = " + tx + " from Z-axis = " + tz + targeted = getCoord(tdist, tx,tz) + distBet = distanceBetween(coords, targeted) + if distBet > 20 then + dx = targeted[0] - coords[0] + dy = targeted[1] - coords[1] + dz = targeted[2] - coords[2] + xMsg = {false: "Shot in front of target ", true: "Shot behind target "} + print xMsg[dx<0] + dx + " kilometers." + yMsg = {false: "Shot to left of target ", true: "Shot to right of target "} + print yMsg[dy<0] + dy + " kilometers." + zMsg = {false: "Shot above target ", true: "Shot below target "} + print zMsg[dz<0] + dz + " kilometers." + + print "Approx position of explosion: " + coordStr(targeted) + print " Distance from target = " + distBet + print + print + + else + print + print " * * * HIT * * * Target is non-functional" + print + print "Distance of explosion from target was " + distBet + "kilometers." + print + print "Mission accomplished in " + (turns+1) + " shots." + print + gameRunning = false + end if + end if + end while + print + ans = input("Ready for next target? ").lower + ready = ans and ans[0].lower == "y" + print +end while diff --git a/00_Alternate_Languages/87_3-D_Plot/MiniScript/3dplot.ms b/00_Alternate_Languages/87_3-D_Plot/MiniScript/3dplot.ms new file mode 100644 index 000000000..0cf1e5169 --- /dev/null +++ b/00_Alternate_Languages/87_3-D_Plot/MiniScript/3dplot.ms @@ -0,0 +1,26 @@ +// 3dPlot +// +// Converted from BASIC to MiniScript by Joe Strout + +print " "*32 + "3D PLOT" +print " "*15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" +print; print; print +e = 2.71828 + +fna = function(z) + return 30 * e^(-z*z/100) +end function + +for x in range(-30, 30, 1.5) + lastZ = 0 + y1 = 5 * floor(sqrt(900-x*x)/5) + for y in range(y1, -y1, -5) + z = floor(25+fna(sqrt(x*x+y*y))-.7*y) + if z > lastZ then + print " "*(z-lastZ) + "*", "" + lastZ = z + end if + end for + print + wait 0.1 // (optional) +end for diff --git a/00_Alternate_Languages/87_3-D_Plot/MiniScript/README.md b/00_Alternate_Languages/87_3-D_Plot/MiniScript/README.md new file mode 100644 index 000000000..2bcec68c8 --- /dev/null +++ b/00_Alternate_Languages/87_3-D_Plot/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript number.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "number" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/88_3-D_Tic-Tac-Toe/MiniScript/README.md b/00_Alternate_Languages/88_3-D_Tic-Tac-Toe/MiniScript/README.md new file mode 100644 index 000000000..12d6406ec --- /dev/null +++ b/00_Alternate_Languages/88_3-D_Tic-Tac-Toe/MiniScript/README.md @@ -0,0 +1,18 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: +``` + miniscript qubit.ms +``` + +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: +``` + load "qubit" + run +``` diff --git a/00_Alternate_Languages/88_3-D_Tic-Tac-Toe/MiniScript/qubit.ms b/00_Alternate_Languages/88_3-D_Tic-Tac-Toe/MiniScript/qubit.ms new file mode 100644 index 000000000..3074be2d4 --- /dev/null +++ b/00_Alternate_Languages/88_3-D_Tic-Tac-Toe/MiniScript/qubit.ms @@ -0,0 +1,361 @@ +print " "*33 + "QUBIC" +print " "*15 + "Creative Computing Morristown New Jersey" +print; print; print + +getYesNo = function(prompt) + while true + yn = input(prompt + "? ").lower + " " + if yn[0] == "y" then return "yes" + if yn[0] == "n" then return "no" + print "Incorrect answer. Please type 'yes' or 'no'" + end while +end function + +// Data defining "lines" as sets of board indexes which form 4-in-a-row: +ma = [null, + [null, 1,2,3,4], // 1 + [null, 5,6,7,8], // 2 + [null, 9,10,11,12], // 3 + [null, 13,14,15,16], // 4 + [null, 17,18,19,20], // 5 + [null, 21,22,23,24], // 6 + [null, 25,26,27,28], // 7 + [null, 29,30,31,32], // 8 + [null, 33,34,35,36], // 9 + [null, 37,38,39,40], // 10 + [null, 41,42,43,44], // 11 + [null, 45,46,47,48], // 12 + [null, 49,50,51,52], // 13 + [null, 53,54,55,56], // 14 + [null, 57,58,59,60], // 15 + [null, 61,62,63,64], // 16 + [null, 1,17,33,49], // 17 + [null, 5,21,37,53], // 18 + [null, 9,25,41,57], // 19 + [null, 13,29,45,61], // 20 + [null, 2,18,34,50], // 21 + [null, 6,22,38,54], // 22 + [null, 10,26,42,58], // 23 + [null, 14,30,46,62], // 24 + [null, 3,19,35,51], // 25 + [null, 7,23,39,55], // 26 + [null, 11,27,43,59], // 27 + [null, 15,31,47,63], // 28 + [null, 4,20,36,52], // 29 + [null, 8,24,40,56], // 30 + [null, 12,28,44,60], // 31 + [null, 16,32,48,64], // 32 + [null, 1,5,9,13], // 33 + [null, 17,21,25,29], // 34 + [null, 33,37,41,45], // 35 + [null, 49,53,57,61], // 36 + [null, 2,6,10,14], // 37 + [null, 18,22,26,30], // 38 + [null, 34,38,42,46], // 39 + [null, 50,54,58,62], // 40 + [null, 3,7,11,15], // 41 + [null, 19,23,27,31], // 42 + [null, 35,39,43,47], // 43 + [null, 51,55,59,63], // 44 + [null, 4,8,12,16], // 45 + [null, 20,24,28,32], // 46 + [null, 36,40,44,48], // 47 + [null, 52,56,60,64], // 48 + [null, 1,6,11,16], // 49 + [null, 17,22,27,32], // 50 + [null, 33,38,43,48], // 51 + [null, 49,54,59,64], // 52 + [null, 13,10,7,4], // 53 + [null, 29,26,23,20], // 54 + [null, 45,42,39,36], // 55 + [null, 61,58,55,52], // 56 + [null, 1,21,41,61], // 57 + [null, 2,22,42,62], // 58 + [null, 3,23,43,63], // 59 + [null, 4,24,44,64], // 60 + [null, 49,37,25,13], // 61 + [null, 50,38,26,14], // 62 + [null, 51,39,27,15], // 63 + [null, 52,40,28,16], // 64 + [null, 1,18,35,52], // 65 + [null, 5,22,39,56], // 66 + [null, 9,26,43,60], // 67 + [null, 13,30,47,64], // 68 + [null, 49,34,19,4], // 69 + [null, 53,38,23,8], // 70 + [null, 57,42,27,12], // 71 + [null, 61,46,31,16], // 72 + [null, 1,22,43,64], // 73 + [null, 16,27,38,49], // 74 + [null, 4,23,42,61], // 75 + [null, 13,26,39,52]] // 76 + +// "opening book", i.e. spots that are generally good to hold if available +ya = [null, 1,49,52,4,13,61,64,16,22,39,23,38,26,42,27,43] + +showBoard = function + for i in range(1,9); print; end for + for i in range(1, 4) + print + for j in range(1, 4) + str = " " * j + for k in range(1, 4) + q = 16 * i + 4 * j + k - 20 + if xa[q] == 5 then + str += "(M)" + else if xa[q] == 1 then + str += "(Y)" + else + str += "( )" + end if + str += " " + end for + print str + // print (makes display too tall for the screen) + end for + print + end for +end function + +clearFractions = function + for i in range(1, 64) + if xa[i] == 1/8 then xa[i] = 0 + end for +end function + +checkForLines = function + for s in range(1, 76) + j1 = ma[s][1]; + j2 = ma[s][2]; + j3 = ma[s][3]; + j4 = ma[s][4]; + la[s] = xa[j1] + xa[j2] + xa[j3] + xa[j4] + end for +end function + +doPlayerMove = function + clearFractions + while true + print + inp = input("Your move? ") + if inp == "0" then + showBoard + continue + end if + if inp == "1" then exit + ok = true + if inp.len != 3 then + ok = false + else + i = inp[0].val + j = inp[1].val + k = inp[2].val + ok = (0 < i < 5) and (0 < j < 5) and (0 < k < 5) + end if + if not ok then + print "Incorrect move, retype it--" + else + m = 16 * i + 4 * j + k - 20 + if xa[m] != 0 then + print "That square is used, try again." + else + xa[m] = 1 + break + end if + end if + end while +end function + +selectMove = function(lineIndex, valueToReplace) + if lineIndex % 4 <= 1 then a = 1 else a = 2 + for j in range(a, 5 - a, 5 - 2 * a) + if xa[ma[lineIndex][j]] == valueToReplace then + xa[ma[lineIndex][j]] = 5 + m = ma[lineIndex][j] + print "Machine takes " + squareName(m) + return true + end if + end for + return false +end function + +doComputerMove = function + // look for lines with two M's and two blanks; add 1/8 to the blank spots + for i in range(1, 76) + la[i] = xa[ma[i][1]] + xa[ma[i][2]] + xa[ma[i][3]] + xa[ma[i][4]] + l = la[i] + if l == 10 then + for j in range(1, 4) + if (xa[ma[i][j]] == 0) then xa[ma[i][j]] = 1/8 + end for + end if + end for + // ...and if we now find lines containing 4 such spots, or 1 M and 3 such spots, + // then pick one of those spots for our move + checkForLines + for i in range(1, 76) + if la[i] == 4/8 or la[i] == 5 + 3/8 then + selectMove i, 1/8 + return true + end if + end for + + // now look for lines containing 2 Y's, and mark (with 1/8) the blank spots + clearFractions + for i in range(1, 76) + la[i] = xa[ma[i][1]] + xa[ma[i][2]] + xa[ma[i][3]] + xa[ma[i][4]] + l = la[i] + if l == 2 then + for j in range(1, 4) + if (xa[ma[i][j]] == 0) then xa[ma[i][j]] = 1 / 8 + end for + end if + end for + // ...and again, if we find 4 such spots in a row, or 1 player move plus + // 3 marked spots, then pick one of those + checkForLines + for i in range(1, 76) + if la[i] == 4/8 or la[i] == 1 + 3/8 then + selectMove 1/8 + return true + end if + end for + + // do mysterious stuff that depends on the order of lines in ma + // in ways I don't understand + for k in range(1, 18) + p = 0 + for i in range(4 * k - 3, 4 * k) + for j in range(1, 4) + p += xa[ma[i][j]] + end for + end for + if p == 4 or p == 9 then + for i in range(4 * k - 3, 4 * k) + if selectMove(1/8) then return true + end for + s = 0 + end if + end for + + // look for certain "good" spots in our ya array that are still available + clearFractions + found = false + for z in range(1, 17) + if xa[ya[z]] == 0 then + found = true + break + end if + end for + if not found then + // getting desperate, look for any open spot + for i in range(1, 64) + if xa[i] == 0 then + xa[i] = 5 + print "Machine likes " + squareName(i) + return true + end if + end for + print "The game is a draw." + return false + end if + m = ya[z] + xa[m] = 5 + print "Machine moves to " + squareName(m) + return true +end function + +squareName = function(m) + k1 = floor((m - 1) / 16) + 1 + j2 = m - 16 * (k1 - 1) + k2 = floor((j2 - 1) / 4) + 1 + k3 = m - (k1 - 1) * 16 - (k2 - 1) * 4 + return str(k1) + k2 + k3 +end function + +doOneGame = function + globals.xa = [null] + [0] * 64 // board state + globals.la = [null] + [0] * 76 // line data + skipFirst = getYesNo("Do you want to move first") == "no" + while true + if skipFirst then + skipFirst = false + else + doPlayerMove + end if + checkForLines + machineDone = false + // take three passes over the straight lines in the board + for j in range(1, 3) + for i in range(1, 76) + // first pass: check for player win + if j == 1 then + if la[i] != 4 then continue; + print "You win as follows" + " (line " + i + ")" + for j in range(1, 4) + print squareName(ma[i][j]) + end for + return + end if + // second pass: check for machine able to win + if j == 2 then + if la[i] != 15 then continue; + for j in range(1, 4) + m = ma[i][j] + if (xa[m] != 0) then continue; + xa[m] = 5 + break + end for + print "Machine moves to " + squareName(m) + ", and wins as follows" + for j in range(1, 4) + print squareName(ma[i][j]) + end for + return + end if + // third pass: check for player about to win + if j == 3 then + if la[i] != 3 then continue + for j in range(1, 4) + m = ma[i][j] + if xa[m] != 0 then continue + xa[m] = 5 + print "Nice try, machine moves to " + squareName(m) + machineDone = true + end for + break + end if + end for + end for + if (machineDone) then continue + + if not doComputerMove then break + end while +end function + + +// Main program +if getYesNo("Do you want instructions") == "yes" then + print + print "The game is tic-tac-toe in a 4 x 4 x 4 cube." + print "Each move is indicated by a 3 digit number, with each" + print "digit between 1 and 4 inclusive. The digits indicate the" + print "level, row, and column, respectively, of the occupied" + print "place. " + print + print "To print the playing board, type 0 (zero) as your move." + print "The program will print the board with your moves indi-" + print "cated with a (Y), the machine's moves with an (M), and" + print "unused squares with a ( )." // "output is on paper." + print + print "To stop the program run, type 1 as your move." + print + print +end if +while true + doOneGame + print + if getYesNo("Do you want to try another game") == "no" then break +end while + + + diff --git a/00_Alternate_Languages/89_Tic-Tac-Toe/MiniScript/README.md b/00_Alternate_Languages/89_Tic-Tac-Toe/MiniScript/README.md new file mode 100644 index 000000000..c3c2c9259 --- /dev/null +++ b/00_Alternate_Languages/89_Tic-Tac-Toe/MiniScript/README.md @@ -0,0 +1,20 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from , install, and then run the program with a command such as: + +``` + miniscript tictactoe.ms +``` + +2. Mini Micro: +Download Mini Micro from , launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "tictactoe" + run +``` diff --git a/00_Alternate_Languages/89_Tic-Tac-Toe/MiniScript/tictactoe.ms b/00_Alternate_Languages/89_Tic-Tac-Toe/MiniScript/tictactoe.ms new file mode 100644 index 000000000..8d472a7bb --- /dev/null +++ b/00_Alternate_Languages/89_Tic-Tac-Toe/MiniScript/tictactoe.ms @@ -0,0 +1,90 @@ +// This program plays Tic Tac Toe +// The machine goes first and the way this is set up +// there's no way the human player can win. At best +// it will be a draw. +computerNext = function(x) + return x - 8 * floor((x - 1) / 8) +end function + +print " " * 30 + "TIC TAC TOE" +print " " * 15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" +print; print; print + +print "The game board is numbered:" +print +print "1 2 3" +print "8 9 4" +print "7 6 5" +print + +while true + computer = 9 + gameOver = false + // MOVE ONE line 240 in original + // Computer always moves first and takes the center + print "Computer moves " + computer + player = input("Your move? ").val + playerFirstMove = player + + // MOVE TWO line 280 + // Computer's 2nd move - always the next space clockwise + // from the player's + computer = computerNext(player + 1) + canWinAt = computerNext(computer+4) + print "Computer moves " + computer + player = input("Your move? ").val + + // MOVE THREE line 300 + // Computer has two consecutive cells. This includes the + // middle so, to complete this 3-in-a-row, get the opposite + // value of comp's last move - which is four cells clockwise away. + + if player != canWinAt then + computer = canWinAt + print "Computer moves " + computer + print "... and wins ********" + gameOver = true + else + // Blocked - so two cells away from comp's last move + // line 360 + computer = computerNext(computer + 2) + print "Computer moves " + computer + end if + + if gameOver == false then + canWinAt = computerNext(computer+4) + player = input("Your move? ").val + + // MOVE FOUR - line 400 + if player != canWinAt then + computer = canWinAt + print "Computer moves " + computer + print "... and wins ********" + gameOver = true + else + // Foiled again! - line 450 + if playerFirstMove % 2 == 0 then + computer = computerNext(computer + 7) + print "Computer moves " + computer + print "... and wins ********" + gameOver = true + else // line 500 + computer = computerNext(computer + 3) + print "Computer moves " + computer + end if + end if + end if + + if gameOver == false then + // line 520 + player = input("Your move? ").val + if player != computerNext(computer + 4) then + computer = computerNext(computer + 4) + else + computer = computerNext(computer + 6) + end if + print "Computer moves " + computer + print "The game is a draw." + end if + print +end while diff --git a/00_Alternate_Languages/89_Tic-Tac-Toe/MiniScript/tictactoe2.ms b/00_Alternate_Languages/89_Tic-Tac-Toe/MiniScript/tictactoe2.ms new file mode 100644 index 000000000..20981ab08 --- /dev/null +++ b/00_Alternate_Languages/89_Tic-Tac-Toe/MiniScript/tictactoe2.ms @@ -0,0 +1,155 @@ +solutions = [[0,1,2],[3,4,5],[6,7,8]] +solutions += [[0,3,6],[1,4,7],[2,5,8]] +solutions += [[0,4,8],[2,4,6]] + +TicTacToe = {} +TicTacToe.grid = (" " * 9).split("") +TicTacToe.markers = {true: "X",false:"O"} +TicTacToe.AI = "X" +TicTacToe.human = "O" +TicTacToe.next = "X" +TicTacToe.lastMove = -1 + +TicTacToe.show = function + print " " + self.grid[0:3].join(" ! ") + print "---+---+---" + print " " + self.grid[3:6].join(" ! ") + print "---+---+---" + print " " + self.grid[6:].join(" ! ") + print + print +end function + +TicTacToe.set = function(player, pos) + if self.grid[pos] != " " then + return false + end if + self.grid[pos] = player + self.lastMove = pos + return true +end function + +TicTacToe.setMarkers = function(mark) + self.human = self.markers[mark == "X"] + self.AI = self.markers[mark == "O"] +end function + +TicTacToe.getWinner = function + for mark in self.markers.values + for solution in solutions + cnt = 0 + for i in solution + cnt += (self.grid[i] == mark) + end for + if cnt == 3 then return mark + end for + end for + return null +end function + +TicTacToe.potentialWins = function + potential = {"X": [], "O": []} + for mark in self.markers.values + for solution in solutions + cnt = 0 + emptyCells = [] + for i in solution + cnt += (self.grid[i] == mark) + if self.grid[i] == " " then emptyCells.push(i) + end for + if cnt == 2 and emptyCells.len == 1 then potential[mark].push(emptyCells[0]) + end for + end for + return potential +end function + +TicTacToe.moveAvailable = function + return self.grid.indexOf(" ") != null +end function + +TicTacToe.selectAI = function + if self.grid[4] == " " then return 4 + + potential = self.potentialWins + + AIWins = potential[self.AI] + if AIWins.len >0 then return AIWins[0] + + HumanWins = potential[self.human] + + if HumanWins.len > 0 then return HumanWins[0] + + if [1,3,5,7].indexOf(self.lastMove) != null then + for corner in [8,6,2,0] + if self.grid[corner] == " " then + self.grid[corner] = self.AI + potential = self.potentialWins + self.grid[corner] = " " + AIWins = potential[self.AI] + if AIWins.len > 0 then return corner + end if + end for + else + for side in [1,3,5,7] + if self.grid[side] == " " then + self.grid[side] = self.AI + potential = self.potentialWins + self.grid[side] = " " + AIWins = potential[self.AI] + if AIWins.len > 0 then return side + end if + end for + end if + for ix in range(0,8) + if self.grid[ix] == " " then return ix + end for + + return null +end function + +print " " * 15 + "Creative Computing Morristown, New Jersey" +print; print; print +print "The board is numbered." +print; print; print +print " 1 2 3" +print " 4 5 6" +print " 7 8 9" +print +ans = input("Do you want to 'X' or 'O'? ") +if ans.upper != "X" then ans = "O" +TicTacToe.setMarkers(ans.upper) +winner = null +print +while TicTacToe.moveAvailable and winner == null + + if TicTacToe.next == TicTacToe.AI then + move = TicTacToe.selectAI + TicTacToe.set(TicTacToe.AI, move) + TicTacToe.next = TicTacToe.human + print "The computer moves to..." + else + move = input("Where do you move? ").val + if move < 1 or move > 9 then + print "Thanks for the game." + exit + else if not TicTacToe.set(TicTacToe.human, move-1) then + print "That square is occupied." + print + continue + else + TicTacToe.next = TicTacToe.AI + end if + end if + + TicTacToe.show + winner = TicTacToe.getWinner +end while + +if winner == null then + print "It's a draw. Thank you." +else if winner == TicTacToe.AI then + print "I win, turkey!!" +else + print "You beat me! Good game!" +end if + diff --git a/00_Alternate_Languages/90_Tower/MiniScript/README.md b/00_Alternate_Languages/90_Tower/MiniScript/README.md new file mode 100644 index 000000000..fc9672037 --- /dev/null +++ b/00_Alternate_Languages/90_Tower/MiniScript/README.md @@ -0,0 +1,18 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: +``` + miniscript tower.ms +``` + +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: +``` + load "tower" + run +``` diff --git a/00_Alternate_Languages/90_Tower/MiniScript/tower.ms b/00_Alternate_Languages/90_Tower/MiniScript/tower.ms new file mode 100644 index 000000000..a1846545a --- /dev/null +++ b/00_Alternate_Languages/90_Tower/MiniScript/tower.ms @@ -0,0 +1,202 @@ +kInvalidDisk = 100 +kNotTopDisk = 200 +kNotTower = 300 +kGameOver = 300 + +Tower = {"disks": []} +Tower.init = function + noob = new Tower + noob.disks = [] + return noob +end function + +Tower.height = function + return self.disks.len +end function + +Tower.top = function + if self.height == 0 then return 100 + return self.disks[-1] +end function + +Game = {} +Game.towers = [] +Game.numOfDisks = 0 +Game.rangeOfDisks = [] +Game.selectedDisk = 0 +Game.selectedDiskOn = 0 +Game.selectedTower = 0 +Game.inputErrors = 0 +Game.turns = 0 + +Game.display = function + print + for r in range(7,1) + rowstr = "" + for tower in self.towers + if r > tower.height then + rowstr += " " * 12 + "#" + " " * 7 + else + spaces = (15 - tower.disks[r-1])/2 + disks = " " * 4 + tower.disks[r-1] + rowstr += disks[-5:] + " " * spaces + rowstr += "#" * tower.disks[r-1] + rowstr += " " * spaces + end if + rowstr += " " + end for + print rowstr + end for + rowstr = (" " * 5 + "=" * 15 + " ") * 3 + print rowstr + print +end function + +Game.init = function(num) + if num < 1 or num > 7 then + self.inputErrors += 1 + return false + end if + Game.towers = [] + for i in range(0,2) + Game.towers.push(Tower.init) + end for + + first = self.towers[0] + first.disks = range(15, 17 - num * 2, -2) + self.numOfDisks = num + self.rangeOfDisks = range(17 -num * 2, 15, 2) + + // This game doesn't like to be bothered + // and keeps track of how many incorrect inputs + // are made before it stops the game + self.inputErrors = 0 + self.turns = 0 + return true +end function + +Game.diskStatus = function + n = self.selectedDisk + if self.rangeOfDisks.indexOf(n) == null then + self.inputErrors +=1 + return kInvalidDisk + end if + self.inputErrors = 0 + for i in range(0, self.towers.len - 1) + if self.towers[i].top == n then + self.selectedDiskOn = i + self.inputErrors = 0 + return i + end if + end for + return kNotTopDisk +end function + +Game.pickDisk = function + self.selectedDisk = input("Which disk would you like to move? ").val + return self.diskStatus +end function + +Game.pickTower = function + self.selectedTower = input("Place disk on which needle? ").val - 1 + if not(0<= self.selectedTower and self.selectedTower <= 2) then + self.inputErrors += 1 + return kNotTower + end if + return self.selectedTower +end function + +Game.doneWithYou = function + return self.inputErrors >= 2 +end function + +Game.isFinish = function + return self.towers[0].disks.len == 0 and self.towers[1].disks.len == 0 +end function + +Game.move = function + print "Take turn # " + (self.turns + 1) + status = -1 + self.inputErrors = 0 + while 1 + status = self.pickDisk + if 0 <= status and status <= 2 then break + if status == kInvalidDisk and self.doneWithYou then + print "Stop wasting my time. Go bother someone else." + exit + else if status == kInvalidDisk then + msg = "Illegal entry ... you may only type " + msg += self.rangeOfDisks[0:-1].join(",") + " " + if self.rangeOfDisks.len > 1 then + msg += "or " + end if + msg += "15" + print msg + else if status == kNotTopDisk then + print "That disk is below another. Make another choice." + end if + end while + + self.inputErrors = 0 + while 1 + status = self.pickTower + if 0 <= status and status <= 2 then break + if status == kNotTower and self.doneWithYou then + print "I tried to warn you. But you wouldn't listen." + print "Bye bye, big shot." + exit + else if status == kNotTower then + print "I'll assume you hit the wrong key this time. But watch it," + print "I only allow one mistake." + end if + end while + + if self.selectedDisk > self.towers[self.selectedTower].top then + print "You can't place a larger disk on a top of a smaller one," + print "it may crush it!" + else + n=self.towers[self.selectedDiskOn].disks.pop + self.towers[self.selectedTower].disks.push(n) + self.turns += 1 + self.inputErrors = 0 + end if +end function + + +print " " * 33 + "TOWERS" +print " " * 15 + "Creative Computing Morristown, New Jersey" +print; print +print "You must transfer the disks from the left to the right" +print "tower, one at a time, never putting a larger disk on a" +print "smaller disk." +print + +ans = "Y" +while ans[0].upper == "Y" + while 1 + disks = input("How many disks do you want to move (7 is MAX)? ").val + status = Game.init(disks) + if status == false and Game.doneWithYou then + print "All right, wise guy, if you can't play the game right, I'll" + print "take my puzzle and go home. So long." + exit + else if not status then + print "Sorry, but I can't do that job for you" + else + break + end if + end while + + while not Game.isFinish + Game.display + + Game.move + end while + Game.display + print "Congratulations!" + print "You performed the task in " + Game.turns + " moves." + print + ans = input("Play again (Yes or No)? ") + " " +end while +print +print "Thanks for the game!" diff --git a/00_Alternate_Languages/91_Train/D/README.md b/00_Alternate_Languages/91_Train/D/README.md new file mode 100644 index 000000000..6c77f0a9e --- /dev/null +++ b/00_Alternate_Languages/91_Train/D/README.md @@ -0,0 +1,3 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://dlang.org). \ No newline at end of file diff --git a/00_Alternate_Languages/91_Train/D/train.d b/00_Alternate_Languages/91_Train/D/train.d new file mode 100644 index 000000000..b4e595a0b --- /dev/null +++ b/00_Alternate_Languages/91_Train/D/train.d @@ -0,0 +1,50 @@ +import std.stdio; +import std.random : uniform; + +float abs(float num) { + if(num<0){ + return num*-1; + } + + return num; +} + +void main() { + + writeln("\nTIME - SPEED DISTANCE EXERCISE"); + + bool keep_playing = true; + float error_margin = 5.0; + + while(keep_playing){ + int car_speed = uniform!"[]"(40,65); //Random number between 40 and 65 + int delta_time = uniform!"(]"(4,20); //Between 5 and 20 + int train_speed = uniform!"[)"(20,40); //Between 20 and 39; This is the default if not specified: uniform(x,y) + + writeln("\nA CAR TRAVELING AT ", car_speed, " MPH CAN MAKE A CERTAIN TRIP IN ", delta_time, + " HOURS LESS THAN A TRAIN TRAVELING AT ", train_speed, "MPH." ); + + float input; + write("HOW LONG DOES THE TRIP TAKE BY CAR? "); + readf!"%f\n"(input); + + float car_time = cast(float)delta_time * train_speed / (car_speed - train_speed); + int percent = cast(int)( abs(car_time-input) * 100 / car_time + .5); + + if(percent > error_margin){ + writeln("SORRY. YOU WERE OFF BY ", percent, " PERCENT."); + }else{ + writeln("GOOD! ANSWER WITHIN ", percent, " PERCENT."); + } + writeln("CORRECT ANSWER IS ", car_time, " HOURS."); + + string answer; + write("\nANOTHER PROBLEM (YES OR NO)? "); + readf!"%s\n"(answer); + + if( !(answer == "YES" || answer == "Y" || answer == "yes" || answer == "y") ){ + keep_playing = false; + } + } + +} \ No newline at end of file diff --git a/00_Alternate_Languages/91_Train/MiniScript/README.md b/00_Alternate_Languages/91_Train/MiniScript/README.md new file mode 100644 index 000000000..13928cccc --- /dev/null +++ b/00_Alternate_Languages/91_Train/MiniScript/README.md @@ -0,0 +1,22 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +0. "Try-It!" page on the web: +Go to https://miniscript.org/tryit/, clear the default program from the source code editor, paste in the contents of train.ms, and click the "Run Script" button. + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript train.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "train" + run +``` diff --git a/00_Alternate_Languages/91_Train/MiniScript/train.ms b/00_Alternate_Languages/91_Train/MiniScript/train.ms new file mode 100644 index 000000000..27ce5b98f --- /dev/null +++ b/00_Alternate_Languages/91_Train/MiniScript/train.ms @@ -0,0 +1,28 @@ +// TRAIN +// +// Converted from BASIC to MiniScript by Ryushinaka and Joe Strout + +while true + print "TRAIN" + print "CREATIVE COMPUTER MORRISTOWN, NEW JERSEY" + print "" + print "TIME - SPEED DISTANCE EXERCISE" + + carSpeed = floor(25*rnd + 40) + difference = floor(15*rnd + 5) + trainSpeed = floor(19*rnd + 20) + print " A car traveling " + carSpeed + " MPH can make a certain trip in" + print difference + " hours less than a train traveling at " + trainSpeed + " MPH." + answer = input("How long does the trip take by car? ").val + carTime = difference*trainSpeed/(carSpeed-trainSpeed) + error = round(abs((carTime-answer)*100/answer)) + if error < 5 then + print "GOOD! Answer within " + error + " PERCENT." + else + print "Sorry. You were off by " + floor(error) + " percent." + print "Correct answer is " + round(carTime, 1) + " hours." + end if + + answer = input("Another problem (YES or NO)? ") + if not answer or answer[0].upper != "Y" then break +end while diff --git a/00_Alternate_Languages/91_Train/nim/train.nim b/00_Alternate_Languages/91_Train/nim/train.nim new file mode 100644 index 000000000..aa92c1a70 --- /dev/null +++ b/00_Alternate_Languages/91_Train/nim/train.nim @@ -0,0 +1,38 @@ +import std/[random,strutils] + +var + carSpeed, diff, err, guess, trainSpeed, carTime: int + stillplaying: bool = true + +randomize() # Seed the random number generator + +# Return a tuple that'll be carSpeed, diff, trainSpeed +proc randomNumbers(): (int,int,int) = + result = (rand(41..65), rand(6..20), rand(21..39)) + +# Do we want to play again? +proc tryAgain(): bool = + echo "ANOTHER PROBLEM (YES OR NO)" + var answer = readLine(stdin).normalize() + result = (answer == "y") or (answer == "yes") + +echo spaces(33), "TRAIN" +echo spaces(15), "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" +echo "\n" +echo "TIME - SPEED DISTANCE EXERCISE" + +while stillplaying: + echo "" + (carSpeed, diff, trainSpeed) = randomNumbers() # Get random numbers for prompt + echo "A CAR TRAVELING ", carSpeed, " MPH CAN MAKE A CERTAIN TRIP IN" + echo diff, " HOURS LESS THAN A TRAIN TRAVELING AT ", trainSpeed, " MPH." + echo "HOW LONG DOES THE TRIP TAKE BY CAR?" + guess = readLine(stdin).parseInt() # Get guess + carTime = (diff * trainSpeed / (carSpeed - trainSpeed)).toInt() # Calculate answer + err = (((carTime - guess) * 100) / guess).toInt().abs() # Calculate error to an absolute value + if err > 5: # Error within 5%? + echo "SORRY. YOU WERE OFF BY ", err, " PERCENT." + else: + echo "GOOD! ANSWER WITHIN ", err, " PERCENT." + echo "CORRECT ANSWER IS ", carTime, " HOURS." + stillplaying = tryAgain() diff --git a/00_Alternate_Languages/92_Trap/MiniScript/README.md b/00_Alternate_Languages/92_Trap/MiniScript/README.md new file mode 100644 index 000000000..521d3aff7 --- /dev/null +++ b/00_Alternate_Languages/92_Trap/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript trap.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "trap" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/92_Trap/MiniScript/trap.ms b/00_Alternate_Languages/92_Trap/MiniScript/trap.ms new file mode 100644 index 000000000..687a7a1ea --- /dev/null +++ b/00_Alternate_Languages/92_Trap/MiniScript/trap.ms @@ -0,0 +1,69 @@ +// TRAP +// STEVE ULLMAN, 8-1-72 +// Ported to MiniScript by Ryushinaka and Joe Strout, 2023 + +print " "*34 + "TRAP" +print " "*15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" +print + +// constants: +G = 6 // number of guesses +N = 100 // range of numbers + +// Get a yes/no (or at least y/n) response from the user. +askYesNo = function(prompt) + while true + answer = input(prompt).lower[:1] + if answer == "y" or answer == "n" then return answer + end while +end function + +if askYesNo("Instructions? ") == "y" then + print "I am thinking of a number between 1 and " + N + print "Try to guess my number. On each guess, " + print "you are to enter 2 numbers, trying to trap" + print "my number between the two numbers. I will" + print "tell you if you have trapped my number, if my" + print "number is larger than your two numbers, or if" + print "my number is smaller than your two numbers." + print "If you want to guess one single number, type" + print "your guess for both your trap numbers." + print "You get " + G + " guesses to get my number." + print +end if + +doOneGame = function + computers_number = ceil(N*rnd) + + for Q in range(1,G) + print "" + while true + guess = input("Guess #" + Q + ": ").replace(" ","") + guess = guess.split(",") + if guess.len == 2 then break + print "Enter your guess like: 30,40" + end while + A = guess[0].val + B = guess[1].val + + if A == computers_number and B == computers_number then + print "You got it!!!" + return + else if A <= computers_number and B >= computers_number then + print "You have trapped my number." + else if A > computers_number and B > computers_number then + print "My number is smaller than your trap numbers." + else if A < computers_number and B < computers_number then + print "My number is larger than your trap numbers." + end if + end for + print "Sorry, that's " + G + " guesses. The number was " + computers_number +end function + +// main loop +while true + print + doOneGame + print + if askYesNo("Try Again? ") == "n" then break +end while diff --git a/00_Alternate_Languages/93_23_Matches/MiniScript/23matches.ms b/00_Alternate_Languages/93_23_Matches/MiniScript/23matches.ms new file mode 100644 index 000000000..645082acf --- /dev/null +++ b/00_Alternate_Languages/93_23_Matches/MiniScript/23matches.ms @@ -0,0 +1,70 @@ +print " "*31 + "23 MATCHES" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print + +print "This is a game called '23 Matches'." +print +print "When it is your turn, you may take one, two, or three" +print "matches. The object of the game is not to have to take" +print "the last match." +print +print "Let's flip a coin to see who goes first." +print "If it comes up heads, I will win the toss." +print +matches = 23 +humanTurn = floor(rnd * 2) + +if humanTurn then + print "Tails! You go first." + prompt = "How many do you wish to remove? " +else + print "Heads! I win! Ha! Ha!" + print "Prepare to lose, meatball-nose!!" +end if + +choice = 2 +while matches > 0 + if humanTurn then + if matches < 23 then print "Your turn -- you may take 1, 2 or 3 matches." + prompt = "How many do you wish to remove? " + choice = 0 + if matches == 1 then choice = 1 + while choice == 0 + choice = input(prompt).val + if choice < 1 or choice > 3 or choice > matches then + choice = 0 + print "Very funny! Dummy!" + print "Do you want to play or goof around?" + prompt = "Now, how many matches do you want? " + end if + end while + matches = matches - choice + if matches == 0 then + print "You poor boob! You took the last match! I gotcha!!" + print "Ha ! Ha ! I beat you !!" + print + print "Good bye loser!" + else + print "There are now " + matches + " matches remaining." + print + end if + else + choice_comp = 4 - choice + if matches == 1 then + choice_comp = 1 + else if 1 < matches and matches < 4 then + choice_comp = matches - 1 + end if + matches = matches - choice_comp + if matches == 0 then + print "You won, floppy ears!" + print "Think you're pretty smart!" + print "Let's play again and I'll blow your shoes off!!" + else + print "My turn! I remove " + choice_comp + " matches" + print "The number of matches is now " + matches + print + end if + end if + humanTurn = not humanTurn +end while diff --git a/00_Alternate_Languages/93_23_Matches/MiniScript/README.md b/00_Alternate_Languages/93_23_Matches/MiniScript/README.md new file mode 100644 index 000000000..8b9bc04ca --- /dev/null +++ b/00_Alternate_Languages/93_23_Matches/MiniScript/README.md @@ -0,0 +1,18 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: +``` + miniscript 23matches.ms +``` + +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: +``` + load "23matches" + run +``` diff --git a/00_Alternate_Languages/94_War/MiniScript/README.md b/00_Alternate_Languages/94_War/MiniScript/README.md new file mode 100644 index 000000000..0d964946f --- /dev/null +++ b/00_Alternate_Languages/94_War/MiniScript/README.md @@ -0,0 +1,19 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: + +``` + miniscript war.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: + +``` + load "war" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/94_War/MiniScript/war.ms b/00_Alternate_Languages/94_War/MiniScript/war.ms new file mode 100644 index 000000000..acc432157 --- /dev/null +++ b/00_Alternate_Languages/94_War/MiniScript/war.ms @@ -0,0 +1,65 @@ +print " "*33 + "WAR" +print " "*15 + "CREATIVE COMPUTER MORRISTOWN, NEW JERSEY" +print; print; print + +print "This is the card game of War. Each card is given by SUIT-#" +print "as S-7 for Spade 7." + +// Get a yes/no (or at least y/n) response from the user. +askYesNo = function(prompt) + while true + answer = input(prompt + "? ").lower[:1] + if answer == "y" or answer == "n" then return answer + print "Answer yes or no, please." + end while +end function + +if askYesNo("Do you want directions") == "y" then + print "The computer gives you and it a 'card'. The higher card" + print "(numerically) wins. The game ends when you choose not to" + print "continue or when you have finished the pack." +end if +print +print + +cardValues = "2 3 4 5 6 7 8 9 10 J Q K A".split +deck = [] +for suits in "SHCD" + for value in cardValues + deck.push suits + "-" + value + end for +end for +deck.shuffle + +playerScore = 0 +computerScore = 0 + +while true + m1 = deck.pop + m2 = deck.pop + print "You: " + m1 + "; Computer: " + m2 + n1 = cardValues.indexOf(m1[2:]) + n2 = cardValues.indexOf(m2[2:]) + if n1 > n2 then + playerScore += 1 + print "You win. You have " + playerScore + " and the computer has " + computerScore + else if n2 > n1 then + computerScore += 1 + print "The computer wins!!! You have " + playerScore + " and the computer has " + computerScore + else + print "Tie. No score change." + end if + if not deck then break + if askYesNo("Do you want to continue") == "n" then break +end while + +if not deck then + print + print + print "We have run out of cards. Final score: You: " + playerScore + + " The computer: " + computerScore + print +end if +print "Thanks for playing. It was fun." +print + \ No newline at end of file diff --git a/00_Alternate_Languages/95_Weekday/MiniScript/README.md b/00_Alternate_Languages/95_Weekday/MiniScript/README.md new file mode 100644 index 000000000..218cb4cc9 --- /dev/null +++ b/00_Alternate_Languages/95_Weekday/MiniScript/README.md @@ -0,0 +1,17 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: +``` + miniscript weekday.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: +``` + load "weekday" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/95_Weekday/MiniScript/weekday.ms b/00_Alternate_Languages/95_Weekday/MiniScript/weekday.ms new file mode 100644 index 000000000..65641ebc3 --- /dev/null +++ b/00_Alternate_Languages/95_Weekday/MiniScript/weekday.ms @@ -0,0 +1,182 @@ +TAB = char(9) + +Age = {"m": 0, "d": 0, "y": 0} +Age.init = function(m,d,y) + noob = new Age + noob.m = m;noob.d = d;noob.y = y + return noob +end function + +Age.sub = function(a) + m1 = self.m; d1 = self.d; y1 = self.y + d1 = d1 - a.d + if d1 < 0 then + d1 = d1 + 30 + m1 = m1 - 1 + end if + m1 = m1 - a.m + if m1 < 0then + m1 = m1 + 12 + y1 = y1 - 1 + end if + y1 = y1 - a.y + return Age.init(m1,d1,y1) +end function + +Age.multiply = function(multiplier) + ageInDays = self.y *365 + self.m * 30 + self.d + floor(self.m / 2) + newAge = ageInDays * multiplier + years = floor(newAge/ 365) + leftover = newAge % 365 + months = floor(leftover / 30) + days = floor(leftover % 30) + return Age.init(months, days, years) +end function + +Date = {"m": null, "d": null, "y": null} + +// the number of days between the 1st of one month to the next +Date.daysPerMonth = [0,31,28,31,30,31,30, 31,31,30,31,30] +Date.dayNames = ["Sunday", "Monday", "Tuesday", "Wednesday", +"Thursday", "Friday", "Saturday"] + +Date.init = function(dt) + d = dt.split(",") + if d.len != 3 then return + noob = new Date + noob.m = d[0].val + noob.d = d[1].val + noob.y = d[2].val + return noob +end function + +Date.diff = function(mdy) + dday = self.d - mdy.d + dmonth = self.m - mdy.m + if dday < 0 then + dmonth -= 1 + dday += 30 + end if + + dyear = self.y - mdy.y + if dmonth <0 then + dyear -= 1 + dmonth += 12 + end if + return Age.init(dmonth, dday, dyear) +end function + +Date._isLeapYear = function + return (self.y % 4 == 0 and self.y % 100 != 0) or self.y % 400 == 0 +end function + +Date.value = function + //Not accepting dates Jan 1st 1583 this because the + //transistion to Gregorian calendar occurred in 1582. + + //calculating days since the end of 1582 + years = self.y - 1583 + days = years * 365 + self._leapYears + Date.daysPerMonth[:self.m].sum + self.d + return days // returns 1 for 1,1,1583 +end function + +Date.dayOfWeek = function + // 1,1,1583 is a Saturday + // Date.value calculates a value of 1 for that date + return (self.value + 5) % 7 +end function + +Date.weekday = function + return Date.dayNames[self.dayOfWeek] +end function + +// get # of lear yeaps since the change to Gregorian +Date._leapYears = function + ly = floor((self.y - 1580) / 4) + + //exclude centuries + centuries = floor((self.y - 1500) / 100) + + //unless centuries divisible by 400 + centuries400 = floor((self.y - 1200) / 400) + ly = ly - centuries + centuries400 + + if self._isLeapYear and self.m < 3 then ly -= 1 + return ly +end function + +print " "*32 + "WEEKDAY" +print " "*15 + "Creative Computing Morristown, New Jersey" +print; print; print + +print "WEEKDAY is a computer demonstration that" +print "gives facts about a date of interest to you." +print + +mdy = input("Enter today's date in the form: 3,24,1979? ") +today = Date.init(mdy) + + +mdy = input("Enter day of birth (or other day of interest)? ") +dob = Date.init(mdy) + +print +if dob.y < 1583 then + print "Not prepared to give day of the week prior to 1583" + exit +end if + +verb = " was a " +if today.value < dob.value then verb= " will be a " +if today.value == dob.value then verb = " is a " + +if dob.d == 13 and dob.weekday == "Friday" then + endMsg = " The Thirteenth--Beware!" +else + endMsg = "." +end if +print dob.m + "/" + dob.d + "/" + dob.y + verb + dob.weekday + endMsg + +age = today.diff(dob) + +totalAge = Age.init(age.m,age.d,age.y) +if verb == " was a " then + if dob.d == today.d and dob.m == today.m then print "***HAPPY BIRTHDAY***" + + lines= [["", "YEARS", "MONTHS", "DAYS"]] + lines.push(["", "-----", "------", "----"]) + lines.push(["Your age (if birthdate)", age.y,age.m, age.d]) + + spent = age.multiply(.35) + lines.push(["You have slept", spent.y,spent.m, spent.d]) + totalAge = totalAge.sub(spent) + + spent = age.multiply(.17) + lines.push(["You have eaten", spent.y,spent.m, spent.d]) + totalAge = totalAge.sub(spent) + + if totalAge.y <= 3 then + phrase = "You have played" + else if totalAge.y <= 9 then + phrase = "You have played/studied" + else + phrase = "You have worked/played" + end if + + spent = age.multiply(.23) + lines.push([phrase, spent.y,spent.m, spent.d]) + totalAge = totalAge.sub(spent) + + relaxed = totalAge + lines.push(["You have relaxed", relaxed.y, relaxed.m, relaxed.d]) + for line in lines + col0 = (" " * 25 + line[0])[-25:] + col1 = (line[1] + " " * 6)[:6] + col2 = (line[2] + " " * 7)[:7] + col3 = (line[3] + " " * 5)[:5] + print (col0+" " + col1+col2+col3) + end for +end if + +print +print " "*16 + "*** You may retire in " + (dob.y + 65) + " ***" diff --git a/00_Alternate_Languages/96_Word/MiniScript/README.md b/00_Alternate_Languages/96_Word/MiniScript/README.md new file mode 100644 index 000000000..567a84baf --- /dev/null +++ b/00_Alternate_Languages/96_Word/MiniScript/README.md @@ -0,0 +1,17 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: +``` + miniscript word.ms +``` +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: +``` + load "word" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/96_Word/MiniScript/word.ms b/00_Alternate_Languages/96_Word/MiniScript/word.ms new file mode 100644 index 000000000..190ad6117 --- /dev/null +++ b/00_Alternate_Languages/96_Word/MiniScript/word.ms @@ -0,0 +1,63 @@ +words = ["dinky", "smoke", "water", "grass", "train", "might", + "first", "candy", "champ", "would", "clump", "dopey"] + +playGame = function + secret = words[rnd * words.len] + guesses = 0 + exact = ["-"]*5 + + print "You are starting a new game..." + while true + guess = "" + while guess == "" + print + guess = input("Guess a five letter word. ").lower + if guess == "?" then break + if guess.len != 5 then + guess = "" + print "You must guess a five letter word. Try again." + end if + end while + guesses += 1 + + if guess == "?" then + print "The secret word is " + secret + break + else + common = "" + for i in range(0, 4) + if secret.indexOf(guess[i]) != null then + common += guess[i] + if secret[i] == guess[i] then + exact[i] = guess[i] + end if + end if + end for + print "There were " + common.len + " matches and the common letters were..." + common + print "From the exact letter matches, you know"+"."*16 + exact.join("") + + if secret == guess or secret == exact.join("") then + print "You have guessed the word. It took " + guesses + " guesses!" + break + else if common.len < 2 then + print + print "If you give up, type '?' for your next guess." + end if + end if + end while +end function + +print " " * 33 + "WORD" +print " " * 15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" +print +print "I am thinking of a word -- you guess it. I will give you" +print "clues to help you get it. Good luck!" +print + +playing = "y" +while playing == "y" + playGame + print + playing = input("Want to play again? ") + " " + playing = playing[0].lower +end while diff --git a/00_Common/rust/Cargo.lock b/00_Common/rust/Cargo.lock new file mode 100644 index 000000000..2c85bc520 --- /dev/null +++ b/00_Common/rust/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "basic_input" +version = "0.1.0" diff --git a/00_Common/rust/Cargo.toml b/00_Common/rust/Cargo.toml new file mode 100644 index 000000000..23c680f4e --- /dev/null +++ b/00_Common/rust/Cargo.toml @@ -0,0 +1,7 @@ +# +# Although there is currently only the `basic_input` crate here, I am anticipating +# more utility crates for rust ports, as well as proc macro crates. +# +[workspace] +resolver = "2" +members = [ "crates/basic_input" ] diff --git a/00_Common/rust/crates/basic_input/Cargo.toml b/00_Common/rust/crates/basic_input/Cargo.toml new file mode 100644 index 000000000..d32bdb628 --- /dev/null +++ b/00_Common/rust/crates/basic_input/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "basic_input" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/00_Common/rust/crates/basic_input/src/input.rs b/00_Common/rust/crates/basic_input/src/input.rs new file mode 100644 index 000000000..0955d8ec4 --- /dev/null +++ b/00_Common/rust/crates/basic_input/src/input.rs @@ -0,0 +1,354 @@ +mod input_state; +mod io_layer; + +use input_state::InputState; +use io_layer::IoLayer; + +/// Main entrypoint for this module. Display a prompt and read one or more values from the user. +/// See [the crate docs](module@crate) +pub fn get_input>(prompt: S) -> BI { + let mut io = IoLayer::get_io(); + get_input_from_io(prompt, &mut io) +} + +// This is the trait that we return to the user, which allows us to have +// a different function for each return type. +// let one_input : f32 = get_input("..."); +// let (x, y) : (f32, f32) = get_input("..."); +// let (a, b, c) : (f32, f32, f32) = get_input("..."); +pub trait BasicInput { + fn from_string>(s: S, state: &mut InputState) -> Result + where + Self: Sized; +} + +// -------------------------------------------------------------------------------- + +impl BasicInput for f32 { + fn from_string>(s: S, state: &mut InputState) -> Result { + state.input_f32s(s, 1)?; + Ok(state.data[0].as_f32()) + } +} + +impl BasicInput for (f32, f32) { + fn from_string>(s: S, state: &mut InputState) -> Result { + state.input_f32s(s, 2)?; + + Ok((state.data[0].as_f32(), state.data[1].as_f32())) + } +} + +impl BasicInput for (f32, f32, f32) { + fn from_string>(s: S, state: &mut InputState) -> Result { + state.input_f32s(s, 3)?; + + Ok(( + state.data[0].as_f32(), + state.data[1].as_f32(), + state.data[2].as_f32(), + )) + } +} + +impl BasicInput for (f32, f32, f32, f32) { + fn from_string>(s: S, state: &mut InputState) -> Result { + state.input_f32s(s, 4)?; + + Ok(( + state.data[0].as_f32(), + state.data[1].as_f32(), + state.data[2].as_f32(), + state.data[3].as_f32(), + )) + } +} + +impl BasicInput for (f32, f32, f32, f32, f32, f32, f32, f32, f32, f32) { + fn from_string>(s: S, state: &mut InputState) -> Result { + state.input_f32s(s, 10)?; + + Ok(( + state.data[0].as_f32(), + state.data[1].as_f32(), + state.data[2].as_f32(), + state.data[3].as_f32(), + state.data[4].as_f32(), + state.data[5].as_f32(), + state.data[6].as_f32(), + state.data[7].as_f32(), + state.data[8].as_f32(), + state.data[9].as_f32(), + )) + } +} + +impl BasicInput for String { + fn from_string>(s: S, state: &mut InputState) -> Result { + state.input_strings(s, 1)?; + Ok(state.data[0].as_string()) + } +} + +impl BasicInput for (String, String) { + fn from_string>(s: S, state: &mut InputState) -> Result { + state.input_strings(s, 2)?; + Ok((state.data[0].as_string(), state.data[1].as_string())) + } +} + +// -------------------------------------------------------------------------------- + +fn get_input_from_io>(prompt: S, io: &mut IoLayer) -> BI { + let mut state = InputState { + io, + data: Vec::new(), + }; + + state.io.raw_output_string(format!("{}? ", prompt.as_ref())); + + loop { + let buffer = state.io.raw_input_string(); + match BI::from_string(buffer, &mut state) { + Ok(result) => return result, + Err(e) => match e { + BasicInputError::ParseFailed => { + state.io.raw_output_string("?REENTER\n"); + state.io.raw_output_string(format!("{}? ", prompt.as_ref())); + state.data.clear(); + } + BasicInputError::MissingInput => { + state.io.raw_output_string("??? "); + } + }, + } + } +} + +#[derive(Debug)] +pub enum BasicInputError { + ParseFailed, + MissingInput, +} + +#[cfg(test)] +mod test { + use crate::input::get_input_from_io; + use crate::input::io_layer::{IoLayer, TextInput, TextOutput}; + use std::collections::VecDeque; + + fn getio() -> IoLayer { + IoLayer { + output: Box::new(WriteTestLayer::new()), + input: Box::new(ReadTestLayer::new()), + } + } + + #[test] + fn test_trait_single_input() { + let mut testio = getio(); + testio.input.set_input_text("2"); + let result: f32 = get_input_from_io("prompt", &mut testio); + assert_eq!(2.0, result); + } + + #[test] + fn test_trait_dual_input() { + let mut testio = getio(); + testio.input.set_input_text("2,3"); + let result: (f32, f32) = get_input_from_io("prompt", &mut testio); + assert_eq!( + "prompt? ".to_string(), + testio.output.get_last_line().unwrap() + ); + assert_eq!(2.0, result.0); + assert_eq!(3.0, result.1); + } + + #[test] + fn test_invalid_input() { + let mut testio = getio(); + testio.input.set_input_text("abc"); + testio.input.set_input_text("2"); + let result: f32 = get_input_from_io("prompt", &mut testio); + assert_eq!( + "prompt? ".to_string(), + testio.output.get_last_line().unwrap() + ); + assert_eq!( + "?REENTER\n".to_string(), + testio.output.get_last_line().unwrap() + ); + assert_eq!(2.0, result); + } + + #[test] + fn test_multiple_invalid_input() { + let mut testio = getio(); + testio.input.set_input_text("abc"); + testio.input.set_input_text("2,a"); + testio.input.set_input_text("2,3"); + let result: (f32, f32) = get_input_from_io("prompt", &mut testio); + assert_eq!( + "prompt? ".to_string(), + testio.output.get_last_line().unwrap() + ); + assert_eq!( + "?REENTER\n".to_string(), + testio.output.get_last_line().unwrap() + ); + assert_eq!( + "prompt? ".to_string(), + testio.output.get_last_line().unwrap() + ); + assert_eq!( + "?REENTER\n".to_string(), + testio.output.get_last_line().unwrap() + ); + assert_eq!( + "prompt? ".to_string(), + testio.output.get_last_line().unwrap() + ); + assert_eq!(2.0, result.0); + assert_eq!(3.0, result.1); + } + + #[test] + fn two_inputs_over_multiple_lines() { + let mut testio = getio(); + testio.input.set_input_text("2"); + testio.input.set_input_text("3"); + let result: (f32, f32) = get_input_from_io("prompt", &mut testio); + assert_eq!( + "prompt? ".to_string(), + testio.output.get_last_line().unwrap() + ); + assert_eq!("??? ".to_string(), testio.output.get_last_line().unwrap()); + assert_eq!(2.0, result.0); + assert_eq!(3.0, result.1); + } + + #[test] + fn test_triple_f32() { + let mut testio = getio(); + testio.input.set_input_text("1, 2, 3"); + let result: (f32, f32, f32) = get_input_from_io("enter 3 numbers", &mut testio); + assert_eq!(1.0, result.0); + assert_eq!(2.0, result.1); + assert_eq!(3.0, result.2); + } + + #[test] + fn test_input_string() { + let mut testio = getio(); + testio.input.set_input_text("hello"); + let result: String = get_input_from_io("String prompt", &mut testio); + assert_eq!("hello".to_string(), result); + } + + #[test] + fn test_two_input_strings() { + let mut testio = getio(); + testio.input.set_input_text("hello, world"); + let result: (String, String) = get_input_from_io("Double string prompt", &mut testio); + assert_eq!("hello".to_string(), result.0); + assert_eq!("world".to_string(), result.1); + } + + #[test] + fn test_four_nums() { + let mut testio = getio(); + testio.input.set_input_text("1, 2, 3, 4"); + let result: (f32, f32, f32, f32) = get_input_from_io("whatever", &mut testio); + assert_eq!(4.0, result.3); + } + + #[test] + fn test_extra_ignored() { + let mut testio = getio(); + testio.input.set_input_text("1, 2, 3, 4"); + let result: (f32, f32) = get_input_from_io("whatever", &mut testio); + assert_eq!(Some("whatever? "), testio.output.get_last_line().as_deref()); + assert_eq!( + Some("?EXTRA IGNORED\n"), + testio.output.get_last_line().as_deref() + ); + assert_eq!(2.0, result.1); + } + + #[test] + fn test_extra_ignored_bad_parse() { + let mut testio = getio(); + testio.input.set_input_text("1, 2, hello"); + let result: (f32, f32) = get_input_from_io("whatever", &mut testio); + assert_eq!(Some("whatever? "), testio.output.get_last_line().as_deref()); + assert_eq!( + Some("?EXTRA IGNORED\n"), + testio.output.get_last_line().as_deref() + ); + assert_eq!(2.0, result.1); + } + + #[test] + fn test_ten_nums() { + let mut testio = getio(); + testio.input.set_input_text("1, 2, 3, 4, 5, 6, 7, 8, 9, 10"); + let result: (f32, f32, f32, f32, f32, f32, f32, f32, f32, f32) = + get_input_from_io("whatever", &mut testio); + assert_eq!(Some("whatever? "), testio.output.get_last_line().as_deref()); + assert_eq!(None, testio.output.get_last_line()); + assert_eq!(10.0, result.9); + } + + // + // Helpers for test i/o + // + pub struct ReadTestLayer { + next_line: VecDeque, + } + + impl ReadTestLayer { + pub fn new() -> Self { + Self { + next_line: VecDeque::new(), + } + } + } + + impl TextInput for ReadTestLayer { + fn read(&mut self) -> String { + if let Some(line) = self.next_line.pop_front() { + println!(">> returning {line}"); + line + } else { + panic!("Test tried to read too much input"); + } + } + + fn set_input_text(&mut self, text: &str) { + self.next_line.push_back(text.to_string()); + } + } + + pub struct WriteTestLayer { + last_items_written: VecDeque, + } + + impl WriteTestLayer { + pub fn new() -> Self { + Self { + last_items_written: VecDeque::new(), + } + } + } + + impl TextOutput for WriteTestLayer { + fn write(&mut self, to_write: &str) { + self.last_items_written.push_back(to_write.into()); + } + + fn get_last_line(&mut self) -> Option { + self.last_items_written.pop_front() + } + } +} diff --git a/00_Common/rust/crates/basic_input/src/input/input_state.rs b/00_Common/rust/crates/basic_input/src/input/input_state.rs new file mode 100644 index 000000000..58c19e88f --- /dev/null +++ b/00_Common/rust/crates/basic_input/src/input/input_state.rs @@ -0,0 +1,77 @@ +use crate::{BasicInputError, input::io_layer::IoLayer}; + +pub struct InputState<'a> { + pub(crate) io: &'a mut IoLayer, + pub(crate) data: Vec, +} + +impl<'a> InputState<'a> { + pub(crate) fn input_f32s>( + &mut self, + s: S, + count: usize, + ) -> Result<(), BasicInputError> { + let vals = crate::parse::parse(s); + + for v in vals { + if self.data.len() >= count { + self.io.raw_output_string("?EXTRA IGNORED\n"); + return Ok(()); + } + + let value = v + .trim() + .parse::() + .map_err(|_| BasicInputError::ParseFailed)?; + self.data.push(Value::Num(value)); + } + + if self.data.len() < count { + return Err(BasicInputError::MissingInput); + } + + Ok(()) + } + + pub(crate) fn input_strings>( + &mut self, + s: S, + count: usize, + ) -> Result<(), BasicInputError> { + let vals = crate::parse::parse(s); + + for v in vals { + let value = v.trim(); + self.data.push(Value::Str(value.to_string())); + } + if self.data.len() < count { + return Err(BasicInputError::MissingInput); + } + if self.data.len() > count { + self.io.raw_output_string("?EXTRA IGNORED\n"); + } + Ok(()) + } +} + +#[derive(Clone, Debug)] +pub enum Value { + Num(f32), + Str(String), +} + +impl Value { + pub(crate) fn as_f32(&self) -> f32 { + match self { + Value::Num(num) => *num, + Value::Str(_) => panic!("tried to extract a number, but Value was a String"), + } + } + + pub(crate) fn as_string(&self) -> String { + match self { + Value::Num(_) => panic!("tried to extract a string, but Value was a Num"), + Value::Str(s) => s.clone(), + } + } +} diff --git a/00_Common/rust/crates/basic_input/src/input/io_layer.rs b/00_Common/rust/crates/basic_input/src/input/io_layer.rs new file mode 100644 index 000000000..5939da31f --- /dev/null +++ b/00_Common/rust/crates/basic_input/src/input/io_layer.rs @@ -0,0 +1,80 @@ +use std::io::{BufRead, StdoutLock, Write}; + +pub struct IoLayer { + pub(super) output: Box, + pub(super) input: Box, +} + +impl IoLayer { + pub(crate) fn get_io() -> Self { + Self { + output: StdoutLayer::new(), + input: StdinLayer::new(), + } + } + + pub(crate) fn raw_output_string>(&mut self, s: S) { + self.output.write(s.as_ref()); + } + + pub(crate) fn raw_input_string(&mut self) -> String { + self.input.read() + } +} + +pub(crate) trait TextOutput { + fn write(&mut self, to_write: &str); + #[cfg(test)] + fn get_last_line(&mut self) -> Option; +} + +pub(crate) trait TextInput { + fn read(&mut self) -> String; + #[cfg(test)] + fn set_input_text(&mut self, text: &str); +} + +struct StdoutLayer<'a> { + stdout: StdoutLock<'a>, +} + +impl<'a> TextOutput for StdoutLayer<'a> { + fn write(&mut self, to_write: &str) { + print!("{to_write}"); + let _ = self.stdout.flush(); + } + + #[cfg(test)] + fn get_last_line(&mut self) -> Option { + unreachable!(); + } +} + +struct StdinLayer; +impl TextInput for StdinLayer { + fn read(&mut self) -> String { + let mut buffer = String::new(); + let stdin = std::io::stdin(); + let mut handle = stdin.lock(); + let _ = handle.read_line(&mut buffer); + buffer + } + + #[cfg(test)] + fn set_input_text(&mut self, _text: &str) { + unreachable!(); + } +} + +impl<'a> StdoutLayer<'a> { + fn new() -> Box { + Box::new(Self { + stdout: std::io::stdout().lock(), + }) + } +} +impl StdinLayer { + fn new() -> Box { + Box::new(Self) + } +} diff --git a/00_Common/rust/crates/basic_input/src/lib.rs b/00_Common/rust/crates/basic_input/src/lib.rs new file mode 100644 index 000000000..8d14d9a5f --- /dev/null +++ b/00_Common/rust/crates/basic_input/src/lib.rs @@ -0,0 +1,42 @@ +//! A library for reading input from a basic program. +//! +//! In BASIC, you can have a statement like +//! ```basic +//! 10 INPUT "PROMPT";A$ +//! ``` +//! This crate emulates this with: +//! ```no_run +//! let a_value: String = basic_input::get_input("PROMPT"); +//! ``` +//! +//! BASIC also allows programmers to read multiple values at a time. For example, to +//! read two numbers, the program will have `INPUT "PROMPT";X1,Y1`. This can be done +//! in Rust with a tuple: +//! ```no_run +//! let (x1, y1): (f32, f32) = basic_input::get_input("Enter coordinates"); +//! ``` +//! The code will automatically re-prompt if the user doesn't enter all the values. +//! +//! Guided by the README.md under 00_Common, we know that there are `INPUT` statements +//! in the various games which ask for +//! +//! * 1 or 2 strings, +//! * 1-4 floating-point numbers, or 10(!) floating-point numbers +//! +//! and there are no other kinds of input required. All of these have been accounted +//! for in the `input` module (see `impl BasicInput for `) but they are easy +//! enough to extend/enhance for different kinds of input. +//! +//! To use this in your Rust code, you can reference the local crate by adding the +//! following line to your `Cargo.toml`: +//! +//! ```toml +//! basic_input = { path = "../../00_Common/rust/crates/basic_input" } +//! ``` +//! +//! (adjust the number of `../` for the depth of your own subdirectory) + +pub mod input; +mod parse; + +pub use input::*; diff --git a/00_Common/rust/crates/basic_input/src/parse.rs b/00_Common/rust/crates/basic_input/src/parse.rs new file mode 100644 index 000000000..fa2ac9eff --- /dev/null +++ b/00_Common/rust/crates/basic_input/src/parse.rs @@ -0,0 +1,85 @@ +pub(crate) fn parse>(buffer: B) -> Vec { + let mut result = Vec::new(); + let mut in_quote = false; + let mut need_comma = false; + + let mut current = String::new(); + for ch in buffer.as_ref().chars() { + match (ch, in_quote) { + (',', false) => { + result.push(current.trim().to_string()); + current.clear(); + } + ('"', true) => { + need_comma = true; + result.push(current.trim().to_string()); + current.clear(); + } + ('"', false) => { + in_quote = true; + } + + (',', true) if need_comma => { + need_comma = false; + in_quote = false; + } + (',', true) => current.push(ch), + + _ => { + current.push(ch); + } + } + } + if !current.is_empty() { + result.push(current.trim().to_string()); + } + + result +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_single_value() { + let result = parse("123"); + assert_eq!("123".to_string(), result[0]); + assert_eq!(1, result.len()); + } + + #[test] + fn parse_two_values() { + let result = parse("123, 456"); + assert_eq!("123".to_string(), result[0]); + assert_eq!("456".to_string(), result[1]); + assert_eq!(2, result.len()); + } + + #[test] + fn parse_quoted_value() { + let result = parse("123, \"456,789\", 10"); + assert_eq!("123".to_string(), result[0]); + assert_eq!("456,789".to_string(), result[1]); + assert_eq!("10".to_string(), result[2]); + assert_eq!(3, result.len()); + } + + #[test] + fn parse_missing_closing_quote() { + let result = parse("123, \"456,789, 10"); + assert_eq!("123".to_string(), result[0]); + assert_eq!("456,789, 10".to_string(), result[1]); + assert_eq!(2, result.len()); + } + + #[test] + fn parse_quoted_value_twice() { + let result = parse("123, \"456,789\", 10, 11"); + assert_eq!("123".to_string(), result[0]); + assert_eq!("456,789".to_string(), result[1]); + assert_eq!("10".to_string(), result[2]); + assert_eq!("11".to_string(), result[3]); + assert_eq!(4, result.len()); + } +} diff --git a/00_Utilities/TODO.md b/00_Utilities/TODO.md index 6ccc8e7ff..714807486 100644 --- a/00_Utilities/TODO.md +++ b/00_Utilities/TODO.md @@ -3,98 +3,99 @@ ------------------------------ | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- 01_Acey_Ducey | ✅ | ✅ | ✅ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ [02_Amazing](../02_Amazing) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ✅ -[03_Animal](../03_Animal) | ✅ | ✅ | ✅ | ✅ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ -[04_Awari](../04_Awari) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ +[03_Animal](../03_Animal) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⬜️ +[04_Awari](../04_Awari) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ⬜️ [05_Bagels](../05_Bagels) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⬜️ -[06_Banner](../06_Banner) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ✅ -[07_Basketball](../07_Basketball) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ -[08_Batnum](../08_Batnum) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ -[09_Battle](../09_Battle) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ +[06_Banner](../06_Banner) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ✅ +[07_Basketball](../07_Basketball) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ +[08_Batnum](../08_Batnum) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ✅ +[09_Battle](../09_Battle) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ✅ | ⬜️ [10_Blackjack](../10_Blackjack) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ -[11_Bombardment](../11_Bombardment) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ -[12_Bombs_Away](../12_Bombs_Away) | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ -[13_Bounce](../13_Bounce) | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ -[14_Bowling](../14_Bowling) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ -[15_Boxing](../15_Boxing) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ -[16_Bug](../16_Bug) | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ -[17_Bullfight](../17_Bullfight) | ✅ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ -[18_Bullseye](../18_Bullseye) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ +[11_Bombardment](../11_Bombardment) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ⬜️ +[12_Bombs_Away](../12_Bombs_Away) | ⬜️ | ✅ | ✅ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ✅ | ⬜️ +[13_Bounce](../13_Bounce) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ +[14_Bowling](../14_Bowling) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ +[15_Boxing](../15_Boxing) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ +[16_Bug](../16_Bug) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ +[17_Bullfight](../17_Bullfight) | ✅ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ +[18_Bullseye](../18_Bullseye) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ✅ | ⬜️ [19_Bunny](../19_Bunny) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ -[20_Buzzword](../20_Buzzword) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ -[21_Calendar](../21_Calendar) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ +[20_Buzzword](../20_Buzzword) | ✅ | ✅ | ✅ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ✅ | ⬜️ +[21_Calendar](../21_Calendar) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ⬜️ [22_Change](../22_Change) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ✅ | ⬜️ -[23_Checkers](../23_Checkers) | ✅ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ -[24_Chemist](../24_Chemist) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ -[25_Chief](../25_Chief) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ -[26_Chomp](../26_Chomp) | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ +[23_Checkers](../23_Checkers) | ✅ | ⬜️ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ +[24_Chemist](../24_Chemist) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ✅ | ⬜️ +[25_Chief](../25_Chief) | ✅ | ✅ | ✅ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ✅ | ⬜️ +[26_Chomp](../26_Chomp) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ [27_Civil_War](../27_Civil_War) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ -[28_Combat](../28_Combat) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ -[29_Craps](../29_Craps) | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ -[30_Cube](../30_Cube) | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ +[28_Combat](../28_Combat) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ +[29_Craps](../29_Craps) | ⬜️ | ✅ | ✅ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ✅ | ⬜️ +[30_Cube](../30_Cube) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ [31_Depth_Charge](../31_Depth_Charge) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ -[32_Diamond](../32_Diamond) | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ -[33_Dice](../33_Dice) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ✅ -[34_Digits](../34_Digits) | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ -[35_Even_Wins](../35_Even_Wins) | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ -[36_Flip_Flop](../36_Flip_Flop) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ +[32_Diamond](../32_Diamond) | ✅ | ✅ | ✅ | ✅ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ⬜️ +[33_Dice](../33_Dice) | ✅ | ✅ | ✅ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ +[34_Digits](../34_Digits) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ +[35_Even_Wins](../35_Even_Wins) | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ✅ | ⬜️ +[36_Flip_Flop](../36_Flip_Flop) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ⬜️ [37_Football](../37_Football) | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ -[38_Fur_Trader](../38_Fur_Trader) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ +[38_Fur_Trader](../38_Fur_Trader) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ [39_Golf](../39_Golf) | ✅ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ [40_Gomoko](../40_Gomoko) | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ -[41_Guess](../41_Guess) | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ⬜️ -[42_Gunner](../42_Gunner) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ -[43_Hammurabi](../43_Hammurabi) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ +[41_Guess](../41_Guess) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ⬜️ +[42_Gunner](../42_Gunner) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ +[43_Hammurabi](../43_Hammurabi) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ✅ | ⬜️ [44_Hangman](../44_Hangman) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ -[45_Hello](../45_Hello) | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ +[45_Hello](../45_Hello) | ⬜️ | ✅ | ✅ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ [46_Hexapawn](../46_Hexapawn) | ✅ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ -[47_Hi-Lo](../47_Hi-Lo) | ✅ | ✅ | ✅ | ✅ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ⬜️ +[47_Hi-Lo](../47_Hi-Lo) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⬜️ [48_High_IQ](../48_High_IQ) | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ -[49_Hockey](../49_Hockey) | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ | ⬜️ | ⬜️ | ⬜️ | ⬜️ -[50_Horserace](../50_Horserace) | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ -[51_Hurkle](../51_Hurkle) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ +[49_Hockey](../49_Hockey) | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ +[50_Horserace](../50_Horserace) | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ✅ | ⬜️ +[51_Hurkle](../51_Hurkle) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ⬜️ [52_Kinema](../52_Kinema) | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ -[53_King](../53_King) | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ +[53_King](../53_King) | ✅ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ⬜️ | ✅ | ⬜️ [54_Letter](../54_Letter) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ⬜️ -[55_Life](../55_Life) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ -[56_Life_for_Two](../56_Life_for_Two) | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ | ⬜️ | ⬜️ | ⬜️ | ⬜️ -[57_Literature_Quiz](../57_Literature_Quiz) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ -[58_Love](../58_Love) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ +[55_Life](../55_Life) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ⬜️ +[56_Life_for_Two](../56_Life_for_Two) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ +[57_Literature_Quiz](../57_Literature_Quiz) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ✅ | ⬜️ +[58_Love](../58_Love) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ⬜️ [59_Lunar_LEM_Rocket](../59_Lunar_LEM_Rocket) | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ✅ | ⬜️ [60_Mastermind](../60_Mastermind) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ✅ | ⬜️ [61_Math_Dice](../61_Math_Dice) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ⬜️ -[62_Mugwump](../62_Mugwump) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ -[63_Name](../63_Name) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ -[64_Nicomachus](../64_Nicomachus) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ +[62_Mugwump](../62_Mugwump) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ✅ | ⬜️ +[63_Name](../63_Name) | ✅ | ✅ | ✅ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ +[64_Nicomachus](../64_Nicomachus) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ✅ | ⬜️ [65_Nim](../65_Nim) | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ [66_Number](../66_Number) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ✅ | ⬜️ -[67_One_Check](../67_One_Check) | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ -[68_Orbit](../68_Orbit) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ -[69_Pizza](../69_Pizza) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ -[70_Poetry](../70_Poetry) | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ -[71_Poker](../71_Poker) | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ⬜️ | ⬜️ | ⬜️ | ⬜️ -[72_Queen](../72_Queen) | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ +[67_One_Check](../67_One_Check) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ +[68_Orbit](../68_Orbit) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ⬜️ +[69_Pizza](../69_Pizza) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ⬜️ +[70_Poetry](../70_Poetry) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ +[71_Poker](../71_Poker) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ⬜️ | ⬜️ | ⬜️ | ⬜️ +[72_Queen](../72_Queen) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ✅ | ⬜️ [73_Reverse](../73_Reverse) | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ✅ -[74_Rock_Scissors_Paper](../74_Rock_Scissors_Paper) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ⬜️ -[75_Roulette](../75_Roulette) | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ -[76_Russian_Roulette](../76_Russian_Roulette) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ -[77_Salvo](../77_Salvo) | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ -[78_Sine_Wave](../78_Sine_Wave) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ⬜️ +[74_Rock_Scissors_Paper](../74_Rock_Scissors_Paper) | ✅ | ✅ | ✅ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ✅ | ⬜️ +[75_Roulette](../75_Roulette) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ✅ | ⬜️ +[76_Russian_Roulette](../76_Russian_Roulette) | ✅ | ✅ | ✅ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ✅ | ⬜️ +[77_Salvo](../77_Salvo) | ✅ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ +[78_Sine_Wave](../78_Sine_Wave) | ✅ | ✅ | ✅ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ✅ | ⬜️ [79_Slalom](../79_Slalom) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ [80_Slots](../80_Slots) | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ -[81_Splat](../81_Splat) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ +[81_Splat](../81_Splat) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ✅ | ⬜️ [82_Stars](../82_Stars) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ⬜️ [83_Stock_Market](../83_Stock_Market) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ -[84_Super_Star_Trek](../84_Super_Star_Trek) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ -[85_Synonym](../85_Synonym) | ✅ | ✅ | ✅ | ✅ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ -[86_Target](../86_Target) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ +[84_Super_Star_Trek](../84_Super_Star_Trek) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ✅ | ⬜️ +[85_Synonym](../85_Synonym) | ✅ | ✅ | ✅ | ✅ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ⬜️ +[86_Target](../86_Target) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ [87_3-D_Plot](../87_3-D_Plot) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ [88_3-D_Tic-Tac-Toe](../88_3-D_Tic-Tac-Toe) | ✅ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ [89_Tic-Tac-Toe](../89_Tic-Tac-Toe) | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ✅ | ✅ | ⬜️ | ✅ | ⬜️ -[90_Tower](../90_Tower) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ -[91_Train](../91_Train) | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ -[92_Trap](../92_Trap) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ -[93_23_Matches](../93_23_Matches) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ +[90_Tower](../90_Tower) | ✅ | ✅ | ✅ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ✅ | ⬜️ +[91_Train](../91_Train) | ⬜️ | ✅ | ✅ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ✅ | ⬜️ +[92_Trap](../92_Trap) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ⬜️ +[93_23_Matches](../93_23_Matches) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ⬜️ [94_War](../94_War) | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ⬜️ [95_Weekday](../95_Weekday) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ✅ | ⬜️ +[96_Word](../96_Word) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ✅ ------------------------------ | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- -Sum of 95 | 63 | 80 | 95 | 7 | 2 | 60 | 92 | 49 | 20 | 6 +Sum of 96 | 79 | 83 | 96 | 9 | 16 | 75 | 95 | 51 | 58 | 7 diff --git a/00_Utilities/find-missing-implementations.js b/00_Utilities/find-missing-implementations.js index 61d849feb..a458f190e 100644 --- a/00_Utilities/find-missing-implementations.js +++ b/00_Utilities/find-missing-implementations.js @@ -16,10 +16,13 @@ const languages = [ { name: "csharp", extension: "cs" }, { name: "java", extension: "java" }, { name: "javascript", extension: "html" }, + { name: "kotlin", extension: "kt" }, + { name: "lua", extension: "lua" }, { name: "pascal", extension: "pas" }, { name: "perl", extension: "pl" }, { name: "python", extension: "py" }, { name: "ruby", extension: "rb" }, + { name: "rust", extension: "rs" }, { name: "vbnet", extension: "vb" }, ]; diff --git a/00_Utilities/find-unimplemented.js b/00_Utilities/find-unimplemented.js index e048d9ce2..fb88fdbe2 100644 --- a/00_Utilities/find-unimplemented.js +++ b/00_Utilities/find-unimplemented.js @@ -17,11 +17,14 @@ let languages = [ { name: "csharp", extension: "cs" }, { name: "java", extension: "java" }, { name: "javascript", extension: "html" }, + { name: "kotlin", extension: "kt" }, + { name: "lua", extension: "lua" }, { name: "pascal", extension: "pas" }, { name: "perl", extension: "pl" }, { name: "python", extension: "py" }, { name: "ruby", extension: "rb" }, { name: "vbnet", extension: "vb" }, + { name: "rust", extension: "rs" }, ]; const getFilesRecursive = async (path, extension) => { diff --git a/01_Acey_Ducey/README.md b/01_Acey_Ducey/README.md index 26856f717..0fabfb087 100644 --- a/01_Acey_Ducey/README.md +++ b/01_Acey_Ducey/README.md @@ -15,10 +15,13 @@ As published in Basic Computer Games (1978): Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html +#### Known Bugs + +- Entering a negative bet allows you to gain arbitrarily large amounts of money upon losing the round. #### Porting Notes -(please note any difficulties or challenges in porting here) +- The assignment `N = 100` in line 100 has no effect; variable `N` is not used anywhere else in the program. #### External Links - Common Lisp: https://github.com/koalahedron/lisp-computer-games/blob/master/01%20Acey%20Ducey/common-lisp/acey-deucy.lisp diff --git a/01_Acey_Ducey/elixir/acey_ducey.exs b/01_Acey_Ducey/elixir/acey_ducey.exs new file mode 100644 index 000000000..31308df78 --- /dev/null +++ b/01_Acey_Ducey/elixir/acey_ducey.exs @@ -0,0 +1,97 @@ +######################################################## +# +# Acey Ducey +# +# From: BASIC Computer Games (1978) +# Edited by David Ahl +# +# "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. The game keeps +# going on until you lose all your money or interrupt +# the program. +# +# "The original BASIC program author was Bill Palmby +# of Prairie View, Illinois." +# +# To run this file: +# > mix run acey_ducey.exs --no-mix-exs +# +# This uses the following techniques: +# +# - The `Game` module uses a recursive `play/1` function. +# - The `Game` module stores the game state in a `%Game{}` struct. +# - The classic 52 playing card deck is set as a module attribute generated via a comprehension. +# - The deck is automatically shuffled when there are less than 3 cards remaining in the deck. +# - The initial deck defaults to an empty list which triggers a shuffle when the game begins. +# - The initial funds defaults to 100 but it can be explicitly set in the `%Game{}` struct. +# - The prompt to place a bet will automatically re-prompt when given an invalid input. +# - The bets are assumed to be the whole integers for simplicity. +# +######################################################## + +defmodule Game do + @deck for suit <- [:spades, :hearts, :clubs, :diamonds], value <- 1..13, do: {suit, value} + + defstruct funds: 100, deck: [] + + def play(), do: play(%__MODULE__{}) # for convenience + + def play(%__MODULE__{funds: funds}) when funds <= 0, do: IO.puts("~~~ game over ~~~") + def play(%__MODULE__{deck: deck} = game) when length(deck) < 3, do: play(%{game | deck: Enum.shuffle(@deck)}) + def play(%__MODULE__{deck: deck, funds: funds} = game) do + IO.gets("\n") + + [first_card, second_card, third_card | remaining_deck] = deck + + IO.puts("~~~ new round ~~~") + IO.puts("first card: #{format(first_card)}") + IO.puts("second card: #{format(second_card)}\n") + IO.puts("funds: $#{funds}") + + bet = prompt_to_place_bet(funds) + new_funds = if win?(first_card, second_card, third_card), do: funds + bet, else: funds - bet + + IO.puts("\nthird card: #{format(third_card)}") + IO.puts("funds: $#{funds} => $#{new_funds}") + IO.puts("~~~ end round ~~~\n") + + play(%{game | deck: remaining_deck, funds: new_funds}) + end + + # re-prompt if invalid integer and/or out of bounds + defp prompt_to_place_bet(funds) do + input = IO.gets("place your bet: $") + case Integer.parse(input) do + {bet, _} when bet in 0..funds -> bet + _ -> prompt_to_place_bet(funds) + end + end + + # for a stricter win condition (non-inclusive) + defp win?({_, first}, {_, second}, {_, third}) do + [floor, ceiling] = Enum.sort([first, second]) + (floor < third) && (third < ceiling) + end + # for a looser win condition (inclusive) + #defp win?({_, first}, {_, second}, {_, third}) do + #[_, middle, _] = Enum.sort([first, second, third]) + #middle == third + #end + + defp format({suit, value}) do + case value do + 1 -> "ace of #{suit}" + 11 -> "prince of #{suit}" + 12 -> "queen of #{suit}" + 13 -> "king of #{suit}" + value -> "#{value} of #{suit}" + end + end +end + +Game.play() # equivalent to Game.play(%Game{funds: 100, deck: 100}) diff --git a/01_Acey_Ducey/python/acey_ducey.py b/01_Acey_Ducey/python/acey_ducey.py index 603e01afa..e6dca6f21 100755 --- a/01_Acey_Ducey/python/acey_ducey.py +++ b/01_Acey_Ducey/python/acey_ducey.py @@ -7,7 +7,6 @@ import random cards = { - 1: "1", 2: "2", 3: "3", 4: "4", @@ -16,10 +15,11 @@ 7: "7", 8: "8", 9: "9", - 10: "Jack", - 11: "Queen", - 12: "King", - 13: "Ace", + 10: "10", + 11: "Jack", + 12: "Queen", + 13: "King", + 14: "Ace", } @@ -27,11 +27,14 @@ def play_game() -> None: cash = 100 while cash > 0: print(f"You now have {cash} dollars\n") - print("Here are you next two cards") - round_cards = list(cards.keys()) - random.shuffle(round_cards) - card_a, card_b, card_c = round_cards.pop(), round_cards.pop(), round_cards.pop() - if card_a > card_b: + print("Here are your next two cards") + round_cards = list(cards.keys()) # gather cards from dictionary + card_a = random.choice(round_cards) # choose a card + card_b = card_a # clone the first card, so we avoid the same number for the second card + while (card_a == card_b): # if the cards are the same, choose another card + card_b = random.choice(round_cards) + card_c = random.choice(round_cards) # choose last card + if card_a > card_b: # swap cards if card_a is greater than card_b card_a, card_b = card_b, card_a print(f" {cards[card_a]}") print(f" {cards[card_b]}\n") @@ -53,7 +56,7 @@ def play_game() -> None: print("Please enter a positive number") print(f" {cards[card_c]}") if bet > 0: - if card_a < card_c < card_b: + if card_a <= card_c <= card_b: print("You win!!!") cash += bet * 2 else: @@ -82,4 +85,5 @@ def main() -> None: if __name__ == "__main__": + random.seed() main() diff --git a/01_Acey_Ducey/python/acey_ducey_oo.py b/01_Acey_Ducey/python/acey_ducey_oo.py index 35b26d845..cd5cad76f 100644 --- a/01_Acey_Ducey/python/acey_ducey_oo.py +++ b/01_Acey_Ducey/python/acey_ducey_oo.py @@ -8,6 +8,7 @@ """ from typing import List, Literal, NamedTuple, TypeAlias, get_args +import random Suit: TypeAlias = Literal["\u2665", "\u2666", "\u2663", "\u2660"] Rank: TypeAlias = Literal[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] @@ -34,8 +35,6 @@ def build(self) -> None: self.cards.append(Card(suit, rank)) def shuffle(self) -> None: - import random - random.shuffle(self.cards) def deal(self) -> Card: @@ -72,7 +71,7 @@ def play(self) -> None: if 0 < bet <= self.money: print(f"Your deal:\t {player_card}") - if card_a.rank < player_card.rank < card_b.rank: + if card_a.rank <= player_card.rank <= card_b.rank: print("You Win!") self.money += bet else: @@ -87,9 +86,6 @@ def play(self) -> None: else: print("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 @@ -129,4 +125,5 @@ def main() -> None: if __name__ == "__main__": + random.seed() main() diff --git a/01_Acey_Ducey/rust/Cargo.lock b/01_Acey_Ducey/rust/Cargo.lock new file mode 100644 index 000000000..4fe2abbea --- /dev/null +++ b/01_Acey_Ducey/rust/Cargo.lock @@ -0,0 +1,75 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/02_Amazing/README.md b/02_Amazing/README.md index b53cbbf50..b8e4b9b41 100644 --- a/02_Amazing/README.md +++ b/02_Amazing/README.md @@ -13,6 +13,10 @@ As published in Basic Computer Games (1978): Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html +#### Known Bugs + +- The input dimensions are checked for values of 1, but not for values of 0 or less. Such inputs will cause the program to break. + #### Porting Notes **2022-01-04:** patched original source in [#400](https://github.com/coding-horror/basic-computer-games/pull/400) to fix a minor bug where a generated maze may be missing an exit, particularly at small maze sizes. diff --git a/02_Amazing/python/amazing.py b/02_Amazing/python/amazing.py index 9d7ab4195..22f141c9f 100644 --- a/02_Amazing/python/amazing.py +++ b/02_Amazing/python/amazing.py @@ -47,7 +47,7 @@ def display(self) -> None: print(" ", end="") print() for col in range(self.width): - if self.walls[row][col] == 0 or self.walls[row][col] == 2: + if self.walls[row][col] in [0, 2]: print(":--", end="") else: print(": ", end="") @@ -148,7 +148,7 @@ def make_opening( maze.walls[pos.row][pos.col] = maze.walls[pos.row][pos.col] + EXIT_DOWN pos.row = pos.row + 1 maze.used[pos.row][pos.col] = count - count = count + 1 + count += 1 return pos, count diff --git a/02_Amazing/rust/Cargo.lock b/02_Amazing/rust/Cargo.lock new file mode 100644 index 000000000..4fe2abbea --- /dev/null +++ b/02_Amazing/rust/Cargo.lock @@ -0,0 +1,75 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/03_Animal/lua/Animal.lua b/03_Animal/lua/Animal.lua new file mode 100644 index 000000000..293c53e41 --- /dev/null +++ b/03_Animal/lua/Animal.lua @@ -0,0 +1,181 @@ +-- +-- Animal.lua +-- + +-- maintain a flat table/list of all the known animals +local animals = { + "FISH", + "BIRD" +} + +-- store the questions as a binary tree with each node having a +-- "y" or "n" branch +-- if the node has a member named "a" it's an answer, with an index +-- into the animals list. +-- Otherwise, it's a question that leads to more nodes. +local questionTree = { + q = "DOES IT SWIM", + y = { a = 1 }, + n = { a = 2 } +} + +-- print the given prompt string and then wait for input +-- loops until a non-empty input is given +-- returns the input as an upper-case string +function askPrompt(promptString) + local answered = false + local a + while (not answered) do + print(promptString) + a = io.read() + a = string.upper(a) + if (string.len(a) > 0) then + answered = true + end + end + return a +end + +-- print the given prompt string and then wait for the +-- user to enter a string beginning with "Y" or "N" +function askYesOrNo(promptString) + local a + while ((a ~= "Y") and (a ~= "N")) do + a = askPrompt(promptString) + a = a:sub(1,1) + end + return a +end + +-- prints the introductory text from the original BASIC program +function printIntro() + print(string.format("%32s", " ") .. "ANIMAL") + print(string.format("%15s", " ") .. "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") + print() + print() + print() + print("PLAY 'GUESS THE ANIMAL'") + print("THINK OF AN ANIMAL AND THE COMPUTER WILL TRY TO GUESS IT.") + print() +end + +-- prints the animals known in the source list +function listKnownAnimals() + print() + print("ANIMALS I ALREADY KNOW ARE:") + + local x + local item + for x = 1,#animals do + -- use string.format to space each animal in a 12-character-wide "cell" + item = string.format("%-12s", animals[x]) + + -- io.write() works like print(), but doesn't automatically add a carriage return/newline + io.write(item) + + -- every fifth item, start a new line + if ((x % 5) == 0) then + io.write("\n") + end + end + + print() + print() +end + +-- Prompts the user for info about the animal they were thinking of, then +-- uses that to add a new branch to the tree +-- curNode: the node in the tree where the computer made a wrong guess +-- branch: the answer the user gave to curNode's question +function addAnimalToTree(curNode, branch) + local newAnimal + local curResponse = curNode[branch] + local guessedIndex = curResponse.a + local guessedAnimal = animals[guessedIndex] + local newQuestion, newAnswer, newIndex + local newNode + + newAnimal = askPrompt("THE ANIMAL YOU WERE THINKING OF WAS A ?") + newQuestion = askPrompt("PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A ".. + tostring(newAnimal).." FROM A "..tostring(guessedAnimal)) + newAnswer = askYesOrNo("FOR A "..tostring(newAnimal).." THE ANSWER WOULD BE?") + + -- add the new animal to the master list at the end, and + -- save off its index in the list + table.insert(animals, newAnimal) + newIndex = #animals + + -- create a new node for the question we just learned + newNode = {} + newNode.q = newQuestion + if (newAnswer == "Y") then + newNode.y = { a = newIndex } + newNode.n = { a = guessedIndex } + else + newNode.y = { a = guessedIndex } + newNode.n = { a = newIndex } + end + + -- replace the previous answer with our new node + curNode[branch] = newNode +end + +-- Starts at the root of the question tree and asks questions about +-- the user's animal until the computer hits an "a" answer node and tries +-- to make a guess +function askAboutAnimal() + local curNode = questionTree + local finished = false + local response, responseIndex + local nextNode, animalName + while (not finished) do + response = askYesOrNo(curNode.q .. "?") + + -- convert the response "Y" or "N" to the lowercase "y" or "n" that we use to name our branches + branch = string.lower(response) + nextNode = curNode[branch] + + -- is the next node an answer node, or another question? + if (nextNode.a ~= nil) then + -- it's an answer, so make a guess + animalName = animals[nextNode.a] + response = askYesOrNo("IS IT A "..tostring(animalName).."?") + if (response == "Y") then + -- we got the correct answer, so prompt for a new animal + print() + print("WHY NOT TRY ANOTHER ANIMAL?") + else + -- incorrect answer, so add a new entry at this point in the tree + addAnimalToTree(curNode, branch) + end + + -- whether we were right or wrong, we're finished with this round + finished = true + else + -- it's another question, so advance down the tree + curNode = nextNode + end + end +end + +-- MAIN CONTROL SECTION + +printIntro() + +-- loop forever until the player requests an exit by entering a blank line +local exitRequested = false +local answer + +while (not exitRequested) do + print("ARE YOU THINKING OF AN ANIMAL?") + answer = io.read() + answer = string.upper(answer) + + if (string.len(answer) == 0) then + exitRequested = true + elseif (answer:sub(1,4) == "LIST") then + listKnownAnimals() + elseif (answer:sub(1,1) == "Y") then + askAboutAnimal() + end +end diff --git a/03_Animal/python/animal.py b/03_Animal/python/animal.py index 618ef7d64..1c29f2faf 100644 --- a/03_Animal/python/animal.py +++ b/03_Animal/python/animal.py @@ -104,17 +104,13 @@ def parse_input(message: str, check_list: bool, root_node: Optional[Node]) -> st list_known_animals(root_node) print("\n") - if len(inp) > 0: - token = inp[0].lower() - else: - token = "" - + token = inp[0].lower() if len(inp) > 0 else "" return token def avoid_void_input(message: str) -> str: answer = "" - while answer == "": + while not answer: answer = input(message) return answer @@ -134,8 +130,12 @@ def main() -> None: # Main loop of game print_intro() - keep_playing = parse_input("Are you thinking of an animal? ", True, root) == "y" - while keep_playing: + while ( + parse_input( + "Are you thinking of an animal? ", True, root + ) + == "y" + ): keep_asking = True # Start traversing the tree by the root actual_node: Node = root @@ -170,17 +170,13 @@ def main() -> None: f"for a {new_animal} the answer would be: ", False, None ) - actual_node.update_node( - new_question + "?", answer_new_question, new_animal - ) + actual_node.update_node(f"{new_question}?", answer_new_question, new_animal) else: print("Why not try another animal?") keep_asking = False - keep_playing = parse_input("Are you thinking of an animal? ", True, root) == "y" - ######################################################## # Porting Notes diff --git a/03_Animal/rust/Cargo.lock b/03_Animal/rust/Cargo.lock new file mode 100644 index 000000000..9c4ba014c --- /dev/null +++ b/03_Animal/rust/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "animal" +version = "0.1.0" diff --git a/04_Awari/python/awari.py b/04_Awari/python/awari.py index 85669cee5..df4906f58 100644 --- a/04_Awari/python/awari.py +++ b/04_Awari/python/awari.py @@ -83,11 +83,10 @@ def draw_pit(line: str, board, pit_index) -> str: val = board[pit_index] - line = line + " " + line += " " if val < 10: - line = line + " " - line = line + str(val) + " " - return line + line += " " + return line + str(val) + " " def draw_board(board) -> None: @@ -148,7 +147,7 @@ def play_game(board: List[int]) -> None: print(msg) break if landing_spot == home: - landing_spot, is_still_going, home, msg = computer_move(msg + " , ", board) + landing_spot, is_still_going, home, msg = computer_move(f"{msg} , ", board) if not is_still_going: print(msg) break @@ -248,7 +247,7 @@ def computer_move(msg: str, board) -> Tuple[int, bool, int, str]: move_str = chr(42 + selected_move) if msg: - msg += ", " + move_str + msg += f", {move_str}" else: msg = move_str @@ -323,10 +322,7 @@ def execute_move(move, home: int, board) -> Tuple[int, bool, int]: # losses. losing_book[game_number] = losing_book[game_number] * 6 + move_digit - if player_has_stones(board) and computer_has_stones(board): - is_still_going = True - else: - is_still_going = False + is_still_going = bool(player_has_stones(board) and computer_has_stones(board)) return last_location, is_still_going, home diff --git a/04_Awari/rust/Cargo.lock b/04_Awari/rust/Cargo.lock new file mode 100644 index 000000000..4fe2abbea --- /dev/null +++ b/04_Awari/rust/Cargo.lock @@ -0,0 +1,75 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/05_Bagels/javascript/bagels.js b/05_Bagels/javascript/bagels.js index 5a62a751a..c0d519e2b 100644 --- a/05_Bagels/javascript/bagels.js +++ b/05_Bagels/javascript/bagels.js @@ -84,7 +84,7 @@ async function main() } while (j >= 1) ; } print("\n"); - print("O.K. I HAVE A NUMBER IN MIND.\n"); + print("OK. I HAVE A NUMBER IN MIND.\n"); for (i = 1; i <= 20; i++) { while (1) { print("GUESS #" + i); @@ -142,7 +142,7 @@ async function main() print("\n"); } else { print("OH WELL.\n"); - print("THAT'S A TWENTY GUESS. MY NUMBER WAS " + a[1] + a[2] + a[3]); + print("THAT'S YOUR TWENTIETH GUESS. MY NUMBER WAS " + a[1] + a[2] + a[3]); } y++; print("PLAY AGAIN (YES OR NO)"); diff --git a/05_Bagels/python/bagels.py b/05_Bagels/python/bagels.py index c9c0ca844..e0662d18c 100644 --- a/05_Bagels/python/bagels.py +++ b/05_Bagels/python/bagels.py @@ -49,9 +49,8 @@ def pick_number() -> List[str]: # as separate strings, not a single integer or string numbers = list(range(10)) random.shuffle(numbers) - num = numbers[0:3] - num_str = [str(i) for i in num] - return num_str + num = numbers[:3] + return [str(i) for i in num] def get_valid_guess(guesses: int) -> str: diff --git a/05_Bagels/rust/Cargo.lock b/05_Bagels/rust/Cargo.lock new file mode 100644 index 000000000..4fe2abbea --- /dev/null +++ b/05_Bagels/rust/Cargo.lock @@ -0,0 +1,75 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/06_Banner/README.md b/06_Banner/README.md index 3acbf7fc7..f01b876d1 100644 --- a/06_Banner/README.md +++ b/06_Banner/README.md @@ -15,5 +15,9 @@ http://www.vintage-basic.net/games.html #### Porting Notes +- The "SET PAGE" input, stored in `O$`, has no effect. It was probably meant as an opportunity for the user to set their pin-feed printer to the top of the page before proceeding. + +- The data values for each character are the bit representation of each horizontal row of the printout (vertical column of a character), plus one. Perhaps because of this +1, the original code (and some of the ports here) are much more complicated than they need to be. + (please note any difficulties or challenges in porting here) diff --git a/06_Banner/perl/banner.pl b/06_Banner/perl/banner.pl new file mode 100644 index 000000000..2524ef00c --- /dev/null +++ b/06_Banner/perl/banner.pl @@ -0,0 +1,150 @@ +#!/usr/bin/perl + +# Banner program in Perl +# Translated by Kevin Brannen (kbrannen) + +use strict; +use warnings; + +sub print_lines +{ + my $lines = shift; + print "\n" x $lines; +} + +# each letter is made of 7 slices (or rows); +# the initial & unused 0 is to allow for Perl arrays being 0-based +# but allow the algorithm to be 1-based (like Basic arrays); +# the numbers are essentially the dots/columns per slice in powers of 2. +my %data = ( + " " => [ 0, 0, 0, 0, 0, 0, 0, 0 ], + "." => [ 0, 1, 1, 129, 449, 129, 1, 1 ], + "!" => [ 0, 1, 1, 1, 384, 1, 1, 1 ], + "=" => [ 0, 41, 41, 41, 41, 41, 41, 41 ], + "?" => [ 0, 5, 3, 2, 354, 18, 11, 5 ], + "*" => [ 0, 69, 41, 17, 512, 17, 41, 69 ], + "0" => [ 0, 57, 69, 131, 258, 131, 69, 57 ], + "1" => [ 0, 0, 0, 261, 259, 512, 257, 257 ], + "2" => [ 0, 261, 387, 322, 290, 274, 267, 261 ], + "3" => [ 0, 66, 130, 258, 274, 266, 150, 100 ], + "4" => [ 0, 33, 49, 41, 37, 35, 512, 33 ], + "5" => [ 0, 160, 274, 274, 274, 274, 274, 226 ], + "6" => [ 0, 194, 291, 293, 297, 305, 289, 193 ], + "7" => [ 0, 258, 130, 66, 34, 18, 10, 8 ], + "8" => [ 0, 69, 171, 274, 274, 274, 171, 69 ], + "9" => [ 0, 263, 138, 74, 42, 26, 10, 7 ], + "A" => [ 0, 505, 37, 35, 34, 35, 37, 505 ], + "B" => [ 0, 512, 274, 274, 274, 274, 274, 239 ], + "C" => [ 0, 125, 131, 258, 258, 258, 131, 69 ], + "D" => [ 0, 512, 258, 258, 258, 258, 131, 125 ], + "E" => [ 0, 512, 274, 274, 274, 274, 258, 258 ], + "F" => [ 0, 512, 18, 18, 18, 18, 2, 2 ], + "G" => [ 0, 125, 131, 258, 258, 290, 163, 101 ], + "H" => [ 0, 512, 17, 17, 17, 17, 17, 512 ], + "I" => [ 0, 258, 258, 258, 512, 258, 258, 258 ], + "J" => [ 0, 65, 129, 257, 257, 257, 129, 128 ], + "K" => [ 0, 512, 17, 17, 41, 69, 131, 258 ], + "L" => [ 0, 512, 257, 257, 257, 257, 257, 257 ], + "M" => [ 0, 512, 7, 13, 25, 13, 7, 512 ], + "N" => [ 0, 512, 7, 9, 17, 33, 193, 512 ], + "O" => [ 0, 125, 131, 258, 258, 258, 131, 125 ], + "P" => [ 0, 512, 18, 18, 18, 18, 18, 15 ], + "Q" => [ 0, 125, 131, 258, 258, 322, 131, 381 ], + "R" => [ 0, 512, 18, 18, 50, 82, 146, 271 ], + "S" => [ 0, 69, 139, 274, 274, 274, 163, 69 ], + "T" => [ 0, 2, 2, 2, 512, 2, 2, 2 ], + "U" => [ 0, 128, 129, 257, 257, 257, 129, 128 ], + "V" => [ 0, 64, 65, 129, 257, 129, 65, 64 ], + "W" => [ 0, 256, 257, 129, 65, 129, 257, 256 ], + "X" => [ 0, 388, 69, 41, 17, 41, 69, 388 ], + "Y" => [ 0, 8, 9, 17, 481, 17, 9, 8 ], + "Z" => [ 0, 386, 322, 290, 274, 266, 262, 260 ], +); + +my ($horz, $vert, $center, $char, $msg) = (0, 0, '', '', ''); + +# get args to run with +while ($horz < 1) +{ + print "HORIZONTAL (1 or more): "; + chomp($horz = <>); + $horz = int($horz); +} + +while ($vert < 1) +{ + print "VERTICAL (1 or more): "; + chomp($vert = <>); + $vert = int($vert); +} + +print "CENTERED (Y/N): "; +chomp($center = <>); +$center = ($center =~ m/^Y/i) ? 1 : 0; + +# note you can enter multiple chars and the program will do the right thing +# thanks to the length() calls below, which was in the original Basic +print "CHARACTER TO PRINT (TYPE 'ALL' IF YOU WANT CHARACTER BEING PRINTED): "; +chomp($char = uc(<>)); + +while (!$msg) +{ + print "STATEMENT: "; + chomp($msg = uc(<>)); +} + +print "SET PAGE TO PRINT, HIT RETURN WHEN READY"; +$_ = <>; +print_lines(2 * $horz); + +# print the message +for my $letter ( split(//, $msg) ) +{ + if (!exists($data{$letter})) + { + die "Cannot use letter '$letter'!"; + } + my @s = @{$data{$letter}}; + #if ($letter eq " ") { print_lines(7 * $horz); next; } + + my $print_letter = ($char eq "ALL") ? $letter : $char; + for my $slice (1 .. 7) + { + my (@j, @f); + for (my $k = 8; $k >= 0; $k--) + { + if (2**$k < $s[$slice]) + { + $j[9 - $k] = 1; + $s[$slice] = $s[$slice] - 2**$k; + if ($s[$slice] == 1) + { + $f[$slice] = 9 - $k; + } + } + else + { + $j[9 - $k] = 0; + } + } + + for my $t1 (1 .. $horz) + { + print " " x int((63 - 4.5 * $vert) * $center / (length($print_letter)) + 1); + for my $b (1 .. (defined($f[$slice]) ? $f[$slice] : 0)) + { + my $str = $j[$b] ? $print_letter : (" " x length($print_letter)); + print $str for (1 .. $vert); + } + print "\n"; + } + } + + # space between letters + print_lines(2 * $horz); +} + +# while in the original code, this seems pretty excessive +#print_lines(75); + +exit(0); diff --git a/06_Banner/python/banner.py b/06_Banner/python/banner.py index ad85b807a..d42a7763d 100644 --- a/06_Banner/python/banner.py +++ b/06_Banner/python/banner.py @@ -74,9 +74,7 @@ def print_banner() -> None: except ValueError: print("Please enter a number greater than zero") - g1 = 0 - if input("Centered ").lower().startswith("y"): - g1 = 1 + g1 = 1 if input("Centered ").lower().startswith("y") else 0 character = input( "Character (type 'ALL' if you want character being printed) " ).upper() @@ -87,7 +85,7 @@ def print_banner() -> None: for statement_char in statement: s = letters[statement_char].copy() x_str = character - if character == "ALL": + if x_str == "ALL": x_str = statement_char if x_str == " ": print("\n" * (7 * horizontal)) diff --git a/07_Basketball/perl/README.md b/07_Basketball/perl/README.md index e69c8b819..51373c0c8 100644 --- a/07_Basketball/perl/README.md +++ b/07_Basketball/perl/README.md @@ -1,3 +1,21 @@ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Perl](https://www.perl.org/) + +There are two version of the code here, a "faithful" translation (basketball-orig.pl) and +a "modern" translation (basketball.pl). The main difference between the 2 are is that the +faithful translation has 3 GOTOs in it while the modern version has no GOTO. I have added +a "TIME" print when the score is shown so the Clock is visible. Halftime is at "50" and +end of game is at 100 (per the Basic code). + +The 3 GOTOs in the faitful version are because of the way the original code jumped into +the "middle of logic" that has no obivious way to avoid ... that I can see, at least while +still maintaining something of the look and structure of the original Basic. + +The modern version avoided the GOTOs by restructuring the program in the 2 "play()" subs. +Despite the change, this should play the same way as the faithful version. + +All of the percentages remain the same. If writing this from scratch, we really should +have only a single play() sub which uses the same code for both teams, which would also +make the game more fair ... but that wasn't done so the percent edge to Darmouth has been +maintained here. diff --git a/07_Basketball/perl/basketball-orig.pl b/07_Basketball/perl/basketball-orig.pl new file mode 100755 index 000000000..14627ffc6 --- /dev/null +++ b/07_Basketball/perl/basketball-orig.pl @@ -0,0 +1,415 @@ +#!/usr/bin/perl + +# Basketball program in Perl +# This is fairly faithful translation from the original Basic. +# This becomes apparent because there are actually 3 GOTOs still present +# because of the way the original code jumped into the "middle of logic" +# that has no obivious way to avoid ... that I can see. +# For better structure and no GOTOs, see the other version of this program. +# Translated by Kevin Brannen (kbrannen) + +use strict; +use warnings; + +# globals +my $Defense=0; # dartmouth defense value +my $Opponent; # name of opponent +my @Score = (0, 0); # scores, dart is [0], opponent is [1] +my $Player = 0; # player, 0 = dart, 1 = opp +my $Timer = 0; # time tick, 100 ticks per game, 50 is end of first half, if tie at end then back to T=93 +my $DoPlay = 1; # true if game is still being played +my $ConTeam; # controlling team, "dart" or "opp" +my $ShotType = 0; # current shot type + + +print "\n"; +print " " x 31, "BASKETBALL"; +print " " x 15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"; +print "\n\n\n"; + +print "THIS IS DARTMOUTH COLLEGE BASKETBALL. YOU WILL BE DARTMOUTH\n"; +print "CAPTAIN AND PLAYMAKER. CALL SHOTS AS FOLLOWS:\n"; +print " 1. LONG (30 FT.) JUMP SHOT;\n"; +print " 2. SHORT (15 FT.) JUMP SHOT;\n"; +print " 3. LAY UP;\n"; +print " 4. SET SHOT.\n"; +print "BOTH TEAMS WILL USE THE SAME DEFENSE. CALL DEFENSE AS FOLLOWS:\n"; +print " 6. PRESS;\n"; +print " 6.5 MAN-TO MAN;\n"; +print " 7. ZONE;\n"; +print " 7.5 NONE.\n"; +print "TO CHANGE DEFENSE, JUST TYPE 0 AS YOUR NEXT SHOT.\n\n"; +get_defense(); +print "\n"; +print "CHOOSE YOUR OPPONENT: "; +chomp($Opponent = <>); + +$ConTeam = center_jump(); +while ($DoPlay) +{ + print "\n"; + if ($ConTeam eq "dart") + { + $Player = 0; + get_your_shot(); + dartmouth_play(); + } + else + { + opponent_play(); + } + if ($Timer >= 100) + { + check_end_game(); + last if (!$DoPlay); + $Timer = 93; + $ConTeam = center_jump(); + } +} +exit(0); + +############################################################### + +sub dartmouth_play +{ + if ($ShotType == 1 || $ShotType == 2) + { + $Timer++; + if ($Timer == 50) + { + end_first_half(); + return; + } + if ($Timer == 92) + { + two_min_left(); + } + + print "JUMP SHOT\n"; + if (rand(1) <= 0.341 * $Defense / 8) + { + print "SHOT IS GOOD.\n"; + dartmouth_score(); + $ConTeam = "opp"; + return; + } + + if (rand(1) <= 0.682*$Defense/8) + { + print "SHOT IS OFF TARGET.\n"; + if ($Defense/6*rand(1) > 0.45) + { + print "REBOUND TO ", $Opponent, "\n"; + $ConTeam = "opp"; + return; + } + + print "DARTMOUTH CONTROLS THE REBOUND.\n"; + if (rand(1) <= 0.4) { goto L1300; } + if ($Defense == 6) + { + if (rand(1) <= 0.6) + { + print "PASS STOLEN BY $Opponent, EASY LAYUP.\n"; + opp_score(); + $ConTeam = "dart"; return; + return; + } + } + print "BALL PASSED BACK TO YOU.\n"; + $ConTeam = "dart"; + return; + } + + if (rand(1) <= 0.782*$Defense/8) + { + print "SHOT IS BLOCKED. BALL CONTROLLED BY "; # no NL + if (rand(1) > 0.5) + { + print "$Opponent.\n"; + $ConTeam = "opp"; + return; + } + else + { + print "DARTMOUTH.\n"; + $ConTeam = "dart"; + return; + } + } + + if (rand(1) > 0.843*$Defense/8) + { + print "CHARGING FOUL. DARTMOUTH LOSES BALL.\n"; + $ConTeam = "opp"; + return; + } + else + { + print "SHOOTER IS FOULED. TWO SHOTS.\n"; + foul_shooting(); + $ConTeam = "opp"; + return; + } + } + + L1300: + while (1) + { + $Timer++; + if ($Timer == 50) + { + end_first_half(); + return; + } + if ($Timer == 92) { two_min_left(); } + if ($ShotType == 0) + { + get_defense(); + return; + } + print '', ($ShotType > 3 ? "SET SHOT." : "LAY UP."), "\n"; + if (7 / $Defense * rand(1) <= 0.4) + { + print "SHOT IS GOOD. TWO POINTS.\n"; + dartmouth_score(); + $ConTeam = "opp"; + return; + } + + if (7 / $Defense * rand(1) <= 0.7) + { + print "SHOT IS OFF THE RIM.\n"; + if (rand(1) <= 0.667) + { + print "$Opponent CONTROLS THE REBOUND.\n"; + $ConTeam = "opp"; + return; + } + + print "DARTMOUTH CONTROLS THE REBOUND.\n"; + next if (rand(1) <= 0.4); + + print "BALL PASSED BACK TO YOU.\n"; + $ConTeam = "dart"; + return; + } + + if (7 / $Defense * rand(1) <= 0.875) + { + print "SHOOTER FOULED. TWO SHOTS.\n"; + foul_shooting(); + $ConTeam = "opp"; + return; + } + + if (7 / $Defense * rand(1) <= 0.925) + { + print "SHOT BLOCKED. $Opponent\'S BALL.\n"; + $ConTeam = "opp"; + return; + } + + print "CHARGING FOUL. DARTMOUTH LOSES THE BALL.\n"; + $ConTeam = "opp"; + return; + } +} + +sub get_defense +{ + $Defense = 0; + while ($Defense < 6 || $Defense > 7.5) + { + print "YOUR NEW DEFENSIVE ALLIGNMENT IS (6, 6.5, 7. 7.5): "; + chomp($Defense = <>); + ($Defense) =~ m/(\d(\.\d)?)/; + } +} + +sub opponent_play +{ + $Player = 1; + $Timer++; + if ($Timer == 50) + { + end_first_half(); + $ConTeam = center_jump(); + return; + } + + print "\n"; + while (1) + { + my $shot = 10.0 / 4 * rand(1) + 1; + if ($shot <= 2.0) + { + print "JUMP SHOT.\n"; + if (8.0 / $Defense * rand(1) <= 0.35) + { + print "SHOT IS GOOD.\n"; + opp_score(); + $ConTeam = "dart"; + return; + } + + if (8.0 / $Defense * rand(1) <= 0.75) + { + print "SHOT IS OFF RIM.\n"; + + L3110: + if ($Defense / 6.0 * rand(1) <= 0.5) + { + print "DARTMOUTH CONTROLS THE REBOUND.\n"; + $ConTeam = "dart"; + return; + } + print "$Opponent CONTROLS THE REBOUND.\n"; + if ($Defense == 6) + { + if (rand(1) <= 0.75) + { + print "BALL STOLEN. EASY LAY UP FOR DARTMOUTH.\n"; + dartmouth_score(); + $ConTeam = "opp"; + return; + } + } + if (rand(1) <= 0.5) + { + print "PASS BACK TO $Opponent GUARD.\n"; + $ConTeam = "opp"; + return; + } + goto L3500; + } + + if (8.0 / $Defense * rand(1) <= 0.9) + { + print "PLAYER FOULED. TWO SHOTS.\n"; + foul_shooting(); + $ConTeam = "dart"; + return; + } + print "OFFENSIVE FOUL. DARTMOUTH'S BALL.\n"; + $ConTeam = "dart"; + return; + } + + L3500: + print ($shot > 3 ? "SET SHOT.\n" : "LAY UP.\n"); + if (7.0 / $Defense * rand(1) > 0.413) + { + print "SHOT IS MISSED.\n"; + { + no warnings; + goto L3110; + } + } + else + { + print "SHOT IS GOOD.\n"; + opp_score(); + $ConTeam = "dart"; + return; + } + } +} + +sub opp_score +{ + $Score[0] += 2; + print_score(); +} + +sub dartmouth_score +{ + $Score[1] += 2; + print_score(); +} + +sub print_score +{ + print "SCORE: $Score[1] TO $Score[0]\n"; + print "TIME: $Timer\n"; +} + +sub end_first_half +{ + print "\n ***** END OF FIRST HALF *****\n\n"; + print "SCORE: DARTMOUTH: $Score[1] $Opponent: $Score[0]\n\n\n"; + center_jump(); +} + +sub get_your_shot +{ + $ShotType = -1; + while ($ShotType < 0 || $ShotType > 4) + { + print "YOUR SHOT (0-4): "; + chomp($ShotType = <>); + $ShotType = int($ShotType); + if ($ShotType < 0 || $ShotType > 4) + { + print "INCORRECT ANSWER. RETYPE IT. "; + } + } +} + +sub center_jump +{ + print "CENTER JUMP\n"; + if (rand(1) <= 0.6) + { + print "$Opponent CONTROLS THE TAP.\n"; + return "opp"; + } + print "DARTMOUTH CONTROLS THE TAP.\n"; + return "dart"; +} + +sub check_end_game +{ + print "\n"; + if ($Score[1] != $Score[0]) + { + print " ***** END OF GAME *****\n"; + print "FINAL SCORE: DARTMOUTH: $Score[1] $Opponent: $Score[0]\n\n"; + $DoPlay = 0; + } + else + { + print "\n ***** END OF SECOND HALF *****\n"; + print "SCORE AT END OF REGULATION TIME:\n"; + print " DARTMOUTH: $Score[1] $Opponent: $Score[0]\n\n"; + print "BEGIN TWO MINUTE OVERTIME PERIOD\n"; + } +} + +sub two_min_left +{ + print "\n *** TWO MINUTES LEFT IN THE GAME ***\n\n"; +} + +sub foul_shooting +{ + if (rand(1) > 0.49) + { + if (rand(1) > 0.75) + { + print "BOTH SHOTS MISSED.\n"; + } + else + { + print "SHOOTER MAKES ONE SHOT AND MISSES ONE.\n"; + $Score[1 - $Player]++; + } + } + else + { + print "SHOOTER MAKES BOTH SHOTS.\n"; + $Score[1 - $Player] += 2; + } + + print_score(); +} diff --git a/07_Basketball/perl/basketball.pl b/07_Basketball/perl/basketball.pl new file mode 100755 index 000000000..ac3483094 --- /dev/null +++ b/07_Basketball/perl/basketball.pl @@ -0,0 +1,393 @@ +#!/usr/bin/perl + +# Basketball program in Perl +# While this should play the same way as the fairly faithful translation version, +# there are no GOTOs in this code. That was achieved by restructuring the program +# in the 2 *_play() subs. All of the percentages remain the same. If writing this +# from scratch, we really should have only a play() sub which uses the same code +# for both teams, but the percent edge to Darmouth has been maintained here. +# Translated by Kevin Brannen (kbrannen) + +use strict; +use warnings; + +# globals +my $Defense=0; # dartmouth defense value +my $Opponent; # name of opponent +my @Score = (0, 0); # scores, dart is [0], opponent is [1] +my $Player = 0; # player, 0 = dart, 1 = opp +my $Timer = 0; # time tick, 100 ticks per game, 50 is end of first half, if tie at end then back to T=93 +my $DoPlay = 1; # true if game is still being played +my $ConTeam; # controlling team, "dart" or "opp" +my $ShotType = 0; # current shot type + + +print "\n"; +print " " x 31, "BASKETBALL"; +print " " x 15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"; +print "\n\n\n"; + +print "THIS IS DARTMOUTH COLLEGE BASKETBALL. YOU WILL BE DARTMOUTH\n"; +print "CAPTAIN AND PLAYMAKER. CALL SHOTS AS FOLLOWS:\n"; +print " 1. LONG (30 FT.) JUMP SHOT;\n"; +print " 2. SHORT (15 FT.) JUMP SHOT;\n"; +print " 3. LAY UP;\n"; +print " 4. SET SHOT.\n"; +print "BOTH TEAMS WILL USE THE SAME DEFENSE. CALL DEFENSE AS FOLLOWS:\n"; +print " 6. PRESS;\n"; +print " 6.5 MAN-TO MAN;\n"; +print " 7. ZONE;\n"; +print " 7.5 NONE.\n"; +print "TO CHANGE DEFENSE, JUST TYPE 0 AS YOUR NEXT SHOT.\n\n"; +get_defense(); +print "\n"; +print "CHOOSE YOUR OPPONENT: "; +chomp($Opponent = <>); + +$ConTeam = center_jump(); +while ($DoPlay) +{ + print "\n"; + if ($ConTeam eq "dart") + { + get_your_shot(); + dartmouth_play(); + } + else + { + $ShotType = 10.0 / 4 * rand(1) + 1; + opponent_play(); + } + if ($Timer >= 100) + { + check_end_game(); + last if (!$DoPlay); + $Timer = 93; + $ConTeam = center_jump(); + } +} +exit(0); + +############################################################### + +sub dartmouth_play +{ + $Player = 0; + print "\n"; + while (1) + { + $Timer++; + if ($Timer == 50) { end_first_half(); return; } + if ($Timer == 92) { two_min_left(); } + + if ($ShotType == 0) + { + get_defense(); + return; # for new ShotType + } + elsif ($ShotType == 1 || $ShotType == 2) + { + print "JUMP SHOT\n"; + if (rand(1) <= 0.341 * $Defense / 8) + { + print "SHOT IS GOOD.\n"; + dartmouth_score(); + last; + } + + if (rand(1) <= 0.682*$Defense/8) + { + print "SHOT IS OFF TARGET.\n"; + if ($Defense/6*rand(1) > 0.45) + { + print "REBOUND TO $Opponent\n"; + last; + } + + print "DARTMOUTH CONTROLS THE REBOUND.\n"; + if (rand(1) <= 0.4) + { + $ShotType = (rand(1) <= 0.5) ? 3 : 4; + next; + } + if ($Defense == 6) + { + if (rand(1) <= 0.6) + { + print "PASS STOLEN BY $Opponent, EASY LAYUP.\n"; + opp_score(); + next; + } + } + print "BALL PASSED BACK TO YOU.\n"; + next; + } + + if (rand(1) <= 0.782*$Defense/8) + { + print "SHOT IS BLOCKED. BALL CONTROLLED BY "; # no NL + if (rand(1) > 0.5) + { + print "$Opponent.\n"; + last; + } + else + { + print "DARTMOUTH.\n"; + next; + } + } + + if (rand(1) > 0.843*$Defense/8) + { + print "CHARGING FOUL. DARTMOUTH LOSES BALL.\n"; + last; + } + else + { + print "SHOOTER IS FOULED. TWO SHOTS.\n"; + foul_shooting(); + last; + } + } + else # elsif ($ShotType >= 3) + { + print '', ($ShotType > 3 ? "SET SHOT." : "LAY UP."), "\n"; + if (7 / $Defense * rand(1) <= 0.4) + { + print "SHOT IS GOOD. TWO POINTS.\n"; + dartmouth_score(); + last; + } + + if (7 / $Defense * rand(1) <= 0.7) + { + print "SHOT IS OFF THE RIM.\n"; + if (rand(1) <= 0.667) + { + print "$Opponent CONTROLS THE REBOUND.\n"; + last; + } + + print "DARTMOUTH CONTROLS THE REBOUND.\n"; + next if (rand(1) <= 0.4); + + print "BALL PASSED BACK TO YOU.\n"; + next; + } + + if (7 / $Defense * rand(1) <= 0.875) + { + print "SHOOTER FOULED. TWO SHOTS.\n"; + foul_shooting(); + last; + } + + if (7 / $Defense * rand(1) <= 0.925) + { + print "SHOT BLOCKED. $Opponent\'S BALL.\n"; + last; + } + + print "CHARGING FOUL. DARTMOUTH LOSES THE BALL.\n"; + last; + } + } + $ConTeam = "opp"; +} + +sub get_defense +{ + $Defense = 0; + do { + print "YOUR NEW DEFENSIVE ALLIGNMENT IS (6, 6.5, 7. 7.5): "; + chomp($Defense = <>); + ($Defense) =~ m/(\d(\.\d)?)/; + } while ($Defense < 6.0 || $Defense > 7.5) +} + +sub opponent_play +{ + $Player = 1; + print "\n"; + while (1) + { + $Timer++; + if ($Timer == 50) { end_first_half(); return; } + if ($Timer == 92) { two_min_left(); } + + if ($ShotType <= 2.0) + { + print "JUMP SHOT.\n"; + if (8.0 / $Defense * rand(1) <= 0.35) + { + print "SHOT IS GOOD.\n"; + opp_score(); + last; + } + + if (8.0 / $Defense * rand(1) <= 0.75) + { + print "SHOT IS OFF RIM.\n"; + opp_missed(); + return; # for possible new ShotType or team change + } + + if (8.0 / $Defense * rand(1) <= 0.9) + { + print "PLAYER FOULED. TWO SHOTS.\n"; + foul_shooting(); + last; + } + print "OFFENSIVE FOUL. DARTMOUTH'S BALL.\n"; + last; + } + else # ShotType >= 3 + { + print ($ShotType > 3 ? "SET SHOT.\n" : "LAY UP.\n"); + if (7.0 / $Defense * rand(1) > 0.413) + { + print "SHOT IS MISSED.\n"; + { + opp_missed(); + return; # for possible new ShotType or team change + } + } + else + { + print "SHOT IS GOOD.\n"; + opp_score(); + last; + } + } + } + $ConTeam = "dart"; +} + +sub opp_missed +{ + if ($Defense / 6.0 * rand(1) <= 0.5) + { + print "DARTMOUTH CONTROLS THE REBOUND.\n"; + $ConTeam = "dart"; + } + else + { + print "$Opponent CONTROLS THE REBOUND.\n"; + if ($Defense == 6) + { + if (rand(1) <= 0.75) + { + print "BALL STOLEN. EASY LAY UP FOR DARTMOUTH.\n"; + dartmouth_score(); + #$ConTeam = "opp"; + return; # for possible new ShotType + } + } + if (rand(1) <= 0.5) + { + print "PASS BACK TO $Opponent GUARD.\n"; + #$ConTeam = "opp"; + return; # for possible new ShotType + } + $ShotType = (rand(1) <= 0.5) ? 3 : 4; + } +} + +sub opp_score +{ + $Score[0] += 2; + print_score(); +} + +sub dartmouth_score +{ + $Score[1] += 2; + print_score(); +} + +sub print_score +{ + print "SCORE: $Score[1] TO $Score[0]\n"; + print "TIME: $Timer\n"; +} + +sub end_first_half +{ + print "\n ***** END OF FIRST HALF *****\n\n"; + print "SCORE: DARTMOUTH: $Score[1] $Opponent: $Score[0]\n\n\n"; + $ConTeam = center_jump(); +} + +sub get_your_shot +{ + $ShotType = -1; + while ($ShotType < 0 || $ShotType > 4) + { + print "YOUR SHOT (0-4): "; + chomp($ShotType = <>); + $ShotType = int($ShotType); + if ($ShotType < 0 || $ShotType > 4) + { + print "INCORRECT ANSWER. RETYPE IT. "; + } + } +} + +sub center_jump +{ + print "CENTER JUMP\n"; + if (rand(1) <= 0.6) + { + print "$Opponent CONTROLS THE TAP.\n"; + return "opp"; + } + print "DARTMOUTH CONTROLS THE TAP.\n"; + return "dart"; +} + +sub check_end_game +{ + print "\n"; + if ($Score[1] != $Score[0]) + { + print " ***** END OF GAME *****\n"; + print "FINAL SCORE: DARTMOUTH: $Score[1] $Opponent: $Score[0]\n\n"; + $DoPlay = 0; + } + else + { + print "\n ***** END OF SECOND HALF *****\n"; + print "SCORE AT END OF REGULATION TIME:\n"; + print " DARTMOUTH: $Score[1] $Opponent: $Score[0]\n\n"; + print "BEGIN TWO MINUTE OVERTIME PERIOD\n"; + } +} + +sub two_min_left +{ + print "\n *** TWO MINUTES LEFT IN THE GAME ***\n\n"; +} + +sub foul_shooting +{ + if (rand(1) > 0.49) + { + if (rand(1) > 0.75) + { + print "BOTH SHOTS MISSED.\n"; + } + else + { + print "SHOOTER MAKES ONE SHOT AND MISSES ONE.\n"; + $Score[1 - $Player]++; + } + } + else + { + print "SHOOTER MAKES BOTH SHOTS.\n"; + $Score[1 - $Player] += 2; + } + + print_score(); +} diff --git a/07_Basketball/python/basketball.py b/07_Basketball/python/basketball.py index 758e5cb9e..122185a13 100644 --- a/07_Basketball/python/basketball.py +++ b/07_Basketball/python/basketball.py @@ -122,32 +122,30 @@ def dartmouth_jump_shot(self) -> None: if random.random() > 0.782 * self.defense / 8: if random.random() > 0.843 * self.defense / 8: print("Charging foul. Dartmouth loses ball.\n") - self.opponent_ball() else: # player is fouled self.foul_shots(1) - self.opponent_ball() + self.opponent_ball() + elif random.random() > 0.5: + print( + "Shot is blocked. Ball controlled by " + + self.opponent + + ".\n" + ) + self.opponent_ball() else: - if random.random() > 0.5: - print( - "Shot is blocked. Ball controlled by " - + self.opponent - + ".\n" - ) - self.opponent_ball() - else: - print("Shot is blocked. Ball controlled by Dartmouth.") - self.dartmouth_ball() + print("Shot is blocked. Ball controlled by Dartmouth.") + self.dartmouth_ball() else: print("Shot is off target.") if self.defense / 6 * random.random() > 0.45: - print("Rebound to " + self.opponent + "\n") + print(f"Rebound to {self.opponent}\n") self.opponent_ball() else: print("Dartmouth controls the rebound.") if random.random() > 0.4: if self.defense == 6 and random.random() > 0.6: - print("Pass stolen by " + self.opponent + ", easy lay up") + print(f"Pass stolen by {self.opponent}, easy lay up") self.add_points(0, 2) self.dartmouth_ball() else: @@ -186,13 +184,11 @@ def dartmouth_non_jump_shot(self) -> None: if 7 / self.defense * random.random() > 0.875: if 7 / self.defense * random.random() > 0.925: print("Charging foul. Dartmouth loses the ball.\n") - self.opponent_ball() else: - print("Shot blocked. " + self.opponent + "'s ball.\n") - self.opponent_ball() + print(f"Shot blocked. {self.opponent}'s ball.\n") else: self.foul_shots(1) - self.opponent_ball() + self.opponent_ball() else: print("Shot is off the rim.") if random.random() > 2 / 3: @@ -216,35 +212,35 @@ def dartmouth_ball(self) -> None: self.shot = shot if self.time < 100 or random.random() < 0.5: - if self.shot == 1 or self.shot == 2: + if self.shot in [1, 2]: self.dartmouth_jump_shot() else: self.dartmouth_non_jump_shot() + elif self.score[0] == self.score[1]: + print("\n ***** End Of Second Half *****") + print("Score at end of regulation time:") + print( + " Dartmouth: " + + str(self.score[1]) + + " " + + self.opponent + + ": " + + str(self.score[0]) + ) + print("Begin two minute overtime period") + self.time = 93 + self.start_of_period() + else: - if self.score[0] != self.score[1]: - print("\n ***** End Of Game *****") - print( - "Final Score: Dartmouth: " - + str(self.score[1]) - + " " - + self.opponent - + ": " - + str(self.score[0]) - ) - else: - print("\n ***** End Of Second Half *****") - print("Score at end of regulation time:") - print( - " Dartmouth: " - + str(self.score[1]) - + " " - + self.opponent - + ": " - + str(self.score[0]) - ) - print("Begin two minute overtime period") - self.time = 93 - self.start_of_period() + print("\n ***** End Of Game *****") + print( + "Final Score: Dartmouth: " + + str(self.score[1]) + + " " + + self.opponent + + ": " + + str(self.score[0]) + ) def opponent_jumpshot(self) -> None: """Simulate the opponents jumpshot""" @@ -253,32 +249,35 @@ def opponent_jumpshot(self) -> None: if 8 / self.defense * random.random() > 0.75: if 8 / self.defense * random.random() > 0.9: print("Offensive foul. Dartmouth's ball.\n") - self.dartmouth_ball() else: self.foul_shots(0) - self.dartmouth_ball() + self.dartmouth_ball() else: print("Shot is off the rim.") if self.defense / 6 * random.random() > 0.5: - print(self.opponent + " controls the rebound.") - if self.defense == 6: - if random.random() > 0.75: - print("Ball stolen. Easy lay up for Dartmouth.") - self.add_points(1, 2) - self.opponent_ball() - else: - if random.random() > 0.5: - print() - self.opponent_non_jumpshot() - else: - print("Pass back to " + self.opponent + " guard.\n") - self.opponent_ball() + print(f"{self.opponent} controls the rebound.") + if ( + self.defense == 6 + and random.random() <= 0.75 + and random.random() > 0.5 + ): + print() + self.opponent_non_jumpshot() + elif ( + self.defense == 6 + and random.random() <= 0.75 + and random.random() <= 0.5 + or self.defense != 6 + and random.random() <= 0.5 + ): + print(f"Pass back to {self.opponent} guard.\n") + self.opponent_ball() + elif self.defense == 6 and random.random() > 0.75: + print("Ball stolen. Easy lay up for Dartmouth.") + self.add_points(1, 2) + self.opponent_ball() else: - if random.random() > 0.5: - self.opponent_non_jumpshot() - else: - print("Pass back to " + self.opponent + " guard.\n") - self.opponent_ball() + self.opponent_non_jumpshot() else: print("Dartmouth controls the rebound.\n") self.dartmouth_ball() @@ -296,26 +295,30 @@ def opponent_non_jumpshot(self) -> None: if 7 / self.defense * random.random() > 0.413: print("Shot is missed.") if self.defense / 6 * random.random() > 0.5: - print(self.opponent + " controls the rebound.") - if self.defense == 6: - if random.random() > 0.75: - print("Ball stolen. Easy lay up for Dartmouth.") - self.add_points(1, 2) - self.opponent_ball() - else: - if random.random() > 0.5: - print() - self.opponent_non_jumpshot() - else: - print("Pass back to " + self.opponent + " guard.\n") - self.opponent_ball() + print(f"{self.opponent} controls the rebound.") + if ( + self.defense == 6 + and random.random() <= 0.75 + and random.random() > 0.5 + or self.defense != 6 + and random.random() > 0.5 + ): + print() + self.opponent_non_jumpshot() + elif ( + self.defense == 6 + and random.random() <= 0.75 + and random.random() <= 0.5 + ): + print(f"Pass back to {self.opponent} guard.\n") + self.opponent_ball() + elif self.defense == 6 and random.random() > 0.75: + print("Ball stolen. Easy lay up for Dartmouth.") + self.add_points(1, 2) + self.opponent_ball() else: - if random.random() > 0.5: - print() - self.opponent_non_jumpshot() - else: - print("Pass back to " + self.opponent + " guard\n") - self.opponent_ball() + print(f"Pass back to {self.opponent} guard\n") + self.opponent_ball() else: print("Dartmouth controls the rebound.\n") self.dartmouth_ball() diff --git a/07_Basketball/rust/Cargo.lock b/07_Basketball/rust/Cargo.lock new file mode 100644 index 000000000..5d92a1cf5 --- /dev/null +++ b/07_Basketball/rust/Cargo.lock @@ -0,0 +1,77 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "basketball" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chacha20" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" +dependencies = [ + "cfg-if", + "cpufeatures", + "rand_core", +] + +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "rand_core", +] + +[[package]] +name = "libc" +version = "0.2.189" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" +dependencies = [ + "chacha20", + "getrandom", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" diff --git a/07_Basketball/rust/Cargo.toml b/07_Basketball/rust/Cargo.toml new file mode 100644 index 000000000..3a4c7c80b --- /dev/null +++ b/07_Basketball/rust/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "basketball" +version = "0.1.0" +edition = "2024" + +[dependencies] +rand = "0.10.0" diff --git a/07_Basketball/rust/src/main.rs b/07_Basketball/rust/src/main.rs new file mode 100644 index 000000000..ca0d6792d --- /dev/null +++ b/07_Basketball/rust/src/main.rs @@ -0,0 +1,444 @@ +use rand::{RngExt, rngs::ThreadRng}; +use std::io::{self, Write}; + +fn main() { + println!("{:>30}BASKETBALL", ""); + println!("{:>15}CREATIVE COMPUTING MORRISTOWN, NEW JERSEY", ""); + println!("\n\n"); + + println!("THIS IS DARTMOUTH COLLEGE BASKETBALL. YOU WILL BE DARTMOUTH"); + println!(" CAPTAIN AND PLAYMAKER. CALL SHOTS AS FOLLOWS: 1. LONG"); + println!(" (30 FT.) JUMP SHOT; 2. SHORT (15 FT.) JUMP SHOT; 3. LAY"); + println!(" UP; 4. SET SHOT."); + println!("BOTH TEAMS WILL USE THE SAME DEFENSE. CALL DEFENSE AS"); + println!("FOLLOWS: 6. PRESS; 6.5 MAN-TO MAN; 7. ZONE; 7.5 NONE."); + println!("TO CHANGE DEFENSE, JUST TYPE 0 AS YOUR NEXT SHOT."); + + let mut game = BasketballGame::new(); + game.play(); +} + +struct BasketballGame { + rng: ThreadRng, + // D is the defensive alignment. It is one of 6, 6.5, 7 or 7.5 (but the + // original code only validates that D >= 6). + defense: f64, + // The opponent's name. + opponent: String, + // Game clock. Incremented on most possessions; 50 ends the first half, + // 92 triggers the two-minute warning and 100 forces overtime or the + // final whistle. + time: i32, + // Score: index 0 is the opponent, index 1 is Dartmouth. + score: [i32; 2], +} + +impl BasketballGame { + fn new() -> Self { + let rng = rand::rng(); + + // Line 76: prompt for the starting defense. The original accepts any + // value >= 6, so we replicate that rather than restricting to the + // documented options. + let defense = loop { + print!("YOUR STARTING DEFENSE WILL BE"); + let d: f64 = read_input(); + if d >= 6.0 { + break d; + } + // BASIC line 76 falls through to 2010 which re-prompts for the + // defensive alignment; reproducing that behaviour here. + }; + println!(); + + print!("CHOOSE YOUR OPPONENT"); + let opponent = read_line().trim().to_string(); + + Self { + rng, + defense, + opponent, + time: 0, + score: [0, 0], + } + } + + fn play(&mut self) { + // Line 370: center jump to open each half/overtime period. + self.center_jump(); + } + + /// Line 370-420: simulates the center jump that starts each period. + fn center_jump(&mut self) { + println!("CENTER JUMP"); + if self.rng.random::() > 3.0 / 5.0 { + println!("DARTMOUTH CONTROLS THE TAP."); + println!(); + self.dartmouth_possession(); + } else { + println!("{} CONTROLS THE TAP.", self.opponent); + self.opponent_possession(); + } + } + + /// Lines 425-500: Dartmouth has the ball and asks the player for a shot. + fn dartmouth_possession(&mut self) { + let z = self.read_shot(); + // Line 460: half the time, when the clock has run out, we go to the + // end-of-game / overtime logic instead of resolving the shot. + if self.rng.random::() < 0.5 && self.time >= 100 { + self.end_of_game_or_overtime(); + return; + } + + match z { + 1 | 2 => self.dartmouth_jump_shot(), + _ => self.dartmouth_non_jump_shot(z), + } + } + + /// Line 430-455: read and validate the player's shot choice. + fn read_shot(&mut self) -> i32 { + loop { + print!("YOUR SHOT"); + let line = read_line(); + match line.trim().parse::() { + Ok(z) if (0..=4).contains(&z) => return z, + _ => print!("INCORRECT ANSWER. RETYPE IT. "), + } + } + } + + /// Lines 1040-1280: long or short jump shot for Dartmouth. + fn dartmouth_jump_shot(&mut self) { + self.advance_clock_with_two_minute_warning(); + println!("JUMP SHOT"); + + if self.rng.random::() <= 0.341 * self.defense / 8.0 { + println!("SHOT IS GOOD."); + self.add_dartmouth_score(); + self.opponent_possession(); + return; + } + if self.rng.random::() <= 0.682 * self.defense / 8.0 { + println!("SHOT IS OFF TARGET."); + if self.defense / 6.0 * self.rng.random::() > 0.45 { + println!("REBOUND TO {}", self.opponent); + self.opponent_possession(); + } else { + println!("DARTMOUTH CONTROLS THE REBOUND."); + if self.rng.random::() <= 0.4 { + println!(); + self.dartmouth_non_jump_shot(3); + } else if self.defense == 6.0 && self.rng.random::() > 0.6 { + // Line 5100-5130: pass stolen, easy lay-up for opponent. + println!("PASS STOLEN BY {} EASY LAYUP.", self.opponent); + self.add_opponent_score(); + self.dartmouth_possession(); + } else { + print!("BALL PASSED BACK TO YOU. "); + self.dartmouth_possession(); + } + } + return; + } + if self.rng.random::() <= 0.782 * self.defense / 8.0 { + print!("SHOT IS BLOCKED. BALL CONTROLLED BY "); + if self.rng.random::() > 0.5 { + println!("DARTMOUTH."); + self.dartmouth_possession(); + } else { + println!("{}.", self.opponent); + self.opponent_possession(); + } + return; + } + if self.rng.random::() <= 0.843 * self.defense / 8.0 { + println!("SHOOTER IS FOULED. TWO SHOTS."); + self.foul_shots(1); + self.opponent_possession(); + return; + } + println!("CHARGING FOUL. DARTMOUTH LOSES BALL."); + self.opponent_possession(); + } + + /// Lines 1300-1700: lay up (3) or set shot (4); also re-entered for a + /// rebound put-back. A shot value of 0 means the player wants to change + /// the defensive alignment (lines 2010-2040). + fn dartmouth_non_jump_shot(&mut self, z: i32) { + self.advance_clock_with_two_minute_warning(); + + if z == 0 { + self.change_defense(); + return; + } + + if z == 4 { + println!("SET SHOT."); + } else { + println!("LAY UP."); + } + + if 7.0 / self.defense * self.rng.random::() <= 0.4 { + println!("SHOT IS GOOD. TWO POINTS."); + self.add_dartmouth_score(); + self.opponent_possession(); + return; + } + if 7.0 / self.defense * self.rng.random::() <= 0.7 { + println!("SHOT IS OFF THE RIM."); + if self.rng.random::() > 2.0 / 3.0 { + println!("DARTMOUTH CONTROLS THE REBOUND."); + if self.rng.random::() > 0.4 { + println!("BALL PASSED BACK TO YOU."); + self.dartmouth_possession(); + } else { + self.dartmouth_non_jump_shot(3); + } + } else { + println!("{} CONTROLS THE REBOUND.", self.opponent); + self.opponent_possession(); + } + return; + } + if 7.0 / self.defense * self.rng.random::() <= 0.875 { + println!("SHOOTER FOULED. TWO SHOTS."); + self.foul_shots(1); + self.opponent_possession(); + return; + } + if 7.0 / self.defense * self.rng.random::() <= 0.925 { + println!("SHOT BLOCKED. {}'S BALL.", self.opponent); + self.opponent_possession(); + return; + } + println!("CHARGING FOUL. DARTMOUTH LOSES THE BALL."); + self.opponent_possession(); + } + + /// Lines 2010-2040: change the defensive alignment. Any value >= 6 is + /// accepted, matching the original (unvalidated) behaviour. + fn change_defense(&mut self) { + loop { + print!("YOUR NEW DEFENSIVE ALLIGNMENT IS"); + let d: f64 = read_input(); + if d >= 6.0 { + self.defense = d; + break; + } + } + self.dartmouth_possession(); + } + + /// Lines 3000-3810: opponent has the ball. Picks a shot type at random. + fn opponent_possession(&mut self) { + self.advance_clock(); + + let z1 = 10.0 / 4.0 * self.rng.random::() + 1.0; + if z1 > 2.0 { + self.opponent_non_jump_shot(z1); + } else { + self.opponent_jump_shot(); + } + } + + /// Lines 3040-3310: opponent jump shot. + fn opponent_jump_shot(&mut self) { + println!("JUMP SHOT."); + if 8.0 / self.defense * self.rng.random::() <= 0.35 { + println!("SHOT IS GOOD."); + self.add_opponent_score(); + self.dartmouth_possession(); + return; + } + if 8.0 / self.defense * self.rng.random::() <= 0.75 { + println!("SHOT IS OFF RIM."); + if self.defense / 6.0 * self.rng.random::() > 0.5 { + println!("{} CONTROLS THE REBOUND.", self.opponent); + if self.defense == 6.0 && self.rng.random::() > 0.75 { + // Line 5000-5030: ball stolen, easy lay-up for Dartmouth. + println!("BALL STOLEN. EASY LAY UP FOR DARTMOUTH."); + self.add_dartmouth_score(); + self.opponent_possession(); + } else if self.rng.random::() > 0.5 { + println!("PASS BACK TO {} GUARD.", self.opponent); + self.opponent_possession(); + } else { + self.opponent_non_jump_shot(3.0); + } + } else { + println!("DARTMOUTH CONTROLS THE REBOUND."); + self.dartmouth_possession(); + } + return; + } + if 8.0 / self.defense * self.rng.random::() <= 0.9 { + println!("PLAYER FOULED. TWO SHOTS."); + self.foul_shots(0); + self.dartmouth_possession(); + return; + } + println!("OFFENSIVE FOUL. DARTMOUTH'S BALL."); + self.dartmouth_possession(); + } + + /// Lines 3500-3810: opponent lay up or set shot. `z1` carries the + /// randomly chosen shot type so the put-back message can be re-shown. + fn opponent_non_jump_shot(&mut self, z1: f64) { + if z1 > 3.0 { + println!("SET SHOT."); + } else { + println!("LAY UP"); + } + if 7.0 / self.defense * self.rng.random::() <= 0.413 { + println!("SHOT IS GOOD."); + self.add_opponent_score(); + self.dartmouth_possession(); + return; + } + println!("SHOT IS MISSED."); + if self.defense / 6.0 * self.rng.random::() > 0.5 { + println!("{} CONTROLS THE REBOUND.", self.opponent); + if self.defense == 6.0 && self.rng.random::() > 0.75 { + println!("BALL STOLEN. EASY LAY UP FOR DARTMOUTH."); + self.add_dartmouth_score(); + self.opponent_possession(); + } else if self.rng.random::() > 0.5 { + self.opponent_non_jump_shot(z1); + } else { + println!("PASS BACK TO {} GUARD.", self.opponent); + self.opponent_possession(); + } + } else { + println!("DARTMOUTH CONTROLS THE REBOUND."); + self.dartmouth_possession(); + } + } + + /// Lines 4000-4110: two foul shots. `team` is 0 for the opponent (points + /// added to Dartmouth's opponent) or 1 for Dartmouth (points added to + /// Dartmouth). + fn foul_shots(&mut self, team: usize) { + if self.rng.random::() <= 0.49 { + println!("SHOOTER MAKES BOTH SHOTS."); + self.score[1 - team] += 2; + self.print_score(); + } else if self.rng.random::() <= 0.75 { + println!("SHOOTER MAKES ONE SHOT AND MISSES ONE."); + self.score[1 - team] += 1; + self.print_score(); + } else { + println!("BOTH SHOTS MISSED."); + self.print_score(); + } + } + + /// Line 6010: prints the running score. + fn print_score(&self) { + println!("SCORE: {} TO {}", self.score[1], self.score[0]); + } + + /// Line 7000: Dartmouth scores two points, then prints the score. + fn add_dartmouth_score(&mut self) { + self.score[1] += 2; + self.print_score(); + } + + /// Line 6000: opponent scores two points, then prints the score. + fn add_opponent_score(&mut self) { + self.score[0] += 2; + self.print_score(); + } + + /// Lines 1041/1301/3008: advance the clock and handle the end-of-half + /// break at T == 50. + fn advance_clock(&mut self) { + self.time += 1; + if self.time == 50 { + self.end_of_first_half(); + } + } + + /// Lines 1042-1046 / 1302-1304 / 3012-3015: advance the clock, handle the + /// end-of-half break at T == 50 and the two-minute warning at T == 92. + fn advance_clock_with_two_minute_warning(&mut self) { + self.time += 1; + if self.time == 50 { + self.end_of_first_half(); + } + if self.time == 92 { + self.two_minute_warning(); + } + } + + /// Lines 610-620: two-minute warning message. + fn two_minute_warning(&self) { + println!(); + println!(" *** TWO MINUTES LEFT IN THE GAME ***"); + println!(); + } + + /// Lines 8000-8020: end of the first half, then re-jump to start the + /// second half. + fn end_of_first_half(&mut self) { + println!(); + println!(" ***** END OF FIRST HALF *****"); + println!(); + println!( + "SCORE: DARTMOUTH:{} {}:{}", + self.score[1], self.opponent, self.score[0] + ); + println!(); + println!(); + self.center_jump(); + } + + /// Lines 490-520: the clock has expired. Either start overtime (if the + /// game is tied) or end the game. + fn end_of_game_or_overtime(&mut self) { + println!(); + if self.score[1] != self.score[0] { + println!(" ***** END OF GAME *****"); + println!( + "FINAL SCORE: DARTMOUTH:{} {}:{}", + self.score[1], self.opponent, self.score[0] + ); + return; + } + + println!(); + println!(" ***** END OF SECOND HALF *****"); + println!(); + println!("SCORE AT END OF REGULATION TIME:"); + println!( + " DARTMOUTH:{} {}:{}", + self.score[1], self.opponent, self.score[0] + ); + println!(); + println!("BEGIN TWO MINUTE OVERTIME PERIOD"); + self.time = 93; + self.center_jump(); + } +} + +/// Reads a line of input, flushing stdout first so the prompt is visible. +fn read_line() -> String { + print!("? "); + let _ = io::stdout().flush(); + + let mut buffer = String::new(); + let _ = io::stdin().read_line(&mut buffer); + buffer +} + +/// Reads a line and parses it as the requested numeric type, re-prompting on +/// any parse failure. +fn read_input() -> T { + loop { + let line = read_line(); + if let Ok(value) = line.trim().parse::() { + return value; + } + } +} diff --git a/08_Batnum/README.md b/08_Batnum/README.md index 840529867..bb74a4ec7 100644 --- a/08_Batnum/README.md +++ b/08_Batnum/README.md @@ -19,6 +19,10 @@ As published in Basic Computer Games (1978): Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html +#### Known Bugs + +- Though the instructions say "Enter a negative number for new pile size to stop playing," this does not actually work. + #### Porting Notes (please note any difficulties or challenges in porting here) diff --git a/08_Batnum/python/batnum.py b/08_Batnum/python/batnum.py index 3a54ee49b..bb41d8619 100644 --- a/08_Batnum/python/batnum.py +++ b/08_Batnum/python/batnum.py @@ -153,11 +153,8 @@ def computer_pick( q = pile_size - 1 if win_option == WinOptions.AvoidLast else pile_size c = min_select + max_select computer_pick = q - (c * int(q / c)) - if computer_pick < min_select: - computer_pick = min_select - if computer_pick > max_select: - computer_pick = max_select - return computer_pick + computer_pick = max(computer_pick, min_select) + return min(computer_pick, max_select) def computer_move( @@ -184,7 +181,7 @@ def computer_move( # Otherwise, we determine how many the computer selects curr_sel = computer_pick(pile_size, min_select, max_select, win_option) - pile_size = pile_size - curr_sel + pile_size -= curr_sel print(f"COMPUTER TAKES {curr_sel} AND LEAVES {pile_size}") return (False, pile_size) diff --git a/08_Batnum/rust/Cargo.lock b/08_Batnum/rust/Cargo.lock new file mode 100644 index 000000000..b21cc6a2d --- /dev/null +++ b/08_Batnum/rust/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "rust" +version = "0.1.0" diff --git a/08_Batnum/rust/Cargo.toml b/08_Batnum/rust/Cargo.toml new file mode 100644 index 000000000..1ec696335 --- /dev/null +++ b/08_Batnum/rust/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "rust" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/08_Batnum/rust/src/main.rs b/08_Batnum/rust/src/main.rs new file mode 100644 index 000000000..761e1939b --- /dev/null +++ b/08_Batnum/rust/src/main.rs @@ -0,0 +1,239 @@ +use std::io::{self, Write}; + +/// Print out the introduction and rules for the game. +fn print_intro() { + println!(); + println!(); + println!("{:>33}", "BATNUM"); + println!("{:>15}", "CREATIVE COMPUTING MORRISSTOWN, NEW JERSEY"); + println!(); + println!(); + println!(); + println!("THIS PROGRAM IS A 'BATTLE OF NUMBERS' GAME, WHERE THE"); + println!("COMPUTER IS YOUR OPPONENT."); + println!(); + println!("THE GAME STARTS WITH AN ASSUMED PILE OF OBJECTS. YOU"); + println!("AND YOUR OPPONENT ALTERNATELY REMOVE OBJECTS FROM THE PILE."); + println!("WINNING IS DEFINED IN ADVANCE AS TAKING THE LAST OBJECT OR"); + println!("NOT. YOU CAN ALSO SPECIFY SOME OTHER BEGINNING CONDITIONS."); + println!("DON'T USE ZERO, HOWEVER, IN PLAYING THE GAME."); + println!("ENTER A NEGATIVE NUMBER FOR NEW PILE SIZE TO STOP PLAYING."); + println!(); +} + +/// This requests the necessary parameters to play the game. +/// five game parameters: +/// * pile_size - the starting size of the object pile +/// * min_select - minimum selection that can be made on each turn +/// * max_select - maximum selection that can be made on each turn +/// * start_option - computer first or player first +/// * win_option - goal is to take the last object +/// or the goal is to not take the last object +struct Params { + pub pile_size: usize, + pub min_select: usize, + pub max_select: usize, + pub start_option: StartOption, + pub win_option: WinOption, +} + +#[derive(PartialEq, Eq)] +enum StartOption { + ComputerFirst, + PlayerFirst, +} + +#[derive(PartialEq, Eq)] +enum WinOption { + TakeLast, + AvoidLast, +} + +impl Params { + pub fn get_params() -> Self { + let pile_size = Self::get_pile_size(); + let (min_select, max_select) = Self::get_min_max(); + let start_option = Self::get_start_option(); + let win_option = Self::get_win_option(); + + Self { + pile_size, + min_select, + max_select, + start_option, + win_option, + } + } + + fn get_pile_size() -> usize { + print!("ENTER PILE SIZE "); + let _ = io::stdout().flush(); + read_input_integer() + } + + fn get_win_option() -> WinOption { + print!("ENTER WIN OPTION: 1 TO TAKE LAST, 2 TO AVOID LAST: "); + let _ = io::stdout().flush(); + + loop { + match read_input_integer() { + 1 => { + return WinOption::TakeLast; + } + 2 => { + return WinOption::AvoidLast; + } + _ => { + print!("Please enter 1 or 2 "); + let _ = io::stdout().flush(); + continue; + } + } + } + } + + fn get_start_option() -> StartOption { + print!("ENTER START OPTION: 1 COMPUTER FIRST, 2 YOU FIRST "); + let _ = io::stdout().flush(); + + loop { + match read_input_integer() { + 1 => { + return StartOption::ComputerFirst; + } + 2 => { + return StartOption::PlayerFirst; + } + _ => { + print!("Please enter 1 or 2 "); + let _ = io::stdout().flush(); + continue; + } + } + } + } + + fn get_min_max() -> (usize, usize) { + print!("ENTER MIN "); + let _ = io::stdout().flush(); + let min = read_input_integer(); + + print!("ENTER MAX "); + let _ = io::stdout().flush(); + let max = read_input_integer(); + + (min, max) + } +} + +fn read_input_integer() -> usize { + loop { + let mut input = String::new(); + io::stdin() + .read_line(&mut input) + .expect("Failed to read line"); + match input.trim().parse::() { + Ok(num) => { + if num == 0 { + print!("Must be greater than zero "); + let _ = io::stdout().flush(); + continue; + } + return num; + } + Err(_err) => { + print!("Please enter a number greater than zero "); + let _ = io::stdout().flush(); + continue; + } + } + } +} + +fn player_move(pile_size: &mut usize, params: &Params) -> bool { + loop { + print!("YOUR MOVE "); + let _ = io::stdout().flush(); + + let player_move = read_input_integer(); + if player_move == 0 { + println!("I TOLD YOU NOT TO USE ZERO! COMPUTER WINS BY FORFEIT."); + return true; + } + if player_move > params.max_select || player_move < params.min_select { + println!("ILLEGAL MOVE, REENTER IT"); + continue; + } + *pile_size -= player_move; + if *pile_size == 0 { + if params.win_option == WinOption::AvoidLast { + println!("TOUGH LUCK, YOU LOSE."); + } else { + println!("CONGRATULATIONS, YOU WIN.") + } + return true; + } + return false; + } +} + +fn computer_pick(pile_size: usize, params: &Params) -> usize { + let q = if params.win_option == WinOption::AvoidLast { + pile_size - 1 + } else { + pile_size + }; + let c = params.min_select + params.max_select; + let computer_pick = q - (c * (q / c)); + let computer_pick = if computer_pick < params.min_select { + params.min_select + } else { + computer_pick + }; + if computer_pick > params.max_select { + params.max_select + } else { + computer_pick + } +} + +fn computer_move(pile_size: &mut usize, params: &Params) -> bool { + if params.win_option == WinOption::TakeLast && *pile_size <= params.max_select { + println!("COMPUTER TAKES {pile_size} AND WINS."); + return true; + } + if params.win_option == WinOption::AvoidLast && *pile_size >= params.min_select { + println!("COMPUTER TAKES {} AND LOSES.", params.min_select); + return true; + } + + let curr_sel = computer_pick(*pile_size, params); + *pile_size -= curr_sel; + println!("COMPUTER TAKES {curr_sel} AND LEAVES {pile_size}"); + false +} + +fn play_game(params: &Params) { + let mut pile_size = params.pile_size; + + if params.start_option == StartOption::ComputerFirst && computer_move(&mut pile_size, params) { + return; + } + + loop { + if player_move(&mut pile_size, params) { + return; + } + if computer_move(&mut pile_size, params) { + return; + } + } +} + +fn main() -> ! { + loop { + print_intro(); + let params = Params::get_params(); + play_game(¶ms); + } +} diff --git a/09_Battle/README.md b/09_Battle/README.md index 042864528..29d2622e7 100644 --- a/09_Battle/README.md +++ b/09_Battle/README.md @@ -13,7 +13,7 @@ The first thing you should learn is how to locate and designate positions on the 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 main objective and primary education benefit of BATTLE comes from attempting to decode the bad 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. @@ -28,4 +28,4 @@ http://www.vintage-basic.net/games.html #### Porting Notes -(please note any difficulties or challenges in porting here) +- The original game has no way to re-view the fleet disposition code once it scrolls out of view. Ports should consider allowing the user to enter "?" at the "??" prompt, to reprint the disposition code. (This is added by the MiniScript port under Alternate Languages, for example.) \ No newline at end of file diff --git a/09_Battle/python/battle.py b/09_Battle/python/battle.py index 993b9f458..2418b35d5 100755 --- a/09_Battle/python/battle.py +++ b/09_Battle/python/battle.py @@ -40,8 +40,8 @@ def place_ship(sea: SeaType, size: int, code: int) -> None: point = add_vector(point, vector) points.append(point) - if not all([is_within_sea(point, sea) for point in points]) or any( - [value_at(point, sea) for point in points] + if not all(is_within_sea(point, sea) for point in points) or any( + value_at(point, sea) for point in points ): # ship out of bounds or crosses other ship, trying again continue diff --git a/09_Battle/rust/Cargo.lock b/09_Battle/rust/Cargo.lock new file mode 100644 index 000000000..10a595996 --- /dev/null +++ b/09_Battle/rust/Cargo.lock @@ -0,0 +1,75 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/09_Battle/rust/Cargo.toml b/09_Battle/rust/Cargo.toml new file mode 100644 index 000000000..3b1d02f52 --- /dev/null +++ b/09_Battle/rust/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "rust" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rand = "0.8.5" diff --git a/09_Battle/rust/src/main.rs b/09_Battle/rust/src/main.rs new file mode 100644 index 000000000..f9e8d36ec --- /dev/null +++ b/09_Battle/rust/src/main.rs @@ -0,0 +1,273 @@ +use rand::Rng; +use std::{ + cmp::Ordering, + fmt, + io::{self, Write}, +}; + +#[derive(Clone, Copy)] +#[repr(C)] +enum ShipLength { + Destroyer = 2, + Cruiser = 3, + AircraftCarrier = 4, +} + +#[derive(Clone, Copy, PartialEq)] +struct Point(i8, i8); + +impl core::ops::Add for Point { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + Self(self.0 + rhs.0, self.1 + rhs.1) + } +} + +impl Point { + pub fn is_outside(&self, width: usize) -> bool { + let w = width as i8; + (!(0..w).contains(&self.0)) || (!(0..w).contains(&self.1)) + } + + pub fn userinput2coordinate(self) -> Self { + Self(self.0 - 1, See::WIDTH as i8 - self.1) + } +} + +struct Ship(Vec); + +impl Ship { + pub fn new(length: ShipLength) -> Self { + 'try_again: loop { + let start = Point( + rand::thread_rng().gen_range(0..See::WIDTH) as i8, + rand::thread_rng().gen_range(0..See::WIDTH) as i8, + ); + let vector = Self::random_vector(); + + let mut ship = vec![start]; + for _ in 1..length as usize { + let last = ship.last().unwrap(); + let new_part = *last + vector; + if new_part.is_outside(See::WIDTH) { + continue 'try_again; + } + ship.push(new_part); + } + + return Self(ship); + } + } + + fn random_vector() -> Point { + loop { + let vector = Point( + rand::thread_rng().gen_range(-1..2), + rand::thread_rng().gen_range(-1..2), + ); + if vector != Point(0, 0) { + return vector; + } + } + } + + pub fn collide(&self, see: &[Vec]) -> bool { + self.0.iter().any(|p| see[p.0 as usize][p.1 as usize] != 0) + } + + pub fn place(self, see: &mut [Vec], code: i8) { + for p in self.0.iter() { + see[p.0 as usize][p.1 as usize] = code; + } + } +} + +enum Report { + Already(i8), + Splash, + Hit(i8), +} + +struct See { + data: Vec>, +} + +impl See { + pub const WIDTH: usize = 6; + + fn place_ship(data: &mut [Vec], length: ShipLength, code: i8) { + let ship = loop { + let ship = Ship::new(length); + if ship.collide(data) { + continue; + } + break ship; + }; + ship.place(data, code); + } + + pub fn new() -> Self { + let mut data = vec![vec![0; Self::WIDTH]; Self::WIDTH]; + + Self::place_ship(&mut data, ShipLength::Destroyer, 1); + Self::place_ship(&mut data, ShipLength::Destroyer, 2); + Self::place_ship(&mut data, ShipLength::Cruiser, 3); + Self::place_ship(&mut data, ShipLength::Cruiser, 4); + Self::place_ship(&mut data, ShipLength::AircraftCarrier, 5); + Self::place_ship(&mut data, ShipLength::AircraftCarrier, 6); + + Self { data } + } + + pub fn report(&mut self, point: Point) -> Report { + let (x, y) = (point.0 as usize, point.1 as usize); + let value = self.data[x][y]; + match value.cmp(&0) { + Ordering::Less => Report::Already(-value), + Ordering::Equal => Report::Splash, + Ordering::Greater => { + self.data[x][y] = -value; + Report::Hit(value) + } + } + } + + pub fn has_ship(&self, code: i8) -> bool { + self.data.iter().any(|v| v.contains(&code)) + } + + pub fn has_any_ship(&self) -> bool { + (1..=6).any(|c| self.has_ship(c)) + } + + pub fn count_sunk(&self, ship: ShipLength) -> i32 { + let codes = match ship { + ShipLength::Destroyer => (1, 2), + ShipLength::Cruiser => (3, 4), + ShipLength::AircraftCarrier => (5, 6), + }; + + let ret = if self.has_ship(codes.0) { 0 } else { 1 }; + ret + if self.has_ship(codes.1) { 0 } else { 1 } + } +} + +impl fmt::Display for See { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for row in &self.data { + write!(f, "\r\n")?; + for cell in row { + write!(f, "{:2} ", cell)?; + } + } + write!(f, "\r\n") + } +} + +fn input_point() -> Result { + let mut input = String::new(); + io::stdin() + .read_line(&mut input) + .expect("Failed to read line"); + let point_str: Vec<&str> = input.trim().split(',').collect(); + + if point_str.len() != 2 { + return Err(()); + } + + let x = point_str[0].parse::().map_err(|_| ())?; + let y = point_str[1].parse::().map_err(|_| ())?; + + Ok(Point(x, y)) +} + +fn get_next_target() -> Point { + loop { + print!("? "); + let _ = io::stdout().flush(); + + if let Ok(p) = input_point() { + let p = p.userinput2coordinate(); + if !p.is_outside(See::WIDTH) { + return p; + } + } + + println!( + "INVALID. SPECIFY TWO NUMBERS FROM 1 TO {}, SEPARATED BY A COMMA.", + See::WIDTH + ); + } +} + +fn main() { + let mut see = See::new(); + println!( + " + BATTLE +CREATIVE COMPUTING MORRISTOWN, NEW JERSEY + +THE FOLLOWING CODE OF THE BAD GUYS' FLEET DISPOSITION +HAS BEEN CAPTURED BUT NOT DECODED: + " + ); + + println!("{see}"); + + println!( + " + +DE-CODE IT AND USE IT IF YOU CAN +BUT KEEP THE DE-CODING METHOD A SECRET. + +START GAME + " + ); + + let mut splashes = 0; + let mut hits = 0; + + loop { + let target = get_next_target(); + + let r = see.report(target); + if let Report::Hit(c) = r { + println!("A DIRECT HIT ON SHIP NUMBER {c}"); + hits += 1; + + if !see.has_ship(c) { + println!("AND YOU SUNK IT. HURRAH FOR THE GOOD GUYS."); + println!("SO FAR, THE BAD GUYS HAVE LOST"); + println!("{} DESTROYER(S),", see.count_sunk(ShipLength::Destroyer)); + println!("{} CRUISER(S),", see.count_sunk(ShipLength::Cruiser)); + println!( + "AND {} AIRCRAFT CARRIER(S),", + see.count_sunk(ShipLength::AircraftCarrier) + ); + } + } else { + if let Report::Already(c) = r { + println!("YOU ALREADY PUT A HOLE IN SHIP NUMBER {c} AT THAT POINT."); + } + println!("SPLASH! TRY AGAIN."); + splashes += 1; + continue; + } + + if see.has_any_ship() { + println!("YOUR CURRENT SPLASH/HIT RATIO IS {splashes}/{hits}"); + continue; + } + + println!("YOU HAVE TOTALLY WIPED OUT THE BAD GUYS' FLEET "); + println!("WITH A FINAL SPLASH/HIT RATIO OF {splashes}/{hits}"); + + if splashes == 0 { + println!("CONGRATULATIONS -- A DIRECT HIT EVERY TIME."); + } + + println!("\n****************************"); + break; + } +} diff --git a/10_Blackjack/README.md b/10_Blackjack/README.md index dcbe53bb9..66cfee65c 100644 --- a/10_Blackjack/README.md +++ b/10_Blackjack/README.md @@ -15,6 +15,16 @@ As published in Basic Computer Games (1978): Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html + #### Porting Notes -(please note any difficulties or challenges in porting here) +The program makes extensive use of the assumption that a boolean expression evaluates to **-1** for true. This was the case in some classic BASIC environments but not others; and it is not the case in [JS Basic](https://troypress.com/wp-content/uploads/user/js-basic/index.html), leading to nonsensical results. In an environment that uses **1** instead of **-1** for truth, you would need to negate the boolean expression in the following lines: + - 10 + - 570 + - 590 + - 2220 + - 2850 + - 3100 + - 3400 + - 3410 + - 3420 diff --git a/10_Blackjack/python/blackjack.py b/10_Blackjack/python/blackjack.py index a2c38260c..615aec79b 100644 --- a/10_Blackjack/python/blackjack.py +++ b/10_Blackjack/python/blackjack.py @@ -58,10 +58,7 @@ def add_card(self, card: Card) -> None: def get_total(self) -> int: """returns the total points of the cards in this hand""" - total: int = 0 - for card in self.cards: - total += int(card.value) - + total: int = sum(int(card.value) for card in self.cards) # if there is an ACE, and the hand would otherwise bust, # treat the ace like it's worth 1 if total > 21 and any(card.name == "ACE" for card in self.cards): @@ -109,24 +106,21 @@ def draw_card(self) -> Card: draw card from deck, and return it if deck is empty, shuffles discard pile into it and tries again """ - if len(self.deck) == 0: - _len = len(self.discard_pile) - - if _len > 0: - # deck is empty, shuffle discard pile into deck and try again - print("deck is empty, shuffling") - for _i in range(_len): - if len(self.discard_pile) == 0: - raise ValueError("discard pile is empty") - self.deck.append(self.discard_pile.pop()) - self.shuffle() - return self.draw_card() - else: - # discard pile and deck are empty, should never happen - raise Exception("discard pile empty") - else: - card = self.deck.pop() - return card + if len(self.deck) != 0: + return self.deck.pop() + _len = len(self.discard_pile) + + if _len <= 0: + # discard pile and deck are empty, should never happen + raise Exception("discard pile empty") + # deck is empty, shuffle discard pile into deck and try again + print("deck is empty, shuffling") + for _i in range(_len): + if len(self.discard_pile) == 0: + raise ValueError("discard pile is empty") + self.deck.append(self.discard_pile.pop()) + self.shuffle() + return self.draw_card() @dataclass @@ -171,24 +165,19 @@ def hand_as_string(self, hide_dealer: bool) -> str: by *'s for every other card if player is a player, returns every card and the total """ - if not hide_dealer: - s = "" - for cards_in_hand in self.hand.cards[::-1]: - s += f"{cards_in_hand.name}\t" + if hide_dealer and self.player_type == PlayerType.Dealer: + return "".join(f"{c.name}\t" for c in self.hand.cards[1::-1]) + elif ( + hide_dealer + and self.player_type == PlayerType.Player + or not hide_dealer + ): + s = "".join( + f"{cards_in_hand.name}\t" + for cards_in_hand in self.hand.cards[::-1] + ) s += f"total points = {self.hand.get_total()}" return s - else: - if self.player_type == PlayerType.Dealer: - s = "" - for c in self.hand.cards[1::-1]: - s += f"{c.name}\t" - return s - elif self.player_type == PlayerType.Player: - s = "" - for cards_in_hand in self.hand.cards[::-1]: - s += f"{cards_in_hand.name}\t" - s += f"total points = {self.hand.get_total()}" - return s raise Exception("This is unreachable") def get_play(self) -> Play: @@ -197,10 +186,7 @@ def get_play(self) -> Play: # if it's a dealer, use an algorithm to determine the play # if it's a player, ask user for input if self.player_type == PlayerType.Dealer: - if self.hand.get_total() > 16: - return Play.Stand - else: - return Play.Hit + return Play.Stand if self.hand.get_total() > 16 else Play.Hit elif self.player_type == PlayerType.Player: valid_results: List[str] if len(self.hand.cards) > 2: @@ -232,15 +218,11 @@ class Game: @classmethod def new(cls, num_players: int) -> "Game": - players: List[Player] = [] + players: List[Player] = [Player.new(PlayerType.Dealer, 0)] - # add dealer - players.append(Player.new(PlayerType.Dealer, 0)) # create human player(s) (at least one) players.append(Player.new(PlayerType.Player, 1)) - for i in range(2, num_players): # one less than num_players players - players.append(Player.new(PlayerType.Player, i)) - + players.extend(Player.new(PlayerType.Player, i) for i in range(2, num_players)) if get_char_from_user_input("Do you want instructions", ["y", "n"]) == "y": print_instructions() print() @@ -322,9 +304,6 @@ def play_game(self) -> None: else: player.bet = player.balance player.hand.add_card(self.decks.draw_card()) - elif play == Play.Split: - pass - # add player to score cache thing player_hands_message += ( f"{player.get_name()} Hand:\t{player.hand_as_string(True)}\n" @@ -460,7 +439,7 @@ def get_number_from_user_input(prompt: str, min_value: int, max_value: int) -> i # input loop user_input = None while user_input is None or user_input < min_value or user_input > max_value: - raw_input = input(prompt + f" ({min_value}-{max_value})? ") + raw_input = input(f"{prompt} ({min_value}-{max_value})? ") try: user_input = int(raw_input) @@ -475,7 +454,7 @@ def get_char_from_user_input(prompt: str, valid_results: List[str]) -> str: """returns the first character they type""" user_input = None while user_input not in valid_results: - user_input = input(prompt + f" {valid_results}? ").lower() + user_input = input(f"{prompt} {valid_results}? ").lower() if user_input not in valid_results: print("Invalid input, please try again") assert user_input is not None diff --git a/10_Blackjack/rust/Cargo.lock b/10_Blackjack/rust/Cargo.lock new file mode 100644 index 000000000..4fe2abbea --- /dev/null +++ b/10_Blackjack/rust/Cargo.lock @@ -0,0 +1,75 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/11_Bombardment/README.md b/11_Bombardment/README.md index d0d05bf9b..d4bd08d74 100644 --- a/11_Bombardment/README.md +++ b/11_Bombardment/README.md @@ -2,7 +2,7 @@ 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. +At the start of the game, you locate (or hide) your four platoons on your grid. The computer does the same on its 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. @@ -15,6 +15,12 @@ As published in Basic Computer Games (1978): Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html +#### Known Bugs + +- Though the instructions say you can't place two platoons on the same outpost, the code does not enforce this. So the player can "cheat" and guarantee a win by entering the same outpost number two or more times. + #### Porting Notes +- To ensure the instructions don't scroll off the top of the screen, we may want to insert a "(Press Return)" or similar prompt before printing the tear-off matrix. + (please note any difficulties or challenges in porting here) diff --git a/11_Bombardment/rust/Cargo.lock b/11_Bombardment/rust/Cargo.lock new file mode 100644 index 000000000..81365033b --- /dev/null +++ b/11_Bombardment/rust/Cargo.lock @@ -0,0 +1,82 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "morristown" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83b191fd96370b91c2925125774011a7a0f69b4db9e2045815e4fd20af725f6" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "morristown", + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/12_Bombs_Away/README.md b/12_Bombs_Away/README.md index 8a2decaa1..603516fb1 100644 --- a/12_Bombs_Away/README.md +++ b/12_Bombs_Away/README.md @@ -13,6 +13,10 @@ As published in Basic Computer Games (1978): Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html +#### Known Bugs + +- If you play as Japan and say it is not your first mission, it is impossible to complete your mission; the only possible outcomes are "you made it through" or "boom". Moreover, the odds of each outcome depend on a variable (R) that is only set if you played a previous mission as a different side. It's possible this is an intentional layer of complexity meant to encourage repeat play, but it's more likely just a logical error. + #### Porting Notes (please note any difficulties or challenges in porting here) diff --git a/12_Bombs_Away/rust/Cargo.lock b/12_Bombs_Away/rust/Cargo.lock new file mode 100644 index 000000000..59dc11cca --- /dev/null +++ b/12_Bombs_Away/rust/Cargo.lock @@ -0,0 +1,75 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/12_Bombs_Away/rust/Cargo.toml b/12_Bombs_Away/rust/Cargo.toml new file mode 100644 index 000000000..3b1d02f52 --- /dev/null +++ b/12_Bombs_Away/rust/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "rust" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rand = "0.8.5" diff --git a/12_Bombs_Away/rust/src/main.rs b/12_Bombs_Away/rust/src/main.rs new file mode 100644 index 000000000..d049fd849 --- /dev/null +++ b/12_Bombs_Away/rust/src/main.rs @@ -0,0 +1,309 @@ +use std::{ + io::{self, Write}, + str::FromStr, +}; + +use rand::Rng; + +struct Italy; +struct Allies; +struct Japan; +struct Germany; + +#[derive(Debug, PartialEq, Eq)] +struct ParseChoiceTargetError; + +#[derive(PartialEq)] +enum ThreeTarget { + One, + Two, + Three, +} + +impl FromStr for ThreeTarget { + type Err = ParseChoiceTargetError; + + fn from_str(s: &str) -> Result { + let n = s.parse::().map_err(|_| ParseChoiceTargetError)?; + match n { + 1 => Ok(Self::One), + 2 => Ok(Self::Two), + 3 => Ok(Self::Three), + _ => Err(ParseChoiceTargetError), + } + } +} + +enum FourTarget { + One, + Two, + Three, + Four, +} + +impl FromStr for FourTarget { + type Err = ParseChoiceTargetError; + + fn from_str(s: &str) -> Result { + let n = s.parse::().map_err(|_| ParseChoiceTargetError)?; + match n { + 1 => Ok(Self::One), + 2 => Ok(Self::Two), + 3 => Ok(Self::Three), + 4 => Ok(Self::Four), + _ => Err(ParseChoiceTargetError), + } + } +} + +pub trait Brefing { + type TargetOption; + fn prompt<'a>(&self) -> &'a str; + fn targets_to_messages<'a>(&self, target: Self::TargetOption) -> &'a str; +} + +impl Brefing for Italy { + type TargetOption = ThreeTarget; + + fn prompt<'a>(&self) -> &'a str { + "YOUR TARGET -- ALBANIA(1), GREECE(2), NORTH AFRICA(3)" + } + + fn targets_to_messages<'a>(&self, target: Self::TargetOption) -> &'a str { + match target { + ThreeTarget::One => "SHOULD BE EASY -- YOU'RE FLYING A NAZI-MADE PLANE.", + ThreeTarget::Two => "BE CAREFUL!!!", + ThreeTarget::Three => "YOU'RE GOING FOR THE OIL, EH?", + } + } +} + +impl Brefing for Allies { + type TargetOption = FourTarget; + + fn prompt<'a>(&self) -> &'a str { + "AIRCRAFT -- LIBERATOR(1), B-29(2), B-17(3), LANCASTER(4): " + } + + fn targets_to_messages<'a>(&self, target: Self::TargetOption) -> &'a str { + match target { + FourTarget::One => "YOU'VE GOT 2 TONS OF BOMBS FLYING FOR PLOESTI.", + FourTarget::Two => "YOU'RE DUMPING THE A-BOMB ON HIROSHIMA.", + FourTarget::Three => "YOU'RE CHASING THE BISMARK IN THE NORTH SEA.", + FourTarget::Four => "YOU'RE BUSTING A GERMAN HEAVY WATER PLANT IN THE RUHR.", + } + } +} + +impl Brefing for Germany { + type TargetOption = ThreeTarget; + + fn prompt<'a>(&self) -> &'a str { + "A NAZI, EH? OH WELL. ARE YOU GOING FOR RUSSIA(1),\nENGLAND(2), OR FRANCE(3)? " + } + + fn targets_to_messages<'a>(&self, target: Self::TargetOption) -> &'a str { + match target { + ThreeTarget::One => "YOU'RE NEARING STALINGRAD.", + ThreeTarget::Two => "NEARING LONDON. BE CAREFUL, THEY'VE GOT RADAR.", + ThreeTarget::Three => "NEARING VERSAILLES. DUCK SOUP. THEY'RE NEARLY DEFENSELESS.", + } + } +} + +enum Side { + Italy(Italy), + Allies(Allies), + Japan(Japan), + Germany(Germany), +} + +impl From for Side { + fn from(value: FourTarget) -> Self { + match value { + FourTarget::One => Self::Italy(Italy), + FourTarget::Two => Self::Allies(Allies), + FourTarget::Three => Self::Japan(Japan), + FourTarget::Four => Self::Germany(Germany), + } + } +} + +fn stdin_choice(prompt: &str) -> C { + let mut buffer = String::new(); + + print!("{prompt}"); + io::stdout().flush().unwrap(); + io::stdin().read_line(&mut buffer).unwrap(); + loop { + if let Ok(choice) = buffer.trim().parse::() { + return choice; + } + print!("TRY AGAIN..."); + io::stdout().flush().unwrap(); + io::stdin().read_line(&mut buffer).unwrap(); + } +} + +fn stdin_y_or_n(prompt: &str) -> bool { + let mut buffer = String::new(); + + print!("{prompt}"); + io::stdout().flush().unwrap(); + io::stdin().read_line(&mut buffer).unwrap(); + buffer.trim().to_uppercase() == "Y" +} + +fn commence_non_kamikazi_attack() { + let nmissions = loop { + let nmissions = stdin_choice::("HOW MANY MISSIONS HAVE YOU FLOWN? "); + if nmissions < 160 { + break nmissions; + } + println!("MISSIONS, NOT MILES..."); + println!("150 MISSIONS IS HIGH EVEN FOR OLD-TIMERS"); + print!("NOW THEN, "); + }; + + if nmissions >= 100 { + println!("THAT'S PUSHING THE ODDS!"); + } + + if nmissions < 25 { + println!("FRESH OUT OF TRAINING, EH?"); + } + + println!(); + + let mut rng = rand::thread_rng(); + let y: f32 = rng.gen(); + + if nmissions as f32 >= 160_f32 * y { + mission_success(); + } else { + mission_failure(); + } +} + +fn play_japan() { + if !stdin_y_or_n("YOUR FIRST KAMIKAZE MISSION? (Y OR N): ") { + player_death(); + return; + } + + let mut rng = rand::thread_rng(); + let y: f32 = rng.gen(); + if y > 0.65 { + mission_success(); + } else { + player_death(); + } +} + +fn player_death() { + println!("* * * * BOOM * * * *"); + println!("YOU HAVE BEEN SHOT DOWN....."); + println!("DEARLY BELOVED, WE ARE GATHERED HERE TODAY TO PAY OUR"); + println!("LAST TRIBUTE..."); +} + +fn mission_success() { + let mut rng = rand::thread_rng(); + let y: f32 = rng.gen(); + + let killed = (100f32 * y) as i32; + println!("DIRECT HIT!!!! {killed} KILLED."); + println!("MISSION SUCCESSFUL."); +} + +fn mission_failure() { + let mut rng = rand::thread_rng(); + let y: f32 = rng.gen(); + let miles = 2 + (30f32 * y) as i32; + println!("MISSED TARGET BY {miles} MILES!"); + println!("NOW YOU'RE REALLY IN FOR IT !!"); + println!(); + let enemy_weapons = + stdin_choice::("DOES THE ENEMY HAVE GUNS(1), MISSILES(2), OR BOTH(3)? "); + + let enemy_gunner_accuracy = if enemy_weapons != ThreeTarget::Two { + let m = loop { + let m = + stdin_choice::("WHAT'S THE PERCENT HIT RATE OF ENEMY GUNNERS (10 TO 50)? "); + if m <= 50 { + break m; + } + println!("TRY AGAIN..."); + }; + if m < 10 { + println!("YOU LIE, BUT YOU'LL PAY..."); + player_death(); + return; + } + m + } else { + 0 + }; + + let missile_threat_weighting = if enemy_weapons == ThreeTarget::One { + 0 + } else { + 35 + }; + + let death = + death_with_chance((enemy_gunner_accuracy + missile_threat_weighting) as f32 / 100f32); + + if death { + player_death(); + } else { + player_survived(); + } +} + +fn player_survived() { + println!("YOU MADE IT THROUGH TREMENDOUS FLAK!!"); +} + +fn death_with_chance(p_death: f32) -> bool { + let mut rng = rand::thread_rng(); + let y: f32 = rng.gen(); + p_death > y +} + +fn main() { + loop { + println!("YOU ARE A PILOT IN A WORLD WAR II BOMBER."); + let side: Side = + stdin_choice::("WHAT SIDE -- ITALY(1), ALLIES(2), JAPAN(3), GERMANY(4): ") + .into(); + + match side { + Side::Japan(_) => { + println!("YOU'RE FLYING A KAMIKAZE MISSION OVER THE USS LEXINGTON."); + } + Side::Italy(ref s) => { + let target = stdin_choice(s.prompt()); + println!("{}", s.targets_to_messages(target)); + } + Side::Allies(ref s) => { + let target = stdin_choice(s.prompt()); + println!("{}", s.targets_to_messages(target)); + } + Side::Germany(ref s) => { + let target = stdin_choice(s.prompt()); + println!("{}", s.targets_to_messages(target)); + } + } + + match side { + Side::Japan(_) => play_japan(), + _ => commence_non_kamikazi_attack(), + } + + println!(); + if !stdin_y_or_n("ANOTHER MISSION? (Y OR N): ") { + break; + } + } +} diff --git a/13_Bounce/perl/README.md b/13_Bounce/perl/README.md index e69c8b819..6d213dba3 100644 --- a/13_Bounce/perl/README.md +++ b/13_Bounce/perl/README.md @@ -1,3 +1,6 @@ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Perl](https://www.perl.org/) + +Added feature so that if "TIME" value is "0" then it will quit, +so you don't have to hit Control-C. Also added a little error checking of the input. diff --git a/13_Bounce/perl/bounce.pl b/13_Bounce/perl/bounce.pl new file mode 100755 index 000000000..4aeacf1f7 --- /dev/null +++ b/13_Bounce/perl/bounce.pl @@ -0,0 +1,97 @@ +#!/usr/bin/perl + +# Bounce program in Perl +# Translated by Kevin Brannen (kbrannen) + +use strict; +use warnings; + +print "\n"; +print " " x 31,"BOUNCE\n"; +print " " x 15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n"; +print "\n\n\n"; + +print "THIS SIMULATION LETS YOU SPECIFY THE INITIAL VELOCITY\n"; +print "OF A BALL THROWN STRAIGHT UP, AND THE COEFFICIENT OF\n"; +print "ELASTICITY OF THE BALL. PLEASE USE A DECIMAL FRACTION\n"; +print "COEFFICIENCY (LESS THAN 1).\n\n"; +print "YOU ALSO SPECIFY THE TIME INCREMENT TO BE USED IN\n"; +print "'STROBING' THE BALL'S FLIGHT (TRY .1 INITIALLY).\n\n"; + +# deal with basic's tab() for line positioning +# line = line string we're starting with +# pos = position to start writing +# s = string to write +# returns the resultant string, which might not have been changed +sub line_tab +{ + my ($line, $pos, $s) = @_; + my $len = length($line); + # if curser is past position, do nothing + if ($len <= $pos) { $line .= " " x ($pos - $len) . $s; } + return $line; +} + +while (1) +{ + my @T; # time slice? + my $time_inc; # time increment, probably in fractions of seconds + my $velocity; # velocity in feet/sec + my $coeff_elas; # coeeficent of elasticity + my $line_pos; # position on line + my $S1 # duration in full seconds? + + # get input + print "TIME INCREMENT (SEC, 0=QUIT): "; chomp($time_inc = <>); + last if ($time_inc == 0); + print "VELOCITY (FPS): "; chomp($velocity = <>); + print "COEFFICIENT: "; chomp($coeff_elas = <>); + if ($coeff_elas >= 1.0 || $coeff_elas <= 0) + { + print "COEFFICIENT MUST BE > 0 AND < 1.0\n\n\n"; + next; + } + + print "\nFEET\n"; + $S1 = int(70.0 / ($velocity / (16.0 * $time_inc))); + for my $i (1 .. $S1) + { + $T[$i] = $velocity * $coeff_elas ** ($i - 1) / 16.0; + } + + # draw graph + for (my $height=int(-16.0 * ($velocity / 32.0) ** 2.0 + $velocity ** 2.0 / 32.0 + .5) ; $height >= 0 ; $height -= .5) + { + if (int($height) == $height) { print sprintf("%2d", $height); } + else { print " "; } + $line_pos = 0; + my $curr_line = ""; + for my $i (1 .. $S1) + { + my $time; + for ($time=0 ; $time <= $T[$i] ; $time += $time_inc) + { + $line_pos += $time_inc; + if (abs($height - (.5 * (-32) * $time ** 2.0 + $velocity * $coeff_elas ** ($i - 1) * $time)) <= .25) + { + $curr_line = line_tab($curr_line, ($line_pos / $time_inc), "0"); + } + } + $time = ($T[$i + 1] // 0) / 2; # we can reach 1 past the end, use 0 if that happens + last if (-16.0 * $time ** 2.0 + $velocity * $coeff_elas ** ($i - 1) * $time < $height); + } + print "$curr_line\n"; + } + + # draw scale + print " ."; + print "." x (int($line_pos + 1) / $time_inc + 1), "\n"; + print " 0"; + my $curr_line = ""; + for my $i (1 .. int($line_pos + .9995)) + { + $curr_line = line_tab($curr_line, int($i / $time_inc), $i); + } + print "$curr_line\n"; + print " " x (int($line_pos + 1) / (2 * $time_inc) - 2), "SECONDS\n\n"; +} diff --git a/13_Bounce/python/bounce.py b/13_Bounce/python/bounce.py index f36acc94f..e454183b0 100644 --- a/13_Bounce/python/bounce.py +++ b/13_Bounce/python/bounce.py @@ -83,7 +83,7 @@ def run_simulation(delta_t: float, v0: float, coeff_rest: float) -> None: if -16 * tm**2 + v0 * coeff_rest ** (i - 1) * tm < h: break print(line) - h = h - 0.5 + h -= 0.5 print("." * (int((total_time + 1) / delta_t) + 1)) print diff --git a/13_Bounce/rust/Cargo.lock b/13_Bounce/rust/Cargo.lock new file mode 100644 index 000000000..b21cc6a2d --- /dev/null +++ b/13_Bounce/rust/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "rust" +version = "0.1.0" diff --git a/13_Bounce/rust/Cargo.toml b/13_Bounce/rust/Cargo.toml new file mode 100644 index 000000000..7d75412a6 --- /dev/null +++ b/13_Bounce/rust/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "rust" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/13_Bounce/rust/src/main.rs b/13_Bounce/rust/src/main.rs new file mode 100644 index 000000000..99050227d --- /dev/null +++ b/13_Bounce/rust/src/main.rs @@ -0,0 +1,183 @@ +/** BOUNCE GAME + * https://github.com/coding-horror/basic-computer-games/blob/main/13_Bounce/bounce.bas + * Direct conversion from BASIC to Rust by Pablo Marques (marquesrs). + * No additional features or improvements were added. As a faithful translation, + * many of the code here are done in an unrecommended way by today's standards. + * 03/03/25 +*/ + +use std::io::Write; + +fn input(msg: &str) -> String { + print!("{}", msg); + let _ =std::io::stdout().flush().unwrap(); + let mut input = String::new(); + std::io::stdin().read_line(&mut input).unwrap(); + return input.trim().to_uppercase(); +} + +fn main() { + //10 PRINT TAB(33);"BOUNCE" + //20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" + //30 PRINT:PRINT:PRINT + print!("{}{}\n{}{}\n\n\n", + " ".repeat(33), + "BOUNCE", + " ".repeat(15), + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" + ); + + //90 DIM T(20) + let mut t: Vec = Vec::with_capacity(20); + + //100 PRINT "THIS SIMULATION LETS YOU SPECIFY THE INITIAL VELOCITY" + //110 PRINT "OF A BALL THROWN STRAIGHT UP, AND THE COEFFICIENT OF" + //120 PRINT "ELASTICITY OF THE BALL. PLEASE USE A DECIMAL FRACTION" + //130 PRINT "COEFFICIENCY (LESS THAN 1)." + //131 PRINT + //132 PRINT "YOU ALSO SPECIFY THE TIME INCREMENT TO BE USED IN" + //133 PRINT "'STROBING' THE BALL'S FLIGHT (TRY .1 INITIALLY)." + //134 PRINT + print!("{}\n{}\n{}\n{}\n\n{}\n{}\n\n", + "THIS SIMULATION LETS YOU SPECIFY THE INITIAL VELOCITY", + "OF A BALL THROWN STRAIGHT UP, AND THE COEFFICIENT OF", + "ELASTICITY OF THE BALL. PLEASE USE A DECIMAL FRACTION", + "COEFFICIENCY (LESS THAN 1).", + "YOU ALSO SPECIFY THE TIME INCREMENT TO BE USED IN", + "'STROBING' THE BALL'S FLIGHT (TRY .1 INITIALLY).", + ); + + loop { + //135 INPUT "TIME INCREMENT (SEC)";S2 + let s2 = input("TIME INCREMENT (SEC): ").parse::().unwrap(); + //let s2 = 0.2f32; + + //140 PRINT + println!(); + + //150 INPUT "VELOCITY (FPS)";V + let v = input("VELOCITY (FPS): ").parse::().unwrap(); + //let v = 20.0f32; + + //160 PRINT + println!(); + + //170 INPUT "COEFFICIENT";C + let c = input("COEFFICIENT: ").parse::().unwrap(); + //let c = 0.6f32; + + //180 PRINT + //182 PRINT "FEET" + //184 PRINT + print!("\nFEET\n\n"); + + //186 S1=INT(70/(V/(16*S2))) // verified + let s1 = (70.0 / (v/(16.0*s2))) as i32; + + //190 FOR I=1 TO S1 + for i in 1..=s1 { + //200 T(I)=V*C^(I-1)/16 + t.push(v * c.powf(i as f32 - 1.0) / 16.0); // verified + //210 NEXT I + } + + let mut l = 0.0; + + //220 FOR H=INT(-16*(V/32)^2+V^2/32+.5) TO 0 STEP -.5 + let mut h = (-16.0 * (v / 32.0).powi(2) + (v.powi(2)) / 32.0 + 0.5).floor(); + while h >= 0.0 { + let mut line_content = String::new(); + //221 IF INT(H)<>H THEN 225 + if h.floor() == h { + //222 PRINT H; + line_content.push_str(h.to_string().as_str()); + line_content.push(' '); + } + //225 L=0 + l = 0.0; + //230 FOR I=1 TO S1 + for i in 1..=s1 { + let mut t_val = 0.0; + //240 FOR T=0 TO T(I) STEP S2 + while t_val <= t[(i - 1) as usize] { + //245 L=L+S2 + l = l + s2; + + //250 IF ABS(H-(.5*(-32)*T^2+V*C^(I-1)*T))>.25 THEN 270 + let condition = h - (0.5 * (-32.0) * t_val.powf(2.0) + v * c.powf((i-1) as f32) * t_val); + if condition.abs() >= 0.25{ + t_val = t_val + s2; + continue; + } + // TABS ARE NOT SPACES, BUT A TERMINAL POSITION + //260 PRINT TAB(L/S2);"0"; + let spaces = ((l / s2) - 1.0) as usize; + while line_content.len() < spaces { + line_content.push(' '); + } + line_content.push('0'); + + //270 NEXT T + t_val = t_val + s2; + } + + //275 T=T(I+1)/2 + if i as usize == t.len() { break; } + t_val = t[i as usize] / 2.0; + + //276 IF -16*T^2+V*C^(I-1)*T)); +if ($Answer eq "Y") +{ + print "THE GAME OF BOWLING TAKES MIND AND SKILL. DURING THE GAME\n"; + print "THE COMPUTER WILL KEEP SCORE.YOU MAY COMPETE WITH\n"; + print "OTHER PLAYERS[UP TO FOUR]. YOU WILL BE PLAYING TEN FRAMES\n"; + print "ON THE PIN DIAGRAM 'O' MEANS THE PIN IS DOWN...'+' MEANS THE\n"; + print "PIN IS STANDING. AFTER THE GAME THE COMPUTER WILL SHOW YOUR SCORES.\n"; +} + +do { + print "FIRST OF ALL...HOW MANY ARE PLAYING (1-4): "; + $Num_players = int(<>); +} while ($Num_players < 1 || $Num_players > 4); + +print "\nVERY GOOD...\n"; + +while (1) +{ + # reset all scores + for my $p (1 .. $Num_players) # players + { + for my $f (1 .. 10) # frames + { + for my $b (1 .. 3) # balls + { + $Scores[$p][$f][$b] = 0; + } + } + } + + # play the game + for my $frame (1 .. 10) # frame + { + for my $curr_player (1 .. $Num_players) # player + { + my $last_pins=0; # pins down for last ball + my $ball=1; # ball number, 1 or 2 + my $end_result=0; # result at end of turn: 3=strike, 2=spare, 1=pins-left + for my $i (1 .. 15) { $C[$i] = 0 } + + while (1) # another ball + { + # ARK BALL GENERATOR USING MOD '15' SYSTEM + my $K=0; + my $curr_pins=0; # pins down for this ball + for my $i (1 .. 20) + { + my $x = int(rand(1) * 100); + my $j; + for ($j=1 ; $j <= 10 ; $j++) + { + last if ($x < 15 * $j); + } + $C[15 * $j - $x] = 1; + } + + # ARK PIN DIAGRAM + print "PLAYER: $curr_player FRAME: $frame BALL: $ball\n"; + print "PRESS ENTER TO GET THE BALL GOING."; + $Answer = <>; # not used, just need an enter + for my $i (0 .. 3) + { + print "\n"; + print " " x $i; # avoid the TAB(), just shift each row over for the triangle + for my $j (1 .. 4 - $i) + { + $K++; + print ($C[$K] == 1 ? " $char_down" : " $char_up"); + } + } + print "\n"; + + # ARK ROLL ANALYSIS + for my $i (1 .. 10) + { + $curr_pins += $C[$i]; + } + if ($curr_pins - $last_pins == 0) + { + print "GUTTER!!\n"; + } + if ($ball == 1 && $curr_pins == 10) + { + print "STRIKE!!!!!\a\a\a\a\n"; # \a is for bell + $end_result = 3; + } + elsif ($ball == 2 && $curr_pins == 10) + { + print "SPARE!!!!\n"; + $end_result = 2; + } + elsif ($ball == 2 && $curr_pins < 10) + { + if ($Advanced) { print 10 - $curr_pins, " PENS LEFT!!!\n"; } + else { print "ERROR!!!\n"; } + $end_result = 1; + } + if ($ball == 1 && $curr_pins < 10) + { + print "ROLL YOUR 2ND BALL\n"; + } + print "\n"; + + # ARK STORAGE OF THE SCORES + if ($Advanced) { $Scores[$curr_player][$frame][$ball] = $curr_pins - $last_pins; } + else { $Scores[$curr_player][$frame][$ball] = $curr_pins; } + if ($ball == 1) + { + $ball = 2; + $last_pins = $curr_pins; + + if ($end_result == 3) # strike, no more rolls, goto last + { + $Scores[$curr_player][$frame][$ball] = $curr_pins; + } + else + { + $Scores[$curr_player][$frame][$ball] = $curr_pins - $last_pins; + next if ($end_result == 0); # next roll + } + } + last; + } + $Scores[$curr_player][$frame][3] = $end_result; + } # next player + if ($Advanced) + { + print "Scores:\n"; + for my $p (1 .. $Num_players) + { + my $total = calc_score($p); + print "\tPlayer $p: $total\n"; + } + print "\n"; + } + } # next frame + + # end of game, show full scoreboard + show_scoreboard(); + + print "DO YOU WANT ANOTHER GAME (Y/N): "; + chomp($Answer = uc(<>)); + print "\n"; + last if ($Answer ne "Y"); +} +exit(0); + +sub show_scoreboard +{ + print "FRAMES\n"; + for my $i (1 .. 10) + { + print " $i "; + } + print "\n"; + my @results = ( "-", ".", "/", "X" ); + for my $p (1 .. $Num_players) + { + print "Player $p\n" if ($Advanced); + my $ball_max = ($Advanced ? 4 : 3); + for my $b (1 .. $ball_max) + { + for my $f (1 .. 10) + { + if ($b != 3) { print sprintf("%2d ", $Scores[$p][$f][$b]); } + else { print sprintf("%2s ", $results[$Scores[$p][$f][$b]]); } + } + print "\n"; + } + print "\n"; + } +} + +sub calc_score +{ + my $player = shift; + my $total = 0; + for my $frame (1 .. 10) + { + my $score = 0; + if ($frame == 10 || $Scores[$player][$frame][3] == 1) # pins + { + $score = $Scores[$player][$frame][1] + $Scores[$player][$frame][2]; + } + elsif ($Scores[$player][$frame][3] == 2) # spare + { + $score = 10 + $Scores[$player][$frame+1][1]; + } + elsif ($Scores[$player][$frame][3] == 3) # strike + { + $score = 10 + $Scores[$player][$frame+1][1]; + if ($Scores[$player][$frame+1][1] == 10) + { + $score += ($frame < 9 ? $Scores[$player][$frame+2][1] : $Scores[$player][$frame+1][2]); + } + else + { + $score += $Scores[$player][$frame+1][2]; + } + } + $Scores[$player][$frame][4] = $score; + $total += $score; + } + return $total; +} diff --git a/14_Bowling/python/bowling.py b/14_Bowling/python/bowling.py index c7773978d..fc923ddbc 100644 --- a/14_Bowling/python/bowling.py +++ b/14_Bowling/python/bowling.py @@ -57,10 +57,9 @@ def play_frame(self, frame: int) -> None: break # cannot roll more than once in a frame else: print(f"next roll {self.name}") - else: - if score == 10: - print("SPARE!") - extra = 1 + elif score == 10: + print("SPARE!") + extra = 1 prev_score = score # remember previous pins to distinguish ... if frame == 9 and extra > 0: @@ -112,12 +111,12 @@ def main() -> None: print("SCORES.") total_players = int(input("FIRST OF ALL...HOW MANY ARE PLAYING? ")) - player_names = [] print() print("VERY GOOD...") - for index in range(total_players): - player_names.append(Player(input(f"Enter name for player {index + 1}: "))) - + player_names = [ + Player(input(f"Enter name for player {index + 1}: ")) + for index in range(total_players) + ] for frame in range(10): for player in player_names: player.play_frame(frame) diff --git a/14_Bowling/rust/Cargo.lock b/14_Bowling/rust/Cargo.lock new file mode 100644 index 000000000..54f98ac40 --- /dev/null +++ b/14_Bowling/rust/Cargo.lock @@ -0,0 +1,77 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bowling" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chacha20" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" +dependencies = [ + "cfg-if", + "cpufeatures", + "rand_core", +] + +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "rand_core", +] + +[[package]] +name = "libc" +version = "0.2.189" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" +dependencies = [ + "chacha20", + "getrandom", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" diff --git a/14_Bowling/rust/Cargo.toml b/14_Bowling/rust/Cargo.toml new file mode 100644 index 000000000..159e7f1f9 --- /dev/null +++ b/14_Bowling/rust/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "bowling" +version = "0.1.0" +edition = "2024" + +[dependencies] +rand = "0.10.0" diff --git a/14_Bowling/rust/src/main.rs b/14_Bowling/rust/src/main.rs new file mode 100644 index 000000000..0520cfca1 --- /dev/null +++ b/14_Bowling/rust/src/main.rs @@ -0,0 +1,213 @@ +//! Rust port of "Bowl" (14_Bowling) from _Basic Computer Games_ (1978). +use rand::{rngs::ThreadRng, RngExt}; +use std::io::{self, BufRead, Write}; + +/// Number of cells in the pin-accumulator array (`DIM C(15)` in BASIC). +const CELLS: usize = 15; +/// The first ten cells correspond to the ten real pins drawn on the diagram. +const PINS: usize = 10; + +fn main() { + println!("{:>34}BOWL", ""); + println!("{:>15}CREATIVE COMPUTING MORRISTOWN, NEW JERSEY", ""); + println!("\n\n"); + + println!("WELCOME TO THE ALLEY"); + println!("BRING YOUR FRIENDS"); + println!("OKAY LET'S FIRST GET ACUUAINTED"); + println!(); + + let mut game = BowlingGame::new(); + loop { + let answer = prompt_line("THE INSTRUCTIONS (Y/N)"); + if answer.starts_with('Y') { + print_instructions(); + } else if answer.starts_with('N') { + // fall through to the game + } else { + continue; + } + + game.play(); + + let again = prompt_line("DO YOU WANT ANOTHER GAME"); + if !again.starts_with('Y') { + break; + } + } +} + +fn print_instructions() { + println!("THE GAME OF BOWLING TAKES MIND AND SKILL.DURING THE GAME"); + println!("THE COMPUTER WILL KEEP SCORE.YOU MAY COMPETE WITH"); + println!("OTHER PLAYERS[UP TO FOUR].YOU WILL BE PLAYING TEN FRAMES"); + println!("ON THE PIN DIAGRAM 'O' MEANS THE PIN IS DOWN...'+' MEANS THE"); + println!("PIN IS STANDING.AFTER THE GAME THE COMPUTER WILL SHOW YOUR"); + println!("SCORES ."); +} + +struct BowlingGame { + rng: ThreadRng, +} + +impl BowlingGame { + fn new() -> Self { + Self { rng: rand::rng() } + } + + /// Play one complete game (10 frames, all players), then print the scorecard. + fn play(&mut self) { + let players = loop { + // "FIRST OF ALL...HOW MANY ARE PLAYING" + let line = prompt_line("FIRST OF ALL...HOW MANY ARE PLAYING"); + if line.is_empty() { + // EOF on input — bail out of this game. + return; + } + if let Ok(n) = line.trim().parse::() { + if (1..=4).contains(&n) { + break n; + } + } + println!("?RE-ENTER"); + }; + + println!(); + println!("VERY GOOD..."); + + // One row per player: [ball1, ball2, status] for each of 10 frames. + let mut scores: Vec<[[u32; 3]; 10]> = (0..players).map(|_| [[0; 3]; 10]).collect(); + + for frame in 1..=10 { + for (player_idx, player_scores) in scores.iter_mut().enumerate() { + self.play_ball(frame, player_idx + 1, player_scores); + } + } + + print_scorecard(&scores); + } + + /// Roll the balls for one player in one frame, recording the result. + fn play_ball(&mut self, frame: usize, player: usize, scores: &mut [[u32; 3]; 10]) { + let mut prev_down = 0; // M: pins down after the previous ball this frame + let mut status = 0; // Q: 3 strike, 2 spare, 1 error, 0 otherwise + + let mut cells = [0u32; CELLS + 1]; + + for ball in 1..=2 { + // "TYPE ROLL TO GET THE BALL GOING." + let _ = prompt_line("TYPE ROLL TO GET THE BALL GOING"); + + // Knock down more pins: 20 random hits, each landing in cell + // (15*J - X) where J is the smallest bucket with X < 15*J. + self.roll_pins(&mut cells); + + // Draw the triangle diagram (only the first 10 cells are real pins). + print!("PLAYER:{} FRAME:{} BALL:{}\n", player, frame, ball); + print_pin_diagram(&cells); + + // Count pins down among the real pins (cells 1..=10). + let down = cells[1..=PINS].iter().filter(|&&c| c == 1).count() as u32; + + // Roll analysis. Order mirrors the original IF chain. + if down == prev_down { + println!("GUTTER!!"); + } + if ball == 1 && down == 10 { + println!("STRIKE!!!!!\x07\x07\x07\x07"); + status = 3; + } + if ball == 2 && down == 10 { + println!("SPARE!!!!"); + status = 2; + } + if ball == 2 && down < 10 { + println!("ERROR!!!"); + status = 1; + } + if ball == 1 && down < 10 { + println!("ROLL YOUR 2ND BALL"); + } + println!(); + + // Record the score for this ball: total pins down so far this frame. + scores[frame - 1][ball - 1] = down; + + if ball == 1 { + prev_down = down; + // On a strike, the frame ends; otherwise prompt the second ball. + if status == 3 { + scores[frame - 1][1] = 0; + break; + } + } + } + + scores[frame - 1][2] = status; + } + + fn roll_pins(&mut self, cells: &mut [u32]) { + for _ in 0..20 { + let x = (self.rng.random::() * 100.0) as usize; // INT(RND(1)*100) + // Find the smallest J in 1..=10 with x < 15*J. + let mut j = 1; + while j <= 10 && x >= 15 * j { + j += 1; + } + // j may exceed 10 if x >= 150 (impossible here since x < 100) — guard anyway. + if j <= 10 { + let idx = 15 * j - x; // 1..=15 + if idx >= 1 && idx <= CELLS { + cells[idx] = 1; + } + } + } + } +} + +/// Draw the standing/down pins as a triangle: '+' standing, 'O' down. +fn print_pin_diagram(cells: &[u32]) { + let mut k = 1; // pin index, 1-based (K in BASIC) + for i in 0..4 { + print!("{}", " ".repeat(i)); + for _ in 0..(4 - i) { + if cells[k] == 1 { + print!("O "); + } else { + print!("+ "); + } + k += 1; + } + println!(); + } + println!(); +} + +fn print_scorecard(scores: &[[[u32; 3]; 10]]) { + println!("FRAMES"); + for frame in 1..=10 { + print!("{} ", frame); + } + println!(); + for player_scores in scores { + for row in 0..3 { + for frame in 0..10 { + print!("{} ", player_scores[frame][row]); + } + println!(); + } + println!(); + } +} + +fn prompt_line(prompt: &str) -> String { + print!("{}? ", prompt); + let _ = io::stdout().lock().flush(); + + let mut buffer = String::new(); + // Treat EOF (or a read error) as an empty line so callers can decide what + // to do — for this game, an empty answer never matches Y/N, so the program + // simply exits its current loop instead of spinning forever on EOF. + let _ = io::stdin().lock().read_line(&mut buffer); + buffer.trim().to_uppercase() +} diff --git a/15_Boxing/README.md b/15_Boxing/README.md index 12ecab4f1..f0e6692dd 100644 --- a/15_Boxing/README.md +++ b/15_Boxing/README.md @@ -2,7 +2,7 @@ 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. +There are approximately seven major punches per round, although this may be varied. The best out of three rounds wins. Jesse Lynch of St. Paul, Minnesota created this program. @@ -15,6 +15,14 @@ As published in Basic Computer Games (1978): Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html +#### Known Bugs + +- The code that handles player punch type 1 checks for opponent weakness type 4; this is almost certainly a mistake. + +- Line breaks or finishing messages are omitted in various cases. For example, if the player does a hook, and that's the opponent's weakness, then 7 points are silently awarded without outputting any description or line break, and the next sub-round will begin on the same line. + +- When the opponent selects a hook, control flow falls through to the uppercut case. Perhaps related, a player weakness of type 2 (hook) never has any effect on the game. + #### Porting Notes (please note any difficulties or challenges in porting here) diff --git a/15_Boxing/perl/boxing.pl b/15_Boxing/perl/boxing.pl new file mode 100644 index 000000000..5068e58a4 --- /dev/null +++ b/15_Boxing/perl/boxing.pl @@ -0,0 +1,252 @@ +#!/usr/bin/perl + +# Boxing program in Perl +# Required extensive restructuring to remove all of the GOTO's. +# Translated by Kevin Brannen (kbrannen) + +use strict; +use warnings; + +# globals +my $Opp_won = 0; # num rounds opponent has won +my $You_won = 0; # num rounds you have won +my $Opp_name = ""; # opponent name +my $Your_name = ""; # your name +my $Your_best = 0; # your best punch +my $Your_worst = 0; # your worst punch +my $Opp_best; # opponent best punch +my $Opp_worst; # opponent worst punch +my $Opp_damage; # opponent damage ? +my $Your_damage; # your damage ? + +sub get_punch +{ + my $prompt = shift; + my $p; + while (1) + { + print "$prompt: "; + chomp($p = int(<>)); + last if ($p >= 1 && $p <= 4); + print "DIFFERENT PUNCHES ARE: (1) FULL SWING; (2) HOOK; (3) UPPERCUT; (4) JAB.\n"; + } + return $p; +} + +print "\n"; +print " " x 33, "BOXING\n"; +print " " x 15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n"; + +print "BOXING OLYMPIC STYLE (3 ROUNDS -- 2 OUT OF 3 WINS)\n\n"; +print "WHAT IS YOUR OPPONENT'S NAME: "; +chomp($Opp_name = <>); +print "INPUT YOUR MAN'S NAME: "; +chomp($Your_name = <>); +print "DIFFERENT PUNCHES ARE: (1) FULL SWING; (2) HOOK; (3) UPPERCUT; (4) JAB.\n"; +$Your_best = get_punch("WHAT IS YOUR MANS BEST"); +$Your_worst = get_punch("WHAT IS HIS VULNERABILITY"); + +do { + $Opp_best = int(4*rand(1)+1); + $Opp_worst = int(4*rand(1)+1); +} while ($Opp_best == $Opp_worst); +print "$Opp_name\'S ADVANTAGE IS $Opp_best AND VULNERABILITY IS SECRET.\n\n"; + +for my $R (1 .. 3) # rounds +{ + last if ($Opp_won >= 2 || $You_won >= 2); + $Opp_damage = 0; + $Your_damage = 0; + print "ROUND $R BEGINS...\n"; + for my $R1 (1 .. 7) # 7 events per round? + { + if (int(10*rand(1)+1) <= 5) + { + my $your_punch = get_punch("$Your_name\'S PUNCH"); + $Opp_damage += 2 if ($your_punch == $Your_best); + + if ($your_punch == 1) { punch1(); } + elsif ($your_punch == 2) { punch2(); } + elsif ($your_punch == 3) { punch3(); } + else { punch4(); } + next; + } + + my $Opp_punch = int(4*rand(1)+1); + $Your_damage += 2 if ($Opp_punch == $Opp_best); + + if ($Opp_punch == 1) { opp1(); } + elsif ($Opp_punch == 2) { opp2(); } + elsif ($Opp_punch == 3) { opp3(); } + else { opp4(); } + } + + if ($Opp_damage > $Your_damage) + { + print "\n$Your_name WINS ROUND $R\n\n"; + $You_won++; + } + else + { + print "\n$Opp_name WINS ROUND $R\n\n"; + $Opp_won++; + } +} + +if ($Opp_won >= 2) +{ + done("$Opp_name WINS (NICE GOING, $Opp_name)."); +} + +#else # if ($You_won >= 2) +done("$Your_name AMAZINGLY WINS!!"); + +################################################### + +sub done +{ + my $msg = shift; + print $msg; + print "\n\nAND NOW GOODBYE FROM THE OLYMPIC ARENA.\n\n"; + exit(0); +} + +sub punch1 +{ + # $your_punch == 1, full swing + print "$Your_name SWINGS AND "; + if ($Opp_worst == 4 || int(30*rand(1)+1) < 10) + { + print "HE CONNECTS!\n"; + if ($Opp_damage > 35) + { + done("$Opp_name IS KNOCKED COLD AND $Your_name IS THE WINNER AND CHAMP! "); + } + $Opp_damage += 15; + } + else + { + print "HE MISSES\n"; + print "\n\n" if ($Opp_damage != 1); + } +} + +sub punch2 +{ + # $your_punch == 2, hook + print "$Your_name GIVES THE HOOK... "; + if ($Opp_worst == 2) + { + $Opp_damage += 7; + return; + } + if (int(2*rand(1)+1) == 1) + { + print "BUT IT'S BLOCKED!!!!!!!!!!!!!\n"; + } + else + { + print "CONNECTS...\n"; + $Opp_damage += 7; + } +} + +sub punch3 +{ + # $your_punch == 3, uppercut + print "$Your_name TRIES AN UPPERCUT "; + if ($Opp_worst == 3 || int(100*rand(1)+1) < 51) + { + print "AND HE CONNECTS!\n"; + $Opp_damage += 4; + } + else + { + print "AND IT'S BLOCKED (LUCKY BLOCK!)\n"; + } +} + +sub punch4 +{ + # $your_punch == 4, jab + print "$Your_name JABS AT $Opp_name\'S HEAD "; + if ($Opp_worst == 4 || (int(8*rand(1)+1)) >= 4) + { + $Opp_damage += 3; + print "\n"; + } + else + { + print "IT'S BLOCKED.\n"; + } +} + +sub opp1 +{ + # opp_punch == 1 + print "$Opp_name TAKES A FULL SWING AND "; + if ($Your_worst == 1 || int(60*rand(1)+1) < 30) + { + print " POW!!!!! HE HITS HIM RIGHT IN THE FACE!\n"; + if ($Your_damage > 35) + { + done("$Your_name IS KNOCKED COLD AND $Opp_name IS THE WINNER AND CHAMP!"); + } + $Your_damage += 15; + } + else + { + print " IT'S BLOCKED!\n"; + } +} + +sub opp2 +{ + # opp_punch == 2 + print "$Opp_name GETS $Your_name IN THE JAW (OUCH!)\n"; + $Your_damage += 7; + print "....AND AGAIN!\n"; + $Your_damage += 5; + if ($Your_damage > 35) + { + done("$Your_name IS KNOCKED COLD AND $Opp_name IS THE WINNER AND CHAMP!"); + } + print "\n"; + # 2 continues into opp_punch == 3 + opp3(); +} + +sub opp3() +{ + # opp_punch == 3 + print "$Your_name IS ATTACKED BY AN UPPERCUT (OH,OH)...\n"; + if ($Your_worst != 3 && int(200*rand(1)+1) > 75) + { + print " BLOCKS AND HITS $Opp_name WITH A HOOK.\n"; + $Opp_damage += 5; + } + else + { + print "AND $Opp_name CONNECTS...\n"; + $Your_damage += 8; + } +} + +sub opp4 +{ + # opp_punch == 4 + print "$Opp_name JABS AND "; + if ($Your_worst == 4) + { + $Your_damage += 5; + } + elsif (int(7*rand(1)+1) > 4) + { + print " BLOOD SPILLS !!!\n"; + $Your_damage += 5; + } + else + { + print "IT'S BLOCKED!\n"; + } +} diff --git a/15_Boxing/python/boxing.py b/15_Boxing/python/boxing.py index 1943e74ec..907f645c7 100755 --- a/15_Boxing/python/boxing.py +++ b/15_Boxing/python/boxing.py @@ -3,7 +3,7 @@ import random from dataclasses import dataclass from pathlib import Path -from typing import Dict, Literal, NamedTuple, Tuple +from typing import Dict, Literal, NamedTuple, Tuple, cast class PunchProfile(NamedTuple): @@ -39,12 +39,11 @@ class Player: def get_punch_choice(self) -> Literal[1, 2, 3, 4]: if self.is_computer: return random.randint(1, 4) # type: ignore - else: - punch = -1 - while punch not in [1, 2, 3, 4]: - print(f"{self.name}'S PUNCH", end="? ") - punch = int(input()) - return punch # type: ignore + punch = -1 + while punch not in [1, 2, 3, 4]: + print(f"{self.name}'S PUNCH", end="? ") + punch = int(input()) + return punch # type: ignore KNOCKOUT_THRESHOLD = 35 @@ -55,8 +54,7 @@ def get_punch_choice(self) -> Literal[1, 2, 3, 4]: def get_vulnerability() -> int: print("WHAT IS HIS VULNERABILITY", end=QUESTION_PROMPT) - vulnerability = int(input()) - return vulnerability + return int(input()) def get_opponent_stats() -> Tuple[int, int]: @@ -71,10 +69,10 @@ def get_opponent_stats() -> Tuple[int, int]: def read_punch_profiles(filepath: Path) -> Dict[Literal[1, 2, 3, 4], PunchProfile]: with open(filepath) as f: punch_profile_dict = json.load(f) - result = {} - for key, value in punch_profile_dict.items(): - result[int(key)] = PunchProfile(**value) - return result # type: ignore + return { + cast(Literal[1, 2, 3, 4], int(key)): PunchProfile(**value) + for key, value in punch_profile_dict.items() + } def main() -> None: diff --git a/17_Bullfight/README.md b/17_Bullfight/README.md index c801b16e5..b81aab0b7 100644 --- a/17_Bullfight/README.md +++ b/17_Bullfight/README.md @@ -26,6 +26,6 @@ http://www.vintage-basic.net/games.html #### Porting Notes -(please note any difficulties or challenges in porting here) +- There is a fundamental assumption in the pre-fight subroutine at line 1610, that the Picadores and Toreadores are more likely to do a bad job (and possibly get killed) with a low-quality bull. This appears to be a mistake in the original code, but should be retained. -- There is a fundamental assumption in the pre-fight subroutine at line 1610, that the Picadores and Toreadores are more likely to do a bad job (and possibly get killed) with a low-quality bull. This appears to be a mistake in the original code, but should be retained. \ No newline at end of file +- Lines 1800-1820 (part of the pre-fight subroutine) can never be reached. diff --git a/17_Bullfight/java/Bullfight.java b/17_Bullfight/java/Bullfight.java new file mode 100644 index 000000000..16fe299c0 --- /dev/null +++ b/17_Bullfight/java/Bullfight.java @@ -0,0 +1,466 @@ +import java.util.Random; +import java.util.Scanner; + +/** + * BULLFIGHT + *

+ * Converted from BASIC to Java by Aldrin Misquitta (@aldrinm) + */ +public class Horserace { + + private static final String[] horseNames = { + "JOE MAW", + "L.B.J.", + "MR.WASHBURN", + "MISS KAREN", + "JOLLY", + "HORSE", + "JELLY DO NOT", + "MIDNIGHT" + }; + public static final int MAX_DISTANCE = 28; + public static final int NUM_HORSES = 8; + + public static void main(String[] args) { + printHeader(); + + Scanner scanner = new Scanner(System.in); + Random random = new Random(); + + printHelp(scanner); + + List betNames = readBetNames(scanner); + + boolean donePlaying = false; + while (!donePlaying) { + + int[] odds = generateOdds(random); + int sumOdds = Arrays.stream(odds).sum(); + printOdds(sumOdds, odds); + + Map bets = takeBets(scanner, betNames); + + var horsePositions = runRace(horseNames.length, sumOdds, odds, random); + + printRaceResults(horsePositions, bets, sumOdds, odds); + + donePlaying = readDonePlaying(scanner); + } + } + + private static int[] generateOdds(Random random) { + int[] odds = new int[NUM_HORSES]; + for (int i = 0; i < NUM_HORSES; i++) { + odds[i] = (int) (10 * random.nextFloat() + 1); + } + return odds; + } + + private static void printOdds(int R, int[] D) { + System.out.printf("%n%-28s%-14s%-14s%n%n", "HORSE", "NUMBER", "ODDS"); + for (int n = 0; n < horseNames.length; n++) { + System.out.printf("%-28s% -14d%.6f :1%n", horseNames[n], n + 1, ((float) R / D[n])); + } + } + + private static boolean readDonePlaying(Scanner scan) { + System.out.println("DO YOU WANT TO BET ON THE NEXT RACE ?"); + System.out.print("YES OR NO? "); + String choice = scan.nextLine(); + return !choice.equalsIgnoreCase("YES"); + } + + /** + * Simulate the race run, returning the final positions of the horses. + */ + private static int[] runRace(int numberOfHorses, int sumOdds, int[] odds, Random random) { + int[] positionChange = new int[numberOfHorses]; + + System.out.println(); + System.out.println("1 2 3 4 5 6 7 8"); + + int totalDistance = 0; + int[] currentPositions = new int[NUM_HORSES]; + int[] horsePositions = new int[NUM_HORSES]; + + while (totalDistance < MAX_DISTANCE) { + System.out.println("XXXXSTARTXXXX"); + + for (int i = 0; i < numberOfHorses; i++) { + horsePositions[i] = i + 1; + positionChange[i] = calculatePositionChanges(sumOdds, odds[i], random); + currentPositions[i] += positionChange[i]; + } + + sortHorsePositionsBasedOnCurrent(currentPositions, horsePositions); + + totalDistance = currentPositions[horsePositions[7] - 1]; + + boolean raceFinished = false; + int i = 0; + while (i < NUM_HORSES && !raceFinished) { + int distanceToNextHorse = currentPositions[(horsePositions[i] - 1)] - (i < 1 ? 0 : currentPositions[(horsePositions[i - 1] - 1)]); + if (distanceToNextHorse != 0) { + int a = 0; + while (a < distanceToNextHorse && !raceFinished) { + System.out.println(); + if (currentPositions[horsePositions[i] - 1] >= MAX_DISTANCE) { + raceFinished = true; + } + a++; + } + } + + if (!raceFinished) { + System.out.print(" " + horsePositions[i] + " "); // Print horse number + } + i++; + } + + if (!raceFinished) { + //Print additional empty lines + for (int a = 0; a < MAX_DISTANCE - totalDistance; a++) { + System.out.println(); + } + } + + System.out.println("XXXXFINISHXXXX"); + System.out.println("\n"); + System.out.println("---------------------------------------------"); + System.out.println("\n"); + } + + return horsePositions; + } + + /** + * Sorts the horsePositions array in place, based on the currentPositions of the horses. + * (bubble sort) + */ + private static void sortHorsePositionsBasedOnCurrent(int[] currentPositions, int[] horsePositions) { + for (int l = 0; l < NUM_HORSES; l++) { + int i = 0; + /* + uses a do-while instead of a for loop here, because in BASIC + a FOR I=1 TO 0 causes at least one execution of the loop + */ + do { + if (currentPositions[horsePositions[i] - 1] >= currentPositions[horsePositions[i + 1] - 1]) { + int h = horsePositions[i]; + horsePositions[i] = horsePositions[i + 1]; + horsePositions[i + 1] = h; + } + i++; + } while (i < (7 - l)); + } + } + + private static int calculatePositionChanges(int r, int d, Random random) { + int positionChange = (int) (100 * random.nextFloat() + 1); + + if (positionChange < 10) { + positionChange = 1; + } else { + int s = (int) ((float) r / d + 0.5); + if (positionChange < (s + 17)) { + positionChange = 2; + } else if (positionChange < s + 37) { + positionChange = 3; + } else if (positionChange < s + 57) { + positionChange = 4; + } else if (positionChange < s + 77) { + positionChange = 5; + } else if (positionChange < s + 92) { + positionChange = 6; + } else { + positionChange = 7; + } + } + + return positionChange; + } + + private static void printRaceResults(int[] m, Map bets, int r, int[] d) { + System.out.println("THE RACE RESULTS ARE:"); + int z9 = 1; + for (int i = 7; i >= 0; i--) { + int f = m[i]; + System.out.println(); + System.out.println(z9 + " PLACE HORSE NO. " + f + " AT " + (r / d[f - 1]) + ":1"); + z9++; + } + bets.forEach((betName, bet) -> { + if (bet.horseNumber == m[7]) { + int n = bet.horseNumber; + System.out.println(); + System.out.printf("%s WINS $ %.2f %n", bet.betName, ((float) r / d[n]) * bet.amount); + } + }); + } + + private static Map takeBets(Scanner scanner, List betNames) { + Map bets = new HashMap<>(); + System.out.println("--------------------------------------------------"); + System.out.println("PLACE YOUR BETS...HORSE # THEN AMOUNT"); + for (String betName : betNames) { + boolean validInput = false; + while (!validInput) { + int horseNumber = readInt(betName, scanner);//Q in the original + double betAmount = readDouble("?", scanner); //P in the original + if (betAmount < 1 || betAmount > 100000) { + System.out.println(" YOU CAN'T DO THAT!"); + } else { + bets.put(betName, new Bet(betName, horseNumber, betAmount)); + validInput = true; + } + } + } + + return bets; + } + + private static void printHelp(Scanner scanner) { + System.out.print("DO YOU WANT DIRECTIONS"); + + String directions = readChoice(scanner); + + if (!directions.equalsIgnoreCase("NO")) { + System.out.println("UP TO 10 MAY PLAY. A TABLE OF ODDS WILL BE PRINTED. YOU"); + System.out.println("MAY BET ANY + AMOUNT UNDER 100000 ON ONE HORSE."); + System.out.println("DURING THE RACE, A HORSE WILL BE SHOWN BY ITS"); + System.out.println("NUMBER. THE HORSES RACE DOWN THE PAPER!"); + System.out.println(); + } + } + + private static String readChoice(Scanner scanner) { + System.out.print("? "); + return scanner.nextLine(); + } + + private static int readInt(String prompt, Scanner scanner) { + System.out.print(prompt); + while (true) { + System.out.print("? "); + String input = scanner.nextLine(); + try { + return Integer.parseInt(input); + } catch (NumberFormatException e) { + System.out.println("!NUMBER EXPECTED - RETRY INPUT LINE"); + } + } + } + + private static double readDouble(String prompt, Scanner scanner) { + System.out.print(prompt); + while (true) { + System.out.print("? "); + String input = scanner.nextLine(); + try { + return Double.parseDouble(input); + } catch (NumberFormatException e) { + System.out.println("!NUMBER EXPECTED - RETRY INPUT LINE"); + } + } + } + + private static List readBetNames(Scanner scanner) { + int c = readInt("HOW MANY WANT TO BET ", scanner); + System.out.println("WHEN ? APPEARS,TYPE NAME"); + List names = new ArrayList<>(); + for (int i = 1; i <= c; i++) { + System.out.print("? "); + names.add(scanner.nextLine()); + } + + return names; + } + + private static void printHeader() { + System.out.println(" HORSERACE"); + System.out.println(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n"); + System.out.println("\n\n"); + System.out.println("WELCOME TO SOUTH PORTLAND HIGH RACETRACK"); + System.out.println(" ...OWNED BY LAURIE CHEVALIER"); + } + + private static class Bet { + String betName; + int horseNumber; + double amount; + + public Bet(String betName, int horseNumber, double amount) { + this.betName = betName; + this.horseNumber = horseNumber; + this.amount = amount; + } + + @Override + public String toString() { + return "Bet{" + + "betName='" + betName + '\'' + + ", horseNumber=" + horseNumber + + ", amount=" + amount + + '}'; + } + } +} + diff --git a/50_Horserace/python/horserace.py b/50_Horserace/python/horserace.py index b00a63165..075c4573a 100644 --- a/50_Horserace/python/horserace.py +++ b/50_Horserace/python/horserace.py @@ -73,12 +73,8 @@ def setup_players() -> List[str]: # 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 + return [basic_input("") for _ in range(number_of_players)] def setup_horses() -> List[float]: diff --git a/50_Horserace/rust/Cargo.lock b/50_Horserace/rust/Cargo.lock new file mode 100644 index 000000000..4fe2abbea --- /dev/null +++ b/50_Horserace/rust/Cargo.lock @@ -0,0 +1,75 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/51_Hurkle/rust/Cargo.lock b/51_Hurkle/rust/Cargo.lock new file mode 100644 index 000000000..4fe2abbea --- /dev/null +++ b/51_Hurkle/rust/Cargo.lock @@ -0,0 +1,75 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/52_Kinema/rust/Cargo.toml b/52_Kinema/rust/Cargo.toml new file mode 100644 index 000000000..3a87396aa --- /dev/null +++ b/52_Kinema/rust/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "rust" +version = "0.1.0" +edition = "2021" + +[dependencies] +rand = "0.9.0" \ No newline at end of file diff --git a/52_Kinema/rust/src/main.rs b/52_Kinema/rust/src/main.rs new file mode 100644 index 000000000..9ce5c29ed --- /dev/null +++ b/52_Kinema/rust/src/main.rs @@ -0,0 +1,114 @@ +/** KINEMA BY RICHARD PAV + * https://github.com/coding-horror/basic-computer-games/blob/main/52_Kinema/kinema.bas + * Direct conversion from BASIC to Rust by Pablo Marques (marquesrs). + * As a faithful translation, many of the code here are done in an unrecommended way by + * today's standards. + * + * ATTENTION: The original code has mathematical imprecision and uses simplifications + * instead of the real formulation, which could lead to incorrect results. I have solved + * this issue, but kept the old lines. To compile the original version, just uncomment the + * code with the OLD label and comment the lines with the NEW label. + * example: gravity is now 9.81 instead of 10; Inputs and outputs are now float not integers... + * + * FORMULATION + * A BALL IS THROWN UPWARDS AT 9,36 METERS PER SECOND. + * HOW HIGH WILL IT GO (IN METERS)? (9,36 ^2) / (2 * 9,81) = 4,465321101 + * HOW LONG UNTIL IT RETURNS (IN SECONDS)? 2*(9,36 / 9,81) = 1,908256881 + * WHAT WILL ITS VELOCITY BE AFTER 1,09 SECONDS? 9,36- 9,81 * 1,09 = −1,3329 + * + * 17/02/25 +*/ + +use std::io::Write; +use rand::Rng; + +fn subroutine(a: f64, q: &mut i32) { + std::io::stdout().flush().unwrap(); + //500 INPUT G + let mut input = String::new(); + let g; + loop { + std::io::stdin().read_line(&mut input).unwrap(); + match input.trim().parse::() { + Ok(e) => { g = e; break; }, + Err(_) => { print!("\nINVALID. TRY AGAIN: "); continue; }, + }; + } + //502 IF ABS((G-A)/A)<.15 THEN 510 + if f64::abs((g-a)/a) < 0.15 { + //510 PRINT "CLOSE ENOUGH." + print!("CLOSE ENOUGH."); + //511 Q=Q+1 + *q = *q + 1; + } + else { + //504 PRINT "NOT EVEN CLOSE...." + print!("NOT EVEN CLOSE..."); + //506 GOTO 512 + } + //512 PRINT "CORRECT ANSWER IS ";A + print!("\nCORRECT ANSWER IS {a:.2}\n"); + //520 PRINT + //530 RETURN +} + +fn main() { + let mut rng = rand::rng(); + + //10 PRINT TAB(33);"KINEMA" + //20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" + //30 PRINT: PRINT: PRINT + //100 PRINT + //105 PRINT + print!("{}KINEMA\n{}CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n\n", + " ".repeat(33), + " ".repeat(15) + ); + loop { + //106 Q=0 + let mut q = 0; + //110 V=5+INT(35*RND(1)) + let v: f64 = 5.0 + 35.0 * rng.random_range(0.0..1.0); + //111 PRINT "A BALL IS THROWN UPWARDS AT";V;"METERS PER SECOND." + //112 PRINT + print!("\nA BALL IS THROWN UPWARDS AT {v:.2} METERS PER SECOND.\n"); + //115 A=.05*V^2 + //let a = 0.05 * v.powf(2.0); // OLD + let mut a = v.powf(2.0) / (2.0 * 9.81); // NEW + //116 PRINT "HOW HIGH WILL IT GO (IN METERS)"; + print!("\nHOW HIGH WILL IT GO (IN METERS)? "); + + //117 GOSUB 500 + subroutine(a, &mut q); + + //120 A=V/5 + //a = v / 5.0; // OLD + a = 2.0 * v / 9.81; // NEW + //122 PRINT "HOW LONG UNTIL IT RETURNS (IN SECONDS)"; + print!("\nHOW LONG UNTIL IT RETURNS (IN SECONDS)? "); + //124 GOSUB 500 + subroutine(a, &mut q); + + //130 T=1+INT(2*V*RND(1))/10 + let t = 1.0 + (2.0 * v * rng.random_range(0.0..1.0) / 10.0); + //132 A=V-10*T + a = v + (-9.81 * t); + //134 PRINT "WHAT WILL ITS VELOCITY BE AFTER";T;"SECONDS"; + print!("\nWHAT WILL ITS VELOCITY BE AFTER {t:.2} SECONDS? "); + + //136 GOSUB 500 + subroutine(a, &mut q); + + //140 PRINT + //150 PRINT Q;"RIGHT OUT OF 3."; + print!("\n{q} RIGHT OUT OF 3.\n"); + //160 IF Q<2 THEN 100 + if q < 2 { + continue; + } + //170 PRINT " NOT BAD." + //print!(" NOT BAD."); + //180 GOTO 100 + } + //999 END +} diff --git a/53_King/README.md b/53_King/README.md index 14a66fe9a..83fb61c33 100644 --- a/53_King/README.md +++ b/53_King/README.md @@ -78,3 +78,18 @@ On basic line 1310 we see this: but it should probably be: 1310 IF J=0 THEN 1324 + +### Bug 5 + +On basic line 1390 the income from tourism is calculated: + +``` +1390 A=INT(A+Q) +1400 V1=INT(((B-P1)*22)+(RND(1)*500)) +1405 V2=INT((2000-D)*15) +1410 PRINT " YOU MADE";ABS(INT(V1-V2));"RALLODS FROM TOURIST TRADE." +``` + +It is very easily possible that `V2` is larger than `V1` e.g. if all of the land has been sold. In the original game this does not make a difference because of Bug 1 (see above). + +However, judging by how `V1` and `V2` are handled in the code, it looks like `V1` is the basic income from tourism and `V2` is a deduction for pollution. When `ABS(INT(V1-V2))` is used as earnings from tourism, the player actually _gets_ money for a large enough pollution. So a better solution would be to let `V1 - V2` cap out at 0, so once the pollution is large enough, there is no income from tourists anymore. diff --git a/53_King/king_variable_update.bas b/53_King/king_variable_update.bas index 7f88e1014..0ae19de45 100644 --- a/53_King/king_variable_update.bas +++ b/53_King/king_variable_update.bas @@ -8,7 +8,7 @@ 11 IF Z$="AGAIN" THEN 1960 12 PRINT:PRINT:PRINT 20 PRINT "CONGRATULATIONS! YOU'VE JUST BEEN ELECTED PREMIER OF SETATS" - 22 PRINT "DETINU, RALLODS SMALL COMMUNIST ISLAND 30 BY 70 MILES LONG. YOUR" + 22 PRINT "DETINU, A SMALL COMMUNIST ISLAND 30 BY 70 MILES LONG. YOUR" 24 PRINT "JOB IS TO DECIDE UPON THE CONTRY'S BUDGET AND DISTRIBUTE" 26 PRINT "MONEY TO YOUR COUNTRYMEN FROM THE COMMUNAL TREASURY." 28 PRINT "THE MONEY SYSTEM IS RALLODS, AND EACH PERSON NEEDS 100" @@ -66,7 +66,8 @@ 380 PLANTING_AREA=0 390 MONEY_SPENT_ON_POLLUTION_CONTROL=0 395 RALLODS=0 -399 GOTO 1000 +399 GOTO 600 + 400 RALLODS=INT(RALLODS-WELFARE) 410 PRINT "HOW MANY SQUARE MILES DO YOU WISH TO PLANT"; @@ -88,7 +89,7 @@ 490 MONEY_SPENT_ON_POLLUTION_CONTROL=0 495 RALLODS=0 -499 GOTO 1000 +499 GOTO 600 500 RALLODS=RALLODS-MONEY_SPENT_ON_PLANTING diff --git a/53_King/python/king.py b/53_King/python/king.py index 9c8d075c0..0bf85016c 100644 --- a/53_King/python/king.py +++ b/53_King/python/king.py @@ -61,7 +61,7 @@ def settled_people(self) -> int: return self.countrymen - self.population_change def sell_land(self, amount: int) -> None: - assert amount < self.farmland + assert amount <= self.farmland self.land -= amount self.rallods += self.land_buy_price * amount @@ -126,20 +126,22 @@ def handle_deaths( def handle_tourist_trade(self) -> None: V1 = int(self.settled_people * 22 + random() * 500) V2 = int((INITIAL_LAND - self.land) * 15) - tourist_trade_earnings = int(V1 - V2) + tourist_trade_earnings = 0 + if V1 > V2: + tourist_trade_earnings = V1 - V2 print(f" YOU MADE {tourist_trade_earnings} RALLODS FROM TOURIST TRADE.") if V2 != 0 and not (V1 - V2 >= self.tourism_earnings): - print(" DECREASE BECAUSE ") + print(" DECREASE BECAUSE ", end="") reason = randint(0, 10) if reason <= 2: print("FISH POPULATION HAS DWINDLED DUE TO WATER POLLUTION.") - if reason <= 4: + elif reason <= 4: print("AIR POLLUTION IS KILLING GAME BIRD POPULATION.") - if reason <= 6: + elif reason <= 6: print("MINERAL BATHS ARE BEING RUINED BY WATER POLLUTION.") - if reason <= 8: + elif reason <= 8: print("UNPLEASANT SMOG IS DISCOURAGING SUN BATHERS.") - if reason <= 10: + else: print("HOTELS ARE LOOKING SHABBY DUE TO SMOG GRIT.") # NOTE: The following two lines had a bug in the original game: @@ -177,7 +179,7 @@ def handle_foreign_workers( sm_sell_to_industry + (random() * 10) - (random() * 20) ) if self.foreign_workers <= 0: - foreign_workers_influx = foreign_workers_influx + 20 + foreign_workers_influx += 20 print(f"{foreign_workers_influx} WORKERS CAME TO THE COUNTRY AND") surplus_distributed = distributed_rallods / COST_OF_LIVING - self.countrymen @@ -194,7 +196,7 @@ def handle_foreign_workers( print("CAME TO ", end="") print("THE ISLAND") self.countrymen += population_change - self.foreign_workers += int(foreign_workers_influx) + self.foreign_workers += foreign_workers_influx def handle_too_many_deaths(self) -> None: print(f"\n\n\n{self.died_contrymen} COUNTRYMEN DIED IN ONE YEAR!!!!!") @@ -428,8 +430,9 @@ def main() -> None: if state.countrymen < 343: state.handle_third_died() elif ( - state.rallods / 100 - ) > 5 and state.died_contrymen - state.pollution_deaths >= 2: + state.rallods > 500 + and state.died_contrymen - state.pollution_deaths >= 2 + ): state.handle_money_mismanagement() if state.foreign_workers > state.countrymen: state.handle_too_many_foreigners() diff --git a/53_King/rust/Cargo.lock b/53_King/rust/Cargo.lock new file mode 100644 index 000000000..db44c2e7a --- /dev/null +++ b/53_King/rust/Cargo.lock @@ -0,0 +1,16 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "fastrand" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" + +[[package]] +name = "king" +version = "0.1.0" +dependencies = [ + "fastrand", +] diff --git a/53_King/rust/Cargo.toml b/53_King/rust/Cargo.toml new file mode 100644 index 000000000..c4f9c9132 --- /dev/null +++ b/53_King/rust/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "king" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +fastrand = "^2.0.0" diff --git a/53_King/rust/README.md b/53_King/rust/README.md new file mode 100644 index 000000000..529da33b7 --- /dev/null +++ b/53_King/rust/README.md @@ -0,0 +1,28 @@ +King +==== + +Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) + +Conversion to [rust](https://www.rust-lang.org/). + +Porting Notes +------------- + +### Floats + +The original code implicitly uses floating point numbers in many places which are explicitly cast to integers. In this port, I avoided using floats and tried to replicate the behaviour using just integers. It is possible that I missed some places where rounding a value would have made a difference. If you find such a bug, please notify me or make implement a fix yourself. + +### Signed Numbers + +I used unsigned integers for most of the program because it was easier than to check for negative values all the time. Unfortunately, that made the code a bit whacky in one or two places. + +Since I only allow input of positive numbers, it is not possible to exit the game when entering the stats to resume a game, which would be possible by entering negative numbers in the original game. + +### Bugs + +I tried to fix all bugs listed in the [main README for King](../README.md). I have tested this implementation a bit but not extensively, so there may be some portation bugs. If you find them, you are free to fix them. + +Future Development +------------------ + +I plan to add some tests and tidy up the code a bit, but this version should be feature-complete. diff --git a/53_King/rust/src/main.rs b/53_King/rust/src/main.rs new file mode 100644 index 000000000..90b2aac67 --- /dev/null +++ b/53_King/rust/src/main.rs @@ -0,0 +1,577 @@ +#![forbid(unsafe_code)] + +use fastrand::Rng; +use std::io; +use std::io::{stdin, stdout, BufRead, Write}; + +// global variable `N5` in the original game +const TERM_LENGTH: u32 = 8; + +fn main() { + let mut rng = Rng::new(); + let mut input = stdin().lock(); + let mut state = intro(&mut input, &mut rng).expect("input error"); + + loop { + let land_price = 95 + rng.u32(0..10); + let plant_price = 10 + rng.u32(0..5); + print_state(&state, land_price, plant_price); + state = match next_round(&mut input, &mut rng, &state, land_price, plant_price) + .expect("input error") + { + RoundEnd::Next(s) => s, + RoundEnd::GameOver(msg) => { + println!("{}", msg); + return; + } + } + } +} + +// The game is round based (one round per in-game year). +// This struct represents the state before each round +#[derive(Clone, PartialEq, Eq, Debug)] +struct State { + // global variable `A` in the original game (currency: "rallods") + money: u32, + // global variable `B` in the original game + countrymen: u32, + // global variable `C` in the original game + foreign_workers: u32, + // global variable `D` in the original game + land: u32, + // global variable `X5` in original game + year_in_office: u32, + // global variable `X` in original game + show_land_hint: bool, + // global variable `V3` in original game + previous_tourist_trade: u32, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +enum RoundEnd { + GameOver(String), + Next(State), +} + +fn init_state(rng: &mut Rng) -> State { + State { + // the original formula for random values used floating point numbers. + // e.g. `INT(60000+(1000*RND(1))-(1000*RND(1)))` + // I want to avoid floats unless necessary. These values generated here should be close + // enough to the original distribution + money: 60000 + rng.u32(0..1000) - rng.u32(0..1000), + countrymen: 500 + rng.u32(0..10) - rng.u32(0..10), + foreign_workers: 0, + land: 2000, + year_in_office: 0, + show_land_hint: true, + previous_tourist_trade: 0, + } +} + +fn print_state(state: &State, land_price: u32, plant_price: u32) { + print!( + r" +YOU NOW HAVE {} RALLODS IN THE TREASURY. + {} COUNTRYMEN, ", + state.money, state.countrymen + ); + if state.foreign_workers > 0 { + print!("{} FOREIGN WORKERS, ", state.foreign_workers) + } + println!( + r"AND {} SQ. MILES OF LAND. +THIS YEAR INDUSTRY WILL BUY LAND FOR {} RALLODS PER SQUARE MILE. +LAND CURRENTLY COSTS {} RALLODS PER SQUARE MILE TO PLANT. +", + state.land, land_price, plant_price + ); +} + +// print the intro, optional instructions or a previous savegame +fn intro(mut input: R, rng: &mut Rng) -> io::Result { + println!("⚠️ This game includes references to suicide or self-harm."); + println!(" KING"); + println!(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n"); + print!("DO YOU WANT INSTRUCTIONS?? "); + // In the original game, all inputs were made in the same line as the previous output. + // I try to replicate this behaviour here, but if I do not print a line break, the stdout buffer + // will not be flushed and the user may not see the input prompt before the input. + // this means in these cases I have to explicitly flush stdout. + stdout().flush()?; + let mut buf = String::with_capacity(16); + input.read_line(&mut buf)?; + buf.make_ascii_lowercase(); + match buf.trim() { + "n" => Ok(init_state(rng)), + "again" => { + let year_in_office = read_and_verify_int( + &mut input, + &mut buf, + "HOW MANY YEARS HAD YOU BEEN IN OFFICE WHEN INTERRUPTED?? ", + |v| { + if v < 8 { + Ok(v) + } else { + Err(format!( + " COME ON, YOUR TERM IN OFFICE IS ONLY {} YEARS.", + TERM_LENGTH + )) + } + }, + )?; + // The original game exits here when you enter a negative number for any of + // the following values. This looks like intentional behaviour. However, to replicate that + // I would have to change everything to signed number which I do not want right now. + print!("HOW MUCH DID YOU HAVE IN THE TREASURY?? "); + stdout().flush()?; + let money = read_int(&mut input, &mut buf)?; + print!("HOW MANY COUNTRYMEN?? "); + stdout().flush()?; + let countrymen = read_int(&mut input, &mut buf)?; + print!("HOW MANY WORKERS?? "); + stdout().flush()?; + let foreign_workers = read_int(&mut input, &mut buf)?; + let land = read_and_verify_int( + &mut input, + &mut buf, + "HOW MANY SQUARE MILES OF LAND?? ", + |v| { + if !(1000..=2000).contains(&v) { + // Note: the original says "10,000 SQ. MILES OF FOREST LAND", but this is + // inconsistent and listed as Bug 3 in the README.md + Err(" COME ON, YOU STARTED WITH 1000 SQ. MILES OF FARM LAND\n AND 1000 SQ. MILES OF FOREST LAND.".to_owned()) + } else { + Ok(v) + } + }, + )?; + Ok(State { + money, + countrymen, + foreign_workers, + land, + year_in_office, + show_land_hint: true, + previous_tourist_trade: 0, + }) + } + _ => { + println!( + r" +CONGRATULATIONS! YOU'VE JUST BEEN ELECTED PREMIER OF SETATS +DETINU, A SMALL COMMUNIST ISLAND 30 BY 70 MILES LONG. YOUR +JOB IS TO DECIDE UPON THE CONTRY'S BUDGET AND DISTRIBUTE +MONEY TO YOUR COUNTRYMEN FROM THE COMMUNAL TREASURY. +THE MONEY SYSTEM IS RALLODS, AND 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. HALF YOUR LAND IS FARM LAND +WHICH ALSO HAS AN EXCELLENT MINERAL CONTENT AND MAY BE SOLD +TO FOREIGN INDUSTRY (STRIP MINING) WHO IMPORT AND SUPPORT +THEIR OWN WORKERS. CROPS COST BETWEEN 10 AND 15 RALLODS PER +SQUARE MILE TO PLANT. +YOUR GOAL IS TO COMPLETE YOUR {} YEAR TERM OF OFFICE. +GOOD LUCK! +", + TERM_LENGTH + ); + Ok(init_state(rng)) + } + } +} + +static POLLUTION: &[&str] = &[ + "FISH POPULATION HAS DWINDLED DUE TO WATER POLLUTION.", + "AIR POLLUTION IS KILLING GAME BIRD POPULATION.", + "MINERAL BATHS ARE BEING RUINED BY WATER POLLUTION.", + "UNPLEASANT SMOG IS DISCOURAGING SUN BATHERS.", + "HOTELS ARE LOOKING SHABBY DUE TO SMOG GRIT.", +]; + +fn next_round( + mut input: R, + rng: &mut Rng, + state: &State, + land_price: u32, + plant_price: u32, +) -> io::Result { + let mut buf = String::with_capacity(16); + let mut show_land_hint = state.show_land_hint; + // global variable `H` in the original game + let land_sold = read_and_verify_int( + &mut input, + &mut buf, + "HOW MANY SQUARE MILES DO YOU WISH TO SELL TO INDUSTRY?? ", + |v| { + if v + 1000 <= state.land { + Ok(v) + } else if show_land_hint { + show_land_hint = false; + Err(format!( + r"*** THINK AGAIN. YOU ONLY HAVE {} SQUARE MILES OF FARM LAND. +(FOREIGN INDUSTRY WILL ONLY BUY FARM LAND BECAUSE +FOREST LAND IS UNECONOMICAL TO STRIP MINE DUE TO TREES, +THICKER TOP SOIL, ETC.)", + state.land - 1000 + )) + } else { + Err(format!( + r"*** THINK AGAIN. YOU ONLY HAVE {} SQUARE MILES OF FARM LAND.\n", + state.land - 1000 + )) + } + }, + )?; + + let land = state.land - land_sold; + let money = state.money + land_sold * land_price; + + // global variable `I` in the original game + let money_distributed = read_and_verify_int( + &mut input, + &mut buf, + "HOW MANY RALLODS WILL YOU DISTRIBUTE AMONG YOUR COUNTRYMEN?? ", + |v| { + if v <= money { + Ok(v) + } else { + Err(format!( + " THINK AGAIN. YOU'VE ONLY {} RALLODS IN THE TREASURY", + money + )) + } + }, + )?; + let money = money - money_distributed; + + // global variable `J` in the original game + let land_planted = if money > 0 { + read_and_verify_int( + &mut input, + &mut buf, + "HOW MANY SQUARE MILES DO YOU WISH TO PLANT?? ", + |v| { + if v > 2 * state.countrymen { + Err(" SORRY, BUT EACH COUNTRYMAN CAN ONLY PLANT 2 SQ. MILES.".to_owned()) + } else if v + 1000 > land { + Err(format!( + " SORRY, BUT YOU'VE ONLY {} SQ. MILES OF FARM LAND.", + land - 1000 + )) + } else if v * plant_price > money { + Err(format!( + " THINK AGAIN. YOU'VE ONLY {} RALLODS LEFT IN THE TREASURY.", + money + )) + } else { + Ok(v) + } + }, + )? + } else { + 0 + }; + let money = money - land_planted * plant_price; + + // global variable `K` in the original game + let pollution_control = if money > 0 { + read_and_verify_int( + &mut input, + &mut buf, + "HOW MANY RALLODS DO YOU WISH TO SPEND ON POLLUTION CONTROL?? ", + |v| { + if v <= money { + Ok(v) + } else { + Err(format!( + " THINK AGAIN. YOU ONLY HAVE {} RALLODS REMAINING.", + money + )) + } + }, + )? + } else { + 0 + }; + let money = money - pollution_control; + + if land_sold == 0 && money_distributed == 0 && land_planted == 0 && pollution_control == 0 { + return Ok(RoundEnd::GameOver( + r" +GOODBYE. +(IF YOU WISH TO CONTINUE THIS GAME AT A LATER DATE, ANSWER +'AGAIN' WHEN ASKED IF YOU WANT INSTRUCTIONS AT THE START +OF THE GAME)." + .to_owned(), + )); + } + + println!("\n\n"); + + let money_after_expenses = money; + + let starvation_deaths = state.countrymen.saturating_sub(money_distributed / 100); + if starvation_deaths > 0 { + println!("{starvation_deaths} COUNTRYMEN DIED OF STARVATION"); + } + + // the original was using `RND(1)` as factor, but I do not want to deal with floats. + // this solution should do the same in the range of numbers we expect + let pollution = ((2000 - land) * rng.u32(0..=2000)) / 2000; + let pollution_deaths = if pollution_control >= 25 { + pollution / (pollution_control / 25) + } else { + pollution + }; + + if pollution_deaths > 0 { + println!( + "{} COUNTRYMEN DIED OF CARBON-MONOXIDE AND DUST INHALATION", + pollution_deaths + ); + } + + let (money, land) = if pollution_deaths + starvation_deaths > 0 { + let funeral_costs = (pollution_deaths + starvation_deaths) * 9; + println!(" YOU WERE FORCED TO SPEND {funeral_costs} RALLODS ON FUNERAL EXPENSES"); + if funeral_costs > money { + println!(" INSUFFICIENT RESERVES TO COVER COST - LAND WAS SOLD"); + ( + 0, + // I only handle integers here, but I think the basic code implicitly turns integers + // to floats on division. So in order to round up to the next full land unit, I have + // to do this weird modulo stuff here + land - funeral_costs / land_price + + if funeral_costs % land_price == 0 { + 0 + } else { + 1 + }, + ) + } else { + (money - funeral_costs, land) + } + } else { + (money, land) + }; + + let mut countrymen = state + .countrymen + .saturating_sub(starvation_deaths) + .saturating_sub(pollution_deaths); + + let tourist_trade_positive = countrymen * 22 + rng.u32(0..500); + let tourist_trade_negative = (2000 - land) * 15; + + let new_foreign_workers: i32 = if land_sold > 0 { + land_sold as i32 + rng.i32(0..10) - rng.i32(0..20) + + if state.foreign_workers == 0 { 20 } else { 0 } + } else { + 0 + }; + // In theory, we could come up with negative foreign workers here (and in the original game) + // This does not seem to be right, so let's cap them at 0 + let foreign_workers = if new_foreign_workers < 0 { + state + .foreign_workers + .saturating_sub(new_foreign_workers.unsigned_abs()) + } else { + state.foreign_workers + new_foreign_workers as u32 + }; + print!(" {new_foreign_workers} WORKERS CAME TO THE COUNTRY AND "); + + let countryman_migration = (money_distributed as i32 / 100 - countrymen as i32) / 10 + + pollution_control as i32 / 25 + - (2000 - land as i32) / 50 + - pollution_deaths as i32 / 2; + + if countryman_migration < 0 { + println!("{} COUNTRYMEN LEFT THE ISLAND", countryman_migration.abs()); + countrymen = countrymen.saturating_sub(countryman_migration.unsigned_abs()) + } else { + println!("{countryman_migration} COUNTRYMEN CAME TO THE ISLAND"); + countrymen += countryman_migration as u32 + } + + let harvest_lost = ((2000 - land) * (rng.u32(0..2000) + 3000)) / 4000; + // in the original game, this checked for foreign_workers == 0 instead of land_planted == 0 + // this is documented as Bug 4 in the README.md + if land_planted == 0 { + print!("OF {land_planted} SQ. MILES PLANTED,"); + } + println!( + " YOU HARVESTED {} SQ. MILES OF CROPS.", + land_planted.saturating_sub(harvest_lost) + ); + + if harvest_lost > 0 { + // There was a bug here in the original code (Bug 2 in README.md). + // Based on the variable `V1`, the word `INCREASED` was inserted. + // However, no value was ever assigned to `V1`. Since the pollution comes from land that has + // been sold, I used the land difference to check whether the pollution increased. + if state.land < land { + println!(" (DUE TO INCREASED AIR AND WATER POLLUTION FROM FOREIGN INDUSTRY.)"); + } else { + println!(" (DUE TO AIR AND WATER POLLUTION FROM FOREIGN INDUSTRY.)"); + } + } + + let crop_winnings = land_planted.saturating_sub(harvest_lost) * land_price / 2; + println!("MAKING {crop_winnings} RALLODS."); + let money = money + crop_winnings; + + // In the original game, there are two bugs here (documented as Bug 1 and Bug 5 in the README.md) + // The first one made the game ignore the income from tourists and duplicated the money that was + // left at this point (the "DECREASE BECAUSE"-message would never have been shown). + // The second bug made the tourist trade profitable again is the pollution was too high (i.e. + // if `tourist_trade_positive` was less than `tourist_trade_negative` + let tourist_trade = tourist_trade_positive.saturating_sub(tourist_trade_negative); + println!(" YOU MADE {tourist_trade} RALLODS FROM TOURIST TRADE."); + if tourist_trade < state.previous_tourist_trade { + println!( + " DECREASE BECAUSE {}", + POLLUTION[rng.usize(0..POLLUTION.len())] + ); + } + let money = money + tourist_trade; + + if starvation_deaths + pollution_deaths > 200 { + let reason = rng.u8(0..10); + return Ok(RoundEnd::GameOver(format!( + r" + + {} COUNTRYMEN DIED IN ONE YEAR!!!!! +DUE TO THIS EXTREME MISMANAGEMENT, YOU HAVE NOT ONLY +BEEN IMPEACHED AND THROWN OUT OF OFFICE, BUT YOU +{} +", + starvation_deaths + pollution_deaths, + // The reasons are not equally probable in the original game. + // I wonder if this was intentional. + if reason <= 3 { + "ALSO HAD YOUR LEFT EYE GOUGED OUT!" + } else if reason <= 6 { + "HAVE ALSO GAINED A VERY BAD REPUTATION." + } else { + "HAVE ALSO BEEN DECLARED NATIONAL FINK." + } + ))); + } + if countrymen < 343 { + // This is not entirely fair, it is possible that some of them just left, not died. + // Also: the initial number of countrymen varies a bit, but this boundary is fix, so it is + // not always a third + return Ok(RoundEnd::GameOver(format!( + r" + +OVER ONE THIRD OF THE POPULTATION HAS DIED SINCE YOU +WERE ELECTED TO OFFICE. THE PEOPLE (REMAINING) +HATE YOUR GUTS. +{} +", + departure_flavour(rng) + ))); + } + if money_after_expenses / 100 > 5 && starvation_deaths >= 2 { + return Ok(RoundEnd::GameOver( + r" +MONEY WAS LEFT OVER IN THE TREASURY WHICH YOU DID +NOT SPEND. AS A RESULT, SOME OF YOUR COUNTRYMEN DIED +OF STARVATION. THE PUBLIC IS ENRAGED AND YOU HAVE +BEEN FORCED TO EITHER RESIGN OR COMMIT SUICIDE. +THE CHOICE IS YOURS. +IF YOU CHOOSE THE LATTER, PLEASE TURN OFF YOUR COMPUTER +BEFORE PROCEEDING. +" + .to_owned(), + )); + } + if foreign_workers > countrymen { + return Ok(RoundEnd::GameOver(format!( + r" + +THE NUMBER OF FOREIGN WORKERS HAS EXCEEDED THE NUMBER +OF COUNTRYMEN. AS A MINORITY, THEY HAVE REVOLTED AND +TAKEN OVER THE COUNTRY. +{} +", + departure_flavour(rng) + ))); + } + if state.year_in_office + 1 >= TERM_LENGTH { + return Ok(RoundEnd::GameOver(format!( + r" + +CONGRATULATIONS!!!!!!!!!!!!!!!!!! +YOU HAVE SUCCESFULLY COMPLETED YOUR {} YEAR TERM +OF OFFICE. YOU WERE, OF COURSE, EXTREMELY LUCKY, BUT +NEVERTHELESS, IT'S QUITE AN ACHIEVEMENT. GOODBYE AND GOOD +LUCK - YOU'LL PROBABLY NEED IT IF YOU'RE THE TYPE THAT +PLAYS THIS GAME. +", + TERM_LENGTH + ))); + } + + Ok(RoundEnd::Next(State { + money, + countrymen, + foreign_workers, + land, + year_in_office: state.year_in_office + 1, + show_land_hint, + previous_tourist_trade: tourist_trade, + })) +} + +fn departure_flavour(rng: &mut Rng) -> &'static str { + if rng.bool() { + "YOU HAVE BEEN THROWN OUT OF OFFICE AND ARE NOW\nRESIDING IN PRISON.\n" + } else { + "YOU HAVE BEEN ASSASSINATED.\n" + } +} + +fn read_int(mut input: R, buf: &mut String) -> io::Result { + loop { + buf.clear(); + input.read_line(buf)?; + let line = buf.trim(); + // This is implicit behaviour in the original code: empty input is equal to 0 + if line.is_empty() { + return Ok(0); + } + if let Ok(n) = line.parse::() { + return Ok(n); + } else { + print!("??REENTER\n?? "); + stdout().flush()?; + } + } +} + +fn read_and_verify_int( + mut input: R, + buf: &mut String, + prompt: &str, + mut verify: Verify, +) -> io::Result +where + Verify: FnMut(u32) -> Result, +{ + loop { + print!("{}", prompt); + stdout().flush()?; + let v = read_int(&mut input, buf)?; + match verify(v) { + Ok(v) => { + return Ok(v); + } + Err(msg) => { + println!("{}", msg); + } + } + } +} diff --git a/54_Letter/rust/Cargo.lock b/54_Letter/rust/Cargo.lock new file mode 100644 index 000000000..6a34e6ec8 --- /dev/null +++ b/54_Letter/rust/Cargo.lock @@ -0,0 +1,75 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "letter" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/55_Life/README.md b/55_Life/README.md index 1834c1e68..195bd7956 100644 --- a/55_Life/README.md +++ b/55_Life/README.md @@ -37,4 +37,11 @@ http://www.vintage-basic.net/games.html #### Porting Notes +- To make sense of the code, it's important to understand what the values in the A(X,Y) array mean: + - 0: dead cell + - 1: live cell + - 2: currently live, but dead next cycle + - 3: currently dead, but alive next cycle + + (please note any difficulties or challenges in porting here) diff --git a/55_Life/python/life.py b/55_Life/python/life.py index 5a71dfa88..8707d5015 100644 --- a/55_Life/python/life.py +++ b/55_Life/python/life.py @@ -44,7 +44,7 @@ def get_pattern() -> Dict[int, str]: # staying in. if line[0] == ".": - line = " " + line[1:] + line = f" {line[1:]}" pattern[c] = line c += 1 @@ -64,7 +64,7 @@ def main() -> None: max_x = MAX_HEIGHT - 1 max_y = MAX_WIDTH - 1 - a = [[0 for y in range(MAX_WIDTH)] for x in range(MAX_HEIGHT)] + a = [[0 for _ in range(MAX_WIDTH)] for _ in range(MAX_HEIGHT)] p = 0 g = 0 invalid = False @@ -81,11 +81,7 @@ def main() -> None: print() print() while True: - if invalid: - inv_str = "INVALID!" - else: - inv_str = "" - + inv_str = "INVALID!" if invalid else "" print(f"GENERATION: {g}\tPOPULATION: {p} {inv_str}") next_min_x = MAX_HEIGHT - 1 @@ -151,7 +147,7 @@ def main() -> None: count = 0 for i in range(x - 1, x + 2): for j in range(y - 1, y + 2): - if a[i][j] == 1 or a[i][j] == 2: + if a[i][j] in [1, 2]: count += 1 if a[x][y] == 0: if count == 3: diff --git a/55_Life/rust/Cargo.lock b/55_Life/rust/Cargo.lock new file mode 100644 index 000000000..b21cc6a2d --- /dev/null +++ b/55_Life/rust/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "rust" +version = "0.1.0" diff --git a/56_Life_for_Two/README.md b/56_Life_for_Two/README.md index 3d9063420..5ce081d37 100644 --- a/56_Life_for_Two/README.md +++ b/56_Life_for_Two/README.md @@ -26,7 +26,7 @@ A new cell will be generated at (3,3) which will be a `#` since there are two `# | 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.) +On the first move 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. @@ -45,6 +45,21 @@ As published in Basic Computer Games (1978): Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html + #### Porting Notes (please note any difficulties or challenges in porting here) + +Note: The original program has a bug. The instructions say that if both players +enter the same cell that the cell is set to 0 or empty. However, the original +Basic program tells the player "ILLEGAL COORDINATES" and makes another cell be entered, +giving a slightly unfair advantage to the 2nd player. + +The Perl verson of the program fixes the bug and follows the instructions. + +Note: The original code had "GOTO 800" but label 800 didn't exist; it should have gone to label 999. +The Basic program has been fixed. + +Note: The Basic program is written to assume it's being played on a Teletype, i.e. output is printed +on paper. To play on a terminal the input must not be echoed, which can be a challenge to do portably +and without tying the solution to a specific OS. Some versions may tell you how to do this, others might not. diff --git a/56_Life_for_Two/lifefortwo.bas b/56_Life_for_Two/lifefortwo.bas index e970ef6df..4faf1f127 100644 --- a/56_Life_for_Two/lifefortwo.bas +++ b/56_Life_for_Two/lifefortwo.bas @@ -60,8 +60,8 @@ 571 IF M3=0 THEN B=1: GOTO 575 572 IF M2=0 THEN B=2: GOTO 575 573 GOTO 580 -574 PRINT: PRINT "A DRAW":GOTO 800 -575 PRINT: PRINT "PLAYER";B;"IS THE WINNER":GOTO 800 +574 PRINT: PRINT "A DRAW":GOTO 999 +575 PRINT: PRINT "PLAYER";B;"IS THE WINNER":GOTO 999 580 FOR B=1 TO 2: PRINT: PRINT: PRINT "PLAYER";B;: GOSUB 700 581 IF B=99 THEN 560 582 NEXT B diff --git a/56_Life_for_Two/perl/README.md b/56_Life_for_Two/perl/README.md index e69c8b819..7b3b41a3c 100644 --- a/56_Life_for_Two/perl/README.md +++ b/56_Life_for_Two/perl/README.md @@ -1,3 +1,21 @@ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Perl](https://www.perl.org/) + +Note: The original program has a bug (see the README in the above dir). This Perl version fixes it. + +Note: For input, the X value is to the right while the Y value is down. +Therefore, the top right cell is "5,1", not "1,5". + +The original program was made to be played on a Teletype, i.e. a printer on paper. +That allowed the program to "black out" the input line to hide a user's input from his/her +opponent, assuming the opponent was at least looking away. To do the equivalent on a +terminal would require a Perl module that isn't installed by default (i.e. it is not +part of CORE and would also require a C compiler to install), nor do I want to issue a +shell command to "stty" to hide the input because that would restrict the game to Linux/Unix. +This means it would have to be played on the honor system. + +However, if you want to try it, install the module "Term::ReadKey" ("sudo cpan -i Term::ReadKey" +if on Linux/Unix and you have root access). If the code finds that module, it will automatically +use it and hide the input ... and restore echoing input again when the games ends. If the module +is not found, input will be visible. diff --git a/56_Life_for_Two/perl/lifefortwo.pl b/56_Life_for_Two/perl/lifefortwo.pl new file mode 100644 index 000000000..0c50cdef7 --- /dev/null +++ b/56_Life_for_Two/perl/lifefortwo.pl @@ -0,0 +1,215 @@ +#!/usr/bin/perl + +# Life_For_Two program in Perl +# Required extensive restructuring to remove all of the GOTO's. +# Translated by Kevin Brannen (kbrannen) + +use strict; +use warnings; + +# try to load module to hide input, set RKey to true if found +my $Rkey = eval { require Term::ReadKey } // 0; +END { Term::ReadKey::ReadMode('normal') if ($Rkey); } + +# globals +my @Board; # 2D board +my @X; # ? +my @Y; # ? +my $Player; # 1 or 2 +my $M2 = 0; # ? +my $M3 = 0; # ? + +# add 0 on front to make data 1 based +my @K = (0,3,102,103,120,130,121,112,111,12,21,30,1020,1030,1011,1021,1003,1002,1012); +my @A = (0,-1,0,1,0,0,-1,0,1,-1,-1,1,-1,-1,1,1,1); + +print "\n"; +print " " x 33, "LIFE2\n"; +print " " x 15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n"; +print " " x 10, "U.B. LIFE GAME\n"; +for my $j (1 .. 5) { for my $k (1 .. 5) { $Board[$j][$k] = 0; } } + +for (1 .. 2) +{ + $Player = $_; # if we make $Player the loop var, the global isn't set + my $p1 = ($Player == 2) ? 30 : 3; + print "\nPLAYER $Player - 3 LIVE PIECES.\n"; + for (1 .. 3) + { + get_input(); + $Board[$X[$Player]][$Y[$Player]] = $p1 if ($Player != 99); + } +} +print_board(); # print board after initial input + +while (1) +{ + print "\n"; + calc_board(); # calc new positions + print_board(); # print current board after calc + + if ($M2 == 0 && $M3 == 0) + { + print "\nA DRAW\n"; + last; + } + if ($M3 == 0) + { + win(1); + last; + } + if ($M2 == 0) + { + win(2); + last; + } + + for (1 .. 2) + { + $Player = $_; # if we make $Player the loop var, the global isn't set + print "\n\nPLAYER $Player "; + get_input(); + last if ($Player == 99); + } + next if ($Player == 99); + + $Board[$X[1]][$Y[1]] = 100; + $Board[$X[2]][$Y[2]] = 1000; +} +exit(0); + +########################################################### + +sub win +{ + my $p = shift; + print "\nPLAYER $p IS THE WINNER\n"; +} + +sub calc_board +{ + for my $j (1 .. 5) + { + for my $k (1 .. 5) + { + if ($Board[$j][$k] > 99) + { + $Player = $Board[$j][$k] > 999 ? 10 : 1; + for (my $c = 1 ; $c <= 15 ; $c += 2) + { + $Board[$j+$A[$c]][$k+$A[$c+1]] = ($Board[$j+$A[$c]][$k+$A[$c+1]] // 0) + $Player; + } + } + } + } +} + +sub print_board +{ + $M2 = 0; + $M3 = 0; + for my $j (0 .. 6) + { + print "\n"; + for my $k (0 .. 6) + { + if ($j != 0 && $j != 6) + { + if ($k != 0 && $k != 6) + { + print_row($j, $k); + next; + } + if ($j == 6) + { + print "0\n"; + return; + } + print " $j "; + } + else + { + if ($k == 6) + { + print " 0 "; + last; + } + print " $k "; + } + } + } +} + +sub print_row +{ + my ($j, $k) = @_; + + if ($Board[$j][$k] >= 3) + { + my $c; + for $c (1 .. 18) + { + if ($Board[$j][$k] == $K[$c]) + { + if ($c <= 9) + { + $Board[$j][$k] = 100; + $M2++; + print " * "; + } + else + { + $Board[$j][$k] = 1000; + $M3++; + print " # "; + } + return; + } + } + } + $Board[$j][$k] = 0; + print " "; +} + +sub get_input +{ + while (1) + { + print "X,Y\n"; + my $ans; + + if ($Rkey) + { + # code to hide input + Term::ReadKey::ReadMode('noecho'); + $ans = Term::ReadKey::ReadLine(0); + Term::ReadKey::ReadMode('restore'); + print "\n"; # do this since the one entered was hidden + } + else + { + # normal, input visible + chomp($ans = <>); + } + + ($Y[$Player], $X[$Player]) = split(/[,\s]+/, $ans, 2); + if ($X[$Player] > 5 || $X[$Player] < 1 || $Y[$Player] > 5 || $Y[$Player] < 1) + { + print "ILLEGAL COORDS. RETYPE\n"; + next; + } + # this tells you the cell was already taken not zero it out, bug! + #if ($Board[$X[$Player]][$Y[$Player]] != 0) + #{ + # print "ILLEGAL COORDS. RETYPE\n"; + # next; + #} + last; + } + + return if ($Player == 1 || $X[1] != $X[2] || $Y[1] != $Y[2]); + + print "SAME COORD. SET TO 0\n"; + $Board[$X[$Player]+1][$Y[$Player]+1] = 0; + $Player = 99; +} diff --git a/56_Life_for_Two/python/life_for_two.py b/56_Life_for_Two/python/life_for_two.py index da4e16195..c9910f994 100644 --- a/56_Life_for_Two/python/life_for_two.py +++ b/56_Life_for_Two/python/life_for_two.py @@ -5,11 +5,12 @@ Ported by Sajid Sarker (2022). ''' + # Global Variable Initialisation # Initialise the board -gn = [[0 for i in range(6)] for j in range(6)] -gx = [0 for x in range(3)] -gy = [0 for x in range(3)] +gn = [[0 for _ in range(6)] for _ in range(6)] +gx = [0 for _ in range(3)] +gy = [0 for _ in range(3)] gk = [0, 3, 102, 103, 120, 130, 121, 112, 111, 12, 21, 30, 1020, 1030, 1011, 1021, 1003, 1002, 1012] @@ -27,9 +28,9 @@ def tab(number) -> str: def display_header() -> None: - print("{}LIFE2".format(tab(33))) - print("{}CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n".format(tab(15))) - print("{}U.B. LIFE GAME".format(tab(10))) + print(f"{tab(33)}LIFE2") + print(f"{tab(15)}CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n") + print(f"{tab(10)}U.B. LIFE GAME") # Board Functions @@ -37,7 +38,7 @@ def setup_board() -> None: # Players add symbols to initially setup the board for b in range(1, 3): p1 = 3 if b != 2 else 30 - print("\nPLAYER {} - 3 LIVE PIECES.".format(b)) + print(f"\nPLAYER {b} - 3 LIVE PIECES.") for _ in range(1, 4): query_player(b) gn[gx[b]][gy[b]] = p1 @@ -46,7 +47,7 @@ def setup_board() -> None: def modify_board() -> None: # Players take turns to add symbols and modify the board for b in range(1, 3): - print("PLAYER {} ".format(b)) + print(f"PLAYER {b} ") query_player(b) if b == 99: break @@ -72,36 +73,32 @@ def display_board() -> None: for j in range(7): print("") for k in range(7): - if j == 0 or j == 6: + if j in [0, 6]: if k != 6: - print(" " + str(k) + " ", end="") + print(f" {str(k)} ", end="") else: print(" 0 ", end="") - elif k == 0 or k == 6: - if j != 6: - print(" " + str(j) + " ", end="") - else: - print(" 0\n") + elif k in [0, 6]: + print(f" {str(j)} ", end="") + elif gn[j][k] < 3: + gn[j][k] = 0 + print(" ", end="") else: - if gn[j][k] < 3: + for o1 in range(1, 19): + if gn[j][k] == gk[o1]: + break + if o1 <= 18: + if o1 > 9: + gn[j][k] = 1000 + m3 += 1 + print(" # ", end="") + else: + gn[j][k] = 100 + m2 += 1 + print(" * ", end="") + else: gn[j][k] = 0 print(" ", end="") - else: - for o1 in range(1, 19): - if gn[j][k] == gk[o1]: - break - if o1 <= 18: - if o1 > 9: - gn[j][k] = 1000 - m3 += 1 - print(" # ", end="") - else: - gn[j][k] = 100 - m2 += 1 - print(" * ", end="") - else: - gn[j][k] = 0 - print(" ", end="") # Player Functions @@ -113,8 +110,8 @@ def query_player(b) -> None: b_ = input("???") x_ = [int(num) for num in a_.split() if num.isdigit()] y_ = [int(num) for num in b_.split() if num.isdigit()] - x_ = [0] if len(x_) == 0 else x_ - y_ = [0] if len(y_) == 0 else y_ + x_ = [0] if not x_ else x_ + y_ = [0] if not y_ else y_ gx[b] = y_[0] gy[b] = x_[0] if gx[b] in range(1, 6)\ diff --git a/57_Literature_Quiz/rust/Cargo.lock b/57_Literature_Quiz/rust/Cargo.lock new file mode 100644 index 000000000..b21cc6a2d --- /dev/null +++ b/57_Literature_Quiz/rust/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "rust" +version = "0.1.0" diff --git a/57_Literature_Quiz/rust/Cargo.toml b/57_Literature_Quiz/rust/Cargo.toml new file mode 100644 index 000000000..1ec696335 --- /dev/null +++ b/57_Literature_Quiz/rust/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "rust" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/57_Literature_Quiz/rust/README.md b/57_Literature_Quiz/rust/README.md new file mode 100644 index 000000000..fc6468b9f --- /dev/null +++ b/57_Literature_Quiz/rust/README.md @@ -0,0 +1,3 @@ +Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) + +Conversion to [Rust](https://www.rust-lang.org/) diff --git a/57_Literature_Quiz/rust/src/main.rs b/57_Literature_Quiz/rust/src/main.rs new file mode 100644 index 000000000..a9e3923e5 --- /dev/null +++ b/57_Literature_Quiz/rust/src/main.rs @@ -0,0 +1,127 @@ +use std::io; + + +fn print_instructions() { + println!("TEST YOUR KNOWLEDGE OF CHILDREN'S LITERATURE."); + println!(); + println!("THIS IS A MULTIPLE-CHOICE QUIZ."); + println!("TYPE A 1, 2, 3, OR 4 AFTER THE QUESTION MARK."); + println!(); + println!("GOOD LUCK!"); + println!(); + println!(); +} + + +fn print_center(text: String, width: usize) { + let pad_size; + if width > text.len() { + pad_size = (width - text.len()) / 2; + } else { + pad_size = 0; + } + println!("{}{}", " ".repeat(pad_size), text); +} + + +fn print_results(score: usize, number_of_questions: usize) { + if score == number_of_questions { + println!("WOW! THAT'S SUPER! YOU REALLY KNOW YOUR NURSERY"); + println!("YOUR NEXT QUIZ WILL BE ON 2ND CENTURY CHINESE"); + println!("LITERATURE (HA, HA, HA)"); + } else if score < number_of_questions / 2 { + println!("UGH. THAT WAS DEFINITELY NOT TOO SWIFT. BACK TO"); + println!("NURSERY SCHOOL FOR YOU, MY FRIEND."); + } else { + println!("NOT BAD, BUT YOU MIGHT SPEND A LITTLE MORE TIME"); + println!("READING THE NURSERY GREATS."); + } +} + +fn main() { + let page_width: usize = 64; + + struct Question<'a> { + question: &'a str, + choices: Vec<&'a str>, + answer: u8, + correct_response: &'a str, + wrong_response: &'a str, + } + + impl Question<'_>{ + fn ask(&self) -> bool { + println!("{}", self.question); + for i in 0..4 { + print!("{}){}", i+1, self.choices[i]); + if i != 3 { print!(", ")}; + } + println!(""); + let mut user_input: String = String::new(); + io::stdin() + .read_line(&mut user_input) + .expect("Failed to read the line"); + + if user_input.starts_with(&self.answer.to_string()) { + println!("{}", self.correct_response); + true + } else { + println!("{}", self.wrong_response); + false + } + } + } + + let questions: Vec = vec![ + Question{ + question: "IN PINOCCHIO, WHAT WAS THE NAME OF THE CAT?", + choices: vec!["TIGGER", "CICERO", "FIGARO", "GUIPETTO"], + answer: 3, + wrong_response: "SORRY...FIGARO WAS HIS NAME.", + correct_response: "VERY GOOD! HERE'S ANOTHER.", + }, + Question{ + question: "FROM WHOSE GARDEN DID BUGS BUNNY STEAL THE CARROTS?", + choices: vec!["MR. NIXON'S", "ELMER FUDD'S", "CLEM JUDD'S", "STROMBOLI'S"], + answer: 2, + wrong_response: "TOO BAD...IT WAS ELMER FUDD'S GARDEN.", + correct_response: "PRETTY GOOD!", + }, + Question{ + question: "IN THE WIZARD OF OS, DOROTHY'S DOG WAS NAMED?", + choices: vec!["CICERO", "TRIXIA", "KING", "TOTO"], + answer: 4, + wrong_response: "BACK TO THE BOOKS,...TOTO WAS HIS NAME.", + correct_response: "YEA! YOU'RE A REAL LITERATURE GIANT.", + }, + Question{ + question: "WHO WAS THE FAIR MAIDEN WHO ATE THE POISON APPLE?", + choices: vec!["SLEEPING BEAUTY", "CINDERELLA", "SNOW WHITE", "WENDY"], + answer: 3, + wrong_response: "OH, COME ON NOW...IT WAS SNOW WHITE.", + correct_response: "GOOD MEMORY!", + }, + ]; + let number_of_questions: usize = questions.len(); + + print_center("LITERATURE QUIZ".to_string(), page_width); + print_center("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY".to_string(), page_width); + println!(); + println!(); + println!(); + print_instructions(); + + let mut score = 0; + for question in questions { + if question.ask() { + score += 1; + } + println!(); + } + + print_results(score, number_of_questions); + +} + + + diff --git a/58_Love/rust/Cargo.lock b/58_Love/rust/Cargo.lock new file mode 100644 index 000000000..b21cc6a2d --- /dev/null +++ b/58_Love/rust/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "rust" +version = "0.1.0" diff --git a/58_Love/rust/Cargo.toml b/58_Love/rust/Cargo.toml new file mode 100644 index 000000000..1ec696335 --- /dev/null +++ b/58_Love/rust/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "rust" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/58_Love/rust/README.md b/58_Love/rust/README.md new file mode 100644 index 000000000..4cd904309 --- /dev/null +++ b/58_Love/rust/README.md @@ -0,0 +1,3 @@ +Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) + +Conversion to [Rust](https://www.rust-lang.org/) by Jadi. diff --git a/58_Love/rust/src/main.rs b/58_Love/rust/src/main.rs new file mode 100644 index 000000000..5e8e18d72 --- /dev/null +++ b/58_Love/rust/src/main.rs @@ -0,0 +1,92 @@ +use std::io; + +fn show_intro() { + // Displays the intro text + println!("\n Love"); + println!("Creative Computing Morristown, New Jersey"); + println!("\n\n"); + println!("A tribute to the great American artist, Robert Indiana."); + println!("His great work will be reproduced with a message of"); + println!("your choice up to 60 characters. If you can't think of"); + println!("a message, simple type the word 'love'\n"); // (sic) +} + +fn main() { + enum PrintOrPass { + Print, + Pass, + } + + let data = [ + vec![60], + vec![1, 12, 26, 9, 12], + vec![3, 8, 24, 17, 8], + vec![4, 6, 23, 21, 6], + vec![4, 6, 22, 12, 5, 6, 5], + vec![4, 6, 21, 11, 8, 6, 4], + vec![4, 6, 21, 10, 10, 5, 4], + vec![4, 6, 21, 9, 11, 5, 4], + vec![4, 6, 21, 8, 11, 6, 4], + vec![4, 6, 21, 7, 11, 7, 4], + vec![4, 6, 21, 6, 11, 8, 4], + vec![4, 6, 19, 1, 1, 5, 11, 9, 4], + vec![4, 6, 19, 1, 1, 5, 10, 10, 4], + vec![4, 6, 18, 2, 1, 6, 8, 11, 4], + vec![4, 6, 17, 3, 1, 7, 5, 13, 4], + vec![4, 6, 15, 5, 2, 23, 5], + vec![1, 29, 5, 17, 8], + vec![1, 29, 9, 9, 12], + vec![1, 13, 5, 40, 1], + vec![1, 13, 5, 40, 1], + vec![4, 6, 13, 3, 10, 6, 12, 5, 1], + vec![5, 6, 11, 3, 11, 6, 14, 3, 1], + vec![5, 6, 11, 3, 11, 6, 15, 2, 1], + vec![6, 6, 9, 3, 12, 6, 16, 1, 1], + vec![6, 6, 9, 3, 12, 6, 7, 1, 10], + vec![7, 6, 7, 3, 13, 6, 6, 2, 10], + vec![7, 6, 7, 3, 13, 14, 10], + vec![8, 6, 5, 3, 14, 6, 6, 2, 10], + vec![8, 6, 5, 3, 14, 6, 7, 1, 10], + vec![9, 6, 3, 3, 15, 6, 16, 1, 1], + vec![9, 6, 3, 3, 15, 6, 15, 2, 1], + vec![10, 6, 1, 3, 16, 6, 14, 3, 1], + vec![10, 10, 16, 6, 12, 5, 1], + vec![11, 8, 13, 27, 1], + vec![11, 8, 13, 27, 1], + vec![60], + ]; + + const ROW_LEN: usize = 60; + show_intro(); + + let mut input: String = String::new(); + io::stdin().read_line(&mut input).expect("No valid input"); + let input = if input.len() == 1 { + "LOVE" + } else { + input.trim() + }; + // repeat the answer to fill the whole line, we will show chunks of this when needed + let input = input.repeat(ROW_LEN / (input.len()) + 1); + + // Now lets display the Love + print!("{}", "\n".repeat(9)); + for row in data { + let mut print_or_pass = PrintOrPass::Print; + let mut current_start = 0; + for count in row { + match print_or_pass { + PrintOrPass::Print => { + print!("{}", &input[current_start..count + current_start]); + print_or_pass = PrintOrPass::Pass; + } + PrintOrPass::Pass => { + print!("{}", " ".repeat(count)); + print_or_pass = PrintOrPass::Print; + } + } + current_start += count; + } + println!(); + } +} diff --git a/59_Lunar_LEM_Rocket/README.md b/59_Lunar_LEM_Rocket/README.md index 91fe1cd63..98db7aed6 100644 --- a/59_Lunar_LEM_Rocket/README.md +++ b/59_Lunar_LEM_Rocket/README.md @@ -23,6 +23,45 @@ As published in Basic Computer Games (1978): Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html +#### Known Bugs + +### lem.bas + +- The input validation on the thrust value (displayed as P, stored internally as F) appears to be incorrect. It allows negative values up up to -95, but at -96 or more balks and calls it negative. I suspect the intent was to disallow any value less than 0 (in keeping with the instructions), *or* nonzero values less than 10. + +- The physics calculations seem very sus. If you enter "1000,0,0" (i.e. no thrust at all, integrating 1000 seconds at a time) four times in a row, you first fall, but then mysteriously gain vertical speed, and end up being lost in space. This makes no sense. A similar result happened when just periodically applying 10% thrust in an attempt to hover. + + #### Porting Notes (please note any difficulties or challenges in porting here) + +### LUNAR + +Variables: + +`A`: Altitude in miles. Up is positive. +`V`: Velocity in miles / sec. Down is positive. + +`M`: Weight of capsule in pounds, both fuel and machine +`N`: Empty weight of capsule in pounds. So, weight of fuel is M - N. + +`G`: Gravity in miles / sec^2, down is positive. +`Z`: Exhaust velocity in miles / sec + +`L`: time in seconds since start of simulation. +`K`: Burn rate for this 10 second turn, pounds of fuel per sec +`T`: Time left in this 10 second turn, in seconds. +`S`: Burn time in this 10 second turn, input to subroutine 420. + +Subroutines: + +330, Apply updates from one call to subroutine 420. + +370, If you started descending and ended ascending, figure out whether you hit the surface in between. + +420, Compute new velocity and altitude using the Tsiolkovsky rocket equation for S seconds: + +`Q`: Fraction of initial mass that's burnt, i.e. 1 - mf / mo, exactly what we need for the Taylor series of `ln` in the rocket equation. Local to this subroutine. +`J`: Final velocity after S seconds, down is positive. Return value. +`I`: Altitude after S seconds, up is positive. Return value. diff --git a/59_Lunar_LEM_Rocket/javascript/lunar.js b/59_Lunar_LEM_Rocket/javascript/lunar.js index 69306aa28..43a0e892c 100644 --- a/59_Lunar_LEM_Rocket/javascript/lunar.js +++ b/59_Lunar_LEM_Rocket/javascript/lunar.js @@ -102,7 +102,7 @@ async function main() print("0 (FREE FALL) AND 200 (MAXIMUM BURN) POUNDS PER SECOND.\n"); print("SET NEW BURN RATE EVERY 10 SECONDS.\n"); print("\n"); - print("CAPSULE WEIGHT 32,500 LBS; FUEL WEIGHT 16,500 LBS.\n"); + print("CAPSULE WEIGHT 32,500 LBS; FUEL WEIGHT 16,000 LBS.\n"); print("\n"); print("\n"); print("\n"); diff --git a/59_Lunar_LEM_Rocket/lunar.bas b/59_Lunar_LEM_Rocket/lunar.bas index b12037187..301336150 100644 --- a/59_Lunar_LEM_Rocket/lunar.bas +++ b/59_Lunar_LEM_Rocket/lunar.bas @@ -8,7 +8,7 @@ 70 PRINT: PRINT "SET BURN RATE OF RETRO ROCKETS TO ANY VALUE BETWEEN" 80 PRINT "0 (FREE FALL) AND 200 (MAXIMUM BURN) POUNDS PER SECOND." 90 PRINT "SET NEW BURN RATE EVERY 10 SECONDS.": PRINT -100 PRINT "CAPSULE WEIGHT 32,500 LBS; FUEL WEIGHT 16,500 LBS." +100 PRINT "CAPSULE WEIGHT 32,500 LBS; FUEL WEIGHT 16,000 LBS." 110 PRINT: PRINT: PRINT: PRINT "GOOD LUCK" 120 L=0 130 PRINT: PRINT "SEC","MI + FT","MPH","LB FUEL","BURN RATE":PRINT diff --git a/59_Lunar_LEM_Rocket/python/lunar.py b/59_Lunar_LEM_Rocket/python/lunar.py index 82f21fc01..b65e0fa54 100644 --- a/59_Lunar_LEM_Rocket/python/lunar.py +++ b/59_Lunar_LEM_Rocket/python/lunar.py @@ -64,7 +64,7 @@ def add_rjust(line: str, s: Any, pos: int) -> str: def add_ljust(line: str, s: str, pos: int) -> str: """Add a new field to a line left justified starting at pos""" - s = str(s) + s = s if len(line) > pos: line = line[:pos] if len(line) < pos: @@ -85,7 +85,7 @@ def print_intro() -> None: print("SET BURN RATE OF RETRO ROCKETS TO ANY VALUE BETWEEN") print("0 (FREE FALL) AND 200 (MAXIMUM BURN) POUNDS PER SECOND.") print("SET NEW BURN RATE EVERY 10 SECONDS.\n") - print("CAPSULE WEIGHT 32,500 LBS; FUEL WEIGHT 16,500 LBS.\n\n\n") + print("CAPSULE WEIGHT 32,500 LBS; FUEL WEIGHT 16,000 LBS.\n\n\n") print("GOOD LUCK\n") @@ -324,10 +324,7 @@ def run_simulation() -> None: return if capsule.velocity > 0 and new_state.velocity < 0: - # moving away from the moon - - landed = handle_flyaway(sim_clock, capsule) - if landed: + if handle_flyaway(sim_clock, capsule): process_final_tick(delta_t, sim_clock, capsule) return diff --git a/60_Mastermind/README.md b/60_Mastermind/README.md index 7820eed7b..05383f106 100644 --- a/60_Mastermind/README.md +++ b/60_Mastermind/README.md @@ -86,7 +86,7 @@ solutions reporting their black and white pegs against `RBW`. | BRW | 1 | 2 | | WRW | 1 | 1 | | RRW | 2 | 0 | | BRR | 0 | 2 | | WRR | 0 | 2 | | RRR | 1 | 0 | -Now we are going to eliminate every solution that **DOESN'T** matches 0 black and 2 white. +Now we are going to eliminate every solution that **DOESN'T** match 0 black and 2 white. | Guess | Black | White | | Guess | Black | White | | Guess | Black | White | |----------|-------|-------|-----|----------|-------|-------|-----|----------|-------|-------| @@ -130,7 +130,7 @@ report 1 black and 0 whites. | ~~~WWB~~ | 0 | 1 | | ~~~WRR~~ | 2 | 0 | -Only one solution matches and its our secret code! The computer will guess this +Only one solution matches and it's our secret code! The computer will guess this one next as it's the only choice left, for a total of three moves. Coincidentally, I believe the expected maximum number of moves the computer will make is the number of positions plus one for the initial guess with no information. @@ -150,4 +150,8 @@ WB first, the most you can logically deduce if you get 1 black and 1 white is that it is either WW, or BB which could bring your total guesses up to three which is the number of positions plus one. So if your computer's turn is taking longer than the number of positions plus one to find the answer then something -is wrong with your code. \ No newline at end of file +is wrong with your code. + +#### Known Bugs + +- Line 622 is unreachable, as the previous line ends in a GOTO and that line number is not referenced anywhere. It appears that the intent was to tell the user the correct combination after they fail to guess it in 10 tries, which would be a very nice feature, but does not actually work. (In the MiniScript port, I have made this feature work.) diff --git a/60_Mastermind/python/mastermind.py b/60_Mastermind/python/mastermind.py index cc77153c4..8fcecc624 100644 --- a/60_Mastermind/python/mastermind.py +++ b/60_Mastermind/python/mastermind.py @@ -77,11 +77,7 @@ def human_turn() -> None: print_score() return # from human turn, triumphant else: - print( - "You have {} blacks and {} whites".format( - guess_results[1], guess_results[2] - ) - ) + print(f"You have {guess_results[1]} blacks and {guess_results[2]} whites") guesses.append(guess_results) num_moves += 1 @@ -193,16 +189,14 @@ def possibility_to_color_code(possibility: int) -> str: def compare_two_positions(guess: str, answer: str) -> List[Union[str, int]]: """Returns blacks (correct color and position) and whites (correct color only) for candidate position (guess) versus reference position (answer).""" - increment = 0 blacks = 0 whites = 0 initial_guess = guess + increment = 0 for pos in range(0, NUM_POSITIONS): if guess[pos] != answer[pos]: for pos2 in range(0, NUM_POSITIONS): - if not ( - guess[pos] != answer[pos2] or guess[pos2] == answer[pos2] - ): # correct color but not correct place + if guess[pos] == answer[pos2] and guess[pos2] != answer[pos2]: # correct color but not correct place whites = whites + 1 answer = answer[:pos2] + chr(increment) + answer[pos2 + 1:] guess = guess[:pos] + chr(increment + 1) + guess[pos + 1:] diff --git a/61_Math_Dice/python/mathdice.py b/61_Math_Dice/python/mathdice.py index 99220e713..4081e0cca 100644 --- a/61_Math_Dice/python/mathdice.py +++ b/61_Math_Dice/python/mathdice.py @@ -22,23 +22,23 @@ def print_2() -> None: print(" ----- ") - if n in [4, 5, 6]: + if n in {4, 5, 6}: print_2() - elif n in [2, 3]: + elif n in {2, 3}: print("| * |") else: print_0() - if n in [1, 3, 5]: + if n in {1, 3, 5}: print("| * |") - elif n in [2, 4]: + elif n in {2, 4}: print_0() else: print_2() - if n in [4, 5, 6]: + if n in {4, 5, 6}: print_2() - elif n in [2, 3]: + elif n in {2, 3}: print("| * |") else: print_0() diff --git a/61_Math_Dice/rust/Cargo.lock b/61_Math_Dice/rust/Cargo.lock new file mode 100644 index 000000000..4fe2abbea --- /dev/null +++ b/61_Math_Dice/rust/Cargo.lock @@ -0,0 +1,75 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/62_Mugwump/python/mugwump.py b/62_Mugwump/python/mugwump.py index d1e3f7c3d..cc719c172 100644 --- a/62_Mugwump/python/mugwump.py +++ b/62_Mugwump/python/mugwump.py @@ -35,8 +35,7 @@ def reveal_mugwumps(mugwumps: List[List[int]]) -> None: def calculate_distance(guess: Tuple[int, int], mugwump: List[int]) -> float: - d = sqrt(((mugwump[0] - guess[0]) ** 2) + ((mugwump[1] - guess[1]) ** 2)) - return d + return sqrt(((mugwump[0] - guess[0]) ** 2) + ((mugwump[1] - guess[1]) ** 2)) def play_again() -> None: diff --git a/62_Mugwump/rust/Cargo.lock b/62_Mugwump/rust/Cargo.lock new file mode 100644 index 000000000..4fe2abbea --- /dev/null +++ b/62_Mugwump/rust/Cargo.lock @@ -0,0 +1,75 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/63_Name/python/name.py b/63_Name/python/name.py index 3d7eeb1c0..94b8170d6 100644 --- a/63_Name/python/name.py +++ b/63_Name/python/name.py @@ -9,9 +9,7 @@ def is_yes_ish(answer: str) -> bool: cleaned = answer.strip().upper() - if cleaned in ["Y", "YES"]: - return True - return False + return cleaned in {"Y", "YES"} def main() -> None: diff --git a/63_Name/rust/Cargo.toml b/63_Name/rust/Cargo.toml new file mode 100644 index 000000000..7d75412a6 --- /dev/null +++ b/63_Name/rust/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "rust" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/63_Name/rust/src/main.rs b/63_Name/rust/src/main.rs new file mode 100644 index 000000000..1a1f631a7 --- /dev/null +++ b/63_Name/rust/src/main.rs @@ -0,0 +1,116 @@ +/** NAME GAME BY GEOFFREY CHASE + * https://github.com/coding-horror/basic-computer-games/blob/main/63_Name/name.bas + * Direct conversion from BASIC to Rust by Pablo Marques (marquesrs). + * No additional features or improvements were added. As a faithful translation, + * many of the code here are done in an unrecommended way by today's standards. + * 17/02/25 +*/ + +use std::io::Write; + +fn main() { + let mut input = String::new(); + + //1 PRINT TAB(34);"NAME" + //2 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" + //3 PRINT: PRINT: PRINT + print!("{}{}\n{}{}\n\n\n\n", + " ".repeat(34), + "NAME", + " ".repeat(15), + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" + ); + + //5 DIM B$(40) + let mut b = [0; 40]; + + //10 PRINT "HELLO.": PRINT "MY NAME IS CREATIVE COMPUTER." + //20 PRINT "WHAT'S YOUR NAME (FIRST AND LAST";: INPUT A$: L=LEN(A$) + print!("{}\n{}\n{}", + "HELLO.", + "MY NAME IS CREATIVE COMPUTER.", + "WHAT'S YOUR NAME (FIRST AND LAST)? " + ); + + let _ = std::io::stdout().flush().unwrap(); + std::io::stdin().read_line(&mut input).unwrap(); + let a = input.trim().to_uppercase(); + let l = a.len(); + + //30 PRINT: PRINT "THANK YOU, "; + print!("\nTHANK YOU, "); + + //40 FOR I=1 TO L: B$(I)=MID$(A$,I,1): NEXT I + for i in 1..=l { + b[i-1] = a.chars().nth(i-1).unwrap() as u8; + } + + //50 FOR I=L TO 1 STEP -1: PRINT B$(I);: NEXT I + for i in (1..=l).rev() { + print!("{}", b[i-1] as char); + } + + //60 PRINT ".": PRINT "OOPS! I GUESS I GOT IT BACKWARDS. A SMART" + //70 PRINT "COMPUTER LIKE ME SHOULDN'T MAKE A MISTAKE LIKE THAT!": PRINT + //80 PRINT "BUT I JUST NOTICED YOUR LETTERS ARE OUT OF ORDER." + //90 PRINT "LET'S PUT THEM IN ORDER LIKE THIS: "; + print!("{}\n{}\n{}\n\n{}\n{}", + ".", + "OOPS! I GUESS I GOT IT BACKWARDS. A SMART", + "COMPUTER LIKE ME SHOULDN'T MAKE A MISTAKE LIKE THAT!", + "BUT I JUST NOTICED YOUR LETTERS ARE OUT OF ORDER.", + "LET'S PUT THEM IN ORDER LIKE THIS: " + ); + + //100 FOR J=2 TO L: I=J-1: T$=B$(J) + let mut i; + let mut t; + for j in 2..=l { + i = j - 1; + t = b[j-1]; + loop { + //110 IF T$>B$(I) THEN 130 + if i == 0 || t > b[i-1] { + //130 B$(I+1)=T$: NEXT J + b[i] = t; + break; + } else { + //120 B$(I+1)=B$(I): I=I-1: IF I>0 THEN 110 + b[i] = b[i-1]; + i = i - 1; + } + } + } + + //140 FOR I=1 TO L: PRINT B$(I);: NEXT I: PRINT: PRINT + for i in 1..=l { + print!("{}", b[i-1] as char); + } + print!("\n\n"); + + //150 PRINT "DON'T YOU LIKE THAT BETTER";: INPUT D$ + print!("DON'T YOU LIKE THAT BETTER? "); + + let _ = std::io::stdout().flush().unwrap(); + input.clear(); + std::io::stdin().read_line(&mut input).unwrap(); + let d = input.trim().to_uppercase(); + + //160 IF D$="YES" THEN 180 + if d == "YES" || d == "Y" { + //180 PRINT: PRINT "I KNEW YOU'D AGREE!!" + print!("\nI KNEW YOU'D AGREE!!"); + } + else { + //170 PRINT: PRINT "I'M SORRY YOU DON'T LIKE IT THAT WAY.": GOTO 200 + print!("\nI'M SORRY YOU DON'T LIKE IT THAT WAY."); + } + + print!("\n\n{}{}.\n{}\n", + "I REALLY ENJOYED MEETING YOU ", + a, + "HAVE A NICE DAY!" + ); + + //999 END +} diff --git a/64_Nicomachus/python/nicomachus.py b/64_Nicomachus/python/nicomachus.py index 84f477032..1485d4a1c 100644 --- a/64_Nicomachus/python/nicomachus.py +++ b/64_Nicomachus/python/nicomachus.py @@ -43,9 +43,7 @@ def play_game() -> None: print(f"YOUR NUMBER WAS {d}, RIGHT?") - response = get_yes_or_no() - - if response: + if get_yes_or_no(): print("HOW ABOUT THAT!!") else: print("I FEEL YOUR ARITHMETIC IS IN ERROR.") diff --git a/64_Nicomachus/rust/Cargo.lock b/64_Nicomachus/rust/Cargo.lock new file mode 100644 index 000000000..b21cc6a2d --- /dev/null +++ b/64_Nicomachus/rust/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "rust" +version = "0.1.0" diff --git a/65_Nim/README.md b/65_Nim/README.md index 11243f0fc..fae886395 100644 --- a/65_Nim/README.md +++ b/65_Nim/README.md @@ -28,3 +28,7 @@ http://www.vintage-basic.net/games.html #### Porting Notes This can be a real challenge to port because of all the `GOTO`s going out of loops down to code. You may need breaks and continues, or other techniques. + +#### Known Bugs + +- If, after the player moves, all piles are gone, the code prints "MACHINE LOSES" regardless of the win condition (when line 1550 jumps to line 800). This should instead jump to line 800 ("machine loses") if W=1, but jump to 820 ("machine wins") if W=2. diff --git a/65_Nim/python/Traditional_NIM.py b/65_Nim/python/Traditional_NIM.py index bd1470c50..655e5d85c 100644 --- a/65_Nim/python/Traditional_NIM.py +++ b/65_Nim/python/Traditional_NIM.py @@ -30,11 +30,7 @@ def remove_pegs(self, command) -> None: print("\nInvalid value of either Peg or Pile\n") def get_ai_move(self) -> Tuple[int, int]: - possible_pile = [] - for k, v in self.piles.items(): - if v != 0: - possible_pile.append(k) - + possible_pile = [k for k, v in self.piles.items() if v != 0] pile = random.choice(possible_pile) num = random.randint(1, self.piles[pile]) @@ -46,7 +42,7 @@ def _command_integrity(self, num, pile) -> bool: def print_pegs(self) -> None: for pile, peg in self.piles.items(): - print("Pile {} : {}".format(pile, "O " * peg)) + print(f'Pile {pile} : {"O " * peg}') def help(self) -> None: print("-" * 10) @@ -82,15 +78,14 @@ def main() -> None: # Start game loop input("\nPress Enter to start the Game:\n") - end = False + while True: game.print_pegs() # Players Move command = input("\nYOUR MOVE - Number of PILE, Number of Object? ") game.remove_pegs(command) - end = game.check_for_win() - if end: + if game.check_for_win(): print("\nPlayer Wins the Game, Congratulations!!") input("\nPress any key to exit") break @@ -98,13 +93,10 @@ def main() -> None: # Computers Move ai_command = game.get_ai_move() print( - "\nA.I MOVE - A.I Removed {} pegs from Pile {}".format( - ai_command[1], ai_command[0] - ) + f"\nA.I MOVE - A.I Removed {ai_command[1]} pegs from Pile {ai_command[0]}" ) - game.remove_pegs(str(ai_command[0]) + "," + str(ai_command[1])) - end = game.check_for_win() - if end: + game.remove_pegs(f"{str(ai_command[0])},{str(ai_command[1])}") + if game.check_for_win(): print("\nComputer Wins the Game, Better Luck Next Time\n") input("Press any key to exit") break diff --git a/65_Nim/rust/Cargo.lock b/65_Nim/rust/Cargo.lock new file mode 100644 index 000000000..fc74a49e2 --- /dev/null +++ b/65_Nim/rust/Cargo.lock @@ -0,0 +1,23 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "nanorand" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "729eb334247daa1803e0a094d0a5c55711b85571179f5ec6e53eccfdf7008958" + +[[package]] +name = "nim" +version = "1.0.0" +dependencies = [ + "nanorand", + "text_io", +] + +[[package]] +name = "text_io" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f0c8eb2ad70c12a6a69508f499b3051c924f4b1cfeae85bfad96e6bc5bba46" diff --git a/66_Number/README.md b/66_Number/README.md index 43d97db3b..2df37b6b7 100644 --- a/66_Number/README.md +++ b/66_Number/README.md @@ -15,4 +15,4 @@ http://www.vintage-basic.net/games.html #### Porting Notes -(please note any difficulties or challenges in porting here) +Contrary to the description, the computer picks *five* random numbers per turn, not one. You are not rewarded based on how close your guess is to one number, but rather to which of these five random numbers (if any) it happens to match exactly. diff --git a/66_Number/rust/Cargo.lock b/66_Number/rust/Cargo.lock new file mode 100644 index 000000000..4fe2abbea --- /dev/null +++ b/66_Number/rust/Cargo.lock @@ -0,0 +1,75 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/68_Orbit/README.md b/68_Orbit/README.md index 9e4e9afdf..be765b557 100644 --- a/68_Orbit/README.md +++ b/68_Orbit/README.md @@ -20,7 +20,7 @@ of orbit < ^ ship ``` -The distance of the bomb from the ship is computed using the law of consines. The law of cosines states: +The distance of the bomb from the ship is computed using the law of cosines. The law of cosines states: ``` D = SQUAREROOT( R**2 + D1**2 - 2*R*D1*COS(A-A1) ) diff --git a/68_Orbit/python/orbit.py b/68_Orbit/python/orbit.py index f0ce1637e..cfca3c93b 100644 --- a/68_Orbit/python/orbit.py +++ b/68_Orbit/python/orbit.py @@ -107,9 +107,7 @@ def play_game() -> bool: rom_angle = random.randint(0, 359) rom_distance = random.randint(100, 300) rom_angular_velocity = random.randint(10, 30) - hour = 0 - while hour < 7: - hour += 1 + for hour in range(1, 8): print() print() print(f"THIS IS HOUR {hour}, AT WHAT ANGLE DO YOU WISH TO SEND") diff --git a/68_Orbit/rust/Cargo.lock b/68_Orbit/rust/Cargo.lock new file mode 100644 index 000000000..4fe2abbea --- /dev/null +++ b/68_Orbit/rust/Cargo.lock @@ -0,0 +1,75 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/69_Pizza/README.md b/69_Pizza/README.md index 56548b645..7f9e3fcf2 100644 --- a/69_Pizza/README.md +++ b/69_Pizza/README.md @@ -15,6 +15,10 @@ As published in Basic Computer Games (1978): Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html +#### Known Bugs + +- The program does no validation of its input, and crashes if you enter coordinates outside the valid range. (Ports may choose to improve on this, for example by repeating the prompt until valid coordinates are given.) + #### Porting Notes (please note any difficulties or challenges in porting here) diff --git a/69_Pizza/python/pizza.py b/69_Pizza/python/pizza.py index e4e36beac..33bd60b6c 100644 --- a/69_Pizza/python/pizza.py +++ b/69_Pizza/python/pizza.py @@ -155,9 +155,7 @@ def main() -> None: player_name = print_instructions() - more_directions = yes_no_prompt("DO YOU NEED MORE DIRECTIONS?") - - if more_directions: + if yes_no_prompt("DO YOU NEED MORE DIRECTIONS?"): print_more_directions(player_name) understand = yes_no_prompt("UNDERSTAND?") @@ -171,8 +169,8 @@ def main() -> None: print("GOOD LUCK!!") print() + num_turns = 5 while True: - num_turns = 5 play_game(num_turns, player_name) print() diff --git a/69_Pizza/rust/Cargo.lock b/69_Pizza/rust/Cargo.lock new file mode 100644 index 000000000..b4abe0dca --- /dev/null +++ b/69_Pizza/rust/Cargo.lock @@ -0,0 +1,23 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "anyhow", + "fastrand", +] diff --git a/69_Pizza/rust/Cargo.toml b/69_Pizza/rust/Cargo.toml new file mode 100644 index 000000000..5224dbab6 --- /dev/null +++ b/69_Pizza/rust/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "rust" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.75" +fastrand = "2.0.1" diff --git a/69_Pizza/rust/README.md b/69_Pizza/rust/README.md new file mode 100644 index 000000000..ce0d38dc2 --- /dev/null +++ b/69_Pizza/rust/README.md @@ -0,0 +1,3 @@ +Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) + +Conversion to [Rust](https://www.rust-lang.org/). diff --git a/69_Pizza/rust/src/main.rs b/69_Pizza/rust/src/main.rs new file mode 100644 index 000000000..4bfe4f226 --- /dev/null +++ b/69_Pizza/rust/src/main.rs @@ -0,0 +1,178 @@ +use anyhow::Result; +use std::io; + +fn print_centered(text: &str) { + println!("{:^72}", text); +} + +fn read_line() -> Result { + let mut input = String::new(); + io::stdin().read_line(&mut input)?; + input.truncate(input.trim_end().len()); + Ok(input) +} + +fn read_number() -> Result { + let line = read_line()?; + let num = line.parse()?; + Ok(num) +} + +fn print_banner() { + print_centered("PIZZA"); + print_centered("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); + println!(); + println!(); + println!(); +} + +fn print_instructions() -> Result { + println!("PIZZA DELIVERY GAME"); + println!(); + println!("WHAT IS YOUR FIRST NAME"); + + let name = read_line()?; + println!("HI, {name}. IN THIS GAME YOU ARE TO TAKE ORDERS"); + println!("FOR PIZZAS. THEN YOU ARE TO TELL A DELIVERY BOY"); + println!("WHERE TO DELIVER THE ORDERED PIZZAS."); + println!(); + println!(); + + print_map(); + + println!("THE OUTPUT IS A MAP OF THE HOMES WHERE"); + println!("YOU ARE TO SEND PIZZAS."); + println!(); + println!("YOUR JOB IS TO GIVE A TRUCK DRIVER"); + println!("THE LOCATION OR COORDINATES OF THE"); + println!("HOME ORDERING THE PIZZA."); + println!(); + + Ok(name) +} + +fn print_ruler() { + println!(" -----1-----2-----3-----4-----"); +} + +fn print_ticks() { + println!("-"); + println!("-"); + println!("-"); + println!("-"); +} + +fn print_street(i: u32) { + let street_number = 3 - i; + let street_name = (street_number + 1).to_string(); + + let mut line = street_name.clone(); + let space = " "; + for customer_idx in 0..4 { + line.push_str(space); + let customer = 4 * street_number + customer_idx; + line.push(char::from_u32(65 + customer).unwrap()); + } + line.push_str(space); + line.push_str(&street_name); + println!("{line}"); +} + +fn print_map() { + println!("MAP OF THE CITY OF HYATTSVILLE"); + println!(); + print_ruler(); + for i in 0..4 { + print_ticks(); + print_street(i); + } + print_ticks(); + print_ruler(); + println!(); +} + +fn yes_no_prompt(text: &str) -> Result> { + println!("{text}"); + let input = read_line()?; + match input.as_str() { + "YES" => Ok(Some(true)), + "NO" => Ok(Some(false)), + _ => Ok(None), + } +} + +fn play_game(turns: u32, player_name: &str) -> Result<()> { + for _ in 0..turns { + let customer = fastrand::char('A'..='P'); + println!("HELLO {player_name}'S PIZZA. THIS IS {customer}."); + println!(" PLEASE SEND A PIZZA."); + loop { + println!(" DRIVER TO {player_name}: WHERE DOES {customer} LIVE"); + + let x = read_number()?; + let y = read_number()?; + + let input = x - 1 + (y - 1) * 4; + match char::from_u32(65 + input) { + Some(c) if c == customer => { + println!("HELLO {player_name}. THIS IS {customer}, THANKS FOR THE PIZZA."); + break; + } + Some(c @ 'A'..='P') => { + println!("THIS IS {c}. I DID NOT ORDER A PIZZA."); + println!("I LIVE AT {x},{y}"); + } + // this is out of bounds in the original game + _ => (), + } + } + } + + Ok(()) +} + +fn main() -> Result<()> { + print_banner(); + let player_name = print_instructions()?; + let more_directions = loop { + if let Some(x) = yes_no_prompt("DO YOU NEED MORE DIRECTIONS")? { + break x; + } else { + println!("'YES' OR 'NO' PLEASE, NOW THEN,"); + } + }; + + if more_directions { + println!(); + println!("SOMEBODY WILL ASK FOR A PIZZA TO BE"); + println!("DELIVERED. THEN A DELIVERY BOY WILL"); + println!("ASK YOU FOR THE LOCATION."); + println!(" EXAMPLE:"); + println!("THIS IS J. PLEASE SEND A PIZZA."); + println!("DRIVER TO {player_name}. WHERE DOES J LIVE?"); + println!("YOUR ANSWER WOULD BE 2,3"); + println!(); + + if let Some(true) = yes_no_prompt("UNDERSTAND")? { + println!("GOOD. YOU ARE NOW READY TO START TAKING ORDERS."); + println!(); + println!("GOOD LUCK!!"); + println!(); + } else { + println!("THIS JOB IS DEFINITELY TOO DIFFICULT FOR YOU. THANKS ANYWAY"); + return Ok(()); + } + } + + loop { + play_game(5, &player_name)?; + println!(); + + if let Some(false) | None = yes_no_prompt("DO YOU WANT TO DELIVER MORE PIZZAS")? { + println!(); + println!("O.K. {player_name}, SEE YOU LATER!"); + println!(); + return Ok(()); + } + } +} diff --git a/70_Poetry/README.md b/70_Poetry/README.md index 7684ebecc..be21f8bee 100644 --- a/70_Poetry/README.md +++ b/70_Poetry/README.md @@ -30,4 +30,6 @@ http://www.vintage-basic.net/games.html #### Porting Notes -(please note any difficulties or challenges in porting here) +- The program begins by switching on `I`, which has not been initialized. We should probably initialize this to 0, though this means the output always begins with the phrase "midnight dreary". + +- Though the program contains an END statement (line 999), it is unreachable. The program continues to generate output until it is forcibly interrupted. diff --git a/70_Poetry/python/poetry.py b/70_Poetry/python/poetry.py index 302126301..f261db375 100644 --- a/70_Poetry/python/poetry.py +++ b/70_Poetry/python/poetry.py @@ -49,7 +49,7 @@ def process_phrase_2(state: State) -> None: ] words, u_modifier = line_2_options[state.i] state.line += words - if not (u_modifier is None): + if u_modifier is not None: state.u = u_modifier diff --git a/71_Poker/README.md b/71_Poker/README.md index a80820598..08bc04c63 100644 --- a/71_Poker/README.md +++ b/71_Poker/README.md @@ -15,6 +15,17 @@ As published in Basic Computer Games (1978): Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html +#### Known Bugs + +- If you bet more than the computer has, it will still see you, resulting in a negative balance. (To handle this properly, the computer would need to go "all in" and reduce your bet to an amount it can match; or else lose the game, which is what happens to the human player in the same situation.) + +- If you are low on cash and sell your watch, then make a bet much smaller than the amount you just gained from the watch, it sometimes nonetheless tells you you "blew your wad" and ends the game. + +- When the watch is sold (in either direction), the buyer does not actually lose any money. + +- The code in the program about selling your tie tack is unreachable due to a logic bug. + + #### Porting Notes (please note any difficulties or challenges in porting here) diff --git a/71_Poker/python/README.md b/71_Poker/python/README.md index 781945ec4..bc58f0839 100644 --- a/71_Poker/python/README.md +++ b/71_Poker/python/README.md @@ -1,3 +1,30 @@ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) -Conversion to [Python](https://www.python.org/about/) +Conversion to [Python](https://www.python.org/about/) by Alex Conconi, 2026. + +--- + +### Python porting notes + +#### Known BASIC bugs — fixed + +- **Tie tack unreachable** (BASIC line 3970): The condition + `IF O/3<>INT(O/3) THEN 4090` is logically inverted. It checks if the + tie tack has *not* been sold, and if so, incorrectly jumps to the + "Bust" message. This makes the tie tack sale permanently unreachable + even if the player still owns it. The Python port fixes this to restore + the original intended experience where both assets can be sold. + +#### Known BASIC bugs/quirks — kept faithful + +- **$50 buy-back materialises from nowhere** (BASIC lines 3570/3640): + When the player buys back a pawned item, `C=C+50` adds $50 to the + dealer's stack without deducting from the player. Kept as-is — it + models the dealer/pawnbroker liquidating the physical item. + +- **Yes/No prompts accept anything** (e.g. BASIC lines 3550, 3880): + `LEFT$(J$,1)="Y"` treats every non-"Y" input as "no" with no + re-prompt. The Python port normalises all yes/no prompts to a single + `_read_yes_no` that retries on invalid input (matching the stricter + "Do you wish to continue?" prompt at lines 4120–4150). + diff --git a/71_Poker/python/cards/__init__.py b/71_Poker/python/cards/__init__.py new file mode 100644 index 000000000..956089deb --- /dev/null +++ b/71_Poker/python/cards/__init__.py @@ -0,0 +1,11 @@ +"""Cards package initialization.""" + +from .card import Card, CardRank, CardSuit +from .deck import Deck + +__all__ = [ + "Card", + "CardRank", + "CardSuit", + "Deck", +] diff --git a/71_Poker/python/cards/card.py b/71_Poker/python/cards/card.py new file mode 100644 index 000000000..2491c3430 --- /dev/null +++ b/71_Poker/python/cards/card.py @@ -0,0 +1,50 @@ +"""Card primitives: Suits, Ranks, and the Card class.""" + +from dataclasses import dataclass +from enum import Enum, IntEnum + + +class CardSuit(Enum): + """Enumeration of card suits.""" + + CLUBS = 0 + DIAMONDS = 1 + HEARTS = 2 + SPADES = 3 + + def __str__(self) -> str: + return self.name.capitalize() + + +class CardRank(IntEnum): + """Card ranks from Two (0) to Ace (12).""" + + TWO = 0 + THREE = 1 + FOUR = 2 + FIVE = 3 + SIX = 4 + SEVEN = 5 + EIGHT = 6 + NINE = 7 + TEN = 8 + JACK = 9 + QUEEN = 10 + KING = 11 + ACE = 12 + + def __str__(self) -> str: + if self.value <= 8: # TWO(0) through TEN(8) + return f" {str(self.value + 2)} " + return ("Jack", "Queen", "King", "Ace")[self.value - 9] + + +@dataclass(frozen=True) +class Card: + """A single playing card.""" + + suit: CardSuit + rank: CardRank + + def __str__(self) -> str: + return f"{self.rank} of {self.suit}" diff --git a/71_Poker/python/cards/deck.py b/71_Poker/python/cards/deck.py new file mode 100644 index 000000000..862d33e42 --- /dev/null +++ b/71_Poker/python/cards/deck.py @@ -0,0 +1,17 @@ +"""Deck implementation.""" + +import random + +from .card import Card, CardRank, CardSuit + + +class Deck: # pylint: disable=too-few-public-methods + """Standard 52-card deck, shuffled on creation.""" + + def __init__(self) -> None: + self._cards = [Card(suit, rank) for suit in CardSuit for rank in CardRank] + random.shuffle(self._cards) + + def deal(self) -> Card: + """Remove and return the top card.""" + return self._cards.pop() diff --git a/71_Poker/python/game.py b/71_Poker/python/game.py new file mode 100644 index 000000000..be848ec36 --- /dev/null +++ b/71_Poker/python/game.py @@ -0,0 +1,401 @@ +"""Poker game logic — port of the 1978 Creative Computing BASIC original.""" + +import random +from typing import NoReturn + +from cards import Deck +from players import Dealer, Human, Player +from pokerhand import Hand + + +class GameOver(Exception): + """Raised when the game ends (either side busted, player quits, or walks away).""" + + +class PokerGame: # pylint: disable=too-few-public-methods + """Five-card draw poker against the house.""" + + INITIAL_MONEY: int = 200 + ANTE: int = 5 + + human: Human + dealer: Dealer + deck: Deck + pot: int + + def __init__(self) -> None: + """Initialise game state and allocate both sides' starting stack.""" + self.human = Human(PokerGame.INITIAL_MONEY) + self.dealer = Dealer(PokerGame.INITIAL_MONEY) + self.pot = 0 + + # ------------------------------------------------------------------ + # Intro and main loop + # ------------------------------------------------------------------ + + def run(self) -> None: + """Execute the primary game loop until a GameOver exception occurs.""" + self._display_intro() + while True: + self._play_round() + + # ------------------------------------------------------------------ + # Round + # ------------------------------------------------------------------ + + def _play_round(self) -> None: + """Deal one complete round: ante, bet, draw, bet, showdown.""" + print() + + # Reset per-round state + self.deck = Deck() + + # --- Ante --- + if self.dealer.money <= self.ANTE: + self._dealer_goes_bust() + + print(f"The ante is ${self.ANTE}. I will deal:\n") + + if self.human.money <= self.ANTE: + self._human_try_raise_funds(self.ANTE) + + self.pot += self.ANTE * 2 + self.human.pay_ante(self.ANTE) + self.dealer.pay_ante(self.ANTE) + + # --- Deal --- + self.human.receive_new_hand(Hand([self.deck.deal() for _ in range(5)])) + self.dealer.receive_new_hand(Hand([self.deck.deal() for _ in range(5)])) + + print("Your hand:") + print(self.human.hand) + print() + + # --- Opening bet --- + dealer_action = self.dealer.get_opening_action() + if self.dealer.money < self.dealer.bet: + self._dealer_try_raise_funds() + + if dealer_action == Player.Action.CHECK: + print("I check.") + else: + print(f"I'll open with $ {self.dealer.bet}") + + if not self._conduct_betting_round(post_draw=False): + return + + # --- Draw --- + print() + self._player_discard_draw() + draw_count = self.dealer.discard_and_draw(self.deck) + print( + f"\nI am taking {draw_count} card{'s' if draw_count != 1 else ''}" + ) + + # --- Post-draw bet --- + self.dealer.decide_postdraw_bet() + if not self._conduct_betting_round(post_draw=True): + return + + # --- Showdown --- + self._showdown() + + # ------------------------------------------------------------------ + # Player draw + # ------------------------------------------------------------------ + + def _player_discard_draw(self) -> None: + """Ask the player how many cards to replace, then deal replacements.""" + count = self._read_int( + "Now we draw -- how many cards do you want? ", + low=0, + high=3, + too_high_msg="You can't draw more than three cards.", + ) + + if count == 0: + return + + print("What are their numbers:") + for _ in range(count): + pos = self._read_int("", low=1, high=5) + self.human.replace_card(pos - 1, self.deck.deal()) + + print("Your new hand:") + print(self.human.hand) + + # ------------------------------------------------------------------ + # Betting round + # ------------------------------------------------------------------ + + def _conduct_betting_round(self, post_draw: bool) -> bool: + """ + Executes a betting round. + Returns True if the betting phase completed (hand continues). + Returns False if a player folded (hand ends). + """ + while True: + human_action = self._get_human_action() + + if human_action == Player.Action.FOLD: + # Human fold always ends betting loop (hand ends) + self._settle_bets() + self._award_pot(dealer_wins=True) + return False + + if human_action == Player.Action.CALL: + # Human call always ends betting loop (proceed to next phase) + self._settle_bets() + return True + + if human_action == Player.Action.CHECK: + # Pre-draw: human check ends human's turn. + # Post-draw: dealer gets a chance to bet/check back. + if not post_draw or self._handle_human_post_draw_check(): + return True + # If dealer bet, we fall through and loop for human response + + elif human_action == Player.Action.RAISE: + if self._handle_human_raise(): + return True + # If dealer re-raised, we fall through and loop for human response + + else: + raise ValueError(f"Unknown player action: {human_action}") + + def _handle_human_post_draw_check(self) -> bool: + """Process dealer response to human check. Returns True if phase ends.""" + dealer_action = self.dealer.get_check_response() + if self.dealer.money < self.dealer.bet: + self._dealer_try_raise_funds() + + if dealer_action == Player.Action.CHECK: + print("I'll check.") + self._settle_bets() + return True + + print(f"I'll bet $ {self.dealer.bet}") + return False + + def _handle_human_raise(self) -> bool: + """Process dealer response to human raise. Returns True if phase ends.""" + dealer_action = self.dealer.get_raise_response(self.human.bet) + if self.dealer.money < self.dealer.bet: + self._dealer_try_raise_funds() + + if dealer_action == Player.Action.FOLD: + print("I fold.") + self._settle_bets() + self._award_pot(dealer_wins=False) + return True # Round ends + + if dealer_action == Player.Action.CALL: + print("I'll see you.") + self._settle_bets() + return True + + if dealer_action == Player.Action.RAISE: + raise_amount = self.dealer.bet - self.human.bet + print(f"I'll see you, and raise you {raise_amount}") + return False + + raise ValueError(f"Unknown dealer action: {dealer_action}") + + def _get_human_action(self) -> Player.Action: + """Prompt user for a bet and return the resulting Action.""" + while True: + print() + raw = input("What is your bet? ").strip() + + try: + bet = float(raw) + except ValueError: + continue + + # Fractional bet: only .5 is valid, and only as a check + if bet != int(bet): + if self.dealer.bet == 0 and self.human.bet == 0 and bet == 0.5: + return Player.Action.CHECK + print("No small change, please.") + continue + + amount = int(bet) + + if amount == 0: + return Player.Action.FOLD + + if self.human.bet + amount > self.human.money: + self._human_try_raise_funds(self.human.bet + amount) + continue + + if self.human.bet + amount < self.dealer.bet: + print("If you can't see my bet, then fold.") + continue + + self.human.bet += amount + + if self.human.bet == self.dealer.bet: + return Player.Action.CALL + return Player.Action.RAISE + + def _dealer_try_raise_funds(self) -> None: + """Attempt to raise funds for the dealer. Raises GameOver if failed.""" + if not self.human.has_watch: + if self._read_yes_no( + "Would you like to buy back your watch for $50? " + ): + self.dealer.money += 50 + self.human.has_watch = True + if self.dealer.money >= self.dealer.bet: + return + + if not self.human.has_tack: + if self._read_yes_no( + "Would you like to buy back your tie tack for $50? " + ): + self.dealer.money += 50 + self.human.has_tack = True + if self.dealer.money >= self.dealer.bet: + return + + self._dealer_goes_bust() + + def _dealer_goes_bust(self) -> NoReturn: + """Inform the player and terminate the game.""" + print("I'm busted. Congratulations!") + raise GameOver + + def _human_try_raise_funds(self, target_money: int) -> None: + """Offer the player's assets for cash. Raises GameOver if short of target.""" + print("\nYou can't bet with what you haven't got.") + + if self.human.has_watch: + if self._read_yes_no("Would you like to sell your watch? "): + if self._chance(3): + print("That's a pretty crummy watch - I'll give you $25.") + self.human.money += 25 + else: + print("I'll give you $75 for it.") + self.human.money += 75 + self.human.has_watch = False + if self.human.money >= target_money: + return + + if self.human.has_tack: + if self._read_yes_no("Will you part with that diamond tie tack? "): + if self._chance(4): + print("It's paste. $25.") + self.human.money += 25 + else: + print("You are now $100 richer.") + self.human.money += 100 + self.human.has_tack = False + if self.human.money >= target_money: + return + + print("Your wad is shot. So long, sucker!") + raise GameOver + + def _settle_bets(self) -> None: + """Move both sides' committed chips into the pot.""" + self.pot += self.human.settle_bet() + self.pot += self.dealer.settle_bet() + + # ------------------------------------------------------------------ + # Showdown + # ------------------------------------------------------------------ + + def _showdown(self) -> None: + """Reveal hands, compare scores, and award the pot.""" + print("\nNow we compare hands:") + + print("My hand:") + print(self.dealer.hand) + dealer_hand_eval = self.dealer.hand.evaluate() + + player_hand_eval = self.human.hand.evaluate() + + print(f"\nYou have {player_hand_eval}") + print(f"And I have {dealer_hand_eval}") + + if dealer_hand_eval == player_hand_eval: + print("The hand is drawn.") + print(f"All ${self.pot} remains in the pot.") + else: + self._award_pot(dealer_wins=dealer_hand_eval > player_hand_eval) + + def _award_pot(self, dealer_wins: bool) -> None: + """Announce the winner, award the pot, and prompt to continue.""" + if dealer_wins: + print("I win.") + self.dealer.win_pot(self.pot) + else: + print("You win.") + self.human.win_pot(self.pot) + print( + f"Now I have $ {self.dealer.money} and you have $ {self.human.money}" + ) + if not self._read_yes_no("Do you wish to continue? "): + raise GameOver + self.pot = 0 # Reset pot after awarding it + + # ------------------------------------------------------------------ + # Helpers + # ------------------------------------------------------------------ + + # pylint: disable=too-many-arguments + def _read_int( + self, + prompt: str, + *, + low: int | None = None, + high: int | None = None, + too_low_msg: str | None = None, + too_high_msg: str | None = None, + ) -> int: + """Read and validate an integer within an optional range.""" + while True: + raw = input(prompt).strip() + if not raw: + continue + try: + val = int(raw) + except ValueError: + continue + + if low is not None and val < low: + if too_low_msg: + print(too_low_msg) + continue + + if high is not None and val > high: + if too_high_msg: + print(too_high_msg) + continue + + return val + + def _read_yes_no(self, prompt: str) -> bool: + """Read yes/no response. Returns True for yes, False for no.""" + while True: + response = input(prompt).strip().lower() + if response in ["y", "yes"]: + return True + if response in ["n", "no"]: + return False + print("Answer yes or no, please.") + + def _chance(self, n: int) -> bool: + """Return True with probability n/10.""" + return random.random() < n / 10 + + def _display_intro(self) -> None: + """Print the welcome banner.""" + print(" POKER") + print(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n") + print( + f"Welcome to the casino. We each have ${PokerGame.INITIAL_MONEY}." + ) + print("I will open the betting before the draw; you open after.") + print("To fold bet 0; to check bet .5.") + print("Enough talk -- let's get down to business.") diff --git a/71_Poker/python/players/__init__.py b/71_Poker/python/players/__init__.py new file mode 100644 index 000000000..866e40a52 --- /dev/null +++ b/71_Poker/python/players/__init__.py @@ -0,0 +1,7 @@ +"""Players package initialization.""" + +from .dealer import Dealer +from .human import Human +from .player import Player + +__all__ = ["Player", "Human", "Dealer"] diff --git a/71_Poker/python/players/dealer.py b/71_Poker/python/players/dealer.py new file mode 100644 index 000000000..71068f058 --- /dev/null +++ b/71_Poker/python/players/dealer.py @@ -0,0 +1,178 @@ +"""Dealer state and decision-making logic.""" + +import random +from enum import Enum, auto + +from cards import CardRank, Deck +from pokerhand import Hand, HandEvaluation, HandRank + +from .player import Player + + +class Dealer(Player): + """Dealer state: bankroll, strategy, and per-round betting.""" + + class Strategy(Enum): + """The dealer's play style for the current round, set during the opening.""" + + NORMAL = auto() + WEAK = auto() + BLUFFING = auto() + + strategy: Strategy + bet_base: int + was_bluffing: bool + hand_is_weak: bool + bluff_discard_count: int | None + + def receive_new_hand(self, hand: Hand) -> None: + """Reset per-round state and receive a new hand.""" + super().receive_new_hand(hand) + self.strategy = Dealer.Strategy.NORMAL + self.bet_base = 0 + self.was_bluffing = False + self.hand_is_weak = False + self.bluff_discard_count = None + + def decide_postdraw_bet(self) -> None: + """Update bluffing state, evaluate hand and set betting tier.""" + self.was_bluffing = self.strategy == Dealer.Strategy.BLUFFING + + result = self.hand.evaluate() + rank = result.hand_rank + self.bet = 0 + self.hand_is_weak = self._is_hand_weak(result, is_redeal=True) + + if self.was_bluffing: + self.bet_base = 28 + elif self.hand_is_weak: + self.bet_base = 1 + elif rank < HandRank.THREE_OF_A_KIND: + self.bet_base = 2 + if self._chance(1): + self.bet_base = 19 + elif rank <= HandRank.FLUSH: # THREE_OF_A_KIND, STRAIGHT, FLUSH + self.bet_base = 19 + if self._chance(1): + self.bet_base = 11 + else: # FULL_HOUSE, FOUR_OF_A_KIND + self.bet_base = 2 + + def _is_hand_weak(self, result: HandEvaluation, is_redeal: bool) -> bool: + """Check whether the hand qualifies as 'weak'.""" + rank = result.hand_rank + if rank < HandRank.PAIR: + # Schmaltz is always weak; partial straight only on re-deal + return rank != HandRank.PARTIAL_STRAIGHT or is_redeal + if rank <= HandRank.TWO_PAIR: + # Pair/two-pair is weak only when high card is Eight or lower + return result.high_card.rank <= CardRank.EIGHT + return False + + def get_opening_action(self) -> Player.Action: + """Evaluate hand and set the dealer's pre-draw opening bet and strategy.""" + result = self.hand.evaluate() + + if self._is_hand_weak(result, is_redeal=False): + strat, base, discard, action = self._decide_weak_opening() + else: + strat, base, discard, action = self._decide_normal_opening(result) + + self.strategy = strat + self.bet_base = base + self.bluff_discard_count = discard + + if action == Player.Action.RAISE: + self.bet = self.bet_base + random.randint(0, 9) + else: + self.bet = 0 + + return action + + def _decide_weak_opening( + self, + ) -> tuple[Strategy, int, int | None, Player.Action]: + """Pure logic for weak-hand opening decisions.""" + if self._chance(8): + return Dealer.Strategy.BLUFFING, 23, 2, Player.Action.RAISE + if self._chance(8): + return Dealer.Strategy.BLUFFING, 23, 1, Player.Action.RAISE + if self._chance(9): + return Dealer.Strategy.WEAK, 1, None, Player.Action.CHECK + return Dealer.Strategy.BLUFFING, 23, 0, Player.Action.RAISE + + def _decide_normal_opening( + self, result: HandEvaluation + ) -> tuple[Strategy, int, int | None, Player.Action]: + """Pure logic for normal-hand opening decisions.""" + rank = result.hand_rank + if rank < HandRank.THREE_OF_A_KIND: + if self._chance(8): + return Dealer.Strategy.NORMAL, 0, None, Player.Action.CHECK + return Dealer.Strategy.BLUFFING, 23, None, Player.Action.RAISE + + # Strong hand (THREE_OF_A_KIND and above) + if rank > HandRank.FULL_HOUSE: # FOUR_OF_A_KIND + base = 2 if self._chance(9) else 35 + else: + base = 35 + return Dealer.Strategy.NORMAL, base, None, Player.Action.RAISE + + def discard_and_draw(self, deck: Deck) -> int: + """Evaluate hand and replace the dealer's discard cards.""" + if self.bluff_discard_count is not None: + count = self.bluff_discard_count + if count == 0: + return 0 + ranked = sorted(range(5), key=lambda i: self.hand[i].rank.value) + for i in ranked[:count]: + self.replace_card(i, deck.deal()) + return count + + result = self.hand.evaluate() + for i in result.discard_indices: + self.replace_card(i, deck.deal()) + return len(result.discard_indices) + + def get_check_response(self) -> Player.Action: + """Respond to a player's check. Returns the dealer's action.""" + if not self.was_bluffing and self.hand_is_weak: + return Player.Action.CHECK + + self.bet = self.bet_base + random.randint(0, 9) + return Player.Action.RAISE + + def get_raise_response(self, player_paid: int) -> Player.Action: + """Respond to a player's raise. Returns the dealer's action.""" + if self.bet_base != 1: + if player_paid > 3 * self.bet_base: + if self.bet_base == 2 and self._chance(1): + return self._raise(player_paid) + return self._call(player_paid) + return self._raise(player_paid) + + if player_paid > 5: + return Player.Action.FOLD + + if player_paid > 3: + return self._call(player_paid) + + return self._raise(player_paid) + + def _call(self, player_paid: int) -> Player.Action: + """Dealer calls the player's bet.""" + self.bet = player_paid + return Player.Action.CALL + + def _raise(self, player_paid: int) -> Player.Action: + """Raise the player's bet.""" + raise_amount = player_paid - self.bet + random.randint(0, 9) + if raise_amount <= 0: + return self._call(player_paid) + + self.bet = player_paid + raise_amount + return Player.Action.RAISE + + def _chance(self, n: int) -> bool: + """Return True with probability n/10.""" + return random.random() < n / 10 diff --git a/71_Poker/python/players/human.py b/71_Poker/python/players/human.py new file mode 100644 index 000000000..b496e1ceb --- /dev/null +++ b/71_Poker/python/players/human.py @@ -0,0 +1,21 @@ +"""Human player state and behavior.""" + +from .player import Player + + +class Human(Player): + """ + The human player in the game. + + Note: As a state container, this class has no logic methods; + decision-making is provided by the user via the game controller. + """ + + has_watch: bool + has_tack: bool + + def __init__(self, money: int) -> None: + """Initialize the human player with starting assets.""" + super().__init__(money) + self.has_watch = True + self.has_tack = True diff --git a/71_Poker/python/players/player.py b/71_Poker/python/players/player.py new file mode 100644 index 000000000..e5925641d --- /dev/null +++ b/71_Poker/python/players/player.py @@ -0,0 +1,51 @@ +"""Base Player class for both Human and Dealer.""" + +from enum import Enum, auto + +from cards import Card +from pokerhand import Hand + + +class Player: + """Base class representing a participant in the poker game.""" + + money: int + bet: int + hand: Hand + + class Action(Enum): + """Standard actions a player can take.""" + + FOLD = auto() + CHECK = auto() + CALL = auto() + RAISE = auto() + + def __init__(self, money: int) -> None: + """Initialize the player with a starting bankroll.""" + self.money = money + self.bet = 0 + + def receive_new_hand(self, hand: Hand) -> None: + """Reset per-round state and receive a new hand.""" + self.bet = 0 + self.hand = hand + + def pay_ante(self, amount: int) -> None: + """Pay the ante.""" + self.money -= amount + + def win_pot(self, amount: int) -> None: + """Add the pot to the player's money.""" + self.money += amount + + def settle_bet(self) -> int: + """Move the current bet out of the player's money. Returns the bet amount.""" + amount = self.bet + self.money -= amount + self.bet = 0 + return amount + + def replace_card(self, index: int, card: Card) -> None: + """Replace a card in the player's hand.""" + self.hand[index] = card diff --git a/71_Poker/python/poker.py b/71_Poker/python/poker.py new file mode 100644 index 000000000..f027432bf --- /dev/null +++ b/71_Poker/python/poker.py @@ -0,0 +1,38 @@ +""" +From: BASIC Computer Games (1978) +Edited by David H. Ahl + +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. + + +Python port by Alex Conconi, 2026. +""" + +from game import GameOver, PokerGame + + +def main() -> None: + """Entry point for the poker game application.""" + game = PokerGame() + try: + game.run() + except GameOver: + pass + + +if __name__ == "__main__": + main() diff --git a/71_Poker/python/pokerhand/__init__.py b/71_Poker/python/pokerhand/__init__.py new file mode 100644 index 000000000..3de94bf24 --- /dev/null +++ b/71_Poker/python/pokerhand/__init__.py @@ -0,0 +1,7 @@ +"""Hand package initialization.""" + +from .evaluation import HandEvaluation +from .hand import Hand +from .rank import HandRank + +__all__ = ["Hand", "HandEvaluation", "HandRank"] diff --git a/71_Poker/python/pokerhand/evaluation.py b/71_Poker/python/pokerhand/evaluation.py new file mode 100644 index 000000000..6c4693274 --- /dev/null +++ b/71_Poker/python/pokerhand/evaluation.py @@ -0,0 +1,224 @@ +"""Hand evaluation logic.""" + +import functools +from collections import Counter +from collections.abc import Callable +from typing import TYPE_CHECKING + +from cards.card import Card, CardRank + +from .rank import HandRank + +if TYPE_CHECKING: + from .hand import Hand + + +@functools.total_ordering +class HandEvaluation: + """ + Evaluator and Result container for a poker hand. + + When initialized with a Hand, it immediately performs the evaluation + and stores the resulting rank, high card, and recommended discards. + """ + + hand_rank: HandRank + high_card: Card + discard_indices: list[int] + _ctx: "_EvaluationContext" + + def __init__(self, hand: "Hand"): + """Perform evaluation of the provided hand.""" + self.discard_indices = [] + self._evaluate(hand) + + def __str__(self) -> str: + rank = self.hand_rank + card = self.high_card + + if rank in (HandRank.SCHMALTZ, HandRank.PARTIAL_STRAIGHT): + return f"{rank}, {card.rank} high" + if rank in ( + HandRank.PAIR, + HandRank.TWO_PAIR, + HandRank.THREE_OF_A_KIND, + HandRank.FULL_HOUSE, + HandRank.FOUR_OF_A_KIND, + ): + return f"{rank} {card.rank}'s" + if rank == HandRank.STRAIGHT: + return f"{rank}, {card.rank} high" + if rank == HandRank.FLUSH: + return f"{rank} {card.suit}" + return f"{rank} {card.rank}" + + def __lt__(self, other: "HandEvaluation") -> bool: + return (self.hand_rank, self.high_card.rank.value) < ( + other.hand_rank, + other.high_card.rank.value, + ) + + def __eq__(self, other: object) -> bool: + # this object could be compared to any other object with == + if not isinstance(other, HandEvaluation): + return NotImplemented + return (self.hand_rank, self.high_card.rank.value) == ( + other.hand_rank, + other.high_card.rank.value, + ) + + def __hash__(self) -> int: + return hash((self.hand_rank, self.high_card.rank.value)) + + class _EvaluationContext: # pylint: disable=too-few-public-methods + """Intermediate calculations for hand evaluation.""" + + def __init__(self, hand: "Hand"): + self.cards = hand.cards + self.rank_indexed = sorted( + enumerate(self.cards), key=lambda item: item[1].rank.value + ) + + # Frequency analysis + rank_counts = Counter(c.rank for c in self.cards) + sorted_groups = sorted( + rank_counts.items(), + key=lambda rc: (rc[1], rc[0].value), + reverse=True, + ) + self.freq_pattern = tuple(count for _, count in sorted_groups) + self.ranks_by_freq = [rank for rank, _ in sorted_groups] + self.sorted_ranks: list[CardRank] = [ + c.rank for _, c in self.rank_indexed + ] + + def _evaluate(self, hand: "Hand") -> None: + """Internal algorithm to determine hand strength.""" + self._ctx = self._EvaluationContext(hand) + + checks: list[Callable[[], bool]] = [ + self._check_four_of_a_kind, + self._check_full_house, + self._check_flush, + self._check_straight, + self._check_three_of_a_kind, + self._check_two_pair, + self._check_pair, + self._check_partial_straight, + self._check_schmaltz, + ] + + for check in checks: + if check(): + break + + def _check_four_of_a_kind(self) -> bool: + if self._ctx.freq_pattern != (4, 1): + return False + self.hand_rank = HandRank.FOUR_OF_A_KIND + self.high_card = next( + c + for _, c in reversed(self._ctx.rank_indexed) + if c.rank == self._ctx.ranks_by_freq[0] + ) + return True + + def _check_full_house(self) -> bool: + if self._ctx.freq_pattern != (3, 2): + return False + self.hand_rank = HandRank.FULL_HOUSE + self.high_card = next( + c + for _, c in reversed(self._ctx.rank_indexed) + if c.rank == self._ctx.ranks_by_freq[0] + ) + return True + + def _check_flush(self) -> bool: + if len({c.suit for c in self._ctx.cards}) != 1: + return False + self.hand_rank = HandRank.FLUSH + self.high_card = self._ctx.rank_indexed[4][1] + return True + + def _check_straight(self) -> bool: + if ( + len(set(self._ctx.sorted_ranks)) == 5 + and self._ctx.sorted_ranks[4].value + - self._ctx.sorted_ranks[0].value + == 4 + ): + self.hand_rank = HandRank.STRAIGHT + self.high_card = self._ctx.rank_indexed[4][1] + return True + return False + + def _check_three_of_a_kind(self) -> bool: + if self._ctx.freq_pattern != (3, 1, 1): + return False + self.hand_rank = HandRank.THREE_OF_A_KIND + self.high_card = next( + c + for _, c in reversed(self._ctx.rank_indexed) + if c.rank == self._ctx.ranks_by_freq[0] + ) + keep_rank = self._ctx.ranks_by_freq[0] + self.discard_indices = [ + i for i, c in enumerate(self._ctx.cards) if c.rank != keep_rank + ] + return True + + def _check_two_pair(self) -> bool: + if self._ctx.freq_pattern != (2, 2, 1): + return False + self.hand_rank = HandRank.TWO_PAIR + self.high_card = next( + c + for _, c in reversed(self._ctx.rank_indexed) + if c.rank == self._ctx.ranks_by_freq[0] + ) + keep = {self._ctx.ranks_by_freq[0], self._ctx.ranks_by_freq[1]} + self.discard_indices = [ + i for i, c in enumerate(self._ctx.cards) if c.rank not in keep + ] + return True + + def _check_pair(self) -> bool: + if self._ctx.freq_pattern != (2, 1, 1, 1): + return False + self.hand_rank = HandRank.PAIR + self.high_card = next( + c + for _, c in reversed(self._ctx.rank_indexed) + if c.rank == self._ctx.ranks_by_freq[0] + ) + keep_rank = self._ctx.ranks_by_freq[0] + self.discard_indices = [ + i for i, c in enumerate(self._ctx.cards) if c.rank != keep_rank + ] + return True + + def _check_partial_straight(self) -> bool: + if ( + self._ctx.sorted_ranks[3].value - self._ctx.sorted_ranks[0].value + == 3 + ): + self.hand_rank = HandRank.PARTIAL_STRAIGHT + self.high_card = self._ctx.rank_indexed[3][1] + self.discard_indices = [self._ctx.rank_indexed[4][0]] + return True + if ( + self._ctx.sorted_ranks[4].value - self._ctx.sorted_ranks[1].value + == 3 + ): + self.hand_rank = HandRank.PARTIAL_STRAIGHT + self.high_card = self._ctx.rank_indexed[4][1] + self.discard_indices = [self._ctx.rank_indexed[0][0]] + return True + return False + + def _check_schmaltz(self) -> bool: + self.hand_rank = HandRank.SCHMALTZ + self.high_card = self._ctx.rank_indexed[4][1] + self.discard_indices = [idx for idx, _ in self._ctx.rank_indexed[0:3]] + return True diff --git a/71_Poker/python/pokerhand/hand.py b/71_Poker/python/pokerhand/hand.py new file mode 100644 index 000000000..aef598ace --- /dev/null +++ b/71_Poker/python/pokerhand/hand.py @@ -0,0 +1,39 @@ +"""Hand implementation.""" + +from collections.abc import Iterator + +from cards.card import Card + +from .evaluation import HandEvaluation + + +class Hand: + """A 5-card poker hand that acts as a simple data container.""" + + def __init__(self, cards: list[Card]): + if len(cards) != 5: + raise ValueError("Hand must have exactly 5 cards") + self.cards = list(cards) + + def evaluate(self) -> HandEvaluation: + """Factory method to return a hand evaluation.""" + return HandEvaluation(self) + + def __len__(self) -> int: + return len(self.cards) + + def __iter__(self) -> Iterator[Card]: + return iter(self.cards) + + def __getitem__(self, i: int) -> Card: + return self.cards[i] + + def __setitem__(self, i: int, card: Card) -> None: + self.cards[i] = card + + def __str__(self) -> str: + """Return a multi-line formatted description of the hand.""" + entries = [f"{i} -- {card}" for i, card in enumerate(self.cards, 1)] + lines = [f" {entries[i]:<32}{entries[i + 1]}" for i in range(0, 4, 2)] + lines.append(f" {entries[4]}") + return "\n".join(lines) diff --git a/71_Poker/python/pokerhand/rank.py b/71_Poker/python/pokerhand/rank.py new file mode 100644 index 000000000..181cec0a1 --- /dev/null +++ b/71_Poker/python/pokerhand/rank.py @@ -0,0 +1,31 @@ +"""Hand rank enumeration.""" + +from enum import IntEnum, auto + + +class HandRank(IntEnum): + """Poker hand ranks ordered from weakest to strongest.""" + + SCHMALTZ = auto() + PARTIAL_STRAIGHT = auto() + PAIR = auto() + TWO_PAIR = auto() + THREE_OF_A_KIND = auto() + STRAIGHT = auto() + FLUSH = auto() + FULL_HOUSE = auto() + FOUR_OF_A_KIND = auto() + + def __str__(self) -> str: + """Return the display name of the rank.""" + return { + HandRank.SCHMALTZ: "Schmaltz", + HandRank.PARTIAL_STRAIGHT: "Schmaltz", + HandRank.PAIR: "a pair of", + HandRank.TWO_PAIR: "Two pair", + HandRank.THREE_OF_A_KIND: "Three", + HandRank.STRAIGHT: "Straight", + HandRank.FLUSH: "a flush in", + HandRank.FULL_HOUSE: "Full house", + HandRank.FOUR_OF_A_KIND: "Four", + }[self] diff --git a/72_Queen/java/Queen.java b/72_Queen/java/Queen.java new file mode 100644 index 000000000..7dfb6353c --- /dev/null +++ b/72_Queen/java/Queen.java @@ -0,0 +1,370 @@ +import java.util.List; +import java.util.Optional; +import java.util.Scanner; + +/** + * QUEEN + *

+ * Converted from BASIC to Java by Aldrin Misquitta (@aldrinm) + */ +public class Queen { + + public static final int WINNING_POSITION = 158; + public static final int FORFEIT_MOVE = 0; + + // @formatter:off + private static final int[] S = { + 81, 71, 61, 51, 41, 31, 21, 11, + 92, 82, 72, 62, 52, 42, 32, 22, + 103, 93, 83, 73, 63, 53, 43, 33, + 114, 104, 94, 84, 74, 64, 54, 44, + 125, 115, 105, 95, 85, 75, 65, 55, + 136, 126, 116, 106, 96, 86, 76, 66, + 147, 137, 127, 117, 107, 97, 87, 77, + 158, 148, 138, 128, 118, 108, 98, 88 + }; + // @formatter:on + + private static final Scanner scanner = new Scanner(System.in); + + + public static void main(String[] args) { + printWithTab("QUEEN", 33); + printWithTab("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY", 15); + System.out.println("\n\n"); + + askAndShowInstructions(); + + boolean anotherGame; + do { + printBoard(); + + Move firstMove = getUserFirstMove(); + if (firstMove.move == 0) { + printWonByForfeit(); + } + + if (isTopmostRowOrRightmostColumn(firstMove)) { + playOneGame(firstMove); + } + + anotherGame = askForAnotherGame(); + + } while (anotherGame); + + + System.out.println("\nOK --- THANKS AGAIN."); + } + + /** + * Play one game starting with the first move from the user + */ + private static void playOneGame(Move firstMove) { + boolean gameInProgress = true; + Move userMove = firstMove; + + while (gameInProgress) { + + if (userMove.move == WINNING_POSITION) { + //players wins + printCongratulatoryMessage(); + gameInProgress = false; + } else { + + ComputerMove computerMove = getComputerMove(userMove); + + System.out.printf("COMPUTER MOVES TO SQUARE %d\n", computerMove.move); + + if (computerMove.move == WINNING_POSITION) { + printComputerWins(); + gameInProgress = false; + } else { + userMove = getValidUserMove(computerMove); + + if (userMove.move == FORFEIT_MOVE) { + printWonByForfeit(); + gameInProgress = false; + } else if (userMove.move == WINNING_POSITION) { + printCongratulatoryMessage(); + gameInProgress = false; + } + } + } + } + } + + /** + * Get the user's first move + */ + private static Move getUserFirstMove() { + boolean validMove; + Move move; + do { + System.out.print("WHERE WOULD YOU LIKE TO START? "); + int movePosition = scanner.nextInt(); + move = new Move(movePosition); + validMove = false; + if (!isTopmostRowOrRightmostColumn(move)) { + System.out.println("PLEASE READ THE DIRECTIONS AGAIN."); + System.out.println("YOU HAVE BEGUN ILLEGALLY."); + System.out.println(); + } else { + validMove = true; + } + scanner.nextLine(); + } while (!validMove); + return move; + } + + /** + * Prompt and get a valid move from the user. Uses the computer's latest move to validate the next move. + */ + private static Move getValidUserMove(ComputerMove latestComputerMove) { + boolean validUserMove = false; + Move userMove = null; + while (!validUserMove) { + userMove = getUserMove(); + if (!validateUserMove(userMove, latestComputerMove)) { + System.out.println("\nY O U C H E A T . . . TRY AGAIN"); + } else { + validUserMove = true; + } + } + return userMove; + } + + private static void printWonByForfeit() { + System.out.println("\nIT LOOKS LIKE I HAVE WON BY FORFEIT.\n"); + } + + private static boolean validateUserMove(Move userMove, ComputerMove computerMove) { + if (userMove.move <= computerMove.move) { + return false; + } + + if (userMove.move == FORFEIT_MOVE || userMove.move == WINNING_POSITION) { + return true; + } + + int tensValueUser = userMove.move / 10; + int unitsValueUser = userMove.move - (tensValueUser * 10); + int unitsValueComputer = computerMove.u; + int tensValueComputer = computerMove.t; + int p = unitsValueUser - unitsValueComputer; + if (p != 0) { + if ((tensValueUser - tensValueComputer) != p) { + return (tensValueUser - tensValueComputer) == 2 * p; + } else { + return true; + } + } else { + int l = tensValueUser - tensValueComputer; + return l > 0; + } + } + + private static Move getUserMove() { + System.out.print("WHAT IS YOUR MOVE? "); + int movePosition = scanner.nextInt(); + scanner.nextLine(); + return new Move(movePosition); + } + + private static void printComputerWins() { + System.out.println("\nNICE TRY, BUT IT LOOKS LIKE I HAVE WON."); + System.out.println("THANKS FOR PLAYING.\n"); + } + + private static boolean askForAnotherGame() { + System.out.print("ANYONE ELSE CARE TO TRY? "); + do { + String response = Queen.scanner.nextLine(); + if (response.equals("NO")) { + return false; + } else if (response.equals("YES")) { + return true; + } else { + System.out.println("PLEASE ANSWER 'YES' OR 'NO'."); + } + } while (true); + } + + private static boolean isTopmostRowOrRightmostColumn(Move move) { + return move.unitsPlaceValue == 1 || move.unitsPlaceValue == move.tensPlaceValue; + } + + private static ComputerMove getComputerMove(Move userMove) { + int unitsValueUser = userMove.unitsPlaceValue; + int tensValueUser = userMove.tensPlaceValue; + + List moveRandomlyFromPositions = List.of(41, 44, 73, 75, 126, 127); + + if (moveRandomlyFromPositions.contains(userMove.move)) { + //random move + return getMovePositionInRandomDirection(unitsValueUser, tensValueUser); + } else { + + for (int k = 7; k >= 1; k--) { + // consider same row first, left-most position + Optional move = evaluatePositionAndGetMove(unitsValueUser, tensValueUser + k); + if (move.isPresent()) return move.get(); + + //try same column, bottom-most + move = evaluatePositionAndGetMove(unitsValueUser + k, tensValueUser + k); + if (move.isPresent()) return move.get(); + + //now left-most at the bottom-most + move = evaluatePositionAndGetMove(unitsValueUser + k, tensValueUser + k + k); + if (move.isPresent()) return move.get(); + } + + //else move one step in a random direction + return getMovePositionInRandomDirection(unitsValueUser, tensValueUser); + } + + } + + private static Optional evaluatePositionAndGetMove(int newUnitsValue, int newTensValue) { + EvaluationResult result = evaluateComputerMove(newUnitsValue, newTensValue); + if (result.favourablePosition) { + return Optional.of(new ComputerMove(result.move)); + } + return Optional.empty(); + } + + private static EvaluationResult evaluateComputerMove(int u, int t) { + int m = 10 * t + u; + if (m == 158 || m == 127 || m == 126 || m == 75 || m == 73) { + return new EvaluationResult(m, true); + } else { + return new EvaluationResult(m, false); + } + } + + private static void printCongratulatoryMessage() { + System.out.println(); + System.out.println("C O N G R A T U L A T I O N S . . ."); + System.out.println(); + System.out.println("YOU HAVE WON--VERY WELL PLAYED."); + System.out.println("IT LOOKS LIKE I HAVE MET MY MATCH."); + System.out.println("THANKS FOR PLAYING---I CAN'T WIN ALL THE TIME."); + System.out.println(); + + } + + private static ComputerMove getMovePositionInRandomDirection(int u1, int t1) { + double randomValue = Math.random(); + + if (randomValue > 0.6) { + // Move directly down + return new ComputerMove(calculateMove(u1, t1, 1, 1)); + } else if (randomValue > 0.3) { + // Move down left + return new ComputerMove(calculateMove(u1, t1, 1, 2)); + } else { + // Move left + return new ComputerMove(calculateMove(u1, t1, 0, 1)); + } + } + + private static int calculateMove(int u, int t, int uChange, int tChange) { + int newU = u + uChange; + int newT = t + tChange; + return 10 * newT + newU; // Combine units and tens to corresponding position value + } + + + private static void printBoard() { + System.out.println(); + for (int A = 0; A <= 7; A++) { + for (int B = 0; B <= 7; B++) { + int I = 8 * A + B; + System.out.printf(" %d ", S[I]); + } + System.out.println(); + System.out.println(); + System.out.println(); + } + System.out.println(); + } + + private static void askAndShowInstructions() { + do { + System.out.print("DO YOU WANT INSTRUCTIONS? "); + String wish = scanner.nextLine(); + if (wish.equals("NO")) { + break; + } else if (wish.equals("YES")) { + showInstructions(); + break; + } else { + System.out.println("PLEASE ANSWER 'YES' OR 'NO'."); + } + } while (true); + } + + private static void printWithTab(String message, int tab) { + for (int i = 0; i < tab; i++) { + System.out.print(" "); + } + System.out.println(message); + } + + + private static void showInstructions() { + System.out.println("WE ARE GOING TO PLAY A GAME BASED ON ONE OF THE CHESS"); + System.out.println("MOVES. OUR QUEEN WILL BE ABLE TO MOVE ONLY TO THE LEFT,"); + System.out.println("DOWN, OR DIAGONALLY DOWN AND TO THE LEFT."); + System.out.println(); + System.out.println("THE OBJECT OF THE GAME IS TO PLACE THE QUEEN IN THE LOWER"); + System.out.println("LEFT HAND SQUARE BY ALTERNATING MOVES BETWEEN YOU AND THE"); + System.out.println("COMPUTER. THE FIRST ONE TO PLACE THE QUEEN THERE WINS."); + System.out.println(); + System.out.println("YOU GO FIRST AND PLACE THE QUEEN IN ANY ONE OF THE SQUARES"); + System.out.println("ON THE TOP ROW OR RIGHT HAND COLUMN."); + System.out.println("THAT WILL BE YOUR FIRST MOVE."); + System.out.println("WE ALTERNATE MOVES."); + System.out.println("YOU MAY FORFEIT BY TYPING '0' AS YOUR MOVE."); + System.out.println("BE SURE TO PRESS THE RETURN KEY AFTER EACH RESPONSE."); + System.out.println(); + } + + + private static class ComputerMove { + private final int move; + private final int u; + private final int t; + + private ComputerMove(int move) { + this.move = move; + this.t = move / 10; + this.u = move - (t * 10); + } + + } + + private static class EvaluationResult { + private final int move; + private final boolean favourablePosition; + + public EvaluationResult(int move, boolean favourablePosition) { + this.move = move; + this.favourablePosition = favourablePosition; + } + + + } + + private static class Move { + private final int move; + private final int unitsPlaceValue; + private final int tensPlaceValue; + + private Move(int move) { + this.move = move; + this.tensPlaceValue = move / 10; + this.unitsPlaceValue = move % 10; + } + + } +} diff --git a/72_Queen/python/queen.py b/72_Queen/python/queen.py index 94f377a1c..327c43f6e 100755 --- a/72_Queen/python/queen.py +++ b/72_Queen/python/queen.py @@ -82,10 +82,7 @@ def loc_to_num(location: Tuple[int, int], fix_align: bool = False) -> str: """Convert a position given by row, column into a space number.""" row, col = location out_str: str = f"{row + 8 - col}{row + 1}" - if not fix_align or len(out_str) == 3: - return out_str - else: - return out_str + " " + return out_str if not fix_align or len(out_str) == 3 else f"{out_str} " GAME_BOARD: Final[str] = ( @@ -120,9 +117,9 @@ def num_to_loc(num: int) -> Tuple[int, int]: ) # These are the places that the computer will always try to move into. -COMPUTER_PREF_MOVES: Final[ - FrozenSet[Tuple[int, int]] -] = COMPUTER_SAFE_SPOTS | frozenset([WIN_LOC]) +COMPUTER_PREF_MOVES: Final[FrozenSet[Tuple[int, int]]] = ( + COMPUTER_SAFE_SPOTS | frozenset([WIN_LOC]) +) # These are the locations (not including the win location) from which either player can # force a win (but the computer will always choose one of the COMPUTER_PREF_MOVES). @@ -173,15 +170,14 @@ def get_move(current_loc: Optional[Tuple[int, int]]) -> Tuple[int, int]: "YOU HAVE BEGUN ILLEGALLY.\n\n" "WHERE WOULD YOU LIKE TO START? " ) + elif ( + (new_row == row and new_col < col) # move left + or (new_col == col and new_row > row) # move down + or (new_row - row == col - new_col) # move diag left and down + ) and (not FIX_BOARD_BUG or (new_col >= 0 and new_row < 8)): + return new_row, new_col else: - if ( - (new_row == row and new_col < col) # move left - or (new_col == col and new_row > row) # move down - or (new_row - row == col - new_col) # move diag left and down - ) and (not FIX_BOARD_BUG or (new_col >= 0 and new_row < 8)): - return new_row, new_col - else: - prompt = "Y O U C H E A T . . . TRY AGAIN? " + prompt = "Y O U C H E A T . . . TRY AGAIN? " except ValueError: prompt = "!NUMBER EXPECTED - RETRY INPUT LINE\n? " @@ -256,12 +252,12 @@ def ask(prompt: str) -> bool: inpt: str while True: # Normalize input to uppercase, no whitespace, then get first character - inpt = input(prompt + "? ").upper().strip()[0] + inpt = input(f"{prompt}? ").upper().strip()[0] print() - if inpt == "Y": - return True - elif inpt == "N": + if inpt == "N": return False + elif inpt == "Y": + return True print("PLEASE ANSWER 'YES' OR 'NO'.") return False diff --git a/72_Queen/rust/Cargo.lock b/72_Queen/rust/Cargo.lock new file mode 100644 index 000000000..4fe2abbea --- /dev/null +++ b/72_Queen/rust/Cargo.lock @@ -0,0 +1,75 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/73_Reverse/python/reverse.py b/73_Reverse/python/reverse.py index f75ece2f8..e34378406 100755 --- a/73_Reverse/python/reverse.py +++ b/73_Reverse/python/reverse.py @@ -52,7 +52,7 @@ def game_loop() -> None: turns += 1 # Reverse as many items as requested. - newnums = numbers[0:howmany] + newnums = numbers[:howmany] newnums.reverse() newnums.extend(numbers[howmany:]) numbers = newnums diff --git a/73_Reverse/rust/Cargo.lock b/73_Reverse/rust/Cargo.lock new file mode 100644 index 000000000..0da17814e --- /dev/null +++ b/73_Reverse/rust/Cargo.lock @@ -0,0 +1,243 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bitflags" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi", + "windows-targets", +] + +[[package]] +name = "libc" +version = "0.2.170" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy 0.7.35", +] + +[[package]] +name = "proc-macro2" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1f1914ce909e1658d9907913b4b91947430c7d9be598b15a1912935b8c04801" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +dependencies = [ + "rand_chacha", + "rand_core", + "zerocopy 0.8.21", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "syn" +version = "2.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e02e925281e18ffd9d640e234264753c43edc62d64b2d4cf898f1bc5e75f3fc2" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf01143b2dd5d134f11f545cf9f1431b13b749695cb33bcce051e7568f99478" +dependencies = [ + "zerocopy-derive 0.8.21", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712c8386f4f4299382c9abee219bee7084f78fb939d88b6840fcc1320d5f6da2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/73_Reverse/rust/Cargo.toml b/73_Reverse/rust/Cargo.toml new file mode 100644 index 000000000..3a87396aa --- /dev/null +++ b/73_Reverse/rust/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "rust" +version = "0.1.0" +edition = "2021" + +[dependencies] +rand = "0.9.0" \ No newline at end of file diff --git a/73_Reverse/rust/src/main.rs b/73_Reverse/rust/src/main.rs new file mode 100644 index 000000000..8fd06505e --- /dev/null +++ b/73_Reverse/rust/src/main.rs @@ -0,0 +1,209 @@ +/** REVERSE GAME + * https://github.com/marquesrs/basic-computer-games/blob/main/73_Reverse/reverse.bas + * Direct conversion from BASIC to Rust by Pablo Marques (marquesrs). + * No additional features or improvements were added. As a faithful translation, + * many of the code here are done in an unrecommended way by today's standards. + * 03/03/25 +*/ + +use rand::Rng; +use std::io::Write; + +fn input(msg: &str) -> String { + print!("{}", msg); + let _ =std::io::stdout().flush().unwrap(); + let mut input = String::new(); + std::io::stdin().read_line(&mut input).unwrap(); + return input.trim().to_uppercase(); +} + +fn main() { + // 10 PRINT TAB(32);"REVERSE" + // 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" + // 30 PRINT:PRINT:PRINT + // 100 PRINT "REVERSE -- A GAME OF SKILL": PRINT + print!("{}", format!("{}{}{}{}{}{}{}{}", + " ".repeat(31), + "REVERSE", + "\n", + " ".repeat(14), + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n", + "\n\n\n", + "REVERSE -- A GAME OF SKILL\n", + "\n" + )); + + // 130 DIM A(20) + let mut a = vec![0; 20]; + + // 140 REM *** N=NUMBER OF NUMBERS + // 150 N=9 + let n = 9; + + // 160 PRINT "DO YOU WANT THE RULES"; + // 170 INPUT A$ + let opt = input("DO YOU WANT THE RULES (YES OR NO)? "); + + + if opt == "YES" || opt == "Y" { + // 190 GOSUB 710 + sub1(n); + } + // 180 IF A$="NO" THEN 210 + 'c : loop { + // 200 REM *** MAKE A RANDOM LIST A(1) TO A(N) + // 210 A(1)=INT((N-1)*RND(1)+2) + // element 0 + a[0] = ((n-1) as f32 * rand::rng().random_range(0.0..=1.0) + 2.0) as i32; + + // 220 FOR K=2 TO N + for k in 2..=n { + 'a : loop { + // element k + // 230 A(K)=INT(N*RND(1)+1) + a[k-1] = (n as f32 * rand::rng().random_range(0.0..=1.0) + 1.0) as i32; + + // element 0 to k-1 + // 240 FOR J=1 TO K-1 + for j in 1..k { + // 250 IF A(K)=A(J) THEN 230 + if a[k-1] == a[j-1] { + continue 'a; + } + // 260 NEXT J: + } + break; + } + //NEXT K + } + + // 280 REM *** PRINT ORIGINAL LIST AND START GAME + // 290 PRINT: PRINT "HERE WE GO ... THE LIST IS:" + print!("\nHERE WE GO ... THE LIST IS:\n"); + + // 310 T=0 + let mut t = 0; + + // 320 GOSUB 610 + sub2(&a, n); + + 'b : loop { + // 330 PRINT "HOW MANY SHALL I REVERSE"; + // 340 INPUT R + let r = input("HOW MANY SHALL I REVERSE: ").parse::().unwrap(); + + // 350 IF R=0 THEN 520 + if r == 0 { + if replay() { continue 'c; } + else { break 'c; } + } + // 360 IF R<=N THEN 390 + if r <= n { + // 390 T=T+1 + t = t + 1; + + // 400 REM *** REVERSE R NUMBERS AND PRINT NEW LIST + // 410 FOR K=1 TO INT(R/2) + for k in 1..=((r/2) as usize) { + // 420 Z=A(K) + let z = a[k-1]; + // 430 A(K)=A(R-K+1) + a[k-1] = a[r-k]; + // 440 A(R-K+1)=Z + a[r-k] = z; + + // 450 NEXT K + } + // 460 GOSUB 610 + sub2(&a, n); + + // 470 REM *** CHECK FOR A WIN + // 480 FOR K=1 TO N + for k in 1..=n { + // 490 IF A(K)<>K THEN 330 + if a[k-1] != k as i32 { + continue 'b; + } + // 500 NEXT K + } + // 510 PRINT "YOU WON IT IN";T;"MOVES!!!":PRINT + print!("{}{}{}", "YOU WON IT IN ", t, " MOVES!!!\n\n"); + + if replay() { continue 'c; } + else { break 'c; } + } + else { + // 370 PRINT "OOPS! TOO MANY! I CAN REVERSE AT MOST";N:GOTO 330 + print!("OOPS! TOO MANY! I CAN REVERSE AT MOST {n}"); + } + } + } + // 999 END +} + +// 600 REM *** SUBROUTINE TO PRINT LIST +fn sub2(a: &Vec, n: usize) { + // 610 PRINT:FOR K=1 TO N:PRINT A(K);:NEXT K + // 650 PRINT:PRINT:RETURN + for k in 1..=n { + print!("{} ", a[k-1]); + } + print!("\n\n"); +} + +// 700 REM *** SUBROUTINE TO PRINT THE RULES +fn sub1(n: usize) { + // 710 PRINT:PRINT "THIS IS THE GAME OF 'REVERSE'. TO WIN, ALL YOU HAVE" + // 720 PRINT "TO DO IS ARRANGE A LIST OF NUMBERS (1 THROUGH";N;")" + // 730 PRINT "IN NUMERICAL ORDER FROM LEFT TO RIGHT. TO MOVE, YOU" + // 740 PRINT "TELL ME HOW MANY NUMBERS (COUNTING FROM THE LEFT) TO" + // 750 PRINT "REVERSE. FOR EXAMPLE, IF THE CURRENT LIST IS:" + // 760 PRINT:PRINT "2 3 4 5 1 6 7 8 9" + // 770 PRINT:PRINT "AND YOU REVERSE 4, THE RESULT WILL BE:" + // 780 PRINT:PRINT "5 4 3 2 1 6 7 8 9" + // 790 PRINT:PRINT "NOW IF YOU REVERSE 5, YOU WIN!" + // 800 PRINT:PRINT "1 2 3 4 5 6 7 8 9":PRINT + // 810 PRINT "NO DOUBT YOU WILL LIKE THIS GAME, BUT" + // 820 PRINT "IF YOU WANT TO QUIT, REVERSE 0 (ZERO).":PRINT: RETURN + + print!("{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}", + "\n", + "THIS IS THE GAME OF 'REVERSE'. TO WIN, ALL YOU HAVE\n", + "TO DO IS ARRANGE A LIST OF NUMBERS (1 THROUGH ", + n, + ")\n", + "IN NUMERICAL ORDER FROM LEFT TO RIGHT. TO MOVE, YOU\n", + "TELL ME HOW MANY NUMBERS (COUNTING FROM THE LEFT) TO\n", + "REVERSE. FOR EXAMPLE, IF THE CURRENT LIST IS:\n", + "\n", + "2 3 4 5 1 6 7 8 9\n", + "\n", + "AND YOU REVERSE 4, THE RESULT WILL BE:\n", + "\n", + "5 4 3 2 1 6 7 8 9\n", + "\n", + "NOW IF YOU REVERSE 5, YOU WIN!\n", + "\n", + "1 2 3 4 5 6 7 8 9\n", + "\n", + "NO DOUBT YOU WILL LIKE THIS GAME, BUT\n", + "IF YOU WANT TO QUIT, REVERSE 0 (ZERO).\n", + "\n", + ) +} + +fn replay() -> bool { + // 520 PRINT + // 530 PRINT "TRY AGAIN (YES OR NO)"; + // 540 INPUT A$ + let r = input("\nTRY AGAIN (YES OR NO): "); + // 550 IF A$="YES" THEN 210 + if r == "YES" || r == "Y" { + return true; + } + else { + // 560 PRINT: PRINT "O.K. HOPE YOU HAD FUN!!":GOTO 999 + println!("\nO.K. HOPE YOU HAD FUN!!"); + return false; + } +} \ No newline at end of file diff --git a/74_Rock_Scissors_Paper/python/rockscissors.py b/74_Rock_Scissors_Paper/python/rockscissors.py index 898f709cc..3d150bd4f 100644 --- a/74_Rock_Scissors_Paper/python/rockscissors.py +++ b/74_Rock_Scissors_Paper/python/rockscissors.py @@ -44,22 +44,27 @@ def play_game() -> None: print("...Scissors") elif guess_computer == 3: print("...Rock") - if guess_computer == guess_human: + if ( + guess_computer != guess_human + and guess_computer > guess_human + and (guess_human != 1 or guess_computer != 3) + or guess_computer != guess_human + and guess_computer <= guess_human + and guess_computer == 1 + and guess_human == 3 + ): + print("Wow! I win!!!") + won_computer = won_computer + 1 + elif ( + guess_computer != guess_human + and guess_computer > guess_human + or guess_computer != guess_human + and guess_computer == 1 + ): + print("You win!!!") + won_human = won_human + 1 + elif guess_computer == guess_human: print("Tie Game. No winner") - elif guess_computer > guess_human: - if guess_human != 1 or guess_computer != 3: - print("Wow! I win!!!") - won_computer = won_computer + 1 - else: - print("You win!!!") - won_human = won_human + 1 - elif guess_computer == 1: - if guess_human != 3: - print("You win!!!") - won_human = won_human + 1 - else: - print("Wow! I win!!!") - won_computer = won_computer + 1 print("\nHere is the final game score:") print("I have won", won_computer, "game(s).") print("You have won", won_human, "game(s).") diff --git a/74_Rock_Scissors_Paper/rust/Cargo.lock b/74_Rock_Scissors_Paper/rust/Cargo.lock new file mode 100644 index 000000000..81a95200e --- /dev/null +++ b/74_Rock_Scissors_Paper/rust/Cargo.lock @@ -0,0 +1,91 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "nanorand" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "729eb334247daa1803e0a094d0a5c55711b85571179f5ec6e53eccfdf7008958" + +[[package]] +name = "proc-macro2" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rock_scissors_paper" +version = "1.0.0" +dependencies = [ + "nanorand", + "strum", + "strum_macros", + "text_io", +] + +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + +[[package]] +name = "strum" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" + +[[package]] +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "text_io" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f0c8eb2ad70c12a6a69508f499b3051c924f4b1cfeae85bfad96e6bc5bba46" + +[[package]] +name = "unicode-ident" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" diff --git a/75_Roulette/README.md b/75_Roulette/README.md index 4902aa9ba..b68a2ad1b 100644 --- a/75_Roulette/README.md +++ b/75_Roulette/README.md @@ -17,4 +17,4 @@ http://www.vintage-basic.net/games.html #### Porting Notes -(please note any difficulties or challenges in porting here) +- The program keeps a count of how often each number comes up in array `X`, but never makes use of this information. diff --git a/75_Roulette/python/roulette.py b/75_Roulette/python/roulette.py index 7a21d36dc..eec68bed7 100644 --- a/75_Roulette/python/roulette.py +++ b/75_Roulette/python/roulette.py @@ -75,7 +75,7 @@ def query_bets() -> Tuple[List[int], List[int]]: for i in range(bet_count): while bet_ids[i] == -1: try: - in_string = input("NUMBER " + str(i + 1) + "? ").split(",") + in_string = input(f"NUMBER {str(i + 1)}? ").split(",") id_, val = int(in_string[0]), int(in_string[1]) # check other bet_IDs @@ -126,9 +126,9 @@ def get_modifier(id_: int, num: int) -> int: total_winnings += winnings if winnings >= 0: - print("YOU WIN " + str(winnings) + " DOLLARS ON BET " + str(i + 1)) + print(f"YOU WIN {str(winnings)} DOLLARS ON BET {str(i + 1)}") else: - print("YOU LOSE " + str(winnings * -1) + " DOLLARS ON BET " + str(i + 1)) + print(f"YOU LOSE {str(winnings * -1)} DOLLARS ON BET {str(i + 1)}") return winnings @@ -142,7 +142,7 @@ def print_check(amount: int) -> None: print(" " * 40 + "CHECK NO. " + str(random.randint(0, 100))) print(" " * 40 + str(date.today())) print() - print("PAY TO THE ORDER OF -----" + name + "----- $" + str(amount)) + print(f"PAY TO THE ORDER OF -----{name}----- ${amount}") print() print(" " * 40 + "THE MEMORY BANK OF NEW YORK") print(" " * 40 + "THE COMPUTER") @@ -176,9 +176,9 @@ def main() -> None: elif val == 37: print("00") elif val in RED_NUMBERS: - print(str(val) + " RED") + print(f"{val} RED") else: - print(str(val) + " BLACK") + print(f"{val} BLACK") print() total_winnings = bet_results(bet_ids, bet_values, val) @@ -209,7 +209,7 @@ def main() -> None: def string_to_bool(string: str) -> bool: """Converts a string to a bool""" - return string.lower() in ("yes", "y", "true", "t", "yes") + return string.lower() in {"y", "true", "t", "yes"} if __name__ == "__main__": diff --git a/75_Roulette/rust/Cargo.lock b/75_Roulette/rust/Cargo.lock new file mode 100644 index 000000000..81365033b --- /dev/null +++ b/75_Roulette/rust/Cargo.lock @@ -0,0 +1,82 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "morristown" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83b191fd96370b91c2925125774011a7a0f69b4db9e2045815e4fd20af725f6" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "morristown", + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/76_Russian_Roulette/python/russianroulette.py b/76_Russian_Roulette/python/russianroulette.py index f402fb03c..71e0ee129 100644 --- a/76_Russian_Roulette/python/russianroulette.py +++ b/76_Russian_Roulette/python/russianroulette.py @@ -29,8 +29,7 @@ def initial_message() -> None: def parse_input() -> int: while True: try: - i = int(input("? ")) - return i + return int(input("? ")) except ValueError: print("Number expected...") @@ -61,13 +60,12 @@ def main() -> None: print("BANG!!!!! You're Dead!") print("Condolences will be sent to your relatives.\n\n\n") print("...Next victim...") + elif n > NUMBER_OF_ROUNDS: + print("You win!!!!!") + print("Let someone else blow his brain out.\n") else: - if n > NUMBER_OF_ROUNDS: - print("You win!!!!!") - print("Let someone else blow his brain out.\n") - else: - print(" Chicken!!!!!\n\n\n") - print("...Next victim....") + print(" Chicken!!!!!\n\n\n") + print("...Next victim....") if __name__ == "__main__": diff --git a/76_Russian_Roulette/rust/Cargo.lock b/76_Russian_Roulette/rust/Cargo.lock new file mode 100644 index 000000000..4fe2abbea --- /dev/null +++ b/76_Russian_Roulette/rust/Cargo.lock @@ -0,0 +1,75 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/77_Salvo/README.md b/77_Salvo/README.md index 4bb40487e..3ef9f21c9 100644 --- a/77_Salvo/README.md +++ b/77_Salvo/README.md @@ -26,7 +26,9 @@ http://www.vintage-basic.net/games.html #### Porting Notes -As per the analysis in +The program does no validation of ship positions; your ship coordinates may be scattered around the board in any way you like. (Computer ships will not do this, but they may be placed diagonally in such a way that they cross each other.) Scattering your ships in this way probably defeats whatever all that spaghetti-code logic the computer is using to pick its moves, which is based on the assumption of contiguous ships. + +Moreover: as per the analysis in https://forums.raspberrypi.com/viewtopic.php?p=1997950#p1997950 @@ -40,4 +42,5 @@ This typo is interesting because it causes the program to play by a much weaker `3970 K(R,S)=K(R,S)+E(U)-2*INT(H(U)+.5)` -and to change the JavaScript program accordingly. +and to change the JavaScript program accordingly. (And note that some ports — looking at you, Python — do not implement the original strategy at all, but merely pick random unshot locations for every shot.) + diff --git a/77_Salvo/csharp/Coordinate.cs b/77_Salvo/csharp/Coordinate.cs new file mode 100644 index 000000000..4cd2c4334 --- /dev/null +++ b/77_Salvo/csharp/Coordinate.cs @@ -0,0 +1,45 @@ +namespace Salvo; + +internal record struct Coordinate(int Value) +{ + public const int MinValue = 1; + public const int MaxValue = 10; + + public static IEnumerable Range => Enumerable.Range(1, 10).Select(v => new Coordinate(v)); + + public bool IsInRange => Value is >= MinValue and <= MaxValue; + + public static Coordinate Create(float value) => new((int)value); + + public static bool TryCreateValid(float value, out Coordinate coordinate) + { + coordinate = default; + if (value != (int)value) { return false; } + + var result = Create(value); + + if (result.IsInRange) + { + coordinate = result; + return true; + } + + return false; + } + + public Coordinate BringIntoRange(IRandom random) + => Value switch + { + < MinValue => new(MinValue + (int)random.NextFloat(2.5F)), + > MaxValue => new(MaxValue - (int)random.NextFloat(2.5F)), + _ => this + }; + + public static implicit operator Coordinate(float value) => Create(value); + public static implicit operator int(Coordinate coordinate) => coordinate.Value; + + public static Coordinate operator +(Coordinate coordinate, int offset) => new(coordinate.Value + offset); + public static int operator -(Coordinate a, Coordinate b) => a.Value - b.Value; + + public override string ToString() => $" {Value} "; +} diff --git a/77_Salvo/csharp/Extensions/IOExtensions.cs b/77_Salvo/csharp/Extensions/IOExtensions.cs new file mode 100644 index 000000000..6d021deb3 --- /dev/null +++ b/77_Salvo/csharp/Extensions/IOExtensions.cs @@ -0,0 +1,27 @@ +namespace Games.Common.IO; + +internal static class IOExtensions +{ + internal static Position ReadPosition(this IReadWrite io) => Position.Create(io.Read2Numbers("")); + + internal static Position ReadValidPosition(this IReadWrite io) + { + while (true) + { + if (Position.TryCreateValid(io.Read2Numbers(""), out var position)) + { + return position; + } + io.Write(Streams.Illegal); + } + } + + internal static IEnumerable ReadPositions(this IReadWrite io, string shipName, int shipSize) + { + io.WriteLine(shipName); + for (var i = 0; i < shipSize; i++) + { + yield return io.ReadPosition(); + } + } +} diff --git a/77_Salvo/csharp/Extensions/RandomExtensions.cs b/77_Salvo/csharp/Extensions/RandomExtensions.cs new file mode 100644 index 000000000..e83ab2657 --- /dev/null +++ b/77_Salvo/csharp/Extensions/RandomExtensions.cs @@ -0,0 +1,32 @@ +namespace Games.Common.Randomness; + +internal static class RandomExtensions +{ + internal static (Position, Offset) NextShipPosition(this IRandom random) + { + var startX = random.NextCoordinate(); + var startY = random.NextCoordinate(); + var deltaY = random.NextOffset(); + var deltaX = random.NextOffset(); + return (new(startX, startY), new(deltaX, deltaY)); + } + + private static Coordinate NextCoordinate(this IRandom random) + => random.Next(Coordinate.MinValue, Coordinate.MaxValue + 1); + + private static int NextOffset(this IRandom random) => random.Next(-1, 2); + + internal static (Position, Offset) GetRandomShipPositionInRange(this IRandom random, int shipSize) + { + while (true) + { + var (start, delta) = random.NextShipPosition(); + var shipSizeLessOne = shipSize - 1; + var end = start + delta * shipSizeLessOne; + if (delta != 0 && end.IsInRange) + { + return (start, delta); + } + } + } +} diff --git a/77_Salvo/csharp/Fleet.cs b/77_Salvo/csharp/Fleet.cs new file mode 100644 index 000000000..5f267225f --- /dev/null +++ b/77_Salvo/csharp/Fleet.cs @@ -0,0 +1,66 @@ +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; + +namespace Salvo; + +internal class Fleet +{ + private readonly List _ships; + + internal Fleet(IReadWrite io) + { + io.WriteLine(Prompts.Coordinates); + _ships = new() + { + new Battleship(io), + new Cruiser(io), + new Destroyer("A", io), + new Destroyer("B", io) + }; + } + + internal Fleet(IRandom random) + { + _ships = new(); + while (true) + { + _ships.Add(new Battleship(random)); + if (TryPositionShip(() => new Cruiser(random)) && + TryPositionShip(() => new Destroyer("A", random)) && + TryPositionShip(() => new Destroyer("B", random))) + { + return; + } + _ships.Clear(); + } + + bool TryPositionShip(Func shipFactory) + { + var shipGenerationAttempts = 0; + while (true) + { + var ship = shipFactory.Invoke(); + shipGenerationAttempts++; + if (shipGenerationAttempts > 25) { return false; } + if (_ships.Min(ship.DistanceTo) >= 3.59) + { + _ships.Add(ship); + return true; + } + } + } + } + + internal IEnumerable Ships => _ships.AsEnumerable(); + + internal void ReceiveShots(IEnumerable shots, Action reportHit) + { + foreach (var position in shots) + { + var ship = _ships.FirstOrDefault(s => s.IsHit(position)); + if (ship == null) { continue; } + if (ship.IsDestroyed) { _ships.Remove(ship); } + reportHit(ship); + } + } +} diff --git a/77_Salvo/csharp/Game.cs b/77_Salvo/csharp/Game.cs new file mode 100644 index 000000000..a0223d8ba --- /dev/null +++ b/77_Salvo/csharp/Game.cs @@ -0,0 +1,29 @@ +namespace Salvo; + +internal class Game +{ + private readonly IReadWrite _io; + private readonly IRandom _random; + + public Game(IReadWrite io, IRandom random) + { + _io = io; + _random = random; + } + + internal void Play() + { + _io.Write(Streams.Title); + + var turnHandler = new TurnHandler(_io, _random); + _io.WriteLine(); + + Winner? winner; + do + { + winner = turnHandler.PlayTurn(); + } while (winner == null); + + _io.Write(winner == Winner.Computer ? Streams.IWon : Streams.YouWon); + } +} diff --git a/77_Salvo/csharp/Offset.cs b/77_Salvo/csharp/Offset.cs new file mode 100644 index 000000000..3c873f101 --- /dev/null +++ b/77_Salvo/csharp/Offset.cs @@ -0,0 +1,25 @@ +namespace Salvo; + +internal record struct Offset(int X, int Y) +{ + public static readonly Offset Zero = 0; + + public static Offset operator *(Offset offset, int scale) => new(offset.X * scale, offset.Y * scale); + + public static implicit operator Offset(int value) => new(value, value); + + public static IEnumerable Units + { + get + { + for (int x = -1; x <= 1; x++) + { + for (int y = -1; y <= 1; y++) + { + var offset = new Offset(x, y); + if (offset != Zero) { yield return offset; } + } + } + } + } +} diff --git a/77_Salvo/csharp/Position.cs b/77_Salvo/csharp/Position.cs new file mode 100644 index 000000000..3bafe2179 --- /dev/null +++ b/77_Salvo/csharp/Position.cs @@ -0,0 +1,52 @@ +namespace Salvo; + +internal record struct Position(Coordinate X, Coordinate Y) +{ + public bool IsInRange => X.IsInRange && Y.IsInRange; + public bool IsOnDiagonal => X == Y; + + public static Position Create((float X, float Y) coordinates) => new(coordinates.X, coordinates.Y); + + public static bool TryCreateValid((float X, float Y) coordinates, out Position position) + { + if (Coordinate.TryCreateValid(coordinates.X, out var x) && Coordinate.TryCreateValid(coordinates.Y, out var y)) + { + position = new(x, y); + return true; + } + + position = default; + return false; + } + + public static IEnumerable All + => Coordinate.Range.SelectMany(x => Coordinate.Range.Select(y => new Position(x, y))); + + public IEnumerable Neighbours + { + get + { + foreach (var offset in Offset.Units) + { + var neighbour = this + offset; + if (neighbour.IsInRange) { yield return neighbour; } + } + } + } + + internal float DistanceTo(Position other) + { + var (deltaX, deltaY) = (X - other.X, Y - other.Y); + return (float)Math.Sqrt(deltaX * deltaX + deltaY * deltaY); + } + + internal Position BringIntoRange(IRandom random) + => IsInRange ? this : new(X.BringIntoRange(random), Y.BringIntoRange(random)); + + public static Position operator +(Position position, Offset offset) + => new(position.X + offset.X, position.Y + offset.Y); + + public static implicit operator Position(int value) => new(value, value); + + public override string ToString() => $"{X}{Y}"; +} diff --git a/77_Salvo/csharp/Program.cs b/77_Salvo/csharp/Program.cs new file mode 100644 index 000000000..5dde4d052 --- /dev/null +++ b/77_Salvo/csharp/Program.cs @@ -0,0 +1,9 @@ +global using System; +global using Games.Common.IO; +global using Games.Common.Randomness; +global using Salvo; +global using Salvo.Ships; +global using static Salvo.Resources.Resource; + +//new Game(new ConsoleIO(), new RandomNumberGenerator()).Play(); +new Game(new ConsoleIO(), new DataRandom()).Play(); diff --git a/77_Salvo/csharp/Resources/Coordinates.txt b/77_Salvo/csharp/Resources/Coordinates.txt new file mode 100644 index 000000000..387d7a6b4 --- /dev/null +++ b/77_Salvo/csharp/Resources/Coordinates.txt @@ -0,0 +1 @@ +Enter coordinates for... diff --git a/77_Salvo/csharp/Resources/IHaveMoreShotsThanSquares.txt b/77_Salvo/csharp/Resources/IHaveMoreShotsThanSquares.txt new file mode 100644 index 000000000..a6f371107 --- /dev/null +++ b/77_Salvo/csharp/Resources/IHaveMoreShotsThanSquares.txt @@ -0,0 +1 @@ +I have more shots than blank squares. diff --git a/77_Salvo/csharp/Resources/IHaveShots.txt b/77_Salvo/csharp/Resources/IHaveShots.txt new file mode 100644 index 000000000..9157e1a7b --- /dev/null +++ b/77_Salvo/csharp/Resources/IHaveShots.txt @@ -0,0 +1 @@ +I have {0} shots. diff --git a/77_Salvo/csharp/Resources/IHit.txt b/77_Salvo/csharp/Resources/IHit.txt new file mode 100644 index 000000000..3b43216cd --- /dev/null +++ b/77_Salvo/csharp/Resources/IHit.txt @@ -0,0 +1 @@ +I hit your {0} diff --git a/77_Salvo/csharp/Resources/IWon.txt b/77_Salvo/csharp/Resources/IWon.txt new file mode 100644 index 000000000..b3f506348 --- /dev/null +++ b/77_Salvo/csharp/Resources/IWon.txt @@ -0,0 +1 @@ +I have won. diff --git a/77_Salvo/csharp/Resources/Illegal.txt b/77_Salvo/csharp/Resources/Illegal.txt new file mode 100644 index 000000000..6a71787a7 --- /dev/null +++ b/77_Salvo/csharp/Resources/Illegal.txt @@ -0,0 +1 @@ +Illegal, enter again. diff --git a/77_Salvo/csharp/Resources/Resource.cs b/77_Salvo/csharp/Resources/Resource.cs new file mode 100644 index 000000000..8be52fdb0 --- /dev/null +++ b/77_Salvo/csharp/Resources/Resource.cs @@ -0,0 +1,49 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +namespace Salvo.Resources; + +internal static class Resource +{ + internal static class Streams + { + public static Stream Title => GetStream(); + public static Stream YouHaveMoreShotsThanSquares => GetStream(); + public static Stream YouWon => GetStream(); + public static Stream IHaveMoreShotsThanSquares => GetStream(); + public static Stream IWon => GetStream(); + public static Stream Illegal => GetStream(); + } + + internal static class Strings + { + public static string WhereAreYourShips => GetString(); + public static string YouHaveShots(int number) => Format(number); + public static string IHaveShots(int number) => Format(number); + public static string YouHit(string shipName) => Format(shipName); + public static string IHit(string shipName) => Format(shipName); + public static string ShotBefore(int turnNumber) => Format(turnNumber); + public static string Turn(int number) => Format(number); + } + + internal static class Prompts + { + public static string Coordinates => GetString(); + public static string Start => GetString(); + public static string SeeShots => GetString(); + } + + private static string Format(T value, [CallerMemberName] string? name = null) + => string.Format(GetString(name), value); + + private static string GetString([CallerMemberName] string? name = null) + { + using var stream = GetStream(name); + using var reader = new StreamReader(stream); + return reader.ReadToEnd(); + } + + private static Stream GetStream([CallerMemberName] string? name = null) => + Assembly.GetExecutingAssembly().GetManifestResourceStream($"{typeof(Resource).Namespace}.{name}.txt") + ?? throw new Exception($"Could not find embedded resource stream '{name}'."); +} \ No newline at end of file diff --git a/77_Salvo/csharp/Resources/SeeShots.txt b/77_Salvo/csharp/Resources/SeeShots.txt new file mode 100644 index 000000000..7f608b0ba --- /dev/null +++ b/77_Salvo/csharp/Resources/SeeShots.txt @@ -0,0 +1 @@ +Do you want to see my shots \ No newline at end of file diff --git a/77_Salvo/csharp/Resources/ShotBefore.txt b/77_Salvo/csharp/Resources/ShotBefore.txt new file mode 100644 index 000000000..18e90fcbd --- /dev/null +++ b/77_Salvo/csharp/Resources/ShotBefore.txt @@ -0,0 +1 @@ +You shot there before on turn {0} diff --git a/77_Salvo/csharp/Resources/Start.txt b/77_Salvo/csharp/Resources/Start.txt new file mode 100644 index 000000000..283a0fbb0 --- /dev/null +++ b/77_Salvo/csharp/Resources/Start.txt @@ -0,0 +1 @@ +Do you want to start \ No newline at end of file diff --git a/77_Salvo/csharp/Resources/Title.txt b/77_Salvo/csharp/Resources/Title.txt new file mode 100644 index 000000000..6b2ba9b31 --- /dev/null +++ b/77_Salvo/csharp/Resources/Title.txt @@ -0,0 +1,5 @@ + Salvo + Creative Computing Morristown, New Jersey + + + diff --git a/77_Salvo/csharp/Resources/Turn.txt b/77_Salvo/csharp/Resources/Turn.txt new file mode 100644 index 000000000..14bf316ea --- /dev/null +++ b/77_Salvo/csharp/Resources/Turn.txt @@ -0,0 +1,2 @@ + +Turn {0} diff --git a/77_Salvo/csharp/Resources/WhereAreYourShips.txt b/77_Salvo/csharp/Resources/WhereAreYourShips.txt new file mode 100644 index 000000000..e8391a465 --- /dev/null +++ b/77_Salvo/csharp/Resources/WhereAreYourShips.txt @@ -0,0 +1 @@ +Where are your ships? \ No newline at end of file diff --git a/77_Salvo/csharp/Resources/YouHaveMoreShotsThanSquares.txt b/77_Salvo/csharp/Resources/YouHaveMoreShotsThanSquares.txt new file mode 100644 index 000000000..04d58f92e --- /dev/null +++ b/77_Salvo/csharp/Resources/YouHaveMoreShotsThanSquares.txt @@ -0,0 +1 @@ +You have more shots than there are blank squares. diff --git a/77_Salvo/csharp/Resources/YouHaveShots.txt b/77_Salvo/csharp/Resources/YouHaveShots.txt new file mode 100644 index 000000000..43abeda83 --- /dev/null +++ b/77_Salvo/csharp/Resources/YouHaveShots.txt @@ -0,0 +1 @@ +You have {0} shots. diff --git a/77_Salvo/csharp/Resources/YouHit.txt b/77_Salvo/csharp/Resources/YouHit.txt new file mode 100644 index 000000000..b5b5d7614 --- /dev/null +++ b/77_Salvo/csharp/Resources/YouHit.txt @@ -0,0 +1 @@ +You hit my {0}. diff --git a/77_Salvo/csharp/Resources/YouWon.txt b/77_Salvo/csharp/Resources/YouWon.txt new file mode 100644 index 000000000..1f71343bc --- /dev/null +++ b/77_Salvo/csharp/Resources/YouWon.txt @@ -0,0 +1 @@ +You have won. diff --git a/77_Salvo/csharp/Salvo.csproj b/77_Salvo/csharp/Salvo.csproj index d3fe4757c..51470ec9f 100644 --- a/77_Salvo/csharp/Salvo.csproj +++ b/77_Salvo/csharp/Salvo.csproj @@ -2,8 +2,16 @@ Exe net6.0 - 10 + 11 enable enable + + + + + + + + diff --git a/77_Salvo/csharp/Ships/Battleship.cs b/77_Salvo/csharp/Ships/Battleship.cs new file mode 100644 index 000000000..4f864eb6f --- /dev/null +++ b/77_Salvo/csharp/Ships/Battleship.cs @@ -0,0 +1,17 @@ +namespace Salvo.Ships; + +internal sealed class Battleship : Ship +{ + internal Battleship(IReadWrite io) + : base(io) + { + } + + internal Battleship(IRandom random) + : base(random) + { + } + + internal override int Shots => 3; + internal override int Size => 5; +} diff --git a/77_Salvo/csharp/Ships/Cruiser.cs b/77_Salvo/csharp/Ships/Cruiser.cs new file mode 100644 index 000000000..d004f24cf --- /dev/null +++ b/77_Salvo/csharp/Ships/Cruiser.cs @@ -0,0 +1,17 @@ +namespace Salvo.Ships; + +internal sealed class Cruiser : Ship +{ + internal Cruiser(IReadWrite io) + : base(io) + { + } + + internal Cruiser(IRandom random) + : base(random) + { + } + + internal override int Shots => 2; + internal override int Size => 3; +} diff --git a/77_Salvo/csharp/Ships/Destroyer.cs b/77_Salvo/csharp/Ships/Destroyer.cs new file mode 100644 index 000000000..6523395d3 --- /dev/null +++ b/77_Salvo/csharp/Ships/Destroyer.cs @@ -0,0 +1,17 @@ +namespace Salvo.Ships; + +internal sealed class Destroyer : Ship +{ + internal Destroyer(string nameIndex, IReadWrite io) + : base(io, $"<{nameIndex}>") + { + } + + internal Destroyer(string nameIndex, IRandom random) + : base(random, $"<{nameIndex}>") + { + } + + internal override int Shots => 1; + internal override int Size => 2; +} diff --git a/77_Salvo/csharp/Ships/Ship.cs b/77_Salvo/csharp/Ships/Ship.cs new file mode 100644 index 000000000..ee204d3f3 --- /dev/null +++ b/77_Salvo/csharp/Ships/Ship.cs @@ -0,0 +1,37 @@ +namespace Salvo.Ships; + +internal abstract class Ship +{ + private readonly List _positions = new(); + + protected Ship(IReadWrite io, string? nameSuffix = null) + { + Name = GetType().Name + nameSuffix; + _positions = io.ReadPositions(Name, Size).ToList(); + } + + protected Ship(IRandom random, string? nameSuffix = null) + { + Name = GetType().Name + nameSuffix; + + var (start, delta) = random.GetRandomShipPositionInRange(Size); + for (var i = 0; i < Size; i++) + { + _positions.Add(start + delta * i); + } + } + + internal string Name { get; } + internal abstract int Shots { get; } + internal abstract int Size { get; } + internal bool IsDamaged => _positions.Count > 0 && _positions.Count < Size; + internal bool IsDestroyed => _positions.Count == 0; + + internal bool IsHit(Position position) => _positions.Remove(position); + + internal float DistanceTo(Ship other) + => _positions.SelectMany(a => other._positions.Select(b => a.DistanceTo(b))).Min(); + + public override string ToString() + => string.Join(Environment.NewLine, _positions.Select(p => p.ToString()).Prepend(Name)); +} diff --git a/77_Salvo/csharp/Targetting/ComputerShotSelector.cs b/77_Salvo/csharp/Targetting/ComputerShotSelector.cs new file mode 100644 index 000000000..721f50c50 --- /dev/null +++ b/77_Salvo/csharp/Targetting/ComputerShotSelector.cs @@ -0,0 +1,33 @@ +namespace Salvo.Targetting; + +internal class ComputerShotSelector : ShotSelector +{ + private readonly KnownHitsShotSelectionStrategy _knownHitsStrategy; + private readonly SearchPatternShotSelectionStrategy _searchPatternStrategy; + private readonly IReadWrite _io; + private readonly bool _showShots; + + internal ComputerShotSelector(Fleet source, IRandom random, IReadWrite io) + : base(source) + { + _knownHitsStrategy = new KnownHitsShotSelectionStrategy(this); + _searchPatternStrategy = new SearchPatternShotSelectionStrategy(this, random); + _io = io; + _showShots = io.ReadString(Prompts.SeeShots).Equals("yes", StringComparison.InvariantCultureIgnoreCase); + } + + protected override IEnumerable GetShots() + { + var shots = GetSelectionStrategy().GetShots(NumberOfShots).ToArray(); + if (_showShots) + { + _io.WriteLine(string.Join(Environment.NewLine, shots)); + } + return shots; + } + + internal void RecordHit(Ship ship, int turn) => _knownHitsStrategy.RecordHit(ship, turn); + + private ShotSelectionStrategy GetSelectionStrategy() + => _knownHitsStrategy.KnowsOfDamagedShips ? _knownHitsStrategy : _searchPatternStrategy; +} diff --git a/77_Salvo/csharp/Targetting/HumanShotSelector.cs b/77_Salvo/csharp/Targetting/HumanShotSelector.cs new file mode 100644 index 000000000..ea49bf124 --- /dev/null +++ b/77_Salvo/csharp/Targetting/HumanShotSelector.cs @@ -0,0 +1,34 @@ +namespace Salvo.Targetting; + +internal class HumanShotSelector : ShotSelector +{ + private readonly IReadWrite _io; + + internal HumanShotSelector(Fleet source, IReadWrite io) + : base(source) + { + _io = io; + } + + protected override IEnumerable GetShots() + { + var shots = new Position[NumberOfShots]; + + for (var i = 0; i < shots.Length; i++) + { + while (true) + { + var position = _io.ReadValidPosition(); + if (WasSelectedPreviously(position, out var turnTargeted)) + { + _io.WriteLine($"YOU SHOT THERE BEFORE ON TURN {turnTargeted}"); + continue; + } + shots[i] = position; + break; + } + } + + return shots; + } +} diff --git a/77_Salvo/csharp/Targetting/KnownHitsShotSelectionStrategy.cs b/77_Salvo/csharp/Targetting/KnownHitsShotSelectionStrategy.cs new file mode 100644 index 000000000..5e0b9d2ca --- /dev/null +++ b/77_Salvo/csharp/Targetting/KnownHitsShotSelectionStrategy.cs @@ -0,0 +1,71 @@ +namespace Salvo.Targetting; + +internal class KnownHitsShotSelectionStrategy : ShotSelectionStrategy +{ + private readonly List<(int Turn, Ship Ship)> _damagedShips = new(); + + internal KnownHitsShotSelectionStrategy(ShotSelector shotSelector) + : base(shotSelector) + { + } + + internal bool KnowsOfDamagedShips => _damagedShips.Any(); + + internal override IEnumerable GetShots(int numberOfShots) + { + var tempGrid = Position.All.ToDictionary(x => x, _ => 0); + var shots = Enumerable.Range(1, numberOfShots).Select(x => new Position(x, x)).ToArray(); + + foreach (var (hitTurn, ship) in _damagedShips) + { + foreach (var position in Position.All) + { + if (WasSelectedPreviously(position)) + { + tempGrid[position]=-10000000; + continue; + } + + foreach (var neighbour in position.Neighbours) + { + if (WasSelectedPreviously(neighbour, out var turn) && turn == hitTurn) + { + tempGrid[position] += hitTurn + 10 - position.Y * ship.Shots; + } + } + } + } + + foreach (var position in Position.All) + { + var Q9=0; + for (var i = 0; i < numberOfShots; i++) + { + if (tempGrid[shots[i]] < tempGrid[shots[Q9]]) + { + Q9 = i; + } + } + if (position.X <= numberOfShots && position.IsOnDiagonal) { continue; } + if (tempGrid[position] x.Ship == ship); + } + else + { + _damagedShips.Add((turn, ship)); + } + } +} diff --git a/77_Salvo/csharp/Targetting/SearchPattern.cs b/77_Salvo/csharp/Targetting/SearchPattern.cs new file mode 100644 index 000000000..ac2071c8f --- /dev/null +++ b/77_Salvo/csharp/Targetting/SearchPattern.cs @@ -0,0 +1,22 @@ +using System.Collections.Immutable; + +namespace Salvo.Targetting; + +internal class SearchPattern +{ + private static readonly ImmutableArray _offsets = + ImmutableArray.Create(new(1, 1), new(-1, 1), new(1, -3), new(1, 1), new(0, 2), new(-1, 1)); + + private int _nextIndex; + + internal bool TryGetOffset(out Offset offset) + { + offset = default; + if (_nextIndex >= _offsets.Length) { return false; } + + offset = _offsets[_nextIndex++]; + return true; + } + + internal void Reset() => _nextIndex = 0; +} \ No newline at end of file diff --git a/77_Salvo/csharp/Targetting/SearchPatternShotSelector.cs b/77_Salvo/csharp/Targetting/SearchPatternShotSelector.cs new file mode 100644 index 000000000..1eae88f81 --- /dev/null +++ b/77_Salvo/csharp/Targetting/SearchPatternShotSelector.cs @@ -0,0 +1,55 @@ +namespace Salvo.Targetting; + +internal class SearchPatternShotSelectionStrategy : ShotSelectionStrategy +{ + private const int MaxSearchPatternAttempts = 100; + private readonly IRandom _random; + private readonly SearchPattern _searchPattern = new(); + private readonly List _shots = new(); + + internal SearchPatternShotSelectionStrategy(ShotSelector shotSelector, IRandom random) + : base(shotSelector) + { + _random = random; + } + + internal override IEnumerable GetShots(int numberOfShots) + { + _shots.Clear(); + while(_shots.Count < numberOfShots) + { + var (seed, _) = _random.NextShipPosition(); + SearchFrom(numberOfShots, seed); + } + return _shots; + } + + private void SearchFrom(int numberOfShots, Position candidateShot) + { + var attemptsLeft = MaxSearchPatternAttempts; + while (true) + { + _searchPattern.Reset(); + if (attemptsLeft-- == 0) { return; } + candidateShot = candidateShot.BringIntoRange(_random); + if (FindValidShots(numberOfShots, ref candidateShot)) { return; } + } + } + + private bool FindValidShots(int numberOfShots, ref Position candidateShot) + { + while (true) + { + if (IsValidShot(candidateShot)) + { + _shots.Add(candidateShot); + if (_shots.Count == numberOfShots) { return true; } + } + if (!_searchPattern.TryGetOffset(out var offset)) { return false; } + candidateShot += offset; + } + } + + private bool IsValidShot(Position candidate) + => candidate.IsInRange && !WasSelectedPreviously(candidate) && !_shots.Contains(candidate); +} \ No newline at end of file diff --git a/77_Salvo/csharp/Targetting/ShotSelectionStrategy.cs b/77_Salvo/csharp/Targetting/ShotSelectionStrategy.cs new file mode 100644 index 000000000..b4cf14b16 --- /dev/null +++ b/77_Salvo/csharp/Targetting/ShotSelectionStrategy.cs @@ -0,0 +1,17 @@ +namespace Salvo.Targetting; + +internal abstract class ShotSelectionStrategy +{ + private readonly ShotSelector _shotSelector; + protected ShotSelectionStrategy(ShotSelector shotSelector) + { + _shotSelector = shotSelector; + } + + internal abstract IEnumerable GetShots(int numberOfShots); + + protected bool WasSelectedPreviously(Position position) => _shotSelector.WasSelectedPreviously(position); + + protected bool WasSelectedPreviously(Position position, out int turn) + => _shotSelector.WasSelectedPreviously(position, out turn); +} diff --git a/77_Salvo/csharp/Targetting/ShotSelector.cs b/77_Salvo/csharp/Targetting/ShotSelector.cs new file mode 100644 index 000000000..695dbc42d --- /dev/null +++ b/77_Salvo/csharp/Targetting/ShotSelector.cs @@ -0,0 +1,31 @@ +namespace Salvo.Targetting; + +internal abstract class ShotSelector +{ + private readonly Fleet _source; + private readonly Dictionary _previousShots = new(); + + internal ShotSelector(Fleet source) + { + _source = source; + } + + internal int NumberOfShots => _source.Ships.Sum(s => s.Shots); + internal bool CanTargetAllRemainingSquares => NumberOfShots >= 100 - _previousShots.Count; + + internal bool WasSelectedPreviously(Position position) => _previousShots.ContainsKey(position); + + internal bool WasSelectedPreviously(Position position, out int turn) + => _previousShots.TryGetValue(position, out turn); + + internal IEnumerable GetShots(int turnNumber) + { + foreach (var shot in GetShots()) + { + _previousShots.Add(shot, turnNumber); + yield return shot; + } + } + + protected abstract IEnumerable GetShots(); +} diff --git a/77_Salvo/csharp/TurnHandler.cs b/77_Salvo/csharp/TurnHandler.cs new file mode 100644 index 000000000..afde96133 --- /dev/null +++ b/77_Salvo/csharp/TurnHandler.cs @@ -0,0 +1,92 @@ +using Salvo.Targetting; + +namespace Salvo; + +internal class TurnHandler +{ + private readonly IReadWrite _io; + private readonly Fleet _humanFleet; + private readonly Fleet _computerFleet; + private readonly bool _humanStarts; + private readonly HumanShotSelector _humanShotSelector; + private readonly ComputerShotSelector _computerShotSelector; + private readonly Func _turnAction; + private int _turnNumber; + + public TurnHandler(IReadWrite io, IRandom random) + { + _io = io; + _computerFleet = new Fleet(random); + _humanFleet = new Fleet(io); + _turnAction = AskWhoStarts() + ? () => PlayHumanTurn() ?? PlayComputerTurn() + : () => PlayComputerTurn() ?? PlayHumanTurn(); + _humanShotSelector = new HumanShotSelector(_humanFleet, io); + _computerShotSelector = new ComputerShotSelector(_computerFleet, random, io); + } + + public Winner? PlayTurn() + { + _io.Write(Strings.Turn(++_turnNumber)); + return _turnAction.Invoke(); + } + + private bool AskWhoStarts() + { + while (true) + { + var startResponse = _io.ReadString(Prompts.Start); + if (startResponse.Equals(Strings.WhereAreYourShips, StringComparison.InvariantCultureIgnoreCase)) + { + foreach (var ship in _computerFleet.Ships) + { + _io.WriteLine(ship); + } + } + else + { + return startResponse.Equals("yes", StringComparison.InvariantCultureIgnoreCase); + } + } + } + + private Winner? PlayComputerTurn() + { + var numberOfShots = _computerShotSelector.NumberOfShots; + _io.Write(Strings.IHaveShots(numberOfShots)); + if (numberOfShots == 0) { return Winner.Human; } + if (_computerShotSelector.CanTargetAllRemainingSquares) + { + _io.Write(Streams.IHaveMoreShotsThanSquares); + return Winner.Computer; + } + + _humanFleet.ReceiveShots( + _computerShotSelector.GetShots(_turnNumber), + ship => + { + _io.Write(Strings.IHit(ship.Name)); + _computerShotSelector.RecordHit(ship, _turnNumber); + }); + + return null; + } + + private Winner? PlayHumanTurn() + { + var numberOfShots = _humanShotSelector.NumberOfShots; + _io.Write(Strings.YouHaveShots(numberOfShots)); + if (numberOfShots == 0) { return Winner.Computer; } + if (_humanShotSelector.CanTargetAllRemainingSquares) + { + _io.WriteLine(Streams.YouHaveMoreShotsThanSquares); + return Winner.Human; + } + + _computerFleet.ReceiveShots( + _humanShotSelector.GetShots(_turnNumber), + ship => _io.Write(Strings.YouHit(ship.Name))); + + return null; + } +} diff --git a/77_Salvo/csharp/Winner.cs b/77_Salvo/csharp/Winner.cs new file mode 100644 index 000000000..a5a73eb47 --- /dev/null +++ b/77_Salvo/csharp/Winner.cs @@ -0,0 +1,7 @@ +namespace Salvo; + +internal enum Winner +{ + Human, + Computer +} diff --git a/77_Salvo/python/salvo.py b/77_Salvo/python/salvo.py index 368b15da9..417bebc14 100644 --- a/77_Salvo/python/salvo.py +++ b/77_Salvo/python/salvo.py @@ -169,7 +169,7 @@ def generate_ship_coordinates(ship: int) -> List[CoordinateType]: # clockwise. a vector of valid directions where the # ship does not go off the board is determined ship_len = SHIPS[ship][1] - 1 - dirs = [False for x in range(8)] + dirs = [False for _ in range(8)] dirs[0] = (start_x - ship_len) >= 1 dirs[2] = (start_y + ship_len) <= BOARD_WIDTH dirs[1] = dirs[0] and dirs[2] @@ -261,11 +261,7 @@ def generate_board() -> Tuple[BoardType, List[List[CoordinateType]]]: coords = [] while not placed: coords = generate_ship_coordinates(ship) - clear = True - for coord in coords: - if board[coord[0] - 1][coord[1] - 1] is not None: - clear = False - break + clear = all(board[coord[0] - 1][coord[1] - 1] is None for coord in coords) if clear: placed = True place_ship(board, coords, ship) @@ -291,18 +287,17 @@ def execute_shot( def calculate_shots(board: BoardType) -> int: """Examine each board and determine how many shots remaining""" - ships_found = [0 for x in range(len(SHIPS))] + ships_found = [0 for _ in range(len(SHIPS))] for x in range(BOARD_HEIGHT): for y in range(BOARD_WIDTH): square = board[x - 1][y - 1] if square is not None and square >= 0 and square < len(SHIPS): ships_found[square] = 1 - shots = 0 - for ship in range(len(ships_found)): - if ships_found[ship] == 1: - shots += SHIPS[ship][2] - - return shots + return sum( + SHIPS[ship][2] + for ship in range(len(ships_found)) + if ships_found[ship] == 1 + ) def initialize_game() -> None: @@ -413,10 +408,7 @@ def execute_turn(turn: bool, current_turn: int) -> int: # computer, we randomly pick a shot. for the # player we request shots while not valid_shot: - if turn == COMPUTER: - x, y = random_x_y() - else: - x, y = input_coord() + x, y = random_x_y() if turn == COMPUTER else input_coord() square = board[x - 1][y - 1] if square is not None and square > 10: if turn == PLAYER: diff --git a/78_Sine_Wave/README.md b/78_Sine_Wave/README.md index 32789d048..c5c340f3c 100644 --- a/78_Sine_Wave/README.md +++ b/78_Sine_Wave/README.md @@ -1,6 +1,6 @@ ### Sine Wave -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. +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 of different versions. At least those CRTs don’t look so lifeless anymore. --- diff --git a/78_Sine_Wave/rust/Cargo.lock b/78_Sine_Wave/rust/Cargo.lock new file mode 100644 index 000000000..b21cc6a2d --- /dev/null +++ b/78_Sine_Wave/rust/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "rust" +version = "0.1.0" diff --git a/79_Slalom/README.md b/79_Slalom/README.md index 8c45820c0..c95309ceb 100644 --- a/79_Slalom/README.md +++ b/79_Slalom/README.md @@ -15,8 +15,10 @@ As published in Basic Computer Games (1978): Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html -#### Porting Notes +#### Known Bugs + +- In the original version, the data pointer doesn't reset after a race is completed. This causes subsequent races to error at some future point at line 540, `READ Q'. -In the original version, the data pointer doesn't reset after a race is completed. This causes subsequent races to error at some future point at line 540, +- It also doesn't restore the data pointer after executing the MAX command to see the gate speeds, meaning that if you use this command, it effectively skips those gates, and the speeds shown are completely incorrect. - 540 READ Q +#### Porting Notes diff --git a/79_Slalom/python/slalom.py b/79_Slalom/python/slalom.py index a6833331b..e3387e6f6 100644 --- a/79_Slalom/python/slalom.py +++ b/79_Slalom/python/slalom.py @@ -97,7 +97,7 @@ def run(gates, lvl, max_speeds) -> None: case 7: speed -= int(random() * (10 - 5) + 5) - print(f" {int(speed)} M.P.H.") + print(f" {speed} M.P.H.") if speed > max_speeds[i]: if random() < ((speed - max_speeds[i]) * 0.1) + 0.2: print( @@ -184,7 +184,7 @@ def main() -> None: run(gates, lvl, max_speeds) while True: answer = ask("Do you want to play again?") - if answer == "YES" or answer == "NO": + if answer in ["YES", "NO"]: break else: print('Please type "YES" or "NO"') diff --git a/79_Slalom/slalom.bas b/79_Slalom/slalom.bas index b46cc39be..47c6d8010 100644 --- a/79_Slalom/slalom.bas +++ b/79_Slalom/slalom.bas @@ -58,7 +58,7 @@ 825 PRINT "*** SLALOM: THIS IS THE 1976 WINTER OLYMPIC GIANT SLALOM. YOU ARE" 830 PRINT " THE AMERICAN TEAM'S ONLY HOPE OF A GOLD MEDAL." 840 PRINT -845 PRINT " 0 -- TYPE THIS IS YOU WANT TO SEE HOW LONG YOU'VE TAKEN." +845 PRINT " 0 -- TYPE THIS IF YOU WANT TO SEE HOW LONG YOU'VE TAKEN." 850 PRINT " 1 -- TYPE THIS IF YOU WANT TO SPEED UP A LOT." 860 PRINT " 2 -- TYPE THIS IF YOU WANT TO SPEED UP A LITTLE." 870 PRINT " 3 -- TYPE THIS IF YOU WANT TO SPEED UP A TEENSY." diff --git a/80_Slots/README.md b/80_Slots/README.md index dcc3aabe2..522c7c811 100644 --- a/80_Slots/README.md +++ b/80_Slots/README.md @@ -17,6 +17,10 @@ As published in Basic Computer Games (1978): Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html +#### Known Bugs + +- The original program does not correctly detect identical draws in the first and third position as a double (instead, it counts as a loss). This is probably not intended. Some of the ports fix this, so that any two matches count as a double. + #### Porting Notes (please note any difficulties or challenges in porting here) diff --git a/80_Slots/python/slots.py b/80_Slots/python/slots.py index 22c2b3a3f..6464458b6 100644 --- a/80_Slots/python/slots.py +++ b/80_Slots/python/slots.py @@ -59,7 +59,7 @@ def input_betting() -> int: elif b < 1: print("Minium bet is $1") beeping() - return int(b) + return b def beeping() -> None: @@ -113,7 +113,7 @@ def adjust_profits(wheel: List[str], m: int, profits: int) -> int: else: # three different fruits print("\nYou Lost.") - profits = profits - m + profits -= m return profits diff --git a/81_Splat/python/splat.py b/81_Splat/python/splat.py index 30ec74b5d..f521f3a23 100644 --- a/81_Splat/python/splat.py +++ b/81_Splat/python/splat.py @@ -311,9 +311,9 @@ def main() -> None: z = yes_no_input("DO YOU WANT TO PLAY AGAIN") if not z: z = yes_no_input("PLEASE") - if not z: - print("SSSSSSSSSS.") - break + if not z: + print("SSSSSSSSSS.") + break if __name__ == "__main__": diff --git a/81_Splat/rust/Cargo.lock b/81_Splat/rust/Cargo.lock new file mode 100644 index 000000000..4fe2abbea --- /dev/null +++ b/81_Splat/rust/Cargo.lock @@ -0,0 +1,75 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/82_Stars/python/stars.py b/82_Stars/python/stars.py index d2024d6b4..5a5f9fed0 100644 --- a/82_Stars/python/stars.py +++ b/82_Stars/python/stars.py @@ -40,10 +40,7 @@ def print_instructions() -> None: def print_stars(secret_number, guess) -> None: diff = abs(guess - secret_number) - stars = "" - for i in range(8): - if diff < 2**i: - stars += "*" + stars = "".join("*" for i in range(8) if diff < 2**i) print(stars) @@ -51,8 +48,7 @@ def get_guess(prompt: str) -> int: while True: guess_str = input(prompt) if guess_str.isdigit(): - guess = int(guess_str) - return guess + return int(guess_str) def main() -> None: diff --git a/82_Stars/rust/Cargo.lock b/82_Stars/rust/Cargo.lock new file mode 100644 index 000000000..b13b873bb --- /dev/null +++ b/82_Stars/rust/Cargo.lock @@ -0,0 +1,75 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "stars" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/83_Stock_Market/python/Stock_Market.py b/83_Stock_Market/python/Stock_Market.py index e32572ca6..6ad3f8d1a 100644 --- a/83_Stock_Market/python/Stock_Market.py +++ b/83_Stock_Market/python/Stock_Market.py @@ -34,11 +34,10 @@ def total_assets(self) -> float: return self.cash_assets + self.stock_assets def _generate_day_change(self) -> None: - self.changes = [] - for _ in range(len(self.data)): - self.changes.append( - round(random.uniform(-5, 5), 2) - ) # Random % Change b/w -5 and 5 + self.changes: List[float] = [] + self.changes.extend( + round(random.uniform(-5, 5), 2) for _ in range(len(self.data)) + ) def update_prices(self) -> None: self._generate_day_change() @@ -65,9 +64,9 @@ def print_first_day(self) -> None: print("\nSTOCK\t\t\t\t\tINITIALS\tPRICE/SHARE($)") for stock, data in self.data.items(): if stock != "LBJ": - print("{}\t\t\t{}\t\t{}".format(data["Name"], stock, data["Price"])) + print(f'{data["Name"]}\t\t\t{stock}\t\t{data["Price"]}') else: - print("{}\t\t{}\t\t{}".format(data["Name"], stock, data["Price"])) + print(f'{data["Name"]}\t\t{stock}\t\t{data["Price"]}') self.print_exchange_average() self.print_assets() diff --git a/84_Super_Star_Trek/README.md b/84_Super_Star_Trek/README.md index 07da653f2..32f827c67 100644 --- a/84_Super_Star_Trek/README.md +++ b/84_Super_Star_Trek/README.md @@ -3,12 +3,12 @@ #### 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. +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 SPACWR (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). +As every 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 always shown 16 major regions named after celestial 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.” @@ -17,7 +17,7 @@ The relation between the Historical and Standard nomenclatures is shown in the s | | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | |---|--------------|----|-----|----|------------|----|-----|----| | 1 | ANTARES | | | | SIRIUS | | | | -| | I | II | III | IV | I | | III | IV | +| | I | II | III | IV | I | II | III | IV | | 2 | RIGEL | | | | DENEB | | | | | | I | II | III | IV | I | II | III | IV | | 3 | PROCYON | | | | CAPELLA | | | | @@ -88,11 +88,13 @@ The relation between the Historical and Standard nomenclatures is shown in the s 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. +† Designates trademark of Paramount Pictures Corporation. Used by permission of Paramount Pictures Corporation. + --- 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) +- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=172) Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html @@ -110,6 +112,5 @@ Many of the programs in this book and this collection have bugs in the original - lines `8310`,`8330`,`8430`,`8450` : Division by zero is possible - line `440` : `B9` should be initialised to 0, not 2 - #### External Links - C++: https://www.codeproject.com/Articles/28399/The-Object-Oriented-Text-Star-Trek-Game-in-C diff --git a/84_Super_Star_Trek/javascript/README.md b/84_Super_Star_Trek/javascript/README.md index 49b7ece64..06f53f122 100644 --- a/84_Super_Star_Trek/javascript/README.md +++ b/84_Super_Star_Trek/javascript/README.md @@ -1,3 +1,6 @@ -Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) +Original source downloaded [from Vintage Basic][def] -Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) +Conversion to [****J****avaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) + + +[def]: http://www.vintage-basic.net/games.html \ No newline at end of file diff --git a/84_Super_Star_Trek/python/superstartrek.py b/84_Super_Star_Trek/python/superstartrek.py index 21e03e455..892988c76 100644 --- a/84_Super_Star_Trek/python/superstartrek.py +++ b/84_Super_Star_Trek/python/superstartrek.py @@ -8,7 +8,6 @@ **** LEEDOM - APRIL & DECEMBER 1974, **** WITH A LITTLE HELP FROM HIS FRIENDS . . . -Python translation by Jack Boyce - February 2021 Output is identical to BASIC version except for a few fixes (as noted, search `bug`) and minor cleanup. """ @@ -27,8 +26,7 @@ def get_user_float(prompt: str) -> float: while True: answer = input(prompt) try: - answer_float = float(answer) - return answer_float + return float(answer) except ValueError: pass @@ -203,7 +201,7 @@ def quadrant_name(cls, row: int, col: int, region_only: bool = False) -> str: quadrant = region1[row] if col < 4 else region2[row] if not region_only: - quadrant += " " + modifier[col % 4] + quadrant += f" {modifier[col % 4]}" return quadrant @@ -336,7 +334,6 @@ def startup(self) -> None: " THE USS ENTERPRISE --- NCC-1701\n" "\n\n\n\n" ) - self.world = World() world = self.world print( "YOUR ORDERS ARE AS FOLLOWS:\n" @@ -624,7 +621,7 @@ def short_range_scan(self) -> None: for x in range(8): line = "" for y in range(8): - line = line + " " + self.world.quadrant.data[x][y].value + line = f"{line} {self.world.quadrant.data[x][y].value}" if x == 0: line += f" STARDATE {round(int(self.world.stardate * 10) * 0.1, 1)}" @@ -884,8 +881,7 @@ def damage_control(self) -> None: return for i in range(8): - if ship.damage_stats[i] < 0: - ship.damage_stats[i] = 0 + ship.damage_stats[i] = max(ship.damage_stats[i], 0) self.world.stardate += damage_sum + 0.1 def computer(self) -> None: @@ -911,7 +907,7 @@ def computer(self) -> None: print() - if com in [0, 5]: + if com in {0, 5}: if com == 5: print(" THE GALAXY") else: @@ -925,7 +921,7 @@ def computer(self) -> None: print(sep) for i in range(8): - line = " " + str(i + 1) + " " + line = f" {str(i + 1)} " if com == 5: g2s = Quadrant.quadrant_name(i, 0, True) @@ -1123,12 +1119,11 @@ def print_direction(source: Point, to: Point) -> None: else: base = 1 delta1, delta2 = delta2, delta1 + elif delta1 > 0: + base = 3 else: - if delta1 > 0: - base = 3 - else: - base = 5 - delta1, delta2 = delta2, delta1 + base = 5 + delta1, delta2 = delta2, delta1 delta1, delta2 = abs(delta1), abs(delta2) diff --git a/84_Super_Star_Trek/python/superstartrekins.py b/84_Super_Star_Trek/python/superstartrekins.py index 59a8a61bd..6060ad9d4 100644 --- a/84_Super_Star_Trek/python/superstartrekins.py +++ b/84_Super_Star_Trek/python/superstartrekins.py @@ -17,13 +17,13 @@ def print_header() -> None: for _ in range(12): print() t10 = " " * 10 - print(t10 + "*************************************") - print(t10 + "* *") - print(t10 + "* *") - print(t10 + "* * * SUPER STAR TREK * * *") - print(t10 + "* *") - print(t10 + "* *") - print(t10 + "*************************************") + print(f"{t10}*************************************") + print(f"{t10}* *") + print(f"{t10}* *") + print(f"{t10}* * * SUPER STAR TREK * * *") + print(f"{t10}* *") + print(f"{t10}* *") + print(f"{t10}*************************************") for _ in range(8): print() diff --git a/84_Super_Star_Trek/rust/Cargo.lock b/84_Super_Star_Trek/rust/Cargo.lock new file mode 100644 index 000000000..71bbf0c8c --- /dev/null +++ b/84_Super_Star_Trek/rust/Cargo.lock @@ -0,0 +1,169 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "ctrlc" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a011bbe2c35ce9c1f143b7af6f94f29a167beb4cd1d29e6740ce836f723120e" +dependencies = [ + "nix", + "windows-sys", +] + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "nix" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abbbc55ad7b13aac85f9401c796dcda1b864e07fcad40ad47792eaa8932ea502" +dependencies = [ + "bitflags", + "cfg-if", + "libc", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "ctrlc", + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" diff --git a/84_Super_Star_Trek/rust/Cargo.toml b/84_Super_Star_Trek/rust/Cargo.toml new file mode 100644 index 000000000..01457249b --- /dev/null +++ b/84_Super_Star_Trek/rust/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "rust" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +ctrlc = "3.2.5" +rand = "0.8.5" diff --git a/84_Super_Star_Trek/rust/readme.md b/84_Super_Star_Trek/rust/readme.md new file mode 100644 index 000000000..ae2f58dc3 --- /dev/null +++ b/84_Super_Star_Trek/rust/readme.md @@ -0,0 +1,19 @@ +# Super Star Trek - Rust version + +Explanation of modules: + +- [main.rs](./src/main.rs) - creates the galaxy (generation functions are in model.rs as impl methods) then loops listening for commands. after each command checks for victory or defeat condtions. +- [model.rs](./src/model.rs) - all the structs and enums that represent the galaxy. key methods in here (as impl methods) are generation functions on galaxy and quadrant, and various comparison methods on the 'Pos' tuple type. +- [commands.rs](./src/commands.rs) - most of the code that implements instructions given by the player (some code logic is in the model impls, and some in view.rs if its view only). +- [view.rs](./src/view.rs) - all text printed to the output, mostly called by command.rs (like view::bad_nav for example). also contains the prompts printed to the user (e.g. view::prompts::COMMAND). +- [input.rs](./src/input.rs) - utility methods for getting input from the user, including logic for parsing numbers, repeating prompts until a correct value is provided etc. + +Basically the user is asked for the next command, this runs a function that usually checks if the command system is working, and if so will gather additional input (see next note for a slight change here), then either the model is read and info printed, or its mutated in some way (e.g. firing a torpedo, which reduces the torpedo count on the enterprise and can destroy klingons and star bases; finally the klingons fire back and can destroy the enterprise). Finally the win/lose conditions are checked before the loop repeats. + +## Changes from the original + +I have tried to keep it as close as possible. Notable changes are: + +- commands can be given with parameters in line. e.g. while 'nav' will ask for course and then warp speed in the original, here you can *optionally* also do this as one line, e.g. `nav 1 0.1` to move one sector east. I'm sorry - it was driving me insane in its original form (which is still sorted, as is partial application e.g. nav 1 to preset direction and then provide speed). +- text is mostly not uppercase, as text was in the basic version. this would be easy to change however as all text is in view.rs, but I chose not to. +- the navigation system (plotting direction, paths and collision detection) is as close as I could make it to the basic version (by using other language conversions as specification sources) but I suspect is not perfect. seems to work well enough however. \ No newline at end of file diff --git a/84_Super_Star_Trek/rust/src/commands.rs b/84_Super_Star_Trek/rust/src/commands.rs new file mode 100644 index 000000000..599327cac --- /dev/null +++ b/84_Super_Star_Trek/rust/src/commands.rs @@ -0,0 +1,584 @@ +use rand::Rng; + +use crate::{model::*, view, input::{self, param_or_prompt_value, prompt_two_values}}; + +pub fn perform_short_range_scan(galaxy: &Galaxy) { + if galaxy.enterprise.damaged.contains_key(systems::SHORT_RANGE_SCAN) { + view::scanners_out(); + return; + } + + view::short_range_scan(&galaxy) +} + +pub fn get_amount_and_set_shields(galaxy: &mut Galaxy, provided: Vec) { + + if galaxy.enterprise.damaged.contains_key(systems::SHIELD_CONTROL) { + view::inoperable(&systems::name_for(systems::SHIELD_CONTROL)); + return; + } + + view::energy_available(galaxy.enterprise.total_energy); + let value = input::param_or_prompt_value(&provided, 0, view::prompts::SHIELDS, 0, i32::MAX); + if value.is_none() { + view::shields_unchanged(); + return; + } + + let value = value.unwrap() as u16; + if value > galaxy.enterprise.total_energy { + view::ridiculous(); + view::shields_unchanged(); + return; + } + + galaxy.enterprise.shields = value; + view::shields_set(value); +} + +pub fn gather_dir_and_speed_then_move(galaxy: &mut Galaxy, provided: Vec) { + + let course = input::param_or_prompt_value(&provided, 0, view::prompts::COURSE, 1.0, 9.0); + if course.is_none() { + view::bad_course_data(); + return; + } + + let course = course.unwrap(); + + let mut max_warp = 8.0; + if galaxy.enterprise.damaged.contains_key(systems::WARP_ENGINES) { + max_warp = 0.2; + } + + let speed = input::param_or_prompt_value(&provided, 1, &view::prompts::warp_factor(max_warp), 0.0, 8.0); + if speed.is_none() { + view::bad_course_data(); + return; + } + + let speed = speed.unwrap(); + + if speed > max_warp { + view::damaged_engines(max_warp, speed); + return; + } + + klingons_move(galaxy); + klingons_fire(galaxy); + + if galaxy.enterprise.destroyed { + return; + } + + repair_systems(&mut galaxy.enterprise, speed); + repair_or_damage_random_system(&mut galaxy.enterprise); + + move_enterprise(course, speed, galaxy); +} + +fn repair_systems(enterprise: &mut Enterprise, amount: f32) { + + let keys: Vec = enterprise.damaged.keys().map(|k| k.to_string()).collect(); + let mut repaired = Vec::new(); + for key in keys { + let fully_fixed = enterprise.repair_system(&key, amount); + if fully_fixed { + repaired.push(systems::name_for(&key)); + } + } + + if repaired.len() <= 0 { + return; + } + + view::damage_control_report(); + for name in repaired { + view::system_repair_completed(name); + } +} + +fn repair_or_damage_random_system(enterprise: &mut Enterprise) { + let mut rng = rand::thread_rng(); + + if rng.gen::() > 0.2 { + return; + } + + if rng.gen::() >= 0.6 { + if enterprise.damaged.len() == 0 { + return; + } + + let damaged: Vec = enterprise.damaged.keys().map(|k| k.to_string()).collect(); + let system = damaged[rng.gen_range(0..damaged.len())].to_string(); + let system_name = &systems::name_for(&system); + + enterprise.repair_system(&system, rng.gen::() * 3.0 + 1.0); + view::random_repair_report_for(system_name, false); + return; + } + + let system = systems::KEYS[rng.gen_range(0..systems::KEYS.len())].to_string(); + let system_name = &systems::name_for(&system); + + enterprise.damage_system(&system, rng.gen::() * 5.0 + 1.0); + view::random_repair_report_for(system_name, true); +} + +fn move_enterprise(course: f32, warp_speed: f32, galaxy: &mut Galaxy) { + + let ship = &mut galaxy.enterprise; + + let (mut path, mut hit_edge) = find_nav_path(ship.quadrant, ship.sector, course, warp_speed); + for i in 0..path.len() { + let (quadrant, sector) = path[i].to_local_quadrant_sector(); + if quadrant != ship.quadrant { + break; // have left current quadrant, so collision checks removed. if there is a collision at the dest... /shrug? + } + let quadrant = &galaxy.quadrants[quadrant.as_index()]; + if quadrant.sector_status(sector) != SectorStatus::Empty { + path = path[..i].into(); + hit_edge = false; + if i > 0 { + let (_, last_sector) = path[path.len() - 1].to_local_quadrant_sector(); + view::bad_nav(last_sector); + } else { + view::bad_nav(ship.sector); + return; + } + break; + } + } + + if path.len() == 0 { + if hit_edge { + view::hit_edge(ship.quadrant, ship.sector); + } + return; + } + + let energy_cost = path.len() as u16 + 10; + + if energy_cost > ship.total_energy { + view::insuffient_warp_energy(warp_speed); + return + } + + let (end_quadrant, end_sector) = path[path.len() - 1].to_local_quadrant_sector(); + if hit_edge { + view::hit_edge(end_quadrant, end_sector); + } + + if ship.quadrant != end_quadrant { + view::enter_quadrant(end_quadrant); + galaxy.scanned.insert(end_quadrant); + + if galaxy.quadrants[end_quadrant.as_index()].klingons.len() > 0 { + view::condition_red(); + if ship.shields <= 200 { + view::danger_shields(); + } + } + } + + ship.quadrant = end_quadrant; + ship.sector = end_sector; + + let quadrant = &galaxy.quadrants[end_quadrant.as_index()]; + if quadrant.docked_at_starbase(ship.sector) { + ship.shields = 0; + ship.photon_torpedoes = MAX_PHOTON_TORPEDOES; + ship.total_energy = MAX_ENERGY; + } else { + ship.total_energy = ship.total_energy - energy_cost; + if ship.shields > ship.total_energy { + view::divert_energy_from_shields(); + ship.shields = ship.total_energy; + } + } + + view::short_range_scan(&galaxy) +} + +fn find_nav_path(start_quadrant: Pos, start_sector: Pos, course: f32, warp_speed: f32) -> (Vec, bool) { + + let (dx, dy) = calculate_delta(course); + + let mut distance = (warp_speed * 8.0) as i8; + if distance == 0 { + distance = 1; + } + + let mut last_sector = start_sector.as_galactic_sector(start_quadrant); + let mut path = Vec::new(); + let mut hit_edge; + + loop { + let nx = (last_sector.0 as f32 + dx) as i8; + let ny = (last_sector.1 as f32 + dy) as i8; + hit_edge = nx < 0 || ny < 0 || nx >= 64 || ny >= 64; + if hit_edge { + break; + } + last_sector = Pos(nx as u8, ny as u8); + path.push(last_sector); + + distance -= 1; + if distance == 0 { + break; + } + } + + (path, hit_edge) +} + +fn calculate_delta(course: f32) -> (f32, f32) { + // this course delta stuff is a translation (of a translation, of a translation...) of the original basic calcs + let dir = (course - 1.0) % 8.0; + let (dx1, dy1) = COURSES[dir as usize]; + let (dx2, dy2) = COURSES[(dir + 1.0) as usize]; + let frac = dir - (dir as i32) as f32; + + let dx = dx1 + (dx2 - dx1) * frac; + let dy = dy1 + (dy2 - dy1) * frac; + + (dx, dy) +} + +fn klingons_move(galaxy: &mut Galaxy) { + let quadrant = &mut galaxy.quadrants[galaxy.enterprise.quadrant.as_index()]; + for k in 0..quadrant.klingons.len() { + let new_sector: Pos; + loop { + let candidate = quadrant.find_empty_sector(); + if candidate != galaxy.enterprise.sector { + new_sector = candidate; + break; + } + } + quadrant.klingons[k].sector = new_sector; + } +} + +fn klingons_fire(galaxy: &mut Galaxy) { + let quadrant = &mut galaxy.quadrants[galaxy.enterprise.quadrant.as_index()]; + if quadrant.docked_at_starbase(galaxy.enterprise.sector) { + view::starbase_shields(); + return; + } + + for k in 0..quadrant.klingons.len() { + quadrant.klingons[k].fire_on(&mut galaxy.enterprise); + } +} + +pub fn run_damage_control(galaxy: &Galaxy) { + if galaxy.enterprise.damaged.contains_key(systems::DAMAGE_CONTROL) { + view::inoperable(&systems::name_for(systems::DAMAGE_CONTROL)); + return; + } + + view::damage_control(&galaxy.enterprise); +} + +pub fn try_starbase_ship_repair(galaxy: &mut Galaxy) { + let ship = &mut galaxy.enterprise; + let quadrant = &galaxy.quadrants[ship.quadrant.as_index()]; + if ship.damaged.len() == 0 || !quadrant.docked_at_starbase(ship.sector) { + return; + } + + let repair_delay = quadrant.star_base.as_ref().unwrap().repair_delay; + let repair_time = (ship.damaged.len() as f32 * 0.1 + repair_delay).max(0.9); + + view::repair_estimate(repair_time); + if !input::prompt_yes_no(view::prompts::REPAIR) { + return; + } + + ship.damaged.clear(); + galaxy.stardate += repair_time; + view::damage_control(&ship); +} + +pub fn perform_long_range_scan(galaxy: &mut Galaxy) { + if galaxy.enterprise.damaged.contains_key(systems::LONG_RANGE_SCAN) { + view::inoperable(&systems::name_for(systems::LONG_RANGE_SCAN)); + return; + } + + let seen = view::long_range_scan(galaxy); + for pos in seen { + galaxy.scanned.insert(pos); + } +} + +pub fn access_computer(galaxy: &Galaxy, provided: Vec) { + if galaxy.enterprise.damaged.contains_key(systems::COMPUTER) { + view::inoperable(&systems::name_for(systems::COMPUTER)); + return; + } + + let operation : i32; + loop { + let entered = input::param_or_prompt_value(&provided, 0, view::prompts::COMPUTER, 0, 5); + if entered.is_none() { + view::computer_options(); + } else { + operation = entered.unwrap(); + break; + } + } + + match operation { + 0 => view::galaxy_scanned_map(galaxy), + 1 => { + view::status_report(galaxy); + run_damage_control(galaxy); + }, + 2 => show_klingon_direction_data(galaxy), + 3 => show_starbase_direction_data(galaxy), + 4 => direction_dist_calculator(galaxy), + 5 => view::galaxy_region_map(), + _ => () // unreachable + } +} + +fn show_klingon_direction_data(galaxy: &Galaxy) { + let quadrant = &galaxy.quadrants[galaxy.enterprise.quadrant.as_index()]; + if quadrant.klingons.len() == 0 { + view::no_local_enemies(); + return; + } + + view::klingon_report(quadrant.klingons.len() > 1); + let origin = galaxy.enterprise.sector; + for k in &quadrant.klingons { + let target = k.sector; + view::direction_distance(origin.direction(target), origin.dist(target)) + } +} + +fn show_starbase_direction_data(galaxy: &Galaxy) { + let quadrant = &galaxy.quadrants[galaxy.enterprise.quadrant.as_index()]; + match &quadrant.star_base { + None => { + view::no_local_starbase(); + return; + }, + Some(s) => { + view::starbase_report(); + let origin = galaxy.enterprise.sector; + let target = s.sector; + view::direction_distance(origin.direction(target), origin.dist(target)) + } + } +} + +fn direction_dist_calculator(galaxy: &Galaxy) { + view::direction_dist_intro(&galaxy.enterprise); + loop { + let coords1 = prompt_two_values(view::prompts::INITIAL_COORDS, 1, 8).map(|(x, y)| Pos(x, y)); + if coords1.is_none() { + continue; + } + let coords2 = prompt_two_values(view::prompts::TARGET_COORDS, 1, 8).map(|(x, y)| Pos(x, y)); + if coords2.is_none() { + continue; + } + let dir = coords1.unwrap().direction(coords2.unwrap()); + let dist = coords1.unwrap().dist(coords2.unwrap()); + view::direction_distance(dir, dist); + break; + } +} + +pub fn get_power_and_fire_phasers(galaxy: &mut Galaxy, provided: Vec) { + if galaxy.enterprise.damaged.contains_key(systems::PHASERS) { + view::inoperable(&systems::name_for(systems::PHASERS)); + return; + } + + let quadrant = &mut galaxy.quadrants[galaxy.enterprise.quadrant.as_index()]; + if quadrant.klingons.len() == 0 { + view::no_local_enemies(); + return; + } + + let computer_damaged = galaxy.enterprise.damaged.contains_key(systems::COMPUTER); + if computer_damaged { + view::computer_accuracy_issue(); + } + + let available_energy = galaxy.enterprise.total_energy - galaxy.enterprise.shields; + view::phasers_locked(available_energy); + let mut power: f32; + loop { + let setting = param_or_prompt_value(&provided, 0, view::prompts::PHASERS, 0, available_energy); + if setting.is_some() { + power = setting.unwrap() as f32; + break; + } + } + + if power == 0.0 { + return; + } + + galaxy.enterprise.total_energy -= power as u16; + + let mut rng = rand::thread_rng(); + if computer_damaged { + power *= rng.gen::(); + } + + let per_enemy = power / quadrant.klingons.len() as f32; + + for k in &mut quadrant.klingons { + let dist = k.sector.abs_diff(galaxy.enterprise.sector) as f32; + let hit_strength = per_enemy / dist * (2.0 + rng.gen::()); + if hit_strength < 0.15 * k.energy { + view::no_damage(k.sector); + } else { + k.energy -= hit_strength; + view::hit_on_klingon(hit_strength, k.sector); + if k.energy > 0.0 { + view::klingon_remaining_energy(k.energy); + } else { + view::klingon_destroyed(); + } + } + } + + quadrant.klingons.retain(|k| k.energy > 0.0); + + klingons_fire(galaxy); +} + +pub fn gather_dir_and_launch_torpedo(galaxy: &mut Galaxy, provided: Vec) { + let star_bases = galaxy.remaining_starbases(); + let ship = &mut galaxy.enterprise; + + if ship.damaged.contains_key(systems::TORPEDOES) { + view::inoperable(&systems::name_for(systems::TORPEDOES)); + return; + } + + if ship.photon_torpedoes == 0 { + view::no_torpedoes_remaining(); + return; + } + + let course = input::param_or_prompt_value(&provided, 0, view::prompts::TORPEDO_COURSE, 1.0, 9.0); + if course.is_none() { + view::bad_torpedo_course(); + return; + } + + ship.photon_torpedoes -= 1; + view::torpedo_track(); + + let path = find_torpedo_path(ship.sector, course.unwrap()); + let quadrant = &mut galaxy.quadrants[ship.quadrant.as_index()]; + let mut hit = false; + for p in path { + view::torpedo_path(p); + match quadrant.sector_status(p) { + SectorStatus::Empty => continue, + SectorStatus::Star => { + hit = true; + view::star_absorbed_torpedo(p); + break; + }, + SectorStatus::Klingon => { + hit = true; + quadrant.get_klingon(p).unwrap().energy = 0.0; + quadrant.klingons.retain(|k| k.energy > 0.0); + view::klingon_destroyed(); + break; + }, + SectorStatus::StarBase => { + hit = true; + quadrant.star_base = None; + let remaining = star_bases - 1; + view::destroyed_starbase(remaining > 0); + if remaining == 0 { + ship.destroyed = true; + } + break; + } + } + } + + if ship.destroyed { // if you wiped out the last starbase, trigger game over + return; + } + + if !hit { + view::torpedo_missed(); + } + + klingons_fire(galaxy); +} + +fn find_torpedo_path(start_sector: Pos, course: f32) -> Vec { + + let (dx, dy) = calculate_delta(course); + + let mut last_sector = start_sector; + let mut path = Vec::new(); + + let mut nx = last_sector.0 as f32; + let mut ny = last_sector.1 as f32; + + loop { + nx += dx; + ny += dy; + if nx < 0.0 || ny < 0.0 || nx >= 8.0 || ny >= 8.0 { + break; + } + let step = Pos(nx.round() as u8, ny.round() as u8); + if step != last_sector { + last_sector = step; + path.push(last_sector); + } + } + + path +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_find_torpedo_path() { + let path = find_torpedo_path(Pos(0, 0), 7.45); + assert_eq!( + *path.last().unwrap(), + Pos(7, 3), + ); + assert_eq!( + path, + vec!(Pos(1, 0), Pos(2, 1), Pos(3, 1), Pos(4, 2), Pos(5, 2), Pos(6, 3), Pos(7, 3)) + ); + + assert_eq!( + find_torpedo_path(Pos(4, 7), 4.5), + vec!(Pos(4, 6), Pos(3, 5), Pos(3, 4), Pos(2, 3), Pos(2, 2), Pos(1, 1), Pos(1, 0)) + ); + + let enterprise = Pos(3, 3); + for y in 0..=7 { + for x in 0..=7 { + let pos = Pos(y, x); + if pos != enterprise { + let course = enterprise.direction(pos); + let path = find_torpedo_path(enterprise, course); + assert!(path.contains(&pos)); + }; + }; + }; + } +} diff --git a/84_Super_Star_Trek/rust/src/input.rs b/84_Super_Star_Trek/rust/src/input.rs new file mode 100644 index 000000000..62d4daee9 --- /dev/null +++ b/84_Super_Star_Trek/rust/src/input.rs @@ -0,0 +1,73 @@ +use std::{io::{stdin, stdout, Write}, str::FromStr}; + +pub fn prompt(prompt_text: &str) -> Vec { + let stdin = stdin(); + let mut stdout = stdout(); + + print!("{prompt_text} "); + let _ = stdout.flush(); + + let mut buffer = String::new(); + if let Ok(_) = stdin.read_line(&mut buffer) { + return buffer.trim_end().split([' ', ',']).map(|s| s.to_string()).collect(); + } + Vec::new() +} + +pub fn prompt_yes_no(prompt_text: &str) -> bool { + loop { + let response = prompt(&format!("{prompt_text} (Y/N)")); + if response.len() == 0 { + continue; + } + let first_word = response[0].to_uppercase(); + if first_word.starts_with("Y") { + return true; + } + if first_word.starts_with("N") { + return false; + } + } +} + +pub fn prompt_value(prompt_text: &str, min: T, max: T) -> Option { + let passed = prompt(prompt_text); + if passed.len() != 1 { + return None + } + match passed[0].parse::() { + Ok(n) if (n >= min && n <= max) => Some(n), + _ => None + } +} + +pub fn param_or_prompt_value(params: &Vec, param_pos: usize, prompt_text: &str, min: T, max: T) -> Option { + let mut res: Option = None; + if params.len() > param_pos { + match params[param_pos].parse::() { + Ok(n) if (n >= min && n <= max) => res = Some(n), + _ => () + } + } + if res.is_some() { + return res; + } + return prompt_value::(prompt_text, min, max); +} + +pub fn prompt_two_values(prompt_text: &str, min: T, max: T) -> Option<(T, T)> { + let passed = prompt(prompt_text); + if passed.len() != 2 { + return None + } + match passed[0].parse::() { + Ok(n1) if (n1 >= min && n1 <= max) => { + match passed[1].parse::() { + Ok(n2) if (n2 >= min && n2 <= max) => + Some((n1, n2)), + _ => None + } + } + _ => None + } +} \ No newline at end of file diff --git a/84_Super_Star_Trek/rust/src/main.rs b/84_Super_Star_Trek/rust/src/main.rs new file mode 100644 index 000000000..4c1a277ef --- /dev/null +++ b/84_Super_Star_Trek/rust/src/main.rs @@ -0,0 +1,70 @@ +use std::process::exit; + +use input::{prompt, prompt_yes_no}; +use model::{Galaxy, systems}; + +mod input; +mod model; +mod commands; +mod view; + +fn main() { + ctrlc::set_handler(move || { exit(0) }) + .expect("Error setting Ctrl-C handler"); + + view::title(); + if prompt_yes_no(view::prompts::INSTRUCTIONS) { + view::full_instructions(); + let _ = input::prompt(view::prompts::WHEN_READY); + } + + let mut galaxy = Galaxy::generate_new(); + let initial_klingons = galaxy.remaining_klingons(); + let initial_stardate = galaxy.stardate; + + view::enterprise(); + view::intro(&galaxy); + let _ = input::prompt(view::prompts::WHEN_READY); + + view::starting_quadrant(galaxy.enterprise.quadrant); + view::short_range_scan(&galaxy); + + loop { + let command = input::prompt(view::prompts::COMMAND); + if command.len() == 0 { + continue; + } + match command[0].to_uppercase().as_str() { // order is weird because i built it in this order :) + systems::SHORT_RANGE_SCAN => commands::perform_short_range_scan(&galaxy), + systems::WARP_ENGINES => commands::gather_dir_and_speed_then_move(&mut galaxy, command[1..].into()), + systems::SHIELD_CONTROL => commands::get_amount_and_set_shields(&mut galaxy, command[1..].into()), + systems::DAMAGE_CONTROL => { + commands::run_damage_control(&galaxy); + commands::try_starbase_ship_repair(&mut galaxy); + } + systems::LONG_RANGE_SCAN => commands::perform_long_range_scan(&mut galaxy), + systems::COMPUTER => commands::access_computer(&galaxy, command[1..].into()), + systems::PHASERS => commands::get_power_and_fire_phasers(&mut galaxy, command[1..].into()), + systems::TORPEDOES => commands::gather_dir_and_launch_torpedo(&mut galaxy, command[1..].into()), + systems::RESIGN => galaxy.enterprise.destroyed = true, + _ => view::print_command_help() + } + + if galaxy.enterprise.destroyed || galaxy.enterprise.is_stranded() || galaxy.stardate >= galaxy.final_stardate { + view::end_game_failure(&galaxy); + if galaxy.remaining_klingons() > 0 && galaxy.remaining_starbases() > 0 && galaxy.stardate < galaxy.final_stardate { + view::replay(); + let result = prompt(""); + if result.len() > 0 && result[0].to_uppercase() == "AYE" { + galaxy.enterprise = Galaxy::new_captain(&galaxy.quadrants); + continue; + } + } + break; + } else if galaxy.remaining_klingons() == 0 { + let efficiency = 1000.0 * f32::powi(initial_klingons as f32 / (galaxy.stardate - initial_stardate), 2); + view::congratulations(efficiency); + break; + } + } +} diff --git a/84_Super_Star_Trek/rust/src/model.rs b/84_Super_Star_Trek/rust/src/model.rs new file mode 100644 index 000000000..aa08d7345 --- /dev/null +++ b/84_Super_Star_Trek/rust/src/model.rs @@ -0,0 +1,355 @@ +use std::{ops::{Mul, Add}, fmt::Display, collections::{HashMap, HashSet}}; + +use rand::Rng; + +use crate::view; + +pub struct Galaxy { + pub stardate: f32, + pub final_stardate: f32, + pub quadrants: Vec, + pub scanned: HashSet, + pub enterprise: Enterprise +} + +pub struct Quadrant { + pub stars: Vec, + pub star_base: Option, + pub klingons: Vec +} + +pub struct StarBase { + pub sector: Pos, + pub repair_delay: f32, +} + +pub struct Klingon { + pub sector: Pos, + pub energy: f32 +} + +impl Klingon { + pub fn fire_on(&mut self, enterprise: &mut Enterprise) { + let mut rng = rand::thread_rng(); + let attack_strength = rng.gen::(); + let dist_to_enterprise = self.sector.abs_diff(enterprise.sector) as f32; + let hit_strength = self.energy * (2.0 + attack_strength) / dist_to_enterprise; + + self.energy /= 3.0 + attack_strength; + + enterprise.take_hit(self.sector, hit_strength as u16); + } +} + +pub struct Enterprise { + pub destroyed: bool, + pub damaged: HashMap, + pub quadrant: Pos, + pub sector: Pos, + pub photon_torpedoes: u8, + pub total_energy: u16, + pub shields: u16, +} +impl Enterprise { + fn take_hit(&mut self, sector: Pos, hit_strength: u16) { + if self.destroyed { + return; + } + + view::enterprise_hit(&hit_strength, sector); + + if self.shields <= hit_strength { + view::enterprise_destroyed(); + self.destroyed = true; + return; + } + + self.shields -= hit_strength; + + view::shields_hit(self.shields); + + if hit_strength >= 20 { + self.take_damage(hit_strength) + } + } + + fn take_damage(&mut self, hit_strength: u16) { + let mut rng = rand::thread_rng(); + + let hit_past_shield = hit_strength as f32 / self.shields as f32; + if rng.gen::() > 0.6 || hit_past_shield < 0.02 { + return + } + + let system = systems::KEYS[rng.gen_range(0..systems::KEYS.len())].to_string(); + let damage = hit_past_shield + rng.gen::() * 0.5; + self.damage_system(&system, damage); + } + + pub fn damage_system(&mut self, system: &str, damage: f32) { + self.damaged.entry(system.to_string()).and_modify(|d| *d -= damage).or_insert(-damage); + } + + pub fn repair_system(&mut self, system: &str, amount: f32) -> bool { + let existing_damage = self.damaged[system]; + if existing_damage + amount >= -0.1 { + self.damaged.remove(system); + return true; + } + + self.damaged.entry(system.to_string()).and_modify(|d| *d += amount); + return false; + } + + pub fn is_stranded(&self) -> bool { + if self.total_energy < 10 || (self.shields + 10 > self.total_energy && self.damaged.contains_key(systems::SHIELD_CONTROL)) { + view::stranded(); + return true; + } + return false; + } +} + +pub mod systems { + + pub const SHORT_RANGE_SCAN: &str = "SRS"; + pub const WARP_ENGINES: &str = "NAV"; + pub const SHIELD_CONTROL: &str = "SHE"; + pub const DAMAGE_CONTROL: &str = "DAM"; + pub const LONG_RANGE_SCAN: &str = "LRS"; + pub const COMPUTER: &str = "COM"; + pub const PHASERS: &str = "PHA"; + pub const TORPEDOES: &str = "TOR"; + + pub const RESIGN: &str = "XXX"; + + pub const KEYS: [&str; 8] = [ + SHORT_RANGE_SCAN, WARP_ENGINES, SHIELD_CONTROL, DAMAGE_CONTROL, LONG_RANGE_SCAN, COMPUTER, PHASERS, TORPEDOES + ]; + + pub fn name_for(key: &str) -> String { + match key { + SHORT_RANGE_SCAN => "Short Range Scanners".into(), + WARP_ENGINES => "Warp Engines".into(), + SHIELD_CONTROL => "Shield Control".into(), + DAMAGE_CONTROL => "Damage Control".into(), + LONG_RANGE_SCAN => "Long Range Scanners".into(), + COMPUTER => "Library-Computer".into(), + PHASERS => "Phaser Control".into(), + TORPEDOES => "Photon Tubes".into(), + _ => "Unknown".into() + } + } +} + +#[derive(PartialEq, Clone, Copy, Debug, Hash, Eq)] +pub struct Pos(pub u8, pub u8); + +impl Pos { + pub fn as_index(&self) -> usize { + (self.0 * 8 + self.1).into() + } + + pub fn abs_diff(&self, other: Pos) -> u8 { + self.0.abs_diff(other.0) + self.1.abs_diff(other.1) + } + + pub fn dist(&self, other: Pos) -> u8 { + let dx = other.0 as f32 - self.0 as f32; + let dy = other.1 as f32 - self.1 as f32; + (f32::powi(dx, 2) + f32::powi(dy, 2)).sqrt() as u8 + } + + pub fn direction(&self, other: Pos) -> f32 { + // this is a replication of the original BASIC code + let dx = other.0 as f32 - self.0 as f32; + let dy = other.1 as f32 - self.1 as f32; + let dx_dominant = dx.abs() > dy.abs(); + + let frac = if dx_dominant { dy / dx } else { -dx / dy }; + let nearest_cardinal = + if dx_dominant { + if dx > 0. { 7. } else { 3. } + } else { + if dy > 0. { 1. } else { 5. } + }; + + let mut dir = nearest_cardinal + frac; + if dir < 1. { + dir += 8. + } + dir + } + + pub fn as_galactic_sector(&self, containing_quadrant: Pos) -> Self { + Pos(containing_quadrant.0 * 8 + self.0, containing_quadrant.1 * 8 + self.1) + } + + pub fn to_local_quadrant_sector(&self) -> (Self, Self) { + (Pos(self.0 / 8, self.1 / 8), Pos(self.0 % 8, self.1 % 8)) + } +} + +impl Mul for Pos { + type Output = Self; + + fn mul(self, rhs: u8) -> Self::Output { + Pos(self.0 * rhs, self.1 * rhs) + } +} + +impl Add for Pos { + type Output = Self; + + fn add(self, rhs: Pos) -> Self::Output { + Pos(self.0 + rhs.0, self.1 + rhs.1) + } +} + +impl Display for Pos { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} , {}", self.0 + 1, self.1 + 1) + } +} + +pub const COURSES : [(f32, f32); 9] = [ + (0., 1.), + (-1., 1.), + (-1., 0.), + (-1., -1.), + (0., -1.), + (1., -1.), + (1., 0.), + (1., 1.), + (0., 1.), // course 9 is equal to course 1 +]; + +#[derive(PartialEq)] +pub enum SectorStatus { + Empty, Star, StarBase, Klingon +} + +pub const MAX_PHOTON_TORPEDOES: u8 = 28; +pub const MAX_ENERGY: u16 = 3000; + +impl Galaxy { + pub fn remaining_klingons(&self) -> u8 { + let quadrants = &self.quadrants; + quadrants.into_iter().map(|q| { q.klingons.len() as u8 }).sum::() + } + + pub fn remaining_starbases(&self) -> u8 { + let quadrants = &self.quadrants; + quadrants.into_iter().filter(|q| q.star_base.is_some()).count() as u8 + } + + pub fn generate_new() -> Self { + let quadrants = Self::generate_quadrants(); + + let mut rng = rand::thread_rng(); + let stardate = rng.gen_range(20..=40) as f32 * 100.0; + + let enterprise = Self::new_captain(&quadrants); + + let mut scanned = HashSet::new(); + scanned.insert(enterprise.quadrant); + + Galaxy { + stardate, + final_stardate: stardate + rng.gen_range(25..=35) as f32, + quadrants, + scanned, + enterprise + } + } + + pub fn new_captain(quadrants: &Vec) -> Enterprise { + let mut rng = rand::thread_rng(); + let enterprise_quadrant = Pos(rng.gen_range(0..8), rng.gen_range(0..8)); + let enterprise_sector = quadrants[enterprise_quadrant.as_index()].find_empty_sector(); + Enterprise { + destroyed: false, + damaged: HashMap::new(), + quadrant: enterprise_quadrant, + sector: enterprise_sector, + photon_torpedoes: MAX_PHOTON_TORPEDOES, + total_energy: MAX_ENERGY, + shields: 0 } + } + + fn generate_quadrants() -> Vec { + let mut rng = rand::thread_rng(); + let mut result = Vec::new(); + for _ in 0..64 { + + let mut quadrant = Quadrant { stars: Vec::new(), star_base: None, klingons: Vec::new() }; + let star_count = rng.gen_range(0..=7); + for _ in 0..star_count { + quadrant.stars.push(quadrant.find_empty_sector()); + } + + if rng.gen::() > 0.96 { + quadrant.star_base = Some(StarBase { sector: quadrant.find_empty_sector(), repair_delay: rng.gen::() * 0.5 }); + } + + let klingon_count = + match rng.gen::() { + n if n > 0.98 => 3, + n if n > 0.95 => 2, + n if n > 0.8 => 1, + _ => 0 + }; + for _ in 0..klingon_count { + quadrant.klingons.push(Klingon { sector: quadrant.find_empty_sector(), energy: rng.gen_range(100..=300) as f32 }); + } + + result.push(quadrant); + } + result + } +} + +impl Quadrant { + pub fn sector_status(&self, sector: Pos) -> SectorStatus { + if self.stars.contains(§or) { + SectorStatus::Star + } else if self.is_starbase(sector) { + SectorStatus::StarBase + } else if self.has_klingon(sector) { + SectorStatus::Klingon + } else { + SectorStatus::Empty + } + } + + fn is_starbase(&self, sector: Pos) -> bool { + match &self.star_base { + None => false, + Some(p) => p.sector == sector + } + } + + fn has_klingon(&self, sector: Pos) -> bool { + let klingons = &self.klingons; + klingons.into_iter().find(|k| k.sector == sector).is_some() + } + + pub fn get_klingon(&mut self, sector: Pos) -> Option<&mut Klingon> { + let klingons = &mut self.klingons; + klingons.into_iter().find(|k| k.sector == sector) + } + + pub fn find_empty_sector(&self) -> Pos { + let mut rng = rand::thread_rng(); + loop { + let pos = Pos(rng.gen_range(0..8), rng.gen_range(0..8)); + if self.sector_status(pos) == SectorStatus::Empty { + return pos + } + } + } + + pub fn docked_at_starbase(&self, enterprise_sector: Pos) -> bool { + self.star_base.is_some() && self.star_base.as_ref().unwrap().sector.abs_diff(enterprise_sector) == 1 + } +} diff --git a/84_Super_Star_Trek/rust/src/view.rs b/84_Super_Star_Trek/rust/src/view.rs new file mode 100644 index 000000000..caf6b74d2 --- /dev/null +++ b/84_Super_Star_Trek/rust/src/view.rs @@ -0,0 +1,635 @@ +use crate::model::{Galaxy, Pos, SectorStatus, Enterprise, systems}; + +pub mod prompts { + pub const INSTRUCTIONS: &str = "Do you need instructions"; + pub const COURSE: &str = "Course (1-9)?"; + pub const TORPEDO_COURSE: &str = "Photon torpedo course (1-9)?"; + pub const SHIELDS: &str = "Number of units to shields"; + pub const REPAIR: &str = "Will you authorize the repair order"; + pub const COMPUTER: &str = "Computer active and waiting command?"; + pub const PHASERS: &str = "Number of units to fire"; + pub const WHEN_READY: &str = "Press Enter when ready to accept command"; + pub const COMMAND: &str = "Command?"; + pub const INITIAL_COORDS: &str = " Initial coordinates (X/Y)"; + pub const TARGET_COORDS: &str = " Final coordinates (X/Y)"; + + pub fn warp_factor(max_warp: f32) -> String { + format!("Warp Factor (0-{})?", max_warp) + } +} + +pub fn title() { + println!(" + + + + + + + + + + + + ************************************* + * * + * * + * * * SUPER STAR TREK * * * + * * + * * + ************************************* + + + + + + + + + + "); +} + +pub fn full_instructions() { + println!( +" INSTRUCTIONS FOR 'SUPER STAR TREK' + + 1. When you see \"Command ?\" printed, enter one of the legal + commands (NAV, SRS, LRS, PHA, TOR, SHE, DAM, COM, OR XXX). + 2. If you should type in an illegal command, you'll get a short + list of the legal commands printed out. + 3. Some commands require you to enter data (for example, the + 'NAV' command comes back with 'Course (1-9) ?'.) If you + type in illegal data (like negative numbers), then command + will be aborted. + + The galaxy is divided into an 8 X 8 quadrant grid, + and each quadrant is further divided into an 8 X 8 sector grid. + + You will be assigned a starting point somewhere in the + galaxy to begin a tour of duty as commander of the starship + Enterprise; your mission: to seek and destroy the fleet of + Klingon warships which are menacing the United Federation of + Planets. + + You have the following commands available to you as captain + of the starship Enterprise: + + NAV command = Warp Engine Control + Course is in a circular numerical 4 3 2 + vector arrangement as shown . . . + integer and real values may be ... + used. (Thus course 1.5 is half- 5 ---*--- 1 + way between 1 and 2. ... + . . . + Values may approach 9.0, which 6 7 8 + itself is equivalent to 1.0 + COURSE + One warp factor is the size of + one quadrant. Therefore, to get + from quadrant 6,5 to 5,5, you WOULD + use course 3, warp factor 1. + + SRS command = Short Range Sensor Scan + Shows you a scan of your present quadrant. + + Symbology on your sensor screen is as follows: + <*> = Your starship's position + +K+ = Klingon battle cruiser + >!< = Federation starbase (refuel/repair/re-arm here!) + * = Star + + A condensed 'status report' will also be presented. + + LRS command = Long Range Sensor Scan + Shows conditions in space for one quadrant on each side + of the Enterprise (which is in the middle of the scan). + The scan is coded in the form ###, where the units digit + is the number of stars, the tens digit is the number of + starbases, and the hundreds digit is the number of + Klingons. + + Example - 207 = 2 Klingons, No starbases, & 7 stars. + + PHA command = Phaser Control + Allows you to destroy the Klingon battle cruisers by + zapping them with suitably large units of energy to + deplete their shield power. (Remember, Klingons have + phasers, too!) + + TOR command = Photon Torpedo Control + Torpedo course is the same as used in warp engine control. + If you hit the Klingon vessel, he is destroyed and + cannot fire back at you. If you miss, you are subject to + his phaser fire. In either case, you are also subject to + the phaser fire of all other Klingons in the quadrant. + + The library-computer (COM command) has an option to + compute torpedo trajectory for you (Option 2). + + SHE command = Shield Control + Defines the number of energy units to be assigned to the + shields. Energy is taken from total ship's energy. Note + that the status display total energy includes shield energy. + + DAM command = Damage Control Report + Gives the state of repair of all devices. Where a negative + 'state of repair' shows that the device is temporarily + damaged. + + COM command = Library-Computer + The library-computer contains six options: + Option 0 = Cumulative Galactic Record + This option shows computer memory of the results of all + previous short and long range sensor scans. + Option 1 = Status Report + This option shows the number of Klingons, Stardates, + and starbases remaining in the game. + Option 2 = Photon Torpedo Data + Which gives directions and distance from the Enterprise + to all Klingons in your quadrant. + Option 3 = Starbase Nav Data + This option gives direction and distance to any + starbase within your quadrant. + Option 4 = Direction/Distance Calculator + This option allows you to enter coordinates for + direction/distance calculations. + Option 5 = Galactic Region Name Map + This option prints the names of the sixteen major + galactic regions referred to in the game. + +") +} + +pub fn enterprise() { + println!(" + + + + + + + + + + + ,------*------, + ,------------- '--- ------' + '-------- --' / / + ,---' '-------/ /--, + '----------------' + + THE USS ENTERPRISE --- NCC-1701 + + + + + + +") +} + +pub fn intro(model: &Galaxy) { + let star_bases = model.remaining_starbases(); + let mut star_base_message: String = "There is 1 starbase".into(); + if star_bases > 1 { + star_base_message = format!("There are {} starbases", star_bases); + } + println!( +"Your orders are as follows: + Destroy the {} Klingon warships which have invaded + the galaxy before they can attack federation headquarters + on stardate {:.1}. This gives you {} days. {} in the galaxy for resupplying your ship.\n", + model.remaining_klingons(), model.final_stardate, model.final_stardate - model.stardate, star_base_message) +} + +const REGION_NAMES: [&str; 16] = [ + "Antares", + "Sirius", + "Rigel", + "Deneb", + "Procyon", + "Capella", + "Vega", + "Betelgeuse", + "Canopus", + "Aldebaran", + "Altair", + "Regulus", + "Sagittarius", + "Arcturus", + "Pollux", + "Spica" +]; + +const SUB_REGION_NAMES: [&str; 4] = ["I", "II", "III", "IV"]; + +fn quadrant_name(quadrant: Pos) -> String { + format!("{} {}", + REGION_NAMES[((quadrant.0 << 1) + (quadrant.1 >> 2)) as usize], + SUB_REGION_NAMES[(quadrant.1 % 4) as usize]) +} + +pub fn starting_quadrant(quadrant: Pos) { + println!( +"\nYour mission begins with your starship located +in the galactic quadrant, '{}'.\n", quadrant_name(quadrant)) +} + +pub fn enter_quadrant(quadrant: Pos) { + println!("\nNow entering {} quadrant . . .\n", quadrant_name(quadrant)) +} + +pub fn short_range_scan(model: &Galaxy) { + let quadrant = &model.quadrants[model.enterprise.quadrant.as_index()]; + let mut condition = "GREEN"; + if quadrant.docked_at_starbase(model.enterprise.sector) { + println!("Shields dropped for docking purposes"); + condition = "DOCKED"; + } else if quadrant.klingons.len() > 0 { + condition = "*RED*"; + } else if model.enterprise.damaged.len() > 0 { + condition = "YELLOW"; + } + + let data : [String; 8] = [ + format!("Stardate {:.1}", model.stardate), + format!("Condition {}", condition), + format!("Quadrant {}", model.enterprise.quadrant), + format!("Sector {}", model.enterprise.sector), + format!("Photon torpedoes {}", model.enterprise.photon_torpedoes), + format!("Total energy {}", model.enterprise.total_energy), + format!("Shields {}", model.enterprise.shields), + format!("Klingons remaining {}", model.remaining_klingons()), + ]; + + println!("{:-^33}", ""); + for x in 0..=7 { + for y in 0..=7 { + let pos = Pos(x, y); + if &pos == &model.enterprise.sector { + print!("<*> ") + } else { + match quadrant.sector_status(pos) { + SectorStatus::Star => print!(" * "), + SectorStatus::StarBase => print!(">!< "), + SectorStatus::Klingon => print!("+K+ "), + _ => print!(" "), + } + } + } + println!("{:>9}{}", "", data[x as usize]) + } + println!("{:-^33}", ""); +} + +pub fn print_command_help() { + println!( +"Enter one of the following: + NAV (To set course) + SRS (For short range sensor scan) + LRS (For long range sensor scan) + PHA (To fire phasers) + TOR (To fire photon torpedoes) + SHE (To raise or lower shields) + DAM (For damage control reports) + COM (To call on library-computer) + XXX (To resign your command) + ") +} + +pub fn end_game_failure(galaxy: &Galaxy) { + println!( +"Is is stardate {:.1}. +There were {} Klingon battle cruisers left at +the end of your mission. +", galaxy.stardate, galaxy.remaining_klingons()); +} + +pub fn enterprise_destroyed() { + println!("The Enterprise has been destroyed. The Federation will be conquered."); +} + +pub fn bad_course_data() { + println!(" Lt. Sulu reports, 'Incorrect course data, sir!'") +} + +pub fn bad_nav(current_sector: Pos) { + println!("Warp engines shut down at sector {current_sector} dues to bad navigation") +} + +pub fn bad_torpedo_course() { + println!(" Ensign Chekov reports, 'Incorrect course data, sir!'") +} + +pub fn enterprise_hit(hit_strength: &u16, from_sector: Pos) { + println!("{hit_strength} unit hit on Enterprise from sector {from_sector}"); +} + +pub fn hit_edge(quadrant: Pos, sector: Pos) { + println!( +"Lt. Uhura report message from Starfleet Command: + 'Permission to attempt crossing of galactic perimeter + is hereby *Denied*. Shut down your engines.' + Chief Engineer Scott reports, 'Warp engines shut down + at sector {} of quadrant {}.'", sector, quadrant); +} + +pub fn condition_red() { + println!("COMBAT AREA CONDITION RED") +} + +pub fn danger_shields() { + println!(" SHIELDS DANGEROUSLY LOW ") +} + +pub fn insuffient_warp_energy(warp_speed: f32) { + println!( +"Engineering reports, 'Insufficient energy available + for maneuvering at warp {warp_speed} !'") +} + +pub fn divert_energy_from_shields() { + println!("Shield Control supplies energy to complete the maneuver.") +} + +pub fn energy_available(total_energy: u16) { + println!("Energy available = {{{total_energy}}}") +} + +pub fn shields_unchanged() { + println!("") +} + +pub fn ridiculous() { + println!("Shield Control reports, 'This is not the Federation Treasury.'") +} + +pub fn shields_set(value: u16) { + println!( +"Deflector control room report: + 'Shields now at {value} units per your command.'") +} + +pub fn shields_hit(shields: u16) { + println!(" ") +} + +pub fn inoperable(arg: &str) { + println!("{} inoperable", arg) +} + +pub fn scanners_out() { + println!("*** Short Range Sensors are out ***") +} + +pub fn damaged_engines(max_warp: f32, warp_factor: f32) { + println!( +"Warp engines are damaged. Maximum speed = warp {max_warp} + Chief Engineer Scott reports, 'The engines won't take warp {warp_factor} !'") +} + +pub fn damage_control_report() { + println!("Damage Control report:") +} + +pub fn random_repair_report_for(name: &str, damaged: bool) { + let mut message = "state of repair improved"; + if damaged { + message = "damaged"; + } + println!("Damage Control report: {name} {message}") +} + +pub fn system_repair_completed(name: String) { + println!(" {name} repair completed.") +} + +pub fn damage_control(enterprise: &Enterprise) { + println!("Device State of Repair"); + for key in systems::KEYS { + let damage = enterprise.damaged.get(key).unwrap_or(&0.0); + println!("{:<25}{}", systems::name_for(key), damage) + } + println!(); +} + +pub fn long_range_scan(galaxy: &Galaxy) -> Vec { + + let cx = galaxy.enterprise.quadrant.0 as i8; + let cy = galaxy.enterprise.quadrant.1 as i8; + + let mut seen = Vec::new(); + + println!("Long range scan for quadrant {}", galaxy.enterprise.quadrant); + println!("{:-^19}", ""); + for x in cx - 1..=cx + 1 { + for y in cy - 1..=cy + 1 { + let mut klingons = "*".into(); + let mut star_bases = "*".into(); + let mut stars = "*".into(); + + if y >= 0 && y < 8 && x >= 0 && x < 8 { + let pos = Pos(x as u8, y as u8); + seen.push(pos); + + let quadrant = &galaxy.quadrants[pos.as_index()]; + klingons = format!("{}", quadrant.klingons.len()); + star_bases = quadrant.star_base.as_ref().map_or("0", |_| "1"); + stars = format!("{}", quadrant.stars.len()); + } + + print!(": {}{}{} ", klingons, star_bases, stars) + } + println!(":"); + println!("{:-^19}", ""); + } + + seen +} + +pub fn stranded() { + println!( +"** FATAL ERROR ** You've just stranded your ship in space +You have insufficient maneuvering energy, and shield control +is presently incapable of cross-circuiting to engine room!!") +} + +pub fn computer_options() { + println!( +" 0 = Cumulative galactic record + 1 = Status report + 2 = Photon torpedo data + 3 = Starbase nav data + 4 = Direction/distance calculator + 5 = Galaxy 'region name' map") +} + +pub fn galaxy_region_map() { + println!( +" The Galaxy + 1 2 3 4 5 6 7 8 + ----- ----- ----- ----- ----- ----- ----- -----"); + for i in (0..REGION_NAMES.len()-1).step_by(2) { + println!( +"{} {:^23} {:^23} + ----- ----- ----- ----- ----- ----- ----- -----", (i/2)+1, REGION_NAMES[i], REGION_NAMES[i+1]); + } +} + +pub fn galaxy_scanned_map(galaxy: &Galaxy) { + println!( +"Computer record of galaxy for quadrant {} + 1 2 3 4 5 6 7 8 + ----- ----- ----- ----- ----- ----- ----- -----", galaxy.enterprise.quadrant); + for x in 0..8 { + print!("{} ", x+1); + for y in 0..8 { + let pos = Pos(x, y); + if galaxy.scanned.contains(&pos) { + let quadrant = &galaxy.quadrants[pos.as_index()]; + print!(" {}{}{} ", quadrant.klingons.len(), quadrant.star_base.as_ref().map_or("0", |_| "1"), quadrant.stars.len()) + } else { + print!(" *** "); + } + } + println!( +"\n ----- ----- ----- ----- ----- ----- ----- -----") + } +} + +pub fn no_local_enemies() { + println!( +"Science Officer Spock reports, 'Sensors show no enemy ships + in this quadrant'") +} + +pub fn computer_accuracy_issue() { + println!("Computer failure hampers accuracy") +} + +pub fn phasers_locked(available_energy: u16) { + println!("Phasers locked on target; Energy available = {available_energy} units") +} + +pub fn starbase_shields() { + println!("Starbase shields protect the Enterprise") +} + +pub fn repair_estimate(repair_time: f32) { + println!( +"Technicians standing by to effect repairs to your ship; +Estimated time to repair: {repair_time:.1} stardates.") +} + +pub fn no_damage(sector: Pos) { + println!("Sensors show no damage to enemy at {sector}") +} + +pub fn hit_on_klingon(hit_strength: f32, sector: Pos) { + println!("{hit_strength} unit hit on Klingon at sector {sector}") +} + +pub fn klingon_remaining_energy(energy: f32) { + println!(" (sensors show {energy} units remaining)") +} + +pub fn klingon_destroyed() { + println!("*** Klingon destroyed ***") +} + +pub fn congratulations(efficiency: f32) { + println!(" +Congratulations, Captain! The last Klingon battle cruiser +menacing the Federation has been destroyed. + +Your efficiency rating is {efficiency}. + ") +} + +pub fn replay() { + println!(" +The Federation is in need of a new starship commander +for a similar mission -- if there is a volunteer +let him step forward and enter 'Aye'") +} + +pub fn no_torpedoes_remaining() { + println!("All photon torpedoes expended") +} + +pub fn torpedo_track() { + println!("Torpedo track:") +} + +pub fn torpedo_path(sector: Pos) { + println!("{:<16}{}", "", sector) +} + +pub fn torpedo_missed() { + println!("Torpedo missed!") +} + +pub fn star_absorbed_torpedo(sector: Pos) { + println!("Star at {sector} absorbed torpedo energy.") +} + +pub fn destroyed_starbase(not_the_last_starbase: bool) { + println!("*** Starbase destroyed ***"); + if not_the_last_starbase { + println!(" +Starfleet Command reviewing your record to consider +court martial!") + } else { + println!(" +That does it, Captain!! You are hereby relieved of command +and sentenced to 99 stardates at hard labor on Cygnus 12!!") + } +} + +pub fn no_local_starbase() { + println!("Mr. Spock reports, 'Sensors show no starbases in this quadrant.'") +} + +pub fn starbase_report() { + println!("From Enterprise to Starbase:'") +} + +pub fn direction_distance(dir: f32, dist: u8) { + println!( +"Direction = {dir} +Distance = {dist}" + ) +} + +pub fn status_report(galaxy: &Galaxy) { + let klingon_count = galaxy.remaining_klingons(); + let star_bases = galaxy.remaining_starbases(); + let time_remaining = galaxy.final_stardate - galaxy.stardate; + let mut plural_starbase = ""; + if star_bases > 1 { + plural_starbase = "s"; + } + + println!(" Status report: +Klingons left: {klingon_count} +Mission must be completed in {time_remaining:.1} stardates. +The Federation is maintaining {star_bases} starbase{plural_starbase} in the galaxy. +") +} + +pub fn direction_dist_intro(enterprise: &Enterprise) { + let quadrant = enterprise.quadrant; + let sector = enterprise.sector; + println!("Direction/distance calculator: +You are at quadrant {quadrant} sector {sector} +Please enter") +} + +pub fn klingon_report(more_than_one: bool) { + let mut plural = ""; + if more_than_one { + plural = "s"; + } + println!("From Enterprise to Klingon battle cruiser{}", plural) +} \ No newline at end of file diff --git a/85_Synonym/rust/Cargo.lock b/85_Synonym/rust/Cargo.lock new file mode 100644 index 000000000..4fe2abbea --- /dev/null +++ b/85_Synonym/rust/Cargo.lock @@ -0,0 +1,75 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/85_Synonym/rust/Cargo.toml b/85_Synonym/rust/Cargo.toml new file mode 100644 index 000000000..3b1d02f52 --- /dev/null +++ b/85_Synonym/rust/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "rust" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rand = "0.8.5" diff --git a/85_Synonym/rust/README.md b/85_Synonym/rust/README.md new file mode 100644 index 000000000..4c54b94cb --- /dev/null +++ b/85_Synonym/rust/README.md @@ -0,0 +1,3 @@ +Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) + +Conversion to [Rust](https://www.rust-lang.org/) by [Jadi](https://github.com/jadijadi) diff --git a/85_Synonym/rust/src/main.rs b/85_Synonym/rust/src/main.rs new file mode 100644 index 000000000..184f51bd9 --- /dev/null +++ b/85_Synonym/rust/src/main.rs @@ -0,0 +1,102 @@ +use rand::seq::SliceRandom; +use rand::thread_rng; +use rand::Rng; +use std::io::{self, Write}; + +fn print_centered(text: &str, width: usize) { + let pad_size: usize = if width > text.len() { + (width - text.len()) / 2 + } else { + 0 + }; + println!("{}{}", " ".repeat(pad_size), text); +} + +fn print_instructions() { + println!("A SYNONYM OF A WORD MEANS ANOTHER WORD IN THE ENGLISH"); + println!("LANGUAGE WHICH HAS THE SAME OR VERY NEARLY THE SAME MEANING."); + println!("I CHOOSE A WORD -- YOU TYPE A SYNONYM."); + println!("IF YOU CAN'T THINK OF A SYNONYM, TYPE THE WORD 'HELP'"); + println!("AND I WILL TELL YOU A SYNONYM.\n"); +} + +fn ask_question(mut this_question: Vec<&str>) { + let right_words = ["RIGHT", "CORRECT", "FINE", "GOOD!", "CHECK"]; + + // use the first one in the main question + let base_word = this_question.remove(0); + + loop { + print!(" WHAT IS A SYNONYM OF {base_word}? "); + io::stdout().flush().unwrap(); + let mut answer: String = String::new(); + io::stdin() + .read_line(&mut answer) + .expect("Failed to read the line"); + let answer = answer.trim(); + if answer == "HELP" { + // remove one random from the answers and show it + let random_index = thread_rng().gen_range(0..this_question.len()); + println!( + "**** A SYNONYM OF {base_word} IS {}.", + this_question.remove(random_index) + ); + } else if this_question.contains(&answer) { + println!("{}", right_words.choose(&mut rand::thread_rng()).unwrap()); + break; + } + } +} + +fn main() { + const PAGE_WIDTH: usize = 64; + + let mut synonyms = vec![ + vec!["FIRST", "START", "BEGINNING", "ONSET", "INITIAL"], + vec!["SIMILAR", "ALIKE", "SAME", "LIKE", "RESEMBLING"], + vec!["MODEL", "PATTERN", "PROTOTYPE", "STANDARD", "CRITERION"], + vec!["SMALL", "INSIGNIFICANT", "LITTLE", "TINY", "MINUTE"], + vec!["STOP", "HALT", "STAY", "ARREST", "CHECK", "STANDSTILL"], + vec![ + "HOUSE", + "DWELLING", + "RESIDENCE", + "DOMICILE", + "LODGING", + "HABITATION", + ], + vec!["PIT", "HOLE", "HOLLOW", "WELL", "GULF", "CHASM", "ABYSS"], + vec!["PUSH", "SHOVE", "THRUST", "PROD", "POKE", "BUTT", "PRESS"], + vec!["RED", "ROUGE", "SCARLET", "CRIMSON", "FLAME", "RUBY"], + vec![ + "PAIN", + "SUFFERING", + "HURT", + "MISERY", + "DISTRESS", + "ACHE", + "DISCOMFORT", + ], + ]; + + synonyms.shuffle(&mut thread_rng()); + + print_centered("SYNONYM", PAGE_WIDTH); + print_centered("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY", PAGE_WIDTH); + println!("\n\n\n"); + + print_instructions(); + + for this_question in synonyms { + ask_question(this_question) + } + println!("SYNONYM DRILL COMPLETED."); +} + +//////////////////////////////////////////////////////////// +// Poring Notes +// Poring Note: The "HELP" function .removes a variable +// from lists and shows it. This can lead to errors when +// the list becomes empty. But since the same issue happens +// on the original BASIC program, kept it intact +//////////////////////////////////////////////////////////// diff --git a/86_Target/perl/README.md b/86_Target/perl/README.md index e69c8b819..eca6b9fa7 100644 --- a/86_Target/perl/README.md +++ b/86_Target/perl/README.md @@ -1,3 +1,9 @@ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Perl](https://www.perl.org/) + +Modified so that if the user enters "quit" or "stop" for the input, the program will exit. +This way the user doesn't have to enter Contorl-C to quit. + +Target values can be space and/or comma separated, so "1 2 3" is valid, as is "1,2,3" or even "1, 2, 3". +I believe the original Basic program wanted "1,2,3" or else each on a separate line. diff --git a/86_Target/perl/target.pl b/86_Target/perl/target.pl new file mode 100644 index 000000000..cb552a39d --- /dev/null +++ b/86_Target/perl/target.pl @@ -0,0 +1,107 @@ +#!/usr/bin/perl + +# Target program in Perl +# Modified so that if the user enters "quit" or "stop" for the input, the program will exit. +# Values can be space and/or comma separated. +# Translated by Kevin Brannen (kbrannen) + +use strict; +use warnings; + +# globals +my $R = 1; +my $R1 = 57.296; +my $Pi = 3.14159; + +print "\n"; +print " " x 33, "TARGET\n"; +print " " x 15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n"; + +print "YOU ARE THE WEAPONS OFFICER ON THE STARSHIP ENTERPRISE\n"; +print "AND THIS IS A TEST TO SEE HOW ACCURATE A SHOT YOU\n"; +print "ARE IN A THREE-DIMENSIONAL RANGE. YOU WILL BE TOLD\n"; +print "THE RADIAN OFFSET FOR THE X AND Z AXES, THE LOCATION\n"; +print "OF THE TARGET IN THREE DIMENSIONAL RECTANGULAR COORDINATES,\n"; +print "THE APPROXIMATE NUMBER OF DEGREES FROM THE X AND Z\n"; +print "AXES, AND THE APPROXIMATE DISTANCE TO THE TARGET.\n"; +print "YOU WILL THEN PROCEEED TO SHOOT AT THE TARGET UNTIL IT IS\n"; +print "DESTROYED!\n\n"; +print "GOOD LUCK!!\n\n"; + +while (1) +{ + my $A = rand(1) * 2 * $Pi; + my $B = rand(1) * 2 * $Pi; + my $P1 = 100000 * rand(1) + rand(1); + my $X = sin($B) * cos($A) * $P1; + my $Y = sin($B) * sin($A) * $P1; + my $Z = cos($B) * $P1; + print "RADIANS FROM X AXIS = $A FROM Z AXIS = $B\n"; + print "TARGET SIGHTED: APPROXIMATE COORDINATES: X=$X Y=$Y Z=$Z\n"; + + while (1) + { + my $P3; + $R++; + + if ($R == 1) { $P3 = int($P1 * .05) * 20; } + elsif ($R == 2) { $P3 = int($P1 * .1) * 10; } + elsif ($R == 3) { $P3 = int($P1 * .5) * 2; } + elsif ($R == 4) { $P3 = int($P1); } + else { $P3 = $P1; } + + print " ESTIMATED DISTANCE: $P3\n\n"; + print "INPUT ANGLE DEVIATION FROM X, DEVIATION FROM Z, DISTANCE: "; + chomp(my $ans = lc(<>)); + exit(0) if ($ans eq "quit" || $ans eq "stop"); + + my ($A1, $B1, $P2) = split(/[,\s]+/, $ans); + print "\n"; + + if ($P2 >= 20) + { + $A1 /= $R1; + $B1 /= $R1; + print "RADIANS FROM X AXIS = $A1 FROM Z AXIS = $B1\n"; + my $X1 = $P2 * sin($B1) * cos($A1); + my $Y1 = $P2 * sin($B1) * sin($A1); + my $Z1 = $P2 * cos($B1); + my $D = (($X1 - $X) ** 2 + ($Y1 - $Y) ** 2 + ($Z1 - $Z) ** 2) ** (0.5); + + if ($D <= 20) + { + print "\n * * * HIT * * * TARGET IS NON-FUNCTIONAL\n"; + print "\nDISTANCE OF EXPLOSION FROM TARGET WAS $D KILOMETERS.\n"; + print "\nMISSION ACCOMPLISHED IN $R SHOTS.\n"; + last; + } + else + { + my $X2 = $X1 - $X; + my $Y2 = $Y1 - $Y; + my $Z2 = $Z1 - $Z; + + if ($X2 < 0) { print "SHOT BEHIND TARGET ", -$X2, " KILOMETERS.\n"; } + else { print "SHOT IN FRONT OF TARGET $X2 KILOMETERS.\n"; } + + if ($Y2 < 0) { print "SHOT TO RIGHT OF TARGET ", -$Y2, " KILOMETERS.\n"; } + else { print "SHOT TO LEFT OF TARGET $Y2 KILOMETERS.\n"; } + + if ($Z2 < 0) { print "SHOT BELOW TARGET ", -$Z2, " KILOMETERS.\n"; } + else { print "SHOT ABOVE TARGET $Z2 KILOMETERS.\n"; } + + print "APPROX POSITION OF EXPLOSION: X=$X1 Y=$Y1 Z=$Z1\n"; + print " DISTANCE FROM TARGET = $D\n\n\n"; + next; + } + } + else + { + print "YOU BLEW YOURSELF UP!!\n"; + last; + } + } + + $R = 0; + print "\n\n\n\n\nNEXT TARGET...\n\n"; +} diff --git a/87_3-D_Plot/rust/Cargo.lock b/87_3-D_Plot/rust/Cargo.lock new file mode 100644 index 000000000..b21cc6a2d --- /dev/null +++ b/87_3-D_Plot/rust/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "rust" +version = "0.1.0" diff --git a/87_3-D_Plot/rust/Cargo.toml b/87_3-D_Plot/rust/Cargo.toml new file mode 100644 index 000000000..7d75412a6 --- /dev/null +++ b/87_3-D_Plot/rust/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "rust" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/87_3-D_Plot/rust/src/main.rs b/87_3-D_Plot/rust/src/main.rs new file mode 100644 index 000000000..ebd66dfa2 --- /dev/null +++ b/87_3-D_Plot/rust/src/main.rs @@ -0,0 +1,68 @@ +/** 3D Plot GAME + * https://github.com/coding-horror/basic-computer-games/blob/main/87_3-D_Plot/3dplot.bas + * Direct conversion from BASIC to Rust by Pablo Marques (marquesrs). + * No additional features or improvements were added. As a faithful translation, + * many of the code here are done in an unrecommended way by today's standards. + * 03/03/25 +*/ + +fn main() { + //1 PRINT TAB(32);"3D PLOT" + //2 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" + //3 PRINT:PRINT:PRINT + print!("{}", + format!("{}{}\n{}{}\n\n\n\n", + " ".repeat(31), + "3D PLOT", + " ".repeat(14), + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" + ) + ); + + //5 DEF FNA(Z)=30*EXP(-Z*Z/100) + let fna = |z: f32| {30.0 * f32::exp(-z*z/100.0)}; + + //100 PRINT + println!(); + + let mut line_content = String::new(); + //110 FOR X=-30 TO 30 STEP 1.5 + let mut x = -30.0; + while x <= 30.0 { + //120 L=0 + let mut l = 0; + + //130 Y1=5*INT(SQR(900-X*X)/5) + let y1 = 5.0 * (f32::sqrt(900.0-x*x)/5.0).floor(); + + //140 FOR Y=Y1 TO -Y1 STEP -5 + let mut y = y1; + while y >= -y1 { + //150 Z=INT(25+FNA(SQR(X*X+Y*Y))-.7*Y) + let z = (25.0 + fna(f32::sqrt(x*x+y*y))-0.7*y) as i32; + + //160 IF Z<=L THEN 190 + if z <= l { + y = y - 5.0; + continue; + } + //170 L=Z + l = z; + //180 PRINT TAB(Z);"*"; + while (line_content.len() as i32) < (z-1) { + line_content += " "; + } + line_content += "*"; + //190 NEXT Y + y = y - 5.0; + } + print!("{}", line_content); + line_content.clear(); + + //200 PRINT + println!(); + //210 NEXT X + x = x + 1.5; + } + //300 END +} \ No newline at end of file diff --git a/88_3-D_Tic-Tac-Toe/README.md b/88_3-D_Tic-Tac-Toe/README.md index 969de3d83..2ab13480d 100644 --- a/88_3-D_Tic-Tac-Toe/README.md +++ b/88_3-D_Tic-Tac-Toe/README.md @@ -15,6 +15,8 @@ There are at least two bugs from the original BASIC: 1. Code should only allow player to input valid 3D coordinates where every digit is between 1 and 4, but the original code allows any value between 111 and 444 (such as 297, for instance). 2. If the player moves first and the game ends in a draw, the original program will still prompt the player for a move instead of calling for a draw. +Also note that while the file is called qubit.bas (with a "T"), the title shown in the program listing is "QUBIC" (with "C")... and the sample output shows "TIC TAC TOE" instead of either of these. + --- As published in Basic Computer Games (1978): diff --git a/88_3-D_Tic-Tac-Toe/javascript/qubit.js b/88_3-D_Tic-Tac-Toe/javascript/qubit.js index 0057619fb..6f1cbe64e 100644 --- a/88_3-D_Tic-Tac-Toe/javascript/qubit.js +++ b/88_3-D_Tic-Tac-Toe/javascript/qubit.js @@ -193,7 +193,7 @@ function select_move() { } if (j > 5 - a) return false; - xa[ma[i][j]] = s; + xa[ma[i][j]] = 5; m = ma[i][j]; print("MACHINE TAKES"); show_square(m); diff --git a/88_3-D_Tic-Tac-Toe/python/qubit.py b/88_3-D_Tic-Tac-Toe/python/qubit.py index 3a8f1e096..e0dd531e8 100644 --- a/88_3-D_Tic-Tac-Toe/python/qubit.py +++ b/88_3-D_Tic-Tac-Toe/python/qubit.py @@ -137,10 +137,7 @@ def move(self, m, player) -> bool: if self.board[m] > 1: return False - if player == Player.MACHINE: - self.board[m] = 40 - else: - self.board[m] = 8 + self.board[m] = 40 if player == Player.MACHINE else 8 return True def get_3d_position(self, m) -> Tuple[int, int, int]: @@ -152,9 +149,7 @@ def get_3d_position(self, m) -> Tuple[int, int, int]: def evaluate_lines(self) -> None: self.lineValues = [0] * 76 for j in range(76): - value = 0 - for k in range(4): - value += self.board[self.lines[j][k]] + value = sum(self.board[self.lines[j][k]] for k in range(4)) self.lineValues[j] = value def strategy_mark_line(self, i) -> None: @@ -174,9 +169,7 @@ def mark_and_move(self, vlow, vhigh, vmove) -> Optional[Tuple[Move, int]]: or the machine and choose best place to play """ for i in range(76): - value = 0 - for j in range(4): - value += self.board[self.lines[i][j]] + value = sum(self.board[self.lines[i][j]] for j in range(4)) self.lineValues[i] = value if vlow <= value < vhigh: if value > vlow: @@ -186,7 +179,7 @@ def mark_and_move(self, vlow, vhigh, vmove) -> Optional[Tuple[Move, int]]: for i in range(76): value = self.lineValues[i] - if value == 4 or value == vmove: + if value in [4, vmove]: return self.move_diagonals(i, 1) return None @@ -232,11 +225,10 @@ def machine_move(self) -> Union[None, Tuple[Move, int], Tuple[Move, int, int]]: if self.board[y] == 0: return (Move.MOVES, y) - for i in range(64): - if self.board[i] == 0: - return (Move.LIKES, i) - - return (Move.DRAW, -1) + return next( + ((Move.LIKES, i) for i in range(64) if self.board[i] == 0), + (Move.DRAW, -1), + ) def human_win(self, i) -> Tuple[Move, int, int]: return (Move.HUMAN_WIN, -1, i) @@ -260,18 +252,12 @@ def move_triple(self, i) -> Tuple[Move, int]: for j in range(4): m = self.lines[i][j] if self.board[m] == 1: - if self.lineValues[i] < 40: - return (Move.YOU_FOX, m) - else: - return (Move.GET_OUT, m) + return (Move.YOU_FOX, m) if self.lineValues[i] < 40 else (Move.GET_OUT, m) return (Move.CONCEDES, -1) # choose move in corners or center boxes of square 4x4 def move_diagonals(self, i, s) -> Optional[Tuple[Move, int]]: - if 0 < (i % 4) < 3: - jrange = [1, 2] - else: - jrange = [0, 3] + jrange = [1, 2] if 0 < (i % 4) < 3 else [0, 3] for j in jrange: m = self.lines[i][j] if self.board[m] == s: @@ -379,11 +365,7 @@ def play(self) -> None: self.show_win(board, m[2]) # type: ignore break elif m[0] == Move.MACHINE_WIN: - print( - "Machine moves to {}, and wins as follows".format( - self.move_code(board, m[1]) - ) - ) + print(f"Machine moves to {self.move_code(board, m[1])}, and wins as follows") self.show_win(board, m[2]) # type: ignore break elif m[0] == Move.DRAW: diff --git a/89_Tic-Tac-Toe/python/TicTacToe_Hard.py b/89_Tic-Tac-Toe/python/TicTacToe_Hard.py index b5708d802..3a37cc6fe 100644 --- a/89_Tic-Tac-Toe/python/TicTacToe_Hard.py +++ b/89_Tic-Tac-Toe/python/TicTacToe_Hard.py @@ -8,9 +8,7 @@ def __init__(self, pick, sz=3) -> None: self.board = self.clear_board() def clear_board(self) -> List[List[str]]: - board = [["blur" for i in range(self.dim_sz)] for j in range(self.dim_sz)] - # made a 3x3 by-default board - return board + return [["blur" for _ in range(self.dim_sz)] for _ in range(self.dim_sz)] def move_record(self, r, c) -> Union[str, bool]: if r > self.dim_sz or c > self.dim_sz: @@ -86,17 +84,12 @@ def check_win(self) -> int: # 1 you won, 0 computer won, -1 tie if flag11 or flag12: return 1 - if flag21 or flag22: - return 0 - - return -1 + return 0 if flag21 or flag22 else -1 def next_move(self) -> Union[Tuple[int, int], Tuple[List[int], List[int]]]: available_moves = [] # will carry all available moves player_win_spot = [] # if player (user Wins) - comp_pick = "O" - if self.pick == "O": - comp_pick = "X" + comp_pick = "X" if self.pick == "O" else "O" for i in range(0, self.dim_sz): for j in range(0, self.dim_sz): @@ -113,13 +106,13 @@ def next_move(self) -> Union[Tuple[int, int], Tuple[List[int], List[int]]]: player_win_spot.append(t) self.board[i][j] = "blur" - if len(player_win_spot) != 0: + if player_win_spot: self.board[player_win_spot[0][0]][player_win_spot[0][1]] = comp_pick return player_win_spot[0][0], player_win_spot[0][1] if len(available_moves) == 1: self.board[available_moves[0][0]][available_moves[0][1]] = comp_pick return [available_moves[0][0]], [available_moves[0][1]] - if len(available_moves) == 0: + if not available_moves: return -1, -1 c1, c2 = self.dim_sz // 2, self.dim_sz // 2 @@ -172,22 +165,19 @@ def display(game: TicTacToe) -> None: for i in range(0, game.dim_sz): for j in range(0, game.dim_sz - 1): if game.board[i][j] == "blur": - line1 = line1 + " |" + line1 = f"{line1} |" else: - line1 = line1 + " " + game.board[i][j] + " |" + line1 = f"{line1} {game.board[i][j]} |" if game.board[i][game.dim_sz - 1] == "blur": line1 = line1 + " \n" else: - line1 = line1 + " " + game.board[i][game.dim_sz - 1] + " \n" + line1 = f"{line1} {game.board[i][game.dim_sz - 1]} \n" print(line1, "\n\n") def main() -> None: pick = input("Pick 'X' or 'O' ").strip().upper() - if pick == "O": - game = TicTacToe("O") - else: - game = TicTacToe("X") + game = TicTacToe("O") if pick == "O" else TicTacToe("X") display(game=game) while True: temp: Union[bool, str] = False diff --git a/89_Tic-Tac-Toe/python/tictactoe2.py b/89_Tic-Tac-Toe/python/tictactoe2.py index 210ff2e7c..e6fce18a2 100755 --- a/89_Tic-Tac-Toe/python/tictactoe2.py +++ b/89_Tic-Tac-Toe/python/tictactoe2.py @@ -52,14 +52,15 @@ def line_170(board, g, h, j, k): def line_150(board, g, h, j, k): if board[k] != g: # line 150 - if ( - board[k] == h # line 160 - or board[k + 6] != g # line 161 - or board[k + 3] != g - ): # line 162 - return -1 # Goto 170 - else: - return k + 3 # Line 163 + return ( + -1 + if ( + board[k] == h # line 160 + or board[k + 6] != g # line 161 + or board[k + 3] != g + ) + else k + 3 + ) elif board[k + 6] != g: # line 152 if board[k + 6] != 0 or board[k + 3] != g: # line 165 return -1 # Goto 170 @@ -70,16 +71,7 @@ def line_150(board, g, h, j, k): def line_120(board, g, h, j, k): - if board[j] != g: - if board[j] == h or board[j + 2] != g or board[j + 1] != g: - if board[k] != g: - if board[k + 6] != g and (board[k + 6] != 0 or board[k + 3] != g): - # 450 IF G=1 THEN 465 - pass - elif board[j + 2] is not g: # Line 122 - pass - elif board[j + 1] is not OccupiedBy.EMPTY: - pass + pass def line_118(board, g, h): @@ -123,12 +115,13 @@ def think(board, g, h, moves): def render_board(board, space_mapping): vertical_divider = "!" horizontal_divider = "---+---+---" - lines = [] - lines.append(vertical_divider.join(space_mapping[space] for space in board[0:3])) - lines.append(horizontal_divider) - lines.append(vertical_divider.join(space_mapping[space] for space in board[3:6])) - lines.append(horizontal_divider) - lines.append(vertical_divider.join(space_mapping[space] for space in board[6:9])) + lines = [ + vertical_divider.join(space_mapping[space] for space in board[:3]), + horizontal_divider, + vertical_divider.join((space_mapping[space] for space in board[3:6])), + horizontal_divider, + vertical_divider.join((space_mapping[space] for space in board[6:9])), + ] return "\n".join(lines) diff --git a/89_Tic-Tac-Toe/rust/Cargo.lock b/89_Tic-Tac-Toe/rust/Cargo.lock new file mode 100644 index 000000000..b21cc6a2d --- /dev/null +++ b/89_Tic-Tac-Toe/rust/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "rust" +version = "0.1.0" diff --git a/90_Tower/lua/tower.lua b/90_Tower/lua/tower.lua new file mode 100644 index 000000000..b86539a48 --- /dev/null +++ b/90_Tower/lua/tower.lua @@ -0,0 +1,268 @@ +print [[ + + TOWERS +CREATIVE COMPUTING MORRISTOWN, NEW JERSY + + +]] + +local MAX_DISKS = 7 +local MAX_DISK_SIZE = 15 +local MAX_MOVES = 128 +local NUM_TOWERS = 3 + +local towers = { + { size = 0, elem = {} }, + { size = 0, elem = {} }, + { size = 0, elem = {} }, +} + +local total_disks +function ask_how_many_disks() + + local keep_asking = true + local input + local errors = 0 + + while keep_asking do + + io.write(string.format("HOW MANY DISKS DO YOU WANT TO MOVE (%d IS MAX)? ", MAX_DISKS) ) + input = io.read("*number") + io.read() --get rid of the remaining newline character + + if input ~= nil and input>0 and input<=MAX_DISKS then + keep_asking = false + + else + errors = errors + 1 + if errors > 2 then + print "ALL RIGHT, WISE GUY, IF YOU CAN'T PLAY THE GAME RIGHT, I'LL" + print "JUST TAKE MY PUZZLE AND GO HOME. SO LONG." + os.exit() + end + + print "SORRY, BUT I CAN'T DO THAT JOB FOR YOU." + end + end + total_disks = input +end + +function init_game() + print("TOWERS OF HANOIR PUZZLE\n") --'\n' indicates a new line + + print "YOU MUST TRANSFER THE DISKS FROM THE LEFT TO THE RIGHT TOWER," + print "ONE AT A TIME, NEVER PUTTING A LARGER DISK ON A SMALLER DISK.\n" + + ask_how_many_disks() + + print() -- print() already creates a new line at the end, so an empty print leaves an empty line + print "IN THIS PROGRAM, WE SHALL REFER TO DISKS BY NUMERICAL CODE." + print "3 WILL REPRESENT THE SMALLEST DISK, 5 THE NEXT SIZE, 7 THE" + print "NEXT, AND SO ON, UP TO 15. IF YOU DO THE PUZZLE WITH 2 DISKS," + print "THEIR CODE NAMES WOULD BE 13 AND 15, ETC. THE NEEDLES ARE" + print "NUMBERED FROM LEFT TO RIGHT, 1 TO 3. WE WILL START WITH THE" + print "DISKS ON NEEDLE 1, AND ATTEMPT TO MOVE THEM TO NEEDLE 3.\n" + + print "GOOD LUCK!\n" + + local i = 1 + local max = MAX_DISK_SIZE + while i<= total_disks do + towers[1].elem[i] = max + max = max-2 + i = i+1 + end + + towers[1].size = total_disks + + local idx = 2 + while idx <= NUM_TOWERS do + towers[idx].size = 0 + end +end + +function print_towers() + local line = MAX_DISKS + + while line > 0 do + local twr = 1 + + while twr <=3 do + local rpt = 10 + local offset = 0 + if line <= towers[twr].size then + offset = (towers[twr].elem[line] - 1) / 2 + rpt = rpt - offset + end + io.write( string.rep(' ', rpt) ) + io.write( string.rep('*', offset) ) + io.write '*' + io.write( string.rep('*', offset) ) + io.write( string.rep(' ', rpt) ) + twr = twr + 1 + end + print '' + line = line - 1 + end + +end + +function ask_which_disk() + + local keep_asking = true + local input + local errors = 0 + while keep_asking do + + io.write("WHICH DISK WOULD YOU LIKE TO MOVE? ") + input = io.read("*number") + io.read() --get rid of the remaining newline character + + if input==nil or input > MAX_DISK_SIZE or input%2==0 or input <= MAX_DISK_SIZE-(total_disks*2) then + print "ILLEGAL ENTRY... YOU MAY ONLY TYPE 3,5,7,9,11,13 or 15." + errors = errors + 1 + if errors > 1 then + print "STOP WASTING MY TIME. GO BOTHER SOMEONE ELSE." + os.exit() + end + + --[[ + Since there are only 3 towers, it's easier to do an 'if' with three + conditions than to do a loop + ]] + elseif towers[1].elem[ towers[1].size ] ~= input and + towers[2].elem[ towers[2].size ] ~= input and + towers[3].elem[ towers[3].size ] ~= input then + + print "THAT DISK IS BELOW ANOTHER ONE. MAKE ANOTHER CHOICE." + else + keep_asking = false + end + end + + return input +end + +function ask_which_needle(dsk) + + local keep_asking = true + local input + local errors = 0 + + while keep_asking do + + io.write("PLACE DISK ON WHICH NEEDLE? ") + input = io.read("*number") + io.read() --get rid of the remaining newline character + + if input~=nil and towers[input].size > 0 and towers[input].elem[ towers[input].size ] < dsk then + print "YOU CAN'T PLACE A LARGER DISK ON TOP OF A SMALLER ONE," + print "IT MIGHT CRUSH IT!" + return 0 + + elseif input~=nil and input>=1 and input<=3 then + keep_asking = false + + else + errors = errors + 1 + if errors > 1 then + print "I TRIED TO WARN YOU, BUT YOU WOULDN'T LISTEN." + print "BYE BYE, BIG SHOT." + os.exit() --Stop program + else + print "I'LL ASSUME YOU HIT THE WRONG KEY THIS TIME. BUT WATCH IT," + print "I ONLY ALLOW ONE MISTAKE." + end + end + end + return input +end + +function is_game_over() + if towers[1].size == 0 and towers[2].size == 0 then + return true + else + return false + end +end + +function game_loop() + local moves = 0 + local dsk + local twr_to + local twr_fr + + while not is_game_over() do + moves = moves + 1 + + if moves > MAX_MOVES then + print(string.format("SORRY, BUT I HAVE ORDERS TO STOP IF YOU MAKE MORE THAN %d MOVES.", MAX_MOVES)) + os.exit() + end + + + repeat + dsk = ask_which_disk() + twr_to = ask_which_needle(dsk) + until twr_to ~= 0 + + + if towers[1].elem[ towers[1].size ] == dsk then + twr_fr = 1 + elseif towers[2].elem[ towers[2].size ] == dsk then + twr_fr = 2 + else + twr_fr = 3 + end + + towers[twr_fr].size = towers[twr_fr].size - 1 + + towers[twr_to].size = towers[twr_to].size + 1 + towers[twr_to].elem[ towers[twr_to].size ] = dsk + + print_towers() + end + + return moves +end + +function keep_playing() + + while true do + io.write("TRY AGAIN (YES OR NO)? ") + local input = io.read("*line") + + if input == "YES" or input == "yes" then + return true + elseif input == "NO" or input == "no" then + return false + else + print "'YES' OR 'NO' PLEASE" + end + end +end + +function start_loop() + + while true do + init_game() + print_towers() + + local moves = game_loop() + + --check ideal solution + if moves == (2^total_disks) - 1 then + print "CONGRATULATIONS!!" + end + + print ( string.format("YOU HAVE PERFORMED THE TASK IN %d MOVES.\n", moves) ) + + if not keep_playing() then + break + end + end + + print "\nTHANKS FOR THE GAME!" +end + +start_loop() diff --git a/90_Tower/python/tower.py b/90_Tower/python/tower.py index 7d30c9ebb..d0059e86f 100644 --- a/90_Tower/python/tower.py +++ b/90_Tower/python/tower.py @@ -10,7 +10,7 @@ def size(self) -> int: return self.__size def print(self) -> None: - print("[ %s ]" % self.size()) + print(f"[ {self.size()} ]") class Tower: @@ -21,10 +21,7 @@ def empty(self) -> bool: return len(self.__disks) == 0 def top(self) -> Optional[Disk]: - if self.empty(): - return None - else: - return self.__disks[-1] + return None if self.empty() else self.__disks[-1] def add(self, disk: Disk) -> None: if not self.empty(): @@ -42,7 +39,7 @@ def pop(self) -> Disk: return self.__disks.pop() def print(self) -> None: - r = "Needle: [%s]" % (", ".join([str(x.size()) for x in self.__disks])) + r = f'Needle: [{", ".join([str(x.size()) for x in self.__disks])}]' print(r) @@ -87,7 +84,7 @@ def pick_disk(self) -> Optional[Tower]: 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) + assert len(valids) in {0, 1} if not valids: print("THAT DISK IS BELOW ANOTHER ONE. MAKE ANOTHER CHOICE.\n") return None diff --git a/90_Tower/rust/Cargo.lock b/90_Tower/rust/Cargo.lock new file mode 100644 index 000000000..b21cc6a2d --- /dev/null +++ b/90_Tower/rust/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "rust" +version = "0.1.0" diff --git a/91_Train/lua/train.lua b/91_Train/lua/train.lua new file mode 100644 index 000000000..50214fa90 --- /dev/null +++ b/91_Train/lua/train.lua @@ -0,0 +1,59 @@ +print [[ + TRAIN + CREATIVE COMPUTING MORRISTOWN, NEW JERSY + + + + TIME - SPEED DISTANCE EXERCISE]] + +math.randomseed(os.time()) + +local ERROR_MARGIN = 5.0 + +function play() + local car_speed = 25*math.random() + 40--Between 40 and 64 + local delta_time = 15*math.random() + 5--Between 5 and 19 + local train_speed = 19*math.random() + 20--Between 20 and 38 + + print( string.format("\nA CAR TRAVELING AT %u MPH CAN MAKE A CERTAIN TRIP IN %u HOURS LESS THAN A TRAIN TRAVELING AT %u MPH.", car_speed, delta_time, train_speed) ) + + local try = true + local input + while try do + io.write("HOW LONG DOES THE TRIP TAKE BY CAR? ") + input = io.read("n") + if input == nil then + print("PLEASE INSERT A NUMBER") + else + try = false + end + io.read() + end + + local car_time = delta_time * train_speed / (car_speed - train_speed) + local percent = ( math.abs(car_time-input) * 100 / car_time + .5) + + if percent > ERROR_MARGIN then + print( string.format("SORRY. YOU WERE OFF BY %f PERCENT.", percent) ) + else + print( string.format("GOOD! ANSWER WITHIN %f PERCENT.", percent) ) + end + + print( string.format("CORRECT ANSWER IS %f HOURS.", car_time) ) +end + +function game_loop() + local keep_playing = true + while keep_playing do + play() + io.write("\nANOTHER PROBLEM (YES OR NO)? ") + answer = io.read("l") + + if not (answer == "YES" or answer == "Y" or answer == "yes" or answer == "y") then + keep_playing = false + end + end + +end + +game_loop() \ No newline at end of file diff --git a/91_Train/rust/Cargo.lock b/91_Train/rust/Cargo.lock new file mode 100644 index 000000000..4fe2abbea --- /dev/null +++ b/91_Train/rust/Cargo.lock @@ -0,0 +1,75 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/92_Trap/python/trap.py b/92_Trap/python/trap.py index 64220234d..a85be68e6 100644 --- a/92_Trap/python/trap.py +++ b/92_Trap/python/trap.py @@ -24,11 +24,10 @@ def play_game() -> None: int(item) for item in input("\nGuess # " + str(turn) + " ? ").split(",") ] - if len(user_input) == 2: - if sum(1 < x < number_max for x in user_input) == 2: - user_guess = user_input - else: - raise ValueError + if len(user_input) != 2: + raise ValueError + if sum(1 < x < number_max for x in user_input) == 2: + user_guess = user_input else: raise ValueError except (ValueError, IndexError): diff --git a/92_Trap/rust/Cargo.lock b/92_Trap/rust/Cargo.lock new file mode 100644 index 000000000..4fe2abbea --- /dev/null +++ b/92_Trap/rust/Cargo.lock @@ -0,0 +1,75 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/93_23_Matches/README.md b/93_23_Matches/README.md index 8ef8d6f8a..e4ece5fd5 100644 --- a/93_23_Matches/README.md +++ b/93_23_Matches/README.md @@ -19,4 +19,4 @@ http://www.vintage-basic.net/games.html #### Porting Notes -(please note any difficulties or challenges in porting here) +There is an oddity (you can call it a bug, but it is no big deal) in the original code. If there are only two or three matches left at the player's turn and the player picks all of them (or more), the game would still register that as a win for the player. diff --git a/93_23_Matches/python/23matches.py b/93_23_Matches/python/23matches.py index 5375a176e..293cfa1f3 100755 --- a/93_23_Matches/python/23matches.py +++ b/93_23_Matches/python/23matches.py @@ -35,7 +35,7 @@ def play_game() -> None: except ValueError: print("Please enter a number.") prompt_human = "How many do you wish to remove " - matches = matches - choice_human + matches -= choice_human if matches == 0: print("You poor boob! You took the last match! I gotcha!!") print("Ha ! Ha ! I beat you !!\n") @@ -48,7 +48,7 @@ def play_game() -> None: choice_computer = 1 elif 1 < matches < 4: choice_computer = matches - 1 - matches = matches - choice_computer + matches -= choice_computer if matches == 0: print("You won, floppy ears !") print("Think you're pretty smart !") diff --git a/93_23_Matches/rust/Cargo.lock b/93_23_Matches/rust/Cargo.lock new file mode 100644 index 000000000..96e2f79ef --- /dev/null +++ b/93_23_Matches/rust/Cargo.lock @@ -0,0 +1,16 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "fastrand" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" + +[[package]] +name = "twenty-three-matches" +version = "0.1.0" +dependencies = [ + "fastrand", +] diff --git a/93_23_Matches/rust/Cargo.toml b/93_23_Matches/rust/Cargo.toml new file mode 100644 index 000000000..a4227bbd4 --- /dev/null +++ b/93_23_Matches/rust/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "twenty-three-matches" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +fastrand = "^2.0.0" diff --git a/93_23_Matches/rust/README.md b/93_23_Matches/rust/README.md new file mode 100644 index 000000000..6249450a1 --- /dev/null +++ b/93_23_Matches/rust/README.md @@ -0,0 +1,3 @@ +Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) + +Conversion to [rust](https://www.rust-lang.org/) diff --git a/93_23_Matches/rust/src/main.rs b/93_23_Matches/rust/src/main.rs new file mode 100644 index 000000000..a14c39f58 --- /dev/null +++ b/93_23_Matches/rust/src/main.rs @@ -0,0 +1,108 @@ +use std::io; +use std::io::{stdin, stdout, BufRead, Write}; + +fn main() -> io::Result<()> { + intro(); + let mut input = stdin().lock(); + let mut buf = String::with_capacity(16); + // variable `N` in the original game + let mut matches: u8 = 23; + if fastrand::bool() { + println!("TAILS! YOU GO FIRST. \n"); + } else { + println!("HEADS! I WIN! HA! HA!\nPREPARE TO LOSE, MEATBALL-NOSE!!\n\nI TAKE 2 MATCHES"); + matches -= 2; + println!("THE NUMBER OF MATCHES IS NOW {matches}\n"); + println!("YOUR TURN -- YOU MAY TAKE 1, 2 OR 3 MATCHES."); + } + loop { + // variable `K` in the original game + let human_picked = read_matches(&mut input, &mut buf)?; + matches = matches.saturating_sub(human_picked); + if matches == 0 { + // this can only happen if the player could win with the next turn but they take too + // many matches (e.g. if there are three matches left and the player takes three instead of + // two). In the original game, this would count as a win for the player. + println!("\nYOU POOR BOOB! YOU TOOK THE LAST MATCH! I GOTCHA!!\nHA ! HA ! I BEAT YOU !!!\n\nGOOD BYE LOSER!"); + return Ok(()); + } + + println!("THERE ARE NOW {matches} MATCHES REMAINING."); + + if matches <= 1 { + println!("YOU WON, FLOPPY EARS !\nTHINK YOU'RE PRETTY SMART !\nLETS PLAY AGAIN AND I'LL BLOW YOUR SHOES OFF !!"); + return Ok(()); + } + + // variable `Z` in the original game + let ai_picked = ai_pick(matches, human_picked); + println!("MY TURN ! I REMOVE {ai_picked} MATCHES"); + matches = matches.saturating_sub(ai_picked); + // The AI will never pick the last match except for the case where only one match is left (which is handled above) + if matches == 1 { + println!("\nYOU POOR BOOB! YOU TOOK THE LAST MATCH! I GOTCHA!!\nHA ! HA ! I BEAT YOU !!!\n\nGOOD BYE LOSER!"); + return Ok(()); + } + println!("THE NUMBER OF MATCHES IS NOW {matches}\n"); + println!("YOUR TURN -- YOU MAY TAKE 1, 2 OR 3 MATCHES."); + } +} + +fn intro() { + println!( + r" 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. +" + ); +} + +fn read_matches(mut input: R, buf: &mut String) -> io::Result { + print!("HOW MANY DO YOU WISH TO REMOVE ?? "); + stdout().flush()?; + loop { + let input = read_int(&mut input, buf)?; + if input <= 0 || input > 3 { + print!("VERY FUNNY! DUMMY!\nDO YOU WANT TO PLAY OR GOOF AROUND?\nNOW, HOW MANY MATCHES DO YOU WANT ?? "); + stdout().flush()?; + } else { + return Ok(input as u8); + } + } +} + +fn read_int(mut input: R, buf: &mut String) -> io::Result { + loop { + buf.clear(); + input.read_line(buf)?; + let line = buf.trim(); + // This is implicit behaviour in the original code: empty input is equal to 0 + if line.is_empty() { + return Ok(0); + } + if let Ok(n) = line.parse::() { + return Ok(n); + } else { + print!("??REENTER\n?? "); + stdout().flush()?; + } + } +} + +fn ai_pick(matches: u8, human_picked: u8) -> u8 { + if matches < 4 { + matches - 1 + } else { + 4 - human_picked + } +} diff --git a/94_War/rust/Cargo.lock b/94_War/rust/Cargo.lock new file mode 100644 index 000000000..4fe2abbea --- /dev/null +++ b/94_War/rust/Cargo.lock @@ -0,0 +1,75 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/95_Weekday/python/weekday.py b/95_Weekday/python/weekday.py index 15cdae442..0a6b0aabd 100644 --- a/95_Weekday/python/weekday.py +++ b/95_Weekday/python/weekday.py @@ -29,11 +29,13 @@ def get_date_from_user(prompt: str) -> Tuple[int, int, int]: def get_date_from_system() -> Tuple[int, int, int]: - dt = datetime.datetime.today() + dt = datetime.datetime.now() return dt.month, dt.day, dt.year def get_day_of_week(weekday_index, day) -> str: + if weekday_index == 6 and day == 13: + return "FRIDAY THE THIRTEENTH---BEWARE!" day_names = { 1: "SUNDAY", 2: "MONDAY", @@ -44,8 +46,6 @@ def get_day_of_week(weekday_index, day) -> str: 7: "SATURDAY", } - if weekday_index == 6 and day == 13: - return "FRIDAY THE THIRTEENTH---BEWARE!" return day_names[weekday_index] @@ -58,11 +58,7 @@ def previous_day(b) -> int: def is_leap_year(year: int) -> bool: if (year % 4) != 0: return False - if (year % 100) != 0: - return True - if (year % 400) != 0: - return False - return True + return True if (year % 100) != 0 else year % 400 == 0 def adjust_day_for_leap_year(b, year): @@ -86,9 +82,9 @@ def calc_day_value(year, month, day): def deduct_time(frac, days, years_remain, months_remain, days_remain): # CALCULATE TIME IN YEARS, MONTHS, AND DAYS days_available = int(frac * days) - years_used = int(days_available / 365) + years_used = days_available // 365 days_available -= years_used * 365 - months_used = int(days_available / 30) + months_used = days_available // 30 days_used = days_available - (months_used * 30) years_remain = years_remain - years_used months_remain = months_remain - months_used @@ -238,7 +234,7 @@ def main() -> None: rem_years, rem_months, rem_days, used_years, used_months, used_days = deduct_time( 0.23, life_days, rem_years, rem_months, rem_days ) - time_report("YOU HAVE " + label, used_years, used_months, used_days) + time_report(f"YOU HAVE {label}", used_years, used_months, used_days) time_report("YOU HAVE RELAXED", rem_years, rem_months, rem_days) print() diff --git a/95_Weekday/rust/Cargo.lock b/95_Weekday/rust/Cargo.lock new file mode 100644 index 000000000..b21cc6a2d --- /dev/null +++ b/95_Weekday/rust/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "rust" +version = "0.1.0" diff --git a/96_Word/python/word.py b/96_Word/python/word.py index 8b7f0d51f..5ff7604a0 100755 --- a/96_Word/python/word.py +++ b/96_Word/python/word.py @@ -35,7 +35,7 @@ def play_game() -> None: print("You are starting a new game...") while True: guess_word = "" - while guess_word == "": + while not guess_word: guess_word = input("\nGuess a five letter word. ").upper() if guess_word == "?": break diff --git a/96_Word/rust/Cargo.lock b/96_Word/rust/Cargo.lock new file mode 100644 index 000000000..4fe2abbea --- /dev/null +++ b/96_Word/rust/Cargo.lock @@ -0,0 +1,75 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/HOW_TO_RUN_THE_GAMES.md b/HOW_TO_RUN_THE_GAMES.md index 36061f161..1d25fe417 100644 --- a/HOW_TO_RUN_THE_GAMES.md +++ b/HOW_TO_RUN_THE_GAMES.md @@ -23,13 +23,15 @@ Alternatively, for non-dotnet compatible translations, you will need [Visual Stu ## java -The Java translations can be run via the command line or from an IDE such as [Eclipse](https://www.eclipse.org/downloads/packages/release/kepler/sr1/eclipse-ide-java-developers) or [IntelliJ](https://www.jetbrains.com/idea/) +The Java translations can be run via the command line or from an IDE such as [Eclipse](https://www.eclipse.org/downloads/packages/release/kepler/sr1/eclipse-ide-java-developers) or [IntelJ][def] To run from the command line, you will need a Java SDK (eg. [Oracle JDK](https://www.oracle.com/java/technologies/downloads/) or [Open JDK](https://openjdk.java.net/)). 1. Navigate to the corresponding directory. 1. Compile the program with `javac`: - * eg. `javac AceyDuceyGame.java` + * eg. `**```python + javac + ```** AceyDuceyGame.java` 1. Run the compiled program with `java`: * eg. `java AceyDuceyGame` @@ -68,7 +70,7 @@ In addition to the java runtime you will need the `kotlinc` compiler, which can The pascal examples can be run using [Free Pascal](https://www.freepascal.org/). Additionally, `.lsi` project files can be opened with the [Lazarus Project IDE](https://www.lazarus-ide.org/). -The pascal examples include both *simple* (single-file) and *object-oriented* (in the `/object-pascal`directories) examples. +The pascal examples include both ___simple_ (single-file) and_o**bject-oriented_ (in the `/object-pascal`directories) examples. 1. You can compile the program from the command line with the `fpc` command. * eg. `fpc amazing.pas` @@ -92,7 +94,9 @@ The python translations can be run from the command line by using the `py` inter * eg. `python aceyducey.py` **Note** - +{ + "MD013": false +} Some translations include multiple versions for python, such as `acey ducey` which features versions for Python 2 (`aceyducey.py`) and Python 3 (`acey_ducey.py`) as well as an extra object-oriented version (`acey_ducey_oo.py`). You can manage and use different versions of python with [pip](https://pypi.org/project/pip/). @@ -116,3 +120,5 @@ If you don't already have Rust on your computer, you can follow the instruction 1. From the command-line, navigate to the corresponding directory. 2. Run the following command. * `cargo run` + +[def]: https://www.jetbrains.com/idea/ diff --git a/README.md b/README.md index c56d18862..39fa38a07 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ We’re updating the first million selling computer book, [BASIC Computer Games](https://en.wikipedia.org/wiki/BASIC_Computer_Games), for 2022 and beyond! -- [Read the original book](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf) (pdf) +- [Read the original book](https://archive.org/details/basiccomputergam0000unse) (pdf) - [Play the original games in your browser](https://troypress.com/wp-content/uploads/user/js-basic/index.html) ### Where can we discuss it? @@ -17,7 +17,7 @@ I have moved all [the original BASIC source code](http://www.vintage-basic.net/g Each project has subfolders corresponding to the languages we’d like to see the games ported to. This is based on the [2022 TIOBE index of top languages](https://www.tiobe.com/tiobe-index/) that are _**memory safe**_ and _**general purpose scripting languages**_ per [this post](https://discourse.codinghorror.com/t/-/7927/34): -1. C# +1. C# 2. Java 3. JavaScript 4. Kotlin @@ -40,7 +40,7 @@ If you wish to port one of the programs to a language not in our list – that i Feel free to begin converting these classic games into the above list of modern, memory safe languages. In fact, courtesy of @mojoaxel, you can even view the JavaScript versions in your web browser at -https://coding-horror.github.io/basic-computer-games/ + But first, a few guidelines: @@ -66,8 +66,113 @@ We want the general behavior of the original programs to be preserved, _however_ Please note that on the back of the Basic Computer Games book it says **Microsoft 8K Basic, Rev 4.0 was the version David Ahl used to test**, so that is the level of compatibility we are looking for.  QBasic on the DOS emulation is a later version of Basic but one that retains downwards compatibility so far in our testing. To verify behavior, try [running the programs in your browser](https://troypress.com/wp-content/uploads/user/js-basic/index.html) with [JS BASIC, effectively Applesoft BASIC](https://github.com/inexorabletash/jsbasic/). -### Have fun! +### 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 10 agreed upon languages to [Girls Who Code](https://girlswhocode.com/)**. + +### Current Progress + +

toggle for game by language table + +| Name | csharp | java | javascript | kotlin | lua | perl | python | ruby | rust | vbnet | +| ---------------------- | ------ | ---- | ---------- | ------ | --- | ---- | ------ | ---- | ---- | ----- | +| 01_Acey_Ducey | x | x | x | x | x | x | x | x | x | x | +| 02_Amazing | x | x | x | | | x | x | x | x | x | +| 03_Animal | x | x | x | x | x | x | x | x | x | x | +| 04_Awari | x | x | x | | | x | x | x | x | x | +| 05_Bagels | x | x | x | x | x | x | x | x | x | x | +| 06_Banner | x | x | x | | | x | x | x | x | x | +| 07_Basketball | x | x | x | | | x | x | x | x | x | +| 08_Batnum | x | x | x | | | x | x | x | x | x | +| 09_Battle | x | x | x | | | | x | | x | x | +| 10_Blackjack | x | x | x | | | | x | x | x | x | +| 11_Bombardment | x | x | x | | | x | x | x | x | x | +| 12_Bombs_Away | x | x | x | | x | x | x | | x | x | +| 13_Bounce | x | x | x | | | x | x | x | x | x | +| 14_Bowling | x | x | x | | | x | x | | x | x | +| 15_Boxing | x | x | x | | | x | x | | | x | +| 16_Bug | x | x | x | | | | x | x | | x | +| 17_Bullfight | x | x | x | x | | | x | | | x | +| 18_Bullseye | x | x | x | | | x | x | | x | x | +| 19_Bunny | x | x | x | | | x | x | x | x | x | +| 20_Buzzword | x | x | x | | x | x | x | x | x | x | +| 21_Calendar | x | x | x | | | x | x | x | x | x | +| 22_Change | x | x | x | | | x | x | | x | x | +| 23_Checkers | x | | x | | | x | x | x | | x | +| 24_Chemist | x | x | x | | | x | x | | x | x | +| 25_Chief | x | x | x | | x | x | x | x | x | x | +| 26_Chomp | x | x | x | | | x | x | | | x | +| 27_Civil_War | x | x | x | | | | x | | | x | +| 28_Combat | x | x | x | | | x | x | | | x | +| 29_Craps | x | x | x | | x | x | x | x | x | x | +| 30_Cube | x | x | x | | | | x | x | x | x | +| 31_Depth_Charge | x | x | x | | | x | x | x | x | x | +| 32_Diamond | x | x | x | x | | x | x | x | x | x | +| 33_Dice | x | x | x | | x | x | x | x | x | x | +| 34_Digits | x | x | x | | | x | x | | | x | +| 35_Even_Wins | x | | x | | | x | x | | x | x | +| 36_Flip_Flop | x | x | x | | | x | x | x | x | x | +| 37_Football | x | | x | | | | x | | | x | +| 38_Fur_Trader | x | x | x | | | x | x | | | x | +| 39_Golf | x | | x | | | | x | | | x | +| 40_Gomoko | x | x | x | | | x | x | | | x | +| 41_Guess | x | x | x | | | x | x | x | x | x | +| 42_Gunner | x | x | x | | | x | x | | | x | +| 43_Hammurabi | x | x | x | | | | x | | x | x | +| 44_Hangman | x | x | x | | | x | x | x | | x | +| 45_Hello | x | x | x | | x | x | x | x | x | x | +| 46_Hexapawn | x | | | | | | x | | | x | +| 47_Hi-Lo | x | | x | x | x | x | x | x | x | x | +| 48_High_IQ | x | x | x | | | | x | | | x | +| 49_Hockey | x | | x | | | | x | | | x | +| 50_Horserace | x | x | x | | | | | | x | x | +| 51_Hurkle | x | x | x | | | x | x | x | x | x | +| 52_Kinema | x | x | x | | | x | x | x | x | x | +| 53_King | x | | x | | | | x | | x | x | +| 54_Letter | x | x | x | | | x | x | x | x | x | +| 55_Life | x | x | x | | | x | x | x | x | x | +| 56_Life_for_Two | x | x | x | | | x | x | | | x | +| 57_Literature_Quiz | x | x | x | | | x | x | | x | x | +| 58_Love | x | x | x | | | x | x | x | x | x | +| 59_Lunar_LEM_Rocket | x | | x | | | | x | | x | x | +| 60_Mastermind | x | x | x | | | x | x | | x | x | +| 61_Math_Dice | x | x | x | | | x | x | x | x | x | +| 62_Mugwump | x | x | x | | | x | x | | x | x | +| 63_Name | x | x | x | x | | x | x | x | x | x | +| 64_Nicomachus | x | x | x | | | x | x | | x | x | +| 65_Nim | x | | x | | | | x | x | x | x | +| 66_Number | x | x | x | | | x | x | | x | x | +| 67_One_Check | x | x | x | | | x | x | | | x | +| 68_Orbit | x | x | x | | | x | x | x | x | x | +| 69_Pizza | x | x | x | | | x | x | x | x | x | +| 70_Poetry | x | x | x | | | x | x | x | | x | +| 71_Poker | x | x | x | | | | | | | x | +| 72_Queen | x | | x | | | x | x | | x | x | +| 73_Reverse | x | x | x | | | x | x | x | x | x | +| 74_Rock_Scissors_Paper | x | x | x | x | | x | x | x | x | x | +| 75_Roulette | x | x | x | | | x | x | | x | x | +| 76_Russian_Roulette | x | x | x | x | | x | x | x | x | x | +| 77_Salvo | x | | x | | | | x | | x | x | +| 78_Sine_Wave | x | x | x | x | | x | x | x | x | x | +| 79_Slalom | x | | x | | | | x | | | x | +| 80_Slots | x | x | x | | | x | x | x | | x | +| 81_Splat | x | x | x | | | x | x | | x | x | +| 82_Stars | x | x | x | | | x | x | x | x | x | +| 83_Stock_Market | x | x | x | | | | x | | | x | +| 84_Super_Star_Trek | x | x | x | | | | x | | x | x | +| 85_Synonym | x | x | x | | | x | x | x | x | x | +| 86_Target | x | x | x | | | x | x | | | x | +| 87_3-D_Plot | x | x | x | | | x | x | x | x | x | +| 88_3-D_Tic-Tac-Toe | x | | x | | | | x | | | x | +| 89_Tic-Tac-Toe | x | x | x | x | | x | x | | x | x | +| 90_Tower | x | x | x | | | x | x | | x | x | +| 91_Train | x | x | x | | | x | x | x | x | x | +| 92_Trap | x | x | x | | | x | x | x | x | x | +| 93_23_Matches | x | x | x | | | x | x | x | x | x | +| 94_War | x | x | x | x | | x | x | x | x | x | +| 95_Weekday | x | x | x | | | x | x | | x | x | +| 96_Word | x | x | x | | | x | x | x | x | x | + +

+ * Converted from BASIC to Java by Aldrin Misquitta (@aldrinm) + */ +public class Bullfight { + + private static final int MAX_PASSES_BEFORE_CHARGE = 3; + + public static void main(String[] args) { + printHeader(); + + Scanner scanner = new Scanner(System.in); + System.out.print("\n\n"); + + var instructionsChoice = readInstructionsChoice(scanner); + if (instructionsChoice) { + printInstructions(); + } + + Random random = new Random(); + + //initialize the game with default values + GameState gameState = new GameState(); + + //Randomly select a bull grade + int randomGrade = (int) (random.nextFloat() * BullGrade.values().length + 1); + var bullGrade = BullGrade.fromValue(randomGrade); + + printBullGradeInfo(bullGrade); + + System.out.println(); + + //D[1] in the original + gameState.picadoresDamage = firstStage("PICADO", "RES", bullGrade, random); + + //D[2] in the original + gameState.toreadoresDamage = firstStage("TOREAD", "ORES", bullGrade, random); + + boolean done = false; //controls the main game loop + + while (!done) { + + gameState.passNumber++; + System.out.printf("\n\nPASS NUMBER %d \n", gameState.passNumber); + + if (gameState.passNumber < MAX_PASSES_BEFORE_CHARGE) { + System.out.println("THE BULL IS CHARGING AT YOU! YOU ARE THE MATADOR--"); + System.out.print("DO YOU WANT TO KILL THE BULL? "); + } else { + System.out.print("HERE COMES THE BULL. TRY FOR A KILL? "); + } + + BooleanAnswer yesOrNo = readYesOrNo(scanner); + if (yesOrNo.equals(BooleanAnswer.YES)) { + gameState = attemptKillBull(bullGrade, random, gameState, scanner); + done = true; + } else { + int capeMove; + if (gameState.passNumber < MAX_PASSES_BEFORE_CHARGE) { + capeMove = readCapeMove("WHAT MOVE DO YOU MAKE WITH THE CAPE", scanner); + } else { + capeMove = readCapeMove("CAPE MOVE", scanner); + } + + //handle cape move + gameState = handleCapeMove(capeMove, random, scanner, bullGrade, gameState); + + if (gameState.matadorStatus.equals(MatadorStatus.DEFEATED) || gameState.matadorStatus.equals(MatadorStatus.DEAD)) { + done = true; + } + } + + } + + crowdReaction(gameState, bullGrade, random); + + System.out.println("\nADIOS\n\n"); + } + + private static void printBullGradeInfo(BullGrade bullGrade) { + System.out.println("\n\nYOU HAVE DRAWN A " + bullGrade.name() + " BULL."); + if (bullGrade.equals(BullGrade.AWFUL)) { + System.out.println("YOU'RE LUCKY."); + } else if (bullGrade.equals(BullGrade.SUPERB)) { + System.out.println("GOOD LUCK. YOU'LL NEED IT."); + } + } + + private static void crowdReaction(GameState gameState, BullGrade bullGrade, Random random) { + System.out.println("\n\n"); + if (!gameState.matadorStatus.equals(MatadorStatus.DEFEATED)) { + if (!gameState.matadorStatus.equals(MatadorStatus.INJURED)) { + if (gameState.bullStatus.equals(BullStatus.DEAD)) { + System.out.println("THE CROWD CHEERS!"); + } + } else { + System.out.println("THE CROWD CHEERS WILDLY!"); + } + + System.out.println("\nTHE CROWD AWARDS YOU"); + var crowdReactionScore = calculateCrowdReactionScore(gameState, bullGrade, random); + if (crowdReactionScore < 2.4) { + System.out.println("NOTHING AT ALL."); + } else if (crowdReactionScore < 4.9) { + System.out.println("ONE EAR OF THE BULL."); + } else if (crowdReactionScore < 7.4) { + System.out.println("BOTH EARS OF THE BULL!"); + System.out.println("OLE!"); + } else { + System.out.println("OLE! YOU ARE 'MUY HOMBRE'!! OLE! OLE!"); + } + + } else { + System.out.println("THE CROWD BOOS FOR TEN MINUTES. IF YOU EVER DARE TO SHOW"); + System.out.println("YOUR FACE IN A RING AGAIN, THEY SWEAR THEY WILL KILL YOU--"); + System.out.println("UNLESS THE BULL DOES FIRST."); + } + } + + private static GameState handleCapeMove(int capeMove, Random random, Scanner scanner, BullGrade bullGrade, GameState gameState) { + double m; + if (capeMove == 0) { + m = 3; + } else if (capeMove == 1) { + m = 2; + } else { + //capeMove == 2 + m = 0.5; + } + gameState.capeMovesCumulative += m; + + double f = (6 - bullGrade.getValue() + m / 10) * random.nextFloat() / ((gameState.picadoresDamage + gameState.toreadoresDamage + gameState.passNumber / 10d) * 5); + if (f >= 0.51) { + System.out.println("THE BULL HAS GORED YOU!"); + gameState = stateAfterGoring(random, scanner, gameState, bullGrade); + } + return gameState; + } + + private static GameState stateAfterGoring(Random random, Scanner scanner, GameState gameState, BullGrade bullGrade) { + GameState newGameState = gameState.newCopy(); + if (random.nextBoolean()) { + System.out.println("YOU ARE DEAD."); + newGameState.matadorStatus = MatadorStatus.DEAD; + } else { + System.out.println("YOU ARE STILL ALIVE."); + newGameState = readAndHandleForfeitDecision(random, scanner, newGameState, bullGrade); + } + return newGameState; + } + + /** + * Calculate the crowd's reaction score based on the game state plus some randomness. + * (FNC in the original code on line 1390) + */ + private static double calculateCrowdReactionScore(GameState gameState, BullGrade bullGrade, Random random) { + return calculateGameScore(gameState, bullGrade) * random.nextFloat(); + } + + /** + * Calculates the ame score based on the current state and the selected bull grade. + * (FND in the original code on line 1395) + */ + private static double calculateGameScore(GameState gameState, BullGrade bullGrade) { + return (4.5 + gameState.capeMovesCumulative / 6 - (gameState.picadoresDamage + gameState.toreadoresDamage) * 2.5 + 4 * gameState.matadorStatus.getValue() + 2 * gameState.bullStatus.getValue() - Math.pow(gameState.passNumber, 2) / 120f - bullGrade.getValue()); + } + + private static int readInt(String prompt, Scanner scanner) { + System.out.print(prompt); + while (true) { + System.out.print("? "); + String input = scanner.nextLine(); + try { + return Integer.parseInt(input); + } catch (NumberFormatException e) { + System.out.println("!NUMBER EXPECTED - RETRY INPUT LINE"); + } + } + } + + private static int readCapeMove(String initialPrompt, Scanner scanner) { + String prompt = initialPrompt; + while (true) { + int capeMove = readInt(prompt, scanner); + if (capeMove <= 0 || capeMove > 3) { + System.out.println("DON'T PANIC, YOU IDIOT! PUT DOWN A CORRECT NUMBER"); + prompt = ""; + } else { + return capeMove; + } + } + } + + private static BooleanAnswer readYesOrNo(Scanner scanner) { + while (true) { + String answer = scanner.nextLine(); + if (answer.equalsIgnoreCase("YES")) { + return BooleanAnswer.YES; + } else if (answer.equalsIgnoreCase("NO")) { + return BooleanAnswer.NO; + } else { + System.out.println("INCORRECT ANSWER - - PLEASE TYPE 'YES' OR 'NO'."); + } + } + } + + private static void printInstructions() { + System.out.print("HELLO, ALL YOU BLOODLOVERS AND AFICIONADOS.\n"); + System.out.print("HERE IS YOUR BIG CHANCE TO KILL A BULL.\n\n"); + System.out.print("ON EACH PASS OF THE BULL, YOU MAY TRY\n"); + System.out.print("0 - VERONICA (DANGEROUS INSIDE MOVE OF THE CAPE)\n"); + System.out.print("1 - LESS DANGEROUS OUTSIDE MOVE OF THE CAPE\n"); + System.out.print("2 - ORDINARY SWIRL OF THE CAPE.\n\n"); + System.out.print("INSTEAD OF THE ABOVE, YOU MAY TRY TO KILL THE BULL\n"); + System.out.print("ON ANY TURN: 4 (OVER THE HORNS), 5 (IN THE CHEST).\n"); + System.out.print("BUT IF I WERE YOU,\n"); + System.out.print("I WOULDN'T TRY IT BEFORE THE SEVENTH PASS.\n\n"); + System.out.print("THE CROWD WILL DETERMINE WHAT AWARD YOU DESERVE\n"); + System.out.print("(POSTHUMOUSLY IF NECESSARY).\n"); + System.out.print("THE BRAVER YOU ARE, THE BETTER THE AWARD YOU RECEIVE.\n\n"); + System.out.print("THE BETTER THE JOB THE PICADORES AND TOREADORES DO,\n"); + System.out.print("THE BETTER YOUR CHANCES ARE.\n\n"); + } + + private static void printHeader() { + System.out.println(" BULL"); + System.out.println(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n"); + } + + /** + * Random number 1 or 2 + * FNA in the original + */ + private static int randomNumber1or2(Random random) { + return (int) (random.nextFloat() * 2 + 1); + } + + private static double firstStage(String horseType, String suffix, BullGrade bullGrade, Random random) { + double b = ((3d / bullGrade.getValue()) * random.nextFloat()); + double c; + + if (b < 0.37) { + c = 0.5; + } else if (b < 0.5) { + c = 0.4; + } else if (b < 0.63) { + c = 0.3; + } else if (b < 0.87) { + c = 0.2; + } else { + c = 0.1; + } + + int t = (int) (10 * c + 0.2); + + System.out.println("THE " + horseType + suffix + " DID A " + getPerformanceRating(t) + " JOB."); + + if (t >= 4) { + if (t == 5) { + if (!horseType.equals("TOREAD")) { + System.out.println(" " + randomNumber1or2(random) + " OF THE HORSES OF THE " + horseType + suffix + " KILLED."); + } + System.out.println(" " + randomNumber1or2(random) + " OF THE " + horseType + suffix + " KILLED."); + + } else { + if (random.nextBoolean()) { + System.out.println("ONE OF THE " + horseType + " " + suffix + " WAS KILLED."); + } else { + System.out.println("NO " + horseType + " " + suffix + " WERE KILLED."); + } + } + } + + System.out.println(); + return c; + } + + public static String getPerformanceRating(int t) { + switch (t) { + case 1: + return "SUPERB"; + case 2: + return "GOOD"; + case 3: + return "FAIR"; + case 4: + return "POOR"; + default: + return "AWFUL"; + } + } + + private static GameState attemptKillBull(BullGrade bullGrade, Random random, GameState gameState, Scanner scanner) { + GameState newGameState = gameState.newCopy(); + System.out.println("\nIT IS THE MOMENT OF TRUTH.\n"); + + int h = readInt("HOW DO YOU TRY TO KILL THE BULL", scanner); + if (h == 4 || h == 5) { + var K = (6 - bullGrade.getValue()) * 10 * random.nextFloat() / ((gameState.picadoresDamage + gameState.toreadoresDamage) * 5 * gameState.passNumber); + if (h == 4) { + if (K > 0.8) { + System.out.println("THE BULL HAS GORED YOU!"); + newGameState = stateAfterGoring(random, scanner, newGameState, bullGrade); + } else { + System.out.println("YOU KILLED THE BULL!"); + newGameState.bullStatus = BullStatus.DEAD; + return newGameState;//game over + } + } else { + if (K > 0.2) { + System.out.println("THE BULL HAS GORED YOU!"); + newGameState = stateAfterGoring(random, scanner, newGameState, bullGrade); + } else { + System.out.println("YOU KILLED THE BULL!"); + newGameState.bullStatus = BullStatus.DEAD; + return newGameState;//game over + } + } + + } else { + System.out.println("YOU PANICKED. THE BULL GORED YOU."); + if (random.nextBoolean()) { + System.out.println("YOU ARE DEAD."); + newGameState.matadorStatus = MatadorStatus.DEAD; + return newGameState; + } else { + return readAndHandleForfeitDecision(random, scanner, newGameState, bullGrade); + } + + } + + return newGameState; + } + + private static GameState readAndHandleForfeitDecision(Random random, Scanner scanner, GameState gameState, BullGrade bullGrade) { + GameState newGameState = gameState.newCopy(); + + System.out.print("\nDO YOU RUN FROM THE RING? "); + BooleanAnswer yesOrNo = readYesOrNo(scanner); + if (yesOrNo == BooleanAnswer.NO) { + System.out.println("\n\nYOU ARE BRAVE. STUPID, BUT BRAVE."); + if (random.nextBoolean()) { + newGameState.matadorStatus = MatadorStatus.INJURED; + return newGameState; + } else { + System.out.println("YOU ARE GORED AGAIN!"); + return stateAfterGoring(random, scanner, newGameState, bullGrade); + } + } else { + System.out.println("COWARD"); + newGameState.matadorStatus = MatadorStatus.DEFEATED; + return newGameState; + + } + } + + private static boolean readInstructionsChoice(Scanner scan) { + System.out.print("DO YOU WANT INSTRUCTIONS? "); + + String choice = scan.nextLine(); + return !choice.equalsIgnoreCase("NO"); + } + + + private enum BooleanAnswer { + YES, NO + } + + private enum BullGrade { + SUPERB(1), + GOOD(2), + FAIR(3), + POOR(4), + AWFUL(5); + + private final int value; + + BullGrade(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static BullGrade fromValue(int value) { + for (BullGrade grade : BullGrade.values()) { + if (grade.getValue() == value) { + return grade; + } + } + throw new IllegalArgumentException("Invalid value: " + value); + } + + } + + /** + * Represents the game state. + */ + private static class GameState { + private MatadorStatus matadorStatus; //D[4] in the original + private BullStatus bullStatus; //D[5] in the original + private double picadoresDamage; //D[1] in the original + private double toreadoresDamage; //D[2] in the original + private int passNumber; //D[3] in the original + private double capeMovesCumulative; //L in the original + + public GameState() { + picadoresDamage = 0; + toreadoresDamage = 0; + passNumber = 0; + matadorStatus = MatadorStatus.ALIVE; + bullStatus = BullStatus.ALIVE; + capeMovesCumulative = 1; + } + + public GameState newCopy() { + GameState newState = new GameState(); + newState.matadorStatus = this.matadorStatus; + newState.bullStatus = this.bullStatus; + newState.picadoresDamage = this.picadoresDamage; + newState.toreadoresDamage = this.toreadoresDamage; + newState.passNumber = this.passNumber; + newState.capeMovesCumulative = this.capeMovesCumulative; + return newState; + } + } + + private enum MatadorStatus { + ALIVE(1), + INJURED(2), + DEAD(1.5), + DEFEATED(0); + + private final double value; + + MatadorStatus(double value) { + this.value = value; + } + + public double getValue() { + return value; + } + } + + private enum BullStatus { + ALIVE(1), + DEAD(2); + + private final double value; + + BullStatus(double value) { + this.value = value; + } + + public double getValue() { + return value; + } + } + +} + diff --git a/17_Bullfight/python/bullfight.py b/17_Bullfight/python/bullfight.py index 2bb789e2a..cfe3ed7ed 100644 --- a/17_Bullfight/python/bullfight.py +++ b/17_Bullfight/python/bullfight.py @@ -29,11 +29,8 @@ def determine_player_kills( print(f"THE {player_type}{plural_form} DID A {job_qualities[job_quality]} JOB.") if job_quality >= 4: if job_quality == 5: - player_was_killed = random.choice([True, False]) - if player_was_killed: + if random.choice([True, False]): print(f"ONE OF THE {player_type}{plural_form} WAS KILLED.") - elif player_was_killed: - print(f"NO {player_type}{plural_form} WERE KILLED.") else: if player_type != "TOREAD": killed_horses = random.randint(1, 2) @@ -177,12 +174,13 @@ def handle_bullkill_attempt( * job_quality_by_round[3] ) ) - if kill_method == 4: - if kill_probability > 0.8: - gore = 1 - else: - if kill_probability > 0.2: - gore = 1 + if ( + kill_method == 4 + and kill_probability > 0.8 + or kill_method != 4 + and kill_probability > 0.2 + ): + gore = 1 if gore == 0: print("YOU KILLED THE BULL!") job_quality_by_round[5] = 2 @@ -254,7 +252,7 @@ def main() -> None: # Round 3 job_quality_by_round[3] = 0 while True: - job_quality_by_round[3] = job_quality_by_round[3] + 1 + job_quality_by_round[3] += 1 print(f"PASS NUMBER {job_quality_by_round[3]}") if job_quality_by_round[3] >= 3: run_from_ring = ask_bool("HERE COMES THE BULL. TRY FOR A KILL? ") @@ -299,8 +297,13 @@ def main() -> None: print("YOU ARE STILL ALIVE.") print() print("DO YOU RUN FROM THE RING? ", end="") - run_from_ring = ask_bool("DO YOU RUN FROM THE RING? ") - if not run_from_ring: + if run_from_ring := ask_bool("DO YOU RUN FROM THE RING? "): + print("COWARD") + job_quality_by_round[4] = 0 + death = True + break + + else: print("YOU ARE BRAVE. STUPID, BUT BRAVE.") if random.randint(1, 2) == 1: job_quality_by_round[4] = 2 @@ -308,12 +311,6 @@ def main() -> None: break else: print("YOU ARE GORED AGAIN!") - else: - print("COWARD") - job_quality_by_round[4] = 0 - death = True - break - if death: break diff --git a/18_Bullseye/python/bullseye.py b/18_Bullseye/python/bullseye.py index 490a08984..df931f35c 100644 --- a/18_Bullseye/python/bullseye.py +++ b/18_Bullseye/python/bullseye.py @@ -54,7 +54,7 @@ def main() -> None: players.append(Player(player_name)) round_number = 0 - while len(winners) == 0: + while not winners: round_number += 1 print() print(f"ROUND {round_number}---------") @@ -62,10 +62,10 @@ def main() -> None: print() while True: throw = int(input(f"{player.name}'S THROW? ")) - if throw not in [1, 2, 3]: - print("INPUT 1, 2, OR 3!") - else: + if throw in {1, 2, 3}: break + else: + print("INPUT 1, 2, OR 3!") if throw == 1: probability_1 = 0.65 probability_2 = 0.55 @@ -99,10 +99,11 @@ def main() -> None: points = 0 player.score += points print(f"TOTAL SCORE = {player.score}") - for player_index, player in enumerate(players): - if player.score > 200: - winners.append(player_index) - + winners.extend( + player_index + for player_index, player in enumerate(players) + if player.score > 200 + ) print_outro(players, winners) diff --git a/18_Bullseye/rust/Cargo.lock b/18_Bullseye/rust/Cargo.lock new file mode 100644 index 000000000..4fe2abbea --- /dev/null +++ b/18_Bullseye/rust/Cargo.lock @@ -0,0 +1,75 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/19_Bunny/rust/Cargo.toml b/19_Bunny/rust/Cargo.toml new file mode 100644 index 000000000..7d75412a6 --- /dev/null +++ b/19_Bunny/rust/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "rust" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/19_Bunny/rust/src/main.rs b/19_Bunny/rust/src/main.rs new file mode 100644 index 000000000..4eb9b7316 --- /dev/null +++ b/19_Bunny/rust/src/main.rs @@ -0,0 +1,114 @@ +/** BUNNY GAME + * https://github.com/coding-horror/basic-computer-games/tree/main/19_Bunny + * Direct conversion from BASIC to Rust by Pablo Marques (marquesrs). + * No additional features or improvements were added. As a faithful translation, + * many of the code here are done in an unrecommended way by today's standards. + * 17/02/25 +*/ + +//290 DATA 2,21,14,14,25 +//300 DATA 1,2,-1,0,2,45,50,-1,0,5,43,52,-1,0,7,41,52,-1 +//310 DATA 1,9,37,50,-1,2,11,36,50,-1,3,13,34,49,-1,4,14,32,48,-1 +//320 DATA 5,15,31,47,-1,6,16,30,45,-1,7,17,29,44,-1,8,19,28,43,-1 +//330 DATA 9,20,27,41,-1,10,21,26,40,-1,11,22,25,38,-1,12,22,24,36,-1 +//340 DATA 13,34,-1,14,33,-1,15,31,-1,17,29,-1,18,27,-1 +//350 DATA 19,26,-1,16,28,-1,13,30,-1,11,31,-1,10,32,-1 +//360 DATA 8,33,-1,7,34,-1,6,13,16,34,-1,5,12,16,35,-1 +//370 DATA 4,12,16,35,-1,3,12,15,35,-1,2,35,-1,1,35,-1 +//380 DATA 2,34,-1,3,34,-1,4,33,-1,6,33,-1,10,32,34,34,-1 +//390 DATA 14,17,19,25,28,31,35,35,-1,15,19,23,30,36,36,-1 +//400 DATA 14,18,21,21,24,30,37,37,-1,13,18,23,29,33,38,-1 +//410 DATA 12,29,31,33,-1,11,13,17,17,19,19,22,22,24,31,-1 +//420 DATA 10,11,17,18,22,22,24,24,29,29,-1 +//430 DATA 22,23,26,29,-1,27,29,-1,28,29,-1,4096 + +// 4096 is the end of file +// -1 is the end of line +const DATA: [i32;233] = [ + 2,21,14,14,25, + 1,2,-1,0,2,45,50,-1,0,5,43,52,-1,0,7,41,52,-1, + 1,9,37,50,-1,2,11,36,50,-1,3,13,34,49,-1,4,14,32,48,-1, + 5,15,31,47,-1,6,16,30,45,-1,7,17,29,44,-1,8,19,28,43,-1, + 9,20,27,41,-1,10,21,26,40,-1,11,22,25,38,-1,12,22,24,36,-1, + 13,34,-1,14,33,-1,15,31,-1,17,29,-1,18,27,-1, + 19,26,-1,16,28,-1,13,30,-1,11,31,-1,10,32,-1, + 8,33,-1,7,34,-1,6,13,16,34,-1,5,12,16,35,-1, + 4,12,16,35,-1,3,12,15,35,-1,2,35,-1,1,35,-1, + 2,34,-1,3,34,-1,4,33,-1,6,33,-1,10,32,34,34,-1, + 14,17,19,25,28,31,35,35,-1,15,19,23,30,36,36,-1, + 14,18,21,21,24,30,37,37,-1,13,18,23,29,33,38,-1, + 12,29,31,33,-1,11,13,17,17,19,19,22,22,24,31,-1, + 10,11,17,18,22,22,24,24,29,29,-1, + 22,23,26,29,-1,27,29,-1,28,29,-1,4096, +]; + +fn main() { + let mut col = 0; + + //10 PRINT TAB(33);"BUNNY" + //20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" + //30 PRINT: PRINT: PRINT + //100 REM "BUNNY" FROM DAVID AHL'S 'BASIC COMPUTER GAMES' + print!("{}{}\n{}{}\n", + " ".repeat(33), + "BUNNY", + " ".repeat(15), + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" + ); + + //120 FOR I=0 TO 4: READ B(I): NEXT I + let mut b = [0;5]; + for i in 0..=4 { + b[i] = DATA[col]; + col = col + 1; + } + + //130 GOSUB 260 + //260 FOR I=1 TO 6: PRINT CHR$(10);: NEXT I + for _ in 1..=6 { + println!(); + } + //270 RETURN + + //140 L=64: REM ASCII LETTER CODE... + let l = 64; + let mut prev = 0; + loop { + //170 READ X: IF X<0 THEN 160 + let x = DATA[col]; + col = col + 1; + if x < 0 { + //160 PRINT + println!(); + prev = 0; + continue; + } + //175 IF X>128 THEN 240 + else if x > 128 { + //240 GOSUB 260: GOTO 450 + for _ in 1..=6 { + println!(); + } + break; + } + else { + //180 PRINT TAB(X);: READ Y + print!("{}", " ".repeat((x - prev) as usize)); // verificar + prev = x; + let y = DATA[col]; + col = col + 1; + //190 FOR I=X TO Y: J=I-5*INT(I/5) + for i in x..=y { + let j = i - 5 * (i / 5); + //200 PRINT CHR$(L+B(J)); + print!("{}", (l + b[j as usize]) as u8 as char); + prev = prev + 1; + //210 NEXT I + + } + //220 GOTO 170 + } + } + //450 END +} + diff --git a/20_Buzzword/rust/Cargo.lock b/20_Buzzword/rust/Cargo.lock new file mode 100644 index 000000000..4fe2abbea --- /dev/null +++ b/20_Buzzword/rust/Cargo.lock @@ -0,0 +1,75 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/20_Buzzword/rust/Cargo.toml b/20_Buzzword/rust/Cargo.toml new file mode 100644 index 000000000..3b1d02f52 --- /dev/null +++ b/20_Buzzword/rust/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "rust" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rand = "0.8.5" diff --git a/20_Buzzword/rust/README.md b/20_Buzzword/rust/README.md new file mode 100644 index 000000000..fc6468b9f --- /dev/null +++ b/20_Buzzword/rust/README.md @@ -0,0 +1,3 @@ +Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) + +Conversion to [Rust](https://www.rust-lang.org/) diff --git a/20_Buzzword/rust/src/main.rs b/20_Buzzword/rust/src/main.rs new file mode 100644 index 000000000..dae4b8198 --- /dev/null +++ b/20_Buzzword/rust/src/main.rs @@ -0,0 +1,112 @@ +use rand::seq::SliceRandom; +use std::io::{self, Write}; + +fn main() { + let words = vec![ + vec![ + "Ability", + "Basal", + "Behavioral", + "Child-centered", + "Differentiated", + "Discovery", + "Flexible", + "Heterogeneous", + "Homogenous", + "Manipulative", + "Modular", + "Tavistock", + "Individualized", + ], + vec![ + "learning", + "evaluative", + "objective", + "cognitive", + "enrichment", + "scheduling", + "humanistic", + "integrated", + "non-graded", + "training", + "vertical age", + "motivational", + "creative", + ], + vec![ + "grouping", + "modification", + "accountability", + "process", + "core curriculum", + "algorithm", + "performance", + "reinforcement", + "open classroom", + "resource", + "structure", + "facility", + "environment", + ], + ]; + + // intro text + println!("\n Buzzword Generator"); + println!("Creative Computing Morristown, New Jersey"); + println!("\n\n"); + println!("This program prints highly acceptable phrases in"); + println!("'educator-speak' that you can work into reports"); + println!("and speeches. Whenever a question mark is printed,"); + println!("type a 'Y' for another phrase or 'N' to quit."); + println!("\n\nHere's the first phrase:"); + + let mut continue_running: bool = true; + + while continue_running { + let mut first_word: bool = true; + for section in &words { + if !first_word { + print!(" "); + } + first_word = false; + print!("{}", section.choose(&mut rand::thread_rng()).unwrap()); + } + print!("\n\n? "); + io::stdout().flush().unwrap(); + + let mut cont_question: String = String::new(); + io::stdin() + .read_line(&mut cont_question) + .expect("Failed to read the line"); + if !cont_question.to_uppercase().starts_with("Y") { + continue_running = false; + } + } + println!("Come back when you need help with another report!\n"); + +} + + +///////////////////////////////////////////////////////////////////////// +// +// Porting Notes +// +// The original program stored all 39 words in one array, then +// built the buzzword phrases by randomly sampling from each of the +// three regions of the array (1-13, 14-26, and 27-39). +// +// Here, we're storing the words for each section in separate +// tuples. That makes it easy to just loop through the sections +// to stitch the phrase together, and it easily accommodates adding +// (or removing) elements from any section. They don't all need to +// be the same length. +// +// The author of this program (and founder of Creative Computing +// magazine) first started working at DEC--Digital Equipment +// Corporation--as a consultant helping the company market its +// computers as educational products. He later was editor of a DEC +// newsletter named "EDU" that focused on using computers in an +// educational setting. No surprise, then, that the buzzwords in +// this program were targeted towards educators! +// +///////////////////////////////////////////////////////////////////////// diff --git a/21_Calendar/README.md b/21_Calendar/README.md index 05c3ab965..5f5b04eac 100644 --- a/21_Calendar/README.md +++ b/21_Calendar/README.md @@ -24,4 +24,6 @@ http://www.vintage-basic.net/games.html #### Porting Notes -(please note any difficulties or challenges in porting here) +- While many modern environments have time/date functions that would make this program both easier and more automatic, in these ports we are choosing to do without them, as in the original program. + +- Some ports choose to ask the user the starting day of week, and whether it's a leap year, rather than force changes to the code to fit the desired year. diff --git a/21_Calendar/python/calendar.py b/21_Calendar/python/calendar.py index 922e348e6..5c59e37e3 100644 --- a/21_Calendar/python/calendar.py +++ b/21_Calendar/python/calendar.py @@ -44,7 +44,7 @@ def parse_input() -> Tuple[int, bool]: while not correct_day_input: weekday = input("INSERT THE STARTING DAY OF THE WEEK OF THE YEAR:") - for day_k in days_mapping.keys(): + for day_k in days_mapping: if weekday.lower() in day_k: day = days_mapping[day_k] correct_day_input = True diff --git a/21_Calendar/rust/Cargo.lock b/21_Calendar/rust/Cargo.lock new file mode 100644 index 000000000..b21cc6a2d --- /dev/null +++ b/21_Calendar/rust/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "rust" +version = "0.1.0" diff --git a/22_Change/rust/Cargo.lock b/22_Change/rust/Cargo.lock new file mode 100644 index 000000000..b21cc6a2d --- /dev/null +++ b/22_Change/rust/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "rust" +version = "0.1.0" diff --git a/23_Checkers/README.md b/23_Checkers/README.md index 1d9143ca2..f2263df51 100644 --- a/23_Checkers/README.md +++ b/23_Checkers/README.md @@ -24,5 +24,6 @@ should be more readable. - If the computer moves a checker to the bottom row, it promotes, but leaves the original checker in place. (See line 1240) - Human players may move non-kings as if they were kings. (See lines 1590 to 1810) - - Human players are not required to jump if it is possible. + - Human players are not required to jump if it is possible, and may make a number + other illegal moves (jumping their own pieces, jumping empty squares, etc.). - Curious writing to "I" variable without ever reading it. (See lines 1700 and 1806) diff --git a/23_Checkers/perl/README.md b/23_Checkers/perl/README.md index e69c8b819..4323cf4ad 100644 --- a/23_Checkers/perl/README.md +++ b/23_Checkers/perl/README.md @@ -1,3 +1,9 @@ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Perl](https://www.perl.org/) + +Note: This version has lines and columns numbers to help you with choosing the cell +to move from and to, so you don't have to continually count. It also puts a "." only for +blank cells you can move to, which I think makes for a more pleasing look and makes +it easier to play. If you want the original behavior, start the program with an arg +of "-o" for the original behavior. diff --git a/23_Checkers/perl/checkers.pl b/23_Checkers/perl/checkers.pl new file mode 100755 index 000000000..46b8f2d4c --- /dev/null +++ b/23_Checkers/perl/checkers.pl @@ -0,0 +1,351 @@ +#!/usr/bin/perl + +# Checkers program in Perl +# Started with checkers.annotated.bas +# Translated by Kevin Brannen (kbrannen) + +use strict; +use warnings; + +# globals +# +# The current move: (rating, current x, current y, new x, new y) +# 'rating' represents how good the move is; higher is better. +my @ratings = (-99); # (4); # Start with minimum score +# The board. Pieces are represented by numeric values: +# +# - 0 = empty square +# - -1,-2 = X (-1 for regular piece, -2 for king) +# - 1,2 = O (1 for regular piece, 2 for king) +# +# This program's player ("me") plays X. +my @board; # (7,7) +# chars to print for the board, add 2 to the board value as an index to the char +my @chars = ("X*", "X", ".", "O", "O*"); +my $neg1 = -1; # constant holding -1 +my $winner = ""; +my $upgrade = shift(@ARGV) // ""; +$upgrade = $upgrade eq "-o" ? 0 : 1; + +##### + +print "\n"; +print " " x 32, "CHECKERS\n"; +print " " x 15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n"; + + +print "THIS IS THE GAME OF CHECKERS. THE COMPUTER IS X,\n"; +print "AND YOU ARE O. THE COMPUTER WILL MOVE FIRST.\n"; +print "SQUARES ARE REFERRED TO BY A COORDINATE SYSTEM.\n"; +print "(0,0) IS THE LOWER LEFT CORNER\n"; +print "(0,7) IS THE UPPER LEFT CORNER\n"; +print "(7,0) IS THE LOWER RIGHT CORNER\n"; +print "(7,7) IS THE UPPER RIGHT CORNER\n"; +print "THE COMPUTER WILL TYPE '+TO' WHEN YOU HAVE ANOTHER\n"; +print "JUMP. TYPE TWO NEGATIVE NUMBERS IF YOU CANNOT JUMP.\n"; +print "ENTER YOUR MOVE POSITION LIKE '0 0' OR '0,0'.\n\n\n"; + +# Initialize the board. Data is 2 length-wise strips repeated. +my @data = (); +for (1 .. 32) { push(@data, (1,0,1,0,0,0,-1,0, 0,1,0,0,0,-1,0,-1)); } +for my $x (0 .. 7) +{ + for my $y (0 .. 7) + { + $board[$x][$y] = shift(@data); + } +} + +# Start of game loop. First, my turn. +while (1) +{ + + # For each square on the board, search for one of my pieces + # and if it can make the best move so far, store that move in 'r' + for my $x (0 .. 7) + { + for my $y (0 .. 7) + { + # Skip if this is empty or an opponent's piece + next if ($board[$x][$y] > -1); + + # If this is one of my ordinary pieces, analyze possible + # forward moves. + if ($board[$x][$y] == -1) + { + for (my $a = -1 ; $a <= 1 ; $a +=2) + { + $b = $neg1; + find_move($x, $y, $a, $b); + } + } + + # If this is one of my kings, analyze possible forward + # and backward moves. + if ($board[$x][$y] == -2) + { + for (my $a = -1 ; $a <= 1 ; $a += 2) + { + for (my $b = -1 ; $a <= 1 ; $b += 2) { find_move($x, $y, $a, $b); } + } + } + } + } + + + if ($ratings[0] == -99) # Game is lost if no move could be found. + { + $winner = "you"; + last; + } + + # Print the computer's move. (Note: chr$(30) is an ASCII RS + # (record separator) code; probably no longer relevant.) + print "FROM $ratings[1],$ratings[2] TO $ratings[3],$ratings[4] "; + $ratings[0] = -99; + + # Make the computer's move. If the piece finds its way to the + # end of the board, crown it. + LOOP1240: { + if ($ratings[4] == 0) + { + $board[$ratings[3]][$ratings[4]] = -2; + last LOOP1240; + } + $board[$ratings[3]][$ratings[4]] = $board[$ratings[1]][$ratings[2]]; + $board[$ratings[1]][$ratings[2]] = 0; + + # If the piece has jumped 2 squares, it means the computer has + # taken an opponents' piece. + if (abs($ratings[1] - $ratings[3]) == 2) + { + $board [($ratings[1]+$ratings[3])/2] [($ratings[2]+$ratings[4])/2] = 0; # Delete the opponent's piece + + # See if we can jump again. Evaluate all possible moves. + my $x = $ratings[3]; + my $y = $ratings[4]; + for (my $a = -2 ; $a <= 2 ; $a += 4) + { + if ($board[$x][$y] == -1) + { + $b = -2; + eval_move($x, $y, $a, $b); + } + if ($board[$x][$y] == -2) + { + for (my $b = -2 ; $b <= 2 ; $b += 4) { eval_move($x, $y, $a, $b); } + } + } + + # If we've found a move, go back and make that one as well + if ($ratings[0] != -99) + { + print "TO $ratings[3], $ratings[4] "; + $ratings[0] = -99; + next LOOP1240; + } + } + } # LOOP1240 + + # Now, print the board + print "\n\n\n"; + for (my $y = 7 ; $y >= 0 ; $y--) + { + my $line = ""; + $line = "$y|" if ($upgrade); + for my $x (0 .. 7) + { + my $c = $chars[$board[$x][$y] + 2]; + $c = ' ' if ($upgrade && (($y % 2 == 0 && $x % 2 == 1) || ($y % 2 == 1 && $x % 2 == 0))); + $line = tab($line, 5*$x+7, $c); + } + print $line; + print " \n\n"; + } + print " _ _ _ _ _ _ _ _\n" if ($upgrade); + print " 0 1 2 3 4 5 6 7\n" if ($upgrade); + print "\n"; + + # Check if either player is out of pieces. If so, announce the + # winner. + my ($z, $t) = (0, 0); + for my $x (0 .. 7) + { + for my $y (0 .. 7) + { + if ($board[$x][$y] == 1 || $board[$x][$y] == 2) { $z = 1; } + if ($board[$x][$y] == -1 || $board[$x][$y] == -2) { $t = 1; } + } + } + if ($z != 1) { $winner = "comp"; last; } + if ($t != 1) { $winner = "you"; last; } + + # Prompt the player for their move. + ($z, $t) = (0, 0); + my ($x, $y, $e, $h, $a, $b); + do { + ($e,$h) = get_pos("FROM:"); + $x = $e; + $y = $h; + } while ($board[$x][$y] <= 0); + do { + ($a,$b) = get_pos("TO:"); + $x = $a; + $y = $b; + } while (!($board[$x][$y] == 0 && abs($a-$e) <= 2 && abs($a-$e) == abs($b-$h))); + + LOOP1750: { + # Make the move and stop unless it might be a jump. + $board[$a][$b] = $board[$e][$h]; + $board[$e][$h] = 0; + if (abs($e-$a) != 2) { last LOOP1750; } + + # Remove the piece jumped over + $board[($e+$a)/2][($h+$b)/2] = 0; + + # Prompt for another move; -1 means player can't, so I've won. + # Keep prompting until there's a valid move or the player gives + # up. + my ($a1, $b1); + do { + ($a1,$b1) = get_pos("+TO:"); + if ($a1 < 0) { last LOOP1750; } + } while ($board[$a1][$b1] != 0 || abs($a1-$a) != 2 || abs($b1-$b) != 2); + + # Update the move variables to correspond to the next jump + $e = $a; + $h = $b; + $a = $a1; + $b = $b1; + } + + # If the player has reached the end of the board, crown this piece + if ($b == 7) { $board[$a][$b] = 2; } + + # And play the next turn. +} + +# Endgame: +print "\n", ($winner eq "you" ? "YOU" : "I"), " WIN\n"; +exit(0); + +########################################### + +# make sure we get a 2 value position +sub get_pos +{ + my $prompt = shift; + my ($p1, $p2); + do { + print "$prompt "; + chomp(my $ans = <>); + ($p1,$p2) = split(/[, ]/, $ans); + } while (!defined($p1) || !defined($p2) || $p1 < -1 || $p2 < -1 || $p1 > 7 || $p2 > 7); + return ($p1,$p2); +} + +# deal with basic's tab() for line positioning +# line = line string we're starting with +# pos = position to start writing +# s = string to write +# returns the resultant string, which might not have been changed +sub tab +{ + my ($line, $pos, $str) = @_; + my $len = length($line); + # if curser is past position, do nothing + if ($len <= $pos) { $line .= " " x ($pos - $len) . $str; } + return $line; +} + +# Analyze a move from (x,y) to (x+a, y+b) and schedule it if it's +# the best candidate so far. +sub find_move +{ + my ($x, $y, $a, $b) = @_; + my $u = $x+$a; + my $v = $y+$b; + + # Done if it's off the board + return if ($u < 0 || $u > 7 || $v < 0 || $ v> 7); + + # Consider the destination if it's empty + eval_jump($x, $y, $u, $v) if ($board[$u][$v] == 0); + + # If it's got an opponent's piece, jump it instead + if ($board[$u][$v] > 0) + { + + # Restore u and v, then return if it's off the board + $u += $a; + $v += $b; + return if ($u < 0 || $v < 0 || $u > 7 || $v > 7); + + # Otherwise, consider u,v + eval_jump($x, $y, $u, $v) if ($board[$u][$v] == 0); + } +} + +# Evaluate jumping (x,y) to (u,v). +# +# Computes a score for the proposed move and if it's higher +# than the best-so-far move, uses that instead by storing it +# and its score in @ratings. +sub eval_jump +{ + my ($x, $y, $u, $v) = @_; + + # q is the score; it starts at 0 + my $q = 0; + + # +2 if it promotes this piece + $q += 2 if ($v == 0 && $board[$x][$y] == -1); + + # +5 if it takes an opponent's piece + $q += 5 if (abs($y-$v) == 2); + + # -2 if the piece is moving away from the top boundary + $q -= 2 if ($y == 7); + + # +1 for putting the piece against a vertical boundary + $q++ if ($u == 0 || $u == 7); + + for (my $c = -1 ; $c <= 1 ; $c += 2) + { + next if ($u+$c < 0 || $u+$c > 7 || $v+$neg1 < 0); + + # +1 for each adjacent friendly piece + if ($board[$u+$c][$v+$neg1] < 0) + { + $q++; + next; + } + + # Prevent out-of-bounds testing + next if ($u-$c < 0 || $u-$c > 7 || $v-$neg1 > 7); + + # -2 for each opponent piece that can now take this piece here + $q -= 2 if ($board[$u+$c][$v+$neg1] > 0 && ($board[$u-$c][$v-$neg1] == 0 || ($u-$c == $x && $v-$neg1 == $y))); + } + + # Use this move if it's better than the previous best + if ($q > $ratings[0]) + { + $ratings[0] = $q; + $ratings[1] = $x; + $ratings[2] = $y; + $ratings[3] = $u; + $ratings[4] = $v; + } +} + +# If (u,v) is in the bounds, evaluate it as a move using +# the sub at 910, so storing eval in @ratings. +sub eval_move +{ + my ($x, $y, $a, $b) = @_; + my $u = $x+$a; + my $v = $y+$b; + return if ($u < 0 || $u > 7 || $v < 0 || $v > 7); + eval_jump($x, $y, $u, $v) if ($board[$u][$v] == 0 && $board[$x+$a/2][$y+$b/2] > 0); +} diff --git a/23_Checkers/python/checkers.py b/23_Checkers/python/checkers.py index 7ae2b7e55..42ae48c8b 100644 --- a/23_Checkers/python/checkers.py +++ b/23_Checkers/python/checkers.py @@ -64,7 +64,7 @@ def is_legal_board_coordinate(x: int, y: int) -> bool: class Board: def __init__(self) -> None: - self.spaces = [[0 for y in range(8)] for x in range(8)] + self.spaces = [[0 for _ in range(8)] for _ in range(8)] for x in range(8): if (x % 2) == 0: self.spaces[x][6] = COMPUTER_PIECE @@ -114,11 +114,10 @@ def get_spaces_with_human_pieces(self) -> Iterator[Tuple[int, int]]: def get_legal_deltas_for_space(self, x: int, y: int) -> Iterator[Tuple[int, int]]: contents = self.spaces[x][y] - if contents == COMPUTER_PIECE: - for delta_x in (-1, 1): + for delta_x in (-1, 1): + if contents == COMPUTER_PIECE: yield (delta_x, -1) - else: - for delta_x in (-1, 1): + else: for delta_y in (-1, 1): yield (delta_x, delta_y) @@ -183,7 +182,7 @@ def evaluate_move( if start_y == 7: # prefer to defend back row quality -= 2 - if dest_x in (0, 7): + if dest_x in {0, 7}: # moving to edge column quality += 1 for delta_x in (-1, 1): @@ -272,9 +271,8 @@ def play_computer_move(self, move_record: MoveRecord) -> None: if best_move is None: return - else: - print(f"TO {best_move.dest_x} {best_move.dest_y}") - move_record = best_move + print(f"TO {best_move.dest_x} {best_move.dest_y}") + move_record = best_move def try_extend( self, start_x: int, start_y: int, delta_x: int, delta_y: int @@ -352,10 +350,10 @@ def play_human_move( self.spaces[dest_x][dest_y] = HUMAN_KING def check_pieces(self) -> bool: - if len(list(self.get_spaces_with_computer_pieces())) == 0: + if not list(self.get_spaces_with_computer_pieces()): print_human_won() return False - if len(list(self.get_spaces_with_computer_pieces())) == 0: + if not list(self.get_spaces_with_computer_pieces()): print_computer_won() return False return True diff --git a/24_Chemist/README.md b/24_Chemist/README.md index fc62425a1..8871a0acb 100644 --- a/24_Chemist/README.md +++ b/24_Chemist/README.md @@ -13,11 +13,13 @@ As published in Basic Computer Games (1978): Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html +#### Known Bugs + +- There is a typo in the original Basic, "...DECIDE **WHO** MUCH WATER..." should be "DECIDE **HOW** MUCH WATER" + #### Porting Notes (please note any difficulties or challenges in porting here) -There is a type in the original Basic, "...DECIDE **WHO** MUCH WATER..." should be "DECIDE **HOW** MUCH WATER" - #### External Links - C: https://github.com/ericfischer/basic-computer-games/blob/main/24%20Chemist/c/chemist.c diff --git a/24_Chemist/rust/Cargo.lock b/24_Chemist/rust/Cargo.lock new file mode 100644 index 000000000..4fe2abbea --- /dev/null +++ b/24_Chemist/rust/Cargo.lock @@ -0,0 +1,75 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/25_Chief/python/chief.py b/25_Chief/python/chief.py index d8af319b5..050e281f3 100644 --- a/25_Chief/python/chief.py +++ b/25_Chief/python/chief.py @@ -39,7 +39,6 @@ def game() -> None: print("\nHuh, I Knew I was unbeatable") print("And here is how i did it") print_solution(comp_guess) - input() else: original_number = float(input("\nHUH!! what was you original number? ")) @@ -50,7 +49,6 @@ def game() -> None: ) print("Here is how i did it") print_solution(comp_guess) - input() else: print("\nSo you think you're so smart, EH?") print("Now, Watch") @@ -60,14 +58,14 @@ def game() -> None: if believe_me.lower() == "yes": print("\nOk, Lets play again sometime bye!!!!") - input() else: print("\nYOU HAVE MADE ME VERY MAD!!!!!") print("BY THE WRATH OF THE MATHEMATICS AND THE RAGE OF THE GODS") print("THERE SHALL BE LIGHTNING!!!!!!!") print_lightning_bolt() print("\nI Hope you believe me now, for your own sake") - input() + + input() if __name__ == "__main__": diff --git a/25_Chief/rust/Cargo.lock b/25_Chief/rust/Cargo.lock new file mode 100644 index 000000000..b21cc6a2d --- /dev/null +++ b/25_Chief/rust/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "rust" +version = "0.1.0" diff --git a/25_Chief/rust/Cargo.toml b/25_Chief/rust/Cargo.toml new file mode 100644 index 000000000..1ec696335 --- /dev/null +++ b/25_Chief/rust/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "rust" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/25_Chief/rust/README.md b/25_Chief/rust/README.md new file mode 100644 index 000000000..8bdbf2e2b --- /dev/null +++ b/25_Chief/rust/README.md @@ -0,0 +1,3 @@ +Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) + +Conversion to [Rust](https://www.rust-lang.org/) by [Jadi](https://github.com/jadijadi) \ No newline at end of file diff --git a/25_Chief/rust/src/main.rs b/25_Chief/rust/src/main.rs new file mode 100644 index 000000000..123f1a670 --- /dev/null +++ b/25_Chief/rust/src/main.rs @@ -0,0 +1,133 @@ +use std::io; + +fn print_center(text: String, width: usize) { + let pad_size: usize = if width > text.len() { + (width - text.len()) / 2 + } else { + 0 + }; + println!("{}{}", " ".repeat(pad_size), text); +} + +fn send_lightening() { + println!( + "YOU HAVE MADE ME MAD!!! +THERE MUST BE A GREAT LIGHTNING BOLT! + + X X + X X + X X + X X + X X + X X + X X + X X + X X + X XXX + X X + XX X + X X + X X + X X + X X + X X + X X + X X + X X + XX + X + * + +######################### + +I HOPE YOU BELIEVE ME NOW, FOR YOUR SAKE!!" + ); +} + +fn check_yes_answer() -> bool { + // reads from input and return true if it starts with Y or y + + let mut answer: String = String::new(); + io::stdin() + .read_line(&mut answer) + .expect("Error reading from stdin"); + + answer.to_uppercase().starts_with('Y') +} + +fn main() { + const PAGE_WIDTH: usize = 64; + print_center("CHIEF".to_string(), PAGE_WIDTH); + print_center( + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY".to_string(), + PAGE_WIDTH, + ); + println!("\n\n\n"); + + println!("I AM CHIEF NUMBERS FREEK, THE GREAT INDIAN MATH GOD."); + println!("ARE YOU READY TO TAKE THE TEST YOU CALLED ME OUT FOR?"); + + if !check_yes_answer() { + println!("SHUT UP, PALE FACE WITH WISE TONGUE."); + } + + println!("TAKE A NUMBER AND ADD 3. DIVIDE THIS NUMBER BY 5 AND"); + println!("MULTIPLY BY 8. DIVIDE BY 5 AND ADD THE SAME. SUBTRACT 1."); + println!(" WHAT DO YOU HAVE?"); + + // read a float number + let mut answer: String = String::new(); + io::stdin() + .read_line(&mut answer) + .expect("Error reading from stdin"); + let guess: f32 = answer.trim().parse().expect("Input not a number"); + + let calculated_answer: f32 = (guess + 1.0 - 5.0) * 5.0 / 8.0 * 5.0 - 3.0; + + println!("I BET YOUR NUMBER WAS {calculated_answer}. AM I RIGHT?"); + + if check_yes_answer() { + println!("BYE!!!"); + } else { + println!("WHAT WAS YOUR ORIGINAL NUMBER?"); + + // read a float number + let mut answer: String = String::new(); + io::stdin() + .read_line(&mut answer) + .expect("Error reading from stdin"); + let claimed: f32 = answer.trim().parse().expect("Input not a number"); + + println!("SO YOU THINK YOU'RE SO SMART, EH?"); + println!("NOW WATCH."); + println!( + "{claimed} PLUS 3 EQUALS {}. THIS DIVIDED BY 5 EQUALS {};", + claimed + 3.0, + (claimed + 3.0) / 5.0 + ); + println!( + "THIS TIMES 8 EQUALS {}. IF WE DIVIDE BY 5 AND ADD 5,", + (claimed + 3.0) / 5.0 * 8.0 + ); + println!( + "WE GET {} , WHICH, MINUS 1, EQUALS {}.", + ((claimed + 3.0) / 5.0 * 8.0 / 5.0) + 5.0, + ((claimed + 3.0) / 5.0 * 8.0 / 5.0) + 4.0 + ); + println!("NOW DO YOU BELIEVE ME?"); + + if check_yes_answer() { + println!("BYE!!!"); + } else { + send_lightening(); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Porting notes: +// In floating point arithmetics in "modern" languages we might see +// unfamiliar situations such as 6.9999999 instead of 7 and such. +// resolving this needs using specific mathematical libraries which +// IMO is out of scope in these basic programs +/////////////////////////////////////////////////////////////////// diff --git a/26_Chomp/python/chomp.py b/26_Chomp/python/chomp.py index 69810a5fd..d7b67950e 100755 --- a/26_Chomp/python/chomp.py +++ b/26_Chomp/python/chomp.py @@ -13,16 +13,16 @@ class Canvas: def __init__(self, width=9, height=9, fill="*") -> None: self._buffer = [] for _ in range(height): - line = [] - for _ in range(width): - line.append(fill) + line = [fill for _ in range(width)] self._buffer.append(line) self._buffer[0][0] = "P" def render(self) -> str: lines = [" 1 2 3 4 5 6 7 8 9"] - for row, line in enumerate(self._buffer, start=1): - lines.append(" " + str(row) + " " * 5 + " ".join(line)) + lines.extend( + f" {str(row)}" + " " * 5 + " ".join(line) + for row, line in enumerate(self._buffer, start=1) + ) return "\n".join(lines) def chomp(self, r, c) -> str: diff --git a/27_Civil_War/README.md b/27_Civil_War/README.md index 3a609ea86..7cbd62e2a 100644 --- a/27_Civil_War/README.md +++ b/27_Civil_War/README.md @@ -17,6 +17,10 @@ As published in Basic Computer Games (1978): Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html +#### Known Bugs + +- At the end of a single-player game, the program reports strategies used by "THE SOUTH", but these are in fact strategies used by the North (the computer player) -- the South is always a human player. + #### Porting Notes (please note any difficulties or challenges in porting here) diff --git a/27_Civil_War/python/Civilwar.py b/27_Civil_War/python/Civilwar.py index 14b88e017..309a35172 100644 --- a/27_Civil_War/python/Civilwar.py +++ b/27_Civil_War/python/Civilwar.py @@ -46,10 +46,7 @@ class PlayerStat: excessive_losses = False def set_available_money(self): - if self.is_player: - factor = 1 + (self.r - self.q) / (self.r + 1) - else: - factor = 1 + factor = 1 + (self.r - self.q) / (self.r + 1) if self.is_player else 1 self.available_money = 100 * math.floor( (self.army_m * (100 - self.inflation) / 2000) * factor + 0.5 ) @@ -70,8 +67,7 @@ def simulate_losses(player1: PlayerStat, player2: PlayerStat) -> float: 1 + 1 / (2 * (abs(player1.strategy - player2.strategy) + 1)) ) tmp = tmp * (1.28 + (5 * player1.army_m / 6) / (player1.ammunition + 1)) - tmp = math.floor(tmp * (1 + 1 / player1.morale) + 0.5) - return tmp + return math.floor(tmp * (1 + 1 / player1.morale) + 0.5) def update_army(player: PlayerStat, enemy: PlayerStat, use_factor=False) -> None: diff --git a/28_Combat/README.md b/28_Combat/README.md index 10918fc45..e4463ab23 100644 --- a/28_Combat/README.md +++ b/28_Combat/README.md @@ -15,6 +15,11 @@ As published in Basic Computer Games (1978): Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html +#### Known Bugs + +- The original game misspells "unguarded" on line 1751. +- In an initial army attack, the program claims that the computer loses 2/3 of its army, but it actually loses its entire army (lines 150-155). + #### Porting Notes (please note any difficulties or challenges in porting here) diff --git a/28_Combat/perl/combat.pl b/28_Combat/perl/combat.pl new file mode 100755 index 000000000..fc44a0e4c --- /dev/null +++ b/28_Combat/perl/combat.pl @@ -0,0 +1,202 @@ +#!/usr/bin/perl + +# Combat program in Perl +# Translated by Kevin Brannen (kbrannen) + +use strict; +use warnings; + +# globals +my $User_army; +my $User_navy; +my $User_AF; +my $Comp_army = 30000; +my $Comp_navy = 20000; +my $Comp_AF = 22000; +my $Attack_type; +my $Attack_num; + +print "\n"; +print " " x 33, "COMBAT\n"; +print " " x 15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n"; + +print "I AM AT WAR WITH YOU.\nWE HAVE 72000 SOLDIERS APIECE.\n\n"; + +do { + print "DISTRIBUTE YOUR FORCES.\n"; + print "\tME\t YOU\n"; + print "ARMY\t$Comp_army\t"; + chomp($User_army = <>); + print "NAVY\t$Comp_navy\t"; + chomp($User_navy = <>); + print "A. F.\t$Comp_AF\t"; + chomp($User_AF = <>); +} while ($User_army + $User_navy + $User_AF > 72000); + +do { + print "YOU ATTACK FIRST. TYPE (1) FOR ARMY; (2) FOR NAVY;\n"; + print "AND (3) FOR AIR FORCE.\n"; + chomp($Attack_num = <>); +} while ($Attack_type < 1 && $Attack_type > 3); +do { + print "HOW MANY MEN\n"; + chomp($Attack_type = <>); +} while ($Attack_type < 0 + || ($Attack_num == 1 && $Attack_type > $User_army) + || ($Attack_num == 2 && $Attack_type > $User_navy) + || ($Attack_num == 3 && $Attack_type > $User_AF)); + +if ($Attack_num == 1) +{ + if ($Attack_type<$User_army/3) + { + print "YOU LOST $Attack_type MEN FROM YOUR ARMY.\n"; + $User_army = int($User_army-$Attack_type); + } + if ($Attack_type<2*$User_army/3) + { + print "YOU LOST ", int($Attack_type/3), " MEN, BUT I LOST ", int(2*$Comp_army/3), "\n"; + $User_army = int($User_army-$Attack_type/3); + $Comp_army = int(2*$Comp_army/3); + } + else + { + s270(); + } +} +elsif ($Attack_num == 2) +{ + if ($Attack_type < $Comp_navy/3) + { + print "YOUR ATTACK WAS STOPPED!\n"; + $User_navy = int($User_navy-$Attack_type); + } + if ($Attack_type < 2*$Comp_navy/3) + { + print "YOU DESTROYED ", int(2*$Comp_navy/3), " OF MY ARMY.\n"; + $Comp_navy = int(2*$Comp_navy/3); + } + else + { + s270(); + } +} +else # $Attack_num == 3 +{ + if ($Attack_type < $User_AF/3) + { + print "YOUR ATTACK WAS WIPED OUT.\n"; + $User_AF = int($User_AF-$Attack_type); + } + if ($Attack_type < 2*$User_AF/3) + { + print "WE HAD A DOGFIGHT. YOU WON - AND FINISHED YOUR MISSION.\n"; + $Comp_army = int(2*$Comp_army/3); + $Comp_navy = int($Comp_navy/3); + $Comp_AF = int($Comp_AF/3); + } + else + { + print "YOU WIPED OUT ONE OF MY ARMY PATROLS, BUT I DESTROYED\n"; + print "TWO NAVY BASES AND BOMBED THREE ARMY BASES.\n"; + $User_army = int($User_army/4); + $User_navy = int($User_navy/3); + $User_AF = int(2*$User_AF/3); + } +} + +print "\n\tYOU\tME\n"; +print "ARMY\t$User_army\t$Comp_army\n"; +print "NAVY\t$User_navy\t$Comp_navy\n"; +print "A. F.\t$User_AF\t$Comp_AF\n"; +do { + print "WHAT IS YOUR NEXT MOVE?\n"; + print "ARMY=1 NAVY=2 AIR FORCE=3\n"; + chomp($Attack_type = <>); +} while ($Attack_type < 1 && $Attack_type > 3); +do { + print "HOW MANY MEN\n"; + chomp($Attack_num = <>); +} while ($Attack_num < 0 + || ($Attack_type == 1 && $Attack_num > $User_army) + || ($Attack_type == 2 && $Attack_num > $User_navy) + || ($Attack_type == 3 && $Attack_num > $User_AF)); + +if ($Attack_num == 1) +{ + if ($Attack_num < $Comp_army/2) + { + print "I WIPED OUT YOUR ATTACK!\n"; + $User_army -= $Attack_num; + } + else + { + print "YOU DESTROYED MY ARMY!\n"; + $Comp_army = 0; + } +} +elsif ($Attack_num == 2) +{ + if ($Attack_num < $Comp_navy/2) + { + print "I SUNK TWO OF YOUR BATTLESHIPS, AND MY AIR FORCE\n"; + print "WIPED OUT YOUR UNGAURDED CAPITOL.\n"; + $User_army /= 4; + $User_navy /= 2; + } + else + { + print "YOUR NAVY SHOT DOWN THREE OF MY XIII PLANES,\n"; + print "AND SUNK THREE BATTLESHIPS.\n"; + $Comp_AF = 2*$Comp_AF/3; + $Comp_navy /= 2; + } +} +else # $Attack_num == 3 +{ + if ($Attack_num > $Comp_AF/2) + { + print "MY NAVY AND AIR FORCE IN A COMBINED ATTACK LEFT\n"; + print "YOUR COUNTRY IN SHAMBLES.\n"; + $User_army /= 3; + $User_navy /= 3; + $User_AF /= 3; + } + else + { + print "ONE OF YOUR PLANES CRASHED INTO MY HOUSE. I AM DEAD.\n"; + print "MY COUNTRY FELL APART.\n"; + $Comp_army = $Comp_navy = $Comp_AF = 0; + } +} + +print "\nFROM THE RESULTS OF BOTH OF YOUR ATTACKS,\n"; +my $total_user = $User_army+$User_navy+$User_AF; +my $total_comp = $Comp_army+$Comp_navy+$Comp_AF; +if ($total_user > 3/2*($total_comp)) +{ + print "YOU WON, OH! SHUCKS!!!!\n"; +} +elsif ($total_user < 2/3*($total_comp)) +{ + print "YOU LOST-I CONQUERED YOUR COUNTRY. IT SERVES YOU\n"; + print "RIGHT FOR PLAYING THIS STUPID GAME!!!\n"; +} +else +{ + print "THE TREATY OF PARIS CONCLUDED THAT WE TAKE OUR\n"; + print "RESPECTIVE COUNTRIES AND LIVE IN PEACE.\n"; +} +print "\n"; +exit(0); + +####################################################### + +sub s270 +{ + print "YOU SUNK ONE OF MY PATROL BOATS, BUT I WIPED OUT TWO\n"; + print "OF YOUR AIR FORCE BASES AND 3 ARMY BASES.\n"; + $User_army = int($User_army/3); + $User_AF = int($User_AF/3); + $Comp_navy = int(2*$Comp_navy/3); +} diff --git a/28_Combat/python/combat.py b/28_Combat/python/combat.py index f8a7a5de3..0543b205a 100644 --- a/28_Combat/python/combat.py +++ b/28_Combat/python/combat.py @@ -15,7 +15,7 @@ def show_intro() -> None: print(" " * 14 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") print("\n\n") print("I AM AT WAR WITH YOU.") - print("WE HAVE " + str(MAX_UNITS) + " SOLDIERS APIECE.") + print(f"WE HAVE {str(MAX_UNITS)} SOLDIERS APIECE.") def get_forces() -> None: @@ -24,11 +24,11 @@ def get_forces() -> None: while True: print("DISTRIBUTE YOUR FORCES.") print(" ME YOU") - print("ARMY " + str(cpu_army) + " ? ", end="") + print(f"ARMY {str(cpu_army)} ? ", end="") usr_army = int(input()) - print("NAVY " + str(cpu_navy) + " ? ", end="") + print(f"NAVY {str(cpu_navy)} ? ", end="") usr_navy = int(input()) - print("A. F. " + str(cpu_air) + " ? ", end="") + print(f"A. F. {str(cpu_air)} ? ", end="") usr_air = int(input()) if (usr_army + usr_navy + usr_air) <= MAX_UNITS: break @@ -38,7 +38,6 @@ def attack_first() -> None: global usr_army, usr_navy, usr_air global cpu_army, cpu_navy, cpu_air - num_units = 0 unit_type = 0 while True: @@ -46,32 +45,28 @@ def attack_first() -> None: print("AND (3) FOR AIR FORCE.") print("?", end=" ") unit_type = int(input()) - if not (unit_type < 1 or unit_type > 3): + if unit_type >= 1 and unit_type <= 3: break + num_units = 0 while True: print("HOW MANY MEN") print("?", end=" ") num_units = int(input()) - if not ( - (num_units < 0) - or ((unit_type == 1) and (num_units > usr_army)) - or ((unit_type == 2) and (num_units > usr_navy)) - or ((unit_type == 3) and (num_units > usr_air)) + if ( + num_units >= 0 + and (unit_type != 1 or num_units <= usr_army) + and (unit_type != 2 or num_units <= usr_navy) + and (unit_type != 3 or num_units <= usr_air) ): break if unit_type == 1: if num_units < (usr_army / 3): - print("YOU LOST " + str(num_units) + " MEN FROM YOUR ARMY.") + print(f"YOU LOST {str(num_units)} MEN FROM YOUR ARMY.") usr_army = usr_army - num_units elif num_units < (2 * usr_army / 3): - print( - "YOU LOST " - + str(int(num_units / 3)) - + " MEN, BUT I LOST " - + str(int(2 * cpu_army / 3)) - ) + print(f"YOU LOST {int(num_units / 3)} MEN, BUT I LOST {int(2 * cpu_army / 3)}") usr_army = int(usr_army - (num_units / 3)) cpu_army = 0 else: @@ -85,7 +80,7 @@ def attack_first() -> None: print("YOUR ATTACK WAS STOPPED!") usr_navy = usr_navy - num_units elif num_units < 2 * cpu_navy / 3: - print("YOU DESTROYED " + str(int(2 * cpu_navy / 3)) + " OF MY ARMY.") + print(f"YOU DESTROYED {int(2 * cpu_navy / 3)} OF MY ARMY.") cpu_navy = int(cpu_navy / 3) else: print("YOU SUNK ONE OF MY PATROL BOATS, BUT I WIPED OUT TWO") @@ -113,7 +108,6 @@ def attack_first() -> None: def attack_second() -> None: global usr_army, usr_navy, usr_air, cpu_army, cpu_navy, cpu_air global plane_crash_win - num_units = 0 unit_type = 0 print() @@ -130,18 +124,19 @@ def attack_second() -> None: print("ARMY=1 NAVY=2 AIR FORCE=3") print("? ", end="") unit_type = int(input()) - if not ((unit_type < 1) or (unit_type > 3)): + if unit_type >= 1 and unit_type <= 3: break + num_units = 0 while True: print("HOW MANY MEN") print("? ", end="") num_units = int(input()) - if not ( - (num_units < 0) - or ((unit_type == 1) and (num_units > usr_army)) - or ((unit_type == 2) and (num_units > usr_navy)) - or ((unit_type == 3) and (num_units > usr_air)) + if ( + num_units >= 0 + and (unit_type != 1 or num_units <= usr_army) + and (unit_type != 2 or num_units <= usr_navy) + and (unit_type != 3 or num_units <= usr_air) ): break diff --git a/29_Craps/rust/Cargo.lock b/29_Craps/rust/Cargo.lock new file mode 100644 index 000000000..4fe2abbea --- /dev/null +++ b/29_Craps/rust/Cargo.lock @@ -0,0 +1,75 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/30_Cube/README.md b/30_Cube/README.md index c220c8d4d..a257f1105 100644 --- a/30_Cube/README.md +++ b/30_Cube/README.md @@ -17,6 +17,14 @@ http://www.vintage-basic.net/games.html (please note any difficulties or challenges in porting here) +##### Known Bugs + +This program does very little validation of its input, enabling the user to cheat in two ways: +- One can enter a large negative wager, purposely lose, and gain that much money. +- One can move outside the cube (using coordinates 0 or 4), then safely walk "around" the standard play volume to the destination square. + +It's remotely possible that these are clever solutions the user is intended to find, solving an otherwise purely random game. + ##### Randomization Logic The BASIC code uses an interesting technique for choosing the random coordinates for the mines. The first coordinate is diff --git a/30_Cube/python/cube.py b/30_Cube/python/cube.py index 4928b08e9..260882dd1 100755 --- a/30_Cube/python/cube.py +++ b/30_Cube/python/cube.py @@ -31,7 +31,11 @@ def play_game() -> None: for _ in range(5): while True: mine = mine_position() - if not (mine in mines or mine == (1, 1, 1) or mine == (3, 3, 3)): + if ( + mine not in mines + and mine != (1, 1, 1) + and mine != (3, 3, 3) + ): break mines.append(mine) wager = -1 diff --git a/30_Cube/rust/Cargo.lock b/30_Cube/rust/Cargo.lock new file mode 100644 index 000000000..4fe2abbea --- /dev/null +++ b/30_Cube/rust/Cargo.lock @@ -0,0 +1,75 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/31_Depth_Charge/python/depth_charge.py b/31_Depth_Charge/python/depth_charge.py index a177af9ab..6134f8ce9 100644 --- a/31_Depth_Charge/python/depth_charge.py +++ b/31_Depth_Charge/python/depth_charge.py @@ -14,7 +14,7 @@ def show_welcome() -> None: # Clear screen. chr(27) is `Esc`, and the control sequence is # initiated by Ctrl+[ # `J` is "Erase in Display" and `2J` means clear the entire screen - print(chr(27) + "[2J") + print(f"{chr(27)}[2J") # Show the intro text, centered print("DEPTH CHARGE".center(45)) diff --git a/31_Depth_Charge/rust/Cargo.lock b/31_Depth_Charge/rust/Cargo.lock new file mode 100644 index 000000000..8be5f5fa6 --- /dev/null +++ b/31_Depth_Charge/rust/Cargo.lock @@ -0,0 +1,243 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bitflags" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi", + "windows-targets", +] + +[[package]] +name = "libc" +version = "0.2.170" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy 0.7.35", +] + +[[package]] +name = "proc-macro2" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1f1914ce909e1658d9907913b4b91947430c7d9be598b15a1912935b8c04801" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +dependencies = [ + "rand_chacha", + "rand_core", + "zerocopy 0.8.21", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "syn" +version = "2.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e02e925281e18ffd9d640e234264753c43edc62d64b2d4cf898f1bc5e75f3fc2" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" + +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf01143b2dd5d134f11f545cf9f1431b13b749695cb33bcce051e7568f99478" +dependencies = [ + "zerocopy-derive 0.8.21", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712c8386f4f4299382c9abee219bee7084f78fb939d88b6840fcc1320d5f6da2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/31_Depth_Charge/rust/Cargo.toml b/31_Depth_Charge/rust/Cargo.toml new file mode 100644 index 000000000..3a87396aa --- /dev/null +++ b/31_Depth_Charge/rust/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "rust" +version = "0.1.0" +edition = "2021" + +[dependencies] +rand = "0.9.0" \ No newline at end of file diff --git a/31_Depth_Charge/rust/src/main.rs b/31_Depth_Charge/rust/src/main.rs new file mode 100644 index 000000000..f997fda36 --- /dev/null +++ b/31_Depth_Charge/rust/src/main.rs @@ -0,0 +1,145 @@ +/** DEPTH CHARGE GAME + * https://github.com/marquesrs/basic-computer-games/blob/main/31_Depth_Charge/depthcharge.bas + * Direct conversion from BASIC to Rust by Pablo Marques (marquesrs). + * No additional features or improvements were added. As a faithful translation, + * many of the code here are done in an unrecommended way by today's standards. + * 03/03/25 +*/ + +use std::io::Write; +use rand::Rng; + +fn input(msg: &str) -> String { + print!("{}", msg); + let _ =std::io::stdout().flush().unwrap(); + let mut input = String::new(); + std::io::stdin().read_line(&mut input).unwrap(); + return input.trim().to_uppercase(); +} + +fn main() { + // 2 PRINT TAB(30);"DEPTH CHARGE" + // 4 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" + // 6 PRINT: PRINT: PRINT + print!("{}", format!("{}{}\n{}{}\n\n\n\n", + " ".repeat(29), + "DEPTH CHARGE", + " ".repeat(14), + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" + )); + + // 20 INPUT "DIMENSION OF SEARCH AREA";G: PRINT + let g = input("DIMENSION OF SEARCH AREA: ").parse::().unwrap(); + + // 30 N=INT(LOG(G)/LOG(2))+1 + let n = (f32::ln(g as f32) / f32::ln(2.0) + 1.0) as i32; + + // 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 + let a = (g * rand::rng().random_range(0..=1)) as i32; + let b = (g * rand::rng().random_range(0..=1)) as i32; + let c = (g * rand::rng().random_range(0..=1)) as i32; + print!("{}", format!("{}{}{}{}{}{}{}{}{}{}{}", + "\nYOU ARE THE CAPTAIN OF THE DESTROYER USS COMPUTER\n", + "AN ENEMY SUB HAS BEEN CAUSING YOU TROUBLE. YOUR\n", + "MISSION IS TO DESTROY IT. YOU HAVE ", + n, + " SHOTS.\n", + "SPECIFY DEPTH CHARGE EXPLOSION POINT WITH A\n", + "TRIO OF NUMBERS -- THE FIRST TWO ARE THE\n", + "SURFACE COORDINATES; THE THIRD IS THE DEPTH.\n", + format!("EXAMPLE FOR DIMENSION {}: ", g), + format!("{}, {}, {}", a, b, c), + "\nGOOD LUCK !\n" + )); + + 'main: loop { + // 110 A=INT(G*RND(1)) : B=INT(G*RND(1)) : C=INT(G*RND(1)) + let a = (g * rand::rng().random_range(0..=1)) as i32; + let b = (g * rand::rng().random_range(0..=1)) as i32; + let c = (g * rand::rng().random_range(0..=1)) as i32; + // 120 FOR D=1 TO N : PRINT : PRINT "TRIAL #";D; : INPUT X,Y,Z + let mut x; + let mut y; + let mut z; + for d in 1..=n { + print!("\nTRIAL #{}\n", d); + x = input("X: ").parse::().unwrap(); + y = input("Y: ").parse::().unwrap(); + z = input("Z: ").parse::().unwrap(); + // 130 IF ABS(X-A)+ABS(Y-B)+ABS(Z-C)=0 THEN 300 + if i32::abs(x-a) + i32::abs(y-b) + i32::abs(z-c) == 0 { + // 300 PRINT : PRINT "B O O M ! ! YOU FOUND IT IN";D;"TRIES!" + print!("{}", format!("{}{}{}{}{}", + "\n", + "B O O M ! ! YOU FOUND IT IN ", + d, + " TRIES!\n", + "\n" + )); + // 400 PRINT : PRINT: INPUT "ANOTHER GAME (Y OR N)";A$ + if replay() { continue 'main; } + else { break 'main; } + } + + // 140 GOSUB 500 : PRINT : NEXT D + subroutine(x, y, z, a, b, c); + println!(); + } + // 200 PRINT : PRINT "YOU HAVE BEEN TORPEDOED! ABANDON SHIP!" + // 210 PRINT "THE SUBMARINE WAS AT";A;",";B;",";C : GOTO 400 + print!("{}", format!("{}{}{}{}{}{}{}{}", + "\nYOU HAVE BEEN TORPEDOED! ABANDON SHIP!\n", + "THE SUBMARINE WAS AT ", + a, + ",", + b, + ",", + c, + "\n" + )); + + replay(); + } + // 600 END +} + +// 500 PRINT "SONAR REPORTS SHOT WAS "; +// 510 IF Y>B THEN PRINT "NORTH"; +// 520 IF YA THEN PRINT "EAST"; +// 540 IF XB OR X<>A THEN PRINT " AND"; +// 560 IF Z>C THEN PRINT " TOO LOW." +// 570 IF Zb { print!("NORTH"); }; + if ya { print!("EAST"); }; + if xc { print!(" TOO LOW."); }; + if z bool { + let r = input("ANOTHER GAME (Y OR N): "); + // 410 IF A$="Y" THEN 100 + if r == "Y" { + return true; + } + else { + // 420 PRINT "OK. HOPE YOU ENJOYED YOURSELF." : GOTO 600 + println!("OK. HOPE YOU ENJOYED YOURSELF."); + return false; + } +} \ No newline at end of file diff --git a/32_Diamond/python/diamond.py b/32_Diamond/python/diamond.py index 5ebcd2aab..3b261d36c 100644 --- a/32_Diamond/python/diamond.py +++ b/32_Diamond/python/diamond.py @@ -16,10 +16,7 @@ def print_diamond(begin_width, end_width, step, width, count) -> None: line_buffer = " " * ((width - n) // 2) for across in range(count): for a in range(n): - if a >= len(edge_string): - line_buffer += fill - else: - line_buffer += edge_string[a] + line_buffer += fill if a >= len(edge_string) else edge_string[a] line_buffer += " " * ( (width * (across + 1) + (width - n) // 2) - len(line_buffer) ) @@ -39,7 +36,7 @@ def main() -> None: PAGE_WIDTH = 60 - count = int(PAGE_WIDTH / width) + count = PAGE_WIDTH // width for _down in range(count): print_diamond(1, width, 2, width, count) diff --git a/32_Diamond/rust/Cargo.lock b/32_Diamond/rust/Cargo.lock new file mode 100644 index 000000000..b21cc6a2d --- /dev/null +++ b/32_Diamond/rust/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "rust" +version = "0.1.0" diff --git a/33_Dice/rust/Cargo.lock b/33_Dice/rust/Cargo.lock new file mode 100644 index 000000000..4fe2abbea --- /dev/null +++ b/33_Dice/rust/Cargo.lock @@ -0,0 +1,75 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/34_Digits/README.md b/34_Digits/README.md index 569122c8e..6e82efda5 100644 --- a/34_Digits/README.md +++ b/34_Digits/README.md @@ -18,4 +18,5 @@ http://www.vintage-basic.net/games.html #### Porting Notes -(please note any difficulties or challenges in porting here) +- The program contains a lot of mysterious and seemingly arbitrary constants. It's not clear there is any logic or rationality behind it. +- The key equation involved in the guess (line 700) involves a factor of `A`, but `A` is always 0, making that term meaningless. As a result, all the work to build and update array K and value Z2 appear to be meaningless, too. diff --git a/34_Digits/perl/digits.pl b/34_Digits/perl/digits.pl new file mode 100755 index 000000000..2f49bd0e9 --- /dev/null +++ b/34_Digits/perl/digits.pl @@ -0,0 +1,125 @@ +#!/usr/bin/perl + +# Digits program in Perl +# Translated by Kevin Brannen (kbrannen) + +use strict; +use warnings; + +# globals +my $Answer; + +print "\n"; +print " " x 33, "DIGITS"; +print " " x 15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n"; + +print "THIS IS A GAME OF GUESSING.\n"; +print "FOR INSTRUCTIONS, TYPE '1', ELSE TYPE '0': "; +chomp($Answer = <>); +if ($Answer == 1) +{ + print "\nPLEASE TAKE A PIECE OF PAPER AND WRITE DOWN\n"; + print "THE DIGITS '0', '1', OR '2' THIRTY TIMES AT RANDOM.\n"; + print "ARRANGE THEM IN THREE LINES OF TEN DIGITS EACH.\n"; + print "I WILL ASK FOR THEN TEN AT A TIME.\n"; + print "I WILL ALWAYS GUESS THEM FIRST AND THEN LOOK AT YOUR\n"; + print "NEXT NUMBER TO SEE IF I WAS RIGHT. BY PURE LUCK,\n"; + print "I OUGHT TO BE RIGHT TEN TIMES. BUT I HOPE TO DO BETTER\n"; + print "THAN THAT. *****\n\n\n"; +} + +my ($A, $B, $C) = (0, 1, 3); +my (@M, @K, @L); # DIM M(26,2),K(2,2),L(8,2) +while (1) +{ + for my $i (0 .. 26) { for my $j (0 .. 2) { $M[$i][$j] = 1; } } + for my $i (0 .. 2) { for my $j (0 .. 2) { $K[$i][$j] = 9; } } + for my $i (0 .. 8) { for my $j (0 .. 2) { $L[$i][$j] = 3; } } + $L[0][0] = $L[4][1] = $L[8][2] = 2; + my $Z = 26; + my $Z1 = 8; + my $Z2 = 2; + my $X = 0; + my @N; + + for my $T (1 .. 3) + { + my $have_input = 0; + while (!$have_input) + { + $have_input = 1; + print "\nTEN NUMBERS, PLEASE: "; + chomp($Answer = <>); + $Answer = "0 " . $Answer; # need to be 1-based, so prepend a throw-away value for [0] + @N = split(/\s+/, $Answer); + for my $i (1 .. 10) + { + if (!defined($N[$i]) || ($N[$i] != 0 && $N[$i] != 1 && $N[$i] != 2)) + { + print "ONLY USE THE DIGITS '0', '1', OR '2'.\n"; + print "LET'S TRY AGAIN."; + $have_input = 0; + last; + } + } + } + + print "\nMY GUESS\tYOUR NO.\tRESULT\tNO. RIGHT\n\n"; + for my $U (1 .. 10) + { + my $num = $N[$U]; + my $S = 0; + my $G; + for my $J (0 .. 2) + { + my $S1 = $A * $K[$Z2][$J] + $B * $L[$Z1][$J] + $C * $M[$Z][$J]; + next if ($S > $S1); + if ($S >= $S1) + { + next if (rand(1) < .5); + } + $S = $S1; + $G = $J; + } # NEXT J + print " $G\t\t$N[$U]\t\t"; + if ($G == $N[$U]) + { + $X++; + print "RIGHT\t$X\n"; + $M[$Z][$num]++; + $L[$Z1][$num]++; + $K[$Z2][$num]++; + $Z -= int($Z / 9) * 9; + $Z = 3 * $Z + $N[$U]; + } + else + { + print "WRONG\t$X\n"; + } + $Z1 = $Z - int($Z / 9) * 9; + $Z2 = $N[$U]; + } # NEXT U + } # NEXT T + + print "\n"; + if ($X == 10) + { + print "I GUESSED EXACTLY 1/3 OF YOUR NUMBERS.\n"; + print "IT'S A TIE GAME.\n"; + } + elsif ($X > 10) + { + print "I GUESSED MORE THAN 1/3, OR $X, OF YOUR NUMBERS.\n"; + print "I WIN.\a\a\a\a\a\a\a\a\a\a" + } + else + { + print "I GUESSED LESS THAN 1/3, OR $X, OF YOUR NUMBERS.\n"; + print "YOU BEAT ME. CONGRATULATIONS *****\n"; + } + + print "\nDO YOU WANT TO TRY AGAIN (1 FOR YES, 0 FOR NO): "; + chomp($Answer = <>); + last if ($Answer != 1); +} +print "\nTHANKS FOR THE GAME.\n"; diff --git a/35_Even_Wins/python/evenwins.py b/35_Even_Wins/python/evenwins.py index 066c7d57b..b5a198298 100644 --- a/35_Even_Wins/python/evenwins.py +++ b/35_Even_Wins/python/evenwins.py @@ -46,9 +46,7 @@ def print_intro() -> None: def marbles_str(n: int) -> str: - if n == 1: - return "1 marble" - return f"{n} marbles" + return "1 marble" if n == 1 else f"{n} marbles" def choose_first_player() -> PlayerType: @@ -66,10 +64,7 @@ def choose_first_player() -> PlayerType: def toggle_player(whose_turn: PlayerType) -> PlayerType: - if whose_turn == "human": - return "computer" - else: - return "human" + return "computer" if whose_turn == "human" else "human" def to_int(s: str) -> Tuple[bool, int]: @@ -132,11 +127,7 @@ def computer_turn(marbles: MarbleCounts) -> None: r = marbles.middle - 6 * int(marbles.middle / 6) if int(marbles.human / 2) == marbles.human / 2: - if r < 1.5 or r > 5.3: - marbles_to_take = 1 - else: - marbles_to_take = r - 1 - + marbles_to_take = 1 if r < 1.5 or r > 5.3 else r - 1 elif marbles.middle < 4.2: marbles_to_take = marbles.middle elif r > 3.4: diff --git a/35_Even_Wins/rust/Cargo.lock b/35_Even_Wins/rust/Cargo.lock new file mode 100644 index 000000000..b21cc6a2d --- /dev/null +++ b/35_Even_Wins/rust/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "rust" +version = "0.1.0" diff --git a/36_Flip_Flop/lua/flipflop-game.lua b/36_Flip_Flop/lua/flipflop-game.lua new file mode 100644 index 000000000..d529d1017 --- /dev/null +++ b/36_Flip_Flop/lua/flipflop-game.lua @@ -0,0 +1,183 @@ +--[[ +Game of FlipFlop +=============== + +Based on the original BASIC game of FlipFlop from the 1970s. + +This is a faithful recreation of the classic game, maintaining the original gameplay +while modernizing the implementation for Lua. For example, the original's complex +trigonometric random number generation has been replaced with Lua's math.random, +as modern random number generators are more sophisticated than those available +in 1970s BASIC. + +How to Play: +----------- +The game presents a row of 10 X's. Your goal is to flip all X's to O's. +Enter a number from 1-10 to flip that position. The computer will also make +random moves in response. Special commands: +- Enter 0 to see the current board again +- Enter 11 to quit the game +Try to complete the puzzle in 12 moves or fewer for the best score! + +Strategy Tips: +------------ +- When you make a move, watch how the computer responds. It will often + make a predictable counter-move. +- If you enter the same position twice, the computer will use a different + random pattern for its response. +- Try to work systematically from one end of the board to the other, + rather than making random moves. +- If you get stuck, sometimes starting over (11) is better than continuing + with a poor position. + +Technical Notes: +-------------- +- Written for Lua 5.1 compatibility +- Uses tables for game state management +- Implements original game logic without goto statements +- Preserves the original game's move counting and win conditions +- Maintains the classic text-based interface style + +Original BASIC Version: +-------------------- +The original game used complex trigonometric functions for random number +generation, likely due to limitations in BASIC's random number capabilities: + R=TAN(Q+N/Q-N)-SIN(Q/N)+336*SIN(8*N) + N=R-INT(R) + N=INT(10*N) + +This has been simplified to use Lua's built-in random number generator +while maintaining the same gameplay experience. + +Converted from BASIC to Lua by Brian Wilkins +March 2025 +]]-- + +local function showInstructions() + print(" FLIP FLOP") + print(" CREATIVE COMPUTING") + print(" MORRISTOWN, NEW JERSEY") + print("\n\n") + print("THE OBJECT OF THIS GAME IS TO FLIP ALL THE X'S TO O'S.") + print("WHEN YOU INPUT A NUMBER, THAT NUMBER AND ALL ADJACENT") + print("NUMBERS WILL BE FLIPPED.") + print("\n") + print("INPUT A NUMBER FROM 1 TO 10 TO START THE GAME. TO QUIT") + print("THE GAME AT ANY TIME, TYPE A 0 (ZERO). TO START OVER WITH") + print("A NEW PUZZLE IN THE MIDDLE OF A GAME, TYPE A 11 (ELEVEN).") + print("\n\n") +end + +local function printUpdatedBoard(board) + print("1 2 3 4 5 6 7 8 9 10") + print(table.concat(board, " ")) + print("\n\n") +end + +local function processMove(gameState, position) + -- Check if current position is O + if gameState.board[position] == "O" then + gameState.board[position] = "X" + if position == gameState["lastMove"] then + -- Generate random position using simplified random logic + local R = math.random() -- between 0 and 1 + local N = math.floor(R * 10) + 1 + if gameState.board[N] == "O" then + gameState.board[N] = "X" + else + gameState.board[N] = "O" + end + end + else + gameState.board[position] = "O" + -- Generate another random position + local R = math.random() + local N = math.floor(R * 10) + 1 + if gameState.board[N] == "O" then + gameState.board[N] = "X" + else + gameState.board[N] = "O" + end + end +end + +local function checkWin(gameState) + -- Check if all positions are O's + for i = 1, 10 do + if gameState.board[i] ~= "O" then + return false + end + end + return true +end + +local function initializeGame() + local gameState = { + board = {}, + lastMove = nil, + moves = 0 + } + + -- Initialize board + for i = 1, 10 do + gameState.board[i] = "X" + end + return gameState +end + +local function playGame() + showInstructions() + + local gameState = initializeGame() + local playing = true + + print("HERE IS THE STARTING LINE OF X'S.") + print("\n\n") + printUpdatedBoard(gameState.board) + + while playing do + print("INPUT THE NUMBER") + local N = tonumber(io.read()) + + -- Check if input is valid + if not N or N ~= math.floor(N) then + print("ILLEGAL ENTRY--TRY AGAIN.") + elseif N == 11 then + playing = false + elseif N > 11 then + print("ILLEGAL ENTRY--TRY AGAIN.") + elseif N == 0 then + printUpdatedBoard(gameState.board) + else + -- Process the move + processMove(gameState, N) + gameState["lastMove"] = N + gameState.moves = gameState.moves + 1 + + -- Show the updated board + printUpdatedBoard(gameState.board) + + -- Check for win + if checkWin(gameState) then + if gameState.moves <= 12 then + print(string.format("VERY GOOD. YOU GUESSED IT IN ONLY %d GUESSES.", gameState.moves)) + else + print(string.format("TRY HARDER NEXT TIME. IT TOOK YOU %d GUESSES.", gameState.moves)) + end + + print("DO YOU WANT TO TRY ANOTHER PUZZLE? (Y/N)") + local answer = io.read():upper() + if answer:sub(1,1) == "Y" then + gameState = initializeGame() + print("HERE IS THE STARTING LINE OF X'S.") + print("\n\n") + printUpdatedBoard(gameState.board) + else + playing = false + end + end + end + end +end + +playGame() diff --git a/36_Flip_Flop/perl/flipflop.pl b/36_Flip_Flop/perl/flipflop.pl new file mode 100755 index 000000000..63e3f84e7 --- /dev/null +++ b/36_Flip_Flop/perl/flipflop.pl @@ -0,0 +1,124 @@ +#!/usr/bin/perl + +# Flip Flop program in Perl +# Translated by Kevin Brannen (kbrannen) + +use strict; +use warnings; +use Math::Trig; + +print "\n"; +print " " x 32, "FLIPFLOP"; +print " " x 15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n"; +# *** CREATED BY MICHAEL CASS + +print "THE OBJECT OF THIS PUZZLE IS TO CHANGE THIS:\n\n"; +print "X X X X X X X X X X\n\n"; +print "TO THIS:\n\n"; +print "O O O O O O O O O O\n\n"; +print "BY TYPING THE NUMBER CORRESPONDING TO THE POSITION OF THE\n"; +print "LETTER ON SOME NUMBERS, ONE POSITION WILL CHANGE, ON\n"; +print "OTHERS, TWO WILL CHANGE. TO RESET LINE TO ALL X'S, TYPE 0\n"; +print "(ZERO) AND TO START OVER IN THE MIDDLE OF A GAME, TYPE \n"; +print "11 (ELEVEN).\n\n"; + +sub initialize +{ + my @a; + print "1 2 3 4 5 6 7 8 9 10\n"; + print "X X X X X X X X X X\n"; + for my $i (0 .. 10) { $a[$i] = "X"; } # make sure [0] has a value just in case + return @a; +} + +while (1) +{ + my $Q = rand(1); + my $C = 0; + print "HERE IS THE STARTING LINE OF X'S.\n\n"; + my @A = initialize(); + + while (1) + { + my $M = 0; + my $N; + while (1) + { + print "\nINPUT THE NUMBER: "; + chomp($N = <>); + if ($N != int($N) || $N < 0 || $N > 11) + { + print "ILLEGAL ENTRY--TRY AGAIN.\n"; + next; + } + last; + } + if ($N == 11) # start a new game + { + print "\n\n"; + last; + } + if ($N == 0) # reset line + { + @A = initialize(); + next; + } + + if ($M != $N) + { + $M = $N; + $A[$N] = ($A[$N] eq "O") ? "X" : "O"; + while ($M == $N) + { + my $R = tan($Q + $N / $Q - $N) - sin($Q / $N) + 336 * sin(8 * $N); + $N = $R - int($R); + $N = int(10 * $N); + if ($A[$N] eq "O") + { + $A[$N] = "X"; + next; + } + $A[$N] = "O"; + last; # GOTO 610 + + $A[$N] = "X"; + } + } + else + { + if ($A[$N] ne "O") { $A[$N] = "O"; } + while ($M == $N) + { + my $R = .592 * (1 / tan($Q / $N + $Q)) / sin( $N * 2 + $Q) - cos($N); + $N = $R - int($R); + $N = int(10 * $N); + if ($A[$N] eq "O") + { + $A[$N] = "X"; + next; + } + $A[$N] = "O"; + last; + } + } + + print "1 2 3 4 5 6 7 8 9 10\n"; + for my $i (1 .. 10) { print "$A[$i] "; } + print "\n"; + $C++; + my $i; + for ($i=1 ; $i <= 10 ; $i++) { + last if ($A[$i] ne "O"); + } + if ($i == 11) + { + if ($C <= 12) { print "VERY GOOD. YOU GUESSED IT IN ONLY $C GUESSES.\n"; } + else { print "TRY HARDER NEXT TIME. IT TOOK YOU $C GUESSES.\n"; } + last; + } + } + print "DO YOU WANT TO TRY ANOTHER PUZZLE (Y/N): "; + $_ = <>; + print "\n"; + last if (m/^n/i); +} diff --git a/36_Flip_Flop/rust/Cargo.lock b/36_Flip_Flop/rust/Cargo.lock new file mode 100644 index 000000000..81365033b --- /dev/null +++ b/36_Flip_Flop/rust/Cargo.lock @@ -0,0 +1,82 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "morristown" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83b191fd96370b91c2925125774011a7a0f69b4db9e2045815e4fd20af725f6" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "morristown", + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/37_Football/python/football.py b/37_Football/python/football.py index 748bc997a..c84c8df61 100644 --- a/37_Football/python/football.py +++ b/37_Football/python/football.py @@ -49,8 +49,7 @@ def ask_int(prompt: str) -> int: while True: answer = input(prompt) try: - int_answer = int(answer) - return int_answer + return int(answer) except Exception: pass diff --git a/38_Fur_Trader/README.md b/38_Fur_Trader/README.md index 8406487e7..6b08911ae 100644 --- a/38_Fur_Trader/README.md +++ b/38_Fur_Trader/README.md @@ -15,6 +15,10 @@ As published in Basic Computer Games (1978): Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html +#### Known Bugs + +- The value of some furs are not changed from the previous fort when you select fort 2 or 3. As a result, you will get a different value for your firs depending on whether you have previously visited a different fort. (All fur values are set when you visit Fort 1.) + #### Porting Notes (please note any difficulties or challenges in porting here) diff --git a/38_Fur_Trader/perl/README.md b/38_Fur_Trader/perl/README.md index e69c8b819..c7001be76 100644 --- a/38_Fur_Trader/perl/README.md +++ b/38_Fur_Trader/perl/README.md @@ -1,3 +1,5 @@ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Perl](https://www.perl.org/) + +You can answer yes/no questions in lower case if desired. diff --git a/38_Fur_Trader/perl/furtrader.pl b/38_Fur_Trader/perl/furtrader.pl new file mode 100755 index 000000000..f1e6b01ad --- /dev/null +++ b/38_Fur_Trader/perl/furtrader.pl @@ -0,0 +1,265 @@ +#!/usr/bin/perl + +# Fur Trader program in Perl +# Translated by Kevin Brannen (kbrannen) + +use strict; +use warnings; + +# globals +my @Pelts = (qw(0 MINK BEAVER ERMINE FOX )); +my $Num_pelts = 4; +my @Quantity; # how many of each fur +my $Money; +my $Max_pelts = 190; +my $Ermine_price; # like we have @Pelts and @Quantity we could have @Prices +my $Beaver_price; # then have 4 constants as index into the arrays to avoid +my $Fox_price; # the magic numbers 1-4, or better have a array of objects (really a hash) +my $Mink_price; # with the 3 keys (name, number, price), but well keep it + # with 4 vars like the basic program did + +print "\n"; +print " " x 31, "FUR TRADER\n"; +print " " x 15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n"; + +init(); +while (1) +{ + my $ans = get_yesno(); + last if ($ans ne "YES"); + + $Ermine_price = new_price(.15, 0.95); + $Beaver_price = new_price(.25, 1.00); + + print "\nYOU HAVE \$$Money SAVINGS.\n"; + print "AND $Max_pelts FURS TO BEGIN THE EXPEDITION.\n"; + print "\nYOUR $Max_pelts FURS ARE DISTRIBUTED AMONG THE FOLLOWING\n"; + print "KINDS OF PELTS: MINK, BEAVER, ERMINE AND FOX.\n"; + reset_furs(); + + my $total = 0; + for my $j ( 1 .. $Num_pelts) + { + print "\nHOW MANY $Pelts[$j] PELTS DO YOU HAVE? "; + chomp(my $ans = <>); + $Quantity[$j] = int($ans); + $total += $Quantity[$j]; + } + if ($total > $Max_pelts) + { + print "\nYOU MAY NOT HAVE THAT MANY FURS.\n"; + print "DO NOT TRY TO CHEAT. I CAN ADD.\n"; + print "YOU MUST START AGAIN.\n"; + init(); + next; + } + + print "\nYOU MAY TRADE YOUR FURS AT FORT 1, FORT 2,\n"; + print "OR FORT 3. FORT 1 IS FORT HOCHELAGA (MONTREAL)\n"; + print "AND IS UNDER THE PROTECTION OF THE FRENCH ARMY.\n"; + print "FORT 2 IS FORT STADACONA (QUEBEC) AND IS UNDER THE\n"; + print "PROTECTION OF THE FRENCH ARMY. HOWEVER, YOU MUST\n"; + print "MAKE A PORTAGE AND CROSS THE LACHINE RAPIDS.\n"; + print "FORT 3 IS FORT NEW YORK AND IS UNDER DUTCH CONTROL.\n"; + print "YOU MUST CROSS THROUGH IROQUOIS LAND.\n"; + my $done = 0; + while (!$done) + { + $ans = 0; + while ($ans < 1 || $ans > 3) + { + no warnings; # in case user enters alpha chars, then int() will return 0 with no warnings + print "ANSWER 1, 2, OR 3: "; + $ans = int(<>); + } + # returns 0 if they want to go somewhere else; + # or returns the old basic line number to show what to do + if ($ans == 1) { $done = fort1(); } + elsif ($ans == 2) { $done = fort2(); } + elsif ($ans == 3) { $done = fort3(); } + } + + if ($done == 1410) + { + print "YOUR BEAVER SOLD FOR \$", $Beaver_price * $Quantity[2], "\t"; + } + + if ($done <= 1414) + { + print "YOUR FOX SOLD FOR \$", $Fox_price * $Quantity[4], "\n"; + print "YOUR ERMINE SOLD FOR \$", $Ermine_price * $Quantity[3], "\t"; + print "YOUR MINK SOLD FOR \$", $Mink_price * $Quantity[1], "\n"; + } + + # 1418 is always done + $Money += $Mink_price * $Quantity[1] + $Beaver_price * $Quantity[2] + $Ermine_price * $Quantity[3] + $Fox_price * $Quantity[4]; + print "\nYOU NOW HAVE \$$Money INCLUDING YOUR PREVIOUS SAVINGS\n"; + print "\nDO YOU WANT TO TRADE FURS NEXT YEAR? "; +} +exit(0); + +############################################################### + +sub init +{ + print "YOU ARE THE LEADER OF A FRENCH FUR TRADING EXPEDITION IN\n"; + print "1776 LEAVING THE LAKE ONTARIO AREA TO SELL FURS AND GET\n"; + print "SUPPLIES FOR THE NEXT YEAR. YOU HAVE A CHOICE OF THREE\n"; + print "FORTS AT WHICH YOU MAY TRADE. THE COST OF SUPPLIES\n"; + print "AND THE AMOUNT YOU RECEIVE FOR YOUR FURS WILL DEPEND\n"; + print "ON THE FORT THAT YOU CHOOSE.\n"; + + $Money = 600; + print "DO YOU WISH TO TRADE FURS?\n"; +} + +sub new_price +{ + my ($base, $factor) = @_; + return int(($base * rand(1) + $factor) * 100 + .5) / 100; +} + +sub supplies_fs +{ + print "SUPPLIES AT FORT STADACONA COST \$125.00.\n"; + print "YOUR TRAVEL EXPENSES TO STADACONA WERE \$15.00.\n"; +} + +sub supplies_ny +{ + print "SUPPLIES AT NEW YORK COST \$80.00.\n"; + print "YOUR TRAVEL EXPENSES TO NEW YORK WERE \$25.00.\n"; +} + +sub reset_furs +{ + for my $j (1 .. $Num_pelts) { $Quantity[$j] = 0; } +} + +sub get_yesno +{ + my $ans; + print "ANSWER YES OR NO: "; + chomp($ans = uc(<>)); + return $ans; +} + +sub trade_elsewhere +{ + print "DO YOU WANT TO TRADE AT ANOTHER FORT? "; + my $ans = get_yesno(); + return $ans; +} + +sub fort1 +{ + print "\nYOU HAVE CHOSEN THE EASIEST ROUTE. HOWEVER, THE FORT\n"; + print "IS FAR FROM ANY SEAPORT. THE VALUE\n"; + print "YOU RECEIVE FOR YOUR FURS WILL BE LOW AND THE COST\n"; + print "OF SUPPLIES HIGHER THAN AT FORTS STADACONA OR NEW YORK.\n"; + my $ans = trade_elsewhere(); + if ($ans eq "YES") { return 0; } + + $Money -= 160; + $Mink_price = new_price(.2, .7 ); + $Ermine_price = new_price(.2, .65); + $Beaver_price = new_price(.2, .75); + $Fox_price = new_price(.2, .8 ); + print "\nSUPPLIES AT FORT HOCHELAGA COST \$150.00.\n"; + print "YOUR TRAVEL EXPENSES TO HOCHELAGA WERE \$10.00.\n"; + return 1410; +} + +sub fort2 +{ + print "\nYOU HAVE CHOSEN A HARD ROUTE. IT IS, IN COMPARSION,\n"; + print "HARDER THAN THE ROUTE TO HOCHELAGA BUT EASIER THAN\n"; + print "THE ROUTE TO NEW YORK. YOU WILL RECEIVE AN AVERAGE VALUE\n"; + print "FOR YOUR FURS AND THE COST OF YOUR SUPPLIES WILL BE AVERAGE.\n"; + my $ans = trade_elsewhere(); + if ($ans eq "YES") { return 0; } + + $Money -= 140; + print "\n"; + $Mink_price = new_price(.3, .85); + $Ermine_price = new_price(.15, .8); + $Beaver_price = new_price(.2, .9); + my $P = int(10 * rand(1)) + 1; + if ($P <= 2) + { + $Quantity[2] = 0; + print "YOUR BEAVER WERE TOO HEAVY TO CARRY ACROSS\n"; + print "THE PORTAGE. YOU HAD TO LEAVE THE PELTS, BUT FOUND\n"; + print "THEM STOLEN WHEN YOU RETURNED.\n"; + supplies_fs(); + return 1414; + } + elsif ($P <= 6) + { + print "YOU ARRIVED SAFELY AT FORT STADACONA.\n"; + supplies_fs(); + } + elsif ($P <= 8) + { + reset_furs(); + print "YOUR CANOE UPSET IN THE LACHINE RAPIDS. YOU\n"; + print "LOST ALL YOUR FURS.\n"; + supplies_fs(); + return 1418; + } + elsif ($P <= 10) + { + $Quantity[4] = 0; + print "YOUR FOX PELTS WERE NOT CURED PROPERLY.\n"; + print "NO ONE WILL BUY THEM.\n"; + supplies_fs(); + } + return 1410; +} + +sub fort3 +{ + print "\nYOU HAVE CHOSEN THE MOST DIFFICULT ROUTE. AT\n"; + print "FORT NEW YORK YOU WILL RECEIVE THE HIGHEST VALUE\n"; + print "FOR YOUR FURS. THE COST OF YOUR SUPPLIES\n"; + print "WILL BE LOWER THAN AT ALL THE OTHER FORTS.\n"; + my $ans = trade_elsewhere(); + if ($ans eq "YES") { return 0; } + + $Money -= 105; + print "\n"; + $Mink_price = new_price(.15, 1.05); + $Fox_price = new_price(.25, 1.1); + $Fox_price = new_price(.25, 1.1); + my $P = int(10 * rand(1)) + 1; + if ($P <= 2) + { + print "YOU WERE ATTACKED BY A PARTY OF IROQUOIS.\n"; + print "ALL PEOPLE IN YOUR TRADING GROUP WERE\n"; + print "KILLED. THIS ENDS THE GAME.\n"; + exit(0); + } + elsif ($P <= 6) + { + print "YOU WERE LUCKY. YOU ARRIVED SAFELY\n"; + print "AT FORT NEW YORK.\n"; + supplies_ny(); + } + elsif ($P <= 8) + { + reset_furs(); + print "YOU NARROWLY ESCAPED AN IROQUOIS RAIDING PARTY.\n"; + print "HOWEVER, YOU HAD TO LEAVE ALL YOUR FURS BEHIND.\n"; + supplies_ny(); + return 1418; + } + elsif ($P <= 10) + { + $Beaver_price /= 2; + $Mink_price /= 2; + print "YOUR MINK AND BEAVER WERE DAMAGED ON YOUR TRIP.\n"; + print "YOU RECEIVE ONLY HALF THE CURRENT PRICE FOR THESE FURS.\n"; + supplies_ny(); + } + return 1410; +} diff --git a/38_Fur_Trader/python/furtrader.py b/38_Fur_Trader/python/furtrader.py index 190b7c3fd..0755c4100 100755 --- a/38_Fur_Trader/python/furtrader.py +++ b/38_Fur_Trader/python/furtrader.py @@ -82,7 +82,7 @@ def show_fort_comment(which_fort) -> None: print("FOR YOUR FURS. THE COST OF YOUR SUPPLIES") print("WILL BE LOWER THAN AT ALL THE OTHER FORTS.") else: - print("Internal error #1, fort " + str(which_fort) + " does not exist") + print(f"Internal error #1, fort {str(which_fort)} does not exist") sys.exit(1) # you have a bug print() @@ -109,7 +109,7 @@ def get_furs_purchase() -> List[int]: Accept numeric inputs, re-prompting on incorrect input values""" results: List[int] = [] - print("YOUR " + str(MAX_FURS) + " FURS ARE DISTRIBUTED AMONG THE FOLLOWING") + print(f"YOUR {str(MAX_FURS)} FURS ARE DISTRIBUTED AMONG THE FOLLOWING") print("KINDS OF PELTS: MINK, BEAVER, ERMINE AND FOX.") print() @@ -130,9 +130,9 @@ def main() -> None: print(" " * 15 + "(Ported to Python Oct 2012 krt@krt.com.au)") print("\n\n\n") - game_state = "starting" fox_price = None # sometimes this takes the "last" price (probably this was a bug) + game_state = "starting" while True: if game_state == "starting": @@ -150,7 +150,7 @@ def main() -> None: elif game_state == "trading": print() print("YOU HAVE $ %1.2f IN SAVINGS" % (player_funds)) - print("AND " + str(MAX_FURS) + " FURS TO BEGIN THE EXPEDITION") + print(f"AND {str(MAX_FURS)} FURS TO BEGIN THE EXPEDITION") player_furs = get_furs_purchase() if sum(player_furs) > MAX_FURS: @@ -221,10 +221,7 @@ def main() -> None: print("NO ONE WILL BUY THEM.") player_furs[FUR_FOX] = 0 else: - print( - "Internal Error #3, Out-of-bounds event_picker" - + str(event_picker) - ) + print(f"Internal Error #3, Out-of-bounds event_picker{str(event_picker)}") sys.exit(1) # you have a bug print() @@ -268,10 +265,7 @@ def main() -> None: print("YOUR MINK AND BEAVER WERE DAMAGED ON YOUR TRIP.") print("YOU RECEIVE ONLY HALF THE CURRENT PRICE FOR THESE FURS.") else: - print( - "Internal Error #4, Out-of-bounds event_picker" - + str(event_picker) - ) + print(f"Internal Error #4, Out-of-bounds event_picker{str(event_picker)}") sys.exit(1) # you have a bug print() @@ -280,7 +274,7 @@ def main() -> None: player_funds -= 105 else: - print("Internal error #2, fort " + str(which_fort) + " does not exist") + print(f"Internal error #2, fort {str(which_fort)} does not exist") sys.exit(1) # you have a bug # Calculate sales diff --git a/39_Golf/README.md b/39_Golf/README.md index 262e7c386..f8769f99b 100644 --- a/39_Golf/README.md +++ b/39_Golf/README.md @@ -14,6 +14,10 @@ As published in Basic Computer Games (1978): Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html +#### Known Bugs + +- The weakness numbers printed in the original BASIC program are wrong. It says 4=TRAP SHOTS, 5=PUTTING, but in the code, trap shots and putting are 3 and 4, respectively. + #### Porting Notes (please note any difficulties or challenges in porting here) diff --git a/39_Golf/python/golf.py b/39_Golf/python/golf.py index 9132da2e6..d60bb60bb 100644 --- a/39_Golf/python/golf.py +++ b/39_Golf/python/golf.py @@ -565,13 +565,9 @@ def tee_up(self) -> None: ): self.putt = 10 print("[PUTTER: average 10 yards]") - if odds(20): - msg = "Keep your head down.\n" - else: - msg = "" - + msg = "Keep your head down.\n" if odds(20) else "" self.ask( - msg + "Choose your putt potency. (1-10)", + f"{msg}Choose your putt potency. (1-10)", 1, 10, self.set_putter_and_stroke, @@ -599,7 +595,7 @@ def stroke(self, club_amt: float, club_index: int) -> None: self.is_in_rough(self.ball) or self.is_in_hazard(self.ball, GameObjType.SAND) ) - and not (club_index == 8 or club_index == 9) + and club_index not in {8, 9} and odds(40) ): flags |= dub @@ -637,11 +633,10 @@ def stroke(self, club_amt: float, club_index: int) -> None: flags |= slice_ else: flags |= hook + elif odds(50): + flags |= hook else: - if odds(50): - flags |= hook - else: - flags |= slice_ + flags |= slice_ # beginner's luck ! # If handicap is greater than 15, there's a 10% chance of avoiding all errors @@ -664,21 +659,14 @@ def stroke(self, club_amt: float, club_index: int) -> None: distance: float rnd = random.randint(1, 101) - if self.handicap < 15: - if rnd <= 25: - distance = club_amt - (club_amt * (self.handicap / 100.0)) - elif rnd > 25 and rnd <= 75: - distance = club_amt - else: - distance = club_amt + (club_amt * 0.10) + if self.handicap < 15 and rnd <= 25 or self.handicap >= 15 and rnd <= 75: + distance = club_amt - (club_amt * (self.handicap / 100.0)) + elif self.handicap < 15 and rnd <= 75 or self.handicap >= 15: + distance = club_amt else: - if rnd <= 75: - distance = club_amt - (club_amt * (self.handicap / 100.0)) - else: - distance = club_amt - + distance = club_amt + (club_amt * 0.10) if self.player_difficulty == 3 and odds(80): # poor distance - distance = distance * 0.80 + distance *= 0.80 if (flags & luck) == luck: distance = club_amt @@ -755,7 +743,7 @@ def interpret_results(self, plot: Plot, flags: int) -> None: msg = "Your ball has gone to a watery grave." else: msg = "Your ball is lost in the water." - print(msg + " Take a penalty stroke.") + print(f"{msg} Take a penalty stroke.") self.score_card_record_stroke(self.ball) self.tee_up() return @@ -773,27 +761,18 @@ def interpret_results(self, plot: Plot, flags: int) -> None: return if (flags & in_cup) == in_cup: - if odds(50): - msg = "You holed it." - else: - msg = "It's in!" + msg = "You holed it." if odds(50) else "It's in!" print(msg) self.score_card_record_stroke(Ball(plot.x, plot.y, 0, GameObjType.BALL)) self.report_current_score() return - if ((flags & slice_) == slice_) and not ((flags & on_green) == on_green): - if (flags & out_of_bounds) == out_of_bounds: - bad = "badly" - else: - bad = "" + if (flags & slice_) == slice_ and flags & on_green != on_green: + bad = "badly" if (flags & out_of_bounds) == out_of_bounds else "" print(f"You sliced{bad}: {plot.offline} yards offline.") - if ((flags & hook) == hook) and not ((flags & on_green) == on_green): - if (flags & out_of_bounds) == out_of_bounds: - bad = "badly" - else: - bad = "" + if (flags & hook) == hook and flags & on_green != on_green: + bad = "badly" if (flags & out_of_bounds) == out_of_bounds else "" print(f"You hooked{bad}: {plot.offline} yards offline.") if self.stroke_num > 1: @@ -814,7 +793,7 @@ def interpret_results(self, plot: Plot, flags: int) -> None: if (flags & on_green) == on_green: if cup_distance < 4: - pd = str(cup_distance * 3) + " feet" + pd = f"{str(cup_distance * 3)} feet" else: pd = f"{cup_distance} yards" print(f"You're on the green. It's {pd} from the pin.") @@ -844,16 +823,10 @@ def report_current_score(self) -> None: if len(self.score_card[self.hole_num]) == (par - 3): print("Double Eagle! Unbelievable.") - total_par: int = 0 - for i in range(1, self.hole_num + 1): - total_par += CourseInfo[i].par - + total_par: int = sum(CourseInfo[i].par for i in range(1, self.hole_num + 1)) print(" ") print("-----------------------------------------------------") - if self.hole_num > 1: - hole_str = "holes" - else: - hole_str = "hole" + hole_str = "holes" if self.hole_num > 1 else "hole" print( f" Total par for {self.hole_num} {hole_str} is: {total_par}. " f"Your total is: {self.score_card_get_total()}." @@ -908,10 +881,7 @@ def is_on_green(self, ball: Ball) -> bool: def hazard_hit(self, h: Hazard, ball: Ball, hazard: GameObjType) -> bool: d = get_distance(Point(ball.X, ball.Y), Point(h.X, h.Y)) - result = False - if (d < h.Radius) and h.Type == hazard: - result = True - return result + return d < h.Radius and h.Type == hazard def is_in_hazard(self, ball: Ball, hazard: GameObjType) -> bool: result: bool = False @@ -938,9 +908,7 @@ def score_card_get_previous_stroke(self) -> Ball: return self.score_card[self.hole_num][len(self.score_card[self.hole_num]) - 1] def score_card_get_total(self) -> int: - total: int = 0 - for h in self.score_card: - total += len(h) + total: int = sum(len(h) for h in self.score_card) return total def ask( @@ -963,11 +931,8 @@ def ask( success = False n = 0 - if success: - if n >= min_ and n <= max_: - callback(n) - else: - self.ask(question, min_, max_, callback) + if success and n >= min_ and n <= max_: + callback(n) else: self.ask(question, min_, max_, callback) diff --git a/40_Gomoko/gomoko.bas b/40_Gomoko/gomoko.bas index 662cca132..d57c10a1b 100644 --- a/40_Gomoko/gomoko.bas +++ b/40_Gomoko/gomoko.bas @@ -28,7 +28,7 @@ 440 A(I,J)=1 500 REM *** COMPUTER TRIES AN INTELLIGENT MOVE *** 510 FOR E=-1 TO 1: FOR F=-1 TO 1: IF E+F-E*F=0 THEN 590 -540 X=I+F: Y=J+F: GOSUB 910 +540 X=I+E: Y=J+F: GOSUB 910 570 IF L=0 THEN 590 580 IF A(X,Y)=1 THEN 710 590 NEXT F: NEXT E diff --git a/40_Gomoko/python/Gomoko.py b/40_Gomoko/python/Gomoko.py index 7036d7f22..cc4930a3e 100644 --- a/40_Gomoko/python/Gomoko.py +++ b/40_Gomoko/python/Gomoko.py @@ -13,9 +13,7 @@ def print_board(A: List[List[Any]], n: int) -> None: def check_move(_I, _J, _N) -> bool: # 910 - if _I < 1 or _I > _N or _J < 1 or _J > _N: - return False - return True + return _I >= 1 and _I <= _N and _J >= 1 and _J <= _N def print_banner() -> None: @@ -36,11 +34,10 @@ def get_board_dimensions() -> int: n = 0 while True: n = int(input("WHAT IS YOUR BOARD SIZE (MIN 7/ MAX 19)? ")) - if n < 7 or n > 19: - print("I SAID, THE MINIMUM IS 7, THE MAXIMUM IS 19.") - print() - else: + if n >= 7 and n <= 19: break + print("I SAID, THE MINIMUM IS 7, THE MAXIMUM IS 19.") + print() return n @@ -62,9 +59,7 @@ def initialize_board(n: int) -> List[List[int]]: # Initialize the board board = [] for _x in range(n): - sub_a = [] - for _y in range(n): - sub_a.append(0) + sub_a = [0 for _y in range(n)] board.append(sub_a) return board @@ -87,51 +82,49 @@ def main() -> None: break elif not check_move(x, y, n): print("ILLEGAL MOVE. TRY AGAIN...") - else: - if board[x - 1][y - 1] != 0: - print("SQUARE OCCUPIED. TRY AGAIN...") - else: - board[x - 1][y - 1] = 1 - # COMPUTER TRIES AN INTELLIGENT MOVE - skip_ef_loop = False - for E in range(-1, 2): - for F in range(-1, 2): - if E + F - E * F == 0 or skip_ef_loop: - continue - X = x + F - Y = y + F - if not check_move(X, Y, n): - continue - if board[X - 1][Y - 1] == 1: - skip_ef_loop = True - X = x - E - Y = y - F - if not check_move(X, Y, n): # 750 - while True: # 610 - X = random.randint(1, n) - Y = random.randint(1, n) - if ( - check_move(X, Y, n) - and board[X - 1][Y - 1] == 0 - ): - board[X - 1][Y - 1] = 2 - print_board(board, n) - break - else: - if board[X - 1][Y - 1] != 0: - while True: - X = random.randint(1, n) - Y = random.randint(1, n) - if ( - check_move(X, Y, n) - and board[X - 1][Y - 1] == 0 - ): - board[X - 1][Y - 1] = 2 - print_board(board, n) - break - else: + elif board[x - 1][y - 1] == 0: + board[x - 1][y - 1] = 1 + # COMPUTER TRIES AN INTELLIGENT MOVE + skip_ef_loop = False + for E in range(-1, 2): + for F in range(-1, 2): + if E + F - E * F == 0 or skip_ef_loop: + continue + X = x + F + Y = y + F + if not check_move(X, Y, n): + continue + if board[X - 1][Y - 1] == 1: + skip_ef_loop = True + X = x - E + Y = y - F + if not check_move(X, Y, n): # 750 + while True: # 610 + X = random.randint(1, n) + Y = random.randint(1, n) + if ( + check_move(X, Y, n) + and board[X - 1][Y - 1] == 0 + ): board[X - 1][Y - 1] = 2 print_board(board, n) + break + elif board[X - 1][Y - 1] == 0: + board[X - 1][Y - 1] = 2 + print_board(board, n) + else: + while True: + X = random.randint(1, n) + Y = random.randint(1, n) + if ( + check_move(X, Y, n) + and board[X - 1][Y - 1] == 0 + ): + board[X - 1][Y - 1] = 2 + print_board(board, n) + break + else: + print("SQUARE OCCUPIED. TRY AGAIN...") print() print("THANKS FOR THE GAME!!") repeat = int(input("PLAY AGAIN (1 FOR YES, 0 FOR NO)? ")) diff --git a/40_Gomoko/rust/Cargo.lock b/40_Gomoko/rust/Cargo.lock new file mode 100644 index 000000000..7ef3861c9 --- /dev/null +++ b/40_Gomoko/rust/Cargo.lock @@ -0,0 +1,411 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chacha20" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +dependencies = [ + "cfg-if", + "cpufeatures", + "rand_core", +] + +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "rand_core", + "wasip2", + "wasip3", +] + +[[package]] +name = "gomoku" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", + "serde", + "serde_core", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.183" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8" +dependencies = [ + "chacha20", + "getrandom", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/40_Gomoko/rust/Cargo.toml b/40_Gomoko/rust/Cargo.toml new file mode 100644 index 000000000..6874f0e90 --- /dev/null +++ b/40_Gomoko/rust/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "gomoku" +version = "0.1.0" +edition = "2024" + +[dependencies] +rand = "0.10.0" diff --git a/40_Gomoko/rust/src/main.rs b/40_Gomoko/rust/src/main.rs new file mode 100644 index 000000000..53ec70ae9 --- /dev/null +++ b/40_Gomoko/rust/src/main.rs @@ -0,0 +1,209 @@ +use std::{ + fmt::Display, + io::{BufRead, Write}, +}; + +use rand::prelude::*; + +fn main() { + println!("{:>33}GOMOKU", ""); + println!("{:>15}CREATIVE COMPUTING MORISSTOWN, NEW JERSEY\n\n\n", ""); + + println!("Welcome to the oriental game of Gomoku."); + println!("\nThe game is played on an N by N grid of a size"); + println!("that you specify. During your play, you may cover one grid"); + println!("intersection with a marker. The object of the game is to get"); + println!("5 adjacent markers in a row -- horizontally, vertically, or"); + println!("diagonally. On the board diagram, your moves are marked"); + println!("with a '1' and the computer moves with a '2'."); + println!("\nThe computer does not keep track of who has won."); + println!("To end the game, type -1,-1 for your move.\n"); + + loop { + let size = get_board_size(); + + // Line 210: initialize the game board + let mut game = Gomoku::new(size); + + if !game.play() { + break; + } + } +} + +struct Gomoku { + board: Vec>, + size: isize, +} + +impl Gomoku { + fn new(size: usize) -> Self { + Self { + board: vec![vec![Piece::Empty; size]; size], + size: size as isize, + } + } + + fn get_player_move(&self) -> (isize, isize) { + let mut first_attempt = true; + loop { + if !first_attempt { + println!("Illegal move. Try again..."); + } + first_attempt = false; + let response = prompt_for_input("Your play (I,J)"); + let Some((i, j)) = response.split_once(',') else { + continue; + }; + let Ok(i) = i.trim().parse::() else { + continue; + }; + let Ok(j) = j.trim().parse::() else { + continue; + }; + if i == -1 { + return (-1, -1); + } + if i < 1 || i > self.size || j < 1 || j > self.size { + continue; + } + return (i, j); + } + } + + fn play(&mut self) -> bool { + let mut rng = rand::rng(); + + // Line 300 + println!("\nWe alternate moves. You go first...\n"); + + 'next_move: loop { + println!("{self}"); + + // Line 310 + let (i, j) = self.get_player_move(); + + // Line 320 + if i == -1 { + break; + } + + let pos = self.get(i, j); + + match *pos { + Piece::Empty => *pos = Piece::Human, + Piece::Computer | Piece::Human => { + println!("Square occupied. Try again..."); + continue; + } + } + + // Lines 500-590: The computer tries an intelligent move + for e in -1..=1 { + for f in -1..=1 { + // Line 510: + if e == 0 && f == 0 { + continue; + } + + // Line 540: + let x = i + e; + let y = j + f; + if x < 1 || x > self.size || y < 1 || y > self.size { + continue; + } + + let pos = self.get(x, y); + if *pos == Piece::Human { + let x = i - e; + let y = j - f; + if x < 1 || x > self.size || y < 1 || y > self.size { + continue; + } + let pos = self.get(x, y); + if *pos == Piece::Empty { + *pos = Piece::Computer; + continue 'next_move; + } + } + } + } + + // Lines 600-660: The computer tries a random move + loop { + let x = rng.random_range(1..=self.size as usize) as isize; + let y = rng.random_range(1..=self.size as usize) as isize; + let pos = self.get(x, y); + if *pos == Piece::Empty { + *pos = Piece::Computer; + continue 'next_move; + } + } + } + + // Lines 980-999 + println!("\nThanks for the game!!"); + prompt_for_input("Play again (1 for yes, 0 for no)") == "1" + } + + fn get(&mut self, i: isize, j: isize) -> &mut Piece { + &mut self.board[i as usize - 1][j as usize - 1] + } +} + +impl Display for Gomoku { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for row in &self.board { + for col in row { + write!(f, "{col}")?; + } + writeln!(f)?; + } + Ok(()) + } +} + +#[derive(Clone, Copy, PartialEq)] +enum Piece { + Empty, + Computer, + Human, +} + +impl Display for Piece { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + " {}", + match self { + Piece::Empty => '0', + Piece::Computer => '2', + Piece::Human => '1', + } + ) + } +} + +fn prompt_for_input>(prompt: S) -> String { + print!("{}? ", prompt.as_ref()); + let mut lock = std::io::stdout().lock(); + let _ = lock.flush(); + + let mut buffer = String::new(); + let stdin = std::io::stdin(); + let mut handle = stdin.lock(); + let _ = handle.read_line(&mut buffer); + buffer.trim().to_string() +} + +fn get_board_size() -> usize { + loop { + let buffer = prompt_for_input("What is your board size (min 7/ max 19)"); + if let Ok(val) = buffer.trim().parse::() + && (7..=19).contains(&val) + { + return val; + } + println!("I said, the minimum is 7, the maximum is 19."); + } +} diff --git a/41_Guess/rust/Cargo.lock b/41_Guess/rust/Cargo.lock new file mode 100644 index 000000000..5329dedd1 --- /dev/null +++ b/41_Guess/rust/Cargo.lock @@ -0,0 +1,75 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "guess" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/42_Gunner/perl/gunner.pl b/42_Gunner/perl/gunner.pl new file mode 100755 index 000000000..9df20deb4 --- /dev/null +++ b/42_Gunner/perl/gunner.pl @@ -0,0 +1,81 @@ +#!/usr/bin/perl + +# Gunner program in Perl +# Required extensive restructuring to remove all of the GOTO's. +# Translated by Kevin Brannen (kbrannen) + +use strict; +use warnings; + +# globals +my $Max_range = int(40000*rand(1)+20000); +my $Total_shots = 0; +my $Games = 0; + +print "\n"; +print " " x 30, "GUNNER\n"; +print " " x 15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n"; + +print "YOU ARE THE OFFICER-IN-CHARGE, GIVING ORDERS TO A GUN\n"; +print "CREW, TELLING THEM THE DEGREES OF ELEVATION YOU ESTIMATE\n"; +print "WILL PLACE A PROJECTILE ON TARGET. A HIT WITHIN 100 YARDS\n"; +print "OF THE TARGET WILL DESTROY IT.\n\n"; +print "MAXIMUM RANGE OF YOUR GUN IS $Max_range YARDS.\n\n"; + +GAME: while (1) +{ + my $target_dist = int($Max_range * (.1 + .8 * rand(1))); + my $shots = 0; + print "DISTANCE TO THE TARGET IS $target_dist YARDS.\n\n"; + while (1) + { + my $elevation = get_elevation(); # in degrees + $shots++; + my $dist = int($target_dist - ($Max_range * sin(2 * $elevation / 57.3))); + if (abs($dist) < 100) + { + print "*** TARGET DESTROYED *** $shots ROUNDS OF AMMUNITION EXPENDED.\n"; + $Total_shots += $shots; + if ($Games++ == 4) + { + print "\n\nTOTAL ROUNDS EXPENDED WERE: $Total_shots\n"; + if ($Total_shots > 18) { print "BETTER GO BACK TO FORT SILL FOR REFRESHER TRAINING!\n"; } + else { print "NICE SHOOTING !!\n"; } + last; + } + print "\nTHE FORWARD OBSERVER HAS SIGHTED MORE ENEMY ACTIVITY...\n"; + next GAME; + } + if ($dist > 100) { print "SHORT OF TARGET BY ", abs($dist)," YARDS.\n"; } + else { print "OVER TARGET BY ", abs($dist), " YARDS.\n"; } + + if ($shots >= 5) + { + print "\nBOOM !!!! YOU HAVE JUST BEEN DESTROYED BY THE ENEMY.\n\n\n\n"; + print "BETTER GO BACK TO FORT SILL FOR REFRESHER TRAINING!\n"; + last; + } + } + + print "\nTRY AGAIN (Y OR N): "; + chomp(my $ans=uc(<>)); + if ($ans ne "Y") { last; } + else { $Games = 0; $Total_shots = 0; } +} + +print "\nOK. RETURN TO BASE CAMP.\n"; + +#################################### + +sub get_elevation +{ + my $elevation; + while (1) + { + print "\nELEVATION: "; + chomp($elevation = <>); + if ($elevation > 89) { print "MAXIMUM ELEVATION IS 89 DEGREES.\n"; } + elsif ($elevation < 1) { print "MINIMUM ELEVATION IS ONE DEGREE.\n"; } + else { return $elevation; } + } +} diff --git a/42_Gunner/python/gunner.py b/42_Gunner/python/gunner.py index 06d55d188..6c6d19c72 100644 --- a/42_Gunner/python/gunner.py +++ b/42_Gunner/python/gunner.py @@ -50,21 +50,19 @@ def gunner() -> None: print("\n\nTOTAL ROUNDS EXPENDED WERE: ", S1) if S1 > 18: print("BETTER GO BACK TO FORT SILL FOR REFRESHER TRAINING!") - return else: print("NICE SHOOTING !!") - return + return else: killed_enemies += 1 print( "\nTHE FORWARD OBSERVER HAS SIGHTED MORE ENEMY ACTIVITY..." ) break + elif shot_proximity_int > 100: + print("SHORT OF TARGET BY", abs(shot_proximity_int), "YARDS.") else: - if shot_proximity_int > 100: - print("SHORT OF TARGET BY", abs(shot_proximity_int), "YARDS.") - else: - print("OVER TARGET BY", abs(shot_proximity_int), "YARDS.") + print("OVER TARGET BY", abs(shot_proximity_int), "YARDS.") else: print("\nBOOM !!!! YOU HAVE JUST BEEN DESTROYED BY THE ENEMY.\n\n\n") print("BETTER GO BACK TO FORT SILL FOR REFRESHER TRAINING!") diff --git a/42_Gunner/rust/Cargo.lock b/42_Gunner/rust/Cargo.lock new file mode 100644 index 000000000..1bc05f192 --- /dev/null +++ b/42_Gunner/rust/Cargo.lock @@ -0,0 +1,411 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anyhow" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chacha20" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +dependencies = [ + "cfg-if", + "cpufeatures", + "rand_core", +] + +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "getrandom" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "rand_core", + "wasip2", + "wasip3", +] + +[[package]] +name = "gunner" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", + "serde", + "serde_core", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.182" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8" +dependencies = [ + "chacha20", + "getrandom", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "syn" +version = "2.0.115" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e614ed320ac28113fa64972c4262d5dbc89deacdfd00c34a3e4cea073243c12" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "537dd038a89878be9b64dd4bd1b260315c1bb94f4d784956b81e27a088d9a09e" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/42_Gunner/rust/Cargo.toml b/42_Gunner/rust/Cargo.toml new file mode 100644 index 000000000..cfc8f092c --- /dev/null +++ b/42_Gunner/rust/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "gunner" +version = "0.1.0" +edition = "2024" + +[dependencies] +rand = "0.10.0" diff --git a/42_Gunner/rust/src/main.rs b/42_Gunner/rust/src/main.rs new file mode 100644 index 000000000..1f4547d53 --- /dev/null +++ b/42_Gunner/rust/src/main.rs @@ -0,0 +1,161 @@ +use rand::{RngExt, rngs::ThreadRng}; +use std::{ + convert::Infallible, + io::{self, BufRead, Write}, + str::FromStr, +}; + +fn main() { + println!("{:>30}GUNNER", ""); + println!("{:>15}CREATIVE COMPUTING MORRISTOWN, NEW JERSEY", ""); + println!("\n\n"); + + println!("You are the officer-in-charge, giving orders to a gun"); + println!("crew, telling them the degrees of elevation you estimate"); + println!("will place a projectile on target. A hit wihtin 100 yards"); + println!("of the target will destroy it.\n"); + + let mut game = GunnerGame::new(); + while game.run() == PlayAgain::Yes { + // do nothing -- just loop around and call game.run() again + } +} + +struct GunnerGame { + gun_range: usize, // each game has different range for the gun. "R" in the basic code + targets_destroyed: isize, // the "Z" counter in basic + total_rounds: isize, // how many rounds were fired across all targets. "S1" in basic + rng: ThreadRng, // our PRNG +} + +impl GunnerGame { + fn new() -> Self { + let mut rng = rand::rng(); + let gun_range = rng.random_range(20_000..=60_000); + + Self { + gun_range, + rng, + targets_destroyed: 0, + total_rounds: 0, + } + } + + fn run(&mut self) -> PlayAgain { + println!("Maximum range of your gun is {} yards.\n", self.gun_range); + + for targets in 0..4 { + if targets > 0 { + // After the first round, we want to let the user know that there's + // another target in sight + println!("\nThe forward observer has sighted more enemy activity..."); + } + + // let the player try to destroy it + let target_destroyed = self.handle_target(); + + if !target_destroyed { + println!("\nBoom !!!! You have just been destroyed by the enemy.\n\n\n"); + println!("Better go back to Fort Sill for refresher training!"); + + println!(); + break; + } else { + self.targets_destroyed += 1; + } + } + + // only display success message if all targets were destroyed + if self.targets_destroyed == 4 { + println!("\n\nTotal rounds expended were: {}", self.total_rounds); + if self.total_rounds <= 18 { + println!("Nice shooting !!"); + } + } + + println!("\n"); + get_input("Try again (Y or N)") + } + + fn handle_target(&mut self) -> bool { + let target_distance = + ((self.gun_range as f32) * (0.1 + 0.8 * self.rng.random::())) as isize; + + println!("Distance to the target is {target_distance} yards.\n\n"); + + // The player gets 5 shots. On the 6th shot we still ask for an elevation, + // but then report that they've been destroyed. + for shots_fired in 1..=6 { + let elevation = loop { + let elevation: f32 = get_input("Elevation"); + + // Let the player know how well they did. + if elevation < 1.0 { + println!("Miniumum elevation is one degree."); + } else if elevation > 89.0 { + println!("Maximum elevation is 89 degrees."); + } else { + break elevation; + } + }; + + // Only allow the player to destroy the target with the first 5 shots. The + // sixth is ignored. + if shots_fired < 6 { + let angle = 2.0 * elevation / 57.3; + let intersection = self.gun_range as f32 * angle.sin(); + let delta = target_distance - (intersection as isize); + + if delta.abs() < 100 { + println!( + "*** Target Destroyed *** {shots_fired} rounds of ammunition expended." + ); + self.total_rounds += shots_fired; + return true; + } else if delta > 100 { + println!("Short of target by {delta} yards."); + } else { + println!("Over target by {} yards.", delta.abs()); + } + } + } + + false + } +} + +#[derive(PartialEq, Eq)] +enum PlayAgain { + Yes, + No, +} + +impl FromStr for PlayAgain { + type Err = Infallible; + + fn from_str(line: &str) -> Result { + if line.to_uppercase() == "Y" { + Ok(Self::Yes) + } else { + Ok(Self::No) + } + } +} + +fn get_input>(prompt: S) -> R { + loop { + print!("{}? ", prompt.as_ref()); + let mut stdout = io::stdout().lock(); + let _ = stdout.flush(); + + let mut buffer = String::new(); + let stdin = std::io::stdin(); + let mut handle = stdin.lock(); + let _ = handle.read_line(&mut buffer); + + if let Ok(result) = buffer.trim().to_string().parse::() { + return result; + } + println!("?Re-enter"); + } +} diff --git a/43_Hammurabi/README.md b/43_Hammurabi/README.md index 2e3d6397d..937044c86 100644 --- a/43_Hammurabi/README.md +++ b/43_Hammurabi/README.md @@ -23,8 +23,7 @@ http://www.vintage-basic.net/games.html #### Porting Notes -(please note any difficulties or challenges in porting here) - +- Though the file name and README both spell "Hammurabi" with two M's, the program itself consistently uses only one M. #### External Links - C: https://github.com/beyonddream/hamurabi diff --git a/43_Hammurabi/perl/lib/BasicComputerGames/Hammurabi.pm b/43_Hammurabi/perl/lib/BasicComputerGames/Hammurabi.pm new file mode 100644 index 000000000..cfcffb343 --- /dev/null +++ b/43_Hammurabi/perl/lib/BasicComputerGames/Hammurabi.pm @@ -0,0 +1,472 @@ +#!/usr/bin/env perl +package BasicComputerGames::Hammurabi; + +use v5.24; +use warnings; +use experimental 'signatures'; + +{ + # Quick and dirty accessors + no strict 'refs'; + for my $feature ( + qw< year population store rats_toll had_plague planted + production_per_acre acres new_arrivals starved status max_year fed + percent_starved total_starved cost_per_acre > + ) + { + *{__PACKAGE__ . '::' . $feature} = sub ($self, @new) { + $self->{$feature} = $new[0] if @new; + return $self->{$feature}; + }; + } ## end for my $feature (...) +} + +sub new ($package, %args) { + my $self = bless { + + # These defaults can be overridden by %args + population => 100, + store => 2800, + rats_toll => 200, + production_per_acre => 3, + acres => 1000, + new_arrivals => 5, + fed => 0, + max_year => 10, + + %args, + + # These starting values cannot be overridden by %args + status => 'start', + year => 1, + starved => 0, + total_starved => 0, + percent_starved => 0, + had_plague => 0, + planted => 0, + cost_per_acre => 0, + }, $package; + + return $self; +} ## end sub new + +sub step ($self, $input) { + my $method = $self->can('_handle_' . $self->status); + $self->$method($input); +} + +######################################################################## +# +# All _handle_* methods below represents handlers for different states +# of the game, e.g. state `start` is managed by _handle_start(). Each +# handler receives two input arguments: an instance to the game object and +# the input that was collected by the UI for that particular state (if +# any). + +# start of the game +sub _handle_start ($self, $input) { + $self->status('start_of_year'); +} + +# start of each year +sub _handle_start_of_year ($self, $input) { + $self->cost_per_acre(int(rand(10)) + 17); + $self->status('advertise_cost_per_acre'); +} + +# intermediate state to allow for printing the cost per acre, moves +# directly to following state +sub _handle_advertise_cost_per_acre ($self, $input) { + $self->status('buy_acres'); +} + +# buy acres of land, making sure to be able to cover for the cost +sub _handle_buy_acres ($self, $input) { + return $self->status('bail_out') if $input < 0; + return $self->status('sell_acres') if $input == 0; + my $cpa = $self->cost_per_acre; + my $cost = $cpa * $input; + return $self->status('buy_acres_again') + if $cost > $self->store; + $self->acres($self->acres + $input); + $self->store($self->store - $cost); + return $self->status('feeding'); +} ## end sub _handle_buy_acres + +# intermediate state to allow for notifying that the request for new +# acres of land could not be covered, moves directly to the following +# state +sub _handle_buy_acres_again ($self, $input) { + $self->status('buy_acres'); +} + +# sell acres of land, making sure to sell only what can be sold. +sub _handle_sell_acres ($self, $input) { + return $self->status('bail_out') if $input < 0; + return $self->status('sell_acres_again') if $input >= $self->acres; + $self->acres($self->acres - $input); + $self->store($self->store + $self->cost_per_acre * $input); + return $self->status('feeding'); +} ## end sub _handle_sell_acres + +# intermediate state to allow for notifying that the request to sell +# acres of land could not be covered, moves directly to the following +# state +sub _handle_sell_acres_again ($self, $input) { + $self->status('sell_acres'); +} + +# feed people, making sure we have the necessary resources +sub _handle_feeding ($self, $input) { + return $self->status('bail_out') if $input < 0; + return $self->status('feeding_again') if $input >= $self->store; + $self->store($self->store - $input); + $self->fed($input); + $self->status('planting'); +} ## end sub _handle_feeding + +# intermediate state to allow for notifying that the request to use +# bushels of grain could not be covered, moves directly to the following +# state +sub _handle_feeding_again ($self, $input) { + $self->status('feeding'); +} + +# plant crops, making sure we have the land, the seeds and the workers. +sub _handle_planting ($self, $input) { + return $self->status('bail_out') if $input < 0; + + return $self->status('planting_fail_acres') if $input > $self->acres; + + my $store = $self->store; + return $self->status('planting_fail_seeds') + if $store < int($input / 2); + + return $self->status('planting_fail_people') + if $input >= $self->population * 10; + + $self->planted($input); + $self->store($store - int($input / 2)); + $self->status('simulate_year'); +} ## end sub _handle_planting + +# complain about lack of land to cover the planting request +sub _handle_planting_fail_acres ($self, $input) { + $self->status('planting'); +} + +# complain about lack of seeds to cover the planting request +sub _handle_planting_fail_seeds ($self, $input) { + $self->status('planting'); +} + +# complain about lack of workers to cover the planting request +sub _handle_planting_fail_people ($self, $input) { + $self->status('planting'); +} + +# simulate the rest of the year after all inputs, i.e. rats, crops, etc. +sub _handle_simulate_year ($self, $input) { + my $store = $self->store; + + # rats might take a toll during the year + my $c = 1 + int(rand(5)); + my $rats_toll = $c % 2 ? 0 : int($store / $c); + $self->rats_toll($rats_toll); + + # planting also gains us grain after the harvest + my $ppa = $self->production_per_acre(1 + int(rand(5))); + my $harvest = $ppa * $self->planted; + + # let's update the stored seeds finally + $self->store($store += $harvest - $rats_toll); + + # let's see how population evolved + my $population = $self->population; + + # how many people had full tummies + my $fed_people = int($self->fed / 20); + my $starved = $population - $fed_people; + $starved = 0 if $starved < 0; # cannot create people from seeds + $self->starved($starved); + + # check preliminary exit condition for a very bad year + return $self->status('impeach_year') + if $starved > $population * 0.45; + + # update statistics + $self->total_starved($self->total_starved + $starved); + my $perc = $self->percent_starved; + my $year = $self->year; + $perc = (($year - 1) * $perc + $starved * 100 / $population) / $year; + $self->percent_starved($perc); + + # babies + my $acres = $self->acres; + my $rand = 1 + int(rand(5)); + my $arrivals = $self->new_arrivals( + int(1 + $rand * (20 * $acres + $store) / $population / 100)); + + $population += $arrivals - $starved; + + # HORROS, A 15% CHANCE OF PLAGUE + my $had_plague = $self->had_plague(rand(1) < 0.15); + $population = int($population / 2) if $had_plague; + + # save population for next round + $self->population($population); + + # advance to next year + $self->year(++$year); + if ($year > $self->max_year) { + $self->status('summary'); + } + else { + $self->status('start_of_year'); + } +} ## end sub _handle_simulate_year + +# this is a transition after the impeachment message +sub _handle_impeach_year ($self, $input) { + $self->status('goodbye'); +} + +# this is a transition after printing the summary +sub _handle_summary ($self, $input) { + $self->status('goodbye'); +} + +# this is a transition after printing the final salutation message +sub _handle_goodbye ($self, $input) { + $self->status('game_over'); +} + +# this is a transition after asking the king to hire someone else! +sub _handle_bail_out ($self, $input) { + $self->status('game_over'); +} + +# The following package implements all the User Interface, using the +# game state (as exposed by $game->status) to figure out what to print +# and if an input is needed from the user. It all happens on the +# standard input and output. +package BasicComputerGames::Hammurabi::DefaultIO; + +# All __io_* functions take a $game object as input, in case of need for +# some specific data (e.g. population amount or amassed grain bushels). +# They usually print something out and collect input from standard +# input for states that require a user input. All functions are named +# after the available states in BasicComputerGames::Hammurabi. + +sub __io_start ($game) { + say ' ' x 32, 'HAMURABI'; + say ' ' x 15, 'CREATIVE COMPUTING MORRISTOWN, NEW JERSEY'; + print "\n\n\n"; + say 'TRY YOUR HAND AT GOVERNING ANCIENT SUMERIA'; + say 'FOR A TEN-YEAR TERM OF OFFICE'; + print "\n"; + return; +} ## end sub __io_start + +sub __io_start_of_year ($game) { + print "\n\n"; + say "HAMURABI: I BEG TO REPORT TO YOU,"; + printf "IN YEAR %d , %d PEOPLE STARVED, %d CAME TO THE CITY,\n", + $game->year, $game->starved, $game->new_arrivals; + say 'A HORRIBLE PLAGUE STRUCK! HALF THE PEOPLE DIED.' + if $game->had_plague; + say 'POPULATION IS NOW ', $game->population; + say 'THE CITY NOW OWNS ', $game->acres, ' ACRES.'; + say 'YOU HARVESTED ', $game->production_per_acre, ' BUSHELS PER ACRE.'; + say 'THE RATS ATE ', $game->rats_toll, ' BUSHELS.'; + say 'YOU NOW HAVE ', $game->store, ' BUSHELS IN STORE.'; + print "\n"; + return; +} ## end sub __io_start_of_year + +sub get_input ($game = undef) { + while () { + chomp(my $input = $_); + return 0 unless $input; + return $input if $input =~ m{\A -? \d+ \z}mxs; + print "REENTER?\n?? "; + } ## end while () + die "\n"; +} ## end sub get_input + +sub __io_bail_out ($game) { + say "\nHAMURABI: I CANNOT DO WHAT YOU WISH."; + say 'GET YOURSELF ANOTHER STEWARD!!!!!'; + return; +} + +sub __not_enough_bushels ($game) { + say 'HAMURABI: THINK AGAIN. YOU HAVE ONLY'; + say $game->store, ' BUSHELS OF GRAIN. NOW, THEN,'; +} + +sub __not_enough_acres ($game) { + say 'HAMURABI: THINK AGAIN. YOU OWN ONLY ', + $game->acres, ' ACRES. NOW, THEN,'; +} + +sub __io_buy_acres ($game) { + print 'HOW MANY ACRES DO YOU WISH TO BUY?? '; + return get_input(); +} + +sub __io_advertise_cost_per_acre ($game) { + say 'LAND IS TRADING AT ', $game->cost_per_acre, ' BUSHELS PER ACRE.'; + return; +} + +sub __io_sell_acres ($game) { + print 'HOW MANY ACRES DO YOU WISH TO SELL?? '; + return get_input(); +} + +sub __io_feeding ($game) { + print "\nHOW MANY BUSHELS DO YOU WISH TO FEED YOUR PEOPLE?? "; + return get_input(); +} + +sub __io_planting ($game) { + print "\nHOW MANY ACRES DO YOU WISH TO PLANT WITH SEED?? "; + return get_input(); +} + +sub __io_buy_acres_again ($game) { __not_enough_bushels($game) } + +sub __io_sell_acres_again ($game) { __not_enough_acres($game) } + +sub __io_feeding_again ($game) { __not_enough_bushels($game) } + +sub __io_planting_fail_acres ($game) { __not_enough_acres($game) } + +sub __io_planting_fail_seeds ($game) { __not_enough_bushels($game) } + +sub __io_planting_fail_people ($game) { + say 'BUT YOU HAVE ONLY ', $game->population, + ' PEOPLE TO TEND THE FIELDS! NOW, THEN,'; +} + +sub __impeachment { + say 'DUE TO THIS EXTREME MISMANAGEMENT YOU HAVE NOT ONLY'; + say 'BEEN IMPEACHED AND THROWN OUT OF OFFICE BUT YOU HAVE'; + say 'ALSO BEEN DECLARED NATIONAL FINK!!!!'; +} + +sub __io_impeach_year ($game) { + printf "\nYOU STARVED %d PEOPLE IN ONE YEAR!!!\n", $game->starved; + return __impeachment(); +} + +sub __io_goodbye ($game) { + say "\nSO LONG FOR NOW.\n"; + return; +} + +# Final summary for the game, print statistics and evaluation +sub __io_summary ($game) { + my $starved = $game->total_starved; + my $years = $game->max_years; + my $p1 = 100 * $starved / $years; + my $l = $game->acres / $game->population; + printf "IN YOUR %d-YEAR TERM OF OFFICE, %d PERCENT OF THE\n", + $years, $p1; + say 'POPULATION STARVED PER YEAR ON THE AVERAGE, I.E. A TOTAL OF'; + printf "%d PEOPLE DIED!!\n", $starved; + say 'YOU STARTED WITH 10 ACRES PER PERSON AND ENDED WITH'; + printf "%.2f ACRES PER PERSON.\n\n", $l; + + if ($p1 > 33 || $l < 7) { + __impeachment(); + } + elsif ($p1 > 10 || $l < 9) { + say 'YOUR HEAVY-HANDED PERFORMANCE SMACKS OF NERO AND IVAN IV.'; + say 'THE PEOPLE (REMAINING) FIND YOU AN UNPLEASANT RULER, AND,'; + say 'FRANKLY, HATE YOUR GUTS!!'; + } + elsif ($p1 > 3 || $l < 10) { + my $haters = int($game->population * rand(0.8)); + say 'YOUR PERFORMANCE COULD HAVE BEEN SOMEWHAT BETTER, BUT'; + say "REALLY WASN'T TOO BAD AT ALL. $haters PEOPLE"; + say 'WOULD DEARLY LIKE TO SEE YOU ASSASSINATED BUT WE ALL HAVE OUR'; + say 'TRIVIAL PROBLEMS.'; + } ## end elsif ($p1 > 3 || $l < 10) + else { + say 'A FANTASTIC PERFORMANCE!!! CHARLEMANGE, DISRAELI, AND'; + say 'JEFFERSON COMBINED COULD NOT HAVE DONE BETTER!'; + } + + return; +} ## end sub __io_summary + +# this class method allows using this module... easily. Call with +# arguments to be fed to the BasicComputerGames::Hammurabi constructor. +sub run ($package, @args) { + my $game = BasicComputerGames::Hammurabi->new(@args); + while ((my $status = $game->status) ne 'game_over') { + eval { + my $retval; + if (my $cb = $package->can('__io_' . $status)) { + $retval = $cb->($game); + } + $game->step($retval); + 1; + } or last; + } ## end while ((my $status = $game...)) + say ''; + return 0; +} ## end sub run + +# Modulino (https://gitlab.com/polettix/notechs/-/snippets/1868370) +exit __PACKAGE__->run(@ARGV) unless caller; + +1; +__END__ + +=pod + +=encoding UTF-8 + +=head1 NAME + +BasicComputerGames::Hammurabi - the Hammurabi game from BASIC + +=head1 SYNOPSIS + + use BasicComputerGames::Hammurabi; + + # if you have a way to manage the UI yourself, then you can get the + # game logic handler + my $game_handler = BasicComputerGames::Hammurabi->new; + while ((my $status = $game_handler->status) ne 'game_over') { + # figure out what to print out with $status, this is totally + # up to the interface implementation, which also has to collect + # the inputs + my $retval = manage_ui_for($game_handler); + + # now we feed whatever came from the interface back to the handler + $game_handler->step($retval); + } + + # Want the plain terminal experience? No problem: + BasicComputerGames::Hammurabi::DefaultIO->run; + +=head1 IMPLEMENTATION DETAILS + +The code tries to behave like the original BASIC, including some dubious +conditions checks that e.g. do not allow using the full potential of +available resources for lack of an equal sign. + +The calculation of the final average of starved people per year is +differnet from the original and avoids what is considered (by me) a bug +that kicks in when there are years in which nobody starves. + +=head1 AUTHOR + +Adapted by Flavio Poletti from the BASIC version by David Ahl. Game text +copied verbatim from the original BASIC implementation, including typos. + +=cut diff --git a/43_Hammurabi/python/hamurabi.py b/43_Hammurabi/python/hamurabi.py index c259ec802..46ef975d6 100644 --- a/43_Hammurabi/python/hamurabi.py +++ b/43_Hammurabi/python/hamurabi.py @@ -62,7 +62,7 @@ def main() -> None: while year < 11: # line 270. main loop. while the year is less than 11 print("\n\n\nHAMURABI: I BEG TO REPORT TO YOU") - year = year + 1 # year + year += 1 print( "IN YEAR", year, @@ -97,9 +97,7 @@ def main() -> None: elif bushels_per_acre * plague > grain_stores: # can't afford it bad_input_710(grain_stores) plague = -99 # give'm a second change to get it right - elif ( - bushels_per_acre * plague <= grain_stores - ): # normal case, can afford it + else: acres = acres + plague # increase the number of acres by Q grain_stores = ( grain_stores - bushels_per_acre * plague @@ -169,15 +167,8 @@ def main() -> None: # REM *** A BOUNTIFUL HARVEST! bushels_per_acre = C H = people * bushels_per_acre - eaten_rats = 0 - C = gen_random() - if int(C / 2) == C / 2: # even number. 50/50 chance - # REM *** RATS ARE RUNNING WILD!! - eaten_rats = int( - grain_stores / C - ) # calc losses due to rats, based on previous random number - + eaten_rats = int(grain_stores / C) if int(C / 2) == C / 2 else 0 grain_stores = grain_stores - eaten_rats + H # deduct losses from stores C = gen_random() diff --git a/43_Hammurabi/rust/Cargo.lock b/43_Hammurabi/rust/Cargo.lock new file mode 100644 index 000000000..4fe2abbea --- /dev/null +++ b/43_Hammurabi/rust/Cargo.lock @@ -0,0 +1,75 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rust" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/43_Hammurabi/rust/Cargo.toml b/43_Hammurabi/rust/Cargo.toml new file mode 100644 index 000000000..3b1d02f52 --- /dev/null +++ b/43_Hammurabi/rust/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "rust" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rand = "0.8.5" diff --git a/43_Hammurabi/rust/README.md b/43_Hammurabi/rust/README.md new file mode 100644 index 000000000..fc6468b9f --- /dev/null +++ b/43_Hammurabi/rust/README.md @@ -0,0 +1,3 @@ +Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) + +Conversion to [Rust](https://www.rust-lang.org/) diff --git a/43_Hammurabi/rust/src/main.rs b/43_Hammurabi/rust/src/main.rs new file mode 100644 index 000000000..529a61451 --- /dev/null +++ b/43_Hammurabi/rust/src/main.rs @@ -0,0 +1,262 @@ +use std::io; +use rand::Rng; + +fn run() { + // Set up variables + let mut year = 0; + let mut population = 95; + let mut immigrants = 5; + let mut starved = 0; + let mut total_starved = 0; + let mut plague = false; + let mut grain = 2800; + let mut bushels_fed; + let mut harvest; + let mut planted; + let mut yield_acre = 3; + let mut eaten_rats = 200; + let mut acres = 1000; + let mut land_price; + let mut bought_land; + let mut perc_starved = 0.0; + let mut game_failed = false; + + 'main: loop { + year += 1; + if year > 11 { + break; + } + println!("\n\n\nHAMURABI: I BEG TO REPORT TO YOU,"); + println!("IN YEAR {year}, {starved} PEOPLE STARVED, {immigrants} CAME TO THE CITY,"); + population += immigrants; + if plague{ + population /= 2; + plague = false; + println!("A HORRIBLE PLAGUE STRUCK! HALF THE PEOPLE DIED."); + } + println!("POPULATION IS NOW {population}"); + println!("THE CITY NOW OWNS {acres} ACRES."); + println!("YOU HARVESTED {yield_acre} BUSHELS PER ACRE."); + println!("THE RATS ATE {eaten_rats} BUSHELS."); + println!("YOU NOW HAVE {grain} BUSHELS IN STORE.\n"); + let r = rand::thread_rng().gen_range(1..10); + land_price = r + 17; + println!("LAND IS TRADING AT {land_price} BUSHELS PER ACRE."); + + loop { + println!("HOW MANY ACRES DO YOU WISH TO BUY? "); + if let Some(qty) = get_input() { + // Player decides not to buy any land + if qty == 0 { + bought_land = false; + break; + } + // Trying to buy more land than you can afford? + if land_price * qty > grain { + insufficient_grain(grain); + continue; + } + // Everything checks out OK + if land_price * qty <= grain { + acres += qty ; + grain -= land_price * qty ; + bought_land = true; + break; + } + } else { + impossible_task(); + game_failed = true; + break 'main; + } + } + + if !bought_land { + loop { + println!("HOW MANY ACRES DO YOU WISH TO SELL? "); + if let Some(qty) = get_input() { + // Everything checks out OK + if qty <= acres { + acres -= qty; + grain += land_price * qty; + break; + } + // Trying to sell more land that you own + insufficient_land(acres); + } else { + impossible_task(); + game_failed = true; + break 'main; + } + } + } + + loop { + println!("HOW MANY BUSHELS DO YOU WISH TO FEED YOUR PEOPLE? "); + if let Some(qty) = get_input() { + // Trying to use more grain than is in silos? + if qty > grain { + insufficient_grain(grain); + continue; + } + // Everything checks out OK + bushels_fed = qty; + grain -= bushels_fed; + break; + } else { + impossible_task(); + game_failed = true; + break 'main; + } + } + + loop { + println!("HOW MANY ACRES DO YOU WISH TO PLANT WITH SEED? "); + if let Some(qty) = get_input() { + // Trying to plant more acres than you own? + if qty > acres { + insufficient_land(acres); + continue; + } + // Enough grain for seed? + if qty / 2 > grain { + insufficient_grain(grain); + continue; + } + // Enough people to tend the crops? + if qty > (10 * population) { + insufficient_people(population); + continue; + } + // Everything checks out OK + planted = qty; + grain -= planted / 2; + break; + } else { + impossible_task(); + game_failed = true; + break 'main; + } + } + + // A bountiful harvest! + yield_acre = gen_random(); + harvest = planted * yield_acre; + eaten_rats = 0; + + // Determine if any grain was eaten by rats + let mut c = gen_random(); + if c % 2 == 0 { // If c is even... + // Rats are running wild! + eaten_rats = grain / c; + } + // Update the amount of grain held + grain = grain - eaten_rats + harvest; + + // Let's have some babies + c = gen_random(); + immigrants = c * (20 * acres + grain) / population / 100 + 1; + + // How many people had full tummies? + c = bushels_fed / 20; + // Horrors, a 15% chance of plague + let rf: f32 = rand::thread_rng().gen(); + let plague_chance = (10. * ((2. * rf) - 0.3)) as i32; + if plague_chance == 0 { + plague = true; + } + if population >= c { + // Starve enough for impeachment? + starved = population - c; + if starved > (0.45 * population as f32) as u32 { + println!("YOU STARVED {starved} PEOPLE IN ONE YEAR!!!"); + national_fink(); + game_failed = true; + break; + } + // Calculate percentage of people that starved per year on average + perc_starved = ((year - 1) as f32 * perc_starved + starved as f32 * 100. / population as f32) / year as f32; + population = c; + total_starved += starved; + } + } + + if !game_failed { + println!("IN YOUR 10-YEAR TERM OF OFFICE {perc_starved} PERCENT OF THE"); + println!("POPULATION STARVED PER YEAR ON THE AVERAGE, I.E. A TOTAL OF"); + println!("{total_starved} PEOPLE DIED!!"); + let acres_head = acres / population; + println!("YOU STARTED WITH 10 ACRES PER PERSON AND ENDED WITH"); + println!("{acres_head} ACRES PER PERSON.\n"); + if perc_starved > 33. || acres_head < 7 { + national_fink(); + } + else if perc_starved > 10. || acres_head < 9 { + println!("YOUR HEAVY-HANDED PERFORMANCE SMACKS OF NERO AND IVAN IV."); + println!("THE PEOPLE (REMAINING) FIND YOU AND UNPLEASANT RULER, AND,"); + println!("FRANKLY, HATE YOUR GUTS!!"); + } + else if perc_starved > 3. || acres_head < 10 { + let haters = (population as f32 * 0.8 * gen_random() as f32) as u32; + println!("YOUR PERFORMANCE COULD HAVE BEEN SOMEWHAT BETTER, BUT"); + println!("REALLY WASN'T TOO BAD AT ALL. {haters} PEOPLE"); + println!("WOULD DEARLY LIKE TO SEE YOU ASSASSINATED BUT WE ALL HAVE OUR"); + println!("TRIVIAL PROBLEMS."); + } else { + println!("A FANTASTIC PERFORMANCE!!! CHARLEMANGE, DISRAELI, AND"); + println!("JEFFERSON COMBINED COULD NOT HAVE DONE BETTER!\n"); + } + for _ in 1..10 { + println!(); + } + } + + println!("\nSO LONG FOR NOW.\n"); +} + +fn get_input() -> Option { + let mut input = String::new(); + io::stdin().read_line(&mut input).expect("Failed read_line"); + match input.trim().parse() { + Ok(num) => Some(num), + Err(_) => None, + } +} + +fn gen_random() -> u32 { + let r: f32 = rand::thread_rng().gen(); + (r * 5.0 + 1.0) as u32 +} + +fn impossible_task() { + println!("HAMURABI: I CANNOT DO WHAT YOU WISH."); + println!("GET YOURSELF ANOTHER STEWARD!!!!!"); +} + +fn insufficient_grain(grain: u32) { + println!("HAMURABI: THINK AGAIN. YOU HAVE ONLY"); + println!("{grain} BUSHELS OF GRAIN. NOW THEN,"); +} + +fn insufficient_land(acres: u32) { + println!("HAMURABI: THINK AGAIN. YOU OWN ONLY {acres} ACRES. NOW THEN,"); +} + +fn insufficient_people(population: u32) { + println!("BUT YOU HAVE ONLY {population} PEOPLE TO TEND THE FIELDS! NOW THEN,"); +} + +fn national_fink() { + println!("DUE TO THIS EXTREME MISMANAGEMENT YOU HAVE NOT ONLY"); + println!("BEEN IMPEACHED AND THROWN OUT OF OFFICE BUT YOU HAVE"); + println!("ALSO BEEN DECLARED NATIONAL FINK!!!!"); +} + +fn main() { + println!(" HAMURABI"); + println!("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); + print!("\n\n\n\n"); + println!("TRY YOUR HAND AT GOVERNING ANCIENT SUMERIA"); + println!("FOR A TEN-YEAR TERM OF OFFICE.\n"); + + run(); +} diff --git a/44_Hangman/python/hangman.py b/44_Hangman/python/hangman.py index 3e84ce8d6..59d443dbc 100755 --- a/44_Hangman/python/hangman.py +++ b/44_Hangman/python/hangman.py @@ -16,9 +16,7 @@ class Canvas: def __init__(self, width: int = 12, height: int = 12, fill: str = " ") -> None: self._buffer = [] for _ in range(height): - line = [] - for _ in range(width): - line.append("") + line = ["" for _ in range(width)] self._buffer.append(line) self.clear() @@ -29,11 +27,7 @@ def clear(self, fill: str = " ") -> None: row[x] = fill def render(self) -> str: - lines = [] - for line in self._buffer: - # Joining by the empty string ("") smooshes all of the - # individual characters together as one line. - lines.append("".join(line)) + lines = ["".join(line) for line in self._buffer] return "\n".join(lines) def put(self, s: str, x: int, y: int) -> None: @@ -192,7 +186,7 @@ def play_game(guess_target: str) -> None: print("".join(guess_progress) + "\n") guess_letter = "" guess_word = "" - while guess_letter == "": + while not guess_letter: guess_letter = input("What is your guess? ").upper()[0] if not guess_letter.isalpha(): @@ -215,7 +209,7 @@ def play_game(guess_target: str) -> None: break else: print("\n" + "".join(guess_progress) + "\n") - while guess_word == "": + while not guess_word: guess_word = input("What is your guess for the word? ").upper() if not guess_word.isalpha(): guess_word = "" @@ -234,7 +228,7 @@ def play_game(guess_target: str) -> None: print("Sorry, that letter isn't in the word.") if wrong_guesses == 10: - print("Sorry, you lose. The word was " + guess_target) + print(f"Sorry, you lose. The word was {guess_target}") break diff --git a/45_Hello/README.md b/45_Hello/README.md index 87c47f65d..243efa700 100644 --- a/45_Hello/README.md +++ b/45_Hello/README.md @@ -2,7 +2,7 @@ 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. +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. diff --git a/45_Hello/python/hello.py b/45_Hello/python/hello.py index ccf0501c1..661ea60a1 100644 --- a/45_Hello/python/hello.py +++ b/45_Hello/python/hello.py @@ -47,8 +47,7 @@ def prompt_for_problems(user_name: str) -> str: print("THOSE DEALING WITH GREECE. WHAT KIND OF PROBLEMS DO") print("YOU HAVE? (ANSWER SEX, HEALTH, MONEY, OR JOB)") - problem_type = input().upper() - return problem_type + return input().upper() def prompt_too_much_or_too_little() -> Tuple[bool, Optional[bool]]: @@ -122,11 +121,10 @@ def ask_question_loop(user_name: str) -> None: valid, value, msg = get_yes_or_no() if valid: - if value: - print("WHAT KIND (SEX, MONEY, HEALTH, JOB)") - break - else: + if not value: return + print("WHAT KIND (SEX, MONEY, HEALTH, JOB)") + break print(f"JUST A SIMPLE 'YES' OR 'NO' PLEASE, {user_name}.") @@ -184,10 +182,7 @@ def main() -> None: ask_for_fee(user_name) - if False: - happy_goodbye(user_name) - else: - unhappy_goodbye(user_name) + unhappy_goodbye(user_name) if __name__ == "__main__": diff --git a/45_Hello/rust/Cargo.toml b/45_Hello/rust/Cargo.toml new file mode 100644 index 000000000..7d75412a6 --- /dev/null +++ b/45_Hello/rust/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "rust" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/45_Hello/rust/src/main.rs b/45_Hello/rust/src/main.rs new file mode 100644 index 000000000..310c5db32 --- /dev/null +++ b/45_Hello/rust/src/main.rs @@ -0,0 +1,247 @@ +/** HELLO GAME BY DAVID AHL + * https://github.com/coding-horror/basic-computer-games/blob/main/45_Hello/hello.bas + * Direct conversion from BASIC to Rust by Pablo Marques (marquesrs). + * No additional features or improvements were added. As a faithful translation, + * many of the code here are done in an unrecommended way by today's standards. + * 17/02/25 +*/ + +use std::io::Write; + +fn main() { + // 2 PRINT TAB(33);"HELLO" + // 4 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" + // 6 PRINT: PRINT: PRINT + print!( + "{}{}\n{}{}\n\n\n\n", + " ".repeat(33), + "HELLO", + " ".repeat(15), + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" + ); + + let mut input = String::new(); + + //10 PRINT "HELLO. MY NAME IS CREATIVE COMPUTER." + //20 PRINT: PRINT: INPUT "WHAT'S YOUR NAME";N$: PRINT + print!("HELLO. MY NAME IS CREATIVE COMPUTER.\n\nWHAT'S YOUR NAME? "); + let _ = std::io::stdout().flush().unwrap(); + input.clear(); + std::io::stdin().read_line(&mut input).unwrap(); + let n = input.trim().to_uppercase(); + + //30 PRINT "HI THERE, ";N$;", ARE YOU ENJOYING YOURSELF HERE"; + //40 INPUT B$: PRINT + print!("\nHI THERE, {n}, ARE YOU ENJOYING YOURSELF HERE? "); + loop { + let _ = std::io::stdout().flush().unwrap(); + input.clear(); + std::io::stdin().read_line(&mut input).unwrap(); + let b = input.trim().to_uppercase(); + + //50 IF B$="YES" THEN 70 + if b == "YES" { + //70 PRINT "I'M GLAD TO HEAR THAT, ";N$;".": PRINT + //75 GOTO 100 + println!("\nI'M GLAD TO HEAR THAT, {n}."); + break; + } + //55 IF B$="NO" THEN 80 + else if b == "NO" { + //80 PRINT "OH, I'M SORRY TO HEAR THAT, ";N$;". MAYBE WE CAN" + //85 PRINT "BRIGHTEN UP YOUR VISIT A BIT." + println!("\nOH, I'M SORRY TO HEAR THAT, {n}. MAYBE WE CAN\n{}", + "BRIGHTEN UP YOUR VISIT A BIT." + ); + break; + } + else { + //60 PRINT N$;", I DON'T UNDERSTAND YOUR ANSWER OF '";B$;"'." + //65 PRINT "PLEASE ANSWER 'YES' OR 'NO'. DO YOU LIKE IT HERE";: GOTO 40 + print!("\n{n}, I DON'T UNDERSTAND YOUR ANSWER OF '{b}'.\n{}", + "PLEASE ANSWER 'YES' OR 'NO'. DO YOU LIKE IT HERE? " + ); + } + } + + //100 PRINT + //105 PRINT "SAY, ";N$;", I CAN SOLVE ALL KINDS OF PROBLEMS EXCEPT" + //110 PRINT "THOSE DEALING WITH GREECE. WHAT KIND OF PROBLEMS DO" + //120 PRINT "YOU HAVE (ANSWER SEX, HEALTH, MONEY, OR JOB)"; + //125 INPUT C$ + //126 PRINT + print!("\nSAY, {n}, I CAN SOLVE ALL KINDS OF PROBLEMS EXCEPT\n{}\n{}", + "THOSE DEALING WITH GREECE. WHAT KIND OF PROBLEMS DO", + "YOU HAVE (ANSWER SEX, HEALTH, MONEY, OR JOB)? " + ); + 'outer: loop { + let _ = std::io::stdout().flush().unwrap(); + input.clear(); + std::io::stdin().read_line(&mut input).unwrap(); + let c = input.trim().to_uppercase(); + + //130 IF C$="SEX" THEN 200 + if c == "SEX" { + loop { + //200 INPUT "IS YOUR PROBLEM TOO MUCH OR TOO LITTLE";D$: PRINT + print!("\nIS YOUR PROBLEM TOO MUCH OR TOO LITTLE? "); + let _ = std::io::stdout().flush().unwrap(); + input.clear(); + std::io::stdin().read_line(&mut input).unwrap(); + let d = input.trim().to_uppercase(); + + //210 IF D$="TOO MUCH" THEN 220 + if d == "TOO MUCH" { + //220 PRINT "YOU CALL THAT A PROBLEM?!! I SHOULD HAVE SUCH PROBLEMS!" + //225 PRINT "IF IT BOTHERS YOU, ";N$;", TAKE A COLD SHOWER." + //228 GOTO 250 + println!("\nYOU CALL THAT A PROBLEM?!! I SHOULD HAVE SUCH PROBLEMS!\n{}", + format!("IF IT BOTHERS YOU, {n}, TAKE A COLD SHOWER.") + ); + break; + } + //212 IF D$="TOO LITTLE" THEN 230 + else if d == "TOO LITTLE" { + //230 PRINT "WHY ARE YOU HERE IN SUFFERN, ";N$;"? YOU SHOULD BE" + //235 PRINT "IN TOKYO OR NEW YORK OR AMSTERDAM OR SOMEPLACE WITH SOME" + //240 PRINT "REAL ACTION." + //250 PRINT + println!("\nWHY ARE YOU HERE IN SUFFERN, {n}? YOU SHOULD BE\n{}\n{}", + "IN TOKYO OR NEW YORK OR AMSTERDAM OR SOMEPLACE WITH", + "SOME REAL ACTION." + ); + break; + } + else { + //215 PRINT "DON'T GET ALL SHOOK, ";N$;", JUST ANSWER THE QUESTION" + //217 INPUT "WITH 'TOO MUCH' OR 'TOO LITTLE'. WHICH IS IT";D$:GOTO 210 + println!("\nDON'T GET ALL SHOOK, {n}, JUST ANSWER THE QUESTION\n{}", + "WITH 'TOO MUCH' OR 'TOO LITTLE'. WHICH IS IT? " + ); + } + } + } + //132 IF C$="HEALTH" THEN 180 + else if c == "HEALTH" { + //180 PRINT "MY ADVICE TO YOU ";N$;" IS:" + //185 PRINT " 1. TAKE TWO ASPRIN" + //188 PRINT " 2. DRINK PLENTY OF FLUIDS (ORANGE JUICE, NOT BEER!)" + //190 PRINT " 3. GO TO BED (ALONE)" + //195 GOTO 250 + println!("\nMY ADVICE TO YOU {n} IS:\n{}\n{}\n{}", + " 1. TAKE TWO ASPRIN", + " 2. DRINK PLENTY OF FLUIDS (ORANGE JUICE, NOT BEER!)", + " 3. GO TO BED (ALONE)" + ); + } + //134 IF C$="MONEY" THEN 160 + else if c == "MONEY" { + //160 PRINT "SORRY, ";N$;", I'M BROKE TOO. WHY DON'T YOU SELL" + //162 PRINT "ENCYCLOPEADIAS OR MARRY SOMEONE RICH OR STOP EATING" + //164 PRINT "SO YOU WON'T NEED SO MUCH MONEY?" + //170 GOTO 250 + println!("\nSORRY, {n}, I'M BROKE TOO. WHY DON'T YOU SELL\n{}\n{}", + "ENCYCLOPEADIAS OR MARRY SOMEONE RICH OR STOP EATING", + "SO YOU WON'T NEED SO MUCH MONEY? " + ); + } + //136 IF C$="JOB" THEN 145 + else if c == "JOB" { + //145 PRINT "I CAN SYMPATHIZE WITH YOU ";N$;". I HAVE TO WORK" + //148 PRINT "VERY LONG HOURS FOR NO PAY -- AND SOME OF MY BOSSES" + //150 PRINT "REALLY BEAT ON MY KEYBOARD. MY ADVICE TO YOU, ";N$;"," + //153 PRINT "IS TO OPEN A RETAIL COMPUTER STORE. IT'S GREAT FUN." + //155 GOTO 250 + println!("\nI CAN SYMPATHIZE WITH YOU {n}. I HAVE TO WORK\n{}\n{}\n{}", + "VERY LONG HOURS FOR NO PAY -- AND SOME OF MY BOSSES", + format!("REALLY BEAT ON MY KEYBOARD. MY ADVICE TO YOU, {n}"), + "IS TO OPEN A RETAIL COMPUTER STORE. IT'S GREAT FUN." + ); + } + else { + //138 PRINT "OH, ";N$;", YOUR ANSWER OF ";C$;" IS GREEK TO ME." + //140 GOTO 250 + println!("\nOH, {n}, YOUR ANSWER OF {c} IS GREEK TO ME."); + } + + loop { + //255 PRINT "ANY MORE PROBLEMS YOU WANT SOLVED, ";N$; + //260 INPUT E$: PRINT + print!("\nANY MORE PROBLEMS YOU WANT SOLVED, {n}? "); + let _ = std::io::stdout().flush().unwrap(); + input.clear(); + std::io::stdin().read_line(&mut input).unwrap(); + let e = input.trim().to_uppercase(); + + //270 IF E$="YES" THEN 280 + if e == "YES" { + //280 PRINT "WHAT KIND (SEX, MONEY, HEALTH, JOB)"; + //282 GOTO 125 + print!("\nWHAT KIND (SEX, MONEY, HEALTH, JOB)? "); + continue 'outer; + } + //273 IF E$="NO" THEN 300 + else if e == "NO" { + break 'outer; + } + else { + //275 PRINT "JUST A SIMPLE 'YES' OR 'NO' PLEASE, ";N$;"." + //277 GOTO 255 + println!("\nJUST A SIMPLE 'YES' OR 'NO' PLEASE, {n}."); + } + } + } + //300 PRINT + //302 PRINT "THAT WILL BE $5.00 FOR THE ADVICE, ";N$;"." + //305 PRINT "PLEASE LEAVE THE MONEY ON THE TERMINAL." + println!("\nTHAT WILL BE $5.00 FOR THE ADVICE, {n}.\n{}", + "PLEASE LEAVE THE MONEY ON THE TERMINAL." + ); + //307 FOR I=1 TO 2000: NEXT I + //310 PRINT: PRINT: PRINT + loop { + //315 PRINT "DID YOU LEAVE THE MONEY"; + //320 INPUT G$: PRINT + print!("\nDID YOU LEAVE THE MONEY? "); + let _ = std::io::stdout().flush().unwrap(); + input.clear(); + std::io::stdin().read_line(&mut input).unwrap(); + let g = input.trim().to_uppercase(); + + //325 IF G$="YES" THEN 350 + if g == "YES" { + //350 PRINT "HEY, ";N$;"??? YOU LEFT NO MONEY AT ALL!" + //355 PRINT "YOU ARE CHEATING ME OUT OF MY HARD-EARNED LIVING." + //360 PRINT:PRINT "WHAT A RIP OFF, ";N$;"!!!":PRINT + //365 GOTO 385 + println!("\nHEY, {n}??? YOU LEFT NO MONEY AT ALL!\n{}\n{}", + "YOU ARE CHEATING ME OUT OF MY HARD-EARNED LIVING.", + format!("WHAT A RIP OFF, {n}!!!\n\nTAKE A WALK, {n}") + ); + break; + } + //330 IF G$="NO" THEN 370 + else if g == "NO" { + //370 PRINT "THAT'S HONEST, ";N$;", BUT HOW DO YOU EXPECT" + //375 PRINT "ME TO GO ON WITH MY PSYCHOLOGY STUDIES IF MY PATIENTS" + //380 PRINT "DON'T PAY THEIR BILLS?" + //385 PRINT:PRINT "TAKE A WALK, ";N$;".":PRINT:PRINT:GOTO 999 + println!("\nTHAT'S HONEST, {n}, BUT HOW DO YOU EXPECT\n{}\n{}", + "ME TO GO ON WITH MY PSYCHOLOGY STUDIES IF MY PATIENTS", + format!("DON'T PAY THEIR BILLS?\n\nTAKE A WALK, {n}") + ); + break; + } + else { + //335 PRINT "YOUR ANSWER OF '";G$;"' CONFUSES ME, ";N$;"." + //340 PRINT "PLEASE RESPOND WITH 'YES' OR 'NO'.": GOTO 315 + println!("YOUR ANSWER OF '{g}' CONFUSES ME, {n}.\n{}", + "PLEASE RESPOND WITH 'YES' OR 'NO'." + ); + } + } + + //390 PRINT "NICE MEETING YOU, ";N$;", HAVE A NICE DAY." -> unreachable + //400 REM + //999 END +} diff --git a/46_Hexapawn/README.md b/46_Hexapawn/README.md index bbcf0ad67..6376c0367 100644 --- a/46_Hexapawn/README.md +++ b/46_Hexapawn/README.md @@ -15,6 +15,10 @@ As published in Basic Computer Games (1978): Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html +#### Known Bugs + +- There are valid board positions that will cause the program to print "ILLEGAL BOARD PATTERN" and break. For example: human 8,5; computer 1,5; human 9,5; computer 3,5; human 7,5. This is a valid game-over pattern, but it is not detected as such because of incorrect logic in lines 240-320 (intended to detect whether the computer has any legal moves). + #### Porting Notes (please note any difficulties or challenges in porting here) diff --git a/46_Hexapawn/python/hexapawn.py b/46_Hexapawn/python/hexapawn.py index 9a2801c35..f5cac438b 100644 --- a/46_Hexapawn/python/hexapawn.py +++ b/46_Hexapawn/python/hexapawn.py @@ -278,14 +278,7 @@ def is_legal_human_move(board: List[int], m1: int, m2: int) -> bool: # Destination is not open return False - if m2 - m1 < -4: - # too far - return False - - if m1 == 7 and m2 == 3: - # can't jump corner to corner (wrapping around the board) - return False - return True + return False if m2 - m1 < -4 else m1 != 7 or m2 != 3 def player_piece_on_back_row(board: List[int]) -> bool: @@ -297,11 +290,11 @@ def computer_piece_on_front_row(board: List[int]) -> bool: def all_human_pieces_captured(board: List[int]) -> bool: - return len(list(get_human_spaces(board))) == 0 + return not list(get_human_spaces(board)) def all_computer_pieces_captured(board: List[int]) -> bool: - return len(list(get_computer_spaces(board))) == 0 + return not list(get_computer_spaces(board)) def human_win(last_computer_move: ComputerMove) -> None: @@ -312,11 +305,7 @@ def human_win(last_computer_move: ComputerMove) -> None: def computer_win(has_moves: bool) -> None: - if not has_moves: - msg = "YOU CAN'T MOVE, SO " - else: - msg = "" - msg += "I WIN" + msg = ("YOU CAN'T MOVE, SO " if not has_moves else "") + "I WIN" print(msg) global wins wins += 1 @@ -340,14 +329,14 @@ def human_has_move(board: List[int]) -> bool: else: continue elif i < 7: - assert (i == 4) or (i == 6) + assert i in [4, 6] if board_contents(board, 2) == COMPUTER_PIECE: # can capture computer piece at 2 return True else: continue elif board_contents(board, 5) == COMPUTER_PIECE: - assert (i == 7) or (i == 9) + assert i in [7, 9] # can capture computer piece at 5 return True else: @@ -391,20 +380,16 @@ def has_computer_move(board: List[int]) -> bool: board_contents(board, i + 4) == HUMAN_PIECE ): return True - else: - if i > 3: - # beyond the first row - if board_contents(board, 8) == HUMAN_PIECE: - # can capture on 8 - return True - else: - continue - else: - if board_contents(board, 5) == HUMAN_PIECE: - # can capture on 5 - return True - else: - continue + elif ( + i > 3 + and board_contents(board, 8) == HUMAN_PIECE + or i <= 3 + and board_contents(board, 5) == HUMAN_PIECE + ): + # can capture on 8 + return True + elif i <= 3 or board_contents(board, 8) == HUMAN_PIECE: + continue return False diff --git a/47_Hi-Lo/rust/Cargo.lock b/47_Hi-Lo/rust/Cargo.lock new file mode 100644 index 000000000..937c07c63 --- /dev/null +++ b/47_Hi-Lo/rust/Cargo.lock @@ -0,0 +1,75 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "guessing_game" +version = "0.1.0" +dependencies = [ + "rand", +] + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" diff --git a/48_High_IQ/python/High_IQ.py b/48_High_IQ/python/High_IQ.py index 6dcceaf5e..5b8c2204f 100644 --- a/48_High_IQ/python/High_IQ.py +++ b/48_High_IQ/python/High_IQ.py @@ -121,13 +121,8 @@ def play_game() -> None: while not move(board): print("ILLEGAL MOVE! TRY AGAIN") - # Check peg count and print the user's score - peg_count = 0 - for key in board.keys(): - if board[key] == "!": - peg_count += 1 - - print("YOU HAD " + str(peg_count) + " PEGS REMAINING") + peg_count = sum(1 for key in board.keys() if board[key] == "!") + print(f"YOU HAD {str(peg_count)} PEGS REMAINING") if peg_count == 1: print("BRAVO! YOU MADE A PERFECT SCORE!") @@ -158,11 +153,7 @@ def move(board: Dict[int, str]) -> bool: difference = abs(start - end) center = int((end + start) / 2) - if ( - (difference == 2 or difference == 18) - and board[end] == "O" - and board[center] == "!" - ): + if difference in [2, 18] and board[center] == "!": board[start] = "O" board[center] = "O" board[end] = "!" @@ -187,9 +178,10 @@ def is_game_finished(board) -> bool: next_to_peg = ((pos + space) in board) and board[pos + space] == "!" # Checks both going forward (+ location) or backwards (-location) has_movable_space = ( - not ((pos - space) in board and board[pos - space] == "!") - ) or ( - not ((pos + space * 2) in board and board[pos + space * 2] == "!") + pos - space not in board + or board[pos - space] != "!" + or pos + space * 2 not in board + or board[pos + space * 2] != "!" ) if next_to_peg and has_movable_space: return False diff --git a/49_Hockey/README.md b/49_Hockey/README.md index 3decfe1b1..98db22d58 100644 --- a/49_Hockey/README.md +++ b/49_Hockey/README.md @@ -15,6 +15,11 @@ As published in Basic Computer Games (1978): Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html +#### Known Bugs + +- An apparent missing line 430 causes the code to fall through from the "FLIPS A WRISTSHOT" case directly to the "BACKHANDS ONE" case. +- The author consistently misspells the verb "lets" (writing it like the contraction "let's"), while having no trouble with "leads", "gets", "hits", etc. + #### Porting Notes (please note any difficulties or challenges in porting here) diff --git a/49_Hockey/python/hockey.py b/49_Hockey/python/hockey.py index f18766194..7b1e1ecf6 100644 --- a/49_Hockey/python/hockey.py +++ b/49_Hockey/python/hockey.py @@ -127,8 +127,9 @@ def print_header() -> None: def instructions() -> None: - wants_it = ask_binary("WOULD YOU LIKE THE INSTRUCTIONS? ", "ANSWER YES OR NO!!") - if wants_it: + if ask_binary( + "WOULD YOU LIKE THE INSTRUCTIONS? ", "ANSWER YES OR NO!!" + ): print() print("THIS IS A SIMULATED HOCKEY GAME.") print("QUESTION RESPONSE") @@ -228,9 +229,7 @@ def team2_action( z1 = 3 elif pass_value == 2: print("IT'S A ' 3 ON 2 '!\n") - print( - "ONLY " + team_a.players[3] + " AND " + team_a.players[4] + " ARE BACK.\n" - ) + print(f"ONLY {team_a.players[3]} AND {team_a.players[4]} ARE BACK.\n") print( team_b.players[player_index[j - 2]] + " GIVES OFF TO " @@ -290,7 +289,7 @@ def final_message(team_a: Team, team_b: Team, player_index: List[int]) -> None: print("\n") print("SHOTS ON NET\n") print(f"{team_a.name}: {team_a.shots_on_net}\n") - print(team_b.name + f": {team_b.shots_on_net}\n") + print(f"{team_b.name}: {team_b.shots_on_net}\n") def main() -> None: @@ -374,11 +373,8 @@ def handle_hit( print(f"{team_a.name}: {team_a.score}\t{team_b.name}: {team_b.score}\n") else: print(f"{team_b.name}: {team_b.score}\t{team_a.name}: {team_a.score}\n") - if controlling_team == 1: - team = team_a - else: - team = team_b - print("GOAL SCORED BY: " + team.players[goal_player] + "\n") + team = team_a if controlling_team == 1 else team_b + print(f"GOAL SCORED BY: {team.players[goal_player]}\n") if goal_assistant1 != 0: if goal_assistant2 != 0: print( @@ -406,16 +402,16 @@ def handle_miss( saving_player = randint(1, 7) if controlling_team == 1: if saving_player == 1: - print("KICK SAVE AND A BEAUTY BY " + team_b.players[5] + "\n") - print("CLEARED OUT BY " + team_b.players[3] + "\n") + print(f"KICK SAVE AND A BEAUTY BY {team_b.players[5]}\n") + print(f"CLEARED OUT BY {team_b.players[3]}\n") remaining_time -= 1 return ("continue", remaining_time) if saving_player == 2: - print("WHAT A SPECTACULAR GLOVE SAVE BY " + team_b.players[5] + "\n") - print("AND " + team_b.players[5] + " GOLFS IT INTO THE CROWD\n") + print(f"WHAT A SPECTACULAR GLOVE SAVE BY {team_b.players[5]}\n") + print(f"AND {team_b.players[5]} GOLFS IT INTO THE CROWD\n") return ("break", remaining_time) if saving_player == 3: - print("SKATE SAVE ON A LOW STEAMER BY " + team_b.players[5] + "\n") + print(f"SKATE SAVE ON A LOW STEAMER BY {team_b.players[5]}\n") remaining_time -= 1 return ("continue", remaining_time) if saving_player == 4: @@ -451,22 +447,20 @@ def handle_miss( print("ON THE LOOSE PUCK!\n") return ("break", remaining_time) if saving_player == 3: - print("SKATE SAVE BY " + team_a.players[5] + "\n") + print(f"SKATE SAVE BY {team_a.players[5]}\n") print(team_a.players[5] + " WHACKS THE LOOSE PUCK INTO THE STANDS\n") return ("break", remaining_time) if saving_player == 4: - print( - "STICK SAVE BY " + team_a.players[5] + " AND HE CLEARS IT OUT HIMSELF\n" - ) + print(f"STICK SAVE BY {team_a.players[5]} AND HE CLEARS IT OUT HIMSELF\n") remaining_time -= 1 return ("continue", remaining_time) if saving_player == 5: - print("KICKED OUT BY " + team_a.players[5] + "\n") + print(f"KICKED OUT BY {team_a.players[5]}\n") print("AND IT REBOUNDS ALL THE WAY TO CENTER ICE\n") remaining_time -= 1 return ("continue", remaining_time) if saving_player == 6: - print("GLOVE SAVE " + team_a.players[5] + " AND HE HANGS ON\n") + print(f"GLOVE SAVE {team_a.players[5]} AND HE HANGS ON\n") return ("break", remaining_time) return ("continue", remaining_time) @@ -488,8 +482,9 @@ def simulate_game_round( j = 0 for j in range(1, (pass_value + 2) + 1): player_index[j] = randint(1, 5) - if player_index[j - 1] == player_index[j - 2] or ( - pass_value + 2 >= 3 + if ( + player_index[j - 1] == player_index[j - 2] + or pass_value >= 1 and ( player_index[j - 1] == player_index[j - 3] or player_index[j - 2] == player_index[j - 3] @@ -511,7 +506,7 @@ def simulate_game_round( ) while True: shot_type = int(input("SHOT? ")) - if not (shot_type < 1 or shot_type > 4): + if shot_type >= 1 and shot_type <= 4: break if controlling_team == 1: print(team_a.players[goal_player], end="") @@ -521,21 +516,21 @@ def simulate_game_round( print(" LET'S A BIG SLAP SHOT GO!!\n") z = 4 z += z1 - if shot_type == 2: + elif shot_type == 2: print(" RIPS A WRIST SHOT OFF\n") z = 2 z += z1 - if shot_type == 3: + elif shot_type == 3: print(" GETS A BACKHAND OFF\n") z = 3 z += z1 - if shot_type == 4: + elif shot_type == 4: print(" SNAPS OFF A SNAP SHOT\n") z = 2 z += z1 while True: goal_area = int(input("AREA? ")) - if not (goal_area < 1 or goal_area > 4): + if goal_area >= 1 and goal_area <= 4: break if controlling_team == 1: team_a.shots_on_net += 1 diff --git a/50_Horserace/java/Horserace.java b/50_Horserace/java/Horserace.java new file mode 100644 index 000000000..9a5a7651b --- /dev/null +++ b/50_Horserace/java/Horserace.java @@ -0,0 +1,313 @@ +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Scanner; + +/** + * HORSERACE + *