Repository: coding-horror/basic-computer-games Branch: main Commit: 5301155192d9 Files: 3600 Total size: 6.7 MB Directory structure: gitextract_eq2gu5pa/ ├── .coveragerc ├── .github/ │ └── workflows/ │ ├── check-python.yml │ ├── file-size.yml │ └── rust.yml ├── .gitignore ├── .nojekyll ├── .pre-commit-config.yaml ├── 00_Alternate_Languages/ │ ├── 01_Acey_Ducey/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── aceyducey.ms │ │ ├── README.md │ │ ├── aceyducey.bas │ │ ├── c++/ │ │ │ ├── README.md │ │ │ ├── build/ │ │ │ │ ├── x64/ │ │ │ │ │ ├── Debug/ │ │ │ │ │ │ └── aceyducey.pdb │ │ │ │ │ └── Release/ │ │ │ │ │ └── aceyducey.pdb │ │ │ │ └── x86/ │ │ │ │ ├── Debug/ │ │ │ │ │ └── aceyducey.pdb │ │ │ │ └── Release/ │ │ │ │ └── aceyducey.pdb │ │ │ └── source/ │ │ │ ├── Aceyducey.cpp │ │ │ └── Aceyducey.h │ │ ├── d/ │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── aceyducey.d │ │ │ └── aceyducey_literal.d │ │ ├── elm/ │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── docs/ │ │ │ │ ├── app.js │ │ │ │ └── index.html │ │ │ ├── elm.json │ │ │ ├── package.json │ │ │ ├── resources/ │ │ │ │ └── index.html │ │ │ └── src/ │ │ │ └── Main.elm │ │ ├── go/ │ │ │ └── main.go │ │ ├── nim/ │ │ │ └── aceyducey.nim │ │ └── pascal/ │ │ ├── .gitattributes │ │ ├── .gitignore │ │ ├── README.md │ │ ├── object-pascal/ │ │ │ ├── aceyducey.lpi │ │ │ ├── aceyducey.pas │ │ │ ├── deck.pas │ │ │ └── game.pas │ │ └── simple/ │ │ ├── aceyducey.lpi │ │ └── aceyducey.pas │ ├── 02_Amazing/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── amazing.ms │ │ ├── README.md │ │ ├── amazing.bas │ │ ├── go/ │ │ │ └── main.go │ │ └── pascal/ │ │ ├── .gitattributes │ │ ├── .gitignore │ │ ├── README.md │ │ ├── object-pascal/ │ │ │ ├── amazing.lpi │ │ │ ├── amazing.pas │ │ │ ├── amazingapplication.pas │ │ │ ├── maze.pas │ │ │ └── room.pas │ │ └── simple/ │ │ ├── amazing.lpi │ │ └── amazing.pas │ ├── 03_Animal/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── animal.ms │ │ ├── README.md │ │ ├── animal.bas │ │ └── go/ │ │ └── main.go │ ├── 04_Awari/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── awari.ms │ │ ├── README.md │ │ ├── awari.bas │ │ └── elm/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── docs/ │ │ │ └── index.html │ │ ├── elm.json │ │ ├── package.json │ │ ├── resources/ │ │ │ └── index.html │ │ └── src/ │ │ └── Main.elm │ ├── 05_Bagels/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── bagels.ms │ │ ├── README.md │ │ ├── bagels.bas │ │ ├── go/ │ │ │ └── main.go │ │ └── nim/ │ │ └── bagels.nim │ ├── 06_Banner/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── banner.ms │ │ ├── README.md │ │ └── banner.bas │ ├── 07_Basketball/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── basketball.ms │ │ ├── README.md │ │ └── basketball.bas │ ├── 08_Batnum/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── batnum.ms │ │ ├── README.md │ │ ├── batnum.bas │ │ └── go/ │ │ └── main.go │ ├── 09_Battle/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── battle.ms │ │ ├── README.md │ │ ├── battle.bas │ │ └── go/ │ │ └── main.go │ ├── 10_Blackjack/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── blackjack.ms │ │ ├── README.md │ │ ├── blackjack.bas │ │ └── pascal/ │ │ └── README.md │ ├── 11_Bombardment/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── bombardment.ms │ │ ├── README.md │ │ ├── bombardment.bas │ │ └── go/ │ │ └── main.go │ ├── 12_Bombs_Away/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── bombsaway.ms │ │ ├── README.md │ │ ├── bombsaway.bas │ │ └── go/ │ │ └── main.go │ ├── 13_Bounce/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── bounce.ms │ │ ├── README.md │ │ └── bounce.bas │ ├── 14_Bowling/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── bowling.ms │ │ ├── README.md │ │ └── bowling.bas │ ├── 15_Boxing/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── boxing.ms │ │ ├── README.md │ │ └── boxing.bas │ ├── 16_Bug/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── bug.ms │ │ ├── README.md │ │ └── bug.bas │ ├── 17_Bullfight/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── bull.ms │ │ ├── README.md │ │ └── bullfight.bas │ ├── 18_Bullseye/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── bullseye.ms │ │ ├── README.md │ │ └── bullseye.bas │ ├── 19_Bunny/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── bunny.ms │ │ ├── README.md │ │ └── bunny.bas │ ├── 20_Buzzword/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── buzzword.ms │ │ ├── README.md │ │ ├── buzzword.bas │ │ ├── go/ │ │ │ └── main.go │ │ └── nim/ │ │ └── buzzword.nim │ ├── 21_Calendar/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── calendar.ms │ │ ├── README.md │ │ └── calendar.bas │ ├── 22_Change/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── change.ms │ │ ├── README.md │ │ ├── change.bas │ │ └── go/ │ │ └── main.go │ ├── 23_Checkers/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── checkers.ms │ │ ├── README.md │ │ ├── checkers.annotated.bas │ │ └── checkers.bas │ ├── 24_Chemist/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── chemist.ms │ │ ├── README.md │ │ └── chemist.bas │ ├── 25_Chief/ │ │ ├── C/ │ │ │ └── chief.c │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── chief.ms │ │ ├── README.md │ │ ├── chief.bas │ │ └── go/ │ │ └── main.go │ ├── 26_Chomp/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── chomp.ms │ │ ├── README.md │ │ └── chomp.bas │ ├── 27_Civil_War/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── civilwar.ms │ │ ├── README.md │ │ └── civilwar.bas │ ├── 28_Combat/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── combat.ms │ │ ├── README.md │ │ └── combat.bas │ ├── 29_Craps/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── craps.ms │ │ ├── README.md │ │ ├── craps.bas │ │ ├── distributions.bas │ │ └── nim/ │ │ └── craps.nim │ ├── 30_Cube/ │ │ ├── C/ │ │ │ └── cube.c │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── cube.ms │ │ ├── README.md │ │ └── cube.bas │ ├── 31_Depth_Charge/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── depthcharge.ms │ │ ├── README.md │ │ ├── depthcharge.bas │ │ └── go/ │ │ └── main.go │ ├── 32_Diamond/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── diamond.ms │ │ ├── README.md │ │ └── diamond.bas │ ├── 33_Dice/ │ │ ├── C/ │ │ │ └── dice.c │ │ ├── Julia/ │ │ │ ├── Dice.jl │ │ │ └── README.md │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── dice.ms │ │ ├── README.md │ │ ├── dice.bas │ │ ├── go/ │ │ │ └── main.go │ │ └── nim/ │ │ └── dice.nim │ ├── 34_Digits/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── digits.ms │ │ ├── README.md │ │ ├── digits.bas │ │ └── go/ │ │ └── main.go │ ├── 35_Even_Wins/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ ├── evenwins.ms │ │ │ └── gameofevenwins.ms │ │ ├── README.md │ │ ├── evenwins.bas │ │ ├── gameofevenwins.bas │ │ └── go/ │ │ └── evenwins.go │ ├── 36_Flip_Flop/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── flipflop.ms │ │ ├── README.md │ │ └── flipflop.bas │ ├── 37_Football/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ ├── football.ms │ │ │ └── ftball.ms │ │ ├── README.md │ │ ├── football.bas │ │ └── ftball.bas │ ├── 38_Fur_Trader/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── furtrader.ms │ │ ├── README.md │ │ ├── c/ │ │ │ ├── README.md │ │ │ └── furtrader.c │ │ ├── furtrader.bas │ │ └── go/ │ │ └── main.go │ ├── 39_Golf/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── golf.ms │ │ ├── README.md │ │ └── golf.bas │ ├── 40_Gomoko/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── gomoko.ms │ │ ├── README.md │ │ └── gomoko.bas │ ├── 41_Guess/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── guess.ms │ │ ├── README.md │ │ ├── go/ │ │ │ └── main.go │ │ └── guess.bas │ ├── 42_Gunner/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── gunner.ms │ │ ├── README.md │ │ ├── go/ │ │ │ └── main.go │ │ └── gunner.bas │ ├── 43_Hammurabi/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── hammurabi.ms │ │ ├── README.md │ │ └── hammurabi.bas │ ├── 44_Hangman/ │ │ ├── C/ │ │ │ ├── dictionary.txt │ │ │ └── main.c │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── hangman.ms │ │ ├── README.md │ │ └── hangman.bas │ ├── 45_Hello/ │ │ ├── ANSI_C/ │ │ │ └── hello.c │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── hello.ms │ │ ├── README.md │ │ ├── Swift/ │ │ │ └── hello.swift │ │ ├── go/ │ │ │ └── main.go │ │ ├── hello.bas │ │ └── hello.c │ ├── 46_Hexapawn/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── hexapawn.ms │ │ ├── README.md │ │ └── hexapawn.bas │ ├── 47_Hi-Lo/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── hi-lo.ms │ │ ├── README.md │ │ ├── go/ │ │ │ └── main.go │ │ └── hi-lo.bas │ ├── 48_High_IQ/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── highiq.ms │ │ ├── README.md │ │ └── highiq.bas │ ├── 49_Hockey/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── hockey.ms │ │ ├── README.md │ │ └── hockey.bas │ ├── 50_Horserace/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── horserace.ms │ │ ├── README.md │ │ └── horserace.bas │ ├── 51_Hurkle/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── hurkle.ms │ │ ├── README.md │ │ └── hurkle.bas │ ├── 52_Kinema/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── kinema.ms │ │ ├── README.md │ │ └── kinema.bas │ ├── 53_King/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── king.ms │ │ ├── README.md │ │ ├── king.bas │ │ └── king_variable_update.bas │ ├── 54_Letter/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── letter.ms │ │ ├── README.md │ │ └── letter.bas │ ├── 55_Life/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── life.ms │ │ ├── README.md │ │ └── life.bas │ ├── 56_Life_for_Two/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── lifefortwo.ms │ │ ├── README.md │ │ └── lifefortwo.bas │ ├── 57_Literature_Quiz/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── litquiz.ms │ │ ├── README.md │ │ └── litquiz.bas │ ├── 58_Love/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── love.ms │ │ ├── README.md │ │ └── love.bas │ ├── 59_Lunar_LEM_Rocket/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ ├── lem.ms │ │ │ ├── lunar.ms │ │ │ └── rocket.ms │ │ ├── README.md │ │ ├── lem.bas │ │ ├── lunar.bas │ │ └── rocket.bas │ ├── 60_Mastermind/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── mastermind.ms │ │ ├── README.md │ │ └── mastermind.bas │ ├── 61_Math_Dice/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── mathdice.ms │ │ ├── README.md │ │ ├── mathdice.bas │ │ └── pascal/ │ │ ├── README.md │ │ └── mathdice.pas │ ├── 62_Mugwump/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── mugwump.ms │ │ ├── README.md │ │ └── mugwump.bas │ ├── 63_Name/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── name.ms │ │ ├── README.md │ │ └── name.bas │ ├── 64_Nicomachus/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── nicomachus.ms │ │ ├── README.md │ │ └── nicomachus.bas │ ├── 65_Nim/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── nim.ms │ │ ├── README.md │ │ └── nim.bas │ ├── 66_Number/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── number.ms │ │ ├── README.md │ │ └── number.bas │ ├── 67_One_Check/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── onecheck.ms │ │ ├── README.md │ │ └── onecheck.bas │ ├── 68_Orbit/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── orbit.ms │ │ ├── README.md │ │ └── orbit.bas │ ├── 69_Pizza/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── pizza.ms │ │ ├── README.md │ │ └── pizza.bas │ ├── 70_Poetry/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── poetry.ms │ │ ├── README.md │ │ ├── poetry.bas │ │ └── poetry.pl │ ├── 71_Poker/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── poker.ms │ │ ├── README.md │ │ └── poker.bas │ ├── 72_Queen/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── queen.ms │ │ ├── README.md │ │ └── queen.bas │ ├── 73_Reverse/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── reverse.ms │ │ ├── README.md │ │ └── reverse.bas │ ├── 74_Rock_Scissors_Paper/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── rockscissors.ms │ │ ├── README.md │ │ ├── bash/ │ │ │ └── rockscissors.sh │ │ ├── nim/ │ │ │ └── rockscissors.nim │ │ └── rockscissors.bas │ ├── 75_Roulette/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── roulette.ms │ │ ├── README.md │ │ └── roulette.bas │ ├── 76_Russian_Roulette/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── russianroulette.ms │ │ ├── README.md │ │ └── russianroulette.bas │ ├── 77_Salvo/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── salvo.ms │ │ ├── README.md │ │ └── salvo.bas │ ├── 78_Sine_Wave/ │ │ ├── C++/ │ │ │ ├── README.md │ │ │ └── sinewave.cpp │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── sinewave.ms │ │ ├── README.md │ │ ├── pascal/ │ │ │ └── sinewave.pas │ │ └── sinewave.bas │ ├── 79_Slalom/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── slalom.ms │ │ ├── README.md │ │ └── slalom.bas │ ├── 80_Slots/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── slots.ms │ │ ├── README.md │ │ └── slots.bas │ ├── 81_Splat/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── splat.ms │ │ ├── README.md │ │ └── splat.bas │ ├── 82_Stars/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── stars.ms │ │ ├── README.md │ │ └── stars.bas │ ├── 83_Stock_Market/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── stockmarket.ms │ │ ├── README.md │ │ └── stockmarket.bas │ ├── 84_Super_Star_Trek/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── superstartrek.ms │ │ ├── README.md │ │ ├── instructions.txt │ │ ├── superstartrek.bas │ │ └── superstartrekins.bas │ ├── 85_Synonym/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── synonym.ms │ │ ├── README.md │ │ └── synonym.bas │ ├── 86_Target/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── target.ms │ │ ├── README.md │ │ └── target.bas │ ├── 87_3-D_Plot/ │ │ ├── 3dplot.bas │ │ ├── MiniScript/ │ │ │ ├── 3dplot.ms │ │ │ └── README.md │ │ ├── README.md │ │ └── d/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── threedeeplot.d │ │ └── threedeeplot_random.d │ ├── 88_3-D_Tic-Tac-Toe/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── qubit.ms │ │ ├── README.md │ │ └── qubit.bas │ ├── 89_Tic-Tac-Toe/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ ├── tictactoe.ms │ │ │ └── tictactoe2.ms │ │ ├── README.md │ │ ├── go/ │ │ │ ├── README.md │ │ │ └── src/ │ │ │ └── tictactoe1.go │ │ ├── pascal/ │ │ │ ├── README.md │ │ │ └── tictactoe1.pas │ │ ├── tictactoe1.bas │ │ └── tictactoe2.bas │ ├── 90_Tower/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── tower.ms │ │ ├── README.md │ │ └── tower.bas │ ├── 91_Train/ │ │ ├── D/ │ │ │ ├── README.md │ │ │ └── train.d │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── train.ms │ │ ├── README.md │ │ ├── nim/ │ │ │ └── train.nim │ │ └── train.bas │ ├── 92_Trap/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── trap.ms │ │ ├── README.md │ │ └── trap.bas │ ├── 93_23_Matches/ │ │ ├── 23matches.bas │ │ ├── MiniScript/ │ │ │ ├── 23matches.ms │ │ │ └── README.md │ │ └── README.md │ ├── 94_War/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── war.ms │ │ ├── README.md │ │ ├── d/ │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ └── war.d │ │ └── war.bas │ ├── 95_Weekday/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── weekday.ms │ │ ├── README.md │ │ └── weekday.bas │ ├── 96_Word/ │ │ ├── MiniScript/ │ │ │ ├── README.md │ │ │ └── word.ms │ │ ├── README.md │ │ └── word.bas │ └── README.md ├── 00_Common/ │ ├── BASIC_Tests/ │ │ ├── InputTest.bas │ │ ├── OutputTest.bas │ │ └── RndTest.bas │ ├── README.md │ ├── dotnet/ │ │ ├── Directory.Build.props │ │ ├── Games.Common/ │ │ │ ├── Games.Common.csproj │ │ │ ├── IO/ │ │ │ │ ├── ConsoleIO.cs │ │ │ │ ├── IReadWrite.cs │ │ │ │ ├── InsufficientInputException.cs │ │ │ │ ├── Strings.cs │ │ │ │ ├── TextIO.cs │ │ │ │ ├── Token.cs │ │ │ │ ├── TokenReader.cs │ │ │ │ └── Tokenizer.cs │ │ │ ├── Numbers/ │ │ │ │ └── Number.cs │ │ │ ├── Randomness/ │ │ │ │ ├── IRandom.cs │ │ │ │ ├── IRandomExtensions.cs │ │ │ │ └── RandomNumberGenerator.cs │ │ │ └── _InternalsVisibleTo.cs │ │ ├── Games.Common.Test/ │ │ │ ├── Games.Common.Test.csproj │ │ │ └── IO/ │ │ │ ├── TextIOTests/ │ │ │ │ ├── NumberFormatTests.cs │ │ │ │ └── ReadMethodTests.cs │ │ │ ├── TokenReaderTests.cs │ │ │ ├── TokenTests.cs │ │ │ └── TokenizerTests.cs │ │ ├── Games.Common.sln │ │ └── README.md │ └── javascript/ │ ├── WebTerminal/ │ │ ├── HtmlTerminal.css │ │ ├── HtmlTerminal.js │ │ ├── terminal.html │ │ └── terminal_tests.mjs │ └── common.mjs ├── 00_Utilities/ │ ├── DotnetUtils/ │ │ ├── .editorconfig │ │ ├── DotnetUtils/ │ │ │ ├── DotnetUtils.csproj │ │ │ ├── Extensions.cs │ │ │ ├── Functions.cs │ │ │ ├── Globals.cs │ │ │ ├── Methods.cs │ │ │ ├── PortInfo.cs │ │ │ ├── PortInfos.cs │ │ │ └── Program.cs │ │ └── DotnetUtils.sln │ ├── README.md │ ├── TODO.md │ ├── VintageBASIC.xml │ ├── bas2perl.pl │ ├── build-index.js │ ├── find-missing-implementations.js │ ├── find-unimplemented.js │ ├── javascript/ │ │ └── style_terminal.css │ ├── jvmTestUtils/ │ │ └── kotlin/ │ │ └── test/ │ │ └── ConsoleTest.kt │ ├── markdown_todo.py │ ├── markdown_todo_rust/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ ├── python/ │ │ ├── ci-requirements.in │ │ └── ci-requirements.txt │ └── yatol.pl ├── 01_Acey_Ducey/ │ ├── README.md │ ├── aceyducey.bas │ ├── csharp/ │ │ ├── AceyDucey.csproj │ │ ├── AceyDucey.sln │ │ ├── Game.cs │ │ ├── GameState.cs │ │ ├── Program.cs │ │ └── README.md │ ├── elixir/ │ │ └── acey_ducey.exs │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ ├── AceyDucey.java │ │ ├── AceyDucey17.java │ │ ├── AceyDuceyGame.java │ │ └── Card.java │ ├── javascript/ │ │ ├── .prettierrc.json │ │ ├── README.md │ │ ├── aceyducey.html │ │ └── aceyducey.js │ ├── kotlin/ │ │ └── aceyducey.kt │ ├── lua/ │ │ ├── README.md │ │ └── acey_ducey.lua │ ├── perl/ │ │ ├── README.md │ │ └── aceyducey.pl │ ├── python/ │ │ ├── README.md │ │ ├── acey_ducey.py │ │ └── acey_ducey_oo.py │ ├── ruby/ │ │ ├── README.md │ │ └── aceyducey.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── AceyDucey.sln │ ├── AceyDucey.vb │ ├── AceyDucey.vbproj │ ├── Program.vb │ └── README.md ├── 02_Amazing/ │ ├── README.md │ ├── amazing.bas │ ├── csharp/ │ │ ├── Amazing.cs │ │ ├── Amazing.csproj │ │ ├── Amazing.sln │ │ └── README.md │ ├── java/ │ │ ├── Amazing.java │ │ ├── AmazingGame.java │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── amazing.html │ │ └── amazing.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── amazing.pl │ ├── python/ │ │ ├── README.md │ │ └── amazing.py │ ├── ruby/ │ │ ├── README.md │ │ └── amazing.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── Amazing.sln │ ├── Amazing.vbproj │ ├── README.md │ └── program.vb ├── 03_Animal/ │ ├── README.md │ ├── animal.bas │ ├── csharp/ │ │ ├── Animal.csproj │ │ ├── Animal.sln │ │ ├── Branch.cs │ │ ├── Program.cs │ │ └── README.md │ ├── java/ │ │ ├── README.md │ │ ├── src/ │ │ │ └── Animal.java │ │ └── test/ │ │ └── AnimalJavaTest.kt │ ├── javascript/ │ │ ├── README.md │ │ ├── animal.html │ │ └── animal.js │ ├── kotlin/ │ │ ├── README.md │ │ ├── src/ │ │ │ └── Animal.kt │ │ └── test/ │ │ └── AnimalKtTest.kt │ ├── lua/ │ │ ├── Animal.lua │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── animal.pl │ ├── python/ │ │ ├── README.md │ │ └── animal.py │ ├── ruby/ │ │ ├── README.md │ │ └── animal.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── Animal/ │ │ ├── Animal.vbproj │ │ ├── Branch.vb │ │ ├── Game.vb │ │ ├── Program.vb │ │ └── Shared/ │ │ ├── ConsoleAdapter.vb │ │ ├── ConsoleAdapterBase.vb │ │ └── Extensions.vb │ ├── Animal.Tests/ │ │ ├── Animal.Tests.vbproj │ │ ├── EndOfInputsException.vb │ │ ├── MockConsole.vb │ │ └── TestContainer.vb │ ├── Animal.sln │ └── README.md ├── 04_Awari/ │ ├── README.md │ ├── awari.bas │ ├── csharp/ │ │ ├── Awari.csproj │ │ ├── Awari.sln │ │ ├── Game.cs │ │ ├── Program.cs │ │ └── README.md │ ├── java/ │ │ ├── Awari.java │ │ ├── AwariGame.java │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── awari.html │ │ └── awari.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── awari.pl │ ├── python/ │ │ ├── README.md │ │ └── awari.py │ ├── ruby/ │ │ ├── README.md │ │ └── awari.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── Awari.sln │ ├── Awari.vbproj │ └── README.md ├── 05_Bagels/ │ ├── README.md │ ├── bagels.bas │ ├── csharp/ │ │ ├── BagelNumber.cs │ │ ├── Bagels.csproj │ │ ├── Bagels.sln │ │ ├── Game.cs │ │ ├── GameBase.cs │ │ ├── Program.cs │ │ └── README.md │ ├── java/ │ │ ├── BagelGame.java │ │ ├── Bagels.java │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── bagels.html │ │ └── bagels.js │ ├── kotlin/ │ │ ├── README.md │ │ └── bagels.kt │ ├── lua/ │ │ ├── Bagels.lua │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── bagels.pl │ ├── python/ │ │ ├── README.md │ │ └── bagels.py │ ├── ruby/ │ │ ├── README.md │ │ └── bagels.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── Bagels.sln │ ├── Bagels.vbproj │ └── README.md ├── 06_Banner/ │ ├── README.md │ ├── banner.bas │ ├── csharp/ │ │ ├── README.md │ │ ├── banner.cs │ │ ├── banner.csproj │ │ └── banner.sln │ ├── java/ │ │ ├── Banner.java │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── banner.html │ │ └── banner.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── pascal/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── banner.pl │ ├── python/ │ │ ├── README.md │ │ └── banner.py │ ├── ruby/ │ │ ├── README.md │ │ └── banner.rb │ └── vbnet/ │ ├── README.md │ ├── banner.sln │ ├── banner.vb │ └── banner.vbproj ├── 07_Basketball/ │ ├── README.md │ ├── basketball.bas │ ├── csharp/ │ │ ├── Basketball.csproj │ │ ├── Basketball.sln │ │ ├── Clock.cs │ │ ├── Defense.cs │ │ ├── Game.cs │ │ ├── IRandomExtensions.cs │ │ ├── IReadWriteExtensions.cs │ │ ├── JumpShot.cs │ │ ├── Plays/ │ │ │ ├── BallContest.cs │ │ │ ├── HomeTeamPlay.cs │ │ │ ├── Play.cs │ │ │ └── VisitingTeamPlay.cs │ │ ├── Probably.cs │ │ ├── Program.cs │ │ ├── README.md │ │ ├── Resources/ │ │ │ ├── EndOfFirstHalf.txt │ │ │ ├── EndOfGame.txt │ │ │ ├── EndOfSecondHalf.txt │ │ │ ├── Introduction.txt │ │ │ ├── Resource.cs │ │ │ ├── Score.txt │ │ │ └── TwoMinutesLeft.txt │ │ ├── Scoreboard.cs │ │ ├── Shot.cs │ │ └── Team.cs │ ├── java/ │ │ ├── Basketball.java │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── basketball.html │ │ └── basketball.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── pascal/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ ├── basketball-orig.pl │ │ └── basketball.pl │ ├── python/ │ │ ├── README.md │ │ └── basketball.py │ ├── ruby/ │ │ ├── README.md │ │ └── basketball.rb │ └── vbnet/ │ ├── Basketball.sln │ ├── Basketball.vbproj │ └── README.md ├── 08_Batnum/ │ ├── README.md │ ├── batnum.bas │ ├── csharp/ │ │ ├── Batnum.csproj │ │ ├── Batnum.sln │ │ ├── BatnumGame.cs │ │ ├── ConsoleUtilities.cs │ │ ├── Program.cs │ │ ├── Properties/ │ │ │ ├── Resources.Designer.cs │ │ │ ├── Resources.en.resx │ │ │ ├── Resources.fr.resx │ │ │ └── Resources.resx │ │ └── README.md │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ ├── BatNum.java │ │ └── BatNumGame.java │ ├── javascript/ │ │ ├── README.md │ │ ├── batnum.html │ │ └── batnum.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── pascal/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── batnum.pl │ ├── python/ │ │ ├── README.md │ │ └── batnum.py │ ├── ruby/ │ │ ├── README.md │ │ └── batnum.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── Program.vb │ ├── README.md │ ├── batnum.sln │ └── batnum.vbproj ├── 09_Battle/ │ ├── README.md │ ├── battle.bas │ ├── csharp/ │ │ ├── Battle.csproj │ │ ├── Battle.sln │ │ ├── Game.cs │ │ ├── Program.cs │ │ └── README.md │ ├── java/ │ │ ├── .gitignore │ │ ├── Battle.java │ │ ├── Input.java │ │ ├── README.md │ │ ├── Sea.java │ │ └── Ship.java │ ├── javascript/ │ │ ├── README.md │ │ ├── battle.html │ │ └── battle.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── pascal/ │ │ └── README.md │ ├── perl/ │ │ └── README.md │ ├── python/ │ │ ├── README.md │ │ └── battle.py │ ├── ruby/ │ │ └── README.md │ ├── rust/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── Battle.sln │ ├── Battle.vbproj │ └── README.md ├── 10_Blackjack/ │ ├── README.md │ ├── blackjack.bas │ ├── csharp/ │ │ ├── Blackjack.csproj │ │ ├── Blackjack.sln │ │ ├── Card.cs │ │ ├── Deck.cs │ │ ├── Game.cs │ │ ├── Hand.cs │ │ ├── Player.cs │ │ ├── Program.cs │ │ ├── Prompt.cs │ │ └── README.md │ ├── java/ │ │ ├── README.md │ │ ├── src/ │ │ │ ├── Blackjack.java │ │ │ ├── Card.java │ │ │ ├── Deck.java │ │ │ ├── Game.java │ │ │ ├── Player.java │ │ │ ├── ScoringUtils.java │ │ │ └── UserIo.java │ │ └── test/ │ │ ├── DeckTest.java │ │ ├── GameTest.java │ │ ├── ScoringUtilsTest.java │ │ └── UserIoTest.java │ ├── javascript/ │ │ ├── README.md │ │ ├── blackjack.html │ │ └── blackjack.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ └── README.md │ ├── python/ │ │ ├── README.md │ │ └── blackjack.py │ ├── ruby/ │ │ ├── README.md │ │ ├── blackjack.rb │ │ ├── game.rb │ │ └── model/ │ │ ├── card_kind.rb │ │ ├── hand.rb │ │ ├── pack.rb │ │ └── player.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── Blackjack.sln │ ├── Blackjack.vbproj │ └── README.md ├── 11_Bombardment/ │ ├── README.md │ ├── bombardment.bas │ ├── csharp/ │ │ ├── Bombardment.cs │ │ ├── Bombardment.csproj │ │ ├── Bombardment.sln │ │ ├── Program.cs │ │ └── README.md │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ ├── Bombardment.java │ │ └── BombardmentGame.java │ ├── javascript/ │ │ ├── README.md │ │ ├── bombardment.html │ │ └── bombardment.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── bombardment.pl │ ├── python/ │ │ ├── README.md │ │ └── bombardment.py │ ├── ruby/ │ │ ├── README.md │ │ └── bombardment.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── Bombardment.sln │ ├── Bombardment.vbproj │ └── README.md ├── 12_Bombs_Away/ │ ├── README.md │ ├── bombsaway.bas │ ├── csharp/ │ │ ├── BombsAway.sln │ │ ├── BombsAwayConsole/ │ │ │ ├── BombsAwayConsole.csproj │ │ │ ├── ConsoleUserInterface.cs │ │ │ └── Program.cs │ │ ├── BombsAwayGame/ │ │ │ ├── AlliesSide.cs │ │ │ ├── BombsAwayGame.csproj │ │ │ ├── EnemyArtillery.cs │ │ │ ├── Game.cs │ │ │ ├── GermanySide.cs │ │ │ ├── IUserInterface.cs │ │ │ ├── ItalySide.cs │ │ │ ├── JapanSide.cs │ │ │ ├── Mission.cs │ │ │ ├── MissionSide.cs │ │ │ └── Side.cs │ │ └── README.md │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ ├── BombsAway.java │ │ └── BombsAwayGame.java │ ├── javascript/ │ │ ├── README.md │ │ ├── bombsaway.html │ │ └── bombsaway.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ ├── README.md │ │ └── bombs_away.lua │ ├── perl/ │ │ ├── README.md │ │ └── bombsaway.pl │ ├── python/ │ │ ├── README.md │ │ └── bombs_away.py │ ├── ruby/ │ │ └── README.md │ ├── rust/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── BombsAway.sln │ ├── BombsAway.vbproj │ └── README.md ├── 13_Bounce/ │ ├── README.md │ ├── bounce.bas │ ├── csharp/ │ │ ├── Bounce.cs │ │ ├── Bounce.csproj │ │ ├── Bounce.sln │ │ ├── Game.cs │ │ ├── Graph.cs │ │ ├── IReadWriteExtensions.cs │ │ ├── Program.cs │ │ ├── README.md │ │ └── Resources/ │ │ ├── Instructions.txt │ │ ├── Resource.cs │ │ └── Title.txt │ ├── java/ │ │ ├── Bounce.java │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── bounce.html │ │ └── bounce.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── bounce.pl │ ├── python/ │ │ ├── README.md │ │ └── bounce.py │ ├── ruby/ │ │ ├── README.md │ │ └── bounce.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── Bounce.sln │ ├── Bounce.vbproj │ └── README.md ├── 14_Bowling/ │ ├── README.md │ ├── bowling.bas │ ├── csharp/ │ │ ├── Bowling.cs │ │ ├── Bowling.csproj │ │ ├── Bowling.sln │ │ ├── FrameResult.cs │ │ ├── GameResults.cs │ │ ├── Pins.cs │ │ ├── Program.cs │ │ ├── README.md │ │ └── Utility.cs │ ├── java/ │ │ ├── Bowling.java │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── bowling.html │ │ └── bowling.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── bowling.pl │ ├── python/ │ │ ├── README.md │ │ └── bowling.py │ ├── ruby/ │ │ └── README.md │ └── vbnet/ │ ├── Bowling.sln │ ├── Bowling.vbproj │ └── README.md ├── 15_Boxing/ │ ├── README.md │ ├── boxing.bas │ ├── csharp/ │ │ ├── AttackStrategy.cs │ │ ├── Boxer.cs │ │ ├── Boxing.csproj │ │ ├── Boxing.sln │ │ ├── OpponentAttackStrategy.cs │ │ ├── PlayerAttackStrategy.cs │ │ ├── Program.cs │ │ ├── Punch.cs │ │ ├── README.md │ │ ├── Round.cs │ │ └── Utils.cs │ ├── java/ │ │ ├── Basic.java │ │ ├── Boxing.java │ │ ├── BoxingGame.java │ │ ├── GameSession.java │ │ ├── Player.java │ │ ├── Punch.java │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── boxing.html │ │ └── boxing.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── boxing.pl │ ├── python/ │ │ ├── README.md │ │ ├── boxing.py │ │ ├── opponent-profile.json │ │ └── player-profile.json │ ├── ruby/ │ │ └── README.md │ └── vbnet/ │ ├── Boxing.sln │ ├── Boxing.vbproj │ └── README.md ├── 16_Bug/ │ ├── README.md │ ├── bug.bas │ ├── csharp/ │ │ ├── Bug.cs │ │ ├── Bug.csproj │ │ ├── Bug.sln │ │ ├── Game.cs │ │ ├── Parts/ │ │ │ ├── Body.cs │ │ │ ├── Feeler.cs │ │ │ ├── Feelers.cs │ │ │ ├── Head.cs │ │ │ ├── IPart.cs │ │ │ ├── Leg.cs │ │ │ ├── Legs.cs │ │ │ ├── Neck.cs │ │ │ ├── ParentPart.cs │ │ │ ├── Part.cs │ │ │ ├── PartCollection.cs │ │ │ └── Tail.cs │ │ ├── Program.cs │ │ ├── README.md │ │ └── Resources/ │ │ ├── Instructions.txt │ │ ├── Introduction.txt │ │ ├── Message.cs │ │ ├── PlayAgain.txt │ │ └── Resource.cs │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ ├── Bug.java │ │ ├── BugGame.java │ │ ├── ComputerBug.java │ │ ├── Insect.java │ │ └── PlayerBug.java │ ├── javascript/ │ │ ├── README.md │ │ ├── bug.html │ │ └── bug.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ └── README.md │ ├── python/ │ │ ├── README.md │ │ └── bug.py │ ├── ruby/ │ │ ├── Bug.rb │ │ └── README.md │ └── vbnet/ │ ├── Bug.sln │ ├── Bug.vbproj │ └── README.md ├── 17_Bullfight/ │ ├── README.md │ ├── bullfight.bas │ ├── csharp/ │ │ ├── Action.cs │ │ ├── ActionResult.cs │ │ ├── BullFight.cs │ │ ├── Bullfight.csproj │ │ ├── Bullfight.sln │ │ ├── Controller.cs │ │ ├── Events/ │ │ │ ├── BullCharging.cs │ │ │ ├── Event.cs │ │ │ ├── MatchCompleted.cs │ │ │ ├── MatchStarted.cs │ │ │ ├── PlayerGored.cs │ │ │ └── PlayerSurvived.cs │ │ ├── Mediator.cs │ │ ├── Program.cs │ │ ├── Quality.cs │ │ ├── README.md │ │ ├── Reward.cs │ │ ├── RiskLevel.cs │ │ └── View.cs │ ├── java/ │ │ ├── Bullfight.java │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── bullfight.html │ │ └── bullfight.js │ ├── kotlin/ │ │ ├── README.md │ │ └── src/ │ │ └── bullfight/ │ │ └── Main.kt │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ └── README.md │ ├── python/ │ │ ├── README.md │ │ └── bullfight.py │ ├── ruby/ │ │ └── README.md │ └── vbnet/ │ ├── Bullfight.sln │ ├── Bullfight.vbproj │ └── README.md ├── 18_Bullseye/ │ ├── README.md │ ├── bullseye.bas │ ├── csharp/ │ │ ├── Bullseye.csproj │ │ ├── Bullseye.sln │ │ ├── BullseyeGame.cs │ │ ├── Player.cs │ │ ├── Program.cs │ │ └── README.md │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ ├── Bullseye.java │ │ ├── BullseyeGame.java │ │ ├── Player.java │ │ └── Shot.java │ ├── javascript/ │ │ ├── README.md │ │ ├── bullseye.html │ │ └── bullseye.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── bullseye.pl │ ├── python/ │ │ ├── README.md │ │ └── bullseye.py │ ├── ruby/ │ │ └── README.md │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ ├── lib.rs │ │ ├── main.rs │ │ └── note on separation of converns for rust projects.md │ └── vbnet/ │ ├── Bullseye.sln │ ├── Bullseye.vbproj │ └── README.md ├── 19_Bunny/ │ ├── README.md │ ├── bunny.bas │ ├── csharp/ │ │ ├── BasicData.cs │ │ ├── Bunny.cs │ │ ├── Bunny.csproj │ │ ├── Bunny.sln │ │ ├── Program.cs │ │ └── README.md │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ └── Bunny.java │ ├── javascript/ │ │ ├── README.md │ │ ├── bunny.html │ │ └── bunny.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── bunny.pl │ ├── python/ │ │ ├── README.md │ │ ├── bunny.py │ │ └── data.json │ ├── ruby/ │ │ ├── README.md │ │ ├── bunny-faithful.rb │ │ └── bunny-modern.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── Bunny.sln │ ├── Bunny.vbproj │ └── README.md ├── 20_Buzzword/ │ ├── README.md │ ├── buzzword.bas │ ├── csharp/ │ │ ├── Buzzword.csproj │ │ ├── Buzzword.sln │ │ ├── Program.cs │ │ └── README.md │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ ├── Buzzword.java │ │ ├── BuzzwordSupplier.java │ │ └── UserInterface.java │ ├── javascript/ │ │ ├── README.md │ │ ├── buzzword.html │ │ └── buzzword.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ ├── README.md │ │ └── buzzword.lua │ ├── perl/ │ │ ├── README.md │ │ └── buzzword.pl │ ├── python/ │ │ ├── README.md │ │ └── buzzword.py │ ├── ruby/ │ │ ├── README.md │ │ └── buzzword.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── Buzzword.sln │ ├── Buzzword.vbproj │ └── README.md ├── 21_Calendar/ │ ├── README.md │ ├── calendar.bas │ ├── csharp/ │ │ ├── Calendar.csproj │ │ ├── Calendar.sln │ │ ├── Program.cs │ │ └── README.md │ ├── java/ │ │ ├── Calendar.java │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── calendar.html │ │ └── calendar.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── calendar.pl │ ├── python/ │ │ ├── README.md │ │ └── calendar.py │ ├── ruby/ │ │ ├── README.md │ │ └── calendar.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── Calendar.sln │ ├── Calendar.vbproj │ └── README.md ├── 22_Change/ │ ├── README.md │ ├── change.bas │ ├── csharp/ │ │ ├── Change.csproj │ │ ├── Change.sln │ │ ├── Program.cs │ │ └── README.md │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ ├── Change.java │ │ └── ChangeGame.java │ ├── javascript/ │ │ ├── README.md │ │ ├── change.html │ │ └── change.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── change.pl │ ├── python/ │ │ ├── README.md │ │ └── change.py │ ├── ruby/ │ │ └── README.md │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── Change.sln │ ├── Change.vbproj │ └── README.md ├── 23_Checkers/ │ ├── README.md │ ├── checkers.annotated.bas │ ├── checkers.bas │ ├── csharp/ │ │ ├── Checkers.csproj │ │ ├── Checkers.sln │ │ ├── Program.cs │ │ └── README.md │ ├── java/ │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── checkers.html │ │ └── checkers.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── checkers.pl │ ├── python/ │ │ ├── README.md │ │ └── checkers.py │ ├── ruby/ │ │ ├── README.md │ │ └── checkers.rb │ └── vbnet/ │ ├── Checkers.sln │ ├── Checkers.vbproj │ └── README.md ├── 24_Chemist/ │ ├── README.md │ ├── chemist.bas │ ├── csharp/ │ │ ├── Chemist.csproj │ │ ├── Chemist.sln │ │ ├── Program.cs │ │ └── README.md │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ ├── Chemist.java │ │ └── ChemistGame.java │ ├── javascript/ │ │ ├── README.md │ │ ├── chemist.html │ │ └── chemist.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── chemist.pl │ ├── python/ │ │ ├── README.md │ │ └── chemist.py │ ├── ruby/ │ │ └── README.md │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ ├── lib.rs │ │ └── main.rs │ └── vbnet/ │ ├── Chemist.sln │ ├── Chemist.vbproj │ └── README.md ├── 25_Chief/ │ ├── README.md │ ├── chief.bas │ ├── csharp/ │ │ ├── Chief.csproj │ │ ├── Chief.sln │ │ ├── Game.cs │ │ ├── IReadWriteExtensions.cs │ │ ├── Math.cs │ │ ├── Program.cs │ │ ├── README.md │ │ └── Resources/ │ │ ├── Answer.txt │ │ ├── Believe.txt │ │ ├── Bet.txt │ │ ├── Bye.txt │ │ ├── Instructions.txt │ │ ├── Lightning.txt │ │ ├── Original.txt │ │ ├── Ready.txt │ │ ├── Resource.cs │ │ ├── ShutUp.txt │ │ ├── Title.txt │ │ └── Working.txt │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ ├── Chief.java │ │ └── ChiefGame.java │ ├── javascript/ │ │ ├── README.md │ │ ├── chief.html │ │ └── chief.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ ├── README.md │ │ └── chief.lua │ ├── perl/ │ │ ├── README.md │ │ └── chief.pl │ ├── python/ │ │ ├── README.md │ │ └── chief.py │ ├── ruby/ │ │ ├── README.md │ │ └── chief.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── Chief.sln │ ├── Chief.vbproj │ └── README.md ├── 26_Chomp/ │ ├── README.md │ ├── chomp.bas │ ├── csharp/ │ │ ├── Chomp.csproj │ │ ├── Chomp.sln │ │ ├── Cookie.cs │ │ ├── Game.cs │ │ ├── IOExtensions.cs │ │ ├── PlayerNumber.cs │ │ ├── Program.cs │ │ ├── README.md │ │ └── Resources/ │ │ ├── Coordinates.txt │ │ ├── HereWeGo.txt │ │ ├── HowManyColumns.txt │ │ ├── HowManyPlayers.txt │ │ ├── HowManyRows.txt │ │ ├── Introduction.txt │ │ ├── NoFair.txt │ │ ├── Player.txt │ │ ├── Resource.cs │ │ ├── Rules.txt │ │ ├── TooManyColumns.txt │ │ ├── TooManyRows.txt │ │ └── YouLose.txt │ ├── java/ │ │ ├── Chomp.java │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── chomp.html │ │ └── chomp.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── chomp.pl │ ├── python/ │ │ ├── README.md │ │ └── chomp.py │ ├── ruby/ │ │ └── README.md │ └── vbnet/ │ ├── Chomp.sln │ ├── Chomp.vbproj │ └── README.md ├── 27_Civil_War/ │ ├── README.md │ ├── civilwar.bas │ ├── csharp/ │ │ ├── Army.cs │ │ ├── Battle.cs │ │ ├── CivilWar.csproj │ │ ├── CivilWar.sln │ │ ├── ConsoleUtils.cs │ │ ├── GameOptions.cs │ │ ├── Program.cs │ │ └── README.md │ ├── java/ │ │ ├── .gitignore │ │ ├── README.md │ │ └── src/ │ │ └── CivilWar.java │ ├── javascript/ │ │ ├── README.md │ │ ├── civilwar.html │ │ └── civilwar.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ └── README.md │ ├── python/ │ │ ├── Civilwar.py │ │ └── README.md │ ├── ruby/ │ │ └── README.md │ └── vbnet/ │ ├── CivilWar.sln │ ├── CivilWar.vbproj │ └── README.md ├── 28_Combat/ │ ├── README.md │ ├── combat.bas │ ├── csharp/ │ │ ├── ArmedForces.cs │ │ ├── Ceasefire.cs │ │ ├── Combat.csproj │ │ ├── Combat.sln │ │ ├── Controller.cs │ │ ├── FinalCampaign.cs │ │ ├── InitialCampaign.cs │ │ ├── MilitaryBranch.cs │ │ ├── Program.cs │ │ ├── README.md │ │ ├── View.cs │ │ ├── WarResult.cs │ │ └── WarState.cs │ ├── java/ │ │ ├── Combat.java │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── combat.html │ │ └── combat.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── combat.pl │ ├── python/ │ │ ├── README.md │ │ └── combat.py │ ├── ruby/ │ │ └── README.md │ └── vbnet/ │ ├── Combat.sln │ ├── Combat.vbproj │ └── README.md ├── 29_Craps/ │ ├── README.md │ ├── craps.bas │ ├── csharp/ │ │ ├── .gitignore │ │ ├── Craps/ │ │ │ ├── Craps.csproj │ │ │ ├── CrapsGame.cs │ │ │ ├── Dice.cs │ │ │ ├── Program.cs │ │ │ └── UserInterface.cs │ │ ├── Craps.sln │ │ ├── CrapsTester/ │ │ │ ├── CrapsTester.csproj │ │ │ └── CrapsTests.cs │ │ └── README.md │ ├── distributions.bas │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ └── Craps.java │ ├── javascript/ │ │ ├── README.md │ │ ├── craps.html │ │ └── craps.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ ├── README.md │ │ └── craps.lua │ ├── perl/ │ │ ├── README.md │ │ └── craps.pl │ ├── python/ │ │ ├── README.md │ │ └── craps.py │ ├── ruby/ │ │ ├── README.md │ │ └── craps.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ └── src/ │ │ ├── craps_game.rs │ │ ├── main.rs │ │ └── util.rs │ └── vbnet/ │ ├── Craps.sln │ ├── Craps.vbproj │ └── README.md ├── 30_Cube/ │ ├── README.md │ ├── csharp/ │ │ ├── Cube.csproj │ │ ├── Cube.sln │ │ ├── Game.cs │ │ ├── IOExtensions.cs │ │ ├── Program.cs │ │ ├── README.md │ │ ├── RandomExtensions.cs │ │ ├── Resources/ │ │ │ ├── Balance.txt │ │ │ ├── Bang.txt │ │ │ ├── BetAgain.txt │ │ │ ├── Bust.txt │ │ │ ├── Congratulations.txt │ │ │ ├── Goodbye.txt │ │ │ ├── HowMuch.txt │ │ │ ├── IllegalMove.txt │ │ │ ├── Instructions.txt │ │ │ ├── Introduction.txt │ │ │ ├── NextMove.txt │ │ │ ├── Resource.cs │ │ │ ├── TryAgain.txt │ │ │ ├── Wager.txt │ │ │ └── YourMove.txt │ │ └── ZerosGenerator.cs │ ├── cube.bas │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ └── Cube.java │ ├── javascript/ │ │ ├── README.md │ │ ├── cube.html │ │ └── cube.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ └── README.md │ ├── python/ │ │ ├── README.md │ │ └── cube.py │ ├── ruby/ │ │ ├── README.md │ │ └── cube.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ └── src/ │ │ ├── game.rs │ │ ├── main.rs │ │ └── util.rs │ └── vbnet/ │ ├── Cube.sln │ ├── Cube.vbproj │ └── README.md ├── 31_Depth_Charge/ │ ├── README.md │ ├── csharp/ │ │ ├── Controller.cs │ │ ├── DepthCharge.csproj │ │ ├── DepthCharge.sln │ │ ├── Program.cs │ │ ├── README.md │ │ └── View.cs │ ├── depthcharge.bas │ ├── java/ │ │ ├── DepthCharge.java │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── depthcharge.html │ │ └── depthcharge.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── depth-charge.pl │ ├── python/ │ │ ├── README.md │ │ └── depth_charge.py │ ├── ruby/ │ │ ├── .editorconfig │ │ ├── .gitignore │ │ ├── README.md │ │ └── depthcharge.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── DepthCharge.sln │ ├── DepthCharge.vbproj │ └── README.md ├── 32_Diamond/ │ ├── README.md │ ├── csharp/ │ │ ├── Diamond.csproj │ │ ├── Diamond.sln │ │ ├── Pattern.cs │ │ ├── Program.cs │ │ ├── README.md │ │ ├── Resources/ │ │ │ ├── Introduction.txt │ │ │ ├── Resource.cs │ │ │ ├── Rules.txt │ │ │ └── TypeNumber.txt │ │ └── StringBuilderExtensions.cs │ ├── diamond.bas │ ├── java/ │ │ ├── Diamond.java │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── diamond.html │ │ └── diamond.js │ ├── kotlin/ │ │ ├── README.md │ │ └── diamond.kt │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── diamond.pl │ ├── python/ │ │ ├── README.md │ │ └── diamond.py │ ├── ruby/ │ │ ├── README.md │ │ └── diamond.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ ├── lib.rs │ │ └── main.rs │ └── vbnet/ │ ├── Diamond.sln │ ├── Diamond.vbproj │ └── README.md ├── 33_Dice/ │ ├── README.md │ ├── csharp/ │ │ ├── Dice.csproj │ │ ├── Dice.sln │ │ ├── Game.cs │ │ ├── Program.cs │ │ ├── README.md │ │ └── RollGenerator.cs │ ├── dice.bas │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ ├── Dice.java │ │ └── DiceGame.java │ ├── javascript/ │ │ ├── README.md │ │ ├── dice.html │ │ └── dice.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ ├── README.md │ │ └── dice.lua │ ├── perl/ │ │ ├── README.md │ │ └── dice.pl │ ├── python/ │ │ ├── README.md │ │ └── dice.py │ ├── ruby/ │ │ ├── README.md │ │ └── dice.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── Dice.sln │ ├── Dice.vbproj │ ├── README.md │ └── program.vb ├── 34_Digits/ │ ├── README.md │ ├── csharp/ │ │ ├── Digits.csproj │ │ ├── Digits.sln │ │ ├── Game.cs │ │ ├── Guesser.cs │ │ ├── IOExtensions.cs │ │ ├── Matrix.cs │ │ ├── Memory.cs │ │ ├── Program.cs │ │ ├── README.md │ │ └── Resources/ │ │ ├── ForInstructions.txt │ │ ├── GuessResult.txt │ │ ├── Headings.txt │ │ ├── IWin.txt │ │ ├── Instructions.txt │ │ ├── Introduction.txt │ │ ├── ItsATie.txt │ │ ├── Resource.cs │ │ ├── TenNumbers.txt │ │ ├── Thanks.txt │ │ ├── TryAgain.txt │ │ ├── WantToTryAgain.txt │ │ └── YouWin.txt │ ├── digits.bas │ ├── java/ │ │ ├── Digits.java │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── digits.html │ │ └── digits.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── digits.pl │ ├── python/ │ │ ├── Digits.py │ │ └── README.md │ ├── ruby/ │ │ └── README.md │ └── vbnet/ │ ├── Digits.sln │ ├── Digits.vbproj │ └── README.md ├── 35_Even_Wins/ │ ├── README.md │ ├── csharp/ │ │ ├── EvenWins.csproj │ │ ├── EvenWins.sln │ │ └── README.md │ ├── evenwins.bas │ ├── gameofevenwins.bas │ ├── java/ │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── evenwins.html │ │ ├── evenwins.js │ │ ├── gameofevenwins.html │ │ └── gameofevenwins.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── evenwins.pl │ ├── python/ │ │ ├── README.md │ │ └── evenwins.py │ ├── ruby/ │ │ └── README.md │ ├── rust/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── EvenWins.sln │ ├── EvenWins.vbproj │ └── README.md ├── 36_Flip_Flop/ │ ├── README.md │ ├── csharp/ │ │ ├── FlipFlop.cs │ │ ├── FlipFlop.csproj │ │ ├── FlipFlop.sln │ │ └── README.md │ ├── flipflop.bas │ ├── java/ │ │ ├── FlipFlop.java │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── flipflop.html │ │ └── flipflop.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ ├── README.md │ │ └── flipflop-game.lua │ ├── perl/ │ │ ├── README.md │ │ └── flipflop.pl │ ├── python/ │ │ ├── README.md │ │ └── flipflop.py │ ├── ruby/ │ │ ├── README.md │ │ └── flipflop.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ └── src/ │ │ ├── game.rs │ │ └── main.rs │ └── vbnet/ │ ├── FlipFlop.sln │ ├── FlipFlop.vbproj │ └── README.md ├── 37_Football/ │ ├── README.md │ ├── csharp/ │ │ ├── Football.csproj │ │ ├── Football.sln │ │ └── README.md │ ├── football.bas │ ├── ftball.bas │ ├── java/ │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── football.html │ │ ├── football.js │ │ ├── ftball.html │ │ └── ftball.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ └── README.md │ ├── python/ │ │ ├── README.md │ │ ├── data.json │ │ └── football.py │ ├── ruby/ │ │ └── README.md │ └── vbnet/ │ ├── Football.sln │ ├── Football.vbproj │ └── README.md ├── 38_Fur_Trader/ │ ├── README.md │ ├── c/ │ │ ├── README.md │ │ └── furtrader.c │ ├── csharp/ │ │ ├── FurTrader.csproj │ │ ├── FurTrader.sln │ │ ├── Game.cs │ │ ├── GameState.cs │ │ ├── Program.cs │ │ └── README.md │ ├── furtrader.bas │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ ├── FurTrader.java │ │ ├── FurTraderGame.java │ │ └── Pelt.java │ ├── javascript/ │ │ ├── README.md │ │ ├── furtrader.html │ │ └── furtrader.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── furtrader.pl │ ├── python/ │ │ ├── README.md │ │ └── furtrader.py │ ├── ruby/ │ │ └── README.md │ └── vbnet/ │ ├── FurTrader.sln │ ├── FurTrader.vbproj │ └── README.md ├── 39_Golf/ │ ├── README.md │ ├── csharp/ │ │ ├── Golf.csproj │ │ ├── Golf.sln │ │ ├── Program.cs │ │ └── README.md │ ├── golf.bas │ ├── java/ │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── golf.html │ │ └── golf.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ └── README.md │ ├── python/ │ │ ├── README.md │ │ └── golf.py │ ├── ruby/ │ │ └── README.md │ └── vbnet/ │ ├── Golf.sln │ ├── Golf.vbproj │ └── README.md ├── 40_Gomoko/ │ ├── README.md │ ├── csharp/ │ │ ├── Gomoko.csproj │ │ ├── Gomoko.sln │ │ └── README.md │ ├── gomoko.bas │ ├── java/ │ │ ├── Gomoko.java │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── gomoko.html │ │ └── gomoko.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── gomoko.pl │ ├── python/ │ │ ├── Gomoko.py │ │ └── README.md │ ├── ruby/ │ │ └── README.md │ └── vbnet/ │ ├── Gomoko.sln │ ├── Gomoko.vbproj │ └── README.md ├── 41_Guess/ │ ├── README.md │ ├── csharp/ │ │ ├── Game.cs │ │ ├── Guess.csproj │ │ ├── Guess.sln │ │ ├── Program.cs │ │ ├── README.md │ │ └── Resources/ │ │ ├── BlankLines.txt │ │ ├── Good.txt │ │ ├── Introduction.txt │ │ ├── Limit.txt │ │ ├── Resource.cs │ │ ├── ShouldHave.txt │ │ ├── ThatsIt.txt │ │ ├── Thinking.txt │ │ ├── TooHigh.txt │ │ ├── TooLow.txt │ │ └── VeryGood.txt │ ├── guess.bas │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ ├── Guess.java │ │ └── GuessGame.java │ ├── javascript/ │ │ ├── README.md │ │ ├── guess.html │ │ └── guess.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── guess.pl │ ├── python/ │ │ ├── README.md │ │ └── guess.py │ ├── ruby/ │ │ ├── README.md │ │ └── guess.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── Guess.sln │ ├── Guess.vbproj │ └── README.md ├── 42_Gunner/ │ ├── README.md │ ├── csharp/ │ │ ├── Gunner.csproj │ │ ├── Gunner.sln │ │ ├── Program.cs │ │ └── README.md │ ├── gunner.bas │ ├── java/ │ │ ├── Gunner.java │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── gunner.html │ │ └── gunner.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── gunner.pl │ ├── python/ │ │ ├── README.md │ │ └── gunner.py │ ├── ruby/ │ │ └── README.md │ ├── rust/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── Gunner.sln │ ├── Gunner.vbproj │ └── README.md ├── 43_Hammurabi/ │ ├── README.md │ ├── csharp/ │ │ ├── ActionResult.cs │ │ ├── Controller.cs │ │ ├── GameResult.cs │ │ ├── GameState.cs │ │ ├── GreatOffence.cs │ │ ├── Hammurabi.csproj │ │ ├── Hammurabi.sln │ │ ├── PerformanceRating.cs │ │ ├── Program.cs │ │ ├── README.md │ │ ├── Rules.cs │ │ └── View.cs │ ├── hammurabi.bas │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ ├── Hamurabi.java │ │ └── HamurabiGame.java │ ├── javascript/ │ │ ├── README.md │ │ ├── hammurabi.html │ │ └── hammurabi.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── lib/ │ │ └── BasicComputerGames/ │ │ └── Hammurabi.pm │ ├── python/ │ │ ├── README.md │ │ └── hamurabi.py │ ├── ruby/ │ │ └── README.md │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── Hammurabi.sln │ ├── Hammurabi.vbproj │ └── README.md ├── 44_Hangman/ │ ├── README.md │ ├── csharp/ │ │ ├── Graphic.cs │ │ ├── Hangman.csproj │ │ ├── Hangman.sln │ │ ├── Program.cs │ │ └── README.md │ ├── hangman.bas │ ├── java/ │ │ ├── Hangman.java │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── hangman.html │ │ └── hangman.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── hangman.pl │ ├── python/ │ │ ├── README.md │ │ └── hangman.py │ ├── ruby/ │ │ ├── README.md │ │ └── hangman.rb │ └── vbnet/ │ ├── Hangman.sln │ ├── Hangman.vbproj │ └── README.md ├── 45_Hello/ │ ├── README.md │ ├── csharp/ │ │ ├── Hello.csproj │ │ ├── Hello.sln │ │ └── README.md │ ├── hello.bas │ ├── java/ │ │ ├── Hello.java │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── hello.html │ │ └── hello.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ ├── README.md │ │ └── hello.lua │ ├── perl/ │ │ ├── README.md │ │ └── hello.pl │ ├── python/ │ │ ├── README.md │ │ └── hello.py │ ├── ruby/ │ │ ├── README.md │ │ └── hello.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── Hello.sln │ ├── Hello.vbproj │ └── README.md ├── 46_Hexapawn/ │ ├── README.md │ ├── csharp/ │ │ ├── Board.cs │ │ ├── Cell.cs │ │ ├── Computer.cs │ │ ├── Game.cs │ │ ├── GameSeries.cs │ │ ├── Hexapawn.csproj │ │ ├── Hexapawn.sln │ │ ├── Human.cs │ │ ├── IReadWriteExtensions.cs │ │ ├── Move.cs │ │ ├── Pawn.cs │ │ ├── Program.cs │ │ ├── README.md │ │ └── Resources/ │ │ ├── Instructions.txt │ │ ├── Resource.cs │ │ └── Title.txt │ ├── hexapawn.bas │ ├── java/ │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── hexapawn.html │ │ └── hexapawn.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ └── README.md │ ├── python/ │ │ ├── README.md │ │ └── hexapawn.py │ ├── ruby/ │ │ └── README.md │ └── vbnet/ │ ├── Hexapawn.sln │ ├── Hexapawn.vbproj │ └── README.md ├── 47_Hi-Lo/ │ ├── README.md │ ├── csharp/ │ │ ├── HiLo.csproj │ │ ├── HiLo.sln │ │ ├── Program.cs │ │ └── README.md │ ├── hi-lo.bas │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ ├── HiLo.java │ │ └── HiLoGame.java │ ├── javascript/ │ │ ├── README.md │ │ ├── hi-lo.html │ │ └── hi-lo.js │ ├── kotlin/ │ │ ├── HiLo.kt │ │ └── README.md │ ├── lua/ │ │ ├── README.md │ │ └── hilo.lua │ ├── perl/ │ │ ├── README.md │ │ └── hi-lo.pl │ ├── python/ │ │ ├── README.md │ │ └── hilo.py │ ├── ruby/ │ │ ├── README.md │ │ └── hi_lo.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── HiLo.sln │ ├── HiLo.vbproj │ └── README.md ├── 48_High_IQ/ │ ├── README.md │ ├── csharp/ │ │ ├── HighIQ.csproj │ │ ├── HighIQ.sln │ │ └── README.md │ ├── d/ │ │ ├── .gitignore │ │ ├── README.md │ │ └── highiq.d │ ├── highiq.bas │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ ├── HighIQ.java │ │ └── HighIQGame.java │ ├── javascript/ │ │ ├── README.md │ │ ├── highiq.html │ │ └── highiq.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ └── README.md │ ├── python/ │ │ ├── High_IQ.py │ │ └── README.md │ ├── ruby/ │ │ └── README.md │ └── vbnet/ │ ├── HighIQ.sln │ ├── HighIQ.vbproj │ └── README.md ├── 49_Hockey/ │ ├── README.md │ ├── csharp/ │ │ ├── Hockey.csproj │ │ ├── Hockey.sln │ │ └── README.md │ ├── hockey.bas │ ├── java/ │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── hockey.html │ │ └── hockey.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ └── README.md │ ├── python/ │ │ ├── README.md │ │ └── hockey.py │ ├── ruby/ │ │ └── README.md │ └── vbnet/ │ ├── Hockey.sln │ ├── Hockey.vbproj │ └── README.md ├── 50_Horserace/ │ ├── README.md │ ├── csharp/ │ │ ├── Horserace.csproj │ │ ├── Horserace.sln │ │ └── README.md │ ├── horserace.bas │ ├── java/ │ │ ├── Horserace.java │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── horserace.html │ │ └── horserace.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ └── README.md │ ├── python/ │ │ ├── README.md │ │ └── horserace.py │ ├── ruby/ │ │ └── README.md │ ├── rust/ │ │ ├── Cargo.toml │ │ └── src/ │ │ ├── game.rs │ │ ├── horses.rs │ │ ├── main.rs │ │ ├── players.rs │ │ └── util.rs │ └── vbnet/ │ ├── Horserace.sln │ ├── Horserace.vbproj │ └── README.md ├── 51_Hurkle/ │ ├── README.md │ ├── csharp/ │ │ ├── .gitignore │ │ ├── CardinalDirection.cs │ │ ├── ConsoleHurkleView.cs │ │ ├── FailedGuessViewModel.cs │ │ ├── GamePoint.cs │ │ ├── GuessViewModel.cs │ │ ├── Hurkle.csproj │ │ ├── Hurkle.sln │ │ ├── HurkleGame.cs │ │ ├── IHurkleView.cs │ │ ├── LossViewModel.cs │ │ ├── Program.cs │ │ ├── README.md │ │ └── VictoryViewModel.cs │ ├── hurkle.bas │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ ├── Hurkle.java │ │ └── HurkleGame.java │ ├── javascript/ │ │ ├── README.md │ │ ├── hurkle.html │ │ └── hurkle.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── hurkle.pl │ ├── python/ │ │ ├── README.md │ │ └── hurkle.py │ ├── ruby/ │ │ ├── README.md │ │ └── hurkle.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ └── src/ │ │ ├── game.rs │ │ └── main.rs │ └── vbnet/ │ ├── Hurkle.sln │ ├── Hurkle.vbproj │ └── README.md ├── 52_Kinema/ │ ├── README.md │ ├── csharp/ │ │ ├── Kinema.csproj │ │ ├── Kinema.sln │ │ └── README.md │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ ├── Kinema.java │ │ └── KinemaGame.java │ ├── javascript/ │ │ ├── README.md │ │ ├── kinema.html │ │ └── kinema.js │ ├── kinema.bas │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── kinema.pl │ ├── python/ │ │ ├── README.md │ │ └── kinema.py │ ├── ruby/ │ │ ├── README.md │ │ └── kinema.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── Kinema.sln │ ├── Kinema.vbproj │ └── README.md ├── 53_King/ │ ├── README.md │ ├── csharp/ │ │ ├── Country.cs │ │ ├── Game.cs │ │ ├── IOExtensions.cs │ │ ├── King.csproj │ │ ├── King.sln │ │ ├── Program.cs │ │ ├── README.md │ │ ├── Reign.cs │ │ ├── Resources/ │ │ │ ├── DeathsPollution.txt │ │ │ ├── DeathsStarvation.txt │ │ │ ├── Emigration.txt │ │ │ ├── EndAlso.txt │ │ │ ├── EndCongratulations.txt │ │ │ ├── EndConsequences.txt │ │ │ ├── EndForeignWorkers.txt │ │ │ ├── EndManyDead.txt │ │ │ ├── EndMoneyLeftOver.txt │ │ │ ├── EndOneThirdDead.txt │ │ │ ├── FuneralExpenses.txt │ │ │ ├── GiveRallodsError.txt │ │ │ ├── GiveRallodsPrompt.txt │ │ │ ├── Goodbye.txt │ │ │ ├── Harvest.txt │ │ │ ├── HarvestReason.txt │ │ │ ├── Immigration.txt │ │ │ ├── InstructionsPrompt.txt │ │ │ ├── InstructionsText.txt │ │ │ ├── InsufficientReserves.txt │ │ │ ├── LandPlanted.txt │ │ │ ├── PlantLandError1.txt │ │ │ ├── PlantLandError2.txt │ │ │ ├── PlantLandError3.txt │ │ │ ├── PlantLandPrompt.txt │ │ │ ├── PollutionError.txt │ │ │ ├── PollutionPrompt.txt │ │ │ ├── Resource.cs │ │ │ ├── SavedCountrymenPrompt.txt │ │ │ ├── SavedLandError.txt │ │ │ ├── SavedLandPrompt.txt │ │ │ ├── SavedTreasuryPrompt.txt │ │ │ ├── SavedWorkersPrompt.txt │ │ │ ├── SavedYearsError.txt │ │ │ ├── SavedYearsPrompt.txt │ │ │ ├── SellLandError.txt │ │ │ ├── SellLandErrorReason.txt │ │ │ ├── SellLandPrompt.txt │ │ │ ├── StatusSansWorkers.txt │ │ │ ├── StatusWithWorkers.txt │ │ │ ├── Title.txt │ │ │ ├── TourismDecrease.txt │ │ │ ├── TourismEarnings.txt │ │ │ ├── TourismReason.txt │ │ │ └── WorkerMigration.txt │ │ ├── Result.cs │ │ ├── ValidityTest.cs │ │ └── Year.cs │ ├── java/ │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── king.html │ │ └── king.js │ ├── king.bas │ ├── king_variable_update.bas │ ├── kotlin/ │ │ ├── King.kt │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ └── README.md │ ├── python/ │ │ ├── README.md │ │ ├── king.py │ │ └── king_variable_update.py │ ├── ruby/ │ │ └── README.md │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── King.sln │ ├── King.vbproj │ └── README.md ├── 54_Letter/ │ ├── README.md │ ├── csharp/ │ │ ├── Game.cs │ │ ├── GameState.cs │ │ ├── Letter.csproj │ │ ├── Letter.sln │ │ ├── Program.cs │ │ └── README.md │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ ├── Letter.java │ │ └── LetterGame.java │ ├── javascript/ │ │ ├── README.md │ │ ├── letter.html │ │ └── letter.js │ ├── kotlin/ │ │ └── README.md │ ├── letter.bas │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── letter.pl │ ├── python/ │ │ ├── README.md │ │ └── letter.py │ ├── ruby/ │ │ ├── README.md │ │ └── letter.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── Letter.sln │ ├── Letter.vbproj │ └── README.md ├── 55_Life/ │ ├── README.md │ ├── csharp/ │ │ ├── .gitignore │ │ ├── Life.csproj │ │ ├── Life.sln │ │ ├── Program.cs │ │ └── README.md │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ └── java/ │ │ └── Life.java │ ├── javascript/ │ │ ├── README.md │ │ ├── life.html │ │ └── life.js │ ├── kotlin/ │ │ └── README.md │ ├── life.bas │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── life.pl │ ├── python/ │ │ ├── README.md │ │ └── life.py │ ├── ruby/ │ │ ├── README.md │ │ └── life.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── Life.sln │ ├── Life.vbproj │ └── README.md ├── 56_Life_for_Two/ │ ├── README.md │ ├── csharp/ │ │ ├── Board.cs │ │ ├── Coordinates.cs │ │ ├── Game.cs │ │ ├── Generation.cs │ │ ├── IOExtensions.cs │ │ ├── Life.cs │ │ ├── LifeforTwo.csproj │ │ ├── LifeforTwo.sln │ │ ├── Piece.cs │ │ ├── Program.cs │ │ ├── README.md │ │ └── Resources/ │ │ ├── Draw.txt │ │ ├── IllegalCoords.txt │ │ ├── InitialPieces.txt │ │ ├── Player.txt │ │ ├── Resource.cs │ │ ├── SameCoords.txt │ │ ├── Title.txt │ │ └── Winner.txt │ ├── java/ │ │ ├── LifeForTwo.java │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── lifefortwo.html │ │ └── lifefortwo.js │ ├── kotlin/ │ │ └── README.md │ ├── lifefortwo.bas │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── lifefortwo.pl │ ├── python/ │ │ ├── README.md │ │ └── life_for_two.py │ ├── ruby/ │ │ └── README.md │ └── vbnet/ │ ├── LifeforTwo.sln │ ├── LifeforTwo.vbproj │ └── README.md ├── 57_Literature_Quiz/ │ ├── README.md │ ├── csharp/ │ │ ├── LiteratureQuiz.csproj │ │ ├── LiteratureQuiz.sln │ │ ├── README.md │ │ └── litquiz.cs │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ ├── LiteratureQuiz.java │ │ └── LiteratureQuizGame.java │ ├── javascript/ │ │ ├── README.md │ │ ├── litquiz.html │ │ ├── litquiz.js │ │ └── litquiz.mjs │ ├── kotlin/ │ │ └── README.md │ ├── litquiz.bas │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── litquiz.pl │ ├── python/ │ │ ├── README.md │ │ └── litquiz.py │ ├── ruby/ │ │ └── README.md │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── LiteratureQuiz.sln │ ├── LiteratureQuiz.vbproj │ └── README.md ├── 58_Love/ │ ├── README.md │ ├── csharp/ │ │ ├── Love.csproj │ │ ├── Love.sln │ │ ├── LovePattern.cs │ │ ├── Program.cs │ │ ├── README.md │ │ ├── Resources/ │ │ │ ├── Intro.txt │ │ │ └── Resource.cs │ │ ├── SourceCharacters.cs │ │ └── StringBuilderExtensions.cs │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ └── Love.java │ ├── javascript/ │ │ ├── README.md │ │ ├── love.html │ │ └── love.js │ ├── kotlin/ │ │ └── README.md │ ├── love.bas │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── love.pl │ ├── python/ │ │ ├── README.md │ │ └── love.py │ ├── ruby/ │ │ ├── README.md │ │ └── love.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── Love.sln │ ├── Love.vbproj │ └── README.md ├── 59_Lunar_LEM_Rocket/ │ ├── README.md │ ├── csharp/ │ │ ├── LunarLEMRocket.csproj │ │ ├── LunarLEMRocket.sln │ │ └── README.md │ ├── java/ │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── lem.html │ │ ├── lem.js │ │ ├── lunar.html │ │ └── lunar.js │ ├── kotlin/ │ │ └── README.md │ ├── lem.bas │ ├── lua/ │ │ └── README.md │ ├── lunar.bas │ ├── perl/ │ │ └── README.md │ ├── python/ │ │ ├── README.md │ │ └── lunar.py │ ├── rocket.bas │ ├── ruby/ │ │ └── README.md │ ├── rust/ │ │ ├── README.md │ │ └── rocket/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── LunarLEMRocket.sln │ ├── LunarLEMRocket.vbproj │ └── README.md ├── 60_Mastermind/ │ ├── README.md │ ├── csharp/ │ │ ├── Code.cs │ │ ├── CodeFactory.cs │ │ ├── ColorInfo.cs │ │ ├── Colors.cs │ │ ├── Command.cs │ │ ├── Controller.cs │ │ ├── EnumerableExtensions.cs │ │ ├── Mastermind.csproj │ │ ├── Mastermind.sln │ │ ├── Program.cs │ │ ├── README.md │ │ ├── TurnResult.cs │ │ └── View.cs │ ├── java/ │ │ ├── Mastermind.java │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── mastermind.html │ │ └── mastermind.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── mastermind.bas │ ├── perl/ │ │ ├── README.md │ │ └── mastermind.pl │ ├── python/ │ │ ├── README.md │ │ └── mastermind.py │ ├── ruby/ │ │ └── README.md │ ├── rust/ │ │ ├── Mastermind/ │ │ │ ├── Cargo.toml │ │ │ ├── README.md │ │ │ └── src/ │ │ │ └── main.rs │ │ └── Mastermind_refactored_for_conventions/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ ├── lib.rs │ │ └── main.rs │ └── vbnet/ │ ├── Mastermind.sln │ ├── Mastermind.vbproj │ └── README.md ├── 61_Math_Dice/ │ ├── README.md │ ├── csharp/ │ │ ├── GameState.cs │ │ ├── MathDice.csproj │ │ ├── MathDice.sln │ │ ├── Program.cs │ │ ├── README.md │ │ └── StringExtensions.cs │ ├── java/ │ │ ├── Die.java │ │ ├── MathDice.java │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── mathdice.html │ │ └── mathdice.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── mathdice.bas │ ├── perl/ │ │ ├── README.md │ │ └── mathdice.pl │ ├── python/ │ │ ├── README.md │ │ └── mathdice.py │ ├── ruby/ │ │ ├── README.md │ │ └── mathdice.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── MathDice.sln │ ├── MathDice.vbproj │ └── README.md ├── 62_Mugwump/ │ ├── README.md │ ├── csharp/ │ │ ├── Distance.cs │ │ ├── Game.cs │ │ ├── Grid.cs │ │ ├── IRandomExtensions.cs │ │ ├── Mugwump.cs │ │ ├── Mugwump.csproj │ │ ├── Mugwump.sln │ │ ├── Position.cs │ │ ├── Program.cs │ │ ├── README.md │ │ ├── Strings/ │ │ │ └── Intro.txt │ │ └── TextIOExtensions.cs │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ └── Mugwump.java │ ├── javascript/ │ │ ├── README.md │ │ ├── mugwump.html │ │ └── mugwump.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── mugwump.bas │ ├── perl/ │ │ ├── README.md │ │ └── mugwump.pl │ ├── python/ │ │ ├── README.md │ │ └── mugwump.py │ ├── ruby/ │ │ └── README.md │ ├── rust/ │ │ ├── Cargo.toml │ │ └── src/ │ │ ├── coordinate.rs │ │ ├── draw.rs │ │ ├── game.rs │ │ ├── main.rs │ │ └── util.rs │ └── vbnet/ │ ├── Mugwump.sln │ ├── Mugwump.vbproj │ └── README.md ├── 63_Name/ │ ├── README.md │ ├── csharp/ │ │ ├── Name.csproj │ │ ├── Name.sln │ │ ├── Program.cs │ │ ├── README.md │ │ └── StringExtensions.cs │ ├── java/ │ │ ├── Name.java │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── name.html │ │ └── name.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ ├── README.md │ │ └── name.lua │ ├── name.bas │ ├── perl/ │ │ ├── README.md │ │ └── name.pl │ ├── python/ │ │ ├── README.md │ │ └── name.py │ ├── ruby/ │ │ ├── README.md │ │ └── name.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── Name.sln │ ├── Name.vbproj │ └── README.md ├── 64_Nicomachus/ │ ├── README.md │ ├── csharp/ │ │ ├── Nicomachus.csproj │ │ ├── Nicomachus.sln │ │ ├── README.md │ │ └── program.cs │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ └── Nicomachus.java │ ├── javascript/ │ │ ├── README.md │ │ ├── nicomachus.html │ │ └── nicomachus.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── nicomachus.bas │ ├── perl/ │ │ ├── README.md │ │ └── nicomachus.pl │ ├── python/ │ │ ├── README.md │ │ └── nicomachus.py │ ├── ruby/ │ │ └── README.md │ ├── rust/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── Nicomachus.sln │ ├── Nicomachus.vbproj │ └── README.md ├── 65_Nim/ │ ├── README.md │ ├── csharp/ │ │ ├── Nim.csproj │ │ ├── Nim.sln │ │ └── README.md │ ├── java/ │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── nim.html │ │ └── nim.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── nim.bas │ ├── perl/ │ │ └── README.md │ ├── python/ │ │ ├── README.md │ │ └── Traditional_NIM.py │ ├── ruby/ │ │ ├── README.md │ │ └── nim.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── Nim.sln │ ├── Nim.vbproj │ └── README.md ├── 66_Number/ │ ├── README.md │ ├── csharp/ │ │ ├── Number.csproj │ │ ├── Number.sln │ │ ├── README.md │ │ └── program.cs │ ├── java/ │ │ ├── 1/ │ │ │ └── Number.java │ │ ├── 2/ │ │ │ └── Number.java │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── number.html │ │ └── number.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── number.bas │ ├── perl/ │ │ ├── README.md │ │ └── number.pl │ ├── python/ │ │ ├── README.md │ │ └── number.py │ ├── ruby/ │ │ └── README.md │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── Number.sln │ ├── Number.vbproj │ └── README.md ├── 67_One_Check/ │ ├── README.md │ ├── csharp/ │ │ ├── Board.cs │ │ ├── Game.cs │ │ ├── Move.cs │ │ ├── OneCheck.csproj │ │ ├── OneCheck.sln │ │ ├── Program.cs │ │ ├── README.md │ │ └── Resources/ │ │ ├── Bye.txt │ │ ├── From.txt │ │ ├── IllegalMove.txt │ │ ├── Introduction.txt │ │ ├── Resource.cs │ │ ├── Results.txt │ │ ├── To.txt │ │ ├── TryAgain.txt │ │ └── YesOrNo.txt │ ├── java/ │ │ ├── OneCheck.java │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── onecheck.html │ │ └── onecheck.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── onecheck.bas │ ├── perl/ │ │ ├── README.md │ │ └── onecheck.pl │ ├── python/ │ │ ├── README.md │ │ └── onecheck.py │ ├── ruby/ │ │ └── README.md │ └── vbnet/ │ ├── OneCheck.sln │ ├── OneCheck.vbproj │ └── README.md ├── 68_Orbit/ │ ├── README.md │ ├── csharp/ │ │ ├── Orbit.csproj │ │ ├── Orbit.sln │ │ ├── README.md │ │ └── program.cs │ ├── java/ │ │ ├── Orbit.java │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── orbit.html │ │ └── orbit.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── orbit.bas │ ├── perl/ │ │ ├── README.md │ │ └── orbit.pl │ ├── python/ │ │ ├── README.md │ │ └── orbit.py │ ├── ruby/ │ │ ├── README.md │ │ └── orbit.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ ├── lib.rs │ │ └── main.rs │ └── vbnet/ │ ├── Orbit.sln │ ├── Orbit.vbproj │ └── README.md ├── 69_Pizza/ │ ├── README.md │ ├── csharp/ │ │ ├── CustomerMap.cs │ │ ├── Pizza.csproj │ │ ├── Pizza.sln │ │ ├── PizzaGame.cs │ │ ├── Program.cs │ │ ├── README.md │ │ └── StringBuilderExtensions.cs │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ ├── Pizza.java │ │ └── PizzaGame.java │ ├── javascript/ │ │ ├── README.md │ │ ├── pizza.html │ │ └── pizza.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── pizza.pl │ ├── pizza.bas │ ├── python/ │ │ ├── README.md │ │ └── pizza.py │ ├── ruby/ │ │ ├── README.md │ │ └── pizza.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── Pizza.sln │ ├── Pizza.vbproj │ └── README.md ├── 70_Poetry/ │ ├── README.md │ ├── csharp/ │ │ ├── Context.cs │ │ ├── Phrase.cs │ │ ├── Poem.cs │ │ ├── Poetry.csproj │ │ ├── Poetry.sln │ │ ├── Program.cs │ │ ├── README.md │ │ └── Resources/ │ │ ├── Resource.cs │ │ └── Title.txt │ ├── java/ │ │ ├── Poetry.java │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── poetry.html │ │ └── poetry.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── poetry.pl │ ├── poetry.bas │ ├── python/ │ │ ├── README.md │ │ ├── poetry.pl │ │ └── poetry.py │ ├── ruby/ │ │ ├── README.md │ │ └── poetry.rb │ └── vbnet/ │ ├── Poetry.sln │ ├── Poetry.vbproj │ └── README.md ├── 71_Poker/ │ ├── README.md │ ├── csharp/ │ │ ├── Cards/ │ │ │ ├── Card.cs │ │ │ ├── Deck.cs │ │ │ ├── Hand.cs │ │ │ ├── HandRank.cs │ │ │ ├── Rank.cs │ │ │ └── Suit.cs │ │ ├── Game.cs │ │ ├── IReadWriteExtensions.cs │ │ ├── Players/ │ │ │ ├── Computer.cs │ │ │ ├── Human.cs │ │ │ └── Player.cs │ │ ├── Poker.csproj │ │ ├── Poker.sln │ │ ├── Program.cs │ │ ├── README.md │ │ ├── Resources/ │ │ │ ├── Instructions.txt │ │ │ ├── Resource.cs │ │ │ └── Title.txt │ │ ├── Strategies/ │ │ │ ├── Bet.cs │ │ │ ├── Bluff.cs │ │ │ ├── Check.cs │ │ │ ├── Fold.cs │ │ │ ├── None.cs │ │ │ ├── Raise.cs │ │ │ └── Strategy.cs │ │ └── Table.cs │ ├── java/ │ │ ├── Poker.java │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── poker.html │ │ └── poker.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ └── README.md │ ├── poker.bas │ ├── python/ │ │ └── README.md │ ├── ruby/ │ │ └── README.md │ └── vbnet/ │ ├── Poker.sln │ ├── Poker.vbproj │ └── README.md ├── 72_Queen/ │ ├── README.md │ ├── csharp/ │ │ ├── Computer.cs │ │ ├── Game.cs │ │ ├── IOExtensions.cs │ │ ├── Move.cs │ │ ├── Position.cs │ │ ├── Program.cs │ │ ├── Queen.csproj │ │ ├── Queen.sln │ │ ├── README.md │ │ ├── RandomExtensions.cs │ │ └── Resources/ │ │ ├── AnyonePrompt.txt │ │ ├── Board.txt │ │ ├── ComputerMove.txt │ │ ├── Congratulations.txt │ │ ├── Forfeit.txt │ │ ├── IWin.txt │ │ ├── IllegalMove.txt │ │ ├── IllegalStart.txt │ │ ├── Instructions.txt │ │ ├── InstructionsPrompt.txt │ │ ├── MovePrompt.txt │ │ ├── Resource.cs │ │ ├── StartPrompt.txt │ │ ├── Thanks.txt │ │ ├── Title.txt │ │ └── YesOrNo.txt │ ├── java/ │ │ ├── Queen.java │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── queen.html │ │ └── queen.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── queen.pl │ ├── python/ │ │ ├── .flake8 │ │ ├── README.md │ │ └── queen.py │ ├── queen.bas │ ├── ruby/ │ │ └── README.md │ ├── rust/ │ │ ├── Cargo.toml │ │ └── src/ │ │ ├── ai.rs │ │ ├── game.rs │ │ ├── main.rs │ │ └── util.rs │ └── vbnet/ │ ├── Queen.sln │ ├── Queen.vbproj │ └── README.md ├── 73_Reverse/ │ ├── README.md │ ├── csharp/ │ │ ├── README.md │ │ ├── Reverse/ │ │ │ ├── Program.cs │ │ │ ├── Reverse.csproj │ │ │ └── Reverser.cs │ │ ├── Reverse.Tests/ │ │ │ ├── Generators/ │ │ │ │ └── PositiveIntegerGenerator.cs │ │ │ ├── Reverse.Tests.csproj │ │ │ ├── ReverserTests.cs │ │ │ └── TestReverser.cs │ │ └── Reverse.sln │ ├── java/ │ │ ├── README.md │ │ └── Reverse.java │ ├── javascript/ │ │ ├── README.md │ │ ├── reverse.html │ │ └── reverse.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── reverse.pl │ ├── python/ │ │ ├── README.md │ │ └── reverse.py │ ├── reverse.bas │ ├── ruby/ │ │ ├── README.md │ │ └── reverse.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── README.md │ ├── Reverse.sln │ ├── Reverse.vb │ └── Reverse.vbproj ├── 74_Rock_Scissors_Paper/ │ ├── README.md │ ├── csharp/ │ │ ├── Choice.cs │ │ ├── Choices.cs │ │ ├── Game.cs │ │ ├── Program.cs │ │ ├── README.md │ │ ├── RockScissorsPaper.csproj │ │ └── RockScissorsPaper.sln │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ └── RockScissors.java │ ├── javascript/ │ │ ├── README.md │ │ └── rockscissors.mjs │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ ├── README.md │ │ └── rockscissors.lua │ ├── perl/ │ │ ├── README.md │ │ └── rockscissors.pl │ ├── python/ │ │ ├── README.md │ │ └── rockscissors.py │ ├── rockscissors.bas │ ├── ruby/ │ │ ├── README.md │ │ └── rockscissors.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── README.md │ ├── RockScissorsPaper.sln │ └── RockScissorsPaper.vbproj ├── 75_Roulette/ │ ├── README.md │ ├── csharp/ │ │ ├── Bet.cs │ │ ├── BetType.cs │ │ ├── Croupier.cs │ │ ├── Game.cs │ │ ├── IOExtensions.cs │ │ ├── Program.cs │ │ ├── README.md │ │ ├── Resources/ │ │ │ ├── AgainPrompt.txt │ │ │ ├── BetAlready.txt │ │ │ ├── BetPrompt.txt │ │ │ ├── BrokeHouse.txt │ │ │ ├── Check.txt │ │ │ ├── CheckPrompt.txt │ │ │ ├── HowManyBetsPrompt.txt │ │ │ ├── Instructions.txt │ │ │ ├── InstructionsPrompt.txt │ │ │ ├── LastDollar.txt │ │ │ ├── Outcome.txt │ │ │ ├── Resource.cs │ │ │ ├── Slot.txt │ │ │ ├── Spinning.txt │ │ │ ├── Thanks.txt │ │ │ ├── Title.txt │ │ │ └── Totals.txt │ │ ├── Roulette.csproj │ │ ├── Roulette.sln │ │ ├── Slot.cs │ │ ├── Table.cs │ │ └── Wheel.cs │ ├── java/ │ │ ├── README.md │ │ ├── iterative/ │ │ │ └── Roulette.java │ │ └── oop/ │ │ ├── Bet.java │ │ ├── Roulette.java │ │ └── Wheel.java │ ├── javascript/ │ │ ├── README.md │ │ ├── roulette.html │ │ └── roulette.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ ├── make-roulette-test.pl │ │ ├── roulette-test.t │ │ └── roulette.pl │ ├── python/ │ │ ├── README.md │ │ └── roulette.py │ ├── roulette.bas │ ├── ruby/ │ │ └── README.md │ ├── rust/ │ │ ├── Cargo.toml │ │ └── src/ │ │ ├── main.rs │ │ └── util.rs │ └── vbnet/ │ ├── README.md │ ├── Roulette.sln │ └── Roulette.vbproj ├── 76_Russian_Roulette/ │ ├── README.md │ ├── csharp/ │ │ ├── Program.cs │ │ ├── README.md │ │ ├── RussianRoulette.csproj │ │ └── RussianRoulette.sln │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ └── RussianRoulette.java │ ├── javascript/ │ │ ├── README.md │ │ ├── russianroulette.html │ │ └── russianroulette.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ ├── README.md │ │ └── russianroulette.lua │ ├── perl/ │ │ ├── README.md │ │ └── russianroulette.pl │ ├── python/ │ │ ├── README.md │ │ └── russianroulette.py │ ├── ruby/ │ │ ├── README.md │ │ └── russianroulette.rb │ ├── russianroulette.bas │ ├── rust/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── README.md │ ├── RussianRoulette.sln │ └── RussianRoulette.vbproj ├── 77_Salvo/ │ ├── README.md │ ├── csharp/ │ │ ├── Coordinate.cs │ │ ├── Extensions/ │ │ │ ├── IOExtensions.cs │ │ │ └── RandomExtensions.cs │ │ ├── Fleet.cs │ │ ├── Game.cs │ │ ├── Offset.cs │ │ ├── Position.cs │ │ ├── Program.cs │ │ ├── README.md │ │ ├── Resources/ │ │ │ ├── Coordinates.txt │ │ │ ├── IHaveMoreShotsThanSquares.txt │ │ │ ├── IHaveShots.txt │ │ │ ├── IHit.txt │ │ │ ├── IWon.txt │ │ │ ├── Illegal.txt │ │ │ ├── Resource.cs │ │ │ ├── SeeShots.txt │ │ │ ├── ShotBefore.txt │ │ │ ├── Start.txt │ │ │ ├── Title.txt │ │ │ ├── Turn.txt │ │ │ ├── WhereAreYourShips.txt │ │ │ ├── YouHaveMoreShotsThanSquares.txt │ │ │ ├── YouHaveShots.txt │ │ │ ├── YouHit.txt │ │ │ └── YouWon.txt │ │ ├── Salvo.csproj │ │ ├── Salvo.sln │ │ ├── Ships/ │ │ │ ├── Battleship.cs │ │ │ ├── Cruiser.cs │ │ │ ├── Destroyer.cs │ │ │ └── Ship.cs │ │ ├── Targetting/ │ │ │ ├── ComputerShotSelector.cs │ │ │ ├── HumanShotSelector.cs │ │ │ ├── KnownHitsShotSelectionStrategy.cs │ │ │ ├── SearchPattern.cs │ │ │ ├── SearchPatternShotSelector.cs │ │ │ ├── ShotSelectionStrategy.cs │ │ │ └── ShotSelector.cs │ │ ├── TurnHandler.cs │ │ └── Winner.cs │ ├── java/ │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── salvo.html │ │ └── salvo.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ └── README.md │ ├── python/ │ │ ├── README.md │ │ └── salvo.py │ ├── ruby/ │ │ └── README.md │ ├── salvo.bas │ └── vbnet/ │ ├── README.md │ ├── Salvo.sln │ └── Salvo.vbproj ├── 78_Sine_Wave/ │ ├── README.md │ ├── csharp/ │ │ ├── Program.cs │ │ ├── README.md │ │ ├── SineWave.csproj │ │ └── SineWave.sln │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ └── SineWave.java │ ├── javascript/ │ │ ├── README.md │ │ └── sinewave.mjs │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ ├── README.md │ │ └── sinewave.lua │ ├── perl/ │ │ ├── README.md │ │ └── sinewave.pl │ ├── python/ │ │ ├── README.md │ │ └── sinewave.py │ ├── ruby/ │ │ ├── README.md │ │ └── sinewave.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ └── main.rs │ ├── sinewave.bas │ └── vbnet/ │ ├── README.md │ ├── SineWave.sln │ └── SineWave.vbproj ├── 79_Slalom/ │ ├── README.md │ ├── csharp/ │ │ ├── README.md │ │ ├── Slalom.csproj │ │ ├── Slalom.sln │ │ └── program.cs │ ├── java/ │ │ ├── README.md │ │ └── Slalom.java │ ├── javascript/ │ │ ├── README.md │ │ ├── slalom.html │ │ └── slalom.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ └── README.md │ ├── python/ │ │ ├── README.md │ │ └── slalom.py │ ├── ruby/ │ │ └── README.md │ ├── slalom.bas │ └── vbnet/ │ ├── README.md │ ├── Slalom.sln │ └── Slalom.vbproj ├── 80_Slots/ │ ├── README.md │ ├── csharp/ │ │ ├── README.md │ │ ├── Slots.csproj │ │ ├── Slots.sln │ │ └── slots.csx │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ ├── Slots.java │ │ └── SlotsGame.java │ ├── javascript/ │ │ ├── README.md │ │ ├── slots.html │ │ └── slots.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── slots.pl │ ├── python/ │ │ ├── README.md │ │ └── slots.py │ ├── ruby/ │ │ ├── README.md │ │ └── slots.rb │ ├── slots.bas │ └── vbnet/ │ ├── README.md │ ├── Slots.sln │ └── Slots.vbproj ├── 81_Splat/ │ ├── README.md │ ├── csharp/ │ │ ├── Program.cs │ │ ├── README.md │ │ ├── Splat.csproj │ │ └── Splat.sln │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ └── Splat.java │ ├── javascript/ │ │ ├── README.md │ │ ├── splat.html │ │ └── splat.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── splat.pl │ ├── python/ │ │ ├── README.md │ │ └── splat.py │ ├── ruby/ │ │ └── README.md │ ├── rust/ │ │ ├── Cargo.toml │ │ └── src/ │ │ ├── celestial_body.rs │ │ ├── game.rs │ │ ├── main.rs │ │ ├── stats.rs │ │ └── utility.rs │ ├── splat.bas │ └── vbnet/ │ ├── README.md │ ├── Splat.sln │ └── Splat.vbproj ├── 82_Stars/ │ ├── README.md │ ├── csharp/ │ │ ├── Game.cs │ │ ├── Program.cs │ │ ├── README.md │ │ ├── Resources/ │ │ │ ├── Instructions.txt │ │ │ ├── Resource.cs │ │ │ └── Title.txt │ │ ├── Stars.csproj │ │ └── Stars.sln │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ ├── Stars.java │ │ └── StarsGame.java │ ├── javascript/ │ │ ├── README.md │ │ ├── stars.html │ │ └── stars.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── stars.pl │ ├── python/ │ │ ├── README.md │ │ └── stars.py │ ├── ruby/ │ │ ├── README.md │ │ └── stars.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ └── main.rs │ ├── rust_JWB/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ └── main.rs │ ├── stars.bas │ └── vbnet/ │ ├── README.md │ ├── Stars.sln │ └── Stars.vbproj ├── 83_Stock_Market/ │ ├── README.md │ ├── csharp/ │ │ ├── Assets.cs │ │ ├── Broker.cs │ │ ├── Company.cs │ │ ├── Controller.cs │ │ ├── Extensions/ │ │ │ ├── EnumerableExtensions.cs │ │ │ ├── ImmutableArrayExtensions.cs │ │ │ └── RandomExtensions.cs │ │ ├── Program.cs │ │ ├── README.md │ │ ├── StockMarket.cs │ │ ├── StockMarket.csproj │ │ ├── StockMarket.sln │ │ ├── TradingDay.cs │ │ ├── TransactionResult.cs │ │ └── View.cs │ ├── java/ │ │ ├── README.md │ │ └── StockMarket.java │ ├── javascript/ │ │ ├── README.md │ │ ├── stockmarket.html │ │ └── stockmarket.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ └── README.md │ ├── python/ │ │ ├── README.md │ │ └── Stock_Market.py │ ├── ruby/ │ │ └── README.md │ ├── stockmarket.bas │ └── vbnet/ │ ├── README.md │ ├── StockMarket.sln │ └── StockMarket.vbproj ├── 84_Super_Star_Trek/ │ ├── README.md │ ├── csharp/ │ │ ├── Commands/ │ │ │ ├── Command.cs │ │ │ ├── CommandExtensions.cs │ │ │ └── CommandResult.cs │ │ ├── Game.cs │ │ ├── IRandomExtensions.cs │ │ ├── IReadWriteExtensions.cs │ │ ├── Objects/ │ │ │ ├── Enterprise.cs │ │ │ ├── Klingon.cs │ │ │ ├── Star.cs │ │ │ └── Starbase.cs │ │ ├── Program.cs │ │ ├── README.md │ │ ├── Resources/ │ │ │ ├── CombatArea.txt │ │ │ ├── Congratulations.txt │ │ │ ├── CourtMartial.txt │ │ │ ├── Destroyed.txt │ │ │ ├── EndOfMission.txt │ │ │ ├── Enterprise.txt │ │ │ ├── Instructions.txt │ │ │ ├── LowShields.txt │ │ │ ├── NoEnemyShips.txt │ │ │ ├── NoStarbase.txt │ │ │ ├── NowEntering.txt │ │ │ ├── Orders.txt │ │ │ ├── PermissionDenied.txt │ │ │ ├── Protected.txt │ │ │ ├── RegionNames.txt │ │ │ ├── RelievedOfCommand.txt │ │ │ ├── RepairEstimate.txt │ │ │ ├── RepairPrompt.txt │ │ │ ├── ReplayPrompt.txt │ │ │ ├── ShieldsDropped.txt │ │ │ ├── ShieldsSet.txt │ │ │ ├── ShortRangeSensorsOut.txt │ │ │ ├── StartText.txt │ │ │ ├── Stranded.txt │ │ │ ├── Strings.cs │ │ │ └── Title.txt │ │ ├── Space/ │ │ │ ├── Coordinates.cs │ │ │ ├── Course.cs │ │ │ ├── Galaxy.cs │ │ │ ├── Quadrant.cs │ │ │ └── QuadrantInfo.cs │ │ ├── StringExtensions.cs │ │ ├── SuperStarTrek.csproj │ │ ├── SuperStarTrek.sln │ │ ├── Systems/ │ │ │ ├── ComputerFunctions/ │ │ │ │ ├── ComputerFunction.cs │ │ │ │ ├── CumulativeGalacticRecord.cs │ │ │ │ ├── DirectionDistanceCalculator.cs │ │ │ │ ├── GalacticReport.cs │ │ │ │ ├── GalaxyRegionMap.cs │ │ │ │ ├── NavigationCalculator.cs │ │ │ │ ├── StarbaseDataCalculator.cs │ │ │ │ ├── StatusReport.cs │ │ │ │ └── TorpedoDataCalculator.cs │ │ │ ├── DamageControl.cs │ │ │ ├── LibraryComputer.cs │ │ │ ├── LongRangeSensors.cs │ │ │ ├── PhaserControl.cs │ │ │ ├── PhotonTubes.cs │ │ │ ├── ShieldControl.cs │ │ │ ├── ShortRangeSensors.cs │ │ │ ├── Subsystem.cs │ │ │ └── WarpEngines.cs │ │ └── Utils/ │ │ └── DirectionAndDistance.cs │ ├── instructions.txt │ ├── java/ │ │ ├── Enterprise.java │ │ ├── GalaxyMap.java │ │ ├── GameCallback.java │ │ ├── README.md │ │ ├── SuperStarTrekGame.java │ │ ├── SuperStarTrekInstructions.java │ │ └── Util.java │ ├── javascript/ │ │ ├── README.md │ │ ├── cli.mjs │ │ ├── index.html │ │ ├── package.json │ │ └── superstartrek.mjs │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ └── README.md │ ├── python/ │ │ ├── README.md │ │ ├── superstartrek.py │ │ └── superstartrekins.py │ ├── ruby/ │ │ └── README.md │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── readme.md │ │ └── src/ │ │ ├── commands.rs │ │ ├── input.rs │ │ ├── main.rs │ │ ├── model.rs │ │ └── view.rs │ ├── superstartrek.bas │ ├── superstartrekins.bas │ └── vbnet/ │ ├── README.md │ ├── SuperStarTrek.sln │ └── SuperStarTrek.vbproj ├── 85_Synonym/ │ ├── README.md │ ├── csharp/ │ │ ├── README.md │ │ ├── Synonym.cs │ │ ├── Synonym.csproj │ │ └── Synonym.sln │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ ├── Synonym.java │ │ ├── SynonymGame.java │ │ └── SynonymList.java │ ├── javascript/ │ │ ├── README.md │ │ ├── synonym.html │ │ └── synonym.js │ ├── kotlin/ │ │ ├── README.md │ │ └── Synonym.kt │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── synonym.pl │ ├── python/ │ │ ├── README.md │ │ └── synonym.py │ ├── ruby/ │ │ ├── README.md │ │ └── synonim.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ └── main.rs │ ├── synonym.bas │ └── vbnet/ │ ├── README.md │ ├── Synonym.sln │ └── Synonym.vbproj ├── 86_Target/ │ ├── README.md │ ├── csharp/ │ │ ├── Angle.cs │ │ ├── Explosion.cs │ │ ├── FiringRange.cs │ │ ├── Game.cs │ │ ├── Offset.cs │ │ ├── Point.cs │ │ ├── Program.cs │ │ ├── README.md │ │ ├── RandomExtensions.cs │ │ ├── Strings/ │ │ │ └── TitleAndInstructions.txt │ │ ├── Target.csproj │ │ └── Target.sln │ ├── java/ │ │ ├── README.md │ │ └── Target.java │ ├── javascript/ │ │ ├── README.md │ │ ├── target.html │ │ └── target.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── target.pl │ ├── python/ │ │ ├── README.md │ │ └── target.py │ ├── ruby/ │ │ └── README.md │ ├── target.bas │ └── vbnet/ │ ├── README.md │ ├── Target.sln │ └── Target.vbproj ├── 87_3-D_Plot/ │ ├── 3dplot.bas │ ├── README.md │ ├── csharp/ │ │ ├── Function.cs │ │ ├── Plot.csproj │ │ ├── Plot.sln │ │ ├── Program.cs │ │ └── README.md │ ├── d/ │ │ ├── .gitignore │ │ ├── README.md │ │ ├── threedeeplot.d │ │ └── threedeeplot_random.d │ ├── java/ │ │ ├── Plot3D.java │ │ └── README.md │ ├── javascript/ │ │ ├── 3dplot.html │ │ ├── 3dplot.js │ │ └── README.md │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── 3dplot.pl │ │ └── README.md │ ├── python/ │ │ ├── 3dplot.py │ │ └── README.md │ ├── ruby/ │ │ ├── 3dplot.rb │ │ └── README.md │ ├── rust/ │ │ ├── Cargo.toml │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── Plot.sln │ ├── Plot.vbproj │ └── README.md ├── 88_3-D_Tic-Tac-Toe/ │ ├── README.md │ ├── csharp/ │ │ ├── Program.cs │ │ ├── Qubic.cs │ │ ├── QubicData.cs │ │ ├── README.md │ │ ├── ThreeDTicTacToe.csproj │ │ └── ThreeDTicTacToe.sln │ ├── java/ │ │ └── README.md │ ├── javascript/ │ │ ├── README.md │ │ ├── qubit.html │ │ └── qubit.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ └── README.md │ ├── python/ │ │ ├── README.md │ │ └── qubit.py │ ├── qubit.bas │ ├── ruby/ │ │ └── README.md │ └── vbnet/ │ ├── README.md │ ├── ThreeDTicTacToe.sln │ └── ThreeDTicTacToe.vbproj ├── 89_Tic-Tac-Toe/ │ ├── README.md │ ├── csharp/ │ │ ├── README.md │ │ ├── TicTacToe.sln │ │ ├── tictactoe1/ │ │ │ ├── Program.cs │ │ │ └── tictactoe1.csproj │ │ └── tictactoe2/ │ │ ├── Program.cs │ │ └── tictactoe2.csproj │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ ├── Board.java │ │ └── TicTacToe2.java │ ├── javascript/ │ │ ├── README.md │ │ ├── tictactoe1.html │ │ ├── tictactoe1.js │ │ ├── tictactoe2.html │ │ └── tictactoe2.js │ ├── kotlin/ │ │ ├── Board.kt │ │ ├── README.md │ │ └── TicTacToe2.kt │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── tictactoe2.pl │ ├── python/ │ │ ├── README.md │ │ ├── TicTacToe_Hard.py │ │ └── tictactoe2.py │ ├── ruby/ │ │ └── README.md │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ └── main.rs │ ├── tictactoe1.bas │ ├── tictactoe2.bas │ └── vbnet/ │ ├── README.md │ ├── TicTacToe.sln │ └── TicTacToe.vbproj ├── 90_Tower/ │ ├── README.md │ ├── csharp/ │ │ ├── Game.cs │ │ ├── Models/ │ │ │ ├── Needle.cs │ │ │ └── Towers.cs │ │ ├── Program.cs │ │ ├── README.md │ │ ├── Resources/ │ │ │ ├── Congratulations.txt │ │ │ ├── DiskCountPrompt.txt │ │ │ ├── DiskCountQuit.txt │ │ │ ├── DiskCountRetry.txt │ │ │ ├── DiskNotInPlay.txt │ │ │ ├── DiskPrompt.txt │ │ │ ├── DiskQuit.txt │ │ │ ├── DiskRetry.txt │ │ │ ├── DiskUnavailable.txt │ │ │ ├── IllegalMove.txt │ │ │ ├── Instructions.txt │ │ │ ├── Intro.txt │ │ │ ├── NeedlePrompt.txt │ │ │ ├── NeedleQuit.txt │ │ │ ├── NeedleRetry.txt │ │ │ ├── PlayAgainPrompt.txt │ │ │ ├── Strings.cs │ │ │ ├── TaskFinished.txt │ │ │ ├── Thanks.txt │ │ │ ├── Title.txt │ │ │ ├── TooManyMoves.txt │ │ │ └── YesNoPrompt.txt │ │ ├── Tower.csproj │ │ ├── Tower.sln │ │ └── UI/ │ │ ├── Input.cs │ │ ├── Prompt.cs │ │ └── TowerDisplay.cs │ ├── java/ │ │ ├── README.md │ │ └── Tower.java │ ├── javascript/ │ │ ├── README.md │ │ ├── tower.html │ │ └── tower.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ ├── README.md │ │ └── tower.lua │ ├── perl/ │ │ ├── README.md │ │ └── tower.pl │ ├── python/ │ │ ├── README.md │ │ └── tower.py │ ├── ruby/ │ │ └── README.md │ ├── rust/ │ │ ├── Cargo.toml │ │ └── src/ │ │ ├── disk.rs │ │ ├── game.rs │ │ ├── main.rs │ │ ├── needle.rs │ │ └── util.rs │ ├── tower.bas │ └── vbnet/ │ ├── README.md │ ├── Tower.sln │ └── Tower.vbproj ├── 91_Train/ │ ├── README.md │ ├── csharp/ │ │ ├── README.md │ │ ├── Train/ │ │ │ ├── TrainGame.cs │ │ │ └── TrainGame.csproj │ │ ├── Train.sln │ │ └── TrainTests/ │ │ ├── TrainGameTests.cs │ │ └── TrainTests.csproj │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ └── Train.java │ ├── javascript/ │ │ ├── README.md │ │ ├── train.html │ │ └── train.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ ├── README.md │ │ └── train.lua │ ├── perl/ │ │ ├── README.md │ │ └── train.pl │ ├── python/ │ │ ├── README.md │ │ └── train.py │ ├── ruby/ │ │ ├── README.md │ │ └── train.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ ├── lib.rs │ │ └── main.rs │ ├── train.bas │ └── vbnet/ │ ├── README.md │ ├── Train.sln │ └── Train.vbproj ├── 92_Trap/ │ ├── README.md │ ├── csharp/ │ │ ├── Program.cs │ │ ├── README.md │ │ ├── Trap.csproj │ │ └── Trap.sln │ ├── java/ │ │ ├── README.md │ │ └── src/ │ │ ├── Trap.java │ │ └── TrapGame.java │ ├── javascript/ │ │ ├── README.md │ │ ├── trap.html │ │ └── trap.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── trap.pl │ ├── python/ │ │ ├── README.md │ │ └── trap.py │ ├── ruby/ │ │ ├── README.md │ │ └── trap.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ └── main.rs │ ├── trap.bas │ └── vbnet/ │ ├── README.md │ ├── Trap.sln │ └── Trap.vbproj ├── 93_23_Matches/ │ ├── 23matches.bas │ ├── README.md │ ├── csharp/ │ │ ├── 23Matches.cs │ │ └── csharp.csproj │ ├── java/ │ │ ├── CoinSide.java │ │ ├── Messages.java │ │ ├── README.md │ │ ├── TwentyThreeMatches.java │ │ └── TwentyThreeMatchesGame.java │ ├── javascript/ │ │ ├── 23matches.html │ │ ├── 23matches.js │ │ └── README.md │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── 23matches.pl │ │ └── README.md │ ├── python/ │ │ ├── 23matches.py │ │ └── README.md │ ├── ruby/ │ │ ├── 23_matches.rb │ │ └── README.md │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ └── main.rs │ └── vbnet/ │ ├── README.md │ ├── TwentyThreeMatches.sln │ └── TwentyThreeMatches.vbproj ├── 94_War/ │ ├── README.md │ ├── csharp/ │ │ ├── README.md │ │ ├── War/ │ │ │ ├── Cards.cs │ │ │ ├── Program.cs │ │ │ ├── UserInterface.cs │ │ │ └── War.csproj │ │ ├── War.sln │ │ └── WarTester/ │ │ ├── Tests.cs │ │ └── WarTester.csproj │ ├── d/ │ │ ├── .gitignore │ │ ├── README.md │ │ └── war.d │ ├── java/ │ │ ├── README.md │ │ └── War.java │ ├── javascript/ │ │ ├── README.md │ │ ├── war.html │ │ └── war.js │ ├── kotlin/ │ │ ├── README.md │ │ └── War.kt │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── war.pl │ ├── python/ │ │ ├── README.md │ │ ├── cards.json │ │ └── war.py │ ├── ruby/ │ │ ├── README.md │ │ └── war.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ └── main.rs │ ├── vbnet/ │ │ ├── README.md │ │ ├── War.sln │ │ └── War.vbproj │ └── war.bas ├── 95_Weekday/ │ ├── README.md │ ├── csharp/ │ │ ├── Program.cs │ │ ├── README.md │ │ ├── Weekday.csproj │ │ └── Weekday.sln │ ├── java/ │ │ ├── README.md │ │ └── Weekday.java │ ├── javascript/ │ │ ├── README.md │ │ ├── weekday.html │ │ └── weekday.js │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── weekday.pl │ ├── python/ │ │ ├── README.md │ │ └── weekday.py │ ├── ruby/ │ │ └── README.md │ ├── rust/ │ │ ├── Cargo.toml │ │ ├── README.md │ │ └── src/ │ │ └── main.rs │ ├── vbnet/ │ │ ├── README.md │ │ ├── Weekday.sln │ │ └── Weekday.vbproj │ └── weekday.bas ├── 96_Word/ │ ├── README.md │ ├── csharp/ │ │ ├── Program.cs │ │ ├── README.md │ │ ├── word.csproj │ │ └── word.sln │ ├── d/ │ │ ├── .gitignore │ │ ├── README.md │ │ └── word.d │ ├── java/ │ │ ├── README.md │ │ └── Word.java │ ├── javascript/ │ │ ├── README.md │ │ └── word.mjs │ ├── kotlin/ │ │ └── README.md │ ├── lua/ │ │ └── README.md │ ├── perl/ │ │ ├── README.md │ │ └── word.pl │ ├── python/ │ │ ├── README.md │ │ └── word.py │ ├── ruby/ │ │ ├── README.md │ │ └── word.rb │ ├── rust/ │ │ ├── Cargo.toml │ │ └── src/ │ │ ├── main.rs │ │ ├── progress.rs │ │ └── word_game.rs │ ├── vbnet/ │ │ ├── Program.vb │ │ ├── README.md │ │ ├── word.sln │ │ └── word.vbproj │ └── word.bas ├── HOW_TO_RUN_THE_GAMES.md ├── LICENSE ├── README.md ├── index.html └── pyproject.toml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .coveragerc ================================================ [run] omit = *test* ================================================ FILE: .github/workflows/check-python.yml ================================================ name: Check Python code on: [push, pull_request] jobs: build: runs-on: ubuntu-latest strategy: matrix: python-version: ["3.10"] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip pip install -r 00_Utilities/python/ci-requirements.txt - name: Test with mypy run: | mypy . --exclude 79_Slalom --exclude 27_Civil_War --exclude 38_Fur_Trader --exclude 81_Splat --exclude 09_Battle --exclude 40_Gomoko --exclude 36_Flip_Flop --exclude 43_Hammurabi --exclude 04_Awari --exclude 78_Sine_Wave --exclude 77_Salvo --exclude 34_Digits --exclude 17_Bullfight --exclude 16_Bug - name: Test with flake8 run: | flake8 . --ignore E501,W503,E203,E731,B011,SIM119,SIM106 ================================================ FILE: .github/workflows/file-size.yml ================================================ name: File Size Check on: [push, pull_request] jobs: build: runs-on: ubuntu-latest strategy: matrix: python-version: ["3.10"] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Get changed files id: changed-files uses: tj-actions/changed-files@v18.4 - name: List all changed files run: | # MAXSIZE is 1 MB MAXSIZE=1000000 for FILENAME in ${{ steps.changed-files.outputs.all_changed_files }}; do FILESIZE=$(stat -c%s "$FILENAME") echo "Size of $FILENAME = $FILESIZE bytes." if (( FILESIZE > MAXSIZE)); then echo "$FILENAME is too big. Only $MAXSIZE bytes allowed." exit 1 fi done ================================================ FILE: .github/workflows/rust.yml ================================================ name: Rust on: push: branches: [ "main" ] pull_request: branches: [ "main" ] env: CARGO_TERM_COLOR: always jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Build All run: find */rust/Cargo.toml|xargs -I {} dirname {}|xargs -I {} bash -c 'cd {} && cargo build --verbose' - name: Run All tests run: find */rust/Cargo.toml|xargs -I {} dirname {}|xargs -I {} bash -c 'cd {} && cargo test --verbose' ================================================ FILE: .gitignore ================================================ .local/ .vscode/ .gradle/ node_modules/ buildJvm/bin buildJvm/*/build/ .classpath .project .settings .metadata *.iml *.ipr *.class */.vs *.suo bin/ obj/ .idea *.iws *.iml *.ipr out/ *.py[co] .python-version Pipfile venv/ .coverage .DS_Store .vs/ **/target/ **/*.rs.bk /target todo.md .fake .fake ================================================ FILE: .nojekyll ================================================ ================================================ FILE: .pre-commit-config.yaml ================================================ # pre-commit run --all-files repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.1.0 hooks: - id: check-ast - id: check-case-conflict - id: check-docstring-first - id: check-executables-have-shebangs - id: check-json - id: check-yaml - id: debug-statements - id: end-of-file-fixer - id: trailing-whitespace - id: mixed-line-ending - id: check-added-large-files args: ['--maxkb=1000'] - repo: https://github.com/pre-commit/mirrors-isort rev: v5.10.1 hooks: - id: isort - repo: https://github.com/psf/black rev: 22.1.0 hooks: - id: black - repo: https://github.com/asottile/pyupgrade rev: v2.31.1 hooks: - id: pyupgrade args: [--py37-plus] - repo: https://github.com/asottile/blacken-docs rev: v1.12.1 hooks: - id: blacken-docs additional_dependencies: [black==22.1.0] ================================================ FILE: 00_Alternate_Languages/01_Acey_Ducey/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/01_Acey_Ducey/MiniScript/aceyducey.ms ================================================ 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!" ================================================ FILE: 00_Alternate_Languages/01_Acey_Ducey/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. #### External Links - Common Lisp: https://github.com/koalahedron/lisp-computer-games/blob/master/01%20Acey%20Ducey/common-lisp/acey-deucy.lisp - PowerShell: https://github.com/eweilnau/basic-computer-games-powershell/blob/main/AceyDucey.ps1 ================================================ FILE: 00_Alternate_Languages/01_Acey_Ducey/aceyducey.bas ================================================ 10 PRINT TAB(26);"ACEY DUCEY CARD GAME" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 21 PRINT 22 PRINT 30 PRINT"ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER " 40 PRINT"THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP" 50 PRINT"YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING" 60 PRINT"ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE" 70 PRINT"A VALUE BETWEEN THE FIRST TWO." 80 PRINT"IF YOU DO NOT WANT TO BET, INPUT A 0" 100 N=100 110 Q=100 120 PRINT "YOU NOW HAVE ";Q;" DOLLARS." 130 PRINT 140 GOTO 260 210 Q=Q+M 220 GOTO 120 240 Q=Q-M 250 GOTO 120 260 PRINT"HERE ARE YOUR NEXT TWO CARDS: " 270 A=INT(14*RND(1))+2 280 IF A<2 THEN 270 290 IF A>14 THEN 270 300 B=INT(14*RND(1))+2 310 IF B<2 THEN 300 320 IF B>14 THEN 300 330 IF A>=B THEN 270 350 IF A<11 THEN 400 360 IF A=11 THEN 420 370 IF A=12 THEN 440 380 IF A=13 THEN 460 390 IF A=14 THEN 480 400 PRINT A 410 GOTO 500 420 PRINT"JACK" 430 GOTO 500 440 PRINT"QUEEN" 450 GOTO 500 460 PRINT"KING" 470 GOTO 500 480 PRINT"ACE" 500 IF B<11 THEN 550 510 IF B=11 THEN 570 520 IF B=12 THEN 590 530 IF B=13 THEN 610 540 IF B=14 THEN 630 550 PRINT B 560 GOTO 650 570 PRINT"JACK" 580 GOTO 650 590 PRINT"QUEEN" 600 GOTO 650 610 PRINT"KING" 620 GOTO 650 630 PRINT"ACE" 640 PRINT 650 PRINT 660 INPUT"WHAT IS YOUR BET";M 670 IF M<>0 THEN 680 675 PRINT"CHICKEN!!" 676 PRINT 677 GOTO 260 680 IF M<=Q THEN 730 690 PRINT"SORRY, MY FRIEND, BUT YOU BET TOO MUCH." 700 PRINT"YOU HAVE ONLY ";Q;" DOLLARS TO BET." 710 GOTO 650 730 C=INT(14*RND(1))+2 740 IF C<2 THEN 730 750 IF C>14 THEN 730 760 IF C<11 THEN 810 770 IF C=11 THEN 830 780 IF C=12 THEN 850 790 IF C=13 THEN 870 800 IF C=14 THEN 890 810 PRINT C 820 GOTO 910 830 PRINT"JACK" 840 GOTO 910 850 PRINT"QUEEN" 860 GOTO 910 870 PRINT"KING" 880 GOTO 910 890 PRINT "ACE" 900 PRINT 910 IF C>A THEN 930 920 GOTO 970 930 IF C>=B THEN 970 950 PRINT"YOU WIN!!!" 960 GOTO 210 970 PRINT"SORRY, YOU LOSE" 980 IF M #include #include "Aceyducey.h" int main() { //Setting Seed for the Random Generator srand((unsigned int)time(NULL)); bool isPlaying(true); Money = 100; WelcomeMessage(); while (isPlaying) { Play(isPlaying); } printf("O.K., HOPE YOU HAD FUN!\n"); } void WelcomeMessage() { for (int i = 0; i < 25; i++) { printf(" "); } printf("ACEY DUCEY CARD GAME\n"); for (int i = 0; i < 14; i++) { printf(" "); } printf("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\nACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER \n"); printf("THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP\nYOU HAVE AN OPTION TO BET OR NOT BET DEPENDING\n"); printf("ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE\nA VALUE BETWEEN THE FIRST TWO.\n"); printf("IF YOU DO NOT WANT TO BET, INPUT A 0\n"); } void Play(bool& isPlaying) { short int DealerCards[2]; int Bet; short int CurrentCard; printf("YOU NOW HAVE %d DOLLARS.\n\n", Money); printf("HERE ARE YOUR NEXT TWO CARDS: \n"); //Draw Dealers Cards DrawCard(DealerCards[0]); printf("\n"); DrawCard(DealerCards[1]); printf("\n\n\n"); //Check if Bet is Valid do { printf("WHAT IS YOUR BET: "); std::cin >> Bet; if (Bet == 0) { printf("CHICKEN!!\n\n"); } } while (Bet > Money || Bet < 0); //Draw Players Card DrawCard(CurrentCard); printf("\n"); if (CurrentCard > DealerCards[0] && CurrentCard < DealerCards[1]) { printf("YOU WIN!!!\n"); Money += Bet; return; } else { printf("SORRY, YOU LOSE\n"); Money -= Bet; } if (isGameOver()) { printf("TRY AGAIN (YES OR NO)\n\n"); std::string response; std::cin >> response; if (response != "YES") { isPlaying = false; } Money = 100; } } bool isGameOver() { if (Money <= 0) { printf("\n\n"); printf("SORRY, FRIEND, BUT YOU BLEW YOUR WAD.\n\n"); return true; } return false; } void DrawCard(short int& Card) { //Basically generate 2 numbers first one is between 2-11 and second one 0-3 short int RandomNum1 = (rand() % 10) + 2; short int RandomNum2 = rand() % 4; Card = RandomNum1 + RandomNum2; switch (Card) { case 11: printf("JACK"); break; case 12: printf("QUEEN"); break; case 13: printf("KING"); break; case 14: printf("ACE"); break; default: printf("%d", Card); } } ================================================ FILE: 00_Alternate_Languages/01_Acey_Ducey/c++/source/Aceyducey.h ================================================ #pragma once void WelcomeMessage(); void DrawCard(short int& Card); void Play(bool& isPlaying); bool isGameOver(); int Money; ================================================ FILE: 00_Alternate_Languages/01_Acey_Ducey/d/.gitignore ================================================ *.exe *.obj ================================================ FILE: 00_Alternate_Languages/01_Acey_Ducey/d/README.md ================================================ Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html) Converted to [D](https://dlang.org/) by [Bastiaan Veelo](https://github.com/veelo). Two versions are supplied that are functionally equivalent, but differ in source layout:
aceyducey_literal.d
A largely literal transcription of the original Basic source. All unnecessary uglyness is preserved.
aceyducey.d
An idiomatic D refactoring of the original, with a focus on increasing the readability and robustness. Memory-safety is ensured by the language, thanks to the @safe annotation.
## Running the code Assuming the reference [dmd](https://dlang.org/download.html#dmd) compiler: ```shell dmd -run aceyducey.d ``` [Other compilers](https://dlang.org/download.html) also exist. Note that there are compiler switches related to memory-safety (`-preview=dip25` and `-preview=dip1000`) that are not used here because they are unnecessary in this case. What these do is to make the analysis more thorough, so that with them some code that needed to be `@system` can then be inferred to be in fact `@safe`. [Code that compiles without these switches is just as safe as when compiled with them] (https://forum.dlang.org/post/dftgjalswvwfjpyushgn@forum.dlang.org). ================================================ FILE: 00_Alternate_Languages/01_Acey_Ducey/d/aceyducey.d ================================================ @safe: // Make @safe the default for this file, enforcing memory-safety. void main() { import std.stdio : write, writeln; import std.string : center, toUpper, wrap; import std.exception : ifThrown; enum width = 80; writeln(center("Acey Ducey Card Game", width)); writeln(center("(After Creative Computing Morristown, New Jersey)\n", width)); writeln(wrap("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 third card will " ~ "have a value between the first two. If you do not want to bet, input a 0.", width)); enum Hand {low, middle, high} Card[Hand.max + 1] cards; // Three cards. bool play = true; while (play) { int cash = 100; while (cash > 0) { writeln("\nYou now have ", cash, " dollars."); int bet = 0; while (bet <= 0) { do // Draw new cards, until the first card has a smaller value than the last card. { foreach (ref card; cards) card.drawNew; } while (cards[Hand.low] >= cards[Hand.high]); writeln("Here are your next two cards:\n", cards[Hand.low], "\n", cards[Hand.high]); int askBet() // A nested function. { import std.conv : to; write("\nWhat is your bet? "); int answer = readString.to!int. ifThrown!Exception(askBet); // Try again when answer does not convert to int. if (answer <= cash) return answer; writeln("Sorry, my friend, but you bet too much.\nYou have only ", cash, " dollars to bet."); return askBet; // Recurse: Ask again. } bet = askBet; if (bet <= 0) // Negative bets are interpreted as 0. writeln("CHICKEN!!"); } // bet is now > 0. writeln(cards[Hand.middle]); if (cards[Hand.low] < cards[Hand.middle] && cards[Hand.middle] < cards[Hand.high]) { writeln("YOU WIN!!!"); cash += bet; } else { writeln("Sorry, you lose."); cash -= bet; if (cash <= 0) { writeln("\n\nSorry, friend, but you blew your wad."); write("\n\nTry again (Yes or No)? "); play = readString.toUpper == "YES"; } } } } writeln("O.K., hope you had fun!"); } struct Card { int value = 2; alias value this; // Enables Card to stand in as an int, so that cards can be compared as ints. invariant { assert(2 <= value && value <= 14); // Ensure cards always have a valid value. } /// Adopt a new value. void drawNew() { import std.random : uniform; value = uniform!("[]", int, int)(2, 14); // A random int between inclusive bounds. } /// Called for implicit conversion to string. string toString() const pure { import std.conv : text; switch (value) { case 11: return "Jack"; case 12: return "Queen"; case 13: return "King"; case 14: return "Ace"; default: return text(" ", value); // Basic prepends a space. } } } /// Read a string from standard input, stripping newline and other enclosing whitespace. string readString() nothrow { import std.string : strip; try return trustedReadln.strip; catch (Exception) // readln throws on I/O and Unicode errors, which we handle here. return ""; } /** An @trusted wrapper around readln. * * This is the only function that formally requires manual review for memory-safety. * [Arguably readln should be safe already](https://forum.dlang.org/post/rab398$1up$1@digitalmars.com) * which would remove the need to have any @trusted code in this program. */ string trustedReadln() @trusted { import std.stdio : readln; return readln; } ================================================ FILE: 00_Alternate_Languages/01_Acey_Ducey/d/aceyducey_literal.d ================================================ void main() { import std; L10: writef("%26s", ' '); writeln("ACEY DUCEY CARD GAME"); L20: writef("%15s", ' '); writeln("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); L21: writeln; L22: writeln; L30: writeln("ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER "); L40: writeln("THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP"); L50: writeln("YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING"); L60: writeln("ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE"); L70: writeln("A VALUE BETWEEN THE FIRST TWO."); L80: writeln("IF YOU DO NOT WANT TO BET, INPUT A 0"); L100: int N=100; L110: int Q=100, M; L120: writeln("YOU NOW HAVE ",Q," DOLLARS."); L130: writeln; L140: goto L260; L210: Q=Q+M; L220: goto L120; L240: Q=Q-M; L250: goto L120; L260: writeln("HERE ARE YOUR NEXT TWO CARDS: "); L270: auto A=to!int(14*uniform01)+2; L280: if (A<2) goto L270; L290: if (A>14) goto L270; L300: auto B=to!int(14*uniform01)+2; L310: if (B<2) goto L300; L320: if (B>14) goto L300; L330: if (A>=B) goto L270; L350: if (A<11) goto L400; L360: if (A==11) goto L420; L370: if (A==12) goto L440; L380: if (A==13) goto L460; L390: if (A==14) goto L480; L400: writefln("%2d", A); L410: goto L500; L420: writeln("JACK"); L430: goto L500; L440: writeln("QUEEN"); L450: goto L500; L460: writeln("KING"); L470: goto L500; L480: writeln("ACE"); L500: if (B<11) goto L550; L510: if (B==11) goto L570; L520: if (B==12) goto L590; L530: if (B==13) goto L610; L540: if (B==14) goto L630; L550: writefln("%2d", B); L560: goto L650; L570: writeln("JACK"); L580: goto L650; L590: writeln("QUEEN"); L600: goto L650; L610: writeln("KING"); L620: goto L650; L630: writeln("ACE"); L640: writeln; L650: writeln; L660: write("WHAT IS YOUR BET? "); M = stdin.readln.strip.to!int; L670: if (M!=0) goto L680; L675: writeln("CHICKEN!!"); L676: writeln; L677: goto L260; L680: if (M<=Q) goto L730; L690: writeln("SORRY, MY FRIEND, BUT YOU BET TOO MUCH."); L700: writeln("YOU HAVE ONLY ",Q," DOLLARS TO BET."); L710: goto L650; L730: auto C=to!int(14*uniform01)+2; L740: if (C<2) goto L730; L750: if (C>14) goto L730; L760: if (C<11) goto L810; L770: if (C==11) goto L830; L780: if (C==12) goto L850; L790: if (C==13) goto L870; L800: if (C==14) goto L890; L810: writeln(C); L820: goto L910; L830: writeln("JACK"); L840: goto L910; L850: writeln("QUEEN"); L860: goto L910; L870: writeln("KING"); L880: goto L910; L890: writeln( "ACE"); L900: writeln; L910: if (C>A) goto L930; L920: goto L970; L930: if (C>=B) goto L970; L950: writeln("YOU WIN!!!"); L960: goto L210; L970: writeln("SORRY, YOU LOSE"); L980: if (M 100) { stack.push(_Utils_Tuple2(x,y)); return true; } /**_UNUSED/ if (x.$ === 'Set_elm_builtin') { x = $elm$core$Set$toList(x); y = $elm$core$Set$toList(y); } if (x.$ === 'RBNode_elm_builtin' || x.$ === 'RBEmpty_elm_builtin') { x = $elm$core$Dict$toList(x); y = $elm$core$Dict$toList(y); } //*/ /**/ if (x.$ < 0) { x = $elm$core$Dict$toList(x); y = $elm$core$Dict$toList(y); } //*/ for (var key in x) { if (!_Utils_eqHelp(x[key], y[key], depth + 1, stack)) { return false; } } return true; } var _Utils_equal = F2(_Utils_eq); var _Utils_notEqual = F2(function(a, b) { return !_Utils_eq(a,b); }); // COMPARISONS // Code in Generate/JavaScript.hs, Basics.js, and List.js depends on // the particular integer values assigned to LT, EQ, and GT. function _Utils_cmp(x, y, ord) { if (typeof x !== 'object') { return x === y ? /*EQ*/ 0 : x < y ? /*LT*/ -1 : /*GT*/ 1; } /**_UNUSED/ if (x instanceof String) { var a = x.valueOf(); var b = y.valueOf(); return a === b ? 0 : a < b ? -1 : 1; } //*/ /**/ if (typeof x.$ === 'undefined') //*/ /**_UNUSED/ if (x.$[0] === '#') //*/ { return (ord = _Utils_cmp(x.a, y.a)) ? ord : (ord = _Utils_cmp(x.b, y.b)) ? ord : _Utils_cmp(x.c, y.c); } // traverse conses until end of a list or a mismatch for (; x.b && y.b && !(ord = _Utils_cmp(x.a, y.a)); x = x.b, y = y.b) {} // WHILE_CONSES return ord || (x.b ? /*GT*/ 1 : y.b ? /*LT*/ -1 : /*EQ*/ 0); } var _Utils_lt = F2(function(a, b) { return _Utils_cmp(a, b) < 0; }); var _Utils_le = F2(function(a, b) { return _Utils_cmp(a, b) < 1; }); var _Utils_gt = F2(function(a, b) { return _Utils_cmp(a, b) > 0; }); var _Utils_ge = F2(function(a, b) { return _Utils_cmp(a, b) >= 0; }); var _Utils_compare = F2(function(x, y) { var n = _Utils_cmp(x, y); return n < 0 ? $elm$core$Basics$LT : n ? $elm$core$Basics$GT : $elm$core$Basics$EQ; }); // COMMON VALUES var _Utils_Tuple0 = 0; var _Utils_Tuple0_UNUSED = { $: '#0' }; function _Utils_Tuple2(a, b) { return { a: a, b: b }; } function _Utils_Tuple2_UNUSED(a, b) { return { $: '#2', a: a, b: b }; } function _Utils_Tuple3(a, b, c) { return { a: a, b: b, c: c }; } function _Utils_Tuple3_UNUSED(a, b, c) { return { $: '#3', a: a, b: b, c: c }; } function _Utils_chr(c) { return c; } function _Utils_chr_UNUSED(c) { return new String(c); } // RECORDS function _Utils_update(oldRecord, updatedFields) { var newRecord = {}; for (var key in oldRecord) { newRecord[key] = oldRecord[key]; } for (var key in updatedFields) { newRecord[key] = updatedFields[key]; } return newRecord; } // APPEND var _Utils_append = F2(_Utils_ap); function _Utils_ap(xs, ys) { // append Strings if (typeof xs === 'string') { return xs + ys; } // append Lists if (!xs.b) { return ys; } var root = _List_Cons(xs.a, ys); xs = xs.b for (var curr = root; xs.b; xs = xs.b) // WHILE_CONS { curr = curr.b = _List_Cons(xs.a, ys); } return root; } var _List_Nil = { $: 0 }; var _List_Nil_UNUSED = { $: '[]' }; function _List_Cons(hd, tl) { return { $: 1, a: hd, b: tl }; } function _List_Cons_UNUSED(hd, tl) { return { $: '::', a: hd, b: tl }; } var _List_cons = F2(_List_Cons); function _List_fromArray(arr) { var out = _List_Nil; for (var i = arr.length; i--; ) { out = _List_Cons(arr[i], out); } return out; } function _List_toArray(xs) { for (var out = []; xs.b; xs = xs.b) // WHILE_CONS { out.push(xs.a); } return out; } var _List_map2 = F3(function(f, xs, ys) { for (var arr = []; xs.b && ys.b; xs = xs.b, ys = ys.b) // WHILE_CONSES { arr.push(A2(f, xs.a, ys.a)); } return _List_fromArray(arr); }); var _List_map3 = F4(function(f, xs, ys, zs) { for (var arr = []; xs.b && ys.b && zs.b; xs = xs.b, ys = ys.b, zs = zs.b) // WHILE_CONSES { arr.push(A3(f, xs.a, ys.a, zs.a)); } return _List_fromArray(arr); }); var _List_map4 = F5(function(f, ws, xs, ys, zs) { for (var arr = []; ws.b && xs.b && ys.b && zs.b; ws = ws.b, xs = xs.b, ys = ys.b, zs = zs.b) // WHILE_CONSES { arr.push(A4(f, ws.a, xs.a, ys.a, zs.a)); } return _List_fromArray(arr); }); var _List_map5 = F6(function(f, vs, ws, xs, ys, zs) { for (var arr = []; vs.b && ws.b && xs.b && ys.b && zs.b; vs = vs.b, ws = ws.b, xs = xs.b, ys = ys.b, zs = zs.b) // WHILE_CONSES { arr.push(A5(f, vs.a, ws.a, xs.a, ys.a, zs.a)); } return _List_fromArray(arr); }); var _List_sortBy = F2(function(f, xs) { return _List_fromArray(_List_toArray(xs).sort(function(a, b) { return _Utils_cmp(f(a), f(b)); })); }); var _List_sortWith = F2(function(f, xs) { return _List_fromArray(_List_toArray(xs).sort(function(a, b) { var ord = A2(f, a, b); return ord === $elm$core$Basics$EQ ? 0 : ord === $elm$core$Basics$LT ? -1 : 1; })); }); var _JsArray_empty = []; function _JsArray_singleton(value) { return [value]; } function _JsArray_length(array) { return array.length; } var _JsArray_initialize = F3(function(size, offset, func) { var result = new Array(size); for (var i = 0; i < size; i++) { result[i] = func(offset + i); } return result; }); var _JsArray_initializeFromList = F2(function (max, ls) { var result = new Array(max); for (var i = 0; i < max && ls.b; i++) { result[i] = ls.a; ls = ls.b; } result.length = i; return _Utils_Tuple2(result, ls); }); var _JsArray_unsafeGet = F2(function(index, array) { return array[index]; }); var _JsArray_unsafeSet = F3(function(index, value, array) { var length = array.length; var result = new Array(length); for (var i = 0; i < length; i++) { result[i] = array[i]; } result[index] = value; return result; }); var _JsArray_push = F2(function(value, array) { var length = array.length; var result = new Array(length + 1); for (var i = 0; i < length; i++) { result[i] = array[i]; } result[length] = value; return result; }); var _JsArray_foldl = F3(function(func, acc, array) { var length = array.length; for (var i = 0; i < length; i++) { acc = A2(func, array[i], acc); } return acc; }); var _JsArray_foldr = F3(function(func, acc, array) { for (var i = array.length - 1; i >= 0; i--) { acc = A2(func, array[i], acc); } return acc; }); var _JsArray_map = F2(function(func, array) { var length = array.length; var result = new Array(length); for (var i = 0; i < length; i++) { result[i] = func(array[i]); } return result; }); var _JsArray_indexedMap = F3(function(func, offset, array) { var length = array.length; var result = new Array(length); for (var i = 0; i < length; i++) { result[i] = A2(func, offset + i, array[i]); } return result; }); var _JsArray_slice = F3(function(from, to, array) { return array.slice(from, to); }); var _JsArray_appendN = F3(function(n, dest, source) { var destLen = dest.length; var itemsToCopy = n - destLen; if (itemsToCopy > source.length) { itemsToCopy = source.length; } var size = destLen + itemsToCopy; var result = new Array(size); for (var i = 0; i < destLen; i++) { result[i] = dest[i]; } for (var i = 0; i < itemsToCopy; i++) { result[i + destLen] = source[i]; } return result; }); // LOG var _Debug_log = F2(function(tag, value) { return value; }); var _Debug_log_UNUSED = F2(function(tag, value) { console.log(tag + ': ' + _Debug_toString(value)); return value; }); // TODOS function _Debug_todo(moduleName, region) { return function(message) { _Debug_crash(8, moduleName, region, message); }; } function _Debug_todoCase(moduleName, region, value) { return function(message) { _Debug_crash(9, moduleName, region, value, message); }; } // TO STRING function _Debug_toString(value) { return ''; } function _Debug_toString_UNUSED(value) { return _Debug_toAnsiString(false, value); } function _Debug_toAnsiString(ansi, value) { if (typeof value === 'function') { return _Debug_internalColor(ansi, ''); } if (typeof value === 'boolean') { return _Debug_ctorColor(ansi, value ? 'True' : 'False'); } if (typeof value === 'number') { return _Debug_numberColor(ansi, value + ''); } if (value instanceof String) { return _Debug_charColor(ansi, "'" + _Debug_addSlashes(value, true) + "'"); } if (typeof value === 'string') { return _Debug_stringColor(ansi, '"' + _Debug_addSlashes(value, false) + '"'); } if (typeof value === 'object' && '$' in value) { var tag = value.$; if (typeof tag === 'number') { return _Debug_internalColor(ansi, ''); } if (tag[0] === '#') { var output = []; for (var k in value) { if (k === '$') continue; output.push(_Debug_toAnsiString(ansi, value[k])); } return '(' + output.join(',') + ')'; } if (tag === 'Set_elm_builtin') { return _Debug_ctorColor(ansi, 'Set') + _Debug_fadeColor(ansi, '.fromList') + ' ' + _Debug_toAnsiString(ansi, $elm$core$Set$toList(value)); } if (tag === 'RBNode_elm_builtin' || tag === 'RBEmpty_elm_builtin') { return _Debug_ctorColor(ansi, 'Dict') + _Debug_fadeColor(ansi, '.fromList') + ' ' + _Debug_toAnsiString(ansi, $elm$core$Dict$toList(value)); } if (tag === 'Array_elm_builtin') { return _Debug_ctorColor(ansi, 'Array') + _Debug_fadeColor(ansi, '.fromList') + ' ' + _Debug_toAnsiString(ansi, $elm$core$Array$toList(value)); } if (tag === '::' || tag === '[]') { var output = '['; value.b && (output += _Debug_toAnsiString(ansi, value.a), value = value.b) for (; value.b; value = value.b) // WHILE_CONS { output += ',' + _Debug_toAnsiString(ansi, value.a); } return output + ']'; } var output = ''; for (var i in value) { if (i === '$') continue; var str = _Debug_toAnsiString(ansi, value[i]); var c0 = str[0]; var parenless = c0 === '{' || c0 === '(' || c0 === '[' || c0 === '<' || c0 === '"' || str.indexOf(' ') < 0; output += ' ' + (parenless ? str : '(' + str + ')'); } return _Debug_ctorColor(ansi, tag) + output; } if (typeof DataView === 'function' && value instanceof DataView) { return _Debug_stringColor(ansi, '<' + value.byteLength + ' bytes>'); } if (typeof File !== 'undefined' && value instanceof File) { return _Debug_internalColor(ansi, '<' + value.name + '>'); } if (typeof value === 'object') { var output = []; for (var key in value) { var field = key[0] === '_' ? key.slice(1) : key; output.push(_Debug_fadeColor(ansi, field) + ' = ' + _Debug_toAnsiString(ansi, value[key])); } if (output.length === 0) { return '{}'; } return '{ ' + output.join(', ') + ' }'; } return _Debug_internalColor(ansi, ''); } function _Debug_addSlashes(str, isChar) { var s = str .replace(/\\/g, '\\\\') .replace(/\n/g, '\\n') .replace(/\t/g, '\\t') .replace(/\r/g, '\\r') .replace(/\v/g, '\\v') .replace(/\0/g, '\\0'); if (isChar) { return s.replace(/\'/g, '\\\''); } else { return s.replace(/\"/g, '\\"'); } } function _Debug_ctorColor(ansi, string) { return ansi ? '\x1b[96m' + string + '\x1b[0m' : string; } function _Debug_numberColor(ansi, string) { return ansi ? '\x1b[95m' + string + '\x1b[0m' : string; } function _Debug_stringColor(ansi, string) { return ansi ? '\x1b[93m' + string + '\x1b[0m' : string; } function _Debug_charColor(ansi, string) { return ansi ? '\x1b[92m' + string + '\x1b[0m' : string; } function _Debug_fadeColor(ansi, string) { return ansi ? '\x1b[37m' + string + '\x1b[0m' : string; } function _Debug_internalColor(ansi, string) { return ansi ? '\x1b[36m' + string + '\x1b[0m' : string; } function _Debug_toHexDigit(n) { return String.fromCharCode(n < 10 ? 48 + n : 55 + n); } // CRASH function _Debug_crash(identifier) { throw new Error('https://github.com/elm/core/blob/1.0.0/hints/' + identifier + '.md'); } function _Debug_crash_UNUSED(identifier, fact1, fact2, fact3, fact4) { switch(identifier) { case 0: throw new Error('What node should I take over? In JavaScript I need something like:\n\n Elm.Main.init({\n node: document.getElementById("elm-node")\n })\n\nYou need to do this with any Browser.sandbox or Browser.element program.'); case 1: throw new Error('Browser.application programs cannot handle URLs like this:\n\n ' + document.location.href + '\n\nWhat is the root? The root of your file system? Try looking at this program with `elm reactor` or some other server.'); case 2: var jsonErrorString = fact1; throw new Error('Problem with the flags given to your Elm program on initialization.\n\n' + jsonErrorString); case 3: var portName = fact1; throw new Error('There can only be one port named `' + portName + '`, but your program has multiple.'); case 4: var portName = fact1; var problem = fact2; throw new Error('Trying to send an unexpected type of value through port `' + portName + '`:\n' + problem); case 5: throw new Error('Trying to use `(==)` on functions.\nThere is no way to know if functions are "the same" in the Elm sense.\nRead more about this at https://package.elm-lang.org/packages/elm/core/latest/Basics#== which describes why it is this way and what the better version will look like.'); case 6: var moduleName = fact1; throw new Error('Your page is loading multiple Elm scripts with a module named ' + moduleName + '. Maybe a duplicate script is getting loaded accidentally? If not, rename one of them so I know which is which!'); case 8: var moduleName = fact1; var region = fact2; var message = fact3; throw new Error('TODO in module `' + moduleName + '` ' + _Debug_regionToString(region) + '\n\n' + message); case 9: var moduleName = fact1; var region = fact2; var value = fact3; var message = fact4; throw new Error( 'TODO in module `' + moduleName + '` from the `case` expression ' + _Debug_regionToString(region) + '\n\nIt received the following value:\n\n ' + _Debug_toString(value).replace('\n', '\n ') + '\n\nBut the branch that handles it says:\n\n ' + message.replace('\n', '\n ') ); case 10: throw new Error('Bug in https://github.com/elm/virtual-dom/issues'); case 11: throw new Error('Cannot perform mod 0. Division by zero error.'); } } function _Debug_regionToString(region) { if (region.Q.H === region.V.H) { return 'on line ' + region.Q.H; } return 'on lines ' + region.Q.H + ' through ' + region.V.H; } // MATH var _Basics_add = F2(function(a, b) { return a + b; }); var _Basics_sub = F2(function(a, b) { return a - b; }); var _Basics_mul = F2(function(a, b) { return a * b; }); var _Basics_fdiv = F2(function(a, b) { return a / b; }); var _Basics_idiv = F2(function(a, b) { return (a / b) | 0; }); var _Basics_pow = F2(Math.pow); var _Basics_remainderBy = F2(function(b, a) { return a % b; }); // https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote-letter.pdf var _Basics_modBy = F2(function(modulus, x) { var answer = x % modulus; return modulus === 0 ? _Debug_crash(11) : ((answer > 0 && modulus < 0) || (answer < 0 && modulus > 0)) ? answer + modulus : answer; }); // TRIGONOMETRY var _Basics_pi = Math.PI; var _Basics_e = Math.E; var _Basics_cos = Math.cos; var _Basics_sin = Math.sin; var _Basics_tan = Math.tan; var _Basics_acos = Math.acos; var _Basics_asin = Math.asin; var _Basics_atan = Math.atan; var _Basics_atan2 = F2(Math.atan2); // MORE MATH function _Basics_toFloat(x) { return x; } function _Basics_truncate(n) { return n | 0; } function _Basics_isInfinite(n) { return n === Infinity || n === -Infinity; } var _Basics_ceiling = Math.ceil; var _Basics_floor = Math.floor; var _Basics_round = Math.round; var _Basics_sqrt = Math.sqrt; var _Basics_log = Math.log; var _Basics_isNaN = isNaN; // BOOLEANS function _Basics_not(bool) { return !bool; } var _Basics_and = F2(function(a, b) { return a && b; }); var _Basics_or = F2(function(a, b) { return a || b; }); var _Basics_xor = F2(function(a, b) { return a !== b; }); var _String_cons = F2(function(chr, str) { return chr + str; }); function _String_uncons(string) { var word = string.charCodeAt(0); return !isNaN(word) ? $elm$core$Maybe$Just( 0xD800 <= word && word <= 0xDBFF ? _Utils_Tuple2(_Utils_chr(string[0] + string[1]), string.slice(2)) : _Utils_Tuple2(_Utils_chr(string[0]), string.slice(1)) ) : $elm$core$Maybe$Nothing; } var _String_append = F2(function(a, b) { return a + b; }); function _String_length(str) { return str.length; } var _String_map = F2(function(func, string) { var len = string.length; var array = new Array(len); var i = 0; while (i < len) { var word = string.charCodeAt(i); if (0xD800 <= word && word <= 0xDBFF) { array[i] = func(_Utils_chr(string[i] + string[i+1])); i += 2; continue; } array[i] = func(_Utils_chr(string[i])); i++; } return array.join(''); }); var _String_filter = F2(function(isGood, str) { var arr = []; var len = str.length; var i = 0; while (i < len) { var char = str[i]; var word = str.charCodeAt(i); i++; if (0xD800 <= word && word <= 0xDBFF) { char += str[i]; i++; } if (isGood(_Utils_chr(char))) { arr.push(char); } } return arr.join(''); }); function _String_reverse(str) { var len = str.length; var arr = new Array(len); var i = 0; while (i < len) { var word = str.charCodeAt(i); if (0xD800 <= word && word <= 0xDBFF) { arr[len - i] = str[i + 1]; i++; arr[len - i] = str[i - 1]; i++; } else { arr[len - i] = str[i]; i++; } } return arr.join(''); } var _String_foldl = F3(function(func, state, string) { var len = string.length; var i = 0; while (i < len) { var char = string[i]; var word = string.charCodeAt(i); i++; if (0xD800 <= word && word <= 0xDBFF) { char += string[i]; i++; } state = A2(func, _Utils_chr(char), state); } return state; }); var _String_foldr = F3(function(func, state, string) { var i = string.length; while (i--) { var char = string[i]; var word = string.charCodeAt(i); if (0xDC00 <= word && word <= 0xDFFF) { i--; char = string[i] + char; } state = A2(func, _Utils_chr(char), state); } return state; }); var _String_split = F2(function(sep, str) { return str.split(sep); }); var _String_join = F2(function(sep, strs) { return strs.join(sep); }); var _String_slice = F3(function(start, end, str) { return str.slice(start, end); }); function _String_trim(str) { return str.trim(); } function _String_trimLeft(str) { return str.replace(/^\s+/, ''); } function _String_trimRight(str) { return str.replace(/\s+$/, ''); } function _String_words(str) { return _List_fromArray(str.trim().split(/\s+/g)); } function _String_lines(str) { return _List_fromArray(str.split(/\r\n|\r|\n/g)); } function _String_toUpper(str) { return str.toUpperCase(); } function _String_toLower(str) { return str.toLowerCase(); } var _String_any = F2(function(isGood, string) { var i = string.length; while (i--) { var char = string[i]; var word = string.charCodeAt(i); if (0xDC00 <= word && word <= 0xDFFF) { i--; char = string[i] + char; } if (isGood(_Utils_chr(char))) { return true; } } return false; }); var _String_all = F2(function(isGood, string) { var i = string.length; while (i--) { var char = string[i]; var word = string.charCodeAt(i); if (0xDC00 <= word && word <= 0xDFFF) { i--; char = string[i] + char; } if (!isGood(_Utils_chr(char))) { return false; } } return true; }); var _String_contains = F2(function(sub, str) { return str.indexOf(sub) > -1; }); var _String_startsWith = F2(function(sub, str) { return str.indexOf(sub) === 0; }); var _String_endsWith = F2(function(sub, str) { return str.length >= sub.length && str.lastIndexOf(sub) === str.length - sub.length; }); var _String_indexes = F2(function(sub, str) { var subLen = sub.length; if (subLen < 1) { return _List_Nil; } var i = 0; var is = []; while ((i = str.indexOf(sub, i)) > -1) { is.push(i); i = i + subLen; } return _List_fromArray(is); }); // TO STRING function _String_fromNumber(number) { return number + ''; } // INT CONVERSIONS function _String_toInt(str) { var total = 0; var code0 = str.charCodeAt(0); var start = code0 == 0x2B /* + */ || code0 == 0x2D /* - */ ? 1 : 0; for (var i = start; i < str.length; ++i) { var code = str.charCodeAt(i); if (code < 0x30 || 0x39 < code) { return $elm$core$Maybe$Nothing; } total = 10 * total + code - 0x30; } return i == start ? $elm$core$Maybe$Nothing : $elm$core$Maybe$Just(code0 == 0x2D ? -total : total); } // FLOAT CONVERSIONS function _String_toFloat(s) { // check if it is a hex, octal, or binary number if (s.length === 0 || /[\sxbo]/.test(s)) { return $elm$core$Maybe$Nothing; } var n = +s; // faster isNaN check return n === n ? $elm$core$Maybe$Just(n) : $elm$core$Maybe$Nothing; } function _String_fromList(chars) { return _List_toArray(chars).join(''); } function _Char_toCode(char) { var code = char.charCodeAt(0); if (0xD800 <= code && code <= 0xDBFF) { return (code - 0xD800) * 0x400 + char.charCodeAt(1) - 0xDC00 + 0x10000 } return code; } function _Char_fromCode(code) { return _Utils_chr( (code < 0 || 0x10FFFF < code) ? '\uFFFD' : (code <= 0xFFFF) ? String.fromCharCode(code) : (code -= 0x10000, String.fromCharCode(Math.floor(code / 0x400) + 0xD800, code % 0x400 + 0xDC00) ) ); } function _Char_toUpper(char) { return _Utils_chr(char.toUpperCase()); } function _Char_toLower(char) { return _Utils_chr(char.toLowerCase()); } function _Char_toLocaleUpper(char) { return _Utils_chr(char.toLocaleUpperCase()); } function _Char_toLocaleLower(char) { return _Utils_chr(char.toLocaleLowerCase()); } /**_UNUSED/ function _Json_errorToString(error) { return $elm$json$Json$Decode$errorToString(error); } //*/ // CORE DECODERS function _Json_succeed(msg) { return { $: 0, a: msg }; } function _Json_fail(msg) { return { $: 1, a: msg }; } function _Json_decodePrim(decoder) { return { $: 2, b: decoder }; } var _Json_decodeInt = _Json_decodePrim(function(value) { return (typeof value !== 'number') ? _Json_expecting('an INT', value) : (-2147483647 < value && value < 2147483647 && (value | 0) === value) ? $elm$core$Result$Ok(value) : (isFinite(value) && !(value % 1)) ? $elm$core$Result$Ok(value) : _Json_expecting('an INT', value); }); var _Json_decodeBool = _Json_decodePrim(function(value) { return (typeof value === 'boolean') ? $elm$core$Result$Ok(value) : _Json_expecting('a BOOL', value); }); var _Json_decodeFloat = _Json_decodePrim(function(value) { return (typeof value === 'number') ? $elm$core$Result$Ok(value) : _Json_expecting('a FLOAT', value); }); var _Json_decodeValue = _Json_decodePrim(function(value) { return $elm$core$Result$Ok(_Json_wrap(value)); }); var _Json_decodeString = _Json_decodePrim(function(value) { return (typeof value === 'string') ? $elm$core$Result$Ok(value) : (value instanceof String) ? $elm$core$Result$Ok(value + '') : _Json_expecting('a STRING', value); }); function _Json_decodeList(decoder) { return { $: 3, b: decoder }; } function _Json_decodeArray(decoder) { return { $: 4, b: decoder }; } function _Json_decodeNull(value) { return { $: 5, c: value }; } var _Json_decodeField = F2(function(field, decoder) { return { $: 6, d: field, b: decoder }; }); var _Json_decodeIndex = F2(function(index, decoder) { return { $: 7, e: index, b: decoder }; }); function _Json_decodeKeyValuePairs(decoder) { return { $: 8, b: decoder }; } function _Json_mapMany(f, decoders) { return { $: 9, f: f, g: decoders }; } var _Json_andThen = F2(function(callback, decoder) { return { $: 10, b: decoder, h: callback }; }); function _Json_oneOf(decoders) { return { $: 11, g: decoders }; } // DECODING OBJECTS var _Json_map1 = F2(function(f, d1) { return _Json_mapMany(f, [d1]); }); var _Json_map2 = F3(function(f, d1, d2) { return _Json_mapMany(f, [d1, d2]); }); var _Json_map3 = F4(function(f, d1, d2, d3) { return _Json_mapMany(f, [d1, d2, d3]); }); var _Json_map4 = F5(function(f, d1, d2, d3, d4) { return _Json_mapMany(f, [d1, d2, d3, d4]); }); var _Json_map5 = F6(function(f, d1, d2, d3, d4, d5) { return _Json_mapMany(f, [d1, d2, d3, d4, d5]); }); var _Json_map6 = F7(function(f, d1, d2, d3, d4, d5, d6) { return _Json_mapMany(f, [d1, d2, d3, d4, d5, d6]); }); var _Json_map7 = F8(function(f, d1, d2, d3, d4, d5, d6, d7) { return _Json_mapMany(f, [d1, d2, d3, d4, d5, d6, d7]); }); var _Json_map8 = F9(function(f, d1, d2, d3, d4, d5, d6, d7, d8) { return _Json_mapMany(f, [d1, d2, d3, d4, d5, d6, d7, d8]); }); // DECODE var _Json_runOnString = F2(function(decoder, string) { try { var value = JSON.parse(string); return _Json_runHelp(decoder, value); } catch (e) { return $elm$core$Result$Err(A2($elm$json$Json$Decode$Failure, 'This is not valid JSON! ' + e.message, _Json_wrap(string))); } }); var _Json_run = F2(function(decoder, value) { return _Json_runHelp(decoder, _Json_unwrap(value)); }); function _Json_runHelp(decoder, value) { switch (decoder.$) { case 2: return decoder.b(value); case 5: return (value === null) ? $elm$core$Result$Ok(decoder.c) : _Json_expecting('null', value); case 3: if (!_Json_isArray(value)) { return _Json_expecting('a LIST', value); } return _Json_runArrayDecoder(decoder.b, value, _List_fromArray); case 4: if (!_Json_isArray(value)) { return _Json_expecting('an ARRAY', value); } return _Json_runArrayDecoder(decoder.b, value, _Json_toElmArray); case 6: var field = decoder.d; if (typeof value !== 'object' || value === null || !(field in value)) { return _Json_expecting('an OBJECT with a field named `' + field + '`', value); } var result = _Json_runHelp(decoder.b, value[field]); return ($elm$core$Result$isOk(result)) ? result : $elm$core$Result$Err(A2($elm$json$Json$Decode$Field, field, result.a)); case 7: var index = decoder.e; if (!_Json_isArray(value)) { return _Json_expecting('an ARRAY', value); } if (index >= value.length) { return _Json_expecting('a LONGER array. Need index ' + index + ' but only see ' + value.length + ' entries', value); } var result = _Json_runHelp(decoder.b, value[index]); return ($elm$core$Result$isOk(result)) ? result : $elm$core$Result$Err(A2($elm$json$Json$Decode$Index, index, result.a)); case 8: if (typeof value !== 'object' || value === null || _Json_isArray(value)) { return _Json_expecting('an OBJECT', value); } var keyValuePairs = _List_Nil; // TODO test perf of Object.keys and switch when support is good enough for (var key in value) { if (value.hasOwnProperty(key)) { var result = _Json_runHelp(decoder.b, value[key]); if (!$elm$core$Result$isOk(result)) { return $elm$core$Result$Err(A2($elm$json$Json$Decode$Field, key, result.a)); } keyValuePairs = _List_Cons(_Utils_Tuple2(key, result.a), keyValuePairs); } } return $elm$core$Result$Ok($elm$core$List$reverse(keyValuePairs)); case 9: var answer = decoder.f; var decoders = decoder.g; for (var i = 0; i < decoders.length; i++) { var result = _Json_runHelp(decoders[i], value); if (!$elm$core$Result$isOk(result)) { return result; } answer = answer(result.a); } return $elm$core$Result$Ok(answer); case 10: var result = _Json_runHelp(decoder.b, value); return (!$elm$core$Result$isOk(result)) ? result : _Json_runHelp(decoder.h(result.a), value); case 11: var errors = _List_Nil; for (var temp = decoder.g; temp.b; temp = temp.b) // WHILE_CONS { var result = _Json_runHelp(temp.a, value); if ($elm$core$Result$isOk(result)) { return result; } errors = _List_Cons(result.a, errors); } return $elm$core$Result$Err($elm$json$Json$Decode$OneOf($elm$core$List$reverse(errors))); case 1: return $elm$core$Result$Err(A2($elm$json$Json$Decode$Failure, decoder.a, _Json_wrap(value))); case 0: return $elm$core$Result$Ok(decoder.a); } } function _Json_runArrayDecoder(decoder, value, toElmValue) { var len = value.length; var array = new Array(len); for (var i = 0; i < len; i++) { var result = _Json_runHelp(decoder, value[i]); if (!$elm$core$Result$isOk(result)) { return $elm$core$Result$Err(A2($elm$json$Json$Decode$Index, i, result.a)); } array[i] = result.a; } return $elm$core$Result$Ok(toElmValue(array)); } function _Json_isArray(value) { return Array.isArray(value) || (typeof FileList !== 'undefined' && value instanceof FileList); } function _Json_toElmArray(array) { return A2($elm$core$Array$initialize, array.length, function(i) { return array[i]; }); } function _Json_expecting(type, value) { return $elm$core$Result$Err(A2($elm$json$Json$Decode$Failure, 'Expecting ' + type, _Json_wrap(value))); } // EQUALITY function _Json_equality(x, y) { if (x === y) { return true; } if (x.$ !== y.$) { return false; } switch (x.$) { case 0: case 1: return x.a === y.a; case 2: return x.b === y.b; case 5: return x.c === y.c; case 3: case 4: case 8: return _Json_equality(x.b, y.b); case 6: return x.d === y.d && _Json_equality(x.b, y.b); case 7: return x.e === y.e && _Json_equality(x.b, y.b); case 9: return x.f === y.f && _Json_listEquality(x.g, y.g); case 10: return x.h === y.h && _Json_equality(x.b, y.b); case 11: return _Json_listEquality(x.g, y.g); } } function _Json_listEquality(aDecoders, bDecoders) { var len = aDecoders.length; if (len !== bDecoders.length) { return false; } for (var i = 0; i < len; i++) { if (!_Json_equality(aDecoders[i], bDecoders[i])) { return false; } } return true; } // ENCODE var _Json_encode = F2(function(indentLevel, value) { return JSON.stringify(_Json_unwrap(value), null, indentLevel) + ''; }); function _Json_wrap_UNUSED(value) { return { $: 0, a: value }; } function _Json_unwrap_UNUSED(value) { return value.a; } function _Json_wrap(value) { return value; } function _Json_unwrap(value) { return value; } function _Json_emptyArray() { return []; } function _Json_emptyObject() { return {}; } var _Json_addField = F3(function(key, value, object) { object[key] = _Json_unwrap(value); return object; }); function _Json_addEntry(func) { return F2(function(entry, array) { array.push(_Json_unwrap(func(entry))); return array; }); } var _Json_encodeNull = _Json_wrap(null); // TASKS function _Scheduler_succeed(value) { return { $: 0, a: value }; } function _Scheduler_fail(error) { return { $: 1, a: error }; } function _Scheduler_binding(callback) { return { $: 2, b: callback, c: null }; } var _Scheduler_andThen = F2(function(callback, task) { return { $: 3, b: callback, d: task }; }); var _Scheduler_onError = F2(function(callback, task) { return { $: 4, b: callback, d: task }; }); function _Scheduler_receive(callback) { return { $: 5, b: callback }; } // PROCESSES var _Scheduler_guid = 0; function _Scheduler_rawSpawn(task) { var proc = { $: 0, e: _Scheduler_guid++, f: task, g: null, h: [] }; _Scheduler_enqueue(proc); return proc; } function _Scheduler_spawn(task) { return _Scheduler_binding(function(callback) { callback(_Scheduler_succeed(_Scheduler_rawSpawn(task))); }); } function _Scheduler_rawSend(proc, msg) { proc.h.push(msg); _Scheduler_enqueue(proc); } var _Scheduler_send = F2(function(proc, msg) { return _Scheduler_binding(function(callback) { _Scheduler_rawSend(proc, msg); callback(_Scheduler_succeed(_Utils_Tuple0)); }); }); function _Scheduler_kill(proc) { return _Scheduler_binding(function(callback) { var task = proc.f; if (task.$ === 2 && task.c) { task.c(); } proc.f = null; callback(_Scheduler_succeed(_Utils_Tuple0)); }); } /* STEP PROCESSES type alias Process = { $ : tag , id : unique_id , root : Task , stack : null | { $: SUCCEED | FAIL, a: callback, b: stack } , mailbox : [msg] } */ var _Scheduler_working = false; var _Scheduler_queue = []; function _Scheduler_enqueue(proc) { _Scheduler_queue.push(proc); if (_Scheduler_working) { return; } _Scheduler_working = true; while (proc = _Scheduler_queue.shift()) { _Scheduler_step(proc); } _Scheduler_working = false; } function _Scheduler_step(proc) { while (proc.f) { var rootTag = proc.f.$; if (rootTag === 0 || rootTag === 1) { while (proc.g && proc.g.$ !== rootTag) { proc.g = proc.g.i; } if (!proc.g) { return; } proc.f = proc.g.b(proc.f.a); proc.g = proc.g.i; } else if (rootTag === 2) { proc.f.c = proc.f.b(function(newRoot) { proc.f = newRoot; _Scheduler_enqueue(proc); }); return; } else if (rootTag === 5) { if (proc.h.length === 0) { return; } proc.f = proc.f.b(proc.h.shift()); } else // if (rootTag === 3 || rootTag === 4) { proc.g = { $: rootTag === 3 ? 0 : 1, b: proc.f.b, i: proc.g }; proc.f = proc.f.d; } } } function _Process_sleep(time) { return _Scheduler_binding(function(callback) { var id = setTimeout(function() { callback(_Scheduler_succeed(_Utils_Tuple0)); }, time); return function() { clearTimeout(id); }; }); } // PROGRAMS var _Platform_worker = F4(function(impl, flagDecoder, debugMetadata, args) { return _Platform_initialize( flagDecoder, args, impl.aB, impl.aJ, impl.aH, function() { return function() {} } ); }); // INITIALIZE A PROGRAM function _Platform_initialize(flagDecoder, args, init, update, subscriptions, stepperBuilder) { var result = A2(_Json_run, flagDecoder, _Json_wrap(args ? args['flags'] : undefined)); $elm$core$Result$isOk(result) || _Debug_crash(2 /**_UNUSED/, _Json_errorToString(result.a) /**/); var managers = {}; var initPair = init(result.a); var model = initPair.a; var stepper = stepperBuilder(sendToApp, model); var ports = _Platform_setupEffects(managers, sendToApp); function sendToApp(msg, viewMetadata) { var pair = A2(update, msg, model); stepper(model = pair.a, viewMetadata); _Platform_enqueueEffects(managers, pair.b, subscriptions(model)); } _Platform_enqueueEffects(managers, initPair.b, subscriptions(model)); return ports ? { ports: ports } : {}; } // TRACK PRELOADS // // This is used by code in elm/browser and elm/http // to register any HTTP requests that are triggered by init. // var _Platform_preload; function _Platform_registerPreload(url) { _Platform_preload.add(url); } // EFFECT MANAGERS var _Platform_effectManagers = {}; function _Platform_setupEffects(managers, sendToApp) { var ports; // setup all necessary effect managers for (var key in _Platform_effectManagers) { var manager = _Platform_effectManagers[key]; if (manager.a) { ports = ports || {}; ports[key] = manager.a(key, sendToApp); } managers[key] = _Platform_instantiateManager(manager, sendToApp); } return ports; } function _Platform_createManager(init, onEffects, onSelfMsg, cmdMap, subMap) { return { b: init, c: onEffects, d: onSelfMsg, e: cmdMap, f: subMap }; } function _Platform_instantiateManager(info, sendToApp) { var router = { g: sendToApp, h: undefined }; var onEffects = info.c; var onSelfMsg = info.d; var cmdMap = info.e; var subMap = info.f; function loop(state) { return A2(_Scheduler_andThen, loop, _Scheduler_receive(function(msg) { var value = msg.a; if (msg.$ === 0) { return A3(onSelfMsg, router, value, state); } return cmdMap && subMap ? A4(onEffects, router, value.i, value.j, state) : A3(onEffects, router, cmdMap ? value.i : value.j, state); })); } return router.h = _Scheduler_rawSpawn(A2(_Scheduler_andThen, loop, info.b)); } // ROUTING var _Platform_sendToApp = F2(function(router, msg) { return _Scheduler_binding(function(callback) { router.g(msg); callback(_Scheduler_succeed(_Utils_Tuple0)); }); }); var _Platform_sendToSelf = F2(function(router, msg) { return A2(_Scheduler_send, router.h, { $: 0, a: msg }); }); // BAGS function _Platform_leaf(home) { return function(value) { return { $: 1, k: home, l: value }; }; } function _Platform_batch(list) { return { $: 2, m: list }; } var _Platform_map = F2(function(tagger, bag) { return { $: 3, n: tagger, o: bag } }); // PIPE BAGS INTO EFFECT MANAGERS // // Effects must be queued! // // Say your init contains a synchronous command, like Time.now or Time.here // // - This will produce a batch of effects (FX_1) // - The synchronous task triggers the subsequent `update` call // - This will produce a batch of effects (FX_2) // // If we just start dispatching FX_2, subscriptions from FX_2 can be processed // before subscriptions from FX_1. No good! Earlier versions of this code had // this problem, leading to these reports: // // https://github.com/elm/core/issues/980 // https://github.com/elm/core/pull/981 // https://github.com/elm/compiler/issues/1776 // // The queue is necessary to avoid ordering issues for synchronous commands. // Why use true/false here? Why not just check the length of the queue? // The goal is to detect "are we currently dispatching effects?" If we // are, we need to bail and let the ongoing while loop handle things. // // Now say the queue has 1 element. When we dequeue the final element, // the queue will be empty, but we are still actively dispatching effects. // So you could get queue jumping in a really tricky category of cases. // var _Platform_effectsQueue = []; var _Platform_effectsActive = false; function _Platform_enqueueEffects(managers, cmdBag, subBag) { _Platform_effectsQueue.push({ p: managers, q: cmdBag, r: subBag }); if (_Platform_effectsActive) return; _Platform_effectsActive = true; for (var fx; fx = _Platform_effectsQueue.shift(); ) { _Platform_dispatchEffects(fx.p, fx.q, fx.r); } _Platform_effectsActive = false; } function _Platform_dispatchEffects(managers, cmdBag, subBag) { var effectsDict = {}; _Platform_gatherEffects(true, cmdBag, effectsDict, null); _Platform_gatherEffects(false, subBag, effectsDict, null); for (var home in managers) { _Scheduler_rawSend(managers[home], { $: 'fx', a: effectsDict[home] || { i: _List_Nil, j: _List_Nil } }); } } function _Platform_gatherEffects(isCmd, bag, effectsDict, taggers) { switch (bag.$) { case 1: var home = bag.k; var effect = _Platform_toEffect(isCmd, home, taggers, bag.l); effectsDict[home] = _Platform_insert(isCmd, effect, effectsDict[home]); return; case 2: for (var list = bag.m; list.b; list = list.b) // WHILE_CONS { _Platform_gatherEffects(isCmd, list.a, effectsDict, taggers); } return; case 3: _Platform_gatherEffects(isCmd, bag.o, effectsDict, { s: bag.n, t: taggers }); return; } } function _Platform_toEffect(isCmd, home, taggers, value) { function applyTaggers(x) { for (var temp = taggers; temp; temp = temp.t) { x = temp.s(x); } return x; } var map = isCmd ? _Platform_effectManagers[home].e : _Platform_effectManagers[home].f; return A2(map, applyTaggers, value) } function _Platform_insert(isCmd, newEffect, effects) { effects = effects || { i: _List_Nil, j: _List_Nil }; isCmd ? (effects.i = _List_Cons(newEffect, effects.i)) : (effects.j = _List_Cons(newEffect, effects.j)); return effects; } // PORTS function _Platform_checkPortName(name) { if (_Platform_effectManagers[name]) { _Debug_crash(3, name) } } // OUTGOING PORTS function _Platform_outgoingPort(name, converter) { _Platform_checkPortName(name); _Platform_effectManagers[name] = { e: _Platform_outgoingPortMap, u: converter, a: _Platform_setupOutgoingPort }; return _Platform_leaf(name); } var _Platform_outgoingPortMap = F2(function(tagger, value) { return value; }); function _Platform_setupOutgoingPort(name) { var subs = []; var converter = _Platform_effectManagers[name].u; // CREATE MANAGER var init = _Process_sleep(0); _Platform_effectManagers[name].b = init; _Platform_effectManagers[name].c = F3(function(router, cmdList, state) { for ( ; cmdList.b; cmdList = cmdList.b) // WHILE_CONS { // grab a separate reference to subs in case unsubscribe is called var currentSubs = subs; var value = _Json_unwrap(converter(cmdList.a)); for (var i = 0; i < currentSubs.length; i++) { currentSubs[i](value); } } return init; }); // PUBLIC API function subscribe(callback) { subs.push(callback); } function unsubscribe(callback) { // copy subs into a new array in case unsubscribe is called within a // subscribed callback subs = subs.slice(); var index = subs.indexOf(callback); if (index >= 0) { subs.splice(index, 1); } } return { subscribe: subscribe, unsubscribe: unsubscribe }; } // INCOMING PORTS function _Platform_incomingPort(name, converter) { _Platform_checkPortName(name); _Platform_effectManagers[name] = { f: _Platform_incomingPortMap, u: converter, a: _Platform_setupIncomingPort }; return _Platform_leaf(name); } var _Platform_incomingPortMap = F2(function(tagger, finalTagger) { return function(value) { return tagger(finalTagger(value)); }; }); function _Platform_setupIncomingPort(name, sendToApp) { var subs = _List_Nil; var converter = _Platform_effectManagers[name].u; // CREATE MANAGER var init = _Scheduler_succeed(null); _Platform_effectManagers[name].b = init; _Platform_effectManagers[name].c = F3(function(router, subList, state) { subs = subList; return init; }); // PUBLIC API function send(incomingValue) { var result = A2(_Json_run, converter, _Json_wrap(incomingValue)); $elm$core$Result$isOk(result) || _Debug_crash(4, name, result.a); var value = result.a; for (var temp = subs; temp.b; temp = temp.b) // WHILE_CONS { sendToApp(temp.a(value)); } } return { send: send }; } // EXPORT ELM MODULES // // Have DEBUG and PROD versions so that we can (1) give nicer errors in // debug mode and (2) not pay for the bits needed for that in prod mode. // function _Platform_export(exports) { scope['Elm'] ? _Platform_mergeExportsProd(scope['Elm'], exports) : scope['Elm'] = exports; } function _Platform_mergeExportsProd(obj, exports) { for (var name in exports) { (name in obj) ? (name == 'init') ? _Debug_crash(6) : _Platform_mergeExportsProd(obj[name], exports[name]) : (obj[name] = exports[name]); } } function _Platform_export_UNUSED(exports) { scope['Elm'] ? _Platform_mergeExportsDebug('Elm', scope['Elm'], exports) : scope['Elm'] = exports; } function _Platform_mergeExportsDebug(moduleName, obj, exports) { for (var name in exports) { (name in obj) ? (name == 'init') ? _Debug_crash(6, moduleName) : _Platform_mergeExportsDebug(moduleName + '.' + name, obj[name], exports[name]) : (obj[name] = exports[name]); } } // HELPERS var _VirtualDom_divertHrefToApp; var _VirtualDom_doc = typeof document !== 'undefined' ? document : {}; function _VirtualDom_appendChild(parent, child) { parent.appendChild(child); } var _VirtualDom_init = F4(function(virtualNode, flagDecoder, debugMetadata, args) { // NOTE: this function needs _Platform_export available to work /**/ var node = args['node']; //*/ /**_UNUSED/ var node = args && args['node'] ? args['node'] : _Debug_crash(0); //*/ node.parentNode.replaceChild( _VirtualDom_render(virtualNode, function() {}), node ); return {}; }); // TEXT function _VirtualDom_text(string) { return { $: 0, a: string }; } // NODE var _VirtualDom_nodeNS = F2(function(namespace, tag) { return F2(function(factList, kidList) { for (var kids = [], descendantsCount = 0; kidList.b; kidList = kidList.b) // WHILE_CONS { var kid = kidList.a; descendantsCount += (kid.b || 0); kids.push(kid); } descendantsCount += kids.length; return { $: 1, c: tag, d: _VirtualDom_organizeFacts(factList), e: kids, f: namespace, b: descendantsCount }; }); }); var _VirtualDom_node = _VirtualDom_nodeNS(undefined); // KEYED NODE var _VirtualDom_keyedNodeNS = F2(function(namespace, tag) { return F2(function(factList, kidList) { for (var kids = [], descendantsCount = 0; kidList.b; kidList = kidList.b) // WHILE_CONS { var kid = kidList.a; descendantsCount += (kid.b.b || 0); kids.push(kid); } descendantsCount += kids.length; return { $: 2, c: tag, d: _VirtualDom_organizeFacts(factList), e: kids, f: namespace, b: descendantsCount }; }); }); var _VirtualDom_keyedNode = _VirtualDom_keyedNodeNS(undefined); // CUSTOM function _VirtualDom_custom(factList, model, render, diff) { return { $: 3, d: _VirtualDom_organizeFacts(factList), g: model, h: render, i: diff }; } // MAP var _VirtualDom_map = F2(function(tagger, node) { return { $: 4, j: tagger, k: node, b: 1 + (node.b || 0) }; }); // LAZY function _VirtualDom_thunk(refs, thunk) { return { $: 5, l: refs, m: thunk, k: undefined }; } var _VirtualDom_lazy = F2(function(func, a) { return _VirtualDom_thunk([func, a], function() { return func(a); }); }); var _VirtualDom_lazy2 = F3(function(func, a, b) { return _VirtualDom_thunk([func, a, b], function() { return A2(func, a, b); }); }); var _VirtualDom_lazy3 = F4(function(func, a, b, c) { return _VirtualDom_thunk([func, a, b, c], function() { return A3(func, a, b, c); }); }); var _VirtualDom_lazy4 = F5(function(func, a, b, c, d) { return _VirtualDom_thunk([func, a, b, c, d], function() { return A4(func, a, b, c, d); }); }); var _VirtualDom_lazy5 = F6(function(func, a, b, c, d, e) { return _VirtualDom_thunk([func, a, b, c, d, e], function() { return A5(func, a, b, c, d, e); }); }); var _VirtualDom_lazy6 = F7(function(func, a, b, c, d, e, f) { return _VirtualDom_thunk([func, a, b, c, d, e, f], function() { return A6(func, a, b, c, d, e, f); }); }); var _VirtualDom_lazy7 = F8(function(func, a, b, c, d, e, f, g) { return _VirtualDom_thunk([func, a, b, c, d, e, f, g], function() { return A7(func, a, b, c, d, e, f, g); }); }); var _VirtualDom_lazy8 = F9(function(func, a, b, c, d, e, f, g, h) { return _VirtualDom_thunk([func, a, b, c, d, e, f, g, h], function() { return A8(func, a, b, c, d, e, f, g, h); }); }); // FACTS var _VirtualDom_on = F2(function(key, handler) { return { $: 'a0', n: key, o: handler }; }); var _VirtualDom_style = F2(function(key, value) { return { $: 'a1', n: key, o: value }; }); var _VirtualDom_property = F2(function(key, value) { return { $: 'a2', n: key, o: value }; }); var _VirtualDom_attribute = F2(function(key, value) { return { $: 'a3', n: key, o: value }; }); var _VirtualDom_attributeNS = F3(function(namespace, key, value) { return { $: 'a4', n: key, o: { f: namespace, o: value } }; }); // XSS ATTACK VECTOR CHECKS function _VirtualDom_noScript(tag) { return tag == 'script' ? 'p' : tag; } function _VirtualDom_noOnOrFormAction(key) { return /^(on|formAction$)/i.test(key) ? 'data-' + key : key; } function _VirtualDom_noInnerHtmlOrFormAction(key) { return key == 'innerHTML' || key == 'formAction' ? 'data-' + key : key; } function _VirtualDom_noJavaScriptUri(value) { return /^javascript:/i.test(value.replace(/\s/g,'')) ? '' : value; } function _VirtualDom_noJavaScriptUri_UNUSED(value) { return /^javascript:/i.test(value.replace(/\s/g,'')) ? 'javascript:alert("This is an XSS vector. Please use ports or web components instead.")' : value; } function _VirtualDom_noJavaScriptOrHtmlUri(value) { return /^\s*(javascript:|data:text\/html)/i.test(value) ? '' : value; } function _VirtualDom_noJavaScriptOrHtmlUri_UNUSED(value) { return /^\s*(javascript:|data:text\/html)/i.test(value) ? 'javascript:alert("This is an XSS vector. Please use ports or web components instead.")' : value; } // MAP FACTS var _VirtualDom_mapAttribute = F2(function(func, attr) { return (attr.$ === 'a0') ? A2(_VirtualDom_on, attr.n, _VirtualDom_mapHandler(func, attr.o)) : attr; }); function _VirtualDom_mapHandler(func, handler) { var tag = $elm$virtual_dom$VirtualDom$toHandlerInt(handler); // 0 = Normal // 1 = MayStopPropagation // 2 = MayPreventDefault // 3 = Custom return { $: handler.$, a: !tag ? A2($elm$json$Json$Decode$map, func, handler.a) : A3($elm$json$Json$Decode$map2, tag < 3 ? _VirtualDom_mapEventTuple : _VirtualDom_mapEventRecord, $elm$json$Json$Decode$succeed(func), handler.a ) }; } var _VirtualDom_mapEventTuple = F2(function(func, tuple) { return _Utils_Tuple2(func(tuple.a), tuple.b); }); var _VirtualDom_mapEventRecord = F2(function(func, record) { return { t: func(record.t), R: record.R, O: record.O } }); // ORGANIZE FACTS function _VirtualDom_organizeFacts(factList) { for (var facts = {}; factList.b; factList = factList.b) // WHILE_CONS { var entry = factList.a; var tag = entry.$; var key = entry.n; var value = entry.o; if (tag === 'a2') { (key === 'className') ? _VirtualDom_addClass(facts, key, _Json_unwrap(value)) : facts[key] = _Json_unwrap(value); continue; } var subFacts = facts[tag] || (facts[tag] = {}); (tag === 'a3' && key === 'class') ? _VirtualDom_addClass(subFacts, key, value) : subFacts[key] = value; } return facts; } function _VirtualDom_addClass(object, key, newClass) { var classes = object[key]; object[key] = classes ? classes + ' ' + newClass : newClass; } // RENDER function _VirtualDom_render(vNode, eventNode) { var tag = vNode.$; if (tag === 5) { return _VirtualDom_render(vNode.k || (vNode.k = vNode.m()), eventNode); } if (tag === 0) { return _VirtualDom_doc.createTextNode(vNode.a); } if (tag === 4) { var subNode = vNode.k; var tagger = vNode.j; while (subNode.$ === 4) { typeof tagger !== 'object' ? tagger = [tagger, subNode.j] : tagger.push(subNode.j); subNode = subNode.k; } var subEventRoot = { j: tagger, p: eventNode }; var domNode = _VirtualDom_render(subNode, subEventRoot); domNode.elm_event_node_ref = subEventRoot; return domNode; } if (tag === 3) { var domNode = vNode.h(vNode.g); _VirtualDom_applyFacts(domNode, eventNode, vNode.d); return domNode; } // at this point `tag` must be 1 or 2 var domNode = vNode.f ? _VirtualDom_doc.createElementNS(vNode.f, vNode.c) : _VirtualDom_doc.createElement(vNode.c); if (_VirtualDom_divertHrefToApp && vNode.c == 'a') { domNode.addEventListener('click', _VirtualDom_divertHrefToApp(domNode)); } _VirtualDom_applyFacts(domNode, eventNode, vNode.d); for (var kids = vNode.e, i = 0; i < kids.length; i++) { _VirtualDom_appendChild(domNode, _VirtualDom_render(tag === 1 ? kids[i] : kids[i].b, eventNode)); } return domNode; } // APPLY FACTS function _VirtualDom_applyFacts(domNode, eventNode, facts) { for (var key in facts) { var value = facts[key]; key === 'a1' ? _VirtualDom_applyStyles(domNode, value) : key === 'a0' ? _VirtualDom_applyEvents(domNode, eventNode, value) : key === 'a3' ? _VirtualDom_applyAttrs(domNode, value) : key === 'a4' ? _VirtualDom_applyAttrsNS(domNode, value) : ((key !== 'value' && key !== 'checked') || domNode[key] !== value) && (domNode[key] = value); } } // APPLY STYLES function _VirtualDom_applyStyles(domNode, styles) { var domNodeStyle = domNode.style; for (var key in styles) { domNodeStyle[key] = styles[key]; } } // APPLY ATTRS function _VirtualDom_applyAttrs(domNode, attrs) { for (var key in attrs) { var value = attrs[key]; typeof value !== 'undefined' ? domNode.setAttribute(key, value) : domNode.removeAttribute(key); } } // APPLY NAMESPACED ATTRS function _VirtualDom_applyAttrsNS(domNode, nsAttrs) { for (var key in nsAttrs) { var pair = nsAttrs[key]; var namespace = pair.f; var value = pair.o; typeof value !== 'undefined' ? domNode.setAttributeNS(namespace, key, value) : domNode.removeAttributeNS(namespace, key); } } // APPLY EVENTS function _VirtualDom_applyEvents(domNode, eventNode, events) { var allCallbacks = domNode.elmFs || (domNode.elmFs = {}); for (var key in events) { var newHandler = events[key]; var oldCallback = allCallbacks[key]; if (!newHandler) { domNode.removeEventListener(key, oldCallback); allCallbacks[key] = undefined; continue; } if (oldCallback) { var oldHandler = oldCallback.q; if (oldHandler.$ === newHandler.$) { oldCallback.q = newHandler; continue; } domNode.removeEventListener(key, oldCallback); } oldCallback = _VirtualDom_makeCallback(eventNode, newHandler); domNode.addEventListener(key, oldCallback, _VirtualDom_passiveSupported && { passive: $elm$virtual_dom$VirtualDom$toHandlerInt(newHandler) < 2 } ); allCallbacks[key] = oldCallback; } } // PASSIVE EVENTS var _VirtualDom_passiveSupported; try { window.addEventListener('t', null, Object.defineProperty({}, 'passive', { get: function() { _VirtualDom_passiveSupported = true; } })); } catch(e) {} // EVENT HANDLERS function _VirtualDom_makeCallback(eventNode, initialHandler) { function callback(event) { var handler = callback.q; var result = _Json_runHelp(handler.a, event); if (!$elm$core$Result$isOk(result)) { return; } var tag = $elm$virtual_dom$VirtualDom$toHandlerInt(handler); // 0 = Normal // 1 = MayStopPropagation // 2 = MayPreventDefault // 3 = Custom var value = result.a; var message = !tag ? value : tag < 3 ? value.a : value.t; var stopPropagation = tag == 1 ? value.b : tag == 3 && value.R; var currentEventNode = ( stopPropagation && event.stopPropagation(), (tag == 2 ? value.b : tag == 3 && value.O) && event.preventDefault(), eventNode ); var tagger; var i; while (tagger = currentEventNode.j) { if (typeof tagger == 'function') { message = tagger(message); } else { for (var i = tagger.length; i--; ) { message = tagger[i](message); } } currentEventNode = currentEventNode.p; } currentEventNode(message, stopPropagation); // stopPropagation implies isSync } callback.q = initialHandler; return callback; } function _VirtualDom_equalEvents(x, y) { return x.$ == y.$ && _Json_equality(x.a, y.a); } // DIFF // TODO: Should we do patches like in iOS? // // type Patch // = At Int Patch // | Batch (List Patch) // | Change ... // // How could it not be better? // function _VirtualDom_diff(x, y) { var patches = []; _VirtualDom_diffHelp(x, y, patches, 0); return patches; } function _VirtualDom_pushPatch(patches, type, index, data) { var patch = { $: type, r: index, s: data, t: undefined, u: undefined }; patches.push(patch); return patch; } function _VirtualDom_diffHelp(x, y, patches, index) { if (x === y) { return; } var xType = x.$; var yType = y.$; // Bail if you run into different types of nodes. Implies that the // structure has changed significantly and it's not worth a diff. if (xType !== yType) { if (xType === 1 && yType === 2) { y = _VirtualDom_dekey(y); yType = 1; } else { _VirtualDom_pushPatch(patches, 0, index, y); return; } } // Now we know that both nodes are the same $. switch (yType) { case 5: var xRefs = x.l; var yRefs = y.l; var i = xRefs.length; var same = i === yRefs.length; while (same && i--) { same = xRefs[i] === yRefs[i]; } if (same) { y.k = x.k; return; } y.k = y.m(); var subPatches = []; _VirtualDom_diffHelp(x.k, y.k, subPatches, 0); subPatches.length > 0 && _VirtualDom_pushPatch(patches, 1, index, subPatches); return; case 4: // gather nested taggers var xTaggers = x.j; var yTaggers = y.j; var nesting = false; var xSubNode = x.k; while (xSubNode.$ === 4) { nesting = true; typeof xTaggers !== 'object' ? xTaggers = [xTaggers, xSubNode.j] : xTaggers.push(xSubNode.j); xSubNode = xSubNode.k; } var ySubNode = y.k; while (ySubNode.$ === 4) { nesting = true; typeof yTaggers !== 'object' ? yTaggers = [yTaggers, ySubNode.j] : yTaggers.push(ySubNode.j); ySubNode = ySubNode.k; } // Just bail if different numbers of taggers. This implies the // structure of the virtual DOM has changed. if (nesting && xTaggers.length !== yTaggers.length) { _VirtualDom_pushPatch(patches, 0, index, y); return; } // check if taggers are "the same" if (nesting ? !_VirtualDom_pairwiseRefEqual(xTaggers, yTaggers) : xTaggers !== yTaggers) { _VirtualDom_pushPatch(patches, 2, index, yTaggers); } // diff everything below the taggers _VirtualDom_diffHelp(xSubNode, ySubNode, patches, index + 1); return; case 0: if (x.a !== y.a) { _VirtualDom_pushPatch(patches, 3, index, y.a); } return; case 1: _VirtualDom_diffNodes(x, y, patches, index, _VirtualDom_diffKids); return; case 2: _VirtualDom_diffNodes(x, y, patches, index, _VirtualDom_diffKeyedKids); return; case 3: if (x.h !== y.h) { _VirtualDom_pushPatch(patches, 0, index, y); return; } var factsDiff = _VirtualDom_diffFacts(x.d, y.d); factsDiff && _VirtualDom_pushPatch(patches, 4, index, factsDiff); var patch = y.i(x.g, y.g); patch && _VirtualDom_pushPatch(patches, 5, index, patch); return; } } // assumes the incoming arrays are the same length function _VirtualDom_pairwiseRefEqual(as, bs) { for (var i = 0; i < as.length; i++) { if (as[i] !== bs[i]) { return false; } } return true; } function _VirtualDom_diffNodes(x, y, patches, index, diffKids) { // Bail if obvious indicators have changed. Implies more serious // structural changes such that it's not worth it to diff. if (x.c !== y.c || x.f !== y.f) { _VirtualDom_pushPatch(patches, 0, index, y); return; } var factsDiff = _VirtualDom_diffFacts(x.d, y.d); factsDiff && _VirtualDom_pushPatch(patches, 4, index, factsDiff); diffKids(x, y, patches, index); } // DIFF FACTS // TODO Instead of creating a new diff object, it's possible to just test if // there *is* a diff. During the actual patch, do the diff again and make the // modifications directly. This way, there's no new allocations. Worth it? function _VirtualDom_diffFacts(x, y, category) { var diff; // look for changes and removals for (var xKey in x) { if (xKey === 'a1' || xKey === 'a0' || xKey === 'a3' || xKey === 'a4') { var subDiff = _VirtualDom_diffFacts(x[xKey], y[xKey] || {}, xKey); if (subDiff) { diff = diff || {}; diff[xKey] = subDiff; } continue; } // remove if not in the new facts if (!(xKey in y)) { diff = diff || {}; diff[xKey] = !category ? (typeof x[xKey] === 'string' ? '' : null) : (category === 'a1') ? '' : (category === 'a0' || category === 'a3') ? undefined : { f: x[xKey].f, o: undefined }; continue; } var xValue = x[xKey]; var yValue = y[xKey]; // reference equal, so don't worry about it if (xValue === yValue && xKey !== 'value' && xKey !== 'checked' || category === 'a0' && _VirtualDom_equalEvents(xValue, yValue)) { continue; } diff = diff || {}; diff[xKey] = yValue; } // add new stuff for (var yKey in y) { if (!(yKey in x)) { diff = diff || {}; diff[yKey] = y[yKey]; } } return diff; } // DIFF KIDS function _VirtualDom_diffKids(xParent, yParent, patches, index) { var xKids = xParent.e; var yKids = yParent.e; var xLen = xKids.length; var yLen = yKids.length; // FIGURE OUT IF THERE ARE INSERTS OR REMOVALS if (xLen > yLen) { _VirtualDom_pushPatch(patches, 6, index, { v: yLen, i: xLen - yLen }); } else if (xLen < yLen) { _VirtualDom_pushPatch(patches, 7, index, { v: xLen, e: yKids }); } // PAIRWISE DIFF EVERYTHING ELSE for (var minLen = xLen < yLen ? xLen : yLen, i = 0; i < minLen; i++) { var xKid = xKids[i]; _VirtualDom_diffHelp(xKid, yKids[i], patches, ++index); index += xKid.b || 0; } } // KEYED DIFF function _VirtualDom_diffKeyedKids(xParent, yParent, patches, rootIndex) { var localPatches = []; var changes = {}; // Dict String Entry var inserts = []; // Array { index : Int, entry : Entry } // type Entry = { tag : String, vnode : VNode, index : Int, data : _ } var xKids = xParent.e; var yKids = yParent.e; var xLen = xKids.length; var yLen = yKids.length; var xIndex = 0; var yIndex = 0; var index = rootIndex; while (xIndex < xLen && yIndex < yLen) { var x = xKids[xIndex]; var y = yKids[yIndex]; var xKey = x.a; var yKey = y.a; var xNode = x.b; var yNode = y.b; var newMatch = undefined; var oldMatch = undefined; // check if keys match if (xKey === yKey) { index++; _VirtualDom_diffHelp(xNode, yNode, localPatches, index); index += xNode.b || 0; xIndex++; yIndex++; continue; } // look ahead 1 to detect insertions and removals. var xNext = xKids[xIndex + 1]; var yNext = yKids[yIndex + 1]; if (xNext) { var xNextKey = xNext.a; var xNextNode = xNext.b; oldMatch = yKey === xNextKey; } if (yNext) { var yNextKey = yNext.a; var yNextNode = yNext.b; newMatch = xKey === yNextKey; } // swap x and y if (newMatch && oldMatch) { index++; _VirtualDom_diffHelp(xNode, yNextNode, localPatches, index); _VirtualDom_insertNode(changes, localPatches, xKey, yNode, yIndex, inserts); index += xNode.b || 0; index++; _VirtualDom_removeNode(changes, localPatches, xKey, xNextNode, index); index += xNextNode.b || 0; xIndex += 2; yIndex += 2; continue; } // insert y if (newMatch) { index++; _VirtualDom_insertNode(changes, localPatches, yKey, yNode, yIndex, inserts); _VirtualDom_diffHelp(xNode, yNextNode, localPatches, index); index += xNode.b || 0; xIndex += 1; yIndex += 2; continue; } // remove x if (oldMatch) { index++; _VirtualDom_removeNode(changes, localPatches, xKey, xNode, index); index += xNode.b || 0; index++; _VirtualDom_diffHelp(xNextNode, yNode, localPatches, index); index += xNextNode.b || 0; xIndex += 2; yIndex += 1; continue; } // remove x, insert y if (xNext && xNextKey === yNextKey) { index++; _VirtualDom_removeNode(changes, localPatches, xKey, xNode, index); _VirtualDom_insertNode(changes, localPatches, yKey, yNode, yIndex, inserts); index += xNode.b || 0; index++; _VirtualDom_diffHelp(xNextNode, yNextNode, localPatches, index); index += xNextNode.b || 0; xIndex += 2; yIndex += 2; continue; } break; } // eat up any remaining nodes with removeNode and insertNode while (xIndex < xLen) { index++; var x = xKids[xIndex]; var xNode = x.b; _VirtualDom_removeNode(changes, localPatches, x.a, xNode, index); index += xNode.b || 0; xIndex++; } while (yIndex < yLen) { var endInserts = endInserts || []; var y = yKids[yIndex]; _VirtualDom_insertNode(changes, localPatches, y.a, y.b, undefined, endInserts); yIndex++; } if (localPatches.length > 0 || inserts.length > 0 || endInserts) { _VirtualDom_pushPatch(patches, 8, rootIndex, { w: localPatches, x: inserts, y: endInserts }); } } // CHANGES FROM KEYED DIFF var _VirtualDom_POSTFIX = '_elmW6BL'; function _VirtualDom_insertNode(changes, localPatches, key, vnode, yIndex, inserts) { var entry = changes[key]; // never seen this key before if (!entry) { entry = { c: 0, z: vnode, r: yIndex, s: undefined }; inserts.push({ r: yIndex, A: entry }); changes[key] = entry; return; } // this key was removed earlier, a match! if (entry.c === 1) { inserts.push({ r: yIndex, A: entry }); entry.c = 2; var subPatches = []; _VirtualDom_diffHelp(entry.z, vnode, subPatches, entry.r); entry.r = yIndex; entry.s.s = { w: subPatches, A: entry }; return; } // this key has already been inserted or moved, a duplicate! _VirtualDom_insertNode(changes, localPatches, key + _VirtualDom_POSTFIX, vnode, yIndex, inserts); } function _VirtualDom_removeNode(changes, localPatches, key, vnode, index) { var entry = changes[key]; // never seen this key before if (!entry) { var patch = _VirtualDom_pushPatch(localPatches, 9, index, undefined); changes[key] = { c: 1, z: vnode, r: index, s: patch }; return; } // this key was inserted earlier, a match! if (entry.c === 0) { entry.c = 2; var subPatches = []; _VirtualDom_diffHelp(vnode, entry.z, subPatches, index); _VirtualDom_pushPatch(localPatches, 9, index, { w: subPatches, A: entry }); return; } // this key has already been removed or moved, a duplicate! _VirtualDom_removeNode(changes, localPatches, key + _VirtualDom_POSTFIX, vnode, index); } // ADD DOM NODES // // Each DOM node has an "index" assigned in order of traversal. It is important // to minimize our crawl over the actual DOM, so these indexes (along with the // descendantsCount of virtual nodes) let us skip touching entire subtrees of // the DOM if we know there are no patches there. function _VirtualDom_addDomNodes(domNode, vNode, patches, eventNode) { _VirtualDom_addDomNodesHelp(domNode, vNode, patches, 0, 0, vNode.b, eventNode); } // assumes `patches` is non-empty and indexes increase monotonically. function _VirtualDom_addDomNodesHelp(domNode, vNode, patches, i, low, high, eventNode) { var patch = patches[i]; var index = patch.r; while (index === low) { var patchType = patch.$; if (patchType === 1) { _VirtualDom_addDomNodes(domNode, vNode.k, patch.s, eventNode); } else if (patchType === 8) { patch.t = domNode; patch.u = eventNode; var subPatches = patch.s.w; if (subPatches.length > 0) { _VirtualDom_addDomNodesHelp(domNode, vNode, subPatches, 0, low, high, eventNode); } } else if (patchType === 9) { patch.t = domNode; patch.u = eventNode; var data = patch.s; if (data) { data.A.s = domNode; var subPatches = data.w; if (subPatches.length > 0) { _VirtualDom_addDomNodesHelp(domNode, vNode, subPatches, 0, low, high, eventNode); } } } else { patch.t = domNode; patch.u = eventNode; } i++; if (!(patch = patches[i]) || (index = patch.r) > high) { return i; } } var tag = vNode.$; if (tag === 4) { var subNode = vNode.k; while (subNode.$ === 4) { subNode = subNode.k; } return _VirtualDom_addDomNodesHelp(domNode, subNode, patches, i, low + 1, high, domNode.elm_event_node_ref); } // tag must be 1 or 2 at this point var vKids = vNode.e; var childNodes = domNode.childNodes; for (var j = 0; j < vKids.length; j++) { low++; var vKid = tag === 1 ? vKids[j] : vKids[j].b; var nextLow = low + (vKid.b || 0); if (low <= index && index <= nextLow) { i = _VirtualDom_addDomNodesHelp(childNodes[j], vKid, patches, i, low, nextLow, eventNode); if (!(patch = patches[i]) || (index = patch.r) > high) { return i; } } low = nextLow; } return i; } // APPLY PATCHES function _VirtualDom_applyPatches(rootDomNode, oldVirtualNode, patches, eventNode) { if (patches.length === 0) { return rootDomNode; } _VirtualDom_addDomNodes(rootDomNode, oldVirtualNode, patches, eventNode); return _VirtualDom_applyPatchesHelp(rootDomNode, patches); } function _VirtualDom_applyPatchesHelp(rootDomNode, patches) { for (var i = 0; i < patches.length; i++) { var patch = patches[i]; var localDomNode = patch.t var newNode = _VirtualDom_applyPatch(localDomNode, patch); if (localDomNode === rootDomNode) { rootDomNode = newNode; } } return rootDomNode; } function _VirtualDom_applyPatch(domNode, patch) { switch (patch.$) { case 0: return _VirtualDom_applyPatchRedraw(domNode, patch.s, patch.u); case 4: _VirtualDom_applyFacts(domNode, patch.u, patch.s); return domNode; case 3: domNode.replaceData(0, domNode.length, patch.s); return domNode; case 1: return _VirtualDom_applyPatchesHelp(domNode, patch.s); case 2: if (domNode.elm_event_node_ref) { domNode.elm_event_node_ref.j = patch.s; } else { domNode.elm_event_node_ref = { j: patch.s, p: patch.u }; } return domNode; case 6: var data = patch.s; for (var i = 0; i < data.i; i++) { domNode.removeChild(domNode.childNodes[data.v]); } return domNode; case 7: var data = patch.s; var kids = data.e; var i = data.v; var theEnd = domNode.childNodes[i]; for (; i < kids.length; i++) { domNode.insertBefore(_VirtualDom_render(kids[i], patch.u), theEnd); } return domNode; case 9: var data = patch.s; if (!data) { domNode.parentNode.removeChild(domNode); return domNode; } var entry = data.A; if (typeof entry.r !== 'undefined') { domNode.parentNode.removeChild(domNode); } entry.s = _VirtualDom_applyPatchesHelp(domNode, data.w); return domNode; case 8: return _VirtualDom_applyPatchReorder(domNode, patch); case 5: return patch.s(domNode); default: _Debug_crash(10); // 'Ran into an unknown patch!' } } function _VirtualDom_applyPatchRedraw(domNode, vNode, eventNode) { var parentNode = domNode.parentNode; var newNode = _VirtualDom_render(vNode, eventNode); if (!newNode.elm_event_node_ref) { newNode.elm_event_node_ref = domNode.elm_event_node_ref; } if (parentNode && newNode !== domNode) { parentNode.replaceChild(newNode, domNode); } return newNode; } function _VirtualDom_applyPatchReorder(domNode, patch) { var data = patch.s; // remove end inserts var frag = _VirtualDom_applyPatchReorderEndInsertsHelp(data.y, patch); // removals domNode = _VirtualDom_applyPatchesHelp(domNode, data.w); // inserts var inserts = data.x; for (var i = 0; i < inserts.length; i++) { var insert = inserts[i]; var entry = insert.A; var node = entry.c === 2 ? entry.s : _VirtualDom_render(entry.z, patch.u); domNode.insertBefore(node, domNode.childNodes[insert.r]); } // add end inserts if (frag) { _VirtualDom_appendChild(domNode, frag); } return domNode; } function _VirtualDom_applyPatchReorderEndInsertsHelp(endInserts, patch) { if (!endInserts) { return; } var frag = _VirtualDom_doc.createDocumentFragment(); for (var i = 0; i < endInserts.length; i++) { var insert = endInserts[i]; var entry = insert.A; _VirtualDom_appendChild(frag, entry.c === 2 ? entry.s : _VirtualDom_render(entry.z, patch.u) ); } return frag; } function _VirtualDom_virtualize(node) { // TEXT NODES if (node.nodeType === 3) { return _VirtualDom_text(node.textContent); } // WEIRD NODES if (node.nodeType !== 1) { return _VirtualDom_text(''); } // ELEMENT NODES var attrList = _List_Nil; var attrs = node.attributes; for (var i = attrs.length; i--; ) { var attr = attrs[i]; var name = attr.name; var value = attr.value; attrList = _List_Cons( A2(_VirtualDom_attribute, name, value), attrList ); } var tag = node.tagName.toLowerCase(); var kidList = _List_Nil; var kids = node.childNodes; for (var i = kids.length; i--; ) { kidList = _List_Cons(_VirtualDom_virtualize(kids[i]), kidList); } return A3(_VirtualDom_node, tag, attrList, kidList); } function _VirtualDom_dekey(keyedNode) { var keyedKids = keyedNode.e; var len = keyedKids.length; var kids = new Array(len); for (var i = 0; i < len; i++) { kids[i] = keyedKids[i].b; } return { $: 1, c: keyedNode.c, d: keyedNode.d, e: kids, f: keyedNode.f, b: keyedNode.b }; } // ELEMENT var _Debugger_element; var _Browser_element = _Debugger_element || F4(function(impl, flagDecoder, debugMetadata, args) { return _Platform_initialize( flagDecoder, args, impl.aB, impl.aJ, impl.aH, function(sendToApp, initialModel) { var view = impl.aK; /**/ var domNode = args['node']; //*/ /**_UNUSED/ var domNode = args && args['node'] ? args['node'] : _Debug_crash(0); //*/ var currNode = _VirtualDom_virtualize(domNode); return _Browser_makeAnimator(initialModel, function(model) { var nextNode = view(model); var patches = _VirtualDom_diff(currNode, nextNode); domNode = _VirtualDom_applyPatches(domNode, currNode, patches, sendToApp); currNode = nextNode; }); } ); }); // DOCUMENT var _Debugger_document; var _Browser_document = _Debugger_document || F4(function(impl, flagDecoder, debugMetadata, args) { return _Platform_initialize( flagDecoder, args, impl.aB, impl.aJ, impl.aH, function(sendToApp, initialModel) { var divertHrefToApp = impl.P && impl.P(sendToApp) var view = impl.aK; var title = _VirtualDom_doc.title; var bodyNode = _VirtualDom_doc.body; var currNode = _VirtualDom_virtualize(bodyNode); return _Browser_makeAnimator(initialModel, function(model) { _VirtualDom_divertHrefToApp = divertHrefToApp; var doc = view(model); var nextNode = _VirtualDom_node('body')(_List_Nil)(doc.au); var patches = _VirtualDom_diff(currNode, nextNode); bodyNode = _VirtualDom_applyPatches(bodyNode, currNode, patches, sendToApp); currNode = nextNode; _VirtualDom_divertHrefToApp = 0; (title !== doc.aI) && (_VirtualDom_doc.title = title = doc.aI); }); } ); }); // ANIMATION var _Browser_cancelAnimationFrame = typeof cancelAnimationFrame !== 'undefined' ? cancelAnimationFrame : function(id) { clearTimeout(id); }; var _Browser_requestAnimationFrame = typeof requestAnimationFrame !== 'undefined' ? requestAnimationFrame : function(callback) { return setTimeout(callback, 1000 / 60); }; function _Browser_makeAnimator(model, draw) { draw(model); var state = 0; function updateIfNeeded() { state = state === 1 ? 0 : ( _Browser_requestAnimationFrame(updateIfNeeded), draw(model), 1 ); } return function(nextModel, isSync) { model = nextModel; isSync ? ( draw(model), state === 2 && (state = 1) ) : ( state === 0 && _Browser_requestAnimationFrame(updateIfNeeded), state = 2 ); }; } // APPLICATION function _Browser_application(impl) { var onUrlChange = impl.aD; var onUrlRequest = impl.aE; var key = function() { key.a(onUrlChange(_Browser_getUrl())); }; return _Browser_document({ P: function(sendToApp) { key.a = sendToApp; _Browser_window.addEventListener('popstate', key); _Browser_window.navigator.userAgent.indexOf('Trident') < 0 || _Browser_window.addEventListener('hashchange', key); return F2(function(domNode, event) { if (!event.ctrlKey && !event.metaKey && !event.shiftKey && event.button < 1 && !domNode.target && !domNode.hasAttribute('download')) { event.preventDefault(); var href = domNode.href; var curr = _Browser_getUrl(); var next = $elm$url$Url$fromString(href).a; sendToApp(onUrlRequest( (next && curr.ah === next.ah && curr.Z === next.Z && curr.ae.a === next.ae.a ) ? $elm$browser$Browser$Internal(next) : $elm$browser$Browser$External(href) )); } }); }, aB: function(flags) { return A3(impl.aB, flags, _Browser_getUrl(), key); }, aK: impl.aK, aJ: impl.aJ, aH: impl.aH }); } function _Browser_getUrl() { return $elm$url$Url$fromString(_VirtualDom_doc.location.href).a || _Debug_crash(1); } var _Browser_go = F2(function(key, n) { return A2($elm$core$Task$perform, $elm$core$Basics$never, _Scheduler_binding(function() { n && history.go(n); key(); })); }); var _Browser_pushUrl = F2(function(key, url) { return A2($elm$core$Task$perform, $elm$core$Basics$never, _Scheduler_binding(function() { history.pushState({}, '', url); key(); })); }); var _Browser_replaceUrl = F2(function(key, url) { return A2($elm$core$Task$perform, $elm$core$Basics$never, _Scheduler_binding(function() { history.replaceState({}, '', url); key(); })); }); // GLOBAL EVENTS var _Browser_fakeNode = { addEventListener: function() {}, removeEventListener: function() {} }; var _Browser_doc = typeof document !== 'undefined' ? document : _Browser_fakeNode; var _Browser_window = typeof window !== 'undefined' ? window : _Browser_fakeNode; var _Browser_on = F3(function(node, eventName, sendToSelf) { return _Scheduler_spawn(_Scheduler_binding(function(callback) { function handler(event) { _Scheduler_rawSpawn(sendToSelf(event)); } node.addEventListener(eventName, handler, _VirtualDom_passiveSupported && { passive: true }); return function() { node.removeEventListener(eventName, handler); }; })); }); var _Browser_decodeEvent = F2(function(decoder, event) { var result = _Json_runHelp(decoder, event); return $elm$core$Result$isOk(result) ? $elm$core$Maybe$Just(result.a) : $elm$core$Maybe$Nothing; }); // PAGE VISIBILITY function _Browser_visibilityInfo() { return (typeof _VirtualDom_doc.hidden !== 'undefined') ? { az: 'hidden', av: 'visibilitychange' } : (typeof _VirtualDom_doc.mozHidden !== 'undefined') ? { az: 'mozHidden', av: 'mozvisibilitychange' } : (typeof _VirtualDom_doc.msHidden !== 'undefined') ? { az: 'msHidden', av: 'msvisibilitychange' } : (typeof _VirtualDom_doc.webkitHidden !== 'undefined') ? { az: 'webkitHidden', av: 'webkitvisibilitychange' } : { az: 'hidden', av: 'visibilitychange' }; } // ANIMATION FRAMES function _Browser_rAF() { return _Scheduler_binding(function(callback) { var id = _Browser_requestAnimationFrame(function() { callback(_Scheduler_succeed(Date.now())); }); return function() { _Browser_cancelAnimationFrame(id); }; }); } function _Browser_now() { return _Scheduler_binding(function(callback) { callback(_Scheduler_succeed(Date.now())); }); } // DOM STUFF function _Browser_withNode(id, doStuff) { return _Scheduler_binding(function(callback) { _Browser_requestAnimationFrame(function() { var node = document.getElementById(id); callback(node ? _Scheduler_succeed(doStuff(node)) : _Scheduler_fail($elm$browser$Browser$Dom$NotFound(id)) ); }); }); } function _Browser_withWindow(doStuff) { return _Scheduler_binding(function(callback) { _Browser_requestAnimationFrame(function() { callback(_Scheduler_succeed(doStuff())); }); }); } // FOCUS and BLUR var _Browser_call = F2(function(functionName, id) { return _Browser_withNode(id, function(node) { node[functionName](); return _Utils_Tuple0; }); }); // WINDOW VIEWPORT function _Browser_getViewport() { return { al: _Browser_getScene(), ao: { aq: _Browser_window.pageXOffset, ar: _Browser_window.pageYOffset, ap: _Browser_doc.documentElement.clientWidth, Y: _Browser_doc.documentElement.clientHeight } }; } function _Browser_getScene() { var body = _Browser_doc.body; var elem = _Browser_doc.documentElement; return { ap: Math.max(body.scrollWidth, body.offsetWidth, elem.scrollWidth, elem.offsetWidth, elem.clientWidth), Y: Math.max(body.scrollHeight, body.offsetHeight, elem.scrollHeight, elem.offsetHeight, elem.clientHeight) }; } var _Browser_setViewport = F2(function(x, y) { return _Browser_withWindow(function() { _Browser_window.scroll(x, y); return _Utils_Tuple0; }); }); // ELEMENT VIEWPORT function _Browser_getViewportOf(id) { return _Browser_withNode(id, function(node) { return { al: { ap: node.scrollWidth, Y: node.scrollHeight }, ao: { aq: node.scrollLeft, ar: node.scrollTop, ap: node.clientWidth, Y: node.clientHeight } }; }); } var _Browser_setViewportOf = F3(function(id, x, y) { return _Browser_withNode(id, function(node) { node.scrollLeft = x; node.scrollTop = y; return _Utils_Tuple0; }); }); // ELEMENT function _Browser_getElement(id) { return _Browser_withNode(id, function(node) { var rect = node.getBoundingClientRect(); var x = _Browser_window.pageXOffset; var y = _Browser_window.pageYOffset; return { al: _Browser_getScene(), ao: { aq: x, ar: y, ap: _Browser_doc.documentElement.clientWidth, Y: _Browser_doc.documentElement.clientHeight }, ax: { aq: x + rect.left, ar: y + rect.top, ap: rect.width, Y: rect.height } }; }); } // LOAD and RELOAD function _Browser_reload(skipCache) { return A2($elm$core$Task$perform, $elm$core$Basics$never, _Scheduler_binding(function(callback) { _VirtualDom_doc.location.reload(skipCache); })); } function _Browser_load(url) { return A2($elm$core$Task$perform, $elm$core$Basics$never, _Scheduler_binding(function(callback) { try { _Browser_window.location = url; } catch(err) { // Only Firefox can throw a NS_ERROR_MALFORMED_URI exception here. // Other browsers reload the page, so let's be consistent about that. _VirtualDom_doc.location.reload(false); } })); } var _Bitwise_and = F2(function(a, b) { return a & b; }); var _Bitwise_or = F2(function(a, b) { return a | b; }); var _Bitwise_xor = F2(function(a, b) { return a ^ b; }); function _Bitwise_complement(a) { return ~a; }; var _Bitwise_shiftLeftBy = F2(function(offset, a) { return a << offset; }); var _Bitwise_shiftRightBy = F2(function(offset, a) { return a >> offset; }); var _Bitwise_shiftRightZfBy = F2(function(offset, a) { return a >>> offset; }); function _Time_now(millisToPosix) { return _Scheduler_binding(function(callback) { callback(_Scheduler_succeed(millisToPosix(Date.now()))); }); } var _Time_setInterval = F2(function(interval, task) { return _Scheduler_binding(function(callback) { var id = setInterval(function() { _Scheduler_rawSpawn(task); }, interval); return function() { clearInterval(id); }; }); }); function _Time_here() { return _Scheduler_binding(function(callback) { callback(_Scheduler_succeed( A2($elm$time$Time$customZone, -(new Date().getTimezoneOffset()), _List_Nil) )); }); } function _Time_getZoneName() { return _Scheduler_binding(function(callback) { try { var name = $elm$time$Time$Name(Intl.DateTimeFormat().resolvedOptions().timeZone); } catch (e) { var name = $elm$time$Time$Offset(new Date().getTimezoneOffset()); } callback(_Scheduler_succeed(name)); }); } var $elm$core$Basics$EQ = 1; var $elm$core$Basics$GT = 2; var $elm$core$Basics$LT = 0; var $elm$core$List$cons = _List_cons; var $elm$core$Dict$foldr = F3( function (func, acc, t) { foldr: while (true) { if (t.$ === -2) { return acc; } else { var key = t.b; var value = t.c; var left = t.d; var right = t.e; var $temp$func = func, $temp$acc = A3( func, key, value, A3($elm$core$Dict$foldr, func, acc, right)), $temp$t = left; func = $temp$func; acc = $temp$acc; t = $temp$t; continue foldr; } } }); var $elm$core$Dict$toList = function (dict) { return A3( $elm$core$Dict$foldr, F3( function (key, value, list) { return A2( $elm$core$List$cons, _Utils_Tuple2(key, value), list); }), _List_Nil, dict); }; var $elm$core$Dict$keys = function (dict) { return A3( $elm$core$Dict$foldr, F3( function (key, value, keyList) { return A2($elm$core$List$cons, key, keyList); }), _List_Nil, dict); }; var $elm$core$Set$toList = function (_v0) { var dict = _v0; return $elm$core$Dict$keys(dict); }; var $elm$core$Elm$JsArray$foldr = _JsArray_foldr; var $elm$core$Array$foldr = F3( function (func, baseCase, _v0) { var tree = _v0.c; var tail = _v0.d; var helper = F2( function (node, acc) { if (!node.$) { var subTree = node.a; return A3($elm$core$Elm$JsArray$foldr, helper, acc, subTree); } else { var values = node.a; return A3($elm$core$Elm$JsArray$foldr, func, acc, values); } }); return A3( $elm$core$Elm$JsArray$foldr, helper, A3($elm$core$Elm$JsArray$foldr, func, baseCase, tail), tree); }); var $elm$core$Array$toList = function (array) { return A3($elm$core$Array$foldr, $elm$core$List$cons, _List_Nil, array); }; var $elm$core$Result$Err = function (a) { return {$: 1, a: a}; }; var $elm$json$Json$Decode$Failure = F2( function (a, b) { return {$: 3, a: a, b: b}; }); var $elm$json$Json$Decode$Field = F2( function (a, b) { return {$: 0, a: a, b: b}; }); var $elm$json$Json$Decode$Index = F2( function (a, b) { return {$: 1, a: a, b: b}; }); var $elm$core$Result$Ok = function (a) { return {$: 0, a: a}; }; var $elm$json$Json$Decode$OneOf = function (a) { return {$: 2, a: a}; }; var $elm$core$Basics$False = 1; var $elm$core$Basics$add = _Basics_add; var $elm$core$Maybe$Just = function (a) { return {$: 0, a: a}; }; var $elm$core$Maybe$Nothing = {$: 1}; var $elm$core$String$all = _String_all; var $elm$core$Basics$and = _Basics_and; var $elm$core$Basics$append = _Utils_append; var $elm$json$Json$Encode$encode = _Json_encode; var $elm$core$String$fromInt = _String_fromNumber; var $elm$core$String$join = F2( function (sep, chunks) { return A2( _String_join, sep, _List_toArray(chunks)); }); var $elm$core$String$split = F2( function (sep, string) { return _List_fromArray( A2(_String_split, sep, string)); }); var $elm$json$Json$Decode$indent = function (str) { return A2( $elm$core$String$join, '\n ', A2($elm$core$String$split, '\n', str)); }; var $elm$core$List$foldl = F3( function (func, acc, list) { foldl: while (true) { if (!list.b) { return acc; } else { var x = list.a; var xs = list.b; var $temp$func = func, $temp$acc = A2(func, x, acc), $temp$list = xs; func = $temp$func; acc = $temp$acc; list = $temp$list; continue foldl; } } }); var $elm$core$List$length = function (xs) { return A3( $elm$core$List$foldl, F2( function (_v0, i) { return i + 1; }), 0, xs); }; var $elm$core$List$map2 = _List_map2; var $elm$core$Basics$le = _Utils_le; var $elm$core$Basics$sub = _Basics_sub; var $elm$core$List$rangeHelp = F3( function (lo, hi, list) { rangeHelp: while (true) { if (_Utils_cmp(lo, hi) < 1) { var $temp$lo = lo, $temp$hi = hi - 1, $temp$list = A2($elm$core$List$cons, hi, list); lo = $temp$lo; hi = $temp$hi; list = $temp$list; continue rangeHelp; } else { return list; } } }); var $elm$core$List$range = F2( function (lo, hi) { return A3($elm$core$List$rangeHelp, lo, hi, _List_Nil); }); var $elm$core$List$indexedMap = F2( function (f, xs) { return A3( $elm$core$List$map2, f, A2( $elm$core$List$range, 0, $elm$core$List$length(xs) - 1), xs); }); var $elm$core$Char$toCode = _Char_toCode; var $elm$core$Char$isLower = function (_char) { var code = $elm$core$Char$toCode(_char); return (97 <= code) && (code <= 122); }; var $elm$core$Char$isUpper = function (_char) { var code = $elm$core$Char$toCode(_char); return (code <= 90) && (65 <= code); }; var $elm$core$Basics$or = _Basics_or; var $elm$core$Char$isAlpha = function (_char) { return $elm$core$Char$isLower(_char) || $elm$core$Char$isUpper(_char); }; var $elm$core$Char$isDigit = function (_char) { var code = $elm$core$Char$toCode(_char); return (code <= 57) && (48 <= code); }; var $elm$core$Char$isAlphaNum = function (_char) { return $elm$core$Char$isLower(_char) || ($elm$core$Char$isUpper(_char) || $elm$core$Char$isDigit(_char)); }; var $elm$core$List$reverse = function (list) { return A3($elm$core$List$foldl, $elm$core$List$cons, _List_Nil, list); }; var $elm$core$String$uncons = _String_uncons; var $elm$json$Json$Decode$errorOneOf = F2( function (i, error) { return '\n\n(' + ($elm$core$String$fromInt(i + 1) + (') ' + $elm$json$Json$Decode$indent( $elm$json$Json$Decode$errorToString(error)))); }); var $elm$json$Json$Decode$errorToString = function (error) { return A2($elm$json$Json$Decode$errorToStringHelp, error, _List_Nil); }; var $elm$json$Json$Decode$errorToStringHelp = F2( function (error, context) { errorToStringHelp: while (true) { switch (error.$) { case 0: var f = error.a; var err = error.b; var isSimple = function () { var _v1 = $elm$core$String$uncons(f); if (_v1.$ === 1) { return false; } else { var _v2 = _v1.a; var _char = _v2.a; var rest = _v2.b; return $elm$core$Char$isAlpha(_char) && A2($elm$core$String$all, $elm$core$Char$isAlphaNum, rest); } }(); var fieldName = isSimple ? ('.' + f) : ('[\'' + (f + '\']')); var $temp$error = err, $temp$context = A2($elm$core$List$cons, fieldName, context); error = $temp$error; context = $temp$context; continue errorToStringHelp; case 1: var i = error.a; var err = error.b; var indexName = '[' + ($elm$core$String$fromInt(i) + ']'); var $temp$error = err, $temp$context = A2($elm$core$List$cons, indexName, context); error = $temp$error; context = $temp$context; continue errorToStringHelp; case 2: var errors = error.a; if (!errors.b) { return 'Ran into a Json.Decode.oneOf with no possibilities' + function () { if (!context.b) { return '!'; } else { return ' at json' + A2( $elm$core$String$join, '', $elm$core$List$reverse(context)); } }(); } else { if (!errors.b.b) { var err = errors.a; var $temp$error = err, $temp$context = context; error = $temp$error; context = $temp$context; continue errorToStringHelp; } else { var starter = function () { if (!context.b) { return 'Json.Decode.oneOf'; } else { return 'The Json.Decode.oneOf at json' + A2( $elm$core$String$join, '', $elm$core$List$reverse(context)); } }(); var introduction = starter + (' failed in the following ' + ($elm$core$String$fromInt( $elm$core$List$length(errors)) + ' ways:')); return A2( $elm$core$String$join, '\n\n', A2( $elm$core$List$cons, introduction, A2($elm$core$List$indexedMap, $elm$json$Json$Decode$errorOneOf, errors))); } } default: var msg = error.a; var json = error.b; var introduction = function () { if (!context.b) { return 'Problem with the given value:\n\n'; } else { return 'Problem with the value at json' + (A2( $elm$core$String$join, '', $elm$core$List$reverse(context)) + ':\n\n '); } }(); return introduction + ($elm$json$Json$Decode$indent( A2($elm$json$Json$Encode$encode, 4, json)) + ('\n\n' + msg)); } } }); var $elm$core$Array$branchFactor = 32; var $elm$core$Array$Array_elm_builtin = F4( function (a, b, c, d) { return {$: 0, a: a, b: b, c: c, d: d}; }); var $elm$core$Elm$JsArray$empty = _JsArray_empty; var $elm$core$Basics$ceiling = _Basics_ceiling; var $elm$core$Basics$fdiv = _Basics_fdiv; var $elm$core$Basics$logBase = F2( function (base, number) { return _Basics_log(number) / _Basics_log(base); }); var $elm$core$Basics$toFloat = _Basics_toFloat; var $elm$core$Array$shiftStep = $elm$core$Basics$ceiling( A2($elm$core$Basics$logBase, 2, $elm$core$Array$branchFactor)); var $elm$core$Array$empty = A4($elm$core$Array$Array_elm_builtin, 0, $elm$core$Array$shiftStep, $elm$core$Elm$JsArray$empty, $elm$core$Elm$JsArray$empty); var $elm$core$Elm$JsArray$initialize = _JsArray_initialize; var $elm$core$Array$Leaf = function (a) { return {$: 1, a: a}; }; var $elm$core$Basics$apL = F2( function (f, x) { return f(x); }); var $elm$core$Basics$apR = F2( function (x, f) { return f(x); }); var $elm$core$Basics$eq = _Utils_equal; var $elm$core$Basics$floor = _Basics_floor; var $elm$core$Elm$JsArray$length = _JsArray_length; var $elm$core$Basics$gt = _Utils_gt; var $elm$core$Basics$max = F2( function (x, y) { return (_Utils_cmp(x, y) > 0) ? x : y; }); var $elm$core$Basics$mul = _Basics_mul; var $elm$core$Array$SubTree = function (a) { return {$: 0, a: a}; }; var $elm$core$Elm$JsArray$initializeFromList = _JsArray_initializeFromList; var $elm$core$Array$compressNodes = F2( function (nodes, acc) { compressNodes: while (true) { var _v0 = A2($elm$core$Elm$JsArray$initializeFromList, $elm$core$Array$branchFactor, nodes); var node = _v0.a; var remainingNodes = _v0.b; var newAcc = A2( $elm$core$List$cons, $elm$core$Array$SubTree(node), acc); if (!remainingNodes.b) { return $elm$core$List$reverse(newAcc); } else { var $temp$nodes = remainingNodes, $temp$acc = newAcc; nodes = $temp$nodes; acc = $temp$acc; continue compressNodes; } } }); var $elm$core$Tuple$first = function (_v0) { var x = _v0.a; return x; }; var $elm$core$Array$treeFromBuilder = F2( function (nodeList, nodeListSize) { treeFromBuilder: while (true) { var newNodeSize = $elm$core$Basics$ceiling(nodeListSize / $elm$core$Array$branchFactor); if (newNodeSize === 1) { return A2($elm$core$Elm$JsArray$initializeFromList, $elm$core$Array$branchFactor, nodeList).a; } else { var $temp$nodeList = A2($elm$core$Array$compressNodes, nodeList, _List_Nil), $temp$nodeListSize = newNodeSize; nodeList = $temp$nodeList; nodeListSize = $temp$nodeListSize; continue treeFromBuilder; } } }); var $elm$core$Array$builderToArray = F2( function (reverseNodeList, builder) { if (!builder.b) { return A4( $elm$core$Array$Array_elm_builtin, $elm$core$Elm$JsArray$length(builder.e), $elm$core$Array$shiftStep, $elm$core$Elm$JsArray$empty, builder.e); } else { var treeLen = builder.b * $elm$core$Array$branchFactor; var depth = $elm$core$Basics$floor( A2($elm$core$Basics$logBase, $elm$core$Array$branchFactor, treeLen - 1)); var correctNodeList = reverseNodeList ? $elm$core$List$reverse(builder.f) : builder.f; var tree = A2($elm$core$Array$treeFromBuilder, correctNodeList, builder.b); return A4( $elm$core$Array$Array_elm_builtin, $elm$core$Elm$JsArray$length(builder.e) + treeLen, A2($elm$core$Basics$max, 5, depth * $elm$core$Array$shiftStep), tree, builder.e); } }); var $elm$core$Basics$idiv = _Basics_idiv; var $elm$core$Basics$lt = _Utils_lt; var $elm$core$Array$initializeHelp = F5( function (fn, fromIndex, len, nodeList, tail) { initializeHelp: while (true) { if (fromIndex < 0) { return A2( $elm$core$Array$builderToArray, false, {f: nodeList, b: (len / $elm$core$Array$branchFactor) | 0, e: tail}); } else { var leaf = $elm$core$Array$Leaf( A3($elm$core$Elm$JsArray$initialize, $elm$core$Array$branchFactor, fromIndex, fn)); var $temp$fn = fn, $temp$fromIndex = fromIndex - $elm$core$Array$branchFactor, $temp$len = len, $temp$nodeList = A2($elm$core$List$cons, leaf, nodeList), $temp$tail = tail; fn = $temp$fn; fromIndex = $temp$fromIndex; len = $temp$len; nodeList = $temp$nodeList; tail = $temp$tail; continue initializeHelp; } } }); var $elm$core$Basics$remainderBy = _Basics_remainderBy; var $elm$core$Array$initialize = F2( function (len, fn) { if (len <= 0) { return $elm$core$Array$empty; } else { var tailLen = len % $elm$core$Array$branchFactor; var tail = A3($elm$core$Elm$JsArray$initialize, tailLen, len - tailLen, fn); var initialFromIndex = (len - tailLen) - $elm$core$Array$branchFactor; return A5($elm$core$Array$initializeHelp, fn, initialFromIndex, len, _List_Nil, tail); } }); var $elm$core$Basics$True = 0; var $elm$core$Result$isOk = function (result) { if (!result.$) { return true; } else { return false; } }; var $elm$json$Json$Decode$map = _Json_map1; var $elm$json$Json$Decode$map2 = _Json_map2; var $elm$json$Json$Decode$succeed = _Json_succeed; var $elm$virtual_dom$VirtualDom$toHandlerInt = function (handler) { switch (handler.$) { case 0: return 0; case 1: return 1; case 2: return 2; default: return 3; } }; var $elm$browser$Browser$External = function (a) { return {$: 1, a: a}; }; var $elm$browser$Browser$Internal = function (a) { return {$: 0, a: a}; }; var $elm$core$Basics$identity = function (x) { return x; }; var $elm$browser$Browser$Dom$NotFound = $elm$core$Basics$identity; var $elm$url$Url$Http = 0; var $elm$url$Url$Https = 1; var $elm$url$Url$Url = F6( function (protocol, host, port_, path, query, fragment) { return {X: fragment, Z: host, ac: path, ae: port_, ah: protocol, ai: query}; }); var $elm$core$String$contains = _String_contains; var $elm$core$String$length = _String_length; var $elm$core$String$slice = _String_slice; var $elm$core$String$dropLeft = F2( function (n, string) { return (n < 1) ? string : A3( $elm$core$String$slice, n, $elm$core$String$length(string), string); }); var $elm$core$String$indexes = _String_indexes; var $elm$core$String$isEmpty = function (string) { return string === ''; }; var $elm$core$String$left = F2( function (n, string) { return (n < 1) ? '' : A3($elm$core$String$slice, 0, n, string); }); var $elm$core$String$toInt = _String_toInt; var $elm$url$Url$chompBeforePath = F5( function (protocol, path, params, frag, str) { if ($elm$core$String$isEmpty(str) || A2($elm$core$String$contains, '@', str)) { return $elm$core$Maybe$Nothing; } else { var _v0 = A2($elm$core$String$indexes, ':', str); if (!_v0.b) { return $elm$core$Maybe$Just( A6($elm$url$Url$Url, protocol, str, $elm$core$Maybe$Nothing, path, params, frag)); } else { if (!_v0.b.b) { var i = _v0.a; var _v1 = $elm$core$String$toInt( A2($elm$core$String$dropLeft, i + 1, str)); if (_v1.$ === 1) { return $elm$core$Maybe$Nothing; } else { var port_ = _v1; return $elm$core$Maybe$Just( A6( $elm$url$Url$Url, protocol, A2($elm$core$String$left, i, str), port_, path, params, frag)); } } else { return $elm$core$Maybe$Nothing; } } } }); var $elm$url$Url$chompBeforeQuery = F4( function (protocol, params, frag, str) { if ($elm$core$String$isEmpty(str)) { return $elm$core$Maybe$Nothing; } else { var _v0 = A2($elm$core$String$indexes, '/', str); if (!_v0.b) { return A5($elm$url$Url$chompBeforePath, protocol, '/', params, frag, str); } else { var i = _v0.a; return A5( $elm$url$Url$chompBeforePath, protocol, A2($elm$core$String$dropLeft, i, str), params, frag, A2($elm$core$String$left, i, str)); } } }); var $elm$url$Url$chompBeforeFragment = F3( function (protocol, frag, str) { if ($elm$core$String$isEmpty(str)) { return $elm$core$Maybe$Nothing; } else { var _v0 = A2($elm$core$String$indexes, '?', str); if (!_v0.b) { return A4($elm$url$Url$chompBeforeQuery, protocol, $elm$core$Maybe$Nothing, frag, str); } else { var i = _v0.a; return A4( $elm$url$Url$chompBeforeQuery, protocol, $elm$core$Maybe$Just( A2($elm$core$String$dropLeft, i + 1, str)), frag, A2($elm$core$String$left, i, str)); } } }); var $elm$url$Url$chompAfterProtocol = F2( function (protocol, str) { if ($elm$core$String$isEmpty(str)) { return $elm$core$Maybe$Nothing; } else { var _v0 = A2($elm$core$String$indexes, '#', str); if (!_v0.b) { return A3($elm$url$Url$chompBeforeFragment, protocol, $elm$core$Maybe$Nothing, str); } else { var i = _v0.a; return A3( $elm$url$Url$chompBeforeFragment, protocol, $elm$core$Maybe$Just( A2($elm$core$String$dropLeft, i + 1, str)), A2($elm$core$String$left, i, str)); } } }); var $elm$core$String$startsWith = _String_startsWith; var $elm$url$Url$fromString = function (str) { return A2($elm$core$String$startsWith, 'http://', str) ? A2( $elm$url$Url$chompAfterProtocol, 0, A2($elm$core$String$dropLeft, 7, str)) : (A2($elm$core$String$startsWith, 'https://', str) ? A2( $elm$url$Url$chompAfterProtocol, 1, A2($elm$core$String$dropLeft, 8, str)) : $elm$core$Maybe$Nothing); }; var $elm$core$Basics$never = function (_v0) { never: while (true) { var nvr = _v0; var $temp$_v0 = nvr; _v0 = $temp$_v0; continue never; } }; var $elm$core$Task$Perform = $elm$core$Basics$identity; var $elm$core$Task$succeed = _Scheduler_succeed; var $elm$core$Task$init = $elm$core$Task$succeed(0); var $elm$core$List$foldrHelper = F4( function (fn, acc, ctr, ls) { if (!ls.b) { return acc; } else { var a = ls.a; var r1 = ls.b; if (!r1.b) { return A2(fn, a, acc); } else { var b = r1.a; var r2 = r1.b; if (!r2.b) { return A2( fn, a, A2(fn, b, acc)); } else { var c = r2.a; var r3 = r2.b; if (!r3.b) { return A2( fn, a, A2( fn, b, A2(fn, c, acc))); } else { var d = r3.a; var r4 = r3.b; var res = (ctr > 500) ? A3( $elm$core$List$foldl, fn, acc, $elm$core$List$reverse(r4)) : A4($elm$core$List$foldrHelper, fn, acc, ctr + 1, r4); return A2( fn, a, A2( fn, b, A2( fn, c, A2(fn, d, res)))); } } } } }); var $elm$core$List$foldr = F3( function (fn, acc, ls) { return A4($elm$core$List$foldrHelper, fn, acc, 0, ls); }); var $elm$core$List$map = F2( function (f, xs) { return A3( $elm$core$List$foldr, F2( function (x, acc) { return A2( $elm$core$List$cons, f(x), acc); }), _List_Nil, xs); }); var $elm$core$Task$andThen = _Scheduler_andThen; var $elm$core$Task$map = F2( function (func, taskA) { return A2( $elm$core$Task$andThen, function (a) { return $elm$core$Task$succeed( func(a)); }, taskA); }); var $elm$core$Task$map2 = F3( function (func, taskA, taskB) { return A2( $elm$core$Task$andThen, function (a) { return A2( $elm$core$Task$andThen, function (b) { return $elm$core$Task$succeed( A2(func, a, b)); }, taskB); }, taskA); }); var $elm$core$Task$sequence = function (tasks) { return A3( $elm$core$List$foldr, $elm$core$Task$map2($elm$core$List$cons), $elm$core$Task$succeed(_List_Nil), tasks); }; var $elm$core$Platform$sendToApp = _Platform_sendToApp; var $elm$core$Task$spawnCmd = F2( function (router, _v0) { var task = _v0; return _Scheduler_spawn( A2( $elm$core$Task$andThen, $elm$core$Platform$sendToApp(router), task)); }); var $elm$core$Task$onEffects = F3( function (router, commands, state) { return A2( $elm$core$Task$map, function (_v0) { return 0; }, $elm$core$Task$sequence( A2( $elm$core$List$map, $elm$core$Task$spawnCmd(router), commands))); }); var $elm$core$Task$onSelfMsg = F3( function (_v0, _v1, _v2) { return $elm$core$Task$succeed(0); }); var $elm$core$Task$cmdMap = F2( function (tagger, _v0) { var task = _v0; return A2($elm$core$Task$map, tagger, task); }); _Platform_effectManagers['Task'] = _Platform_createManager($elm$core$Task$init, $elm$core$Task$onEffects, $elm$core$Task$onSelfMsg, $elm$core$Task$cmdMap); var $elm$core$Task$command = _Platform_leaf('Task'); var $elm$core$Task$perform = F2( function (toMessage, task) { return $elm$core$Task$command( A2($elm$core$Task$map, toMessage, task)); }); var $elm$browser$Browser$element = _Browser_element; var $author$project$Main$NewCard = function (a) { return {$: 2, a: a}; }; var $elm$random$Random$Generate = $elm$core$Basics$identity; var $elm$random$Random$Seed = F2( function (a, b) { return {$: 0, a: a, b: b}; }); var $elm$core$Bitwise$shiftRightZfBy = _Bitwise_shiftRightZfBy; var $elm$random$Random$next = function (_v0) { var state0 = _v0.a; var incr = _v0.b; return A2($elm$random$Random$Seed, ((state0 * 1664525) + incr) >>> 0, incr); }; var $elm$random$Random$initialSeed = function (x) { var _v0 = $elm$random$Random$next( A2($elm$random$Random$Seed, 0, 1013904223)); var state1 = _v0.a; var incr = _v0.b; var state2 = (state1 + x) >>> 0; return $elm$random$Random$next( A2($elm$random$Random$Seed, state2, incr)); }; var $elm$time$Time$Name = function (a) { return {$: 0, a: a}; }; var $elm$time$Time$Offset = function (a) { return {$: 1, a: a}; }; var $elm$time$Time$Zone = F2( function (a, b) { return {$: 0, a: a, b: b}; }); var $elm$time$Time$customZone = $elm$time$Time$Zone; var $elm$time$Time$Posix = $elm$core$Basics$identity; var $elm$time$Time$millisToPosix = $elm$core$Basics$identity; var $elm$time$Time$now = _Time_now($elm$time$Time$millisToPosix); var $elm$time$Time$posixToMillis = function (_v0) { var millis = _v0; return millis; }; var $elm$random$Random$init = A2( $elm$core$Task$andThen, function (time) { return $elm$core$Task$succeed( $elm$random$Random$initialSeed( $elm$time$Time$posixToMillis(time))); }, $elm$time$Time$now); var $elm$random$Random$step = F2( function (_v0, seed) { var generator = _v0; return generator(seed); }); var $elm$random$Random$onEffects = F3( function (router, commands, seed) { if (!commands.b) { return $elm$core$Task$succeed(seed); } else { var generator = commands.a; var rest = commands.b; var _v1 = A2($elm$random$Random$step, generator, seed); var value = _v1.a; var newSeed = _v1.b; return A2( $elm$core$Task$andThen, function (_v2) { return A3($elm$random$Random$onEffects, router, rest, newSeed); }, A2($elm$core$Platform$sendToApp, router, value)); } }); var $elm$random$Random$onSelfMsg = F3( function (_v0, _v1, seed) { return $elm$core$Task$succeed(seed); }); var $elm$random$Random$Generator = $elm$core$Basics$identity; var $elm$random$Random$map = F2( function (func, _v0) { var genA = _v0; return function (seed0) { var _v1 = genA(seed0); var a = _v1.a; var seed1 = _v1.b; return _Utils_Tuple2( func(a), seed1); }; }); var $elm$random$Random$cmdMap = F2( function (func, _v0) { var generator = _v0; return A2($elm$random$Random$map, func, generator); }); _Platform_effectManagers['Random'] = _Platform_createManager($elm$random$Random$init, $elm$random$Random$onEffects, $elm$random$Random$onSelfMsg, $elm$random$Random$cmdMap); var $elm$random$Random$command = _Platform_leaf('Random'); var $elm$random$Random$generate = F2( function (tagger, generator) { return $elm$random$Random$command( A2($elm$random$Random$map, tagger, generator)); }); var $elm$core$Bitwise$and = _Bitwise_and; var $elm$core$Basics$negate = function (n) { return -n; }; var $elm$core$Bitwise$xor = _Bitwise_xor; var $elm$random$Random$peel = function (_v0) { var state = _v0.a; var word = (state ^ (state >>> ((state >>> 28) + 4))) * 277803737; return ((word >>> 22) ^ word) >>> 0; }; var $elm$random$Random$int = F2( function (a, b) { return function (seed0) { var _v0 = (_Utils_cmp(a, b) < 0) ? _Utils_Tuple2(a, b) : _Utils_Tuple2(b, a); var lo = _v0.a; var hi = _v0.b; var range = (hi - lo) + 1; if (!((range - 1) & range)) { return _Utils_Tuple2( (((range - 1) & $elm$random$Random$peel(seed0)) >>> 0) + lo, $elm$random$Random$next(seed0)); } else { var threshhold = (((-range) >>> 0) % range) >>> 0; var accountForBias = function (seed) { accountForBias: while (true) { var x = $elm$random$Random$peel(seed); var seedN = $elm$random$Random$next(seed); if (_Utils_cmp(x, threshhold) < 0) { var $temp$seed = seedN; seed = $temp$seed; continue accountForBias; } else { return _Utils_Tuple2((x % range) + lo, seedN); } } }; return accountForBias(seed0); } }; }); var $author$project$Main$newCard = A2($elm$random$Random$int, 2, 14); var $author$project$Main$init = function (_v0) { return _Utils_Tuple2( { a: {d: $elm$core$Maybe$Nothing, g: $elm$core$Maybe$Nothing, w: $elm$core$Maybe$Nothing}, C: $elm$core$Maybe$Nothing, D: $elm$core$Maybe$Nothing, i: 100, j: 0 }, A2($elm$random$Random$generate, $author$project$Main$NewCard, $author$project$Main$newCard)); }; var $elm$core$Platform$Sub$batch = _Platform_batch; var $elm$core$Platform$Sub$none = $elm$core$Platform$Sub$batch(_List_Nil); var $author$project$Main$subscriptions = function (_v0) { return $elm$core$Platform$Sub$none; }; var $author$project$Main$NewCardC = function (a) { return {$: 3, a: a}; }; var $elm$core$Platform$Cmd$batch = _Platform_batch; var $elm$core$Platform$Cmd$none = $elm$core$Platform$Cmd$batch(_List_Nil); var $author$project$Main$calculateNewState = F2( function (cardC, model) { var _v0 = model.a.d; if (!_v0.$) { var cardA = _v0.a; var _v1 = model.a.g; if (!_v1.$) { var cardB = _v1.a; var currentGame = model.a; return (_Utils_eq(cardC, cardA) || _Utils_eq(cardC, cardB)) ? _Utils_Tuple2( model, A2($elm$random$Random$generate, $author$project$Main$NewCardC, $author$project$Main$newCard)) : (((_Utils_cmp(cardA, cardC) < 0) && (_Utils_cmp(cardC, cardB) < 0)) ? _Utils_Tuple2( _Utils_update( model, { a: _Utils_update( currentGame, {d: $elm$core$Maybe$Nothing, g: $elm$core$Maybe$Nothing}), D: $elm$core$Maybe$Just( { d: model.a.d, g: model.a.g, w: $elm$core$Maybe$Just(cardC) }), i: model.i + model.j }), A2($elm$random$Random$generate, $author$project$Main$NewCard, $author$project$Main$newCard)) : ((_Utils_cmp(model.j, model.i - model.j) > 0) ? _Utils_Tuple2( _Utils_update( model, { a: _Utils_update( currentGame, {d: $elm$core$Maybe$Nothing, g: $elm$core$Maybe$Nothing}), D: $elm$core$Maybe$Just( { d: model.a.d, g: model.a.g, w: $elm$core$Maybe$Just(cardC) }), i: model.i - model.j, j: model.i - model.j }), A2($elm$random$Random$generate, $author$project$Main$NewCard, $author$project$Main$newCard)) : _Utils_Tuple2( _Utils_update( model, { a: _Utils_update( currentGame, {d: $elm$core$Maybe$Nothing, g: $elm$core$Maybe$Nothing}), D: $elm$core$Maybe$Just( { d: model.a.d, g: model.a.g, w: $elm$core$Maybe$Just(cardC) }), i: model.i - model.j }), A2($elm$random$Random$generate, $author$project$Main$NewCard, $author$project$Main$newCard)))); } else { return _Utils_Tuple2(model, $elm$core$Platform$Cmd$none); } } else { return _Utils_Tuple2(model, $elm$core$Platform$Cmd$none); } }); var $author$project$Main$update = F2( function (msg, model) { switch (msg.$) { case 0: var bet = msg.a; return _Utils_Tuple2( _Utils_update( model, {j: bet}), $elm$core$Platform$Cmd$none); case 1: var value = msg.a; var _v1 = $elm$core$String$toInt(value); if (!_v1.$) { var newValue = _v1.a; return (_Utils_cmp(newValue, model.i) > 0) ? _Utils_Tuple2( _Utils_update( model, { C: $elm$core$Maybe$Just('You cannot bet more than you have'), j: model.i }), $elm$core$Platform$Cmd$none) : _Utils_Tuple2( _Utils_update( model, {C: $elm$core$Maybe$Nothing, j: newValue}), $elm$core$Platform$Cmd$none); } else { return _Utils_Tuple2( _Utils_update( model, { C: $elm$core$Maybe$Just('Wrong input for bet') }), $elm$core$Platform$Cmd$none); } case 2: var card = msg.a; var _v2 = model.a.d; if (_v2.$ === 1) { var currentGame = model.a; return (card > 13) ? _Utils_Tuple2( model, A2($elm$random$Random$generate, $author$project$Main$NewCard, $author$project$Main$newCard)) : _Utils_Tuple2( _Utils_update( model, { a: _Utils_update( currentGame, { d: $elm$core$Maybe$Just(card) }) }), A2($elm$random$Random$generate, $author$project$Main$NewCard, $author$project$Main$newCard)); } else { var cardA = _v2.a; var currentGame = model.a; return (_Utils_cmp(card, cardA) < 1) ? _Utils_Tuple2( _Utils_update( model, { a: _Utils_update( currentGame, { d: $elm$core$Maybe$Just(card) }) }), A2($elm$random$Random$generate, $author$project$Main$NewCard, $author$project$Main$newCard)) : _Utils_Tuple2( _Utils_update( model, { a: _Utils_update( currentGame, { g: $elm$core$Maybe$Just(card) }) }), $elm$core$Platform$Cmd$none); } case 4: return _Utils_Tuple2( model, A2($elm$random$Random$generate, $author$project$Main$NewCardC, $author$project$Main$newCard)); case 3: var card = msg.a; return A2($author$project$Main$calculateNewState, card, model); default: return $author$project$Main$init(0); } }); var $elm$virtual_dom$VirtualDom$style = _VirtualDom_style; var $elm$html$Html$Attributes$style = $elm$virtual_dom$VirtualDom$style; var $author$project$Main$centerHeadlineStyle = _List_fromArray( [ A2($elm$html$Html$Attributes$style, 'display', 'grid'), A2($elm$html$Html$Attributes$style, 'place-items', 'center'), A2($elm$html$Html$Attributes$style, 'margin', '2rem') ]); var $elm$html$Html$div = _VirtualDom_node('div'); var $author$project$Main$NewGame = {$: 5}; var $author$project$Main$Play = {$: 4}; var $author$project$Main$UpdateBetValue = function (a) { return {$: 1, a: a}; }; var $elm$html$Html$article = _VirtualDom_node('article'); var $elm$html$Html$button = _VirtualDom_node('button'); var $author$project$Main$cardContentPStyle = _List_fromArray( [ A2($elm$html$Html$Attributes$style, 'font-size', '2rem') ]); var $author$project$Main$cardToString = function (card) { if (!card.$) { var value = card.a; if (value < 11) { return $elm$core$String$fromInt(value); } else { switch (value) { case 11: return 'Jack'; case 12: return 'Queen'; case 13: return 'King'; case 14: return 'Ace'; default: return 'impossible value'; } } } else { return '-'; } }; var $author$project$Main$gameStyle = _List_fromArray( [ A2($elm$html$Html$Attributes$style, 'width', '100%'), A2($elm$html$Html$Attributes$style, 'max-width', '70rem') ]); var $elm$html$Html$input = _VirtualDom_node('input'); var $elm$json$Json$Encode$string = _Json_wrap; var $elm$html$Html$Attributes$stringProperty = F2( function (key, string) { return A2( _VirtualDom_property, key, $elm$json$Json$Encode$string(string)); }); var $elm$html$Html$Attributes$max = $elm$html$Html$Attributes$stringProperty('max'); var $elm$html$Html$Attributes$min = $elm$html$Html$Attributes$stringProperty('min'); var $elm$virtual_dom$VirtualDom$Normal = function (a) { return {$: 0, a: a}; }; var $elm$virtual_dom$VirtualDom$on = _VirtualDom_on; var $elm$html$Html$Events$on = F2( function (event, decoder) { return A2( $elm$virtual_dom$VirtualDom$on, event, $elm$virtual_dom$VirtualDom$Normal(decoder)); }); var $elm$html$Html$Events$onClick = function (msg) { return A2( $elm$html$Html$Events$on, 'click', $elm$json$Json$Decode$succeed(msg)); }; var $elm$html$Html$Events$alwaysStop = function (x) { return _Utils_Tuple2(x, true); }; var $elm$virtual_dom$VirtualDom$MayStopPropagation = function (a) { return {$: 1, a: a}; }; var $elm$html$Html$Events$stopPropagationOn = F2( function (event, decoder) { return A2( $elm$virtual_dom$VirtualDom$on, event, $elm$virtual_dom$VirtualDom$MayStopPropagation(decoder)); }); var $elm$json$Json$Decode$field = _Json_decodeField; var $elm$json$Json$Decode$at = F2( function (fields, decoder) { return A3($elm$core$List$foldr, $elm$json$Json$Decode$field, decoder, fields); }); var $elm$json$Json$Decode$string = _Json_decodeString; var $elm$html$Html$Events$targetValue = A2( $elm$json$Json$Decode$at, _List_fromArray( ['target', 'value']), $elm$json$Json$Decode$string); var $elm$html$Html$Events$onInput = function (tagger) { return A2( $elm$html$Html$Events$stopPropagationOn, 'input', A2( $elm$json$Json$Decode$map, $elm$html$Html$Events$alwaysStop, A2($elm$json$Json$Decode$map, tagger, $elm$html$Html$Events$targetValue))); }; var $elm$html$Html$p = _VirtualDom_node('p'); var $author$project$Main$standardFontSize = A2($elm$html$Html$Attributes$style, 'font-size', '2rem'); var $elm$virtual_dom$VirtualDom$text = _VirtualDom_text; var $elm$html$Html$text = $elm$virtual_dom$VirtualDom$text; var $author$project$Main$showError = function (value) { if (!value.$) { var string = value.a; return A2( $elm$html$Html$p, _List_fromArray( [$author$project$Main$standardFontSize]), _List_fromArray( [ $elm$html$Html$text(string) ])); } else { return A2($elm$html$Html$div, _List_Nil, _List_Nil); } }; var $author$project$Main$getGameStateMessage = F3( function (cardA, cardB, cardC) { return ((_Utils_cmp(cardA, cardC) < 0) && (_Utils_cmp(cardB, cardC) > 0)) ? A2( $elm$html$Html$div, _List_fromArray( [$author$project$Main$standardFontSize]), _List_fromArray( [ $elm$html$Html$text('You won :)') ])) : A2( $elm$html$Html$div, _List_fromArray( [$author$project$Main$standardFontSize]), _List_fromArray( [ $elm$html$Html$text('You loose :(') ])); }); var $elm$core$Maybe$map3 = F4( function (func, ma, mb, mc) { if (ma.$ === 1) { return $elm$core$Maybe$Nothing; } else { var a = ma.a; if (mb.$ === 1) { return $elm$core$Maybe$Nothing; } else { var b = mb.a; if (mc.$ === 1) { return $elm$core$Maybe$Nothing; } else { var c = mc.a; return $elm$core$Maybe$Just( A3(func, a, b, c)); } } } }); var $elm$core$Maybe$withDefault = F2( function (_default, maybe) { if (!maybe.$) { var value = maybe.a; return value; } else { return _default; } }); var $author$project$Main$showLastWinLose = function (game) { return A2( $elm$core$Maybe$withDefault, $elm$html$Html$text('something is wrong'), A4($elm$core$Maybe$map3, $author$project$Main$getGameStateMessage, game.d, game.g, game.w)); }; var $author$project$Main$showLastGame = function (game) { if (game.$ === 1) { return A2( $elm$html$Html$div, _List_fromArray( [$author$project$Main$standardFontSize]), _List_fromArray( [ $elm$html$Html$text('This is your first game') ])); } else { var value = game.a; return A2( $elm$html$Html$div, _List_Nil, _List_fromArray( [ $author$project$Main$showLastWinLose(value), A2( $elm$html$Html$p, $author$project$Main$cardContentPStyle, _List_fromArray( [ $elm$html$Html$text( 'Card 1: ' + $author$project$Main$cardToString(value.d)) ])), A2( $elm$html$Html$p, $author$project$Main$cardContentPStyle, _List_fromArray( [ $elm$html$Html$text( 'Card 2: ' + $author$project$Main$cardToString(value.g)) ])), A2( $elm$html$Html$p, $author$project$Main$cardContentPStyle, _List_fromArray( [ $elm$html$Html$text( 'Drawn Card: ' + $author$project$Main$cardToString(value.w)) ])) ])); } }; var $elm$html$Html$Attributes$type_ = $elm$html$Html$Attributes$stringProperty('type'); var $elm$html$Html$Attributes$value = $elm$html$Html$Attributes$stringProperty('value'); var $author$project$Main$showGame = function (model) { return (model.i <= 0) ? A2( $elm$html$Html$article, $author$project$Main$gameStyle, _List_fromArray( [ A2( $elm$html$Html$p, $author$project$Main$cardContentPStyle, _List_fromArray( [ $elm$html$Html$text('You lose all you money') ])), A2( $elm$html$Html$button, _List_fromArray( [ $elm$html$Html$Events$onClick($author$project$Main$NewGame), $author$project$Main$standardFontSize ]), _List_fromArray( [ $elm$html$Html$text('Again') ])) ])) : A2( $elm$html$Html$article, $author$project$Main$gameStyle, _List_fromArray( [ A2( $elm$html$Html$p, $author$project$Main$cardContentPStyle, _List_fromArray( [ $elm$html$Html$text( 'Currently you have ' + ($elm$core$String$fromInt(model.i) + ' in your pocket.')) ])), A2( $elm$html$Html$p, $author$project$Main$cardContentPStyle, _List_fromArray( [ $elm$html$Html$text( 'Card 1: ' + $author$project$Main$cardToString(model.a.d)) ])), A2( $elm$html$Html$p, $author$project$Main$cardContentPStyle, _List_fromArray( [ $elm$html$Html$text( 'Card 2: ' + $author$project$Main$cardToString(model.a.g)) ])), A2( $elm$html$Html$p, $author$project$Main$cardContentPStyle, _List_fromArray( [ $elm$html$Html$text( 'Your current bet is ' + $elm$core$String$fromInt(model.j)) ])), A2( $elm$html$Html$input, _List_fromArray( [ $elm$html$Html$Attributes$type_('range'), $elm$html$Html$Attributes$max( $elm$core$String$fromInt(model.i)), $elm$html$Html$Attributes$min('0'), $elm$html$Html$Attributes$value( $elm$core$String$fromInt(model.j)), $elm$html$Html$Events$onInput($author$project$Main$UpdateBetValue) ]), _List_Nil), A2( $elm$html$Html$button, _List_fromArray( [ $elm$html$Html$Events$onClick($author$project$Main$Play), $author$project$Main$standardFontSize ]), _List_fromArray( [ $elm$html$Html$text('Play') ])), $author$project$Main$showLastGame(model.D), $author$project$Main$showError(model.C) ])); }; var $elm$html$Html$h1 = _VirtualDom_node('h1'); var $author$project$Main$headerStyle = _List_fromArray( [ A2($elm$html$Html$Attributes$style, 'font-size', '2rem'), A2($elm$html$Html$Attributes$style, 'text-align', 'center') ]); var $author$project$Main$showHeader = A2( $elm$html$Html$div, $author$project$Main$headerStyle, _List_fromArray( [ A2( $elm$html$Html$h1, _List_fromArray( [ A2($elm$html$Html$Attributes$style, 'font-size', '4rem') ]), _List_fromArray( [ $elm$html$Html$text('ACEY DUCEY CARD GAME') ])), A2( $elm$html$Html$div, _List_Nil, _List_fromArray( [ $elm$html$Html$text('Creative Computing Morristown, New Jersey') ])), A2( $elm$html$Html$div, _List_Nil, _List_fromArray( [ $elm$html$Html$text('\n Acey-Ducey is played in the following manner. The Dealer (Computer) deals two cards face up. \n 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.\n If you do not want to bet, bet 0.\n ') ])) ])); var $author$project$Main$view = function (model) { return A2( $elm$html$Html$div, $author$project$Main$centerHeadlineStyle, _List_fromArray( [ $author$project$Main$showHeader, $author$project$Main$showGame(model) ])); }; var $author$project$Main$main = $elm$browser$Browser$element( {aB: $author$project$Main$init, aH: $author$project$Main$subscriptions, aJ: $author$project$Main$update, aK: $author$project$Main$view}); _Platform_export({'Main':{'init':$author$project$Main$main( $elm$json$Json$Decode$succeed(0))(0)}});}(this)); ================================================ FILE: 00_Alternate_Languages/01_Acey_Ducey/elm/docs/index.html ================================================
================================================ FILE: 00_Alternate_Languages/01_Acey_Ducey/elm/elm.json ================================================ { "type": "application", "source-directories": [ "src" ], "elm-version": "0.19.1", "dependencies": { "direct": { "elm/browser": "1.0.2", "elm/core": "1.0.5", "elm/html": "1.0.0", "elm/random": "1.0.0" }, "indirect": { "elm/json": "1.1.3", "elm/time": "1.0.0", "elm/url": "1.0.0", "elm/virtual-dom": "1.0.2" } }, "test-dependencies": { "direct": {}, "indirect": {} } } ================================================ FILE: 00_Alternate_Languages/01_Acey_Ducey/elm/package.json ================================================ { "name": "01_Acey_Ducey", "version": "1.0.0", "main": "index.js", "repository": "https://github.com/auryn31/01_Acey_Ducey.git", "author": "Auryn Engel ", "license": "MIT", "scripts": { "live": "elm-live src/Main.elm --proxy-prefix=/api --proxy-host=http://localhost:8080/api --open --start-page=resources/index.html -- --output=app.js", "build": "elm make src/Main.elm --optimize --output docs/app.js && cp -R resources/ docs/" }, "devDependencies": { "elm-live": "^4.0.2", "uglify-js": "^3.15.3" } } ================================================ FILE: 00_Alternate_Languages/01_Acey_Ducey/elm/resources/index.html ================================================
================================================ FILE: 00_Alternate_Languages/01_Acey_Ducey/elm/src/Main.elm ================================================ module Main exposing (..) import Browser import Html exposing (..) import Html.Attributes exposing (style, type_, value) import Html.Events exposing (onInput) import Random main : Program () Model Msg main = Browser.element { init = init , view = view , update = update , subscriptions = subscriptions } -- Models type alias Model = { money : Int , currentGame : Game , lastGame : Maybe Game , moneyBet : Int , error : Maybe String } type alias Game = { cardA : Maybe Int , cardB : Maybe Int , cardC : Maybe Int } -- Init init : () -> ( Model, Cmd Msg ) init _ = ( { money = 100, currentGame = { cardA = Nothing, cardB = Nothing, cardC = Nothing }, lastGame = Nothing, moneyBet = 0, error = Nothing }, Random.generate NewCard newCard ) -- Messages type Msg = BetMoney Int | UpdateBetValue String | NewCard Int | NewCardC Int | Play | NewGame -- Update update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = case msg of BetMoney bet -> ( { model | moneyBet = bet }, Cmd.none ) UpdateBetValue value -> case String.toInt value of Just newValue -> if newValue > model.money then ( { model | error = Just "You cannot bet more than you have", moneyBet = model.money }, Cmd.none ) else ( { model | moneyBet = newValue, error = Nothing }, Cmd.none ) Nothing -> ( { model | error = Just "Wrong input for bet" }, Cmd.none ) NewCard card -> case model.currentGame.cardA of Nothing -> let currentGame = model.currentGame in if card > 13 then ( model, Random.generate NewCard newCard ) else ( { model | currentGame = { currentGame | cardA = Just card } }, Random.generate NewCard newCard ) Just cardA -> let currentGame = model.currentGame in if card <= cardA then ( { model | currentGame = { currentGame | cardA = Just card } }, Random.generate NewCard newCard ) else ( { model | currentGame = { currentGame | cardB = Just card } }, Cmd.none ) Play -> ( model, Random.generate NewCardC newCard ) NewCardC card -> calculateNewState card model NewGame -> init () calculateNewState : Int -> Model -> ( Model, Cmd Msg ) calculateNewState cardC model = case model.currentGame.cardA of Just cardA -> case model.currentGame.cardB of Just cardB -> let currentGame = model.currentGame in if cardC == cardA || cardC == cardB then ( model, Random.generate NewCardC newCard ) else if cardA < cardC && cardC < cardB then ( { model | money = model.money + model.moneyBet, currentGame = { currentGame | cardA = Nothing, cardB = Nothing }, lastGame = Just { cardA = model.currentGame.cardA, cardB = model.currentGame.cardB, cardC = Just cardC } }, Random.generate NewCard newCard ) else if model.moneyBet > model.money - model.moneyBet then ( { model | money = model.money - model.moneyBet, moneyBet = model.money - model.moneyBet, currentGame = { currentGame | cardA = Nothing, cardB = Nothing }, lastGame = Just { cardA = model.currentGame.cardA, cardB = model.currentGame.cardB, cardC = Just cardC } }, Random.generate NewCard newCard ) else ( { model | money = model.money - model.moneyBet, currentGame = { currentGame | cardA = Nothing, cardB = Nothing }, lastGame = Just { cardA = model.currentGame.cardA, cardB = model.currentGame.cardB, cardC = Just cardC } }, Random.generate NewCard newCard ) Nothing -> ( model, Cmd.none ) Nothing -> ( model, Cmd.none ) subscriptions : Model -> Sub Msg subscriptions _ = Sub.none -- Views view : Model -> Html Msg view model = div centerHeadlineStyle [ showHeader , showGame model ] showHeader : Html msg showHeader = div headerStyle [ h1 [ style "font-size" "4rem" ] [ text "ACEY DUCEY CARD GAME" ] , div [] [ text "Creative Computing Morristown, New Jersey" ] , div [] [ text """ 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, bet 0. """ ] ] showGame : Model -> Html Msg showGame model = if model.money <= 0 then article gameStyle [ p cardContentPStyle [ text "You lose all you money" ] , button [ Html.Events.onClick NewGame, standardFontSize ] [ text "Again" ] ] else article gameStyle [ p cardContentPStyle [ text ("Currently you have " ++ String.fromInt model.money ++ " in your pocket.") ] , p cardContentPStyle [ text ("Card 1: " ++ cardToString model.currentGame.cardA) ] , p cardContentPStyle [ text ("Card 2: " ++ cardToString model.currentGame.cardB) ] , p cardContentPStyle [ text ("Your current bet is " ++ String.fromInt model.moneyBet) ] , input [ type_ "range", Html.Attributes.max (String.fromInt model.money), Html.Attributes.min "0", Html.Attributes.value (String.fromInt model.moneyBet), onInput UpdateBetValue ] [] , button [ Html.Events.onClick Play, standardFontSize ] [ text "Play" ] , showLastGame model.lastGame , showError model.error ] showLastGame : Maybe Game -> Html Msg showLastGame game = case game of Nothing -> div [ standardFontSize ] [ text "This is your first game" ] Just value -> div [] [ showLastWinLose value , p cardContentPStyle [ text ("Card 1: " ++ cardToString value.cardA) ] , p cardContentPStyle [ text ("Card 2: " ++ cardToString value.cardB) ] , p cardContentPStyle [ text ("Drawn Card: " ++ cardToString value.cardC) ] ] showLastWinLose : Game -> Html Msg showLastWinLose game = Maybe.map3 getGameStateMessage game.cardA game.cardB game.cardC |> Maybe.withDefault (text "something is wrong") getGameStateMessage : Int -> Int -> Int -> Html Msg getGameStateMessage cardA cardB cardC = if cardA < cardC && cardB > cardC then div [ standardFontSize ] [ text "You won :)" ] else div [ standardFontSize ] [ text "You loose :(" ] showError : Maybe String -> Html Msg showError value = case value of Just string -> p [ standardFontSize ] [ text string ] Nothing -> div [] [] -- Helper cardToString : Maybe Int -> String cardToString card = case card of Just value -> if value < 11 then String.fromInt value else case value of 11 -> "Jack" 12 -> "Queen" 13 -> "King" 14 -> "Ace" _ -> "impossible value" Nothing -> "-" newCard : Random.Generator Int newCard = Random.int 2 14 -- Styles headerStyle : List (Attribute msg) headerStyle = [ style "font-size" "2rem", style "text-align" "center" ] cardContentPStyle : List (Attribute msg) cardContentPStyle = [ style "font-size" "2rem" ] gameStyle : List (Attribute msg) gameStyle = [ style "width" "100%" , style "max-width" "70rem" ] centerHeadlineStyle : List (Attribute msg) centerHeadlineStyle = [ style "display" "grid" , style "place-items" "center" , style "margin" "2rem" ] standardFontSize : Attribute msg standardFontSize = style "font-size" "2rem" ================================================ FILE: 00_Alternate_Languages/01_Acey_Ducey/go/main.go ================================================ package main import ( "bufio" "fmt" "math/rand" "os" "sort" "strconv" "strings" "time" ) var welcome = ` 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 ` func main() { rand.Seed(time.Now().UnixNano()) scanner := bufio.NewScanner(os.Stdin) fmt.Println(welcome) for { play(100) fmt.Println("TRY AGAIN (YES OR NO)") scanner.Scan() response := scanner.Text() if strings.ToUpper(response) != "YES" { break } } fmt.Println("O.K., HOPE YOU HAD FUN!") } func play(money int) { scanner := bufio.NewScanner(os.Stdin) var bet int for { // Shuffle the cards cards := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14} rand.Shuffle(len(cards), func(i, j int) { cards[i], cards[j] = cards[j], cards[i] }) // Take the first two for the dealer and sort dealerCards := cards[0:2] sort.Ints(dealerCards) fmt.Printf("YOU NOW HAVE %d DOLLARS.\n\n", money) fmt.Printf("HERE ARE YOUR NEXT TWO CARDS:\n%s\n%s", getCardName(dealerCards[0]), getCardName(dealerCards[1])) fmt.Printf("\n\n") //Check if Bet is Valid for { fmt.Println("WHAT IS YOUR BET:") scanner.Scan() b, err := strconv.Atoi(scanner.Text()) if err != nil { fmt.Println("PLEASE ENTER A POSITIVE NUMBER") continue } bet = b if bet == 0 { fmt.Printf("CHICKEN!\n\n") goto there } if (bet > 0) && (bet <= money) { break } } // Draw Players Card fmt.Printf("YOUR CARD: %s\n", getCardName(cards[2])) if (cards[2] > dealerCards[0]) && (cards[2] < dealerCards[1]) { fmt.Println("YOU WIN!!!") money = money + bet } else { fmt.Println("SORRY, YOU LOSE") money = money - bet } fmt.Println() if money <= 0 { fmt.Printf("%s\n", "SORRY, FRIEND, BUT YOU BLEW YOUR WAD.") return } there: } } func getCardName(c int) string { switch c { case 11: return "JACK" case 12: return "QUEEN" case 13: return "KING" case 14: return "ACE" default: return strconv.Itoa(c) } } ================================================ FILE: 00_Alternate_Languages/01_Acey_Ducey/nim/aceyducey.nim ================================================ 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!" ================================================ FILE: 00_Alternate_Languages/01_Acey_Ducey/pascal/.gitattributes ================================================ # Set the default behavior, in case people don't have core.autocrlf set. * text=auto # Explicitly declare text files you want to always be normalized and converted # to native line endings on checkout. *.inc text *.pas text *.pp text *.lpk text *.lpi text *.lps text *.lpr text *.def text *.css text *.html text *.xml text *.sql text # Declare files that will always have CRLF line endings on checkout. *.dpk text eol=crlf *.dproj text eol=crlf # Declare files that will always have LF line endings on checkout. # Denote all files that are truly binary and should not be modified. *.png binary *.jpg binary *.exe binary *.res binary *.ico binary *.dll binary # Keep these files from archive/exports, mainly from production. .gitignore export-ignore .gitattributes export-ignore ================================================ FILE: 00_Alternate_Languages/01_Acey_Ducey/pascal/.gitignore ================================================ # Basic Computer Programs project specific aceyducey aceyducey.exe # Compiled l10n files: .mo should be ignored *.mo # Ghostwriter backups *.backup # nano editor backup files *.swp # Uncomment these types if you want even more clean repository. But be careful. # It can make harm to an existing project source. Read explanations below. # # Resource files are binaries containing manifest, project icon and version info. # They can not be viewed as text or compared by diff-tools. Consider replacing them with .rc files. *.res # Delphi/Lazarus compiler-generated binaries (safe to delete) *.exe *.dll *.bpl *.bpi *.dcp *.so *.apk *.drc *.map *.dres *.rsm *.tds *.dcu *.lib *.[ao] *.or *.ppu *.dbg *.compiled # Delphi autogenerated files (duplicated info) *.cfg *Resource.rc # Delphi local files (user-specific info) *.local *.identcache *.projdata *.tvsconfig *.dsk # Delphi history and backups __history/ *.~* # Lazarus history, backups and session backup/ *.bak *.lps # Castalia statistics file *.stat ================================================ FILE: 00_Alternate_Languages/01_Acey_Ducey/pascal/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Pascal](https://en.wikipedia.org/wiki/Pascal_(programming_language)) by Gustavo Carreno [gcarreno@github](https://github.com/gcarreno) ================================================ FILE: 00_Alternate_Languages/01_Acey_Ducey/pascal/object-pascal/aceyducey.lpi ================================================ <UseAppBundle Value="False"/> <ResourceType Value="res"/> </General> <BuildModes Count="1"> <Item1 Name="Default" Default="True"/> </BuildModes> <PublishOptions> <Version Value="2"/> <UseFileFilters Value="True"/> </PublishOptions> <RunParams> <FormatVersion Value="2"/> <Modes Count="0"/> </RunParams> <Units Count="3"> <Unit0> <Filename Value="aceyducey.pas"/> <IsPartOfProject Value="True"/> </Unit0> <Unit1> <Filename Value="game.pas"/> <IsPartOfProject Value="True"/> <UnitName Value="Game"/> </Unit1> <Unit2> <Filename Value="deck.pas"/> <IsPartOfProject Value="True"/> <UnitName Value="Deck"/> </Unit2> </Units> </ProjectOptions> <CompilerOptions> <Version Value="11"/> <Target> <Filename Value="aceyducey"/> </Target> <SearchPaths> <IncludeFiles Value="$(ProjOutDir)"/> <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/> </SearchPaths> </CompilerOptions> <Debugging> <Exceptions Count="3"> <Item1> <Name Value="EAbort"/> </Item1> <Item2> <Name Value="ECodetoolError"/> </Item2> <Item3> <Name Value="EFOpenError"/> </Item3> </Exceptions> </Debugging> </CONFIG> ================================================ FILE: 00_Alternate_Languages/01_Acey_Ducey/pascal/object-pascal/aceyducey.pas ================================================ program aceyducey; {$IFDEF FPC} {$mode objfpc}{$H+} {$ENDIF} uses Game , Deck ; var Acey_Ducey: TGame; begin Acey_Ducey:= TGame.Create; Acey_Ducey.Run; end. ================================================ FILE: 00_Alternate_Languages/01_Acey_Ducey/pascal/object-pascal/deck.pas ================================================ unit Deck; {$IFDEF FPC} {$mode objfpc}{$H+} {$ENDIF} interface uses Classes , SysUtils ; type { TDeck } TDeck = class private FDealerLow: Integer; FDealerHigh: Integer; FPlayer: Integer; procedure PrintCard(const ACard: Integer); protected public property DealerLow: Integer read FDealerLow; property DealerHigh: Integer read FDealerHigh; property Player: Integer read FPlayer; procedure DrawCards; procedure ShowDealerCards; procedure ShowPlayerCard; function PlayerWins: Boolean; published end; implementation { TDeck } procedure TDeck.PrintCard(const ACard: Integer); begin if ACard < 11 then begin Write(ACard); end; if ACard = 11 then begin Write('JACK'); end; if ACard = 12 then begin Write('QUEEN'); end; if ACard = 13 then begin Write('KING'); end; if ACard = 14 then begin Write('ACE'); end; end; procedure TDeck.DrawCards; var tmp: Integer; begin repeat FDealerLow:= Random(14) + 2; until (FDealerLow >= 2) and (FDealerLow <= 14); repeat FDealerHigh:= Random(14) + 2; until (FDealerHigh >= 2) and (FDealerHigh <= 14) and (FDealerLow <> FDealerHigh); if FDealerLow > FDealerHigh then begin tmp:= FDealerHigh; FDealerHigh:= FDealerLow; FDealerLow:= tmp; end; repeat FPlayer:= Random(14) + 2; until (FPlayer >= 2) and (FPlayer <= 14); end; procedure TDeck.ShowDealerCards; begin Write('HERE ARE YOUR NEXT TWO CARDS: '); PrintCard(FDealerLow); Write(' '); PrintCard(FDealerHigh); WriteLN; WriteLN; end; procedure TDeck.ShowPlayerCard; begin PrintCard(FPlayer); WriteLN; WriteLN; end; function TDeck.PlayerWins: Boolean; begin Result:= (FPlayer > FDealerLow) and (FPlayer < FDealerHigh); end; end. ================================================ FILE: 00_Alternate_Languages/01_Acey_Ducey/pascal/object-pascal/game.pas ================================================ unit Game; {$IFDEF FPC} {$mode objfpc}{$H+} {$ENDIF} interface uses Classes , SysUtils , Crt , Deck ; type { TGame } TGame = class private FStash: Integer; FBet: Integer; FDeck: TDeck; procedure PrintGreeting; procedure PrintBalance; function GetBet: Integer; function TryAgain: Boolean; protected public constructor Create; destructor Destroy; override; procedure Run; published end; implementation { TGame } procedure TGame.PrintGreeting; begin WriteLN(' ':26, 'ACEY DUCEY CARD GAME'); WriteLN(' ':15, 'CREATIVE COMPUTING MORRISTOWN, NEW JERSEY'); WriteLN; WriteLN; WriteLN('ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER '); WriteLN('THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP'); WriteLN('YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING'); WriteLN('ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE'); WriteLN('A VALUE BETWEEN THE FIRST TWO.'); WriteLN('IF YOU DO NOT WANT TO BET, INPUT A 0'); WriteLN; end; procedure TGame.PrintBalance; begin WriteLN('YOU NOW HAVE ', FStash,' DOLLARS.'); WriteLN; end; function TGame.GetBet: Integer; begin Result:= 0; repeat Write('WHAT IS YOUR BET: '); ReadLN(Result); if Result > FStash then begin WriteLn('SORRY, MY FRIEND, BUT YOU BET TOO MUCH.'); WriteLn('YOU HAVE ONLY ', FStash,' DOLLARS TO BET.'); end; until (Result >=0) and (Result <= FStash); end; function TGame.TryAgain: Boolean; var answer: String; begin Result:= False; Write('TRY AGAIN (YES OR NO)'); ReadLn(answer); Result:= (LowerCase(answer)='yes') or (LowerCase(answer)='y'); end; constructor TGame.Create; begin Randomize; FDeck:= TDeck.Create; end; destructor TGame.Destroy; begin FDeck.Free; inherited Destroy; end; procedure TGame.Run; begin ClrScr; PrintGreeting; repeat FStash:= 100; repeat PrintBalance; FDeck.DrawCards; //DrawDealerCards; FDeck.ShowDealerCards; FBet:= GetBet; if FBet = 0 then begin WriteLN('CHICKEN!!'); continue; end; //DrawPlayerCard; FDeck.ShowPlayerCard; //if (FCardC > FCardA) and (FCardC < FCardB) then if FDeck.PlayerWins then begin WriteLN('YOU WIN!!!'); Inc(FStash, FBet) end else begin WriteLN('SORRY, YOU LOSE'); Dec(FStash, FBet) end; until FStash = 0; WriteLN('SORRY, FRIEND, BUT YOU BLEW YOUR WAD.'); WriteLN; until not TryAgain; WriteLN('O.K., HOPE YOU HAD FUN!'); end; end. ================================================ FILE: 00_Alternate_Languages/01_Acey_Ducey/pascal/simple/aceyducey.lpi ================================================ <?xml version="1.0" encoding="UTF-8"?> <CONFIG> <ProjectOptions> <Version Value="11"/> <General> <Flags> <MainUnitHasCreateFormStatements Value="False"/> <MainUnitHasTitleStatement Value="False"/> <MainUnitHasScaledStatement Value="False"/> </Flags> <SessionStorage Value="InProjectDir"/> <MainUnit Value="0"/> <Title Value="aceyducey"/> <UseAppBundle Value="False"/> <ResourceType Value="res"/> </General> <BuildModes Count="1"> <Item1 Name="Default" Default="True"/> </BuildModes> <PublishOptions> <Version Value="2"/> <UseFileFilters Value="True"/> </PublishOptions> <RunParams> <FormatVersion Value="2"/> <Modes Count="0"/> </RunParams> <Units Count="1"> <Unit0> <Filename Value="aceyducey.pas"/> <IsPartOfProject Value="True"/> </Unit0> </Units> </ProjectOptions> <CompilerOptions> <Version Value="11"/> <Target> <Filename Value="aceyducey"/> </Target> <SearchPaths> <IncludeFiles Value="$(ProjOutDir)"/> <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/> </SearchPaths> </CompilerOptions> <Debugging> <Exceptions Count="3"> <Item1> <Name Value="EAbort"/> </Item1> <Item2> <Name Value="ECodetoolError"/> </Item2> <Item3> <Name Value="EFOpenError"/> </Item3> </Exceptions> </Debugging> </CONFIG> ================================================ FILE: 00_Alternate_Languages/01_Acey_Ducey/pascal/simple/aceyducey.pas ================================================ program aceyducey; {$IFDEF FPC} {$mode objfpc}{$H+} {$ENDIF} uses Crt; var Stash: Integer; CardA: Integer; CardB: Integer; CardC: Integer; Bet: Integer; procedure PrintGreeting; begin WriteLN(' ':26, 'ACEY DUCEY CARD GAME'); WriteLN(' ':15, 'CREATIVE COMPUTING MORRISTOWN, NEW JERSEY'); WriteLN; WriteLN; WriteLN('ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER '); WriteLN('THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP'); WriteLN('YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING'); WriteLN('ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE'); WriteLN('A VALUE BETWEEN THE FIRST TWO.'); WriteLN('IF YOU DO NOT WANT TO BET, INPUT A 0'); WriteLN; end; procedure PrintBalance; begin WriteLN('YOU NOW HAVE ', Stash,' DOLLARS.'); WriteLN; end; procedure PrintCard(const ACard: Integer); begin if ACard < 11 then begin Write(ACard); end; if ACard = 11 then begin Write('JACK'); end; if ACard = 12 then begin Write('QUEEN'); end; if ACard = 13 then begin Write('KING'); end; if ACard = 14 then begin Write('ACE'); end; end; procedure DrawDealerCards; var tmp: Integer; begin Write('HERE ARE YOUR NEXT TWO CARDS: '); repeat CardA:= Random(14) + 2; until (CardA >= 2) and (CardA <= 14); repeat CardB:= Random(14) + 2; until (CardB >= 2) and (CardB <= 14) and (CardA <> CardB); if CardA > CardB then begin tmp:= CardB; CardB:= CardA; CardA:= tmp; end; PrintCard(CardA); Write(' '); PrintCard(CardB); WriteLN; WriteLN; end; procedure DrawPlayerCard; begin repeat CardC:= Random(14) + 2; until (CardC >= 2) and (CardC <= 14); PrintCard(CardC); WriteLN; WriteLN; end; function GetBet: Integer; begin Result:= 0; repeat Write('WHAT IS YOUR BET: '); ReadLN(Result); if Result > Stash then begin WriteLn('SORRY, MY FRIEND, BUT YOU BET TOO MUCH.'); WriteLn('YOU HAVE ONLY ', Stash,' DOLLARS TO BET.'); end; until (Result >=0) and (Result <= Stash); end; function TryAgain: Boolean; var answer: String; begin Result:= False; Write('TRY AGAIN (YES OR NO)'); ReadLn(answer); Result:= (LowerCase(answer)='yes') or (LowerCase(answer)='y'); end; begin Randomize; ClrScr; PrintGreeting; repeat Stash:= 100; repeat PrintBalance; DrawDealerCards; Bet:= GetBet; if Bet = 0 then begin WriteLN('CHICKEN!!'); continue; end; DrawPlayerCard; if (CardC > CardA) and (CardC < CardB) then begin WriteLN('YOU WIN!!!'); Inc(Stash, Bet) end else begin WriteLN('SORRY, YOU LOSE'); Dec(Stash, Bet) end; until Stash = 0; WriteLN('SORRY, FRIEND, BUT YOU BLEW YOUR WAD.'); WriteLN; until not TryAgain; WriteLN('O.K., HOPE YOU HAD FUN!'); end. ================================================ FILE: 00_Alternate_Languages/02_Amazing/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/02_Amazing/MiniScript/amazing.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/02_Amazing/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/02_Amazing/amazing.bas ================================================ 10 PRINT TAB(28);"AMAZING PROGRAM" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 30 PRINT:PRINT:PRINT:PRINT 100 INPUT "WHAT ARE YOUR WIDTH AND LENGTH";H,V 102 IF H<>1 AND V<>1 THEN 110 104 PRINT "MEANINGLESS DIMENSIONS. TRY AGAIN.":GOTO 100 110 DIM W(H,V),V(H,V) 120 PRINT 130 PRINT 140 PRINT 150 PRINT 160 Q=0:Z=0:X=INT(RND(1)*H+1) 165 FOR I=1 TO H 170 IF I=X THEN 173 171 PRINT ".--";:GOTO 180 173 PRINT ". "; 180 NEXT I 190 PRINT "." 195 C=1:W(X,1)=C:C=C+1 200 R=X:S=1:GOTO 260 210 IF R<>H THEN 240 215 IF S<>V THEN 230 220 R=1:S=1:GOTO 250 230 R=1:S=S+1:GOTO 250 240 R=R+1 250 IF W(R,S)=0 THEN 210 260 IF R-1=0 THEN 530 265 IF W(R-1,S)<>0 THEN 530 270 IF S-1=0 THEN 390 280 IF W(R,S-1)<>0 THEN 390 290 IF R=H THEN 330 300 IF W(R+1,S)<>0 THEN 330 310 X=INT(RND(1)*3+1) 320 ON X GOTO 790,820,860 330 IF S<>V THEN 340 334 IF Z=1 THEN 370 338 Q=1:GOTO 350 340 IF W(R,S+1)<>0 THEN 370 350 X=INT(RND(1)*3+1) 360 ON X GOTO 790,820,910 370 X=INT(RND(1)*2+1) 380 ON X GOTO 790,820 390 IF R=H THEN 470 400 IF W(R+1,S)<>0 THEN 470 405 IF S<>V THEN 420 410 IF Z=1 THEN 450 415 Q=1:GOTO 430 420 IF W(R,S+1)<>0 THEN 450 430 X=INT(RND(1)*3+1) 440 ON X GOTO 790,860,910 450 X=INT(RND(1)*2+1) 460 ON X GOTO 790,860 470 IF S<>V THEN 490 480 IF Z=1 THEN 520 485 Q=1:GOTO 500 490 IF W(R,S+1)<>0 THEN 520 500 X=INT(RND(1)*2+1) 510 ON X GOTO 790,910 520 GOTO 790 530 IF S-1=0 THEN 670 540 IF W(R,S-1)<>0 THEN 670 545 IF R=H THEN 610 547 IF W(R+1,S)<>0 THEN 610 550 IF S<>V THEN 560 552 IF Z=1 THEN 590 554 Q=1:GOTO 570 560 IF W(R,S+1)<>0 THEN 590 570 X=INT(RND(1)*3+1) 580 ON X GOTO 820,860,910 590 X=INT(RND(1)*2+1) 600 ON X GOTO 820,860 610 IF S<>V THEN 630 620 IF Z=1 THEN 660 625 Q=1:GOTO 640 630 IF W(R,S+1)<>0 THEN 660 640 X=INT(RND(1)*2+1) 650 ON X GOTO 820,910 660 GOTO 820 670 IF R=H THEN 740 680 IF W(R+1,S)<>0 THEN 740 685 IF S<>V THEN 700 690 IF Z=1 THEN 730 695 Q=1:GOTO 710 700 IF W(R,S+1)<>0 THEN 730 710 X=INT(RND(1)*2+1) 720 ON X GOTO 860,910 730 GOTO 860 740 IF S<>V THEN 760 750 IF Z=1 THEN 780 755 Q=1:GOTO 770 760 IF W(R,S+1)<>0 THEN 780 770 GOTO 910 780 GOTO 1000 790 W(R-1,S)=C 800 C=C+1:V(R-1,S)=2:R=R-1 810 IF C=H*V+1 THEN 1010 815 Q=0:GOTO 260 820 W(R,S-1)=C 830 C=C+1 840 V(R,S-1)=1:S=S-1:IF C=H*V+1 THEN 1010 850 Q=0:GOTO 260 860 W(R+1,S)=C 870 C=C+1:IF V(R,S)=0 THEN 880 875 V(R,S)=3:GOTO 890 880 V(R,S)=2 890 R=R+1 900 IF C=H*V+1 THEN 1010 905 GOTO 530 910 IF Q=1 THEN 960 920 W(R,S+1)=C:C=C+1:IF V(R,S)=0 THEN 940 930 V(R,S)=3:GOTO 950 940 V(R,S)=1 950 S=S+1:IF C=H*V+1 THEN 1010 955 GOTO 260 960 Z=1 970 IF V(R,S)=0 THEN 980 975 V(R,S)=3:Q=0:GOTO 1000 980 V(R,S)=1:Q=0:R=1:S=1:GOTO 250 1000 GOTO 210 1010 IF Z=1 THEN 1015 1011 X=INT(RND(1)*H+1) 1012 IF V(X,V)=0 THEN 1014 1013 V(X,V)=3: GOTO 1015 1014 V(X,V)=1 1015 FOR J=1 TO V 1016 PRINT "I"; 1017 FOR I=1 TO H 1018 IF V(I,J)<2 THEN 1030 1020 PRINT " "; 1021 GOTO 1040 1030 PRINT " I"; 1040 NEXT I 1041 PRINT 1043 FOR I=1 TO H 1045 IF V(I,J)=0 THEN 1060 1050 IF V(I,J)=2 THEN 1060 1051 PRINT ": "; 1052 GOTO 1070 1060 PRINT ":--"; 1070 NEXT I 1071 PRINT "." 1072 NEXT J 1073 END ================================================ FILE: 00_Alternate_Languages/02_Amazing/go/main.go ================================================ package main import ( "bufio" "fmt" "log" "math/rand" "os" "strconv" "time" ) func main() { rand.Seed(time.Now().UnixNano()) printWelcome() h, w := getDimensions() m := NewMaze(h, w) m.draw() } type direction int64 const ( LEFT direction = iota UP RIGHT DOWN ) const ( EXIT_DOWN = 1 EXIT_RIGHT = 2 ) type maze struct { width int length int used [][]int walls [][]int enterCol int } func NewMaze(w, l int) maze { if (w < 2) || (l < 2) { log.Fatal("invalid dimensions supplied") } m := maze{width: w, length: l} m.used = make([][]int, l) for i := range m.used { m.used[i] = make([]int, w) } m.walls = make([][]int, l) for i := range m.walls { m.walls[i] = make([]int, w) } // randomly determine the entry column m.enterCol = rand.Intn(w) // determine layout of walls m.build() // add an exit col := rand.Intn(m.width - 1) row := m.length - 1 m.walls[row][col] = m.walls[row][col] + 1 return m } func (m *maze) build() { row := 0 col := 0 count := 2 for { possibleDirs := m.getPossibleDirections(row, col) if len(possibleDirs) != 0 { row, col, count = m.makeOpening(possibleDirs, row, col, count) } else { for { if col != m.width-1 { col = col + 1 } else if row != m.length-1 { row = row + 1 col = 0 } else { row = 0 col = 0 } if m.used[row][col] != 0 { break } } } if count == (m.width*m.length)+1 { break } } } func (m *maze) getPossibleDirections(row, col int) []direction { possible_dirs := make(map[direction]bool, 4) possible_dirs[LEFT] = true possible_dirs[UP] = true possible_dirs[RIGHT] = true possible_dirs[DOWN] = true if (col == 0) || (m.used[row][col-1] != 0) { possible_dirs[LEFT] = false } if (row == 0) || (m.used[row-1][col] != 0) { possible_dirs[UP] = false } if (col == m.width-1) || (m.used[row][col+1] != 0) { possible_dirs[RIGHT] = false } if (row == m.length-1) || (m.used[row+1][col] != 0) { possible_dirs[DOWN] = false } ret := make([]direction, 0) for d, v := range possible_dirs { if v { ret = append(ret, d) } } return ret } func (m *maze) makeOpening(dirs []direction, row, col, count int) (int, int, int) { dir := rand.Intn(len(dirs)) if dirs[dir] == LEFT { col = col - 1 m.walls[row][col] = int(EXIT_RIGHT) } else if dirs[dir] == UP { row = row - 1 m.walls[row][col] = int(EXIT_DOWN) } else if dirs[dir] == RIGHT { m.walls[row][col] = m.walls[row][col] + EXIT_RIGHT col = col + 1 } else if dirs[dir] == DOWN { m.walls[row][col] = m.walls[row][col] + EXIT_DOWN row = row + 1 } m.used[row][col] = count count = count + 1 return row, col, count } // draw the maze func (m *maze) draw() { for col := 0; col < m.width; col++ { if col == m.enterCol { fmt.Print(". ") } else { fmt.Print(".--") } } fmt.Println(".") for row := 0; row < m.length; row++ { fmt.Print("|") for col := 0; col < m.width; col++ { if m.walls[row][col] < 2 { fmt.Print(" |") } else { fmt.Print(" ") } } fmt.Println() for col := 0; col < m.width; col++ { if (m.walls[row][col] == 0) || (m.walls[row][col] == 2) { fmt.Print(":--") } else { fmt.Print(": ") } } fmt.Println(".") } } func printWelcome() { fmt.Println(" AMAZING PROGRAM") fmt.Print(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n") } func getDimensions() (int, int) { scanner := bufio.NewScanner(os.Stdin) fmt.Println("Enter a width ( > 1 ):") scanner.Scan() w, err := strconv.Atoi(scanner.Text()) if err != nil { log.Fatal("invalid dimension") } fmt.Println("Enter a height ( > 1 ):") scanner.Scan() h, err := strconv.Atoi(scanner.Text()) if err != nil { log.Fatal("invalid dimension") } return w, h } ================================================ FILE: 00_Alternate_Languages/02_Amazing/pascal/.gitattributes ================================================ # Set the default behavior, in case people don't have core.autocrlf set. * text=auto # Explicitly declare text files you want to always be normalized and converted # to native line endings on checkout. *.inc text *.pas text *.pp text *.lpk text *.lpi text *.lps text *.lpr text *.def text *.css text *.html text *.xml text *.sql text # Declare files that will always have CRLF line endings on checkout. *.dpk text eol=crlf *.dproj text eol=crlf # Declare files that will always have LF line endings on checkout. # Denote all files that are truly binary and should not be modified. *.png binary *.jpg binary *.exe binary *.res binary *.ico binary *.dll binary # Keep these files from archive/exports, mainly from production. .gitignore export-ignore .gitattributes export-ignore ================================================ FILE: 00_Alternate_Languages/02_Amazing/pascal/.gitignore ================================================ # Basic Computer Programs project specific amazing amazing.exe # Compiled l10n files: .mo should be ignored *.mo # Ghostwriter backups *.backup # nano editor backup files *.swp # Uncomment these types if you want even more clean repository. But be careful. # It can make harm to an existing project source. Read explanations below. # # Resource files are binaries containing manifest, project icon and version info. # They can not be viewed as text or compared by diff-tools. Consider replacing them with .rc files. *.res # Delphi/Lazarus compiler-generated binaries (safe to delete) *.exe *.dll *.bpl *.bpi *.dcp *.so *.apk *.drc *.map *.dres *.rsm *.tds *.dcu *.lib *.[ao] *.or *.ppu *.dbg *.compiled # Delphi autogenerated files (duplicated info) *.cfg *Resource.rc # Delphi local files (user-specific info) *.local *.identcache *.projdata *.tvsconfig *.dsk # Delphi history and backups __history/ *.~* # Lazarus history, backups and session backup/ *.bak *.lps # Castalia statistics file *.stat ================================================ FILE: 00_Alternate_Languages/02_Amazing/pascal/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Pascal](https://en.wikipedia.org/wiki/Pascal_(programming_language)) by Gustavo Carreno [gcarreno@github](https://github.com/gcarreno) ================================================ FILE: 00_Alternate_Languages/02_Amazing/pascal/object-pascal/amazing.lpi ================================================ <?xml version="1.0" encoding="UTF-8"?> <CONFIG> <ProjectOptions> <Version Value="12"/> <General> <Flags> <MainUnitHasCreateFormStatements Value="False"/> <MainUnitHasTitleStatement Value="False"/> <MainUnitHasScaledStatement Value="False"/> </Flags> <SessionStorage Value="InProjectDir"/> <Title Value="amazing"/> <UseAppBundle Value="False"/> <ResourceType Value="res"/> </General> <BuildModes Count="1"> <Item1 Name="Default" Default="True"/> </BuildModes> <PublishOptions> <Version Value="2"/> <UseFileFilters Value="True"/> </PublishOptions> <RunParams> <FormatVersion Value="2"/> </RunParams> <Units Count="4"> <Unit0> <Filename Value="amazing.pas"/> <IsPartOfProject Value="True"/> </Unit0> <Unit1> <Filename Value="amazingapplication.pas"/> <IsPartOfProject Value="True"/> <UnitName Value="AmazingApplication"/> </Unit1> <Unit2> <Filename Value="maze.pas"/> <IsPartOfProject Value="True"/> <UnitName Value="Maze"/> </Unit2> <Unit3> <Filename Value="room.pas"/> <IsPartOfProject Value="True"/> <UnitName Value="Room"/> </Unit3> </Units> </ProjectOptions> <CompilerOptions> <Version Value="11"/> <Target> <Filename Value="amazing"/> </Target> <SearchPaths> <IncludeFiles Value="$(ProjOutDir)"/> <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/> </SearchPaths> </CompilerOptions> <Debugging> <Exceptions Count="3"> <Item1> <Name Value="EAbort"/> </Item1> <Item2> <Name Value="ECodetoolError"/> </Item2> <Item3> <Name Value="EFOpenError"/> </Item3> </Exceptions> </Debugging> </CONFIG> ================================================ FILE: 00_Alternate_Languages/02_Amazing/pascal/object-pascal/amazing.pas ================================================ program amazing; {$IFDEF FPC} {$mode ObjFPC}{$H+} {$ENDIF} uses AmazingApplication, maze, Room; var AmazingApp: TAmazingApplication; begin AmazingApp:= TAmazingApplication.Create; AmazingApp.Run; end. ================================================ FILE: 00_Alternate_Languages/02_Amazing/pascal/object-pascal/amazingapplication.pas ================================================ unit AmazingApplication; {$IFDEF FPC} {$mode ObjFPC}{$H+} {$ENDIF} interface uses Classes , SysUtils , Crt , Maze ; type { TAmazingApplication } TAmazingApplication = class(TObject) private FMaze: TMaze; procedure PrintGreeting; procedure GetDimensions; procedure BuildMaze; procedure PrintMaze; protected public constructor Create; destructor Destroy; override; procedure Run; published end; implementation { TAmazingApplication } procedure TAmazingApplication.PrintGreeting; begin WriteLN(' ':28, 'AMAZING PROGRAM'); WriteLN(' ':15, 'CREATIVE COMPUTING MORRISTOWN, NEW JERSEY'); WriteLN; WriteLN; WriteLN; WriteLN; end; procedure TAmazingApplication.GetDimensions; var width: Integer; length: Integer; begin repeat Write('WHAT ARE YOUR WIDTH AND LENGTH (SPACE IN BETWEEN): '); ReadLN(width, length); if (width = 1) or (length = 1) then begin WriteLN('MEANINGLESS DIMENSIONS. TRY AGAIN.'); end; until (width > 1) and (length > 1); FMaze:= TMaze.Create(width, length); WriteLN; WriteLN; WriteLN; WriteLN; end; procedure TAmazingApplication.BuildMaze; begin FMaze.Build; end; procedure TAmazingApplication.PrintMaze; begin FMaze.Print; WriteLN; end; constructor TAmazingApplication.Create; begin // end; destructor TAmazingApplication.Destroy; begin if Assigned(FMaze) then begin FMaze.Free; end; inherited Destroy; end; procedure TAmazingApplication.Run; begin //ClrScr; PrintGreeting; GetDimensions; BuildMaze; PrintMaze; end; end. ================================================ FILE: 00_Alternate_Languages/02_Amazing/pascal/object-pascal/maze.pas ================================================ unit Maze; {$IFDEF FPC} {$mode ObjFPC}{$H+} {$ENDIF} interface uses Classes , SysUtils , Room ; type TDirection = (dUp, dRight, dDown, dLeft); TDirections = set of TDirection; { TMaze } TMaze = class(TObject) private FWidth: Integer; FLength: Integer; FEntry: Integer; FLabyrinth: Array of Array of TRoom; function GetRandomDirection(const ADirections: TDirections): TDirection; procedure DebugVisited; procedure DebugWalls; protected public constructor Create(const AWidth, ALength: Integer); destructor Destroy; override; procedure Build; procedure Print; published end; implementation const EXIT_DOWN = 1; EXIT_RIGHT = 2; { TMaze } function TMaze.GetRandomDirection(const ADirections: TDirections): TDirection; var count: Integer; position: Integer; directions: array [0..3] of TDirection; begin count:= 0; position:= 0; if dUp in ADirections then begin Inc(count); directions[position]:= dUp; Inc(position); end; if dRight in ADirections then begin Inc(count); directions[position]:= dRight; Inc(position); end; if dDown in ADirections then begin Inc(count); directions[position]:= dDown; Inc(position); end; if dLeft in ADirections then begin Inc(count); directions[position]:= dLeft; Inc(position); end; Result:= directions[Random(count)]; end; procedure TMaze.DebugVisited; var indexW: Integer; indexL: Integer; begin WriteLN('Visited'); for indexL:= 0 to Pred(FLength) do begin for indexW:= 0 to Pred(FWidth) do begin Write(FLabyrinth[indexW][indexL].Visited:3,' '); end; WriteLN; end; WriteLN; end; procedure TMaze.DebugWalls; var indexW: Integer; indexL: Integer; begin WriteLN('Walls'); for indexL:= 0 to Pred(FLength) do begin for indexW:= 0 to Pred(FWidth) do begin Write(FLabyrinth[indexW][indexL].Walls:3,' '); end; WriteLN; end; WriteLN; end; constructor TMaze.Create(const AWidth, ALength: Integer); var indexW: Integer; indexL: Integer; begin Randomize; FWidth:= AWidth; FLength:= ALength; FEntry:= Random(FWidth); SetLength(FLabyrinth, FWidth, FLength); for indexW:= 0 to Pred(FWidth) do begin for indexL:= 0 to Pred(FLength) do begin FLabyrinth[indexW][indexL]:= TRoom.Create; end; end; end; destructor TMaze.Destroy; var indexW: Integer; indexL: Integer; begin for indexW:= 0 to Pred(FWidth) do begin for indexL:= 0 to Pred(FLength) do begin if Assigned(FLabyrinth[indexW][indexL]) then begin FLabyrinth[indexW][indexL].Free; end; end; end; inherited Destroy; end; procedure TMaze.Build; var indexW: Integer; indexL: Integer; direction: TDirection; directions: TDirections; count: Integer; begin FEntry:= Random(FWidth); indexW:= FEntry; indexL:= 0; count:= 1; FLabyrinth[indexW][indexL].Visited:= count; Inc(count); repeat directions:= [dUp, dRight, dDown, dLeft]; if (indexW = 0) or (FLabyrinth[Pred(indexW)][indexL].Visited <> 0) then begin Exclude(directions, dLeft); end; if (indexL = 0) or (FLabyrinth[indexW][Pred(indexL)].Visited <> 0) then begin Exclude(directions, dUp); end; if (indexW = Pred(FWidth)) or (FLabyrinth[Succ(indexW)][indexL].Visited <> 0) then begin Exclude(directions, dRight); end; if (indexL = Pred(FLength)) or (FLabyrinth[indexW][Succ(indexL)].Visited <> 0) then begin Exclude(directions, dDown); end; if directions <> [] then begin direction:= GetRandomDirection(directions); case direction of dLeft:begin Dec(indexW); FLabyrinth[indexW][indexL].Walls:= EXIT_RIGHT; end; dUp:begin Dec(indexL); FLabyrinth[indexW][indexL].Walls:= EXIT_DOWN; end; dRight:begin FLabyrinth[indexW][indexL].Walls:= FLabyrinth[indexW][indexL].Walls + EXIT_RIGHT; Inc(indexW); end; dDown:begin FLabyrinth[indexW][indexL].Walls:= FLabyrinth[indexW][indexL].Walls + EXIT_DOWN; Inc(indexL); end; end; FLabyrinth[indexW][indexL].Visited:= count; Inc(count); end else begin while True do begin if indexW <> Pred(FWidth) then begin Inc(indexW); end else if indexL <> Pred(FLength) then begin Inc(indexL); indexW:= 0; end else begin indexW:= 0; indexL:= 0; end; if FLabyrinth[indexW][indexL].Visited <> 0 then begin break; end; end; end; until count = (FWidth * FLength) + 1; indexW:= Random(FWidth); indexL:= Pred(FLength); FLabyrinth[indexW][indexL].Walls:= FLabyrinth[indexW][indexL].Walls + 1; end; procedure TMaze.Print; var indexW:Integer; indexL: Integer; begin //DebugVisited; //DebugWalls; for indexW:= 0 to Pred(FWidth) do begin if indexW = FEntry then begin Write('. '); end else begin Write('.--'); end; end; WriteLN('.'); for indexL:= 0 to Pred(FLength) do begin Write('I'); for indexW:= 0 to Pred(FWidth) do begin FLabyrinth[indexW][indexL].PrintRoom; end; WriteLN; for indexW:= 0 to Pred(FWidth) do begin FLabyrinth[indexW][indexL].PrintWall; end; WriteLN('.'); end; end; end. ================================================ FILE: 00_Alternate_Languages/02_Amazing/pascal/object-pascal/room.pas ================================================ unit Room; {$IFDEF FPC} {$mode ObjFPC}{$H+} {$ENDIF} interface uses Classes , SysUtils ; type { TRoom } TRoom = class(TObject) private FVisited: Integer; FWalls: Integer; protected public constructor Create; procedure PrintRoom; procedure PrintWall; property Visited: Integer read FVisited write FVisited; property Walls: Integer read FWalls write FWalls; published end; implementation { TRoom } constructor TRoom.Create; begin FVisited:= 0; FWalls:= 0; end; procedure TRoom.PrintRoom; begin if FWalls < 2 then begin Write(' I'); end else begin Write(' '); end; end; procedure TRoom.PrintWall; begin if (FWalls = 0) or (FWalls = 2) then begin Write(':--'); end else begin Write(': '); end; end; end. ================================================ FILE: 00_Alternate_Languages/02_Amazing/pascal/simple/amazing.lpi ================================================ <?xml version="1.0" encoding="UTF-8"?> <CONFIG> <ProjectOptions> <Version Value="12"/> <General> <Flags> <MainUnitHasCreateFormStatements Value="False"/> <MainUnitHasTitleStatement Value="False"/> <MainUnitHasScaledStatement Value="False"/> <CompatibilityMode Value="True"/> </Flags> <SessionStorage Value="InProjectDir"/> <Title Value="amazing"/> <UseAppBundle Value="False"/> <ResourceType Value="res"/> </General> <BuildModes Count="1"> <Item1 Name="Default" Default="True"/> </BuildModes> <PublishOptions> <Version Value="2"/> <UseFileFilters Value="True"/> </PublishOptions> <RunParams> <FormatVersion Value="2"/> </RunParams> <Units Count="1"> <Unit0> <Filename Value="amazing.pas"/> <IsPartOfProject Value="True"/> <UnitName Value="Amazing"/> </Unit0> </Units> </ProjectOptions> <CompilerOptions> <Version Value="11"/> <Target> <Filename Value="amazing"/> </Target> <SearchPaths> <IncludeFiles Value="$(ProjOutDir)"/> <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/> </SearchPaths> </CompilerOptions> <Debugging> <Exceptions Count="3"> <Item1> <Name Value="EAbort"/> </Item1> <Item2> <Name Value="ECodetoolError"/> </Item2> <Item3> <Name Value="EFOpenError"/> </Item3> </Exceptions> </Debugging> </CONFIG> ================================================ FILE: 00_Alternate_Languages/02_Amazing/pascal/simple/amazing.pas ================================================ program Amazing; {$IFDEF FPC} {$mode objfpc}{$H+} {$ENDIF} uses Crt; type TDirection = (dUp, dRight, dDown, dLeft); TDirections = set of TDirection; var Width: Integer; // H Length: Integer; // V Entry: Integer; MatrixWalls: Array of Array of Integer; MatrixVisited: Array of Array of Integer; const EXIT_DOWN = 1; EXIT_RIGHT = 2; procedure PrintGreeting; begin WriteLN(' ':28, 'AMAZING PROGRAM'); WriteLN(' ':15, 'CREATIVE COMPUTING MORRISTOWN, NEW JERSEY'); WriteLN; WriteLN; WriteLN; WriteLN; end; procedure GetDimensions; begin repeat Write('WHAT ARE YOUR WIDTH AND LENGTH (SPACE IN BETWEEN): '); ReadLN(Width, Length); if (Width = 1) or (Length = 1) then begin WriteLN('MEANINGLESS DIMENSIONS. TRY AGAIN.'); end; until (Width > 1) and (Length > 1); WriteLN; WriteLN; WriteLN; WriteLN; end; procedure ClearMatrices; var indexW: Integer; indexL: Integer; begin SetLength(MatrixWalls, Width, Length); SetLength(MatrixVisited, Width, Length); for indexW:= 0 to Pred(Width) do begin for indexL:= 0 to Pred(Length) do begin MatrixWalls[indexW][indexL]:= 0; MatrixVisited[indexW][indexL]:= 0; end; end; end; function GetRandomDirection(const ADirections: TDirections): TDirection; var count: Integer; position: Integer; directions: array [0..3] of TDirection; begin count:= 0; position:= 0; if dUp in ADirections then begin Inc(count); directions[position]:= dUp; Inc(position); end; if dRight in ADirections then begin Inc(count); directions[position]:= dRight; Inc(position); end; if dDown in ADirections then begin Inc(count); directions[position]:= dDown; Inc(position); end; if dLeft in ADirections then begin Inc(count); directions[position]:= dLeft; Inc(position); end; Result:= directions[Random(count)]; end; procedure BuildMaze; var indexW: Integer; indexL: Integer; direction: TDirection; directions: TDirections; count: Integer; begin Entry:= Random(Width); indexW:= Entry; indexL:= 0; count:= 1; MatrixVisited[indexW][indexL]:= count; Inc(count); repeat directions:= [dUp, dRight, dDown, dLeft]; if (indexW = 0) or (MatrixVisited[Pred(indexW)][indexL] <> 0) then begin Exclude(directions, dLeft); end; if (indexL = 0) or (MatrixVisited[indexW][Pred(indexL)] <> 0) then begin Exclude(directions, dUp); end; if (indexW = Pred(Width)) or (MatrixVisited[Succ(indexW)][indexL] <> 0) then begin Exclude(directions, dRight); end; if (indexL = Pred(Length)) or (MatrixVisited[indexW][Succ(indexL)] <> 0) then begin Exclude(directions, dDown); end; if directions <> [] then begin direction:= GetRandomDirection(directions); case direction of dLeft:begin Dec(indexW); MatrixWalls[indexW][indexL]:= EXIT_RIGHT; end; dUp:begin Dec(indexL); MatrixWalls[indexW][indexL]:= EXIT_DOWN; end; dRight:begin Inc(MatrixWalls[indexW][indexL], EXIT_RIGHT); Inc(indexW); end; dDown:begin Inc(MatrixWalls[indexW][indexL], EXIT_DOWN); Inc(indexL); end; end; MatrixVisited[indexW][indexL]:= count; Inc(count); end else begin while True do begin if indexW <> Pred(Width) then begin Inc(indexW); end else if indexL <> Pred(Length) then begin Inc(indexL); indexW:= 0; end else begin indexW:= 0; indexL:= 0; end; if MatrixVisited[indexW][indexL] <> 0 then begin break; end; end; end; until count = (Width * Length) + 1; indexW:= Random(Width); indexL:= Pred(Length); Inc(MatrixWalls[indexW][indexL]); end; procedure DegubVisited; var indexW: Integer; indexL: Integer; begin WriteLN('Visited'); for indexL:= 0 to Pred(Length) do begin for indexW:= 0 to Pred(Width) do begin Write(MatrixVisited[indexW][indexL]:2,' '); end; WriteLN; end; WriteLN; end; procedure DebugWalls; var indexW: Integer; indexL: Integer; begin WriteLN('Walls'); for indexL:= 0 to Pred(Length) do begin for indexW:= 0 to Pred(Width) do begin Write(MatrixWalls[indexW, indexL]:2, ' '); end; WriteLN; end; WriteLN; end; procedure PrintMaze; var indexW: Integer; indexL: Integer; begin for indexW:= 0 to Pred(Width) do begin if indexW = Entry then begin Write('. '); end else begin Write('.--'); end; end; WriteLN('.'); for indexL:= 0 to Pred(Length) do begin Write('I'); for indexW:= 0 to Pred(Width) do begin if MatrixWalls[indexW, indexL] < 2 then begin Write(' I'); end else begin Write(' '); end; end; WriteLN; for indexW:= 0 to Pred(Width) do begin if (MatrixWalls[indexW, indexL] = 0) or (MatrixWalls[indexW, indexL] = 2) then begin Write(':--'); end else begin Write(': '); end; end; WriteLN('.'); end; WriteLN; end; begin Randomize; ClrScr; PrintGreeting; GetDimensions; ClearMatrices; BuildMaze; //DegubVisited; //DebugWalls; PrintMaze; end. ================================================ FILE: 00_Alternate_Languages/03_Animal/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/03_Animal/MiniScript/animal.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/03_Animal/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/03_Animal/animal.bas ================================================ 10 PRINT TAB(32);"ANIMAL" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 30 PRINT: PRINT: PRINT 40 PRINT "PLAY 'GUESS THE ANIMAL'" 45 PRINT 50 PRINT "THINK OF AN ANIMAL AND THE COMPUTER WILL TRY TO GUESS IT." 60 PRINT 70 DIM A$(200) 80 FOR I=0 TO 3 90 READ A$(I) 100 NEXT I 110 N=VAL(A$(0)) 120 REM MAIN CONTROL SECTION 130 INPUT "ARE YOU THINKING OF AN ANIMAL";A$ 140 IF A$="LIST" THEN 600 150 IF LEFT$(A$,1)<>"Y" THEN 120 160 K=1 170 GOSUB 390 180 IF LEN(A$(K))=0 THEN 999 190 IF LEFT$(A$(K),2)="\Q" THEN 170 200 PRINT "IS IT A ";RIGHT$(A$(K),LEN(A$(K))-2); 210 INPUT A$ 220 A$=LEFT$(A$,1) 230 IF LEFT$(A$,1)="Y" THEN PRINT "WHY NOT TRY ANOTHER ANIMAL?": GOTO 120 240 INPUT "THE ANIMAL YOU WERE THINKING OF WAS A ";V$ 250 PRINT "PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A" 260 PRINT V$;" FROM A ";RIGHT$(A$(K),LEN(A$(K))-2) 270 INPUT X$ 280 PRINT "FOR A ";V$;" THE ANSWER WOULD BE "; 290 INPUT A$ 300 A$=LEFT$(A$,1): IF A$<>"Y" AND A$<>"N" THEN 280 310 IF A$="Y" THEN B$="N" 320 IF A$="N" THEN B$="Y" 330 Z1=VAL(A$(0)) 340 A$(0)=STR$(Z1+2) 350 A$(Z1)=A$(K) 360 A$(Z1+1)="\A"+V$ 370 A$(K)="\Q"+X$+"\"+A$+STR$(Z1+1)+"\"+B$+STR$(Z1)+"\" 380 GOTO 120 390 REM SUBROUTINE TO PRINT QUESTIONS 400 Q$=A$(K) 410 FOR Z=3 TO LEN(Q$) 415 IF MID$(Q$,Z,1)<>"\" THEN PRINT MID$(Q$,Z,1);: NEXT Z 420 INPUT C$ 430 C$=LEFT$(C$,1) 440 IF C$<>"Y" AND C$<>"N" THEN 410 450 T$="\"+C$ 455 FOR X=3 TO LEN(Q$)-1 460 IF MID$(Q$,X,2)=T$ THEN 480 470 NEXT X 475 STOP 480 FOR Y=X+1 TO LEN(Q$) 490 IF MID$(Q$,Y,1)="\" THEN 510 500 NEXT Y 505 STOP 510 K=VAL(MID$(Q$,X+2,Y-X-2)) 520 RETURN 530 DATA "4","\QDOES IT SWIM\Y2\N3\","\AFISH","\ABIRD" 600 PRINT:PRINT "ANIMALS I ALREADY KNOW ARE:" 605 X=0 610 FOR I=1 TO 200 620 IF LEFT$(A$(I),2)<>"\A" THEN 650 624 PRINT TAB(15*X); 630 FOR Z=3 TO LEN(A$(I)) 640 IF MID$(A$(I),Z,1)<>"\" THEN PRINT MID$(A$(I),Z,1);: NEXT Z 645 X=X+1: IF X=4 THEN X=0: PRINT 650 NEXT I 660 PRINT 670 PRINT 680 GOTO 120 999 END ================================================ FILE: 00_Alternate_Languages/03_Animal/go/main.go ================================================ package main import ( "bufio" "fmt" "log" "os" "strings" ) type node struct { text string yesNode *node noNode *node } func newNode(text string, yes_node, no_node *node) *node { n := node{text: text} if yes_node != nil { n.yesNode = yes_node } if no_node != nil { n.noNode = no_node } return &n } func (n *node) update(newQuestion, newAnswer, newAnimal string) { oldAnimal := n.text n.text = newQuestion if newAnswer == "y" { n.yesNode = newNode(newAnimal, nil, nil) n.noNode = newNode(oldAnimal, nil, nil) } else { n.yesNode = newNode(oldAnimal, nil, nil) n.noNode = newNode(newAnimal, nil, nil) } } func (n *node) isLeaf() bool { return (n.yesNode == nil) && (n.noNode == nil) } func listKnownAnimals(root *node) { if root == nil { return } if root.isLeaf() { fmt.Printf("%s ", root.text) return } if root.yesNode != nil { listKnownAnimals(root.yesNode) } if root.noNode != nil { listKnownAnimals(root.noNode) } } func parseInput(message string, checkList bool, rootNode *node) string { scanner := bufio.NewScanner(os.Stdin) token := "" for { fmt.Println(message) scanner.Scan() inp := strings.ToLower(scanner.Text()) if checkList && inp == "list" { fmt.Println("Animals I already know are:") listKnownAnimals(rootNode) fmt.Println() } if len(inp) > 0 { token = inp } else { token = "" } if token == "y" || token == "n" { break } } return token } func avoidVoidInput(message string) string { scanner := bufio.NewScanner(os.Stdin) answer := "" for { fmt.Println(message) scanner.Scan() answer = scanner.Text() if answer != "" { break } } return answer } func printIntro() { fmt.Println(" Animal") fmt.Println(" Creative Computing Morristown, New Jersey") fmt.Println("\nPlay 'Guess the Animal'") fmt.Println("Think of an animal and the computer will try to guess it") } func main() { yesChild := newNode("Fish", nil, nil) noChild := newNode("Bird", nil, nil) rootNode := newNode("Does it swim?", yesChild, noChild) printIntro() keepPlaying := (parseInput("Are you thinking of an animal?", true, rootNode) == "y") for keepPlaying { keepAsking := true actualNode := rootNode for keepAsking { if !actualNode.isLeaf() { answer := parseInput(actualNode.text, false, nil) if answer == "y" { if actualNode.yesNode == nil { log.Fatal("invalid node") } actualNode = actualNode.yesNode } else { if actualNode.noNode == nil { log.Fatal("invalid node") } actualNode = actualNode.noNode } } else { answer := parseInput(fmt.Sprintf("Is it a %s?", actualNode.text), false, nil) if answer == "n" { newAnimal := avoidVoidInput("The animal you were thinking of was a ?") newQuestion := avoidVoidInput(fmt.Sprintf("Please type in a question that would distinguish a '%s' from a '%s':", newAnimal, actualNode.text)) newAnswer := parseInput(fmt.Sprintf("For a '%s' the answer would be", newAnimal), false, nil) actualNode.update(newQuestion+"?", newAnswer, newAnimal) } else { fmt.Println("Why not try another animal?") } keepAsking = false } } keepPlaying = (parseInput("Are you thinking of an animal?", true, rootNode) == "y") } } ================================================ FILE: 00_Alternate_Languages/04_Awari/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/04_Awari/MiniScript/awari.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/04_Awari/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/04_Awari/awari.bas ================================================ 5 PRINT TAB(34);"AWARI" 7 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 10 DATA 0 15 DIM B(13),G(13),F(50):READ N 20 PRINT:PRINT:E=0 25 FOR I=0 TO 12:B(I)=3:NEXT I 30 C=0:F(N)=0:B(13)=0:B(6)=0 35 GOSUB 500 40 PRINT "YOUR MOVE";:GOSUB 110 45 IF E=0 THEN 80 50 IF M=H THEN GOSUB 100 55 IF E=0 THEN 80 60 PRINT "MY MOVE IS ";:GOSUB 800 65 IF E=0 THEN 80 70 IF M=H THEN PRINT ",";:GOSUB 800 75 IF E>0 THEN 35 80 PRINT:PRINT"GAME OVER" 85 D=B(6)-B(13):IF D<0 THEN PRINT "I WIN BY";-D;"POINTS":GOTO 20 90 N=N+1:IF D=0 THEN PRINT "DRAWN GAME":GOTO 20 95 PRINT "YOU WIN BY";D;"POINTS":GOTO 20 100 PRINT "AGAIN"; 110 INPUT M:IF M<7 THEN IF M>0 THEN M=M-1:GOTO 130 120 PRINT "ILLEGAL MOVE":GOTO 100 130 IF B(M)=0 THEN 120 140 H=6:GOSUB 200 150 GOTO 500 200 K=M:GOSUB 600 205 E=0:IF K>6 THEN K=K-7 210 C=C+1:IF C<9 THEN F(N)=F(N)*6+K 215 FOR I=0 TO 5:IF B(I)<>0 THEN 230 220 NEXT I 225 RETURN 230 FOR I=7 TO 12:IF B(I)<>0 THEN E=1:RETURN 235 GOTO 220 500 PRINT:PRINT" "; 505 FOR I=12 TO 7 STEP -1:GOSUB 580 510 NEXT I 515 PRINT:I=13:GOSUB 580 520 PRINT " ";:PRINT B(6):PRINT " "; 525 FOR I=0 TO 5:GOSUB 580 530 NEXT I 535 PRINT:PRINT:RETURN 580 IF B(I)<10 THEN PRINT " "; 585 PRINT B(I);:RETURN 600 P=B(M):B(M)=0 605 FOR P=P TO 1 STEP -1:M=M+1:IF M>13 THEN M=M-14 610 B(M)=B(M)+1:NEXT P 615 IF B(M)=1 THEN IF M<>6 THEN IF M<>13 THEN IF B(12-M)<>0 THEN 625 620 RETURN 625 B(H)=B(H)+B(12-M)+1:B(M)=0:B(12-M)=0:RETURN 800 D=-99:H=13 805 FOR I=0 TO 13:G(I)=B(I):NEXT I 810 FOR J=7 TO 12:IF B(J)=0 THEN 885 815 Q=0:M=J:GOSUB 600 820 FOR I=0 TO 5:IF B(I)=0 THEN 845 825 L=B(I)+I:R=0 830 IF L>13 THEN L=L-14:R=1:GOTO 830 835 IF B(L)=0 THEN IF L<>6 THEN IF L<>13 THEN R=B(12-L)+R 840 IF R>Q THEN Q=R 845 NEXT I 850 Q=B(13)-B(6)-Q:IF C>8 THEN 875 855 K=J:IF K>6 THEN K=K-7 860 FOR I=0 TO N-1:IF F(N)*6+K=INT(F(I)/6^(7-C)+.1) THEN Q=Q-2 870 NEXT I 875 FOR I=0 TO 13:B(I)=G(I):NEXT I 880 IF Q>=D THEN A=J:D=Q 885 NEXT J 890 M=A:PRINT CHR$(42+M);:GOTO 200 900 FOR I=0 TO N-1:PRINT B(I):NEXT I 999 END ================================================ FILE: 00_Alternate_Languages/04_Awari/elm/.gitignore ================================================ # Created by https://www.toptal.com/developers/gitignore/api/macos,visualstudiocode,elm # Edit at https://www.toptal.com/developers/gitignore?templates=macos,visualstudiocode,elm ### Elm ### # elm-package generated files elm-stuff # elm-repl generated files repl-temp-* ### macOS ### # General .DS_Store .AppleDouble .LSOverride # Icon must end with two \r Icon # Thumbnails ._* # Files that might appear in the root of a volume .DocumentRevisions-V100 .fseventsd .Spotlight-V100 .TemporaryItems .Trashes .VolumeIcon.icns .com.apple.timemachine.donotpresent # Directories potentially created on remote AFP share .AppleDB .AppleDesktop Network Trash Folder Temporary Items .apdisk ### VisualStudioCode ### .vscode/* !.vscode/settings.json !.vscode/tasks.json !.vscode/launch.json !.vscode/extensions.json !.vscode/*.code-snippets # Local History for Visual Studio Code .history/ # Built Visual Studio Code Extensions *.vsix ### VisualStudioCode Patch ### # Ignore all local history of files .history .ionide # Support for Project snippet scope # End of https://www.toptal.com/developers/gitignore/api/macos,visualstudiocode,elm # Created by https://www.toptal.com/developers/gitignore/api/node # Edit at https://www.toptal.com/developers/gitignore?templates=node ### Node ### # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* lerna-debug.log* .pnpm-debug.log* # Diagnostic reports (https://nodejs.org/api/report.html) report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json # Runtime data pids *.pid *.seed *.pid.lock # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage *.lcov # nyc test coverage .nyc_output # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) .grunt # Bower dependency directory (https://bower.io/) bower_components # node-waf configuration .lock-wscript # Compiled binary addons (https://nodejs.org/api/addons.html) build/Release # Dependency directories node_modules/ jspm_packages/ # Snowpack dependency directory (https://snowpack.dev/) web_modules/ # TypeScript cache *.tsbuildinfo # Optional npm cache directory .npm # Optional eslint cache .eslintcache # Optional stylelint cache .stylelintcache # Microbundle cache .rpt2_cache/ .rts2_cache_cjs/ .rts2_cache_es/ .rts2_cache_umd/ # Optional REPL history .node_repl_history # Output of 'npm pack' *.tgz # Yarn Integrity file .yarn-integrity # dotenv environment variable files .env .env.development.local .env.test.local .env.production.local .env.local # parcel-bundler cache (https://parceljs.org/) .cache .parcel-cache # Next.js build output .next out # Nuxt.js build / generate output .nuxt dist # Gatsby files .cache/ # Comment in the public line in if your project uses Gatsby and not Next.js # https://nextjs.org/blog/next-9-1#public-directory-support # public # vuepress build output .vuepress/dist # vuepress v2.x temp and cache directory .temp # Docusaurus cache and generated files .docusaurus # Serverless directories .serverless/ # FuseBox cache .fusebox/ # DynamoDB Local files .dynamodb/ # TernJS port file .tern-port # Stores VSCode versions used for testing VSCode extensions .vscode-test # yarn v2 .yarn/cache .yarn/unplugged .yarn/build-state.yml .yarn/install-state.gz .pnp.* ### Node Patch ### # Serverless Webpack directories .webpack/ # Optional stylelint cache # SvelteKit build / generate output .svelte-kit # End of https://www.toptal.com/developers/gitignore/api/node app.js ================================================ FILE: 00_Alternate_Languages/04_Awari/elm/README.md ================================================ # Awari This is an Elm implementation of the `Basic Compouter Games` Game Awari. ## Build App - install elm ```bash yarn yarn build ``` ================================================ FILE: 00_Alternate_Languages/04_Awari/elm/docs/index.html ================================================ <!DOCTYPE html> <html> <head> <link rel="stylesheet" href="https://unpkg.com/@picocss/pico@latest/css/pico.min.css"> </head> <body> <div id="elm-app-is-loaded-here" /> <script src="app.js"></script> <script> var app = Elm.Main.init({ node: document.getElementById("elm-app-is-loaded-here"), flags: {} }); </script> </body> </html> ================================================ FILE: 00_Alternate_Languages/04_Awari/elm/elm.json ================================================ { "type": "application", "source-directories": [ "src" ], "elm-version": "0.19.1", "dependencies": { "direct": { "elm/browser": "1.0.2", "elm/core": "1.0.5", "elm/html": "1.0.0" }, "indirect": { "elm/json": "1.1.3", "elm/time": "1.0.0", "elm/url": "1.0.0", "elm/virtual-dom": "1.0.2" } }, "test-dependencies": { "direct": {}, "indirect": {} } } ================================================ FILE: 00_Alternate_Languages/04_Awari/elm/package.json ================================================ { "name": "04_Awari", "version": "1.0.0", "main": "index.js", "license": "MIT", "scripts": { "live": "elm-live src/Main.elm --proxy-prefix=/api --proxy-host=http://localhost:8080/api --open --start-page=resources/index.html -- --output=app.js --debug", "build": "elm make src/Main.elm --optimize --output docs/app.js && cp -R resources/ docs/" }, "devDependencies": { "elm-live": "^4.0.2" } } ================================================ FILE: 00_Alternate_Languages/04_Awari/elm/resources/index.html ================================================ <!DOCTYPE html> <html> <head> <link rel="stylesheet" href="https://unpkg.com/@picocss/pico@latest/css/pico.min.css"> </head> <body> <div id="elm-app-is-loaded-here" /> <script src="app.js"></script> <script> var app = Elm.Main.init({ node: document.getElementById("elm-app-is-loaded-here"), flags: {} }); </script> </body> </html> ================================================ FILE: 00_Alternate_Languages/04_Awari/elm/src/Main.elm ================================================ module Main exposing (main) import Array exposing (Array, repeat) import Browser import Html exposing (..) import Html.Attributes exposing (style) import Html.Events exposing (onClick) -- Main main : Program () Model Msg main = Browser.element { init = init , view = view , update = update , subscriptions = \_ -> Sub.none } -- Model type alias Model = { board : Array Int , playerOnesTurn : Bool , gameFinished : Bool } init : () -> ( Model, Cmd Msg ) init _ = ( { board = initArray 0 36 (repeat boardLength 0), playerOnesTurn = False, gameFinished = False }, Cmd.none ) -- Messages type Msg = BoardClicked Int | Reset -- Update update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = case msg of BoardClicked index -> if indexIsOnPit index || index < rightPitIndex && model.playerOnesTurn || index > rightPitIndex && not model.playerOnesTurn then ( model, Cmd.none ) else let lastStoneInPitIndex = lastStonePitPosition index model.board boardAfterStonesSet = changesOnBoardAfterStonesSet model.playerOnesTurn lastStoneInPitIndex (boardClicked index model.board) nextPlayer = determineNextPlayer lastStoneInPitIndex model.playerOnesTurn boardAfterStonesSet in ( { model | board = boardAfterStonesSet, playerOnesTurn = nextPlayer, gameFinished = calculateGameFinished boardAfterStonesSet }, Cmd.none ) Reset -> init () -- Constants boardLength : Int boardLength = 14 leftPitIndex : Int leftPitIndex = 0 rightPitIndex : Int rightPitIndex = 7 -- View view : Model -> Html Msg view model = div [ style "margin" "4rem" ] [ h1 [] [ text "Awari" ] , viewDescription , getPlayerTurnText False model.playerOnesTurn , viewBoard model , viewWinnerIfGameFinished model , getPlayerTurnText True model.playerOnesTurn , div [ style "display" "flex" , style "justify-content" "center" ] [ button [ onClick Reset, style "max-width" "10rem", style "margin" "0", style "display" "inline-block" ] [ text "Restart" ] ] ] getPlayerTurnText : Bool -> Bool -> Html msg getPlayerTurnText playerOne playerOnesTurn = if playerOne && playerOnesTurn || not playerOnesTurn && not playerOne then div [ style "height" "2rem", style "margin" "1rem 0", style "font-size" "1.5rem" ] [ text ("It's your turn Player " ++ (if playerOnesTurn then " 2 " else " 1 " ) ) ] else div [ style "height" "2rem", style "margin" "1rem", style "font-size" "1.5rem" ] [] viewWinnerIfGameFinished : Model -> Html msg viewWinnerIfGameFinished model = if not model.gameFinished then div [] [] else div [] [ text ("Winner is" ++ winnerPlayer model) ] winnerPlayer : Model -> String winnerPlayer model = if getLeftPitValue model.board > getRightPitValue model.board then "Player 1" else if getLeftPitValue model.board < getRightPitValue model.board then "Player 2" else "None" viewBoard : Model -> Html Msg viewBoard model = let boardGenerator = boardCard model.playerOnesTurn in div boardSyle (Array.toList <| Array.indexedMap boardGenerator model.board ) boardSyle : List (Attribute msg) boardSyle = [ style "display" "flex" , style "gap" "1rem" , style "display" "grid" , style "grid-template-columns" "repeat(8, 1fr)" , style "align-items" "center" , style "grid-template-areas" """ 'a b c d e f g h' 'a n m l k j i h' """ ] boardCard : Bool -> Int -> Int -> Html Msg boardCard playerOnesTurn index element = div (boardCardStyle playerOnesTurn index) [ text (String.fromInt element) ] boardCardStyle : Bool -> Int -> List (Attribute Msg) boardCardStyle playerOnesTurn index = if not playerOnesTurn && index < rightPitIndex || playerOnesTurn && index >= rightPitIndex then [ onClick (BoardClicked index) , style "grid-area" (areaFromIndex index) , style "cursor" "pointer" , style "background" "#36454F" , style "border-radius" "6px" , style "text-align" "center" , style "padding" "0.5rem" ] else [ style "grid-area" (areaFromIndex index) , style "background" "black" , style "border-radius" "6px" , style "text-align" "center" , style "padding" "0.5rem" ] viewDescription : Html msg viewDescription = details [] [ summary [] [ text "How the game works" ] , p [] [ text """ Awari is an ancient African game played with seven sticks and thirty-six stones or beans laid out as shown above. The board is divided into six compartments or pits on each side. In addition, there are two special home pits at the ends. A move is made by taking all the beans from any (non-empty) pit on your own side. Starting from the pit to the right of this one, these beans are ‘sown’ one in each pit working around the board anticlockwise. A turn consists of one or two moves. If the last bean of your move is sown in your own home you may take a second move. If the last bean sown in a move lands in an empty pit, provided that the opposite pit is not empty, all the beans in the opposite pit, together with the last bean sown are ‘captured’ and moved to the player’s home. When either side is empty, the game is finished. The player with the most beans in his home has won. """ ] ] -- Functions checkIfPlayerCanNotDoMove : Bool -> Array Int -> Bool checkIfPlayerCanNotDoMove playerOnesTurn board = if playerOnesTurn then Array.foldr (+) 0 (Array.slice (rightPitIndex + 1) boardLength board) == 0 else Array.foldr (+) 0 (Array.slice (leftPitIndex + 1) rightPitIndex board) == 0 determineNextPlayer : Int -> Bool -> Array Int -> Bool determineNextPlayer index playerOnesTurn board = if checkIfPlayerCanNotDoMove (not playerOnesTurn) board then playerOnesTurn else if indexIsOnPit index then playerOnesTurn else not playerOnesTurn indexIsOnPit : Int -> Bool indexIsOnPit index = index == leftPitIndex || index == rightPitIndex calculateGameFinished : Array Int -> Bool calculateGameFinished board = Array.foldr (+) 0 board - getLeftPitValue board - getRightPitValue board == 0 getLeftPitValue : Array Int -> Int getLeftPitValue board = getPitValue leftPitIndex board getRightPitValue : Array Int -> Int getRightPitValue board = getPitValue rightPitIndex board getPitValue : Int -> Array Int -> Int getPitValue index board = Maybe.withDefault 0 (Array.get index board) lastStonePitPosition : Int -> Array Int -> Int lastStonePitPosition index board = calcLastStonePosition index board (Maybe.withDefault 0 (Array.get index board)) calcLastStonePosition : Int -> Array Int -> Int -> Int calcLastStonePosition index board stones = if stones == 0 then index else calcLastStonePosition (incrementRotatingIndex index board) board (stones - 1) changesOnBoardAfterStonesSet : Bool -> Int -> Array Int -> Array Int changesOnBoardAfterStonesSet playerOne lastStonesIndex board = if Maybe.withDefault 0 (Array.get lastStonesIndex board) == 1 then if indexIsOnPit lastStonesIndex then board else let sumStones = Maybe.withDefault 0 (Array.get (boardLength - lastStonesIndex) board) + 1 currentPit = if playerOne then getRightPitValue board else getLeftPitValue board baordWithIndexZero = Array.set lastStonesIndex 0 board baordWithOppositeZero = Array.set (boardLength - lastStonesIndex) 0 baordWithIndexZero boardWithPitSum = if playerOne then Array.set rightPitIndex (sumStones + currentPit) baordWithOppositeZero else Array.set leftPitIndex (sumStones + currentPit) baordWithOppositeZero in boardWithPitSum else board initArray : Int -> Int -> Array Int -> Array Int initArray index stones array = if stones == 0 then array else if indexIsOnPit index then initArray (index + 1) stones array else initArray (index + 1) (stones - 3) (Array.set index 3 array) boardClicked : Int -> Array Int -> Array Int boardClicked index board = if indexIsOnPit index then board else let stonesLeft = Maybe.withDefault 0 (Array.get index board) startBoard = Array.set index 0 board in placeStones (incrementRotatingIndex index startBoard) stonesLeft startBoard incrementRotatingIndex : Int -> Array Int -> Int incrementRotatingIndex currentIndex board = if 0 == currentIndex then Array.length board - 1 else currentIndex - 1 placeStones : Int -> Int -> Array Int -> Array Int placeStones index stonesLeft board = if stonesLeft == 0 then board else let oldStones = Maybe.withDefault 0 (Array.get index board) in placeStones (incrementRotatingIndex index board) (stonesLeft - 1) (Array.set index (oldStones + 1) board) areaFromIndex : Int -> String areaFromIndex index = case index of 0 -> "a" 1 -> "b" 2 -> "c" 3 -> "d" 4 -> "e" 5 -> "f" 6 -> "g" 7 -> "h" 8 -> "i" 9 -> "j" 10 -> "k" 11 -> "l" 12 -> "m" 13 -> "n" i -> String.fromInt i ================================================ FILE: 00_Alternate_Languages/05_Bagels/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/05_Bagels/MiniScript/bagels.ms ================================================ 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." ================================================ FILE: 00_Alternate_Languages/05_Bagels/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/05_Bagels/bagels.bas ================================================ 5 PRINT TAB(33);"BAGELS" 10 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY":PRINT:PRINT 15 REM *** BAGLES NUMBER GUESSING GAME 20 REM *** ORIGINAL SOURCE UNKNOWN BUT SUSPECTED TO BE 25 REM *** LAWRENCE HALL OF SCIENCE, U.C. BERKELY 30 DIM A1(3),A(3),B(3) 40 Y=0:T=255 50 PRINT:PRINT:PRINT 70 INPUT "WOULD YOU LIKE THE RULES (YES OR NO)";A$ 90 IF LEFT$(A$,1)="N" THEN 150 100 PRINT:PRINT "I AM THINKING OF A THREE-DIGIT NUMBER. TRY TO GUESS" 110 PRINT "MY NUMBER AND I WILL GIVE YOU CLUES AS FOLLOWS:" 120 PRINT " PICO - ONE DIGIT CORRECT BUT IN THE WRONG POSITION" 130 PRINT " FERMI - ONE DIGIT CORRECT AND IN THE RIGHT POSITION" 140 PRINT " BAGELS - NO DIGITS CORRECT" 150 FOR I=1 TO 3 160 A(I)=INT(10*RND(1)) 165 IF I-1=0 THEN 200 170 FOR J=1 TO I-1 180 IF A(I)=A(J) THEN 160 190 NEXT J 200 NEXT I 210 PRINT:PRINT "O.K. I HAVE A NUMBER IN MIND." 220 FOR I=1 TO 20 230 PRINT "GUESS #";I, 240 INPUT A$ 245 IF LEN(A$)<>3 THEN 630 250 FOR Z=1 TO 3:A1(Z)=ASC(MID$(A$,Z,1)):NEXT Z 260 FOR J=1 TO 3 270 IF A1(J)<48 THEN 300 280 IF A1(J)>57 THEN 300 285 B(J)=A1(J)-48 290 NEXT J 295 GOTO 320 300 PRINT "WHAT?" 310 GOTO 230 320 IF B(1)=B(2) THEN 650 330 IF B(2)=B(3) THEN 650 340 IF B(3)=B(1) THEN 650 350 C=0:D=0 360 FOR J=1 TO 2 370 IF A(J)<>B(J+1) THEN 390 380 C=C+1 390 IF A(J+1)<>B(J) THEN 410 400 C=C+1 410 NEXT J 420 IF A(1)<>B(3) THEN 440 430 C=C+1 440 IF A(3)<>B(1) THEN 460 450 C=C+1 460 FOR J=1 TO 3 470 IF A(J)<>B(J) THEN 490 480 D=D+1 490 NEXT J 500 IF D=3 THEN 680 505 IF C=0 THEN 545 520 FOR J=1 TO C 530 PRINT "PICO "; 540 NEXT J 545 IF D=0 THEN 580 550 FOR J=1 TO D 560 PRINT "FERMI "; 570 NEXT J 580 IF C+D<>0 THEN 600 590 PRINT "BAGELS"; 600 PRINT 605 NEXT I 610 PRINT "OH WELL." 615 PRINT "THAT'S TWENTY GUESSES. MY NUMBER WAS";100*A(1)+10*A(2)+A(3) 620 GOTO 700 630 PRINT "TRY GUESSING A THREE-DIGIT NUMBER.":GOTO 230 650 PRINT "OH, I FORGOT TO TELL YOU THAT THE NUMBER I HAVE IN MIND" 660 PRINT "HAS NO TWO DIGITS THE SAME.":GOTO 230 680 PRINT "YOU GOT IT!!!":PRINT 690 Y=Y+1 700 INPUT "PLAY AGAIN (YES OR NO)";A$ 720 IF LEFT$(A$,1)="Y" THEN 150 730 IF Y=0 THEN 750 740 PRINT:PRINT "A";Y;"POINT BAGELS BUFF!!" 750 PRINT "HOPE YOU HAD FUN. BYE." 999 END ================================================ FILE: 00_Alternate_Languages/05_Bagels/go/main.go ================================================ package main import ( "bufio" "fmt" "math/rand" "os" "strconv" "strings" "time" ) const MAXGUESSES int = 20 func printWelcome() { fmt.Println("\n Bagels") fmt.Println("Creative Computing Morristown, New Jersey") fmt.Println() } func printRules() { fmt.Println() fmt.Println("I am thinking of a three-digit number. Try to guess") fmt.Println("my number and I will give you clues as follows:") fmt.Println(" PICO - One digit correct but in the wrong position") fmt.Println(" FERMI - One digit correct and in the right position") fmt.Println(" BAGELS - No digits correct") } func getNumber() []string { numbers := []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"} rand.Shuffle(len(numbers), func(i, j int) { numbers[i], numbers[j] = numbers[j], numbers[i] }) return numbers[:3] } func getValidGuess(guessNumber int) string { var guess string scanner := bufio.NewScanner(os.Stdin) valid := false for !valid { fmt.Printf("Guess # %d?\n", guessNumber) scanner.Scan() guess = strings.TrimSpace(scanner.Text()) // guess must be 3 characters if len(guess) == 3 { // and should be numeric _, err := strconv.Atoi(guess) if err != nil { fmt.Println("What?") } else { // and the numbers should be unique if (guess[0:1] != guess[1:2]) && (guess[0:1] != guess[2:3]) && (guess[1:2] != guess[2:3]) { valid = true } else { fmt.Println("Oh, I forgot to tell you that the number I have in mind") fmt.Println("has no two digits the same.") } } } else { fmt.Println("Try guessing a three-digit number.") } } return guess } func buildResultString(num []string, guess string) string { result := "" // correct digits in wrong place for i := 0; i < 2; i++ { if num[i] == guess[i+1:i+2] { result += "PICO " } if num[i+1] == guess[i:i+1] { result += "PICO " } } if num[0] == guess[2:3] { result += "PICO " } if num[2] == guess[0:1] { result += "PICO " } // correct digits in right place for i := 0; i < 3; i++ { if num[i] == guess[i:i+1] { result += "FERMI " } } // nothing right? if result == "" { result = "BAGELS" } return result } func main() { rand.Seed(time.Now().UnixNano()) scanner := bufio.NewScanner(os.Stdin) printWelcome() fmt.Println("Would you like the rules (Yes or No)? ") scanner.Scan() response := scanner.Text() if len(response) > 0 { if strings.ToUpper(response[0:1]) != "N" { printRules() } } else { printRules() } gamesWon := 0 stillRunning := true for stillRunning { num := getNumber() numStr := strings.Join(num, "") guesses := 1 fmt.Println("\nO.K. I have a number in mind.") guessing := true for guessing { guess := getValidGuess(guesses) if guess == numStr { fmt.Println("You got it!!") gamesWon++ guessing = false } else { fmt.Println(buildResultString(num, guess)) guesses++ if guesses > MAXGUESSES { fmt.Println("Oh well") fmt.Printf("That's %d guesses. My number was %s\n", MAXGUESSES, numStr) guessing = false } } } validRespone := false for !validRespone { fmt.Println("Play again (Yes or No)?") scanner.Scan() response := scanner.Text() if len(response) > 0 { validRespone = true if strings.ToUpper(response[0:1]) != "Y" { stillRunning = false } } } } if gamesWon > 0 { fmt.Printf("\nA %d point Bagels buff!!\n", gamesWon) } fmt.Println("Hope you had fun. Bye") } ================================================ FILE: 00_Alternate_Languages/05_Bagels/nim/bagels.nim ================================================ 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." ================================================ FILE: 00_Alternate_Languages/06_Banner/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/06_Banner/MiniScript/banner.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/06_Banner/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/06_Banner/banner.bas ================================================ 10 INPUT "HORIZONTAL";X 20 INPUT "VERTICAL";Y 21 INPUT "CENTERED";L$ 22 G1=0:IF L$>"P" THEN G1=1 23 INPUT "CHARACTER (TYPE 'ALL' IF YOU WANT CHARACTER BEING PRINTED)";M$ 29 PRINT "STATEMENT"; 30 INPUT A$ 35 INPUT "SET PAGE";O$ 40 A=ASC(LEFT$(A$,1)) 50 REM 60 REM 70 FOR T=1 TO LEN(A$) 80 P$=MID$(A$,T,1) 90 FOR O=1 TO 50 95 READ S$,S(1),S(2),S(3),S(4),S(5),S(6),S(7) 96 IF P$=" " THEN 812 100 IF P$=S$ THEN 200 120 NEXT O 200 RESTORE 201 X$=M$ 202 IF M$="ALL" THEN X$=S$ 205 FOR U=1 TO 7 210 FOR K=8 TO 0 STEP -1 230 IF 2^K<S(U) THEN 270 240 J(9-K)=0 250 GOTO 280 270 J(9-K)=1: S(U)=S(U)-2^K 272 IF S(U)=1 THEN 815 280 NEXT K 445 FOR T1=1 TO X 447 PRINT TAB((63-4.5*Y)*G1/(LEN(X$))+1); 450 FOR B=1 TO F(U) 460 IF J(B)=0 THEN 500 465 FOR I=1 TO Y: PRINT X$;: NEXT I 470 GOTO 600 500 FOR I=1 TO Y 510 FOR I1=1 TO LEN(X$) 520 PRINT " ";: NEXT I1 530 NEXT I 600 NEXT B 620 PRINT 630 NEXT T1 700 NEXT U 750 FOR H=1 TO 2*X: PRINT: NEXT H 800 NEXT T 806 FOR H=1 TO 75: PRINT: NEXT H 810 END 812 FOR H=1 TO 7*X: PRINT: NEXT H 813 GOTO 800 815 F(U)=9-K: GOTO 445 899 DATA " ",0,0,0,0,0,0,0 900 DATA "A",505,37,35,34,35,37,505 901 DATA "G",125,131,258,258,290,163,101 902 DATA "E",512,274,274,274,274,258,258 903 DATA "T",2,2,2,512,2,2,2 904 DATA "W",256,257,129,65,129,257,256 905 DATA "L",512,257,257,257,257,257,257 906 DATA "S",69,139,274,274,274,163,69 907 DATA "O",125,131,258,258,258,131,125 908 DATA "N",512,7,9,17,33,193,512 909 DATA "F",512,18,18,18,18,2,2 910 DATA "K",512,17,17,41,69,131,258 911 DATA "B",512,274,274,274,274,274,239 912 DATA "D",512,258,258,258,258,131,125 913 DATA "H",512,17,17,17,17,17,512 914 DATA "M",512,7,13,25,13,7,512 915 DATA "?",5,3,2,354,18,11,5 916 DATA "U",128,129,257,257,257,129,128 917 DATA "R",512,18,18,50,82,146,271 918 DATA "P",512,18,18,18,18,18,15 919 DATA "Q",125,131,258,258,322,131,381 920 DATA "Y",8,9,17,481,17,9,8 921 DATA "V",64,65,129,257,129,65,64 922 DATA "X",388,69,41,17,41,69,388 923 DATA "Z",386,322,290,274,266,262,260 924 DATA "I",258,258,258,512,258,258,258 925 DATA "C",125,131,258,258,258,131,69 926 DATA "J",65,129,257,257,257,129,128 927 DATA "1",0,0,261,259,512,257,257 928 DATA "2",261,387,322,290,274,267,261 929 DATA "*",69,41,17,512,17,41,69 930 DATA "3",66,130,258,274,266,150,100 931 DATA "4",33,49,41,37,35,512,33 932 DATA "5",160,274,274,274,274,274,226 933 DATA "6",194,291,293,297,305,289,193 934 DATA "7",258,130,66,34,18,10,8 935 DATA "8",69,171,274,274,274,171,69 936 DATA "9",263,138,74,42,26,10,7 937 DATA "=",41,41,41,41,41,41,41 938 DATA "!",1,1,1,384,1,1,1 939 DATA "0",57,69,131,258,131,69,57 940 DATA ".",1,1,129,449,129,1,1 1000 STOP 1002 END ================================================ FILE: 00_Alternate_Languages/07_Basketball/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/07_Basketball/MiniScript/basketball.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/07_Basketball/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/07_Basketball/basketball.bas ================================================ 5 PRINT TAB(31);"BASKETBALL" 7 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 8 PRINT:PRINT:PRINT 10 PRINT "THIS IS DARTMOUTH COLLEGE BASKETBALL. YOU WILL BE DARTMOUTH" 20 PRINT " CAPTAIN AND PLAYMAKER. CALL SHOTS AS FOLLOWS: 1. LONG" 30 PRINT " (30 FT.) JUMP SHOT; 2. SHORT (15 FT.) JUMP SHOT; 3. LAY" 40 PRINT " UP; 4. SET SHOT." 60 PRINT "BOTH TEAMS WILL USE THE SAME DEFENSE. CALL DEFENSE AS" 70 PRINT "FOLLOWS: 6. PRESS; 6.5 MAN-TO MAN; 7. ZONE; 7.5 NONE." 72 PRINT "TO CHANGE DEFENSE, JUST TYPE 0 AS YOUR NEXT SHOT." 76 INPUT "YOUR STARTING DEFENSE WILL BE";D:IF D<6 THEN 2010 79 PRINT 80 INPUT "CHOOSE YOUR OPPONENT";O$ 370 PRINT "CENTER JUMP" 390 IF RND(1)> 3/5 THEN 420 400 PRINT O$;" CONTROLS THE TAP." 410 GOTO 3000 420 PRINT "DARTMOUTH CONTROLS THE TAP." 425 PRINT 430 INPUT "YOUR SHOT";Z 440 P=0 445 IF Z<>INT(Z) THEN 455 446 IF Z<0 OR Z>4 THEN 455 447 GOTO 460 455 PRINT "INCORRECT ANSWER. RETYPE IT. ";:GOTO 430 460 IF RND(1)<.5 THEN 1000 480 IF T<100 THEN 1000 490 PRINT 491 IF S(1)<>S(0) THEN 510 492 PRINT:PRINT " ***** END OF SECOND HALF *****":PRINT 493 PRINT "SCORE AT END OF REGULATION TIME:" 494 PRINT " DARTMOUTH:";S(1);" ";O$;":";S(0) 495 PRINT 496 PRINT "BEGIN TWO MINUTE OVERTIME PERIOD" 499 T=93 500 GOTO 370 510 PRINT " ***** END OF GAME *****" 515 PRINT "FINAL SCORE: DARTMOUTH:";S(1);" ";O$;":";S(0) 520 STOP 600 PRINT 610 PRINT " *** TWO MINUTES LEFT IN THE GAME ***" 620 PRINT 630 RETURN 1000 ON Z GOTO 1040,1040 1030 GOTO 1300 1040 T=T+1 1041 IF T=50 THEN 8000 1042 IF T=92 THEN 1046 1043 GOTO 1050 1046 GOSUB 600 1050 PRINT "JUMP SHOT" 1060 IF RND(1)>.341*D/8 THEN 1090 1070 PRINT "SHOT IS GOOD." 1075 GOSUB 7000 1085 GOTO 3000 1090 IF RND(1)>.682*D/8 THEN 1200 1100 PRINT "SHOT IS OFF TARGET." 1105 IF D/6*RND(1)>.45 THEN 1130 1110 PRINT "DARTMOUTH CONTROLS THE REBOUND." 1120 GOTO 1145 1130 PRINT "REBOUND TO ";O$ 1140 GOTO 3000 1145 IF RND(1)>.4 THEN 1158 1150 GOTO 1300 1158 IF D=6 THEN 5100 1160 PRINT "BALL PASSED BACK TO YOU. "; 1170 GOTO 430 1180 IF RND(1)>.9 THEN 1190 1185 PRINT "PLAYER FOULED, TWO SHOTS." 1187 GOSUB 4000 1188 GOTO 3000 1190 PRINT "BALL STOLEN. ";O$;"'S BALL." 1195 GOTO 3000 1200 IF RND(1)>.782*D/8 THEN 1250 1210 PRINT "SHOT IS BLOCKED. BALL CONTROLLED BY "; 1230 IF RND(1)>.5 THEN 1242 1235 PRINT "DARTMOUTH." 1240 GOTO 430 1242 PRINT O$;"." 1245 GOTO 3000 1250 IF RND(1)>.843*D/8 THEN 1270 1255 PRINT "SHOOTER IS FOULED. TWO SHOTS." 1260 GOSUB 4000 1265 GOTO 3000 1270 PRINT "CHARGING FOUL. DARTMOUTH LOSES BALL." 1280 GOTO 3000 1300 T=T+1 1301 IF T=50 THEN 8000 1302 IF T=92 THEN 1304 1303 GOTO 1305 1304 GOSUB 600 1305 IF Z=0 THEN 2010 1310 IF Z>3 THEN 1700 1320 PRINT "LAY UP." 1330 IF 7/D*RND(1)>.4 THEN 1360 1340 PRINT "SHOT IS GOOD. TWO POINTS." 1345 GOSUB 7000 1355 GOTO 3000 1360 IF 7/D*RND(1)>.7 THEN 1500 1370 PRINT "SHOT IS OFF THE RIM." 1380 IF RND(1)>2/3 THEN 1415 1390 PRINT O$;" CONTROLS THE REBOUND." 1400 GOTO 3000 1415 PRINT "DARTMOUTH CONTROLS THE REBOUND." 1420 IF RND(1)>.4 THEN 1440 1430 GOTO 1300 1440 PRINT "BALL PASSED BACK TO YOU."; 1450 GOTO 430 1500 IF 7/D*RND(1)>.875 THEN 1600 1510 PRINT "SHOOTER FOULED. TWO SHOTS." 1520 GOSUB 4000 1530 GOTO 3000 1600 IF 7/D*RND(1)>.925 THEN 1630 1610 PRINT "SHOT BLOCKED. ";O$;"'S BALL." 1620 GOTO 3000 1630 PRINT "CHARGING FOUL. DARTMOUTH LOSES THE BALL." 1640 GOTO 3000 1700 PRINT "SET SHOT." 1710 GOTO 1330 2010 INPUT "YOUR NEW DEFENSIVE ALLIGNMENT IS";D 2030 IF D<6 THEN 2010 2040 GOTO 425 3000 P=1 3005 T=T+1 3008 IF T=50 THEN 8000 3012 GOTO 3018 3015 GOSUB 600 3018 PRINT 3020 Z1=10/4*RND(1)+1 3030 IF Z1>2 THEN 3500 3040 PRINT "JUMP SHOT." 3050 IF 8/D*RND(1)>.35 THEN 3100 3060 PRINT "SHOT IS GOOD." 3080 GOSUB 6000 3090 GOTO 425 3100 IF 8/D*RND(1)>.75 THEN 3200 3105 PRINT "SHOT IS OFF RIM." 3110 IF D/6*RND(1)>.5 THEN 3150 3120 PRINT "DARTMOUTH CONTROLS THE REBOUND." 3130 GOTO 425 3150 PRINT O$;" CONTROLS THE REBOUND." 3160 IF D=6 THEN 5000 3165 IF RND(1)>.5 THEN 3175 3168 PRINT "PASS BACK TO ";O$;" GUARD." 3170 GOTO 3000 3175 GOTO 3500 3200 IF 8/D*RND(1)>.9 THEN 3310 3210 PRINT "PLAYER FOULED. TWO SHOTS." 3220 GOSUB 4000 3230 GOTO 425 3310 PRINT "OFFENSIVE FOUL. DARTMOUTH'S BALL." 3320 GOTO 425 3500 IF Z1>3 THEN 3800 3510 PRINT "LAY UP." 3520 IF 7/D*RND(1)>.413 THEN 3600 3530 PRINT "SHOT IS GOOD." 3540 GOSUB 6000 3550 GOTO 425 3600 PRINT "SHOT IS MISSED." 3610 GOTO 3110 3800 PRINT "SET SHOT." 3810 GOTO 3520 4000 REM FOUL SHOOTING 4010 IF RND(1)>.49 THEN 4050 4020 PRINT "SHOOTER MAKES BOTH SHOTS." 4030 S(1-P)=S(1-P)+2 4040 GOSUB 6010 4041 RETURN 4050 IF RND(1)>.75 THEN 4100 4060 PRINT "SHOOTER MAKES ONE SHOT AND MISSES ONE." 4070 S(1-P)=S(1-P)+1 4080 GOTO 4040 4100 PRINT "BOTH SHOTS MISSED." 4110 GOTO 4040 5000 IF RND(1)>.75 THEN 5010 5005 GOTO 3165 5010 PRINT "BALL STOLEN. EASY LAY UP FOR DARTMOUTH." 5015 GOSUB 7000 5030 GOTO 3000 5100 IF RND(1)>.6 THEN 5120 5110 GOTO 1160 5120 PRINT "PASS STOLEN BY ";O$;" EASY LAYUP." 5130 GOSUB 6000 5140 GOTO 425 6000 S(0)=S(0)+2 6010 PRINT "SCORE: ";S(1);"TO";S(0) 6020 RETURN 7000 S(1)=S(1)+2 7010 GOSUB 6010 7020 RETURN 8000 PRINT:PRINT " ***** END OF FIRST HALF *****":PRINT 8010 PRINT "SCORE: DARTMOUTH:";S(1);" ";O$;":";S(0) 8015 PRINT 8016 PRINT 8020 GOTO 370 9999 END ================================================ FILE: 00_Alternate_Languages/08_Batnum/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/08_Batnum/MiniScript/batnum.ms ================================================ 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!" ================================================ FILE: 00_Alternate_Languages/08_Batnum/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/08_Batnum/batnum.bas ================================================ 10 PRINT TAB(33);"BATNUM" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 30 PRINT:PRINT:PRINT 110 PRINT "THIS PROGRAM IS A 'BATTLE OF NUMBERS' GAME, WHERE THE" 120 PRINT "COMPUTER IS YOUR OPPONENT." 130 PRINT 140 PRINT "THE GAME STARTS WITH AN ASSUMED PILE OF OBJECTS. YOU" 150 PRINT "AND YOUR OPPONENT ALTERNATELY REMOVE OBJECTS FROM THE PILE." 160 PRINT "WINNING IS DEFINED IN ADVANCE AS TAKING THE LAST OBJECT OR" 170 PRINT "NOT. YOU CAN ALSO SPECIFY SOME OTHER BEGINNING CONDITIONS." 180 PRINT "DON'T USE ZERO, HOWEVER, IN PLAYING THE GAME." 190 PRINT "ENTER A NEGATIVE NUMBER FOR NEW PILE SIZE TO STOP PLAYING." 200 PRINT 210 GOTO 330 220 FOR I=1 TO 10 230 PRINT 240 NEXT I 330 INPUT "ENTER PILE SIZE";N 350 IF N>=1 THEN 370 360 GOTO 330 370 IF N<>INT(N) THEN 220 380 IF N<1 THEN 220 390 INPUT "ENTER WIN OPTION - 1 TO TAKE LAST, 2 TO AVOID LAST: ";M 410 IF M=1 THEN 430 420 IF M<>2 THEN 390 430 INPUT "ENTER MIN AND MAX ";A,B 450 IF A>B THEN 430 460 IF A<1 THEN 430 470 IF A<>INT(A) THEN 430 480 IF B<>INT(B) THEN 430 490 INPUT "ENTER START OPTION - 1 COMPUTER FIRST, 2 YOU FIRST ";S 500 PRINT:PRINT 510 IF S=1 THEN 530 520 IF S<>2 THEN 490 530 C=A+B 540 IF S=2 THEN 570 550 GOSUB 600 560 IF W=1 THEN 220 570 GOSUB 810 580 IF W=1 THEN 220 590 GOTO 550 600 Q=N 610 IF M=1 THEN 630 620 Q=Q-1 630 IF M=1 THEN 680 640 IF N>A THEN 720 650 W=1 660 PRINT "COMPUTER TAKES";N;"AND LOSES." 670 RETURN 680 IF N>B THEN 720 690 W=1 700 PRINT "COMPUTER TAKES";N;"AND WINS." 710 RETURN 720 P=Q-C*INT(Q/C) 730 IF P>=A THEN 750 740 P=A 750 IF P<=B THEN 770 760 P=B 770 N=N-P 780 PRINT "COMPUTER TAKES";P;"AND LEAVES";N 790 W=0 800 RETURN 810 PRINT:PRINT "YOUR MOVE "; 820 INPUT P 830 IF P<>0 THEN 870 840 PRINT "I TOLD YOU NOT TO USE ZERO! COMPUTER WINS BY FORFEIT." 850 W=1 860 RETURN 870 IF P<>INT(P) THEN 920 880 IF P>=A THEN 910 890 IF P=N THEN 960 900 GOTO 920 910 IF P<=B THEN 940 920 PRINT "ILLEGAL MOVE, REENTER IT "; 930 GOTO 820 940 N=N-P 950 IF N<>0 THEN 1030 960 IF M=1 THEN 1000 970 PRINT "TOUGH LUCK, YOU LOSE." 980 W=1 990 RETURN 1000 PRINT "CONGRATULATIONS, YOU WIN." 1010 W=1 1020 RETURN 1030 IF N>=0 THEN 1060 1040 N=N+P 1050 GOTO 920 1060 W=0 1070 RETURN 1080 END ================================================ FILE: 00_Alternate_Languages/08_Batnum/go/main.go ================================================ package main import ( "bufio" "fmt" "os" "strconv" "strings" ) type StartOption int8 const ( StartUndefined StartOption = iota ComputerFirst PlayerFirst ) type WinOption int8 const ( WinUndefined WinOption = iota TakeLast AvoidLast ) type GameOptions struct { pileSize int winOption WinOption startOption StartOption minSelect int maxSelect int } func NewOptions() *GameOptions { g := GameOptions{} g.pileSize = getPileSize() if g.pileSize < 0 { return &g } g.winOption = getWinOption() g.minSelect, g.maxSelect = getMinMax() g.startOption = getStartOption() return &g } func getPileSize() int { ps := 0 var err error scanner := bufio.NewScanner(os.Stdin) for { fmt.Println("Enter Pile Size ") scanner.Scan() ps, err = strconv.Atoi(scanner.Text()) if err == nil { break } } return ps } func getWinOption() WinOption { scanner := bufio.NewScanner(os.Stdin) for { fmt.Println("ENTER WIN OPTION - 1 TO TAKE LAST, 2 TO AVOID LAST:") scanner.Scan() w, err := strconv.Atoi(scanner.Text()) if err == nil && (w == 1 || w == 2) { return WinOption(w) } } } func getStartOption() StartOption { scanner := bufio.NewScanner(os.Stdin) for { fmt.Println("ENTER START OPTION - 1 COMPUTER FIRST, 2 YOU FIRST ") scanner.Scan() s, err := strconv.Atoi(scanner.Text()) if err == nil && (s == 1 || s == 2) { return StartOption(s) } } } func getMinMax() (int, int) { minSelect := 0 maxSelect := 0 var minErr error var maxErr error scanner := bufio.NewScanner(os.Stdin) for { fmt.Println("ENTER MIN AND MAX ") scanner.Scan() enteredValues := scanner.Text() vals := strings.Split(enteredValues, " ") minSelect, minErr = strconv.Atoi(vals[0]) maxSelect, maxErr = strconv.Atoi(vals[1]) if (minErr == nil) && (maxErr == nil) && (minSelect > 0) && (maxSelect > 0) && (maxSelect > minSelect) { return minSelect, maxSelect } } } // This handles the player's turn - asking the player how many objects // to take and doing some basic validation around that input. Then it // checks for any win conditions. // Returns a boolean indicating whether the game is over and the new pile_size. func playerMove(pile, min, max int, win WinOption) (bool, int) { scanner := bufio.NewScanner(os.Stdin) done := false for !done { fmt.Println("YOUR MOVE") scanner.Scan() m, err := strconv.Atoi(scanner.Text()) if err != nil { continue } if m == 0 { fmt.Println("I TOLD YOU NOT TO USE ZERO! COMPUTER WINS BY FORFEIT.") return true, pile } if m > max || m < min { fmt.Println("ILLEGAL MOVE, REENTER IT") continue } pile -= m done = true if pile <= 0 { if win == AvoidLast { fmt.Println("TOUGH LUCK, YOU LOSE.") } else { fmt.Println("CONGRATULATIONS, YOU WIN.") } return true, pile } } return false, pile } // This handles the logic to determine how many objects the computer // will select on its turn. func computerPick(pile, min, max int, win WinOption) int { var q int if win == AvoidLast { q = pile - 1 } else { q = pile } c := min + max pick := q - (c * int(q/c)) if pick < min { pick = min } else if pick > max { pick = max } return pick } // This handles the computer's turn - first checking for the various // win/lose conditions and then calculating how many objects // the computer will take. // Returns a boolean indicating whether the game is over and the new pile_size. func computerMove(pile, min, max int, win WinOption) (bool, int) { // first check for end-game conditions if win == TakeLast && pile <= max { fmt.Printf("COMPUTER TAKES %d AND WINS\n", pile) return true, pile } if win == AvoidLast && pile <= min { fmt.Printf("COMPUTER TAKES %d AND LOSES\n", pile) return true, pile } // otherwise determine the computer's selection selection := computerPick(pile, min, max, win) pile -= selection fmt.Printf("COMPUTER TAKES %d AND LEAVES %d\n", selection, pile) return false, pile } // This is the main game loop - repeating each turn until one // of the win/lose conditions is met. func play(pile, min, max int, start StartOption, win WinOption) { gameOver := false playersTurn := (start == PlayerFirst) for !gameOver { if playersTurn { gameOver, pile = playerMove(pile, min, max, win) playersTurn = false if gameOver { return } } if !playersTurn { gameOver, pile = computerMove(pile, min, max, win) playersTurn = true } } } // Print out the introduction and rules of the game func printIntro() { fmt.Printf("%33s%s\n", " ", "BATNUM") fmt.Printf("%15s%s\n", " ", "CREATIVE COMPUTING MORRISSTOWN, NEW JERSEY") fmt.Printf("\n\n\n") fmt.Println("THIS PROGRAM IS A 'BATTLE OF NUMBERS' GAME, WHERE THE") fmt.Println("COMPUTER IS YOUR OPPONENT.") fmt.Println() fmt.Println("THE GAME STARTS WITH AN ASSUMED PILE OF OBJECTS. YOU") fmt.Println("AND YOUR OPPONENT ALTERNATELY REMOVE OBJECTS FROM THE PILE.") fmt.Println("WINNING IS DEFINED IN ADVANCE AS TAKING THE LAST OBJECT OR") fmt.Println("NOT. YOU CAN ALSO SPECIFY SOME OTHER BEGINNING CONDITIONS.") fmt.Println("DON'T USE ZERO, HOWEVER, IN PLAYING THE GAME.") fmt.Println("ENTER A NEGATIVE NUMBER FOR NEW PILE SIZE TO STOP PLAYING.") fmt.Println() } func main() { for { printIntro() g := NewOptions() if g.pileSize < 0 { return } play(g.pileSize, g.minSelect, g.maxSelect, g.startOption, g.winOption) } } ================================================ FILE: 00_Alternate_Languages/09_Battle/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/09_Battle/MiniScript/battle.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/09_Battle/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/09_Battle/battle.bas ================================================ 5 PRINT TAB(33);"BATTLE" 7 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 10 REM -- BATTLE WRITTEN BY RAY WESTERGARD 10/70 20 REM COPYRIGHT 1971 BY THE REGENTS OF THE UNIV. OF CALIF. 30 REM PRODUCED AT THE LAWRENCE HALL OF SCIENCE, BERKELEY 40 DIM F(6,6),H(6,6),A(4),B(4),C(6),L(3) 50 FOR X=1 TO 6 51 FOR Y=1 TO 6 52 F(X,Y)=0 53 NEXT Y 54 NEXT X 60 FOR I=1 TO 3 70 N=4-I 80 FOR J=1 TO 2 90 A=INT(6*RND(1)+1) 100 B=INT(6*RND(1)+1) 110 D=INT(4*RND(1)+1) 120 IF F(A,B)>0 THEN 90 130 M=0 140 ON D GOTO 150,340,550,740 150 B(1)=B 160 B(2)=7:B(3)=7 170 FOR K=1 TO N 180 IF M>1 THEN 240 190 IF B(K)=6 THEN 230 200 IF F(A,B(K)+1)>0 THEN 230 210 B(K+1)=B(K)+1 220 GOTO 280 230 M=2 240 IF B(1)<B(2) AND B(1)<B(3) THEN Z=B(1) 242 IF B(2)<B(1) AND B(2)<B(3) THEN Z=B(2) 244 IF B(3)<B(1) AND B(3)<B(2) THEN Z=B(3) 250 IF Z=1 THEN 90 260 IF F(A,Z-1)>0 THEN 90 270 B(K+1)=Z-1 280 NEXT K 290 F(A,B)=9-2*I-J 300 FOR K=1 TO N 310 F(A,B(K+1))=F(A,B) 320 NEXT K 330 GOTO 990 340 A(1)=A 350 B(1)=B 360 A(2)=0:A(3)=0:B(2)=0:B(3)=0 370 FOR K=1 TO N 380 IF M>1 THEN 460 390 IF A(K)=1 OR B(K)=1 THEN 450 400 IF F(A(K)-1,B(K)-1)>0 THEN 450 410 IF F(A(K)-1,B(K))>0 AND F(A(K)-1,B(K))=F(A(K),B(K)-1) THEN 450 420 A(K+1)=A(K)-1 430 B(K+1)=B(K)-1 440 GOTO 530 450 M=2 460 IF A(1)>A(2) AND A(1)>A(3) THEN Z1=A(1) 462 IF A(2)>A(1) AND A(2)>A(3) THEN Z1=A(2) 464 IF A(3)>A(1) AND A(3)>A(2) THEN Z1=A(3) 470 IF B(1)>B(2) AND B(1)>B(3) THEN Z2=B(1) 474 IF B(2)>B(1) AND B(2)>B(3) THEN Z2=B(2) 476 IF B(3)>B(1) AND B(3)>B(2) THEN Z2=B(3) 480 IF Z1=6 OR Z2=6 THEN 90 490 IF F(Z1+1,Z2+1)>0 THEN 90 500 IF F(Z1,Z2+1)>0 AND F(Z1,Z2+1)=F(Z1+1,Z2) THEN 90 510 A(K+1)=Z1+1 520 B(K+1)=Z2+1 530 NEXT K 540 GOTO 950 550 A(1)=A 560 A(2)=7:A(3)=7 570 FOR K=1 TO N 580 IF M>1 THEN 640 590 IF A(K)=6 THEN 630 600 IF F(A(K)+1,B)>0 THEN 630 610 A(K+1)=A(K)+1 620 GOTO 680 630 M=2 640 IF A(1)<A(2) AND A(1)<A(3) THEN Z=A(1) 642 IF A(2)<A(1) AND A(2)<A(3) THEN Z=A(2) 644 IF A(3)<A(1) AND A(3)<A(2) THEN Z=A(3) 650 IF Z=1 THEN 90 660 IF F(Z-1,B)>0 THEN 90 670 A(K+1)=Z-1 680 NEXT K 690 F(A,B)=9-2*I-J 700 FOR K=1 TO N 710 F(A(K+1),B)=F(A,B) 720 NEXT K 730 GOTO 990 740 A(1)=A 750 B(1)=B 760 A(2)=7:A(3)=7 770 B(2)=0:B(3)=0 780 FOR K=1 TO N 790 IF M>1 THEN 870 800 IF A(K)=6 OR B(K)=1 THEN 860 810 IF F(A(K)+1,B(K)-1)>0 THEN 860 820 IF F(A(K)+1,B(K))>0 AND F(A(K)+1,B(K))=F(A(K),B(K)-1) THEN 860 830 A(K+1)=A(K)+1 840 B(K+1)=B(K)-1 850 GOTO 940 860 M=2 870 IF A(1)<A(2) AND A(1)<A(3) THEN Z1=A(1) 872 IF A(2)<A(1) AND A(2)<A(3) THEN Z1=A(2) 874 IF A(3)<A(1) AND A(3)<A(2) THEN Z1=A(3) 880 IF B(1)>B(2) AND B(1)>B(3) THEN Z2=B(1) 882 IF B(2)>B(1) AND B(2)>B(3) THEN Z2=B(2) 884 IF B(3)>B(1) AND B(3)>B(2) THEN Z2=B(3) 890 IF Z1=1 OR Z2=6 THEN 90 900 IF F(Z1-1,Z2+1)>0 THEN 90 910 IF F(Z1,Z2+1)>0 AND F(Z1,Z2+1)=F(Z1-1,Z2) THEN 90 920 A(K+1)=Z1-1 930 B(K+1)=Z2+1 940 NEXT K 950 F(A,B)=9-2*I-J 960 FOR K=1 TO N 970 F(A(K+1),B(K+1))=F(A,B) 980 NEXT K 990 NEXT J 1000 NEXT I 1010 PRINT 1020 PRINT "THE FOLLOWING CODE OF THE BAD GUYS' FLEET DISPOSITION" 1030 PRINT "HAS BEEN CAPTURED BUT NOT DECODED:" 1040 PRINT 1050 FOR I=1 TO 6 1051 FOR J=1 TO 6 1052 H(I,J)=F(J,I) 1053 NEXT J 1054 NEXT I 1060 FOR I=1 TO 6 1061 FOR J=1 TO 6 1062 PRINT H(I,J); 1063 NEXT J 1064 PRINT 1065 NEXT I 1070 PRINT 1080 PRINT "DE-CODE IT AND USE IT IF YOU CAN" 1090 PRINT "BUT KEEP THE DE-CODING METHOD A SECRET." 1100 PRINT 1110 FOR I=1 TO 6 1111 FOR J=1 TO 6 1112 H(I,J)=0 1113 NEXT J 1114 NEXT I 1120 FOR I=1 TO 3 1121 L(I)=0 1122 NEXT I 1130 C(1)=2:C(2)=2 1140 C(3)=1:C(4)=1 1150 C(5)=0:C(6)=0 1160 S=0:H=0 1170 PRINT "START GAME" 1180 INPUT X,Y 1190 IF X<1 OR X>6 OR INT(X)<>ABS(X) THEN 1210 1200 IF Y>0 AND Y<7 AND INT(Y)=ABS(Y) THEN 1230 1210 PRINT "INVALID INPUT. TRY AGAIN." 1220 GOTO 1180 1230 R=7-Y 1240 C=X 1250 IF F(R,C)>0 THEN 1290 1260 S=S+1 1270 PRINT "SPLASH! TRY AGAIN." 1280 GOTO 1180 1290 IF C(F(R,C))<4 THEN 1340 1300 PRINT "THERE USED TO BE A SHIP AT THAT POINT, BUT YOU SUNK IT." 1310 PRINT "SPLASH! TRY AGAIN." 1320 S=S+1 1330 GOTO 1180 1340 IF H(R,C)>0 THEN 1420 1350 H=H+1 1360 H(R,C)=F(R,C) 1370 PRINT "A DIRECT HIT ON SHIP NUMBER";F(R,C) 1380 C(F(R,C))=C(F(R,C))+1 1390 IF C(F(R,C))>=4 THEN 1470 1400 PRINT "TRY AGAIN." 1410 GOTO 1180 1420 PRINT "YOU ALREADY PUT A HOLE IN SHIP NUMBER";F(R,C); 1430 PRINT "AT THAT POINT." 1440 PRINT "SPLASH! TRY AGAIN." 1450 S=S+1 1460 GOTO 1180 1470 L((INT(F(R,C)-1)/2)+1)=L((INT(F(R,C)-1)/2)+1)+1 1480 PRINT "AND YOU SUNK IT. HURRAH FOR THE GOOD GUYS." 1490 PRINT "SO FAR, THE BAD GUYS HAVE LOST" 1500 PRINT L(1);"DESTROYER(S),";L(2);"CRUISER(S), AND"; 1510 PRINT L(3);"AIRCRAFT CARRIER(S)." 1520 PRINT "YOUR CURRENT SPLASH/HIT RATIO IS";S/H 1530 IF (L(1)+L(2)+L(3))<6 THEN 1180 1540 PRINT 1550 PRINT "YOU HAVE TOTALLY WIPED OUT THE BAD GUYS' FLEET" 1560 PRINT "WITH A FINAL SPLASH/HIT RATIO OF";S/H 1570 IF S/H>0 THEN 1590 1580 PRINT "CONGRATULATIONS -- A DIRECT HIT EVERY TIME." 1590 PRINT 1600 PRINT "****************************" 1610 PRINT 1620 GOTO 50 1630 END ================================================ FILE: 00_Alternate_Languages/09_Battle/go/main.go ================================================ package main import ( "bufio" "fmt" "math/rand" "os" "strconv" "strings" "time" ) const ( SEA_WIDTH = 6 DESTROYER_LENGTH = 2 CRUISER_LENGTH = 3 CARRIER_LENGTH = 4 ) type Point [2]int type Vector Point type Sea [][]int func NewSea() Sea { s := make(Sea, 6) for r := 0; r < SEA_WIDTH; r++ { c := make([]int, 6) s[r] = c } return s } func getRandomVector() Vector { v := Vector{} for { v[0] = rand.Intn(3) - 1 v[1] = rand.Intn(3) - 1 if !(v[0] == 0 && v[1] == 0) { break } } return v } func addVector(p Point, v Vector) Point { newPoint := Point{} newPoint[0] = p[0] + v[0] newPoint[1] = p[1] + v[1] return newPoint } func isWithinSea(p Point, s Sea) bool { return (1 <= p[0] && p[0] <= len(s)) && (1 <= p[1] && p[1] <= len(s)) } func valueAt(p Point, s Sea) int { return s[p[1]-1][p[0]-1] } func reportInputError() { fmt.Printf("INVALID. SPECIFY TWO NUMBERS FROM 1 TO %d, SEPARATED BY A COMMA.\n", SEA_WIDTH) } func getNextTarget(s Sea) Point { scanner := bufio.NewScanner(os.Stdin) for { fmt.Println("\n?") scanner.Scan() vals := strings.Split(scanner.Text(), ",") if len(vals) != 2 { reportInputError() continue } x, xErr := strconv.Atoi(strings.TrimSpace(vals[0])) y, yErr := strconv.Atoi(strings.TrimSpace(vals[1])) if (len(vals) != 2) || (xErr != nil) || (yErr != nil) { reportInputError() continue } p := Point{} p[0] = x p[1] = y if isWithinSea(p, s) { return p } } } func setValueAt(value int, p Point, s Sea) { s[p[1]-1][p[0]-1] = value } func hasShip(s Sea, code int) bool { hasShip := false for r := 0; r < SEA_WIDTH; r++ { for c := 0; c < SEA_WIDTH; c++ { if s[r][c] == code { hasShip = true break } } } return hasShip } func countSunk(s Sea, codes []int) int { sunk := 0 for _, c := range codes { if !hasShip(s, c) { sunk += 1 } } return sunk } func placeShip(s Sea, size, code int) { for { start := Point{} start[0] = rand.Intn(SEA_WIDTH) + 1 start[1] = rand.Intn(SEA_WIDTH) + 1 vector := getRandomVector() point := start points := []Point{} for i := 0; i < size; i++ { point = addVector(point, vector) points = append(points, point) } clearPosition := true for _, p := range points { if !isWithinSea(p, s) { clearPosition = false break } if valueAt(p, s) > 0 { clearPosition = false break } } if !clearPosition { continue } for _, p := range points { setValueAt(code, p, s) } break } } func setupShips(s Sea) { placeShip(s, DESTROYER_LENGTH, 1) placeShip(s, DESTROYER_LENGTH, 2) placeShip(s, CRUISER_LENGTH, 3) placeShip(s, CRUISER_LENGTH, 4) placeShip(s, CARRIER_LENGTH, 5) placeShip(s, CARRIER_LENGTH, 6) } func printIntro() { fmt.Println(" BATTLE") fmt.Println("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") fmt.Println() fmt.Println("THE FOLLOWING CODE OF THE BAD GUYS' FLEET DISPOSITION") fmt.Println("HAS BEEN CAPTURED BUT NOT DECODED: ") fmt.Println() } func printInstructions() { fmt.Println() fmt.Println() fmt.Println("DE-CODE IT AND USE IT IF YOU CAN") fmt.Println("BUT KEEP THE DE-CODING METHOD A SECRET.") fmt.Println() fmt.Println("START GAME") } func printEncodedSea(s Sea) { for x := 0; x < SEA_WIDTH; x++ { fmt.Println() for y := SEA_WIDTH - 1; y > -1; y-- { fmt.Printf(" %d", s[y][x]) } } fmt.Println() } func wipeout(s Sea) bool { for c := 1; c <= 7; c++ { if hasShip(s, c) { return false } } return true } func main() { rand.Seed(time.Now().UnixNano()) s := NewSea() setupShips(s) printIntro() printEncodedSea(s) printInstructions() splashes := 0 hits := 0 for { target := getNextTarget(s) targetValue := valueAt(target, s) if targetValue < 0 { fmt.Printf("YOU ALREADY PUT A HOLE IN SHIP NUMBER %d AT THAT POINT.\n", targetValue) } if targetValue <= 0 { fmt.Println("SPLASH! TRY AGAIN.") splashes += 1 continue } fmt.Printf("A DIRECT HIT ON SHIP NUMBER %d\n", targetValue) hits += 1 setValueAt(targetValue*-1, target, s) if !hasShip(s, targetValue) { fmt.Println("AND YOU SUNK IT. HURRAH FOR THE GOOD GUYS.") fmt.Println("SO FAR, THE BAD GUYS HAVE LOST") fmt.Printf("%d DESTROYER(S), %d CRUISER(S), AND %d AIRCRAFT CARRIER(S).\n", countSunk(s, []int{1, 2}), countSunk(s, []int{3, 4}), countSunk(s, []int{5, 6})) } if !wipeout(s) { fmt.Printf("YOUR CURRENT SPLASH/HIT RATIO IS %2f\n", float32(splashes)/float32(hits)) continue } fmt.Printf("YOU HAVE TOTALLY WIPED OUT THE BAD GUYS' FLEET WITH A FINAL SPLASH/HIT RATIO OF %2f\n", float32(splashes)/float32(hits)) if splashes == 0 { fmt.Println("CONGRATULATIONS -- A DIRECT HIT EVERY TIME.") } fmt.Println("\n****************************") break } } ================================================ FILE: 00_Alternate_Languages/10_Blackjack/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/10_Blackjack/MiniScript/blackjack.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/10_Blackjack/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/10_Blackjack/blackjack.bas ================================================ 2 PRINT TAB(31);"BLACK JACK" 4 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 6 PRINT:PRINT:PRINT 10 DEF FNA(Q)=Q+11*(Q>=22) 20 DIM P(15,12),Q(15),C(52),D(52),T(8),S(7),B(15) 30 DIM R(15) 40 REM--P(I,J) IS THE JTH CARD IN HAND I, Q(I) IS TOTAL OF HAND I 50 REM--C IS THE DECK BEING DEALT FROM, D IS THE DISCARD PILE, 60 REM--T(I) IS THE TOTAL FOR PLAYER I, S(I) IS THE TOTAL THIS HAND FOR 70 REM--PLAYER I, B(I) IS TH BET FOR HAND I 80 REM--R(I) IS THE LENGTH OF P(I,*) 90 GOTO 1500 100 REM--SUBROUTINE TO GET A CARD. RESULT IS PUT IN X. 110 IF C<51 THEN 230 120 PRINT "RESHUFFLING" 130 FOR D=D TO 1 STEP -1 140 C=C-1 150 C(C)=D(D) 160 NEXT D 170 FOR C1=52 TO C STEP -1 180 C2=INT(RND(1)*(C1-C+1))+C 190 C3=C(C2) 200 C(C2)=C(C1) 210 C(C1)=C3 220 NEXT C1 230 X=C(C) 240 C=C+1 250 RETURN 300 REM--SUBROUTINE TO EVALUATE HAND I. TOTAL IS PUT INTO 310 REM--Q(I). TOTALS HAVE THE FOLLOWING MEANING: 320 REM-- 2-10...HARD 2-10 330 REM-- 11-21...SOFT 11-21 340 REM-- 22-32...HARD 11-21 350 REM-- 33+....BUSTED 360 Q=0 370 FOR Q2=1 TO R(I) 380 X=P(I,Q2) 390 GOSUB 500 400 NEXT Q2 410 Q(I)=Q 420 RETURN 500 REM--SUBROUTINE TO ADD CARD X TO TOTAL Q. 510 X1=X: IF X1>10 THEN X1=10: REM SAME AS X1=10 MIN X 520 Q1=Q+X1 530 IF Q>=11 THEN 590 540 IF X>1 THEN 570 550 Q=Q+11 560 RETURN 570 Q=Q1-11*(Q1>=11) 580 RETURN 590 Q=Q1-(Q<=21 AND Q1>21) 600 IF Q<33 THEN 620 610 Q=-1 620 RETURN 700 REM--CARD PRINTING SUBROUTINE 710 REM D$ DEFINED ELSEWHERE 720 PRINT MID$(D$,3*X-2,3); 730 PRINT " "; 740 RETURN 750 REM--ALTERNATIVE PRINTING ROUTINE 760 PRINT " ";MID$(D$,3*X-1,2); 770 PRINT " "; 780 RETURN 800 REM--SUBROUTINE TO PLAY OUT A HAND. 810 REM--NO SPLITTING OR BLACKJACKS ALLOWED 820 H1=5 830 GOSUB 1410 840 H1=3 850 ON H GOTO 950,930 860 GOSUB 100 870 B(I)=B(I)*2 880 PRINT "RECEIVED A"; 890 GOSUB 700 900 GOSUB 1100 910 IF Q>0 THEN GOSUB 1300 920 RETURN 930 GOSUB 1320 940 RETURN 950 GOSUB 100 960 PRINT "RECEIVED A"; 970 GOSUB 700 980 GOSUB 1100 990 IF Q<0 THEN 940 1000 PRINT "HIT"; 1010 GOTO 830 1100 REM--SUBROUTINE TO ADD A CARD TO ROW I 1110 R(I)=R(I)+1 1120 P(I,R(I))=X 1130 Q=Q(I) 1140 GOSUB 500 1150 Q(I)=Q 1160 IF Q>=0 THEN 1190 1170 PRINT "...BUSTED" 1180 GOSUB 1200 1190 RETURN 1200 REM--SUBROUTINE TO DISCARD ROW I 1210 IF R(I)<>0 THEN 1230 1220 RETURN 1230 D=D+1 1240 D(D)=P(I,R(I)) 1250 R(I)=R(I)-1 1260 GOTO 1210 1300 REM--PRINTS TOTAL OF HAND I 1310 PRINT 1320 AA=Q(I): GOSUB 3400 1325 PRINT "TOTAL IS";AA 1330 RETURN 1400 REM--SUBROUTINE TO READ REPLY 1410 REM I$ DEFINED ELSEWHERE 1420 INPUT H$: H$=LEFT$(H$,1) 1430 FOR H=1 TO H1 STEP 2 1440 IF H$=MID$(I$,H,1) THEN 1480 1450 NEXT H 1460 PRINT "TYPE ";MID$(I$,1,H1-1);" OR ";MID$(I$,H1,2);" PLEASE"; 1470 GOTO 1420 1480 H=(H+1)/2 1490 RETURN 1500 REM--PROGRAM STARTS HERE 1510 REM--INITIALIZE 1520 D$="N A 2 3 4 5 6 7N 8 9 10 J Q K" 1530 I$="H,S,D,/," 1540 FOR I=1 TO 13 1550 FOR J=4*I-3 TO 4*I 1560 D(J)=I 1570 NEXT J 1580 NEXT I 1590 D=52 1600 C=53 1610 PRINT "DO YOU WANT INSTRUCTIONS"; 1620 INPUT H$ 1630 IF LEFT$(H$,1)="N" OR LEFT$(H$,1)="n" THEN 1760 1640 PRINT "THIS IS THE GAME OF 21. AS MANY AS 7 PLAYERS MAY PLAY THE" 1650 PRINT "GAME. ON EACH DEAL, BETS WILL BE ASKED FOR, AND THE" 1660 PRINT "PLAYERS' BETS SHOULD BE TYPED IN. THE CARDS WILL THEN BE" 1670 PRINT "DEALT, AND EACH PLAYER IN TURN PLAYS HIS HAND. THE" 1680 PRINT "FIRST RESPONSE SHOULD BE EITHER 'D', INDICATING THAT THE" 1690 PRINT "PLAYER IS DOUBLING DOWN, 'S', INDICATING THAT HE IS" 1700 PRINT "STANDING, 'H', INDICATING HE WANTS ANOTHER CARD, OR '/'," 1710 PRINT "INDICATING THAT HE WANTS TO SPLIT HIS CARDS. AFTER THE" 1720 PRINT "INITIAL RESPONSE, ALL FURTHER RESPONSES SHOULD BE 'S' OR" 1730 PRINT "'H', UNLESS THE CARDS WERE SPLIT, IN WHICH CASE DOUBLING" 1740 PRINT "DOWN IS AGAIN PERMITTED. IN ORDER TO COLLECT FOR" 1750 PRINT "BLACKJACK, THE INITIAL RESPONSE SHOULD BE 'S'." 1760 PRINT "NUMBER OF PLAYERS"; 1770 INPUT N 1775 PRINT 1780 IF N<1 OR N>7 OR N>INT(N) THEN 1760 1790 FOR I=1 TO 8: T(I)=0: NEXT I 1800 D1=N+1 1810 IF 2*D1+C>=52 THEN GOSUB 120 1820 IF C=2 THEN C=C-1 1830 FOR I=1 TO N: Z(I)=0: NEXT I 1840 FOR I=1 TO 15: B(I)=0: NEXT I 1850 FOR I=1 TO 15: Q(I)=0: NEXT I 1860 FOR I=1 TO 7: S(I)=0: NEXT I 1870 FOR I=1 TO 15: R(I)=0: NEXT I 1880 PRINT "BETS:" 1890 FOR I=1 TO N: PRINT "#";I;: INPUT Z(I): NEXT I 1900 FOR I=1 TO N 1910 IF Z(I)<=0 OR Z(I)>500 THEN 1880 1920 B(I)=Z(I) 1930 NEXT I 1940 PRINT "PLAYER"; 1950 FOR I=1 TO N 1960 PRINT I;" "; 1970 NEXT I 1980 PRINT "DEALER" 1990 FOR J=1 TO 2 2000 PRINT TAB(5); 2010 FOR I=1 TO D1 2020 GOSUB 100 2030 P(I,J)=X 2040 IF J=1 OR I<=N THEN GOSUB 750 2050 NEXT I 2060 PRINT 2070 NEXT J 2080 FOR I=1 TO D1 2090 R(I)=2 2100 NEXT I 2110 REM--TEST FOR INSURANCE 2120 IF P(D1,1)>1 THEN 2240 2130 PRINT "ANY INSURANCE"; 2140 INPUT H$ 2150 IF LEFT$(H$,1)<>"Y" THEN 2240 2160 PRINT "INSURANCE BETS" 2170 FOR I=1 TO N: PRINT "#";I;: INPUT Z(I): NEXT I 2180 FOR I=1 TO N 2190 IF Z(I)<0 OR Z(I)>B(I)/2 THEN 2160 2200 NEXT I 2210 FOR I=1 TO N 2220 S(I)=Z(I)*(3*(-(P(D1,2)>=10))-1) 2230 NEXT I 2240 REM--TEST FOR DEALER BLACKJACK 2250 L1=1: L2=1 2252 IF P(D1,1)=1 AND P(D1,2)>9 THEN L1=0: L2=0 2253 IF P(D1,2)=1 AND P(D1,1)>9 THEN L1=0: L2=0 2254 IF L1<>0 OR L2<>0 THEN 2320 2260 PRINT:PRINT "DEALER HAS A";MID$(D$,3*P(D1,2)-2,3);" IN THE HOLE "; 2270 PRINT "FOR BLACKJACK" 2280 FOR I=1 TO D1 2290 GOSUB 300 2300 NEXT I 2310 GOTO 3140 2320 REM--NO DEALER BLACKJACK 2330 IF P(D1,1)>1 AND P(D1,1)<10 THEN 2350 2340 PRINT:PRINT "NO DEALER BLACKJACK." 2350 REM--NOW PLAY THE HANDS 2360 FOR I=1 TO N 2370 PRINT "PLAYER";I; 2380 H1=7 2390 GOSUB 1410 2400 ON H GOTO 2550,2410,2510,2600 2410 REM--PLAYER WANTS TO STAND 2420 GOSUB 300 2430 IF Q(I)<>21 THEN 2490 2440 PRINT "BLACKJACK" 2450 S(I)=S(I)+1.5*B(I) 2460 B(I)=0 2470 GOSUB 1200 2480 GOTO 2900 2490 GOSUB 1320 2500 GOTO 2900 2510 REM--PLAYER WANTS TO DOUBLE DOWN 2520 GOSUB 300 2530 GOSUB 860 2540 GOTO 2900 2550 REM--PLAYER WANTS TO BE HIT 2560 GOSUB 300 2570 H1=3 2580 GOSUB 950 2590 GOTO 2900 2600 REM--PLAYER WANTS TO SPLIT 2610 L1=P(I,1): IF P(I,1)>10 THEN L1=10 2612 L2=P(I,2): IF P(I,2)>10 THEN L2=10 2614 IF L1=L2 THEN 2640 2620 PRINT "SPLITTING NOT ALLOWED." 2630 GOTO 2370 2640 REM--PLAY OUT SPLIT 2650 I1=I+D1 2660 R(I1)=2 2670 P(I1,1)=P(I,2) 2680 B(I+D1)=B(I) 2690 GOSUB 100 2700 PRINT "FIRST HAND RECEIVES A"; 2710 GOSUB 700 2720 P(I,2)=X 2730 GOSUB 300 2740 PRINT 2750 GOSUB 100 2760 PRINT "SECOND HAND RECEIVES A"; 2770 I=I1 2780 GOSUB 700 2790 P(I,2)=X 2800 GOSUB 300 2810 PRINT 2820 I=I1-D1 2830 IF P(I,1)=1 THEN 2900 2840 REM--NOW PLAY THE TWO HANDS 2850 PRINT "HAND";1-(I>D1); 2860 GOSUB 800 2870 I=I+D1 2880 IF I=I1 THEN 2850 2890 I=I1-D1 2900 NEXT I 2910 GOSUB 300 2920 REM--TEST FOR PLAYING DEALER'S HAND 2930 FOR I=1 TO N 2940 IF R(I)>0 OR R(I+D1)>0 THEN 3010 2950 NEXT I 2960 PRINT "DEALER HAD A"; 2970 X=P(D1,2) 2980 GOSUB 700 2990 PRINT " CONCEALED." 3000 GOTO 3140 3010 PRINT "DEALER HAS A";MID$(D$,3*P(D1,2)-2,3);" CONCEALED "; 3020 I=D1 3030 AA=Q(I): GOSUB 3400 3035 PRINT "FOR A TOTAL OF";AA 3040 IF AA>16 THEN 3130 3050 PRINT "DRAWS"; 3060 GOSUB 100 3070 GOSUB 750 3080 GOSUB 1100 3090 AA=Q: GOSUB 3400 3095 IF Q>0 AND AA<17 THEN 3060 3100 Q(I)=Q-(Q<0)/2 3110 IF Q<0 THEN 3140 3120 AA=Q: GOSUB 3400 3125 PRINT "---TOTAL IS";AA 3130 PRINT 3140 REM--TALLY THE RESULT 3150 REM 3160 Z$="LOSES PUSHES WINS " 3165 PRINT 3170 FOR I=1 TO N 3180 AA=Q(I): GOSUB 3400 3182 AB=Q(I+D1): GOSUB 3410 3184 AC=Q(D1): GOSUB 3420 3186 S(I)=S(I)+B(I)*SGN(AA-AC)+B(I+D1)*SGN(AB-AC) 3188 B(I+D1)=0 3200 PRINT "PLAYER";I; 3210 PRINT MID$(Z$,SGN(S(I))*6+7,6);" "; 3220 IF S(I)<>0 THEN 3250 3230 PRINT " "; 3240 GOTO 3260 3250 PRINT ABS(S(I)); 3260 T(I)=T(I)+S(I) 3270 PRINT "TOTAL=";T(I) 3280 GOSUB 1200 3290 T(D1)=T(D1)-S(I) 3300 I=I+D1 3310 GOSUB 1200 3320 I=I-D1 3330 NEXT I 3340 PRINT "DEALER'S TOTAL=";T(D1) 3345 PRINT 3350 GOSUB 1200 3360 GOTO 1810 3400 AA=AA+11*(AA>=22): RETURN 3410 AB=AB+11*(AB>=22): RETURN 3420 AC=AC+11*(AC>=22): RETURN ================================================ FILE: 00_Alternate_Languages/10_Blackjack/pascal/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Pascal](https://en.wikipedia.org/wiki/Pascal_(programming_language)) ================================================ FILE: 00_Alternate_Languages/11_Bombardment/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/11_Bombardment/MiniScript/bombardment.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/11_Bombardment/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/11_Bombardment/bombardment.bas ================================================ 10 PRINT TAB(33);"BOMBARDMENT" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 30 PRINT:PRINT:PRINT 100 PRINT "YOU ARE ON A BATTLEFIELD WITH 4 PLATOONS AND YOU" 110 PRINT "HAVE 25 OUTPOSTS AVAILABLE WHERE THEY MAY BE PLACED." 120 PRINT "YOU CAN ONLY PLACE ONE PLATOON AT ANY ONE OUTPOST." 130 PRINT "THE COMPUTER DOES THE SAME WITH ITS FOUR PLATOONS." 135 PRINT 140 PRINT "THE OBJECT OF THE GAME IS TO FIRE MISSLES AT THE" 150 PRINT "OUTPOSTS OF THE COMPUTER. IT WILL DO THE SAME TO YOU." 160 PRINT "THE ONE WHO DESTROYS ALL FOUR OF THE ENEMY'S PLATOONS" 170 PRINT "FIRST IS THE WINNER." 180 PRINT 190 PRINT "GOOD LUCK... AND TELL US WHERE YOU WANT THE BODIES SENT!" 200 PRINT 210 PRINT "TEAR OFF MATRIX AND USE IT TO CHECK OFF THE NUMBERS." 220 FOR R=1 TO 5: PRINT: NEXT R 260 DIM M(100) 270 FOR R=1 TO 5 280 I=(R-1)*5+1 290 PRINT I,I+1,I+2,I+3,I+4 300 NEXT R 350 FOR R=1 TO 10: PRINT: NEXT R 380 C=INT(RND(1)*25)+1 390 D=INT(RND(1)*25)+1 400 E=INT(RND(1)*25)+1 410 F=INT(RND(1)*25)+1 420 IF C=D THEN 390 430 IF C=E THEN 400 440 IF C=F THEN 410 450 IF D=E THEN 400 460 IF D=F THEN 410 470 IF E=F THEN 410 480 PRINT "WHAT ARE YOUR FOUR POSITIONS"; 490 INPUT G,H,K,L 495 PRINT 500 PRINT "WHERE DO YOU WISH TO FIRE YOUR MISSLE"; 510 INPUT Y 520 IF Y=C THEN 710 530 IF Y=D THEN 710 540 IF Y=E THEN 710 550 IF Y=F THEN 710 560 GOTO 630 570 M=INT(RND(1)*25)+1 575 GOTO 1160 580 IF X=G THEN 920 590 IF X=H THEN 920 600 IF X=L THEN 920 610 IF X=K THEN 920 620 GOTO 670 630 PRINT "HA, HA YOU MISSED. MY TURN NOW:" 640 PRINT: PRINT: GOTO 570 670 PRINT "I MISSED YOU, YOU DIRTY RAT. I PICKED";M". YOUR TURN:" 680 PRINT: PRINT: GOTO 500 710 Q=Q+1 720 IF Q=4 THEN 890 730 PRINT "YOU GOT ONE OF MY OUTPOSTS!" 740 IF Q=1 THEN 770 750 IF Q=2 THEN 810 760 IF Q=3 THEN 850 770 PRINT "ONE DOWN, THREE TO GO." 780 PRINT: PRINT: GOTO 570 810 PRINT "TWO DOWN, TWO TO GO." 820 PRINT: PRINT: GOTO 570 850 PRINT "THREE DOWN, ONE TO GO." 860 PRINT: PRINT: GOTO 570 890 PRINT "YOU GOT ME, I'M GOING FAST. BUT I'LL GET YOU WHEN" 900 PRINT "MY TRANSISTO&S RECUP%RA*E!" 910 GOTO 1235 920 Z=Z+1 930 IF Z=4 THEN 1110 940 PRINT "I GOT YOU. IT WON'T BE LONG NOW. POST";X;"WAS HIT." 950 IF Z=1 THEN 990 960 IF Z=2 THEN 1030 970 IF Z=3 THEN 1070 990 PRINT "YOU HAVE ONLY THREE OUTPOSTS LEFT." 1000 PRINT: PRINT: GOTO 500 1030 PRINT "YOU HAVE ONLY TWO OUTPOSTS LEFT." 1040 PRINT: PRINT: GOTO 500 1070 PRINT "YOU HAVE ONLY ONE OUTPOST LEFT." 1080 PRINT: PRINT: GOTO 500 1110 PRINT "YOU'RE DEAD. YOUR LAST OUTPOST WAS AT";X;". HA, HA, HA." 1120 PRINT "BETTER LUCK NEXT TIME." 1150 GOTO 1235 1160 P=P+1 1170 N=P-1 1180 FOR T=1 TO N 1190 IF M=M(T) THEN 570 1200 NEXT T 1210 X=M 1220 M(P)=M 1230 GOTO 580 1235 END ================================================ FILE: 00_Alternate_Languages/11_Bombardment/go/main.go ================================================ package main import ( "bufio" "fmt" "math/rand" "os" "strconv" "strings" "time" ) // Messages correspond to outposts remaining (3, 2, 1, 0) var PLAYER_PROGRESS_MESSAGES = []string{ "YOU GOT ME, I'M GOING FAST. BUT I'LL GET YOU WHEN\nMY TRANSISTO&S RECUP%RA*E!", "THREE DOWN, ONE TO GO.\n\n", "TWO DOWN, TWO TO GO.\n\n", "ONE DOWN, THREE TO GO.\n\n", } var ENEMY_PROGRESS_MESSAGES = []string{ "YOU'RE DEAD. YOUR LAST OUTPOST WAS AT %d. HA, HA, HA.\nBETTER LUCK NEXT TIME.", "YOU HAVE ONLY ONE OUTPOST LEFT.\n\n", "YOU HAVE ONLY TWO OUTPOSTS LEFT.\n\n", "YOU HAVE ONLY THREE OUTPOSTS LEFT.\n\n", } func displayField() { for r := 0; r < 5; r++ { initial := r*5 + 1 for c := 0; c < 5; c++ { //x := strconv.Itoa(initial + c) fmt.Printf("\t%d", initial+c) } fmt.Println() } fmt.Print("\n\n\n\n\n\n\n\n\n") } func printIntro() { fmt.Println(" BOMBARDMENT") fmt.Println(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") fmt.Println() fmt.Println() fmt.Println("YOU ARE ON A BATTLEFIELD WITH 4 PLATOONS AND YOU") fmt.Println("HAVE 25 OUTPOSTS AVAILABLE WHERE THEY MAY BE PLACED.") fmt.Println("YOU CAN ONLY PLACE ONE PLATOON AT ANY ONE OUTPOST.") fmt.Println("THE COMPUTER DOES THE SAME WITH ITS FOUR PLATOONS.") fmt.Println() fmt.Println("THE OBJECT OF THE GAME IS TO FIRE MISSLES AT THE") fmt.Println("OUTPOSTS OF THE COMPUTER. IT WILL DO THE SAME TO YOU.") fmt.Println("THE ONE WHO DESTROYS ALL FOUR OF THE ENEMY'S PLATOONS") fmt.Println("FIRST IS THE WINNER.") fmt.Println() fmt.Println("GOOD LUCK... AND TELL US WHERE YOU WANT THE BODIES SENT!") fmt.Println() fmt.Println("TEAR OFF MATRIX AND USE IT TO CHECK OFF THE NUMBERS.") fmt.Print("\n\n\n\n") } func positionList() []int { positions := make([]int, 25) for i := 0; i < 25; i++ { positions[i] = i + 1 } return positions } // Randomly choose 4 'positions' out of a range of 1 to 25 func generateEnemyPositions() []int { positions := positionList() rand.Shuffle(len(positions), func(i, j int) { positions[i], positions[j] = positions[j], positions[i] }) return positions[:4] } func isValidPosition(p int) bool { return p >= 1 && p <= 25 } func promptForPlayerPositions() []int { scanner := bufio.NewScanner(os.Stdin) var positions []int for { fmt.Println("\nWHAT ARE YOUR FOUR POSITIONS (1-25)?") scanner.Scan() rawPositions := strings.Split(scanner.Text(), " ") if len(rawPositions) != 4 { fmt.Println("PLEASE ENTER FOUR UNIQUE POSITIONS") goto there } for _, p := range rawPositions { pos, err := strconv.Atoi(p) if (err != nil) || !isValidPosition(pos) { fmt.Println("ALL POSITIONS MUST RANGE (1-25)") goto there } positions = append(positions, pos) } if len(positions) == 4 { return positions } there: } } func promptPlayerForTarget() int { scanner := bufio.NewScanner(os.Stdin) for { fmt.Println("\nWHERE DO YOU WISH TO FIRE YOUR MISSILE?") scanner.Scan() target, err := strconv.Atoi(scanner.Text()) if (err != nil) || !isValidPosition(target) { fmt.Println("POSITIONS MUST RANGE (1-25)") continue } return target } } func generateAttackSequence() []int { positions := positionList() rand.Shuffle(len(positions), func(i, j int) { positions[i], positions[j] = positions[j], positions[i] }) return positions } // Performs attack procedure returning True if we are to continue. func attack(target int, positions *[]int, hitMsg, missMsg string, progressMsg []string) bool { for i := 0; i < len(*positions); i++ { if target == (*positions)[i] { fmt.Print(hitMsg) // remove the target just hit (*positions)[i] = (*positions)[len((*positions))-1] (*positions)[len((*positions))-1] = 0 (*positions) = (*positions)[:len((*positions))-1] if len((*positions)) != 0 { fmt.Print(progressMsg[len((*positions))]) } else { fmt.Printf(progressMsg[len((*positions))], target) } return len((*positions)) > 0 } } fmt.Print(missMsg) return len((*positions)) > 0 } func main() { rand.Seed(time.Now().UnixNano()) printIntro() displayField() enemyPositions := generateEnemyPositions() enemyAttacks := generateAttackSequence() enemyAttackCounter := 0 playerPositions := promptForPlayerPositions() for { // player attacks if !attack(promptPlayerForTarget(), &enemyPositions, "YOU GOT ONE OF MY OUTPOSTS!\n\n", "HA, HA YOU MISSED. MY TURN NOW:\n\n", PLAYER_PROGRESS_MESSAGES) { break } // computer attacks hitMsg := fmt.Sprintf("I GOT YOU. IT WON'T BE LONG NOW. POST %d WAS HIT.\n", enemyAttacks[enemyAttackCounter]) missMsg := fmt.Sprintf("I MISSED YOU, YOU DIRTY RAT. I PICKED %d. YOUR TURN:\n\n", enemyAttacks[enemyAttackCounter]) if !attack(enemyAttacks[enemyAttackCounter], &playerPositions, hitMsg, missMsg, ENEMY_PROGRESS_MESSAGES) { break } enemyAttackCounter += 1 } } ================================================ FILE: 00_Alternate_Languages/12_Bombs_Away/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/12_Bombs_Away/MiniScript/bombsaway.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/12_Bombs_Away/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/12_Bombs_Away/bombsaway.bas ================================================ 8 PRINT "YOU ARE A PILOT IN A WORLD WAR II BOMBER." 10 INPUT "WHAT SIDE -- ITALY(1), ALLIES(2), JAPAN(3), GERMANY(4)";A 20 IF A>0 AND A<5 THEN 25 22 PRINT "TRY AGAIN..." : GOTO 10 25 ON A GOTO 30, 110, 200, 220 30 INPUT "YOUR TARGET -- ALBANIA(1), GREECE(2), NORTH AFRICA(3)";B 40 IF B>0 AND B<4 THEN 45 42 PRINT "TRY AGAIN..." : GOTO 30 45 PRINT : ON B GOTO 50, 80,90 50 PRINT "SHOULD BE EASY -- YOU'RE FLYING A NAZI-MADE PLANE." 60 GOTO 280 80 PRINT "BE CAREFUL!!!" : GOTO 280 90 PRINT "YOU'RE GOING FOR THE OIL, EH?" : GOTO 280 110 INPUT "AIRCRAFT -- LIBERATOR(1), B-29(2), B-17(3), LANCASTER(4)";G 120 IF G>0 AND G<5 THEN 125 122 PRINT "TRY AGAIN..." : GOTO 110 125 PRINT : ON G GOTO 130, 150, 170, 190 130 PRINT "YOU'VE GOT 2 TONS OF BOMBS FLYING FOR PLOESTI." : GOTO 280 150 PRINT "YOU'RE DUMPING THE A-BOMB ON HIROSHIMA." : GOTO 280 170 PRINT "YOU'RE CHASING THE BISMARK IN THE NORTH SEA." : GOTO 280 190 PRINT "YOU'RE BUSTING A GERMAN HEAVY WATER PLANT IN THE RUHR." 195 GOTO 280 200 PRINT "YOU'RE FLYING A KAMIKAZE MISSION OVER THE USS LEXINGTON." 205 INPUT "YOUR FIRST KAMIKAZE MISSION(Y OR N)";F$ 207 IF F$="N" THEN S=0 : GOTO 358 210 PRINT : IF RND(1)>.65 THEN 325 215 GOTO 380 220 PRINT "A NAZI, EH? OH WELL. ARE YOU GOING FOR RUSSIA(1)," 230 INPUT "ENGLAND(2), OR FRANCE(3)";M : IF M>0 AND M<4 THEN 235 232 PRINT "TRY AGAIN..." : GOTO 220 235 PRINT : ON M GOTO 250, 260, 270 250 PRINT "YOU'RE NEARING STALINGRAD." : GOTO 280 260 PRINT "NEARING LONDON. BE CAREFUL, THEY'VE GOT RADAR." : GOTO 280 270 PRINT "NEARING VERSAILLES. DUCK SOUP. THEY'RE NEARLY DEFENSELESS." 280 PRINT 285 INPUT "HOW MANY MISSIONS HAVE YOU FLOWN";D 290 IF D<160 THEN 300 292 PRINT "MISSIONS, NOT MILES..." 295 PRINT "150 MISSIONS IS HIGH EVEN FOR OLD-TIMERS." 297 PRINT "NOW THEN, "; : GOTO 285 300 PRINT:IF D<100 THEN 310 305 PRINT "THAT'S PUSHING THE ODDS!" : GOTO 320 310 IF D<25 THEN PRINT "FRESH OUT OF TRAINING, EH?" 320 PRINT : IF D<160*RND(1) THEN 330 325 PRINT "DIRECT HIT!!!! "INT(100*RND(1))"KILLED." 327 PRINT "MISSION SUCCESSFUL." : GOTO 390 330 PRINT "MISSED TARGET BY"INT(2+30*RND(1))"MILES!" 335 PRINT "NOW YOU'RE REALLY IN FOR IT !!" : PRINT 340 INPUT "DOES THE ENEMY HAVE GUNS(1), MISSILES(2), OR BOTH(3)";R 345 IF R>0 AND R<4 THEN 350 347 PRINT "TRY AGAIN..." : GOTO 340 350 PRINT : T=0 : IF R=2 THEN 360 355 INPUT "WHAT'S THE PERCENT HIT RATE OF ENEMY GUNNERS (10 TO 50)";S 357 IF S<10 THEN PRINT "YOU LIE, BUT YOU'LL PAY...": GOTO 380 358 PRINT 360 PRINT : IF R>1 THEN T=35 365 IF S+T>100*RND(1) THEN 380 370 PRINT "YOU MADE IT THROUGH TREMENDOUS FLAK!!" : GOTO 390 380 PRINT "* * * * BOOM * * * *" 384 PRINT "YOU HAVE BEEN SHOT DOWN....." 386 PRINT "DEARLY BELOVED, WE ARE GATHERED HERE TODAY TO PAY OUR" 387 PRINT "LAST TRIBUTE..." 390 PRINT:PRINT:PRINT:INPUT "ANOTHER MISSION (Y OR N)";U$ 395 IF U$="Y" THEN 8 400 PRINT "CHICKEN !!!" : PRINT : END ================================================ FILE: 00_Alternate_Languages/12_Bombs_Away/go/main.go ================================================ package main import ( "bufio" "fmt" "math/rand" "os" "strconv" "strings" "time" ) type Choice struct { idx string msg string } func playerSurvived() { fmt.Println("YOU MADE IT THROUGH TREMENDOUS FLAK!!") } func playerDeath() { fmt.Println("* * * * BOOM * * * *") fmt.Println("YOU HAVE BEEN SHOT DOWN.....") fmt.Println("DEARLY BELOVED, WE ARE GATHERED HERE TODAY TO PAY OUR") fmt.Println("LAST TRIBUTE...") } func missionSuccess() { fmt.Printf("DIRECT HIT!!!! %d KILLED.\n", int(100*rand.Int())) fmt.Println("MISSION SUCCESSFUL.") } // Takes a float between 0 and 1 and returns a boolean // if the player has survived (based on random chance) // Returns True if death, False if survived func deathWithChance(probability float64) bool { return probability > rand.Float64() } func startNonKamikaziAttack() { numMissions := getIntInput("HOW MANY MISSIONS HAVE YOU FLOWN? ") for numMissions > 160 { fmt.Println("MISSIONS, NOT MILES...") fmt.Println("150 MISSIONS IS HIGH EVEN FOR OLD-TIMERS") numMissions = getIntInput("HOW MANY MISSIONS HAVE YOU FLOWN? ") } if numMissions > 100 { fmt.Println("THAT'S PUSHING THE ODDS!") } if numMissions < 25 { fmt.Println("FRESH OUT OF TRAINING, EH?") } fmt.Println() if float32(numMissions) > (160 * rand.Float32()) { missionSuccess() } else { missionFailure() } } func missionFailure() { fmt.Printf("MISSED TARGET BY %d MILES!\n", int(2+30*rand.Float32())) fmt.Println("NOW YOU'RE REALLY IN FOR IT !!") fmt.Println() enemyWeapons := getInputFromList("DOES THE ENEMY HAVE GUNS(1), MISSILES(2), OR BOTH(3)? ", []Choice{{idx: "1", msg: "GUNS"}, {idx: "2", msg: "MISSILES"}, {idx: "3", msg: "BOTH"}}) // If there are no gunners (i.e. weapon choice 2) then // we say that the gunners have 0 accuracy for the purposes // of calculating probability of player death enemyGunnerAccuracy := 0.0 if enemyWeapons.idx != "2" { enemyGunnerAccuracy = float64(getIntInput("WHAT'S THE PERCENT HIT RATE OF ENEMY GUNNERS (10 TO 50)? ")) if enemyGunnerAccuracy < 10.0 { fmt.Println("YOU LIE, BUT YOU'LL PAY...") playerDeath() } } missileThreatWeighting := 35.0 if enemyWeapons.idx == "1" { missileThreatWeighting = 0 } death := deathWithChance((enemyGunnerAccuracy + missileThreatWeighting) / 100) if death { playerDeath() } else { playerSurvived() } } func playItaly() { targets := []Choice{{idx: "1", msg: "SHOULD BE EASY -- YOU'RE FLYING A NAZI-MADE PLANE."}, {idx: "2", msg: "BE CAREFUL!!!"}, {idx: "3", msg: "YOU'RE GOING FOR THE OIL, EH?"}} target := getInputFromList("YOUR TARGET -- ALBANIA(1), GREECE(2), NORTH AFRICA(3)", targets) fmt.Println(target.msg) startNonKamikaziAttack() } func playAllies() { aircraftMessages := []Choice{{idx: "1", msg: "YOU'VE GOT 2 TONS OF BOMBS FLYING FOR PLOESTI."}, {idx: "2", msg: "YOU'RE DUMPING THE A-BOMB ON HIROSHIMA."}, {idx: "3", msg: "YOU'RE CHASING THE BISMARK IN THE NORTH SEA."}, {idx: "4", msg: "YOU'RE BUSTING A GERMAN HEAVY WATER PLANT IN THE RUHR."}} aircraft := getInputFromList("AIRCRAFT -- LIBERATOR(1), B-29(2), B-17(3), LANCASTER(4): ", aircraftMessages) fmt.Println(aircraft.msg) startNonKamikaziAttack() } func playJapan() { acknowledgeMessage := []Choice{{idx: "Y", msg: "Y"}, {idx: "N", msg: "N"}} firstMission := getInputFromList("YOU'RE FLYING A KAMIKAZE MISSION OVER THE USS LEXINGTON.\nYOUR FIRST KAMIKAZE MISSION? (Y OR N): ", acknowledgeMessage) if firstMission.msg == "N" { playerDeath() } if rand.Float64() > 0.65 { missionSuccess() } else { playerDeath() } } func playGermany() { targets := []Choice{{idx: "1", msg: "YOU'RE NEARING STALINGRAD."}, {idx: "2", msg: "NEARING LONDON. BE CAREFUL, THEY'VE GOT RADAR."}, {idx: "3", msg: "NEARING VERSAILLES. DUCK SOUP. THEY'RE NEARLY DEFENSELESS."}} target := getInputFromList("A NAZI, EH? OH WELL. ARE YOU GOING FOR RUSSIA(1),\nENGLAND(2), OR FRANCE(3)? ", targets) fmt.Println(target.msg) startNonKamikaziAttack() } func playGame() { fmt.Println("YOU ARE A PILOT IN A WORLD WAR II BOMBER.") side := getInputFromList("WHAT SIDE -- ITALY(1), ALLIES(2), JAPAN(3), GERMANY(4): ", []Choice{{idx: "1", msg: "ITALY"}, {idx: "2", msg: "ALLIES"}, {idx: "3", msg: "JAPAN"}, {idx: "4", msg: "GERMANY"}}) switch side.idx { case "1": playItaly() case "2": playAllies() case "3": playJapan() case "4": playGermany() } } func main() { rand.Seed(time.Now().UnixNano()) for { playGame() if getInputFromList("ANOTHER MISSION (Y OR N):", []Choice{{idx: "Y", msg: "Y"}, {idx: "N", msg: "N"}}).msg == "N" { break } } } func getInputFromList(prompt string, choices []Choice) Choice { scanner := bufio.NewScanner(os.Stdin) for { fmt.Println(prompt) scanner.Scan() choice := scanner.Text() for _, c := range choices { if strings.EqualFold(strings.ToUpper(choice), strings.ToUpper(c.idx)) { return c } } fmt.Println("TRY AGAIN...") } } func getIntInput(prompt string) int { scanner := bufio.NewScanner(os.Stdin) for { fmt.Println(prompt) scanner.Scan() choice, err := strconv.Atoi(scanner.Text()) if err != nil { fmt.Println("TRY AGAIN...") continue } else { return choice } } } ================================================ FILE: 00_Alternate_Languages/13_Bounce/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/13_Bounce/MiniScript/bounce.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/13_Bounce/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/13_Bounce/bounce.bas ================================================ 10 PRINT TAB(33);"BOUNCE" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 30 PRINT:PRINT:PRINT 90 DIM T(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 135 INPUT "TIME INCREMENT (SEC)";S2 140 PRINT 150 INPUT "VELOCITY (FPS)";V 160 PRINT 170 INPUT "COEFFICIENT";C 180 PRINT 182 PRINT "FEET" 184 PRINT 186 S1=INT(70/(V/(16*S2))) 190 FOR I=1 TO S1 200 T(I)=V*C^(I-1)/16 210 NEXT I 220 FOR H=INT(-16*(V/32)^2+V^2/32+.5) TO 0 STEP -.5 221 IF INT(H)<>H THEN 225 222 PRINT H; 225 L=0 230 FOR I=1 TO S1 240 FOR T=0 TO T(I) STEP S2 245 L=L+S2 250 IF ABS(H-(.5*(-32)*T^2+V*C^(I-1)*T))>.25 THEN 270 260 PRINT TAB(L/S2);"0"; 270 NEXT T 275 T=T(I+1)/2 276 IF -16*T^2+V*C^(I-1)*T<H THEN 290 280 NEXT I 290 PRINT 300 NEXT H 310 PRINT TAB(1); 320 FOR I=1 TO INT(L+1)/S2+1 330 PRINT "."; 340 NEXT I 350 PRINT 355 PRINT " 0"; 360 FOR I=1 TO INT(L+.9995) 380 PRINT TAB(INT(I/S2));I; 390 NEXT I 400 PRINT 410 PRINT TAB(INT(L+1)/(2*S2)-2);"SECONDS" 420 PRINT 430 GOTO 135 440 END ================================================ FILE: 00_Alternate_Languages/14_Bowling/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/14_Bowling/MiniScript/bowling.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/14_Bowling/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/14_Bowling/bowling.bas ================================================ 10 PRINT TAB(34);"BOWL" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 30 PRINT:PRINT:PRINT 270 DIM C(15),A(100,6) 360 PRINT "WELCOME TO THE ALLEY" 450 PRINT "BRING YOUR FRIENDS" 540 PRINT "OKAY LET'S FIRST GET ACQUAINTED" 630 PRINT "" 720 PRINT "THE INSTRUCTIONS (Y/N)" 810 INPUT Z$ 900 IF Z$="Y" THEN 990 960 IF Z$="N" THEN 1530 990 PRINT "THE GAME OF BOWLING TAKES MIND AND SKILL.DURING THE GAME" 1080 PRINT "THE COMPUTER WILL KEEP SCORE.YOU MAY COMPETE WITH" 1170 PRINT "OTHER PLAYERS[UP TO FOUR].YOU WILL BE PLAYING TEN FRAMES" 1260 PRINT "ON THE PIN DIAGRAM 'O' MEANS THE PIN IS DOWN...'+' MEANS THE" 1350 PRINT "PIN IS STANDING.AFTER THE GAME THE COMPUTER WILL SHOW YOUR" 1440 PRINT "SCORES ." 1530 PRINT "FIRST OF ALL...HOW MANY ARE PLAYING"; 1620 INPUT R 1710 PRINT 1800 PRINT "VERY GOOD..." 1890 FOR I=1 TO 100: FOR J=1 TO 6: A(I,J)=0: NEXT J: NEXT I 1980 F=1 2070 FOR P=1 TO R 2160 M=0 2250 B=1 2340 M=0: Q=0 2430 FOR I=1 TO 15: C(I)=0: NEXT I 2520 REM ARK BALL GENERATOR USING MOD '15' SYSTEM 2610 PRINT "TYPE ROLL TO GET THE BALL GOING." 2700 INPUT N$ 2790 K=0: D=0 2880 FOR I=1 TO 20 2970 X=INT(RND(1)*100) 3060 FOR J=1 TO 10 3150 IF X<15*J THEN 3330 3240 NEXT J 3330 C(15*J-X)=1 3420 NEXT I 3510 REM ARK PIN DIAGRAM 3600 PRINT "PLAYER:"P;"FRAME:";F"BALL:"B 3690 FOR I=0 TO 3 3780 PRINT 3870 FOR J=1 TO 4-I 3960 K=K+1 4050 IF C(K)=1 THEN 4320 4140 PRINT TAB(I);"+ "; 4230 GOTO 4410 4320 PRINT TAB(I);"O "; 4410 NEXT J 4500 NEXT I 4590 PRINT "" 4680 REM ARK ROLL ANALYSIS 4770 FOR I=1 TO 10 4860 D=D+C(I) 4950 NEXT I 5040 IF D-M <> 0 THEN 5220 5130 PRINT "GUTTER!!" 5220 IF B<>1 OR D<>10 THEN 5490 5310 PRINT "STRIKE!!!!!" 5400 Q=3 5490 IF B<>2 OR D<>10 THEN 5760 5580 PRINT "SPARE!!!!" 5670 Q=2 5760 IF B<>2 OR D>=10 THEN 6030 5850 PRINT "ERROR!!!" 5940 Q=1 6030 IF B<>1 OR D>=10 THEN 6210 6120 PRINT "ROLL YOUR 2ND BALL" 6210 REM ARK STORAGE OF THE SCORES 6300 PRINT 6390 A(F*P,B)=D 6480 IF B=2 THEN 7020 6570 B=2 6660 M=D 6750 IF Q=3 THEN 6210 6840 A(F*P,B)=D-M 6930 IF Q=0 THEN 2520 7020 A(F*P,3)=Q 7110 NEXT P 7200 F=F+1 7290 IF F<11 THEN 2070 7295 PRINT "FRAMES" 7380 FOR I=1 TO 10 7470 PRINT I; 7560 NEXT I 7650 PRINT 7740 FOR P=1 TO R 7830 FOR I=1 TO 3 7920 FOR J=1 TO 10 8010 PRINT A(J*P,I); 8100 NEXT J 8105 PRINT 8190 NEXT I 8280 PRINT 8370 NEXT P 8460 PRINT "DO YOU WANT ANOTHER GAME" 8550 INPUT A$ 8640 IF LEFT$(A$,1)="Y" THEN 2610 8730 END ================================================ FILE: 00_Alternate_Languages/15_Boxing/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/15_Boxing/MiniScript/boxing.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/15_Boxing/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/15_Boxing/boxing.bas ================================================ 1 PRINT TAB(33);"BOXING" 2 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 3 PRINT:PRINT:PRINT 4 PRINT "BOXING OLYMPIC STYLE (3 ROUNDS -- 2 OUT OF 3 WINS)" 5 J=0 6 L=0 8 PRINT 10 PRINT "WHAT IS YOUR OPPONENT'S NAME"; 20 INPUT J$ 30 PRINT "INPUT YOUR MAN'S NAME"; 40 INPUT L$ 50 PRINT "DIFFERENT PUNCHES ARE: (1) FULL SWING; (2) HOOK; (3) UPPERCUT; (4) JAB." 60 PRINT "WHAT IS YOUR MANS BEST"; 64 INPUT B 70 PRINT "WHAT IS HIS VULNERABILITY"; 80 INPUT D 90 B1=INT(4*RND(1)+1) 100 D1=INT(4*RND(1)+1) 110 IF B1=D1 THEN 90 120 PRINT J$;"'S ADVANTAGE IS";B1;"AND VULNERABILITY IS SECRET.":PRINT 130 FOR R=1 TO 3 140 IF J>= 2 THEN 1040 150 IF L>=2 THEN 1060 160 X=0 170 Y=0 180 PRINT "ROUND";R;"BEGINS..." 185 FOR R1= 1 TO 7 190 I=INT(10*RND(1)+1) 200 IF I>5 THEN 600 210 PRINT L$;"'S PUNCH"; 220 INPUT P 221 IF P=B THEN 225 222 GOTO 230 225 X=X+2 230 IF P=1 THEN 340 240 IF P=2 THEN 450 250 IF P=3 THEN 520 270 PRINT L$;" JABS AT ";J$"'S HEAD "; 271 IF D1=4 THEN 290 275 C=INT(8*RND(1)+1) 280 IF C<4 THEN 310 290 X=X+3 300 GOTO 950 310 PRINT "IT'S BLOCKED." 330 GOTO 950 340 PRINT L$ " SWINGS AND "; 341 IF D1=4 THEN 410 345 X3=INT(30*RND(1)+1) 350 IF X3<10 THEN 410 360 PRINT "HE MISSES "; 370 PRINT 375 IF X=1 THEN 950 380 PRINT 390 PRINT 400 GOTO 300 410 PRINT "HE CONNECTS!" 420 IF X>35 THEN 980 425 X=X+15 440 GOTO 300 450 PRINT L$;" GIVES THE HOOK... "; 455 IF D1=2 THEN 480 460 H1=INT(2*RND(1)+1) 470 IF H1=1 THEN 500 475 PRINT "CONNECTS..." 480 X=X+7 490 GOTO 300 500 PRINT "BUT IT'S BLOCKED!!!!!!!!!!!!!" 510 GOTO 300 520 PRINT L$ " TRIES AN UPPERCUT "; 530 IF D1=3 THEN 570 540 D5=INT(100*RND(1)+1) 550 IF D5<51 THEN 570 560 PRINT "AND IT'S BLOCKED (LUCKY BLOCK!)" 565 GOTO 300 570 PRINT "AND HE CONNECTS!" 580 X=X+4 590 GOTO 300 600 J7=INT(4*RND(1)+1) 601 IF J7 =B1 THEN 605 602 GOTO 610 605 Y=Y+2 610 IF J7=1 THEN 720 620 IF J7=2 THEN 810 630 IF J7 =3 THEN 860 640 PRINT J$;" JABS AND "; 645 IF D=4 THEN 700 650 Z4=INT(7*RND(1)+1) 655 IF Z4>4 THEN 690 660 PRINT "IT'S BLOCKED!" 670 GOTO 300 690 PRINT " BLOOD SPILLS !!!" 700 Y=Y+5 710 GOTO 300 720 PRINT J$" TAKES A FULL SWING AND"; 730 IF D=1 THEN 770 740 R6=INT(60*RND(1)+1) 745 IF R6 <30 THEN 770 750 PRINT " IT'S BLOCKED!" 760 GOTO 300 770 PRINT " POW!!!!! HE HITS HIM RIGHT IN THE FACE!" 780 IF Y>35 THEN 1010 790 Y=Y+15 800 GOTO 300 810 PRINT J$;" GETS ";L$;" IN THE JAW (OUCH!)" 820 Y=Y+7 830 PRINT "....AND AGAIN!" 835 Y=Y+5 840 IF Y>35 THEN 1010 850 PRINT 860 PRINT L$;" IS ATTACKED BY AN UPPERCUT (OH,OH)..." 865 IF D=3 THEN 890 870 Q4=INT(200*RND(1)+1) 880 IF Q4>75 THEN 920 890 PRINT "AND ";J$;" CONNECTS..." 900 Y=Y+8 910 GOTO 300 920 PRINT " BLOCKS AND HITS ";J$;" WITH A HOOK." 930 X=X+5 940 GOTO 300 950 NEXT R1 951 IF X>Y THEN 955 952 PRINT:PRINT J$" WINS ROUND" R 953 J=J+1 954 GOTO 960 955 PRINT:PRINT L$" WINS ROUND"R 956 L=L+1 960 NEXT R 961 IF J>= 2 THEN 1040 962 IF L>=2 THEN 1060 980 PRINT J$ " IS KNOCKED COLD AND " L$" IS THE WINNER AND CHAMP!"; 1000 GOTO 1080 1010 PRINT L$ " IS KNOCKED COLD AND " J$" IS THE WINNER AND CHAMP!"; 1030 GOTO 1000 1040 PRINT J$ " WINS (NICE GOING," J$;")." 1050 GOTO 1000 1060 PRINT L$ " AMAZINGLY WINS!!" 1070 GOTO 1000 1080 PRINT 1085 PRINT 1090 PRINT "AND NOW GOODBYE FROM THE OLYMPIC ARENA." 1100 PRINT 1110 END ================================================ FILE: 00_Alternate_Languages/16_Bug/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/16_Bug/MiniScript/bug.ms ================================================ 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!!" ================================================ FILE: 00_Alternate_Languages/16_Bug/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/16_Bug/bug.bas ================================================ 10 PRINT TAB(34);"BUG" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 30 PRINT:PRINT:PRINT 40 REM 50 A=0: B=0: H=0: L=0: N=0: P=0: Q=0: R=0: S=0: T=0: U=0: V=0: Y=0 60 PRINT "THE GAME BUG" 70 PRINT "I HOPE YOU ENJOY THIS GAME." 80 PRINT 90 PRINT "DO YOU WANT INSTRUCTIONS"; 100 INPUT Z$ 110 IF Z$="NO" THEN 300 120 PRINT "THE OBJECT OF BUG IS TO FINISH YOUR BUG BEFORE I FINISH" 130 PRINT "MINE. EACH NUMBER STANDS FOR A PART OF THE BUG BODY." 140 PRINT "I WILL ROLL THE DIE FOR YOU, TELL YOU WHAT I ROLLED FOR YOU" 150 PRINT "WHAT THE NUMBER STANDS FOR, AND IF YOU CAN GET THE PART." 160 PRINT "IF YOU CAN GET THE PART I WILL GIVE IT TO YOU." 170 PRINT "THE SAME WILL HAPPEN ON MY TURN." 180 PRINT "IF THERE IS A CHANGE IN EITHER BUG I WILL GIVE YOU THE" 190 PRINT "OPTION OF SEEING THE PICTURES OF THE BUGS." 200 PRINT "THE NUMBERS STAND FOR PARTS AS FOLLOWS:" 210 PRINT "NUMBER","PART","NUMBER OF PART NEEDED" 220 PRINT "1","BODY","1" 230 PRINT "2","NECK","1" 240 PRINT "3","HEAD","1" 250 PRINT "4","FEELERS","2" 260 PRINT "5","TAIL","1" 270 PRINT "6","LEGS","6" 280 PRINT 290 PRINT 300 IF Y>0 THEN 2480 310 Z=INT(6*RND(1)+1) 320 C=1 330 PRINT "YOU ROLLED A";Z 340 ON Z GOTO 350,430,540,650,760,870 350 PRINT "1=BODY" 360 IF B=1 THEN 410 370 PRINT "YOU NOW HAVE A BODY." 380 B=1 390 C=0 400 GOTO 970 410 PRINT "YOU DO NOT NEED A BODY." 420 GOTO 970 430 PRINT "2=NECK" 440 IF N=1 THEN 500 450 IF B=0 THEN 520 460 PRINT "YOU NOW HAVE A NECK." 470 N=1 480 C=0 490 GOTO 970 500 PRINT "YOU DO NOT NEED A NECK." 510 GOTO 970 520 PRINT "YOU DO NOT HAVE A BODY." 530 GOTO 970 540 PRINT "3=HEAD" 550 IF N=0 THEN 610 560 IF H=1 THEN 630 570 PRINT "YOU NEEDED A HEAD." 580 H=1 590 C=0 600 GOTO 970 610 PRINT "YOU DO NOT HAVE A NECK." 620 GOTO 970 630 PRINT "YOU HAVE A HEAD." 640 GOTO 970 650 PRINT "4=FEELERS" 660 IF H=0 THEN 740 670 IF A=2 THEN 720 680 PRINT "I NOW GIVE YOU A FEELER." 690 A=A+1 700 C=0 710 GOTO 970 720 PRINT "YOU HAVE TWO FEELERS ALREADY." 730 GOTO 970 740 PRINT "YOU DO NOT HAVE A HEAD." 750 GOTO 970 760 PRINT "5=TAIL" 770 IF B=0 THEN 830 780 IF T=1 THEN 850 790 PRINT "I NOW GIVE YOU A TAIL." 800 T=T+1 810 C=0 820 GOTO 970 830 PRINT "YOU DO NOT HAVE A BODY." 840 GOTO 970 850 PRINT "YOU ALREADY HAVE A TAIL." 860 GOTO 970 870 PRINT "6=LEG" 880 IF L=6 THEN 940 890 IF B=0 THEN 960 900 L=L+1 910 C=0 920 PRINT "YOU NOW HAVE";L;"LEGS." 930 GOTO 970 940 PRINT "YOU HAVE 6 FEET ALREADY." 950 GOTO 970 960 PRINT "YOU DO NOT HAVE A BODY." 970 X=INT(6*RND(1)+1) 971 PRINT 975 FOR DELAY=1 TO 2000:NEXT DELAY 980 PRINT "I ROLLED A";X 990 ON X GOTO 1000,1080,1190,1300,1410,1520 1000 PRINT "1=BODY" 1010 IF P=1 THEN 1060 1020 PRINT "I NOW HAVE A BODY." 1030 C=0 1040 P=1 1050 GOTO 1630 1060 PRINT "I DO NOT NEED A BODY." 1070 GOTO 1630 1080 PRINT "2=NECK" 1090 IF Q=1 THEN 1150 1100 IF P=0 THEN 1170 1110 PRINT "I NOW HAVE A NECK." 1120 Q=1 1130 C=0 1140 GOTO 1630 1150 PRINT "I DO NOT NEED A NECK." 1160 GOTO 1630 1170 PRINT "I DO NOT HAVE A BODY." 1180 GOTO 1630 1190 PRINT "3=HEAD" 1200 IF Q=0 THEN 1260 1210 IF R=1 THEN 1280 1220 PRINT "I NEEDED A HEAD." 1230 R=1 1240 C=0 1250 GOTO 1630 1260 PRINT "I DO NOT HAVE A NECK." 1270 GOTO 1630 1280 PRINT "I DO NOT NEED A HEAD." 1290 GOTO 1630 1300 PRINT "4=FEELERS" 1310 IF R=0 THEN 1390 1320 IF S=2 THEN 1370 1330 PRINT "I GET A FEELER." 1340 S=S+1 1350 C=0 1360 GOTO 1630 1370 PRINT "I HAVE 2 FEELERS ALREADY." 1380 GOTO 1630 1390 PRINT "I DO NOT HAVE A HEAD." 1400 GOTO 1630 1410 PRINT "5=TAIL" 1420 IF P=0 THEN 1480 1430 IF U=1 THEN 1500 1440 PRINT "I NOW HAVE A TAIL." 1450 U=1 1460 C=0 1470 GOTO 1630 1480 PRINT "I DO NOT HAVE A BODY." 1490 GOTO 1630 1500 PRINT "I DO NOT NEED A TAIL." 1510 GOTO 1630 1520 PRINT "6=LEGS" 1530 IF V=6 THEN 1590 1540 IF P=0 THEN 1610 1550 V=V+1 1560 C=0 1570 PRINT "I NOW HAVE";V;"LEGS." 1580 GOTO 1630 1590 PRINT,"I HAVE 6 FEET." 1600 GOTO 1630 1610 PRINT "I DO NOT HAVE A BODY." 1620 GOTO 1630 1630 IF A=2 AND T=1 AND L=6 THEN 1650 1640 GOTO 1670 1650 PRINT "YOUR BUG IS FINISHED." 1660 Y=Y+1 1670 IF S=2 AND P=1 AND V=6 THEN 1690 1680 GOTO 1710 1690 PRINT "MY BUG IS FINISHED." 1700 Y=Y+2 1710 IF C=1 THEN 300 1720 PRINT "DO YOU WANT THE PICTURES"; 1730 INPUT Z$ 1740 IF Z$="NO" THEN 300 1750 PRINT "*****YOUR BUG*****" 1760 PRINT 1770 PRINT 1780 IF A=0 THEN 1860 1790 FOR Z=1 TO 4 1800 FOR X=1 TO A 1810 PRINT TAB(10); 1820 PRINT "A "; 1830 NEXT X 1840 PRINT 1850 NEXT Z 1860 IF H=0 THEN 1880 1870 GOSUB 2470 1880 IF N=0 THEN 1920 1890 FOR Z=1 TO 2 1900 PRINT " N N" 1910 NEXT Z 1920 IF B=0 THEN 2000 1930 PRINT " BBBBBBBBBBBB" 1940 FOR Z=1 TO 2 1950 PRINT " B B" 1960 NEXT Z 1970 IF T<>1 THEN 1990 1980 PRINT "TTTTTB B" 1990 PRINT " BBBBBBBBBBBB" 2000 IF L=0 THEN 2080 2010 FOR Z=1 TO 2 2020 PRINT TAB(5); 2030 FOR X=1 TO L 2040 PRINT " L"; 2050 NEXT X 2060 PRINT 2070 NEXT Z 2080 FOR Z=1 TO 4 2090 PRINT 2100 NEXT Z 2110 PRINT "*****MY BUG*****" 2120 PRINT 2130 PRINT 2140 PRINT 2150 IF S=0 THEN 2230 2160 FOR Z=1 TO 4 2170 PRINT TAB(10); 2180 FOR X=1 TO S 2190 PRINT "F "; 2200 NEXT X 2210 PRINT 2220 NEXT Z 2230 IF R<>1 THEN 2250 2240 GOSUB 2470 2250 IF Q=0 THEN 2280 2260 PRINT " N N" 2270 PRINT " N N" 2280 IF P=0 THEN 2360 2290 PRINT " BBBBBBBBBBBB" 2300 FOR Z=1 TO 2 2310 PRINT " B B" 2320 NEXT Z 2330 IF U<>1 THEN 2350 2340 PRINT "TTTTTB B" 2350 PRINT " BBBBBBBBBBBB" 2360 IF V=0 THEN 2450 2370 FOR Z=1 TO 2 2380 PRINT TAB(5); 2390 FOR X=1 TO V 2400 PRINT " L"; 2410 NEXT X 2420 PRINT 2430 NEXT Z 2450 IF Y<>0 THEN 2540 2460 GOTO 300 2470 PRINT " HHHHHHH" 2480 PRINT " H H" 2490 PRINT " H O O H" 2500 PRINT " H H" 2510 PRINT " H V H" 2520 PRINT " HHHHHHH" 2530 RETURN 2540 PRINT "I HOPE YOU ENJOYED THE GAME, PLAY IT AGAIN SOON!!" 2550 END ================================================ FILE: 00_Alternate_Languages/17_Bullfight/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/17_Bullfight/MiniScript/bull.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/17_Bullfight/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/17_Bullfight/bullfight.bas ================================================ 10 PRINT TAB(34);"BULL" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 30 DEF FNA(K)=INT(RND(1)*2+1) 200 PRINT:PRINT:PRINT 202 L=1 205 PRINT "DO YOU WANT INSTRUCTIONS"; 206 INPUT Z$ 207 IF Z$="NO" THEN 400 210 PRINT "HELLO, ALL YOU BLOODLOVERS AND AFICIONADOS." 220 PRINT "HERE IS YOUR BIG CHANCE TO KILL A BULL." 230 PRINT 240 PRINT "ON EACH PASS OF THE BULL, YOU MAY TRY" 250 PRINT "0 - VERONICA (DANGEROUS INSIDE MOVE OF THE CAPE)" 260 PRINT "1 - LESS DANGEROUS OUTSIDE MOVE OF THE CAPE" 270 PRINT "2 - ORDINARY SWIRL OF THE CAPE." 280 PRINT 290 PRINT "INSTEAD OF THE ABOVE, YOU MAY TRY TO KILL THE BULL" 300 PRINT "ON ANY TURN: 4 (OVER THE HORNS), 5 (IN THE CHEST)." 310 PRINT "BUT IF I WERE YOU," 320 PRINT "I WOULDN'T TRY IT BEFORE THE SEVENTH PASS." 330 PRINT 340 PRINT "THE CROWD WILL DETERMINE WHAT AWARD YOU DESERVE" 350 PRINT "(POSTHUMOUSLY IF NECESSARY)." 360 PRINT "THE BRAVER YOU ARE, THE BETTER THE AWARD YOU RECEIVE." 370 PRINT 380 PRINT "THE BETTER THE JOB THE PICADORES AND TOREADORES DO," 390 PRINT "THE BETTER YOUR CHANCES ARE." 400 PRINT 410 PRINT 420 D(5)=1 430 D(4)=1 450 DIM L$(5) 455 A=INT(RND(1)*5+1) 460 FOR I=1 TO 5 463 READ L$(I) 467 NEXT I 470 DATA "SUPERB","GOOD","FAIR","POOR","AWFUL" 490 PRINT "YOU HAVE DRAWN A ";L$(A);" BULL." 500 IF A>4 THEN 530 510 IF A<2 THEN 550 520 GOTO 570 530 PRINT "YOU'RE LUCKY." 540 GOTO 570 550 PRINT "GOOD LUCK. YOU'LL NEED IT." 560 PRINT 570 PRINT 590 A$="PICADO" 595 B$="RES" 600 GOSUB 1610 610 D(1)=C 630 A$="TOREAD" 635 B$="ORES" 640 GOSUB 1610 650 D(2)=C 660 PRINT 670 PRINT 680 IF Z=1 THEN 1310 690 D(3)=D(3)+1 700 PRINT "PASS NUMBER";D(3) 710 IF D(3)<3 THEN 760 720 PRINT "HERE COMES THE BULL. TRY FOR A KILL"; 730 GOSUB 1930 735 IF Z1=1 THEN 1130 740 PRINT "CAPE MOVE"; 750 GOTO 800 760 PRINT "THE BULL IS CHARGING AT YOU! YOU ARE THE MATADOR--" 770 PRINT "DO YOU WANT TO KILL THE BULL"; 780 GOSUB 1930 785 IF Z1=1 THEN 1130 790 PRINT "WHAT MOVE DO YOU MAKE WITH THE CAPE"; 800 INPUT E 810 IF E<>INT(ABS(E)) THEN 830 820 IF E<3 THEN 850 830 PRINT "DON'T PANIC, YOU IDIOT! PUT DOWN A CORRECT NUMBER" 840 GOTO 800 850 REM 860 IF E=0 THEN 920 870 IF E=1 THEN 900 880 M=.5 890 GOTO 930 900 M=2 910 GOTO 930 920 M=3 930 L=L+M 940 F=(6-A+M/10)*RND(1)/((D(1)+D(2)+D(3)/10)*5) 950 IF F<.51 THEN 660 960 PRINT "THE BULL HAS GORED YOU!" 970 ON FNA(0) GOTO 980,1010 980 PRINT "YOU ARE DEAD." 990 D(4)=1.5 1000 GOTO 1310 1010 PRINT "YOU ARE STILL ALIVE.":PRINT 1020 PRINT "DO YOU RUN FROM THE RING"; 1030 GOSUB 1930 1035 IF Z1=2 THEN 1070 1040 PRINT "COWARD" 1050 D(4)=0 1060 GOTO 1310 1070 PRINT "YOU ARE BRAVE. STUPID, BUT BRAVE." 1080 ON FNA(0) GOTO 1090,1110 1090 D(4)=2 1100 GOTO 660 1110 PRINT "YOU ARE GORED AGAIN!" 1120 GOTO 970 1130 REM 1140 Z=1 1150 PRINT:PRINT "IT IS THE MOMENT OF TRUTH.":PRINT 1155 PRINT "HOW DO YOU TRY TO KILL THE BULL"; 1160 INPUT H 1170 IF H=4 THEN 1230 1180 IF H=5 THEN 1230 1190 PRINT "YOU PANICKED. THE BULL GORED YOU." 1220 GOTO 970 1230 K=(6-A)*10*RND(1)/((D(1)+D(2))*5*D(3)) 1240 IF H=4 THEN 1290 1250 IF K>.2 THEN 960 1260 PRINT "YOU KILLED THE BULL!" 1270 D(5)=2 1280 GOTO 1320 1290 IF K>.8 THEN 960 1300 GOTO 1260 1310 PRINT 1320 PRINT 1330 PRINT 1340 IF D(4)<>0 THEN 1390 1350 PRINT "THE CROWD BOOS FOR TEN MINUTES. IF YOU EVER DARE TO SHOW" 1360 PRINT "YOUR FACE IN A RING AGAIN, THEY SWEAR THEY WILL KILL YOU--" 1370 PRINT "UNLESS THE BULL DOES FIRST." 1380 GOTO 1580 1390 DEF FNC(Q)=FND(Q)*RND(1) 1395 DEF FND(Q)=(4.5+L/6-(D(1)+D(2))*2.5+4*D(4)+2*D(5)-D(3)^2/120-A) 1400 IF D(4)<>2 THEN 1430 1410 PRINT "THE CROWD CHEERS WILDLY!" 1420 GOTO 1450 1430 IF D(5)<>2 THEN 1450 1440 PRINT "THE CROWD CHEERS!":PRINT 1450 PRINT "THE CROWD AWARDS YOU" 1460 IF FNC(Q)<2.4 THEN 1570 1470 IF FNC(Q)<4.9 THEN 1550 1480 IF FNC(Q)<7.4 THEN 1520 1500 PRINT "OLE! YOU ARE 'MUY HOMBRE'!! OLE! OLE!" 1510 GOTO 1580 1520 PRINT "BOTH EARS OF THE BULL!" 1530 PRINT "OLE!" 1540 GOTO 1580 1550 PRINT "ONE EAR OF THE BULL." 1560 GOTO 1580 1570 PRINT "NOTHING AT ALL." 1580 PRINT 1590 PRINT "ADIOS":PRINT:PRINT:PRINT 1600 GOTO 2030 1610 B=3/A*RND(1) 1620 IF B<.37 THEN 1740 1630 IF B<.5 THEN 1720 1640 IF B<.63 THEN 1700 1650 IF B<.87 THEN 1680 1660 C=.1 1670 GOTO 1750 1680 C=.2 1690 GOTO 1750 1700 C=.3 1710 GOTO 1750 1720 C=.4 1730 GOTO 1750 1740 C=.5 1750 T=INT(10*C+.2) 1760 PRINT "THE ";A$;B$;" DID A ";L$(T);" JOB." 1770 IF 4>T THEN 1900 1780 IF 5=T THEN 1870 1790 ON FNA(K) GOTO 1830,1850 1800 IF A$="TOREAD" THEN 1820 1810 PRINT "ONE OF THE HORSES OF THE ";A$;B$;" WAS KILLED." 1820 ON FNA(K) GOTO 1830,1850 1830 PRINT "ONE OF THE ";A$;B$;" WAS KILLED." 1840 GOTO 1900 1850 PRINT "NO ";A$;B$;" WERE KILLED." 1860 GOTO 1900 1870 IF A$="TOREAD" THEN 1890 1880 PRINT FNA(K);"OF THE HORSES OF THE ";A$;B$;" KILLED." 1890 PRINT FNA(K);"OF THE ";A$;B$;" KILLED." 1900 PRINT 1910 RETURN 1920 REM 1930 INPUT A$ 1940 IF A$="YES" THEN 1990 1950 IF A$="NO" THEN 2010 1970 PRINT "INCORRECT ANSWER - - PLEASE TYPE 'YES' OR 'NO'." 1980 GOTO 1930 1990 Z1=1 2000 GOTO 2020 2010 Z1=2 2020 RETURN 2030 END ================================================ FILE: 00_Alternate_Languages/18_Bullseye/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/18_Bullseye/MiniScript/bullseye.ms ================================================ 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." ================================================ FILE: 00_Alternate_Languages/18_Bullseye/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/18_Bullseye/bullseye.bas ================================================ 5 PRINT TAB(32);"BULLSEYE" 10 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 20 PRINT:PRINT:PRINT 30 PRINT "IN THIS GAME, UP TO 20 PLAYERS THROW DARTS AT A TARGET" 40 PRINT "WITH 10, 20, 30, AND 40 POINT ZONES. THE OBJECTIVE IS" 50 PRINT "TO GET 200 POINTS.": PRINT 60 PRINT "THROW",TAB(20);"DESCRIPTION";TAB(45);"PROBABLE SCORE" 70 PRINT" 1";TAB(20);"FAST OVERARM";TAB(45);"BULLSEYE OR COMPLETE MISS" 80 PRINT" 2";TAB(20);"CONTROLLED OVERARM";TAB(45);"10, 20 OR 30 POINTS" 90 PRINT" 3";TAB(20);"UNDERARM";TAB(45);"ANYTHING":PRINT 100 DIM A$(20),S(20),W(10): M=0: R=0: FOR I=1 TO 20: S(I)=0: NEXT I 110 INPUT "HOW MANY PLAYERS";N: PRINT 120 FOR I=1 TO N 130 PRINT "NAME OF PLAYER #";I;:INPUT A$(I) 140 NEXT I 150 R=R+1: PRINT: PRINT "ROUND";R:PRINT "---------" 160 FOR I=1 TO N 170 PRINT: PRINT A$(I)"'S THROW";: INPUT T 180 IF T<1 OR T>3 THEN PRINT "INPUT 1, 2, OR 3!": GOTO 170 190 ON T GOTO 200, 210, 200 200 P1=.65: P2=.55: P3=.5: P4=.5: GOTO 230 210 P1=.99: P2=.77: P3=.43: P4=.01: GOTO 230 220 P1=.95: P2=.75: P3=.45: P4=.05 230 U=RND(1) 240 IF U>=P1 THEN PRINT "BULLSEYE!! 40 POINTS!":B=40: GOTO 290 250 IF U>=P2 THEN PRINT "30-POINT ZONE!":B=30: GOTO 290 260 IF U>=P3 THEN PRINT "20-POINT ZONE":B=20: GOTO 290 270 IF U>=P4 THEN PRINT "WHEW! 10 POINTS.":B=10: GOTO 290 280 PRINT "MISSED THE TARGET! TOO BAD.": B=0 290 S(I)=S(I)+B: PRINT "TOTAL SCORE =";S(I): NEXT I 300 FOR I=1 TO N 310 IF S(I)>=200 THEN M=M+1: W(M)=I 320 NEXT I 330 IF M=0 THEN 150 340 PRINT: PRINT "WE HAVE A WINNER!!": PRINT 350 FOR I=1 TO M: PRINT A$(W(I));" SCORED";S(W(I));"POINTS.": NEXT I 360 PRINT: PRINT "THANKS FOR THE GAME.": END ================================================ FILE: 00_Alternate_Languages/19_Bunny/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/19_Bunny/MiniScript/bunny.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/19_Bunny/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/19_Bunny/bunny.bas ================================================ 10 PRINT TAB(33);"BUNNY" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 30 PRINT: PRINT: PRINT 100 REM "BUNNY" FROM AHL'S 'BASIC COMPUTER GAMES' 110 REM 120 FOR I=0 TO 4: READ B(I): NEXT I 130 GOSUB 260 140 L=64: REM ASCII LETTER CODE... 150 REM 160 PRINT 170 READ X: IF X<0 THEN 160 175 IF X>128 THEN 240 180 PRINT TAB(X);: READ Y 190 FOR I=X TO Y: J=I-5*INT(I/5) 200 PRINT CHR$(L+B(J)); 210 NEXT I 220 GOTO 170 230 REM 240 GOSUB 260: GOTO 450 250 REM 260 FOR I=1 TO 6: PRINT CHR$(10);: NEXT I 270 RETURN 280 REM 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 440 REM 450 END ================================================ FILE: 00_Alternate_Languages/20_Buzzword/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/20_Buzzword/MiniScript/buzzword.ms ================================================ 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!" ================================================ FILE: 00_Alternate_Languages/20_Buzzword/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/20_Buzzword/buzzword.bas ================================================ 10 PRINT TAB(26);"BUZZWORD GENERATOR" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 30 PRINT:PRINT:PRINT 40 PRINT "THIS PROGRAM PRINTS HIGHLY ACCEPTABLE PHRASES IN" 50 PRINT "'EDUCATOR-SPEAK' THAT YOU CAN WORK INTO REPORTS" 60 PRINT "AND SPEECHES. WHENEVER A QUESTION MARK IS PRINTED," 70 PRINT "TYPE A 'Y' FOR ANOTHER PHRASE OR 'N' TO QUIT." 80 PRINT:PRINT:PRINT "HERE'S THE FIRST PHRASE:" 90 DIM A$(40) 100 FOR I=1 TO 39 : READ A$(I) : NEXT I 110 PRINT A$(INT(13*RND(1)+1));" "; 120 PRINT A$(INT(13*RND(1)+14));" "; 130 PRINT A$(INT(13*RND(1)+27)) : PRINT 150 INPUT Y$ : IF Y$="Y" THEN 110 160 GOTO 999 200 DATA "ABILITY","BASAL","BEHAVIORAL","CHILD-CENTERED" 210 DATA "DIFFERENTIATED","DISCOVERY","FLEXIBLE","HETEROGENEOUS" 220 DATA "HOMOGENEOUS","MANIPULATIVE","MODULAR","TAVISTOCK" 230 DATA "INDIVIDUALIZED","LEARNING","EVALUATIVE","OBJECTIVE" 240 DATA "COGNITIVE","ENRICHMENT","SCHEDULING","HUMANISTIC" 250 DATA "INTEGRATED","NON-GRADED","TRAINING","VERTICAL AGE" 260 DATA "MOTIVATIONAL","CREATIVE","GROUPING","MODIFICATION" 270 DATA "ACCOUNTABILITY","PROCESS","CORE CURRICULUM","ALGORITHM" 280 DATA "PERFORMANCE","REINFORCEMENT","OPEN CLASSROOM","RESOURCE" 290 DATA "STRUCTURE","FACILITY","ENVIRONMENT" 999 PRINT "COME BACK WHEN YOU NEED HELP WITH ANOTHER REPORT!":END ================================================ FILE: 00_Alternate_Languages/20_Buzzword/go/main.go ================================================ package main import ( "bufio" "fmt" "math/rand" "os" "strings" "time" ) func main() { rand.Seed(time.Now().UnixNano()) words := [][]string{ { "Ability", "Basal", "Behavioral", "Child-centered", "Differentiated", "Discovery", "Flexible", "Heterogeneous", "Homogenous", "Manipulative", "Modular", "Tavistock", "Individualized", }, { "learning", "evaluative", "objective", "cognitive", "enrichment", "scheduling", "humanistic", "integrated", "non-graded", "training", "vertical age", "motivational", "creative", }, { "grouping", "modification", "accountability", "process", "core curriculum", "algorithm", "performance", "reinforcement", "open classroom", "resource", "structure", "facility", "environment", }, } scanner := bufio.NewScanner(os.Stdin) // Display intro text fmt.Println("\n Buzzword Generator") fmt.Println("Creative Computing Morristown, New Jersey") fmt.Println("\n\n") fmt.Println("This program prints highly acceptable phrases in") fmt.Println("'educator-speak' that you can work into reports") fmt.Println("and speeches. Whenever a question mark is printed,") fmt.Println("type a 'Y' for another phrase or 'N' to quit.") fmt.Println("\n\nHere's the first phrase:") for { phrase := "" for _, section := range words { if len(phrase) > 0 { phrase += " " } phrase += section[rand.Intn(len(section))] } fmt.Println(phrase) fmt.Println() // continue? fmt.Println("?") scanner.Scan() if strings.ToUpper(scanner.Text())[0:1] != "Y" { break } } fmt.Println("Come back when you need help with another report!") } ================================================ FILE: 00_Alternate_Languages/20_Buzzword/nim/buzzword.nim ================================================ 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!" ================================================ FILE: 00_Alternate_Languages/21_Calendar/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/21_Calendar/MiniScript/calendar.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/21_Calendar/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/21_Calendar/calendar.bas ================================================ 10 PRINT TAB(32);"CALENDAR" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 30 PRINT:PRINT:PRINT 100 REM VALUES FOR 1979 - SEE NOTES 110 DIM M(12) 120 FOR I=1 TO 6: PRINT CHR$(10);: NEXT I 130 D=-1: REM 1979 STARTS ON MONDAY (0=SUN, -1=MON, -2=TUES...) 140 S=0 150 REM READ DAYS OF EACH MONTH 160 FOR N=0 TO 12: READ M(N): NEXT N 170 REM 180 FOR N=1 TO 12 190 PRINT: PRINT: S=S+M(N-1) 200 PRINT "**";S;TAB(7); 210 FOR I=1 TO 18: PRINT "*";: NEXT I 220 ON N GOTO 230,240,250,260,270,280,290,300,310,320,330,340 230 PRINT " JANUARY ";: GOTO 350 240 PRINT " FEBRUARY";: GOTO 350 250 PRINT " MARCH ";: GOTO 350 260 PRINT " APRIL ";: GOTO 350 270 PRINT " MAY ";: GOTO 350 280 PRINT " JUNE ";: GOTO 350 290 PRINT " JULY ";: GOTO 350 300 PRINT " AUGUST ";: GOTO 350 310 PRINT "SEPTEMBER";: GOTO 350 320 PRINT " OCTOBER ";: GOTO 350 330 PRINT " NOVEMBER";: GOTO 350 340 PRINT " DECEMBER"; 350 FOR I=1 TO 18: PRINT "*";: NEXT I 360 PRINT 365-S;"**"; 370 REM 366-S; ON LEAP YEARS 380 PRINT CHR$(10): PRINT " S M T W"; 390 PRINT " T F S" 400 PRINT 410 FOR I=1 TO 59: PRINT "*";: NEXT I 420 REM 430 FOR W=1 TO 6 440 PRINT CHR$(10) 450 PRINT TAB(4) 460 REM 470 FOR G=1 TO 7 480 D=D+1 490 D2=D-S 500 IF D2>M(N) THEN 580 510 IF D2>0 THEN PRINT D2; 520 PRINT TAB(4+8*G); 530 NEXT G 540 REM 550 IF D2=M(N) THEN 590 560 NEXT W 570 REM 580 D=D-G 590 NEXT N 600 REM 610 FOR I=1 TO 6: PRINT CHR$(10);: NEXT I 620 DATA 0,31,28,31,30,31,30,31,31,30,31,30,31 630 REM 0,31,29, ..., ON LEAP YEARS 640 END ================================================ FILE: 00_Alternate_Languages/22_Change/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/22_Change/MiniScript/change.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/22_Change/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/22_Change/change.bas ================================================ 2 PRINT TAB(33);"CHANGE" 4 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 5 PRINT:PRINT:PRINT 6 PRINT "I, YOUR FRIENDLY MICROCOMPUTER, WILL DETERMINE" 8 PRINT "THE CORRECT CHANGE FOR ITEMS COSTING UP TO $100." 9 PRINT:PRINT 10 PRINT "COST OF ITEM";:INPUT A:PRINT "AMOUNT OF PAYMENT";:INPUT P 20 C=P-A:M=C:IF C<>0 THEN 90 25 PRINT "CORRECT AMOUNT, THANK YOU." 30 GOTO 400 90 IF C>0 THEN 120 95 PRINT "SORRY, YOU HAVE SHORT-CHANGED ME $";A-P 100 GOTO 10 120 PRINT "YOUR CHANGE, $";C 130 D=INT(C/10) 140 IF D=0 THEN 155 150 PRINT D;"TEN DOLLAR BILL(S)" 155 C=M-(D*10) 160 E=INT(C/5) 170 IF E=0 THEN 185 180 PRINT E;"FIVE DOLLARS BILL(S)" 185 C=M-(D*10+E*5) 190 F=INT(C) 200 IF F=0 THEN 215 210 PRINT F;"ONE DOLLAR BILL(S)" 215 C=M-(D*10+E*5+F) 220 C=C*100 225 N=C 230 G=INT(C/50) 240 IF G=0 THEN 255 250 PRINT G;"ONE HALF DOLLAR(S)" 255 C=N-(G*50) 260 H=INT(C/25) 270 IF H=0 THEN 285 280 PRINT H;"QUARTER(S)" 285 C=N-(G*50+H*25) 290 I=INT(C/10) 300 IF I=0 THEN 315 310 PRINT I;"DIME(S)" 315 C=N-(G*50+H*25+I*10) 320 J=INT(C/5) 330 IF J=0 THEN 345 340 PRINT J;"NICKEL(S)" 345 C=N-(G*50+H*25+I*10+J*5) 350 K=INT(C+.5) 360 IF K=0 THEN 380 370 PRINT K;"PENNY(S)" 380 PRINT "THANK YOU, COME AGAIN." 390 PRINT:PRINT 400 GOTO 10 410 END ================================================ FILE: 00_Alternate_Languages/22_Change/go/main.go ================================================ package main import ( "bufio" "fmt" "math" "os" "strconv" ) func printWelcome() { fmt.Println(" CHANGE") fmt.Println("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") fmt.Println() fmt.Println() fmt.Println() fmt.Println("I, YOUR FRIENDLY MICROCOMPUTER, WILL DETERMINE") fmt.Println("THE CORRECT CHANGE FOR ITEMS COSTING UP TO $100.") fmt.Println() } func computeChange(cost, payment float64) { change := int(math.Round((payment - cost) * 100)) if change == 0 { fmt.Println("\nCORRECT AMOUNT, THANK YOU.") return } if change < 0 { fmt.Printf("\nSORRY, YOU HAVE SHORT-CHANGED ME $%0.2f\n", float64(change)/-100.0) print() return } fmt.Printf("\nYOUR CHANGE, $%0.2f:\n", float64(change)/100.0) d := change / 1000 if d > 0 { fmt.Printf(" %d TEN DOLLAR BILL(S)\n", d) change -= d * 1000 } d = change / 500 if d > 0 { fmt.Printf(" %d FIVE DOLLAR BILL(S)\n", d) change -= d * 500 } d = change / 100 if d > 0 { fmt.Printf(" %d ONE DOLLAR BILL(S)\n", d) change -= d * 100 } d = change / 50 if d > 0 { fmt.Println(" 1 HALF DOLLAR") change -= d * 50 } d = change / 25 if d > 0 { fmt.Printf(" %d QUARTER(S)\n", d) change -= d * 25 } d = change / 10 if d > 0 { fmt.Printf(" %d DIME(S)\n", d) change -= d * 10 } d = change / 5 if d > 0 { fmt.Printf(" %d NICKEL(S)\n", d) change -= d * 5 } if change > 0 { fmt.Printf(" %d PENNY(S)\n", change) } } func main() { scanner := bufio.NewScanner(os.Stdin) printWelcome() var cost, payment float64 var err error for { fmt.Println("COST OF ITEM?") scanner.Scan() cost, err = strconv.ParseFloat(scanner.Text(), 64) if err != nil || cost < 0.0 { fmt.Println("INVALID INPUT. TRY AGAIN.") continue } break } for { fmt.Println("\nAMOUNT OF PAYMENT?") scanner.Scan() payment, err = strconv.ParseFloat(scanner.Text(), 64) if err != nil { fmt.Println("INVALID INPUT. TRY AGAIN.") continue } break } computeChange(cost, payment) fmt.Println() } ================================================ FILE: 00_Alternate_Languages/23_Checkers/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/23_Checkers/MiniScript/checkers.ms ================================================ 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." ================================================ FILE: 00_Alternate_Languages/23_Checkers/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/23_Checkers/checkers.annotated.bas ================================================ # Annotated version of CHECKERS.BAS, modified to improve readability. # # I've made the following changes: # # 1. Added many comments and blank lines. # 2. Separated each statement into its own line. # 3. Indented loops, conditionals and subroutines. # 4. Turned *SOME* conditionals and loops into # structured-BASIC-style if/endif and loop/endloop blocks. # 5. Switched to using '#' to delimit comments. # 6. Subroutines now begin with "Sub_Start" # 7. All non-string text has been converted to lower-case # 8. All line numbers that are not jump destinations have been removed. # # This has helped me make sense of the code. I hope it will also help you. # # Print the banner print tab(32);"CHECKERS" print tab(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 # Declare the "globals": # The current move: (rating, current x, current y, new x, new y) # 'rating' represents how good the move is; higher is better. dim r(4) r(0)=-99 # 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. dim s(7,7) g=-1 # constant holding -1 # Initialize the board. Data is 2 length-wise strips repeated. data 1,0,1,0,0,0,-1,0,0,1,0,0,0,-1,0,-1,15 for x=0 to 7 for y=0 to 7 read j if j=15 then 180 s(x,y)=j goto 200 180 restore read s(x,y) 200 next y,x 230 # Start of game loop. First, my turn. # 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 x=0 to 7 for y=0 to 7 # Skip if this is empty or an opponent's piece if s(x,y) > -1 then 350 # If this is one of my ordinary pieces, analyze possible # forward moves. if s(x,y) = -1 then for a=-1 to 1 step 2 b=g gosub 650 next a endif # If this is one of my kings, analyze possible forward # and backward moves. if s(x,y) = -2 then for a=-1 to 1 step 2 for b=-1 to 1 step 2 gosub 650 next b,a endif 350 next y,x goto 1140 # Skip the subs # Analyze a move from (x,y) to (x+a, y+b) and schedule it if it's # the best candidate so far. 650 Sub_Start u=x+a v=y+b # Done if it's off the board if u<0 or u>7 or v<0 or v>7 then 870 # Consider the destination if it's empty if s(u,v) = 0 then gosub 910 goto 870 endif # If it's got an opponent's piece, jump it instead if s(u,v) > 0 # Restore u and v, then return if it's off the board u=u+a v=v+b if u<0 or v<0 or u>7 or v>7 then 870 # Otherwise, consider u,v if s(u,v)=0 then gosub 910 endif 870 return # 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 array 'r'. 910 Sub_Start # q is the score; it starts at 0 # +2 if it promotes this piece if v=0 and s(x,y)=-1 then q=q+2 # +5 if it takes an opponent's piece if abs(y-v)=2 then q=q+5 # -2 if the piece is moving away from the top boundary if y=7 then q=q-2 # +1 for putting the piece against a vertical boundary if u=0 or u=7 then q=q+1 for c=-1 to 1 step 2 if u+c < 0 or u+c > 7 or v+g < 0 then 1080 # +1 for each adjacent friendly piece if s(u+c, v+g) < 0 then q=q+1 goto 1080 endif # Prevent out-of-bounds testing if u-c < 0 or u-c > 7 or v-g > 7 then 1080 # -2 for each opponent piece that can now take this piece here if s(u+c,v+g) > 0 and(s(u-c,v-g)=0 or(u-c=x and v-g=y))then q=q-2 1080 next c # Use this move if it's better than the previous best if q>r(0) then r(0)=q r(1)=x r(2)=y r(3)=u r(4)=v endif q=0 # reset the score return 1140 if r(0)=-99 then 1880 # Game is lost if no move could be found. # Print the computer's move. (Note: chr$(30) is an ASCII RS # (record separator) code; probably no longer relevant.) print chr$(30)"FROM"r(1);r(2)"TO"r(3);r(4); r(0)=-99 # Make the computer's move. If the piece finds its way to the # end of the board, crown it. 1240 if r(4)=0 then s(r(3),r(4))=-2 goto 1420 endif s(r(3),r(4))=s(r(1),r(2)) s(r(1),r(2))=0 # If the piece has jumped 2 squares, it means the computer has # taken an opponents' piece. if abs(r(1)-r(3)) == 2 then s((r(1)+r(3))/2,(r(2)+r(4))/2)=0 # Delete the opponent's piece # See if we can jump again. Evaluate all possible moves. x=r(3) y=r(4) for a=-2 to 2 step 4 if s(x,y)=-1 then b=-2 gosub 1370 endif if s(x,y)=-2 then for b=-2 to 2 step 4 gosub 1370 next b endif next a # If we've found a move, go back and make that one as well if r(0) <> -99 then print "TO" r(3); r(4); r(0)=-99 goto 1240 endif goto 1420 # Skip the sub # If (u,v) is in the bounds, evaluate it as a move using # the sub at 910 1370 Sub_Start u=x+a v=y+b if u<0 or u>7 or v<0 or v>7 then 1400 if s(u,v)=0 and s(x+a/2,y+b/2)>0 then gosub 910 1400 return 1420 endif # Now, print the board print print print for y=7 to 0 step-1 for x=0 to 7 i=5*x print tab(i); if s(x,y)=0 then print"."; if s(x,y)=1 then print"O"; if s(x,y)=-1 then print"X"; if s(x,y)=-2 then print"X*"; if s(x,y)=2 then print"O*"; next x print" " print next y print # Check if either player is out of pieces. If so, announce the # winner. for l=0 to 7 for m=0 to 7 if s(l,m)=1 or s(l,m)=2 then z=1 if s(l,m)=-1 or s(l,m)=-2 then t=1 next m next l if z<>1 then 1885 if t<>1 then 1880 # Prompt the player for their move. z=0 t=0 1590 input "FROM";e,h x=e y=h if s(x,y)<=0 then 1590 1670 input "TO";a,b x=a y=b if s(x,y)=0 and abs(a-e)<=2 and abs(a-e)=abs(b-h)then 1700 print chr$(7)chr$(11); # bell, vertical tab; invalid move goto 1670 1700 i=46 # Not used; probably a bug 1750 loop # Make the move and stop unless it might be a jump. s(a,b) = s(e,h) s(e,h) = 0 if abs(e-a) <> 2 then break # Remove the piece jumped over s((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. 1802 input "+TO";a1,b1 if a1 < 0 then break if s(a1,b1) <> 0 or abs(a1-a) <>2 or abs(b1-b) <> 2 then 1802 # Update the move variables to correspond to the next jump e=a h=b a=a1 b=b1 i=i+15 # Not used; probably a bug endloop # If the player has reached the end of the board, crown this piece 1810 if b=7 then s(a,b)=2 # And play the next turn. goto 230 # Endgame: 1880 print print "YOU WIN." end 1885 print print "I WIN." end ================================================ FILE: 00_Alternate_Languages/23_Checkers/checkers.bas ================================================ 5 PRINT TAB(32);"CHECKERS" 10 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 15 PRINT:PRINT:PRINT 20 PRINT "THIS IS THE GAME OF CHECKERS. THE COMPUTER IS X," 25 PRINT "AND YOU ARE O. THE COMPUTER WILL MOVE FIRST." 30 PRINT "SQUARES ARE REFERRED TO BY A COORDINATE SYSTEM." 35 PRINT "(0,0) IS THE LOWER LEFT CORNER" 40 PRINT "(0,7) IS THE UPPER LEFT CORNER" 45 PRINT "(7,0) IS THE LOWER RIGHT CORNER" 50 PRINT "(7,7) IS THE UPPER RIGHT CORNER" 55 PRINT "THE COMPUTER WILL TYPE '+TO' WHEN YOU HAVE ANOTHER" 60 PRINT "JUMP. TYPE TWO NEGATIVE NUMBERS IF YOU CANNOT JUMP." 65 PRINT:PRINT:PRINT 80 DIM R(4),S(7,7):G=-1:R(0)=-99 90 DATA 1,0,1,0,0,0,-1,0,0,1,0,0,0,-1,0,-1,15 120 FOR X=0 TO 7:FOR Y=0 TO 7:READ J:IF J=15 THEN 180 160 S(X,Y)=J:GOTO 200 180 RESTORE:READ S(X,Y) 200 NEXT Y,X 230 FOR X=0 TO 7:FOR Y=0 TO 7:IF S(X,Y)>-1 THEN 350 310 IF S(X,Y)=-1 THEN FOR A=-1 TO 1 STEP 2:B=G:GOSUB 650:NEXT A 330 IF S(X,Y)=-2 THEN FOR A=-1 TO 1 STEP 2:FOR B=-1 TO 1 STEP 2:GOSUB 650:NEXT B,A 350 NEXT Y,X:GOTO 1140 650 U=X+A:V=Y+B:IF U<0 OR U>7 OR V<0 OR V>7 THEN 870 740 IF S(U,V)=0 THEN GOSUB 910:GOTO 870 770 IF S(U,V)<0 THEN 870 790 U=U+A:V=V+B:IF U<0 OR V<0 OR U>7 OR V>7 THEN 870 850 IF S(U,V)=0 THEN GOSUB 910 870 RETURN 910 IF V=0 AND S(X,Y)=-1 THEN Q=Q+2 920 IF ABS(Y-V)=2 THEN Q=Q+5 960 IF Y=7 THEN Q=Q-2 980 IF U=0 OR U=7 THEN Q=Q+1 1030 FOR C=-1 TO 1 STEP 2:IF U+C<0 OR U+C>7 OR V+G<0 THEN 1080 1035 IF S(U+C,V+G)<0 THEN Q=Q+1:GOTO 1080 1040 IF U-C<0 OR U-C>7 OR V-G>7 THEN 1080 1045 IF S(U+C,V+G)>0 AND(S(U-C,V-G)=0 OR(U-C=X AND V-G=Y))THEN Q=Q-2 1080 NEXT C:IF Q>R(0)THEN R(0)=Q:R(1)=X:R(2)=Y:R(3)=U:R(4)=V 1100 Q=0:RETURN 1140 IF R(0)=-99 THEN 1880 1230 PRINT CHR$(30)"FROM"R(1);R(2)"TO"R(3);R(4);:R(0)=-99 1240 IF R(4)=0 THEN S(R(3),R(4))=-2:GOTO 1420 1250 S(R(3),R(4))=S(R(1),R(2)) 1310 S(R(1),R(2))=0:IF ABS(R(1)-R(3))<>2 THEN 1420 1330 S((R(1)+R(3))/2,(R(2)+R(4))/2)=0 1340 X=R(3):Y=R(4):IF S(X,Y)=-1 THEN B=-2:FOR A=-2 TO 2 STEP 4:GOSUB 1370 1350 IF S(X,Y)=-2 THEN FOR A=-2 TO 2 STEP 4:FOR B=-2 TO 2 STEP 4:GOSUB 1370:NEXT B 1360 NEXT A:IF R(0)<>-99 THEN PRINT"TO"R(3);R(4);:R(0)=-99:GOTO 1240 1365 GOTO 1420 1370 U=X+A:V=Y+B:IF U<0 OR U>7 OR V<0 OR V>7 THEN 1400 1380 IF S(U,V)=0 AND S(X+A/2,Y+B/2)>0 THEN GOSUB 910 1400 RETURN 1420 PRINT:PRINT:PRINT:FOR Y=7 TO 0 STEP-1:FOR X=0 TO 7:I=5*X:PRINT TAB(I); 1430 IF S(X,Y)=0 THEN PRINT"."; 1470 IF S(X,Y)=1 THEN PRINT"O"; 1490 IF S(X,Y)=-1 THEN PRINT"X"; 1510 IF S(X,Y)=-2 THEN PRINT"X*"; 1530 IF S(X,Y)=2 THEN PRINT"O*"; 1550 NEXT X:PRINT" ":PRINT:NEXT Y:PRINT 1552 FOR L=0 TO 7 1554 FOR M=0 TO 7 1556 IF S(L,M)=1 OR S(L,M)=2 THEN Z=1 1558 IF S(L,M)=-1 OR S(L,M)=-2 THEN T=1 1560 NEXT M 1562 NEXT L 1564 IF Z<>1 THEN 1885 1566 IF T<>1 THEN 1880 1570 Z=0: T=0 1590 INPUT "FROM";E,H:X=E:Y=H:IF S(X,Y)<=0 THEN 1590 1670 INPUT "TO";A,B:X=A:Y=B 1680 IF S(X,Y)=0 AND ABS(A-E)<=2 AND ABS(A-E)=ABS(B-H)THEN 1700 1690 PRINT CHR$(7)CHR$(11);:GOTO 1670 1700 I=46 1750 S(A,B)=S(E,H):S(E,H)=0:IF ABS(E-A)<>2 THEN 1810 1800 S((E+A)/2,(H+B)/2)=0 1802 INPUT "+TO";A1,B1:IF A1<0 THEN 1810 1804 IF S(A1,B1)<>0 OR ABS(A1-A)<>2 OR ABS(B1-B)<>2 THEN 1802 1806 E=A:H=B:A=A1:B=B1:I=I+15:GOTO 1750 1810 IF B=7 THEN S(A,B)=2 1830 GOTO 230 1880 PRINT: PRINT "YOU WIN.": END 1885 PRINT: PRINT "I WIN.": END ================================================ FILE: 00_Alternate_Languages/24_Chemist/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/24_Chemist/MiniScript/chemist.ms ================================================ 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." ================================================ FILE: 00_Alternate_Languages/24_Chemist/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/24_Chemist/chemist.bas ================================================ 3 PRINT TAB(33);"CHEMIST" 6 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 8 PRINT:PRINT:PRINT 10 PRINT "THE FICTITIOUS CHEMICAL KRYPTOCYANIC ACID CAN ONLY BE" 20 PRINT "DILUTED BY THE RATIO OF 7 PARTS WATER TO 3 PARTS ACID." 30 PRINT "IF ANY OTHER RATIO IS ATTEMPTED, THE ACID BECOMES UNSTABLE" 40 PRINT "AND SOON EXPLODES. GIVEN THE AMOUNT OF ACID, YOU MUST" 50 PRINT "DECIDE WHO MUCH WATER TO ADD FOR DILUTION. IF YOU MISS" 60 PRINT "YOU FACE THE CONSEQUENCES." 100 A=INT(RND(1)*50) 110 W=7*A/3 120 PRINT A;"LITERS OF KRYPTOCYANIC ACID. HOW MUCH WATER"; 130 INPUT R 140 D=ABS(W-R) 150 IF D>W/20 THEN 200 160 PRINT " GOOD JOB! YOU MAY BREATHE NOW, BUT DON'T INHALE THE FUMES!" 170 PRINT 180 GOTO 100 200 PRINT " SIZZLE! YOU HAVE JUST BEEN DESALINATED INTO A BLOB" 210 PRINT " OF QUIVERING PROTOPLASM!" 220 T=T+1 230 IF T=9 THEN 260 240 PRINT " HOWEVER, YOU MAY TRY AGAIN WITH ANOTHER LIFE." 250 GOTO 100 260 PRINT " YOUR 9 LIVES ARE USED, BUT YOU WILL BE LONG REMEMBERED FOR" 270 PRINT " YOUR CONTRIBUTIONS TO THE FIELD OF COMIC BOOK CHEMISTRY." 280 END ================================================ FILE: 00_Alternate_Languages/25_Chief/C/chief.c ================================================ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> //check if windows or linux for the clear screen #ifdef _WIN32 #define CLEAR "cls" #else #define CLEAR "clear" #endif void show_solution(float guess); float guess_number(float number); void game(); float guess_number(float number){ float guess; guess = ((((number - 4) * 5) / 8) * 5 - 3); return guess; } void game(){ float number,guess; char answer[4]; printf("Think a number\n"); printf("Then add to it 3 and divide it by 5\n"); printf("Now multiply by 8, divide by 5 and then add 5\n"); printf("Finally substract 1\n"); printf("What is the number you got?(if you got decimals put them ex: 23.6): "); scanf("%f",&number); guess = guess_number(number); printf("The number you thought was %f am I right(Yes or No)?\n",guess); scanf("%s",answer); for(int i = 0; i < strlen(answer); i++){ answer[i] = tolower(answer[i]); } if(strcmp(answer,"yes") == 0){ printf("\nHuh, I Knew I was unbeatable"); printf("And here is how i did it:\n"); show_solution(guess); } else if (strcmp(answer,"no") == 0){ printf("HUH!! what was you original number?: "); scanf("%f",&number); if(number == guess){ printf("Huh, I Knew I was unbeatable"); printf("And here is how i did it:\n"); show_solution(guess); } else{ printf("If I got it wrong I guess you are smarter than me"); } } else{ system(CLEAR); printf("I don't understand what you said\n"); printf("Please answer with Yes or No\n"); game(); } } void show_solution(float guess){ printf("%f plus 3 is %f\n",guess,guess + 3); printf("%f divided by 5 is %f\n",guess + 3,(guess + 3) / 5); printf("%f multiplied by 8 is %f\n",(guess + 3) / 5,(guess + 3) / 5 * 8); printf("%f divided by 5 is %f\n",(guess + 3) / 5 * 8,(guess + 3) / 5 * 8 / 5); printf("%f plus 5 is %f\n",(guess + 3) / 5 * 8 / 5,(guess + 3) / 5 * 8 / 5 + 5); printf("%f minus 1 is %f\n",(guess + 3) / 5 * 8 / 5 + 5,(guess + 3) / 5 * 8 / 5 + 5 - 1); } void main(){ char answer[4]; printf("I am CHIEF NUMBERS FREEK, The GREAT INDIAN MATH GOD.\n"); printf("Are you ready to take the test you called me out for(Yes or No)? "); scanf("%s",answer); for(int i = 0; i < strlen(answer); i++){ answer[i] = tolower(answer[i]); } if(strcmp(answer,"yes") == 0){ game(); }else if (strcmp(answer,"no") == 0){ printf("You are a coward, I will not play with you.%d %s\n",strcmp(answer,"yes"),answer); } else{ system(CLEAR); printf("I don't understand what you said\n"); printf("Please answer with Yes or No\n"); main(); } } ================================================ FILE: 00_Alternate_Languages/25_Chief/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/25_Chief/MiniScript/chief.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/25_Chief/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/25_Chief/chief.bas ================================================ 2 PRINT TAB(30) "CHIEF" 4 PRINT TAB(15) "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 6 PRINT:PRINT:PRINT 10 PRINT "I AM CHIEF NUMBERS FREEK, THE GREAT INDIAN MATH GOD." 20 PRINT "ARE YOU READY TO TAKE THE TEST YOU CALLED ME OUT FOR"; 30 INPUT A$ 40 IF A$="YES" THEN 60 50 PRINT "SHUT UP, PALE FACE WITH WISE TONGUE." 60 PRINT " TAKE A NUMBER AND ADD 3. DIVIDE THIS NUMBER BY 5 AND" 70 PRINT "MULTIPLY BY 8. DIVIDE BY 5 AND ADD THE SAME. SUBTRACT 1." 80 PRINT " WHAT DO YOU HAVE"; 90 INPUT B 100 LET C = (B+1-5)*5/8*5-3 110 PRINT "I BET YOUR NUMBER WAS" C". AM I RIGHT"; 120 INPUT D$ 130 IF D$="YES" THEN 500 140 PRINT "WHAT WAS YOUR ORIGINAL NUMBER"; 150 INPUT K 155 LET F=K+3 160 LET G=F/5 170 LET H=G*8 180 LET I=H/5+5 190 LET J=I-1 200 PRINT "SO YOU THINK YOU'RE SO SMART, EH?" 210 PRINT "NOW WATCH." 230 PRINT K"PLUS 3 EQUALS"F". THIS DIVIDED BY 5 EQUALS"G";" 240 PRINT "THIS TIMES 8 EQUALS"H". IF WE DIVIDE BY 5 AND ADD 5," 250 PRINT "WE GET"I", WHICH, MINUS 1, EQUALS"J"." 260 PRINT "NOW DO YOU BELIEVE ME"; 270 INPUT Z$ 290 IF Z$="YES" THEN 500 295 PRINT "YOU HAVE MADE ME MAD!!!" 300 PRINT "THERE MUST BE A GREAT LIGHTNING BOLT!" 310 PRINT:PRINT 330 FOR X=30 TO 22 STEP -1 340 PRINT TAB(X) "X X" 350 NEXT X 360 PRINT TAB(21) "X XXX" 370 PRINT TAB(20) "X X" 380 PRINT TAB(19) "XX X" 390 FOR Y=20 TO 13 STEP -1 400 PRINT TAB(Y) "X X" 410 NEXT Y 420 PRINT TAB(12) "XX" 430 PRINT TAB(11) "X" 440 PRINT TAB(10) "*" 450 PRINT:PRINT"#########################":PRINT 470 PRINT "I HOPE YOU BELIEVE ME NOW, FOR YOUR SAKE!!" 480 GOTO 520 500 PRINT "BYE!!!" 520 END ================================================ FILE: 00_Alternate_Languages/25_Chief/go/main.go ================================================ package main import ( "bufio" "fmt" "os" "strconv" "strings" ) func printLightning() { fmt.Println("************************************") n := 24 for n > 16 { var b strings.Builder b.Grow(n + 3) for i := 0; i < n; i++ { b.WriteString(" ") } b.WriteString("x x") fmt.Println(b.String()) n-- } fmt.Println(" x xxx") fmt.Println(" x x") fmt.Println(" xx xx") n-- for n > 8 { var b strings.Builder b.Grow(n + 3) for i := 0; i < n; i++ { b.WriteString(" ") } b.WriteString("x x") fmt.Println(b.String()) n-- } fmt.Println(" xx") fmt.Println(" x") fmt.Println("************************************") } func printSolution(n float64) { fmt.Printf("\n%f plus 3 gives %f. This divided by 5 equals %f\n", n, n+3, (n+3)/5) fmt.Printf("This times 8 gives %f. If we divide 5 and add 5.\n", ((n+3)/5)*8) fmt.Printf("We get %f, which, minus 1 equals %f\n", (((n+3)/5)*8)/5+5, ((((n+3)/5)*8)/5+5)-1) } func play() { fmt.Println("\nTake a Number and ADD 3. Now, Divide this number by 5 and") fmt.Println("multiply by 8. Now, Divide by 5 and add the same. Subtract 1") youHave := getFloat("\nWhat do you have?") compGuess := (((youHave-4)*5)/8)*5 - 3 if getYesNo(fmt.Sprintf("\nI bet your number was %f was I right(Yes or No)? ", compGuess)) { fmt.Println("\nHuh, I knew I was unbeatable") fmt.Println("And here is how i did it") printSolution(compGuess) } else { originalNumber := getFloat("\nHUH!! what was you original number? ") if originalNumber == compGuess { fmt.Println("\nThat was my guess, AHA i was right") fmt.Println("Shamed to accept defeat i guess, don't worry you can master mathematics too") fmt.Println("Here is how i did it") printSolution(compGuess) } else { fmt.Println("\nSo you think you're so smart, EH?") fmt.Println("Now, Watch") printSolution(originalNumber) if getYesNo("\nNow do you believe me? ") { print("\nOk, Lets play again sometime bye!!!!") } else { fmt.Println("\nYOU HAVE MADE ME VERY MAD!!!!!") fmt.Println("BY THE WRATH OF THE MATHEMATICS AND THE RAGE OF THE GODS") fmt.Println("THERE SHALL BE LIGHTNING!!!!!!!") printLightning() fmt.Println("\nI Hope you believe me now, for your own sake") } } } } func getFloat(prompt string) float64 { scanner := bufio.NewScanner(os.Stdin) for { fmt.Println(prompt) scanner.Scan() val, err := strconv.ParseFloat(scanner.Text(), 64) if err != nil { fmt.Println("INVALID INPUT, TRY AGAIN") continue } return val } } func getYesNo(prompt string) bool { scanner := bufio.NewScanner(os.Stdin) fmt.Println(prompt) scanner.Scan() return (strings.ToUpper(scanner.Text())[0:1] == "Y") } func main() { fmt.Println("I am CHIEF NUMBERS FREEK, The GREAT INDIAN MATH GOD.") if getYesNo("\nAre you ready to take the test you called me out for(Yes or No)? ") { play() } else { fmt.Println("Ok, Nevermind. Let me go back to my great slumber, Bye") } } ================================================ FILE: 00_Alternate_Languages/26_Chomp/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/26_Chomp/MiniScript/chomp.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/26_Chomp/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/26_Chomp/chomp.bas ================================================ 10 PRINT TAB(33);"CHOMP" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 30 PRINT:PRINT:PRINT 40 DIM A(10,10) 100 REM *** THE GAME OF CHOMP *** COPYRIGHT PCC 1973 *** 110 PRINT 120 PRINT "THIS IS THE GAME OF CHOMP (SCIENTIFIC AMERICAN, JAN 1973)" 130 PRINT "DO YOU WANT THE RULES (1=YES, 0=NO!)"; 140 INPUT R 150 IF R=0 THEN 340 160 F=1 170 R=5 180 C=7 190 PRINT "CHOMP IS FOR 1 OR MORE PLAYERS (HUMANS ONLY)." 200 PRINT 210 PRINT "HERE'S HOW A BOARD LOOKS (THIS ONE IS 5 BY 7):" 220 GOSUB 540 230 PRINT 240 PRINT "THE BOARD IS A BIG COOKIE - R ROWS HIGH AND C COLUMNS" 250 PRINT "WIDE. YOU INPUT R AND C AT THE START. IN THE UPPER LEFT" 260 PRINT "CORNER OF THE COOKIE IS A POISON SQUARE (P). THE ONE WHO" 270 PRINT "CHOMPS THE POISON SQUARE LOSES. TO TAKE A CHOMP, TYPE THE" 280 PRINT "ROW AND COLUMN OF ONE OF THE SQUARES ON THE COOKIE." 290 PRINT "ALL OF THE SQUARES BELOW AND TO THE RIGHT OF THAT SQUARE" 300 PRINT "(INCLUDING THAT SQUARE, TOO) DISAPPEAR -- CHOMP!!" 310 PRINT "NO FAIR CHOMPING SQUARES THAT HAVE ALREADY BEEN CHOMPED," 320 PRINT "OR THAT ARE OUTSIDE THE ORIGINAL DIMENSIONS OF THE COOKIE." 330 PRINT 340 PRINT "HERE WE GO..." 350 REM 360 F=0 370 FOR I=1 TO 10 372 FOR J=1 TO 10 375 A(I,J)=0 377 NEXT J 379 NEXT I 380 PRINT 390 PRINT "HOW MANY PLAYERS"; 400 INPUT P 410 I1=0 420 PRINT "HOW MANY ROWS"; 430 INPUT R 440 IF R <= 9 THEN 470 450 PRINT "TOO MANY ROWS (9 IS MAXIMUM). NOW, "; 460 GOTO 420 470 PRINT "HOW MANY COLUMNS"; 480 INPUT C 490 IF C <= 9 THEN 530 500 PRINT "TOO MANY COLUMNS (9 IS MAXIMUM). NOW, "; 510 GOTO 470 530 PRINT 540 FOR I=1 TO R 550 FOR J=1 TO C 560 A(I,J)=1 570 NEXT J 580 NEXT I 590 A(1,1)=-1 600 REM PRINT THE BOARD 610 PRINT 620 PRINT TAB(7);"1 2 3 4 5 6 7 8 9" 630 FOR I=1 TO R 640 PRINT I;TAB(7); 650 FOR J=1 TO C 660 IF A(I,J)=-1 THEN 700 670 IF A(I,J)=0 THEN 720 680 PRINT "* "; 690 GOTO 710 700 PRINT "P "; 710 NEXT J 720 PRINT 730 NEXT I 740 PRINT 750 IF F=0 THEN 770 760 RETURN 770 REM GET CHOMPS FOR EACH PLAYER IN TURN 780 LET I1=I1+1 790 LET P1=I1-INT(I1/P)*P 800 IF P1 <> 0 THEN 820 810 P1=P 820 PRINT "PLAYER";P1 830 PRINT "COORDINATES OF CHOMP (ROW,COLUMN)"; 840 INPUT R1,C1 850 IF R1<1 THEN 920 860 IF R1>R THEN 920 870 IF C1<1 THEN 920 880 IF C1>C THEN 920 890 IF A(R1,C1)=0 THEN 920 900 IF A(R1,C1)=-1 THEN 1010 910 GOTO 940 920 PRINT "NO FAIR. YOU'RE TRYING TO CHOMP ON EMPTY SPACE!" 930 GOTO 820 940 FOR I=R1 TO R 950 FOR J=C1 TO C 960 A(I,J)=0 970 NEXT J 980 NEXT I 990 GOTO 610 1000 REM END OF GAME DETECTED IN LINE 900 1010 PRINT "YOU LOSE, PLAYER";P1 1020 PRINT 1030 PRINT "AGAIN (1=YES, 0=NO!)"; 1040 INPUT R 1050 IF R=1 THEN 340 1060 END ================================================ FILE: 00_Alternate_Languages/27_Civil_War/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/27_Civil_War/MiniScript/civilwar.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/27_Civil_War/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/27_Civil_War/civilwar.bas ================================================ 2 PRINT TAB(26) "CIVIL WAR" 4 PRINT TAB(15) "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 6 PRINT : PRINT : PRINT 20 REM ORIGINAL GAME DESIGN: CRAM, GOODIE, HIBBARD LEXINGTON H.S. 30 REM MODIFICATIONS: G. PAUL, R. HESS (TIES), 1973 50 DIM S(4),C$(14),M1(14),M2(14),C1(14),C2(14),M(14) 60 REM UNION INFO ON LIKELY CONFEDERATE STRATEGY 70 S(1)=25 : S(2)=25 : S(3)=25 : S(4)=25 82 REM READ HISTORICAL DATA. 84 FOR D=1 TO 14 86 READ C$(D),M1(D),M2(D),C1(D),C2(D),M(D) 88 NEXT D 89 LET D=RND(-1) 90 PRINT 100 PRINT "DO YOU WANT INSTRUCTIONS"; 110 INPUT X$ 120 IF X$="YES" THEN 160 130 IF X$="NO" THEN 370 140 PRINT "YES OR NO -- "; 150 GOTO 110 160 PRINT : PRINT : PRINT : PRINT 170 PRINT "THIS IS A CIVIL WAR SIMULATION." 180 PRINT "TO PLAY TYPE A RESPONSE WHEN THE COMPUTER ASKS." 190 PRINT "REMEMBER THAT ALL FACTORS ARE INTERRELATED AND THAT YOUR" 200 PRINT "RESPONSES COULD CHANGE HISTORY. FACTS AND FIGURES USED ARE" 210 PRINT "BASED ON THE ACTUAL OCCURRENCE. MOST BATTLES TEND TO RESULT" 220 PRINT "AS THEY DID IN THE CIVIL WAR, BUT IT ALL DEPENDS ON YOU!!" 230 PRINT 240 PRINT "THE OBJECT OF THE GAME IS TO WIN AS MANY BATTLES AS "; 245 PRINT "POSSIBLE." 250 PRINT 260 PRINT "YOUR CHOICES FOR DEFENSIVE STRATEGY ARE:" 270 PRINT " (1) ARTILLERY ATTACK" 280 PRINT " (2) FORTIFICATION AGAINST FRONTAL ATTACK" 290 PRINT " (3) FORTIFICATION AGAINST FLANKING MANEUVERS" 300 PRINT " (4) FALLING BACK" 310 PRINT " YOUR CHOICES FOR OFFENSIVE STRATEGY ARE:" 320 PRINT " (1) ARTILLERY ATTACK" 330 PRINT " (2) FRONTAL ATTACK" 340 PRINT " (3) FLANKING MANEUVERS" 350 PRINT " (4) ENCIRCLEMENT" 360 PRINT "YOU MAY SURRENDER BY TYPING A '5' FOR YOUR STRATEGY." 370 PRINT : PRINT : PRINT : PRINT "ARE THERE TWO GENERALS PRESENT "; 380 PRINT "(ANSWER YES OR NO)"; 390 INPUT B$ 400 IF B$="YES" THEN 430 410 IF B$ <> "NO" THEN 380 420 PRINT : PRINT "YOU ARE THE CONFEDERACY. GOOD LUCK!" 425 PRINT 430 LET D=1 440 IF B$ <> "YES" THEN 460 450 LET D=2 460 PRINT "SELECT A BATTLE BY TYPING A NUMBER FROM 1 TO 14 ON" 470 PRINT "REQUEST. TYPE ANY OTHER NUMBER TO END THE SIMULATION." 480 PRINT "BUT '0' BRINGS BACK EXACT PREVIOUS BATTLE SITUATION" 490 PRINT "ALLOWING YOU TO REPLAY IT" 500 PRINT 510 PRINT "NOTE: A NEGATIVE FOOD$ ENTRY CAUSES THE PROGRAM TO " 520 PRINT "USE THE ENTRIES FROM THE PREVIOUS BATTLE" 530 PRINT 540 PRINT "AFTER REQUESTING A BATTLE, DO YOU WISH "; 550 PRINT "BATTLE DESCRIPTIONS "; 560 PRINT "(ANSWER YES OR NO)"; 570 INPUT X$ 580 IF X$="YES" THEN 600 590 IF X$ <> "NO" THEN 560 600 L=0:W=0:R1=0:Q1=0:M3=0:M4=0:P1=0:P2=0:T1=0:T2=0 610 F(2)=0:H(2)=0:B(2)=0:R2=0:Q2=0:C6=0:F=0:W0=0:Y=0:Y2=0:U=0:U2=0 620 PRINT : PRINT : PRINT 630 PRINT "WHICH BATTLE DO YOU WISH TO SIMULATE"; 640 INPUT A 650 IF A <> 0 THEN 660 655 IF R <> 0 THEN 1140 660 IF A <=0 THEN 2860 665 IF A >= 15 THEN 2860 670 LET C$=C$(A) 680 LET M1=M1(A) 690 LET M2=M2(A) 700 LET C1=C1(A) 710 LET C2=C2(A) 720 LET M=M(A) 960 LET U=0 970 REM INFLATION CALC 980 LET I1=10+(L-W)*2 990 LET I2=10+(W-L)*2 1000 REM - MONEY AVAILABLE 1010 LET D(1)=100*INT((M1*(100-I1)/2000)*(1+(R1-Q1)/(R1+1))+.5) 1020 LET D(2)=100*INT(M2*(100-I2)/2000+.5) 1030 IF B$ <> "YES" THEN 1050 1040 LET D(2)=100*INT((M2*(100-I2)/2000)*(1+(R2-Q2)/(R2+1))+.5) 1050 REM - MEN AVAILABLE 1060 LET M5=INT(M1*(1+(P1-T1)/(M3+1))) 1070 LET M6=INT(M2*(1+(P2-T2)/(M4+1))) 1080 LET F1=5*M1/6 1090 PRINT : PRINT : PRINT : PRINT : PRINT 1100 PRINT "THIS IS THE BATTLE OF ";C$ 1110 IF X$="NO" THEN 1150 1120 IF A>11 THEN 1130 1125 ON A GOTO 3580,3620,3650,3690,3720,3750,3780,3800,3830,3860,3890 1130 ON A-11 GOTO 3920,3950,3980 1140 PRINT C$" INSTANT REPLAY" 1150 PRINT 1160 PRINT " ","CONFEDERACY"," UNION" 1170 PRINT "MEN"," "M5," "M6 1180 PRINT "MONEY","$";D(1),"$";D(2) 1190 PRINT "INFLATION"," ";I1+15;"%"," ";I2;"%" 1195 PRINT 1200 REM - ONLY IN PRINTOUT IS CONFED INFLATION = I1+15% 1210 REM - IF TWO GENERALS, INPUT CONFED. FIRST 1220 FOR I=1 TO D 1230 IF B$ <> "YES" THEN 1260 1240 IF I=2 THEN 1260 1250 PRINT "CONFEDERATE GENERAL---"; 1260 PRINT "HOW MUCH DO YOU WISH TO SPEND FOR" 1270 PRINT " - FOOD......"; 1280 INPUT F 1290 IF F >= 0 THEN 1360 1300 IF R1 <> 0 THEN 1330 1310 PRINT "NO PREVIOUS ENTRIES" 1320 GOTO 1270 1330 PRINT "ASSUME YOU WANT TO KEEP SAME ALLOCATIONS" 1340 PRINT 1350 GOTO 1510 1360 LET F(I)=F 1370 PRINT " - SALARIES.."; 1380 INPUT H(I) 1390 LET N=1 1400 IF H(I)<0 THEN 1490 1410 PRINT " - AMMUNITION"; 1420 INPUT B(I) 1430 LET N=2 1440 IF B(I)<0 THEN 1490 1450 PRINT 1460 IF F(I)+H(I)+B(I) <= D(I) THEN 1510 1470 PRINT "THINK AGAIN! YOU HAVE ONLY $"D(I) 1480 GOTO 1270 1490 PRINT "NEGATIVE VALUES NOT ALLOWED." 1500 ON N GOTO 1370,1410 1510 IF B$ <> "YES" THEN 1550 1520 IF I=2 THEN 1550 1530 PRINT "UNION GENERAL---"; 1540 NEXT I 1550 FOR Z=1 TO D 1560 IF B$ <> "YES" THEN 1620 1570 ON Z GOTO 1580,1600 1580 PRINT "CONFEDERATE "; 1590 GOTO 1620 1600 PRINT " UNION "; 1610 REM - FIND MORALE 1620 LET O=((2*F(Z)^2+H(Z)^2)/F1^2+1) 1630 IF O<10 THEN 1660 1640 PRINT "MORALE IS HIGH" 1650 GOTO 1700 1660 IF O<5 THEN 1690 1670 PRINT "MORALE IS FAIR" 1680 GOTO 1700 1690 PRINT "MORALE IS POOR" 1700 IF B$ <> "YES" THEN 1760 1710 LET O(Z)=O 1720 NEXT Z 1730 LET O2=O(2) 1740 LET O=O(1) 1750 PRINT "CONFEDERATE GENERAL---"; 1760 REM - ACTUAL OFF/DEF BATTLE SITUATION 1770 IF M <> 3 THEN 1800 1780 PRINT "YOU ARE ON THE OFFENSIVE" 1790 GOTO 1840 1800 IF M <> 1 THEN 1830 1810 PRINT "YOU ARE ON THE DEFENSIVE" 1820 GOTO 1840 1830 PRINT "BOTH SIDES ARE ON THE OFFENSIVE " 1840 PRINT 1850 REM - CHOOSE STRATEGIES 1860 IF B$ <> "YES" THEN 1910 1870 FOR I=1 TO 2 1880 ON I GOTO 1890,1920 1890 PRINT "CONFEDERATE STRATEGY "; 1900 GOTO 1920 1910 PRINT "YOUR STRATEGY "; 1920 INPUT Y 1930 IF ABS(Y-3)<3 THEN 1960 1940 PRINT "STRATEGY";Y;"NOT ALLOWED." 1950 GOTO 1910 1960 IF B$="YES" THEN 2000 1970 IF Y=5 THEN 2830 1980 GOSUB 3110 1990 GOTO 2170 2000 IF I=2 THEN 2040 2010 LET Y1=Y 2020 PRINT "UNION STRATEGY "; 2030 NEXT I 2040 LET Y2=Y 2050 LET Y=Y1 2060 IF Y2=5 THEN 2020 2070 REM : SIMULATED LOSSES-NORTH 2080 LET C6=(2*C2/5)*(1+1/(2*(ABS(Y2-Y)+1))) 2090 LET C6=C6*(1.28+(5*M2/6)/(B(2)+1)) 2100 LET C6=INT(C6*(1+1/O2)+.5) 2110 REM - IF LOSS > MEN PRESENT, RESCALE LOSSES 2120 LET E2=100/O2 2130 IF INT(C6+E2)<M6 THEN 2190 2140 LET C6=INT(13*M6/20) 2150 LET E2=7*C6/13 2160 LET U2=1 2170 REM - CALCULATE SIMULATED LOSSES 2180 PRINT 2190 PRINT : PRINT : PRINT ,"CONFEDERACY","UNION" 2200 LET C5=(2*C1/5)*(1+1/(2*(ABS(Y2-Y)+1))) 2210 LET C5=INT(C5*(1+1/O)*(1.28+F1/(B(1)+1))+.5) 2220 LET E=100/O 2230 IF C5+100/O<M1*(1+(P1-T1)/(M3+1)) THEN 2270 2240 LET C5=INT(13*M1/20*(1+(P1-T1)/(M3+1))) 2250 LET E=7*C5/13 2260 LET U=1 2270 IF D=1 THEN 2500 2280 PRINT "CASUALTIES",C5,C6 2290 PRINT "DESERTIONS",INT(E),INT(E2) 2300 PRINT 2310 IF B$ <> "YES" THEN 2530 2320 PRINT "COMPARED TO THE ACTUAL CASUALTIES AT "C$ 2330 PRINT "CONFEDERATE:"INT(100*(C5/C1)+.5)"% OF THE ORIGINAL" 2340 PRINT "UNION: "INT(100*(C6/C2)+.5)"% OF THE ORIGINAL" 2350 PRINT 2360 REM - 1 WHO WON 2370 IF U <> 1 THEN 2380 2375 IF U2=1 THEN 2460 2380 IF U=1 THEN 2420 2390 IF U2=1 THEN 2440 2400 IF C5+E=C6+E2 THEN 2460 2410 IF C5+E<C6+E2 THEN 2440 2420 PRINT "THE UNION WINS "C$ 2430 GOTO 2600 2440 PRINT "THE CONFEDERACY WINS "C$ 2450 GOTO 2660 2460 PRINT "BATTLE OUTCOME UNRESOLVED" 2470 LET W0=W0+1 2480 IF A=0 THEN 2790 2490 GOTO 2680 2500 LET C6=INT(17*C2*C1/(C5*20)) 2510 LET E2=5*O 2520 GOTO 2280 2530 PRINT "YOUR CASUALTIES WERE "INT(100*(C5/C1)+.5)"% OF " 2540 PRINT "THE ACTUAL CASUALTIES AT ";C$ 2550 PRINT 2560 REM - FIND WHO WON 2570 IF U=1 THEN 2590 2580 IF C5+E<17*C2*C1/(C5*20)+5*O THEN 2630 2590 PRINT "YOU LOSE ";C$ 2600 IF A=0 THEN 2790 2610 LET L=L+1 2620 GOTO 2680 2630 PRINT "YOU WIN ";C$ 2640 REM - CUMULATIVE BATTLE FACTORS WHICH ALTER HISTORICAL 2650 REM RESOURCES AVAILABLE.IF A REPLAY DON'T UPDATE. 2660 IF A=0 THEN 2790 2670 LET W=W+1 2680 LET T1=T1+C5+E 2690 LET T2=T2+C6+E2 2700 LET P1=P1+C1 2710 LET P2=P2+C2 2720 LET Q1=Q1+(F(1)+H(1)+B(1)) 2730 LET Q2=Q2+(F(2)+H(2)+B(2)) 2740 LET R1=R1+M1*(100-I1)/20 2750 LET R2=R2+M2*(100-I2)/20 2760 LET M3=M3+M1 2770 LET M4=M4+M2 2780 GOSUB 3300 2790 U=0:U2=0 2800 PRINT "---------------" 2810 GOTO 620 2820 REM------FINISH OFF 2830 PRINT "THE CONFEDERACY HAS SURRENDERED" 2840 GOTO 2860 2850 PRINT "THE UNION HAS SURRENDERED." 2860 PRINT : PRINT : PRINT : PRINT : PRINT : PRINT 2870 PRINT "THE CONFEDERACY "; 2880 PRINT "HAS WON "W" BATTLES AND LOST "L 2890 IF Y=5 THEN 2940 2900 IF Y2=5 THEN 2920 2910 IF W <= L THEN 2940 2915 IF Y=5 THEN 2940 2920 PRINT "THE CONFEDERACY HAS WON THE WAR" 2930 GOTO 2950 2940 PRINT "THE UNION HAS WON THE WAR" 2950 PRINT 2960 IF R1=0 THEN 3100 2970 PRINT "FOR THE "W+L+W0" BATTLES FOUGHT (EXCLUDING RERUNS)" 2980 PRINT " "," "," "; 2990 PRINT "CONFEDERACY"," UNION" 3000 PRINT "HISTORICAL LOSSES",INT(P1+.5),INT(P2+.5) 3010 PRINT "SIMULATED LOSSES",INT(T1+.5),INT(T2+.5) 3020 PRINT 3030 PRINT " % OF ORIGINAL",INT(100*(T1/P1)+.5),INT(100*(T2/P2)+.5) 3040 IF B$="YES" THEN 3100 3050 PRINT 3060 PRINT "UNION INTELLIGENCE SUGGESTS THAT THE SOUTH USED " 3070 PRINT "STRATEGIES 1, 2, 3, 4 IN THE FOLLOWING PERCENTAGES" 3080 PRINT S(1);S(2);S(3);S(4) 3090 REM--------------------------------- 3100 STOP 3110 REM - UNION STRATEGY IS COMPUTER CHOSEN 3120 PRINT "UNION STRATEGY IS "; 3130 IF A <> 0 THEN 3180 3140 INPUT Y2 3150 IF Y2 <=0 THEN 3160 3155 IF Y2<5 THEN 3290 3160 PRINT "ENTER 1 , 2 ,3 , OR 4 (USUALLY PREVIOUS UNION STRATEGY)" 3170 GOTO 3140 3180 LET S0=0 3190 LET R=100*RND(0) 3200 FOR I=1 TO 4 3210 LET S0=S0+S(I) 3220 REM - IF ACTUAL STRATEGY INFO IS IN PROGRAM DATA STATEMENTS 3230 REM THEN R-100 IS EXTRA WEIGHT GIVEN TO THAT STATEGY. 3240 IF R<S0 THEN 3270 3250 NEXT I 3260 REM - IF ACTUAL STRAT. IN,THEN HERE IS Y2= HIST. STRAT. 3270 LET Y2=I 3280 PRINT Y2 3290 RETURN 3300 REM LEARN PRESENT STRATEGY, START FORGETTING OLD ONES 3310 REM - PRESENT STRATEGY OF SOUTH GAINS 3*S, OTHERS LOSE S 3320 REM PROBABILITY POINTS, UNLESS A STRATEGY FALLS BELOW 5%. 3330 LET S=3 3340 LET S0=0 3350 FOR I=1 TO 4 3360 IF S(I) <= 5 THEN 3390 3370 LET S(I)=S(I)-S 3380 LET S0=S0+S 3390 NEXT I 3400 LET S(Y)=S(Y)+S0 3410 RETURN 3420 REM - HISTORICAL DATA...CAN ADD MORE (STRAT.,ETC) BY INSERTING 3430 REM DATA STATEMENTS AFTER APPRO. INFO, AND ADJUSTING READ 3440 DATA "BULL RUN",18000,18500,1967,2708,1 3450 DATA "SHILOH",40000.,44894.,10699,13047,3 3460 DATA "SEVEN DAYS",95000.,115000.,20614,15849,3 3470 DATA "SECOND BULL RUN",54000.,63000.,10000,14000,2 3480 DATA "ANTIETAM",40000.,50000.,10000,12000,3 3490 DATA "FREDERICKSBURG",75000.,120000.,5377,12653,1 3500 DATA "MURFREESBORO",38000.,45000.,11000,12000,1 3510 DATA "CHANCELLORSVILLE",32000,90000.,13000,17197,2 3520 DATA "VICKSBURG",50000.,70000.,12000,19000,1 3530 DATA "GETTYSBURG",72500.,85000.,20000,23000,3 3540 DATA "CHICKAMAUGA",66000.,60000.,18000,16000,2 3550 DATA "CHATTANOOGA",37000.,60000.,36700.,5800,2 3560 DATA "SPOTSYLVANIA",62000.,110000.,17723,18000,2 3570 DATA "ATLANTA",65000.,100000.,8500,3700,1 3580 PRINT "JULY 21, 1861. GEN. BEAUREGARD, COMMANDING THE SOUTH, MET" 3590 PRINT "UNION FORCES WITH GEN. MCDOWELL IN A PREMATURE BATTLE AT" 3600 PRINT "BULL RUN. GEN. JACKSON HELPED PUSH BACK THE UNION ATTACK." 3610 GOTO 1150 3620 PRINT "APRIL 6-7, 1862. THE CONFEDERATE SURPRISE ATTACK AT" 3630 PRINT "SHILOH FAILED DUE TO POOR ORGANIZATION." 3640 GOTO 1150 3650 PRINT "JUNE 25-JULY 1, 1862. GENERAL LEE (CSA) UPHELD THE" 3660 PRINT "OFFENSIVE THROUGHOUT THE BATTLE AND FORCED GEN. MCCLELLAN" 3670 PRINT "AND THE UNION FORCES AWAY FROM RICHMOND." 3680 GOTO 1150 3690 PRINT "AUG 29-30, 1862. THE COMBINED CONFEDERATE FORCES UNDER"; 3695 PRINT " LEE" 3700 PRINT "AND JACKSON DROVE THE UNION FORCES BACK INTO WASHINGTON." 3710 GOTO 1150 3720 PRINT "SEPT 17, 1862. THE SOUTH FAILED TO INCORPORATE MARYLAND" 3730 PRINT "INTO THE CONFEDERACY." 3740 GOTO 1150 3750 PRINT "DEC 13, 1862. THE CONFEDERACY UNDER LEE SUCCESSFULLY" 3760 PRINT "REPULSED AN ATTACK BY THE UNION UNDER GEN. BURNSIDE." 3770 GOTO 1150 3780 PRINT "DEC 31, 1862. THE SOUTH UNDER GEN. BRAGG WON A CLOSE "; 3785 PRINT "BATTLE." 3790 GOTO 1150 3800 PRINT "MAY 1-6, 1863. THE SOUTH HAD A COSTLY VICTORY AND LOST" 3810 PRINT "ONE OF THEIR OUTSTANDING GENERALS, 'STONEWALL' JACKSON." 3820 GOTO 1150 3830 PRINT "JULY 4, 1863. VICKSBURG WAS A COSTLY DEFEAT FOR THE SOUTH" 3840 PRINT "BECAUSE IT GAVE THE UNION ACCESS TO THE MISSISSIPPI." 3850 GOTO 1150 3860 PRINT "JULY 1-3, 1863. A SOUTHERN MISTAKE BY GEN. LEE AT "; 3865 PRINT "GETTYSBURG" 3870 PRINT "COST THEM ONE OF THE MOST CRUCIAL BATTLES OF THE WAR." 3880 GOTO 1150 3890 PRINT "SEPT. 15, 1863. CONFUSION IN A FOREST NEAR CHICKAMAUGA LED" 3900 PRINT "TO A COSTLY SOUTHERN VICTORY." 3910 GOTO 1150 3920 PRINT "NOV. 25, 1863. AFTER THE SOUTH HAD SIEGED GEN. ROSENCRANS'" 3930 PRINT "ARMY FOR THREE MONTHS, GEN. GRANT BROKE THE SIEGE." 3940 GOTO 1150 3950 PRINT "MAY 5, 1864. GRANT'S PLAN TO KEEP LEE ISOLATED BEGAN TO" 3960 PRINT "FAIL HERE, AND CONTINUED AT COLD HARBOR AND PETERSBURG." 3970 GOTO 1150 3980 PRINT "AUGUST, 1864. SHERMAN AND THREE VETERAN ARMIES CONVERGED" 3990 PRINT "ON ATLANTA AND DEALT THE DEATH BLOW TO THE CONFEDERACY." 4000 GOTO 1150 4010 END ================================================ FILE: 00_Alternate_Languages/28_Combat/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/28_Combat/MiniScript/combat.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/28_Combat/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/28_Combat/combat.bas ================================================ 1 PRINT TAB(33);"COMBAT" 2 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 3 PRINT: PRINT: PRINT 4 PRINT "I AM AT WAR WITH YOU.": PRINT "WE HAVE 72000 SOLDIERS APIECE." 5 PRINT:PRINT "DISTRIBUTE YOUR FORCES." 6 PRINT ,"ME"," YOU" 7 PRINT "ARMY",30000, 8 INPUT A 9 PRINT "NAVY",20000, 10 INPUT B 11 PRINT "A. F.",22000, 12 INPUT C 13 IF A+B+C>72000 THEN 5 14 D=30000 15 E=20000 16 F=22000 17 PRINT "YOU ATTACK FIRST. TYPE (1) FOR ARMY; (2) FOR NAVY;" 18 PRINT "AND (3) FOR AIR FORCE." 19 INPUT Y 20 PRINT "HOW MANY MEN" 21 INPUT X 22 IF X<0 THEN 20 23 ON Y GOTO 100,200,300 100 IF X>A THEN 20 105 IF X<A/3 THEN 120 110 IF X<2*A/3 THEN 150 115 GOTO 270 120 PRINT "YOU LOST";X;"MEN FROM YOUR ARMY." 125 A=INT(A-X) 130 GOTO 500 150 PRINT "YOU LOST";INT(X/3);"MEN, BUT I LOST ";INT(2*D/3) 155 A=INT(A-X/3) 160 D=0 165 GOTO 500 200 IF X>B THEN 20 210 IF X<E/3 THEN 230 215 IF X<2*E/3 THEN 250 220 GOTO 270 230 PRINT "YOUR ATTACK WAS STOPPED!" 232 B=INT(B-X) 235 GOTO 500 250 PRINT "YOU DESTROYED";INT(2*E/3);"OF MY ARMY." 255 E=INT(E/3) 260 GOTO 500 270 PRINT "YOU SUNK ONE OF MY PATROL BOATS, BUT I WIPED OUT TWO" 275 PRINT "OF YOUR AIR FORCE BASES AND 3 ARMY BASES." 280 A=INT(A/3) 285 C=INT(C/3) 290 E=INT(2*E/3) 293 GOTO 500 300 IF X>C THEN 20 310 IF X<C/3 THEN 350 320 IF X<2*C/3 THEN 370 330 GOTO 380 350 PRINT "YOUR ATTACK WAS WIPED OUT." 355 C=INT(C-X) 360 GOTO 500 370 PRINT "WE HAD A DOGFIGHT. YOU WON - AND FINISHED YOUR MISSION." 375 D=INT(2*D/3) 377 E=INT(E/3) 378 F=INT(F/3) 379 GOTO 500 380 PRINT "YOU WIPED OUT ONE OF MY ARMY PATROLS, BUT I DESTROYED" 381 PRINT "TWO NAVY BASES AND BOMBED THREE ARMY BASES." 385 A=INT(A/4) 387 B=INT(B/3) 390 D=INT(2*D/3) 500 PRINT 501 PRINT,"YOU","ME" 510 PRINT "ARMY",A,D 520 PRINT "NAVY",B,E 530 PRINT "A. F.",C,F 1000 PRINT "WHAT IS YOUR NEXT MOVE?" 1010 PRINT "ARMY=1 NAVY=2 AIR FORCE=3" 1020 INPUT G 1030 PRINT "HOW MANY MEN" 1040 INPUT T 1045 IF T<0 THEN 1030 1050 ON G GOTO 1600,1700,1800 1600 IF T>A THEN 1030 1610 IF T<D/2 THEN 1630 1615 PRINT "YOU DESTROYED MY ARMY!" 1616 D=0 1617 GOTO 2000 1630 PRINT "I WIPED OUT YOUR ATTACK!" 1635 A=A-T 1640 GOTO 2000 1700 IF T>B THEN 1030 1710 IF T<E/2 THEN 1750 1720 GOTO 1770 1750 PRINT "I SUNK TWO OF YOUR BATTLESHIPS, AND MY AIR FORCE" 1751 PRINT "WIPED OUT YOUR UNGAURDED CAPITOL." 1755 A=A/4 1760 B=B/2 1765 GOTO 2000 1770 PRINT "YOUR NAVY SHOT DOWN THREE OF MY XIII PLANES," 1771 PRINT "AND SUNK THREE BATTLESHIPS." 1775 F=2*F/3 1780 E=(E/2) 1790 GOTO 2000 1800 IF T>C THEN 1030 1810 IF T>F/2 THEN 1830 1820 GOTO 1850 1830 PRINT "MY NAVY AND AIR FORCE IN A COMBINED ATTACK LEFT" 1831 PRINT "YOUR COUNTRY IN SHAMBLES." 1835 A=A/3 1837 B=B/3 1840 C=C/3 1845 GOTO 2000 1850 PRINT "ONE OF YOUR PLANES CRASHED INTO MY HOUSE. I AM DEAD." 1851 PRINT "MY COUNTRY FELL APART." 1860 GOTO 2010 2000 PRINT 2001 PRINT "FROM THE RESULTS OF BOTH OF YOUR ATTACKS," 2002 IF A+B+C>3/2*(D+E+F) THEN 2010 2005 IF A+B+C<2/3*(D+E+F) THEN 2015 2006 PRINT "THE TREATY OF PARIS CONCLUDED THAT WE TAKE OUR" 2007 PRINT "RESPECTIVE COUNTRIES AND LIVE IN PEACE." 2008 GOTO 2020 2010 PRINT "YOU WON, OH! SHUCKS!!!!" 2012 GOTO 2020 2015 PRINT "YOU LOST-I CONQUERED YOUR COUNTRY. IT SERVES YOU" 2016 PRINT "RIGHT FOR PLAYING THIS STUPID GAME!!!" 2020 END ================================================ FILE: 00_Alternate_Languages/29_Craps/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/29_Craps/MiniScript/craps.ms ================================================ // 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 ================================================ FILE: 00_Alternate_Languages/29_Craps/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/29_Craps/craps.bas ================================================ 5 PRINT TAB(33);"CRAPS" 10 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 12 PRINT:PRINT:PRINT 15 LET R=0 20 PRINT"2,3,12 ARE LOSERS; 4,5,6,8,9,10 ARE POINTS; 7,11 ARE NATURAL WINNERS." 21 LET T=1 22 PRINT "PICK A NUMBER AND INPUT TO ROLL DICE"; 23 INPUT Z 24 LET X=(RND(0)) 25 LET T =T+1 26 IF T<=Z THEN 24 27 PRINT"INPUT THE AMOUNT OF YOUR WAGER."; 28 INPUT F 30 PRINT "I WILL NOW THROW THE DICE" 40 LET E=INT(7*RND(1)) 41 LET S=INT(7*RND(1)) 42 LET X=E+S 50 IF X=7 THEN 180 55 IF X=11 THEN 180 60 IF X=1 THEN 40 62 IF X=2 THEN 195 65 IF X=0 THEN 40 70 IF X=2 THEN 200 80 IF X=3 THEN 200 90 IF X=12 THEN 200 125 IF X=5 THEN 220 130 IF X =6 THEN 220 140 IF X=8 THEN 220 150 IF X=9 THEN 220 160 IF X =10 THEN 220 170 IF X=4 THEN 220 180 PRINT X "- NATURAL....A WINNER!!!!" 185 PRINT X"PAYS EVEN MONEY, YOU WIN"F"DOLLARS" 190 GOTO 210 195 PRINT X"- SNAKE EYES....YOU LOSE." 196 PRINT "YOU LOSE"F "DOLLARS." 197 LET F=0-F 198 GOTO 210 200 PRINT X " - CRAPS...YOU LOSE." 205 PRINT "YOU LOSE"F"DOLLARS." 206 LET F=0-F 210 LET R= R+F 211 GOTO 320 220 PRINT X "IS THE POINT. I WILL ROLL AGAIN" 230 LET H=INT(7*RND(1)) 231 LET Q=INT(7*RND(1)) 232 LET O=H+Q 240 IF O=1 THEN 230 250 IF O=7 THEN 290 255 IF O=0 THEN 230 260 IF O=X THEN 310 270 PRINT O " - NO POINT. I WILL ROLL AGAIN" 280 GOTO 230 290 PRINT O "- CRAPS. YOU LOSE." 291 PRINT "YOU LOSE $"F 292 F=0-F 293 GOTO 210 300 GOTO 320 310 PRINT X"- A WINNER.........CONGRATS!!!!!!!!" 311 PRINT X "AT 2 TO 1 ODDS PAYS YOU...LET ME SEE..."2*F"DOLLARS" 312 LET F=2*F 313 GOTO 210 320 PRINT " IF YOU WANT TO PLAY AGAIN PRINT 5 IF NOT PRINT 2"; 330 INPUT M 331 IF R<0 THEN 334 332 IF R>0 THEN 336 333 IF R=0 THEN 338 334 PRINT "YOU ARE NOW UNDER $";-R 335 GOTO 340 336 PRINT "YOU ARE NOW AHEAD $";R 337 GOTO 340 338 PRINT "YOU ARE NOW EVEN AT 0" 340 IF M=5 THEN 27 341 IF R<0 THEN 350 342 IF R>0 THEN 353 343 IF R=0 THEN 355 350 PRINT"TOO BAD, YOU ARE IN THE HOLE. COME AGAIN." 351 GOTO 360 353 PRINT"CONGRATULATIONS---YOU CAME OUT A WINNER. COME AGAIN!" 354 GOTO 360 355 PRINT"CONGRATULATIONS---YOU CAME OUT EVEN, NOT BAD FOR AN AMATEUR" 360 END ================================================ FILE: 00_Alternate_Languages/29_Craps/distributions.bas ================================================ 10 PRINT "DISTRIBUTION OF DICE ROLLS WITH INT(7*RND(1)) VS INT(6*RND(1)+1)" 20 DIM A(12) 30 DIM B(12) 100 FOR X = 1 TO 100000 : REM CHOOSE A LARGE NUMBER TO GET A FINER GRAINED HISTOGRAM 140 REM GET A NUMBER FROM 0 TO 6 INCLUSIVE WITH THE INTENT TO THROW AWAY ZEROES. 150 LET D1 = INT(7*RND(1)) 155 LET D2 = INT(7*RND(1)) 160 LET S1 = D1+D2 165 REM IF THIS SUM IS LESS THAN TWO THEN TRY AGAIN. 170 IF S1<2 THEN 150 199 REM GET A NUMBER FROM 0 TO 5 THEN ADD 1 TO IT TO MAKE IT 1 TO 6 200 LET D3 = INT(6*RND(1))+1 210 LET D4 = INT(6*RND(1))+1 220 LET S2 = D3+D4 245 REM USE OUR ARRAY AS A HISTOGRAM, COUNTING EACH OCCURRENCE OF DICE ROLL 250 A(S1) = A(S1) + 1 260 B(S2) = B(S2) + 1 290 NEXT X 300 PRINT "THE INT(7*RND(1)) DISTRIBUTION:" 310 FOR I = 2 TO 12 :PRINT I,:NEXT:PRINT 320 FOR I = 2 TO 12 :PRINT A(I),:NEXT:PRINT 325 PRINT "THE INT(6*RND(1)+1) DISTRIBUTION" 330 FOR I = 2 TO 12 :PRINT I,:NEXT:PRINT 340 FOR I = 2 TO 12 :PRINT B(I),:NEXT:PRINT ================================================ FILE: 00_Alternate_Languages/29_Craps/nim/craps.nim ================================================ 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" ================================================ FILE: 00_Alternate_Languages/30_Cube/C/cube.c ================================================ #include <stdio.h> #include <stdlib.h> #include <time.h> //check if windows or linux for the clear screen #ifdef _WIN32 #define CLEAR "cls" #else #define CLEAR "clear" #endif typedef struct{ int x; int y; int z; }coords; void instuctions(){ printf("\nThis is a game in which you will be playing against the\n"); printf("random decisions of the computer. The field of play is a\n"); printf("cube of side 3. Any of the 27 locations can be designated\n"); printf("by inputing three numbers such as 2,3,1. At the start,\n"); printf("you are automatically at location 1,1,1. The object of\n"); printf("the game is to get to location 3,3,3. One minor detail:\n"); printf("the computer will pick, at random, 5 locations at which\n"); printf("it will plant land mines. If you hit one of these locations\n"); printf("you lose. One other detail: You may move only one space\n"); printf("in one direction each move. For example: From 1,1,2 you\n"); printf("may move to 2,1,2 or 1,1,3. You may not change\n"); printf("two of the numbers on the same move. If you make an illegal\n"); printf("move, you lose and the computer takes the money you may\n"); printf("have bet on that round.\n\n"); printf("When stating the amount of a wager, printf only the number\n"); printf("of dollars (example: 250) you are automatically started with\n"); printf("500 dollars in your account.\n\n"); printf("Good luck!\n"); } void game(int money){ coords player,playerold,mines[5]; int wager,account = money; char choice; if(money == 0){ printf("You have no money left. See ya next time.\n"); exit(0); } player.x = 1; player.y = 1; player.z = 1; printf("You have $%d in your account.\n",account); printf("How much do you want to wager? "); scanf("%d",&wager); while(wager > account){ system(CLEAR); printf("You do not have that much money in your account.\n"); printf("How much do you want to wager? "); scanf("%d",&wager); } srand(time(NULL)); for(int i=0;i<5;i++){ mines[i].x = rand()%3+1; mines[i].y = rand()%3+1; mines[i].z = rand()%3+1; if(mines[i].x == 3 && mines[i].y == 3 && mines[i].z == 3){ i--; } } while(player.x != 3 || player.y != 3 || player.z != 3){ printf("You are at location %d.%d.%d\n",player.x,player.y,player.z); if(player.x == 1 && player.y == 1 && player.z == 1) printf("Enter new location(use commas like 1,1,2 or else the program will break...): "); else printf("Enter new location: "); playerold.x = player.x; playerold.y = player.y; playerold.z = player.z; scanf("%d,%d,%d",&player.x,&player.y,&player.z); if(((player.x + player.y + player.z) > (playerold.x + playerold.y + playerold.z + 1)) || ((player.x + player.y + player.z) < (playerold.x + playerold.y + playerold.z -1))){ system(CLEAR); printf("Illegal move!\n"); printf("You lose $%d.\n",wager); game(account -= wager); break; } if(player.x < 1 || player.x > 3 || player.y < 1 || player.y > 3 || player.z < 1 || player.z > 3){ system(CLEAR); printf("Illegal move. You lose!\n"); game(account -= wager); break; } for(int i=0;i<5;i++){ if(player.x == mines[i].x && player.y == mines[i].y && player.z == mines[i].z){ system(CLEAR); printf("You hit a mine!\n"); printf("You lose $%d.\n",wager); game(account -= wager); exit(0); } } if(account == 0){ system(CLEAR); printf("You have no money left!\n"); printf("Game over!\n"); exit(0); } } if(player.x == 3 && player.y == 3 && player.z == 3){ system(CLEAR); printf("You made it to the end. You win!\n"); game(account += wager); exit(0); } } void init(){ int account = 500; char choice; printf("Welcome to the game of Cube!\n"); printf("wanna see the instructions? (y/n): "); scanf("%c",&choice); if(choice == 'y'){ system(CLEAR); instuctions(); } else if (choice == 'n'){ system(CLEAR); printf("Ok, let's play!\n"); } else{ system(CLEAR); printf("Invalid choice. Try again...\n"); init(); exit(0); } game(account); exit(0); } void main(){ init(); } ================================================ FILE: 00_Alternate_Languages/30_Cube/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/30_Cube/MiniScript/cube.ms ================================================ 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." ================================================ FILE: 00_Alternate_Languages/30_Cube/README.md ================================================ #### External Links - Common Lisp: https://github.com/koalahedron/lisp-computer-games/blob/master/01%20Acey%20Ducey/common-lisp/acey-deucy.lisp - PowerShell: https://github.com/eweilnau/basic-computer-games-powershell/blob/main/AceyDucey.ps1 ================================================ FILE: 00_Alternate_Languages/30_Cube/cube.bas ================================================ 10 PRINT TAB(34);"CUBE" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 30 PRINT : PRINT : PRINT 100 PRINT "DO YOU WANT TO SEE THE INSTRUCTIONS? (YES--1,NO--0)" 110 INPUT B7 120 IF B7=0 THEN 370 130 PRINT"THIS IS A GAME IN WHICH YOU WILL BE PLAYING AGAINST THE" 140 PRINT"RANDOM DECISION OF THE COMPUTER. THE FIELD OF PLAY IS A" 150 PRINT"CUBE OF SIDE 3. ANY OF THE 27 LOCATIONS CAN BE DESIGNATED" 160 PRINT"BY INPUTING THREE NUMBERS SUCH AS 2,3,1. AT THE START," 170 PRINT"YOU ARE AUTOMATICALLY AT LOCATION 1,1,1. THE OBJECT OF" 180 PRINT"THE GAME IS TO GET TO LOCATION 3,3,3. ONE MINOR DETAIL:" 190 PRINT"THE COMPUTER WILL PICK, AT RANDOM, 5 LOCATIONS AT WHICH" 200 PRINT"IT WILL PLANT LAND MINES. IF YOU HIT ONE OF THESE LOCATIONS" 210 PRINT"YOU LOSE. ONE OTHER DETAIL: YOU MAY MOVE ONLY ONE SPACE " 220 PRINT"IN ONE DIRECTION EACH MOVE. FOR EXAMPLE: FROM 1,1,2 YOU" 230 PRINT"MAY MOVE TO 2,1,2 OR 1,1,3. YOU MAY NOT CHANGE" 240 PRINT"TWO OF THE NUMBERS ON THE SAME MOVE. IF YOU MAKE AN ILLEGAL" 250 PRINT"MOVE, YOU LOSE AND THE COMPUTER TAKES THE MONEY YOU MAY" 260 PRINT"HAVE BET ON THAT ROUND." 270 PRINT 280 PRINT 290 PRINT"ALL YES OR NO QUESTIONS WILL BE ANSWERED BY A 1 FOR YES" 300 PRINT"OR A 0 (ZERO) FOR NO." 310 PRINT 320 PRINT"WHEN STATING THE AMOUNT OF A WAGER, PRINT ONLY THE NUMBER" 330 PRINT"OF DOLLARS (EXAMPLE: 250) YOU ARE AUTOMATICALLY STARTED WITH" 340 PRINT"500 DOLLARS IN YOUR ACCOUNT." 350 PRINT 360 PRINT "GOOD LUCK!" 370 LET A1=500 380 LET A=INT(3*(RND(X))) 390 IF A<>0 THEN 410 400 LET A=3 410 LET B=INT(3*(RND(X))) 420 IF B<>0 THEN 440 430 LET B=2 440 LET C=INT(3*(RND(X))) 450 IF C<>0 THEN 470 460 LET C=3 470 LET D=INT(3*(RND(X))) 480 IF D<>0 THEN 500 490 LET D=1 500 LET E=INT(3*(RND(X))) 510 IF E<>0 THEN 530 520 LET E=3 530 LET F=INT(3*(RND(X))) 540 IF F<>0 THEN 560 550 LET F=3 560 LET G=INT(3*(RND(X))) 570 IF G<>0 THEN 590 580 LET G=3 590 LET H=INT(3*(RND(X))) 600 IF H<>0 THEN 620 610 LET H=3 620 LET I=INT(3*(RND(X))) 630 IF I<>0 THEN 650 640 LET I=2 650 LET J=INT(3*(RND(X))) 660 IF J<>0 THEN 680 670 LET J=3 680 LET K=INT(3*(RND(X))) 690 IF K<>0 THEN 710 700 LET K=2 710 LET L=INT(3*(RND(X))) 720 IF L<>0 THEN 740 730 LET L=3 740 LET M=INT(3*(RND(X))) 750 IF M<>0 THEN 770 760 LET M=3 770 LET N=INT(3*(RND(X))) 780 IF N<>0 THEN 800 790 LET N=1 800 LET O=INT (3*(RND(X))) 810 IF O <>0 THEN 830 820 LET O=3 830 PRINT "WANT TO MAKE A WAGER?" 840 INPUT Z 850 IF Z=0 THEN 880 860 PRINT "HOW MUCH "; 870 INPUT Z1 876 IF A1<Z1 THEN 1522 880 LET W=1 890 LET X=1 900 LET Y=1 910 PRINT 920 PRINT "IT'S YOUR MOVE: "; 930 INPUT P,Q,R 940 IF P>W+1 THEN 1030 950 IF P=W+1 THEN 1000 960 IF Q>X+1 THEN 1030 970 IF Q=(X+1) THEN 1010 980 IF R >(Y+1) THEN 1030 990 GOTO 1050 1000 IF Q>= X+1 THEN 1030 1010 IF R>=Y+1 THEN 1030 1020 GOTO 1050 1030 PRINT:PRINT "ILLEGAL MOVE. YOU LOSE." 1040 GOTO 1440 1050 LET W=P 1060 LET X=Q 1070 LET Y=R 1080 IF P=3 THEN 1100 1090 GOTO 1130 1100 IF Q=3 THEN 1120 1110 GOTO 1130 1120 IF R=3 THEN 1530 1130 IF P=A THEN 1150 1140 GOTO 1180 1150 IF Q=B THEN 1170 1160 GOTO 1180 1170 IF R=C THEN 1400 1180 IF P=D THEN 1200 1190 GOTO 1230 1200 IF Q=E THEN 1220 1210 GOTO 1230 1220 IF R=F THEN 1400 1230 IF P=G THEN 1250 1240 GOTO 1280 1250 IF Q=H THEN 1270 1260 GOTO 1280 1270 IF R=I THEN 1400 1280 IF P=J THEN 1300 1290 GOTO 1330 1300 IF Q=K THEN 1320 1310 GOTO 1330 1320 IF R=L THEN 1400 1330 IF P=M THEN 1350 1340 GOTO 1380 1350 IF Q=N THEN 1370 1360 GOTO 1380 1370 IF R=O THEN 1400 1380 PRINT "NEXT MOVE: "; 1390 GOTO 930 1400 PRINT"******BANG******" 1410 PRINT "YOU LOSE!" 1420 PRINT 1430 PRINT 1440 IF Z=0 THEN 1580 1450 PRINT 1460 LET Z2=A1-Z1 1470 IF Z2>0 THEN 1500 1480 PRINT "YOU BUST." 1490 GOTO 1610 1500 PRINT " YOU NOW HAVE"; Z2; "DOLLARS." 1510 LET A1=Z2 1520 GOTO 1580 1522 PRINT"TRIED TO FOOL ME; BET AGAIN"; 1525 GOTO 870 1530 PRINT"CONGRATULATIONS!" 1540 IF Z=0 THEN 1580 1550 LET Z2=A1+Z1 1560 PRINT "YOU NOW HAVE"; Z2;"DOLLARS." 1570 LET A1=Z2 1580 PRINT"DO YOU WANT TO TRY AGAIN "; 1590 INPUT S 1600 IF S=1 THEN 380 1610 PRINT "TOUGH LUCK!" 1620 PRINT 1630 PRINT "GOODBYE." 1640 END ================================================ FILE: 00_Alternate_Languages/31_Depth_Charge/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/31_Depth_Charge/MiniScript/depthcharge.ms ================================================ // 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 ================================================ FILE: 00_Alternate_Languages/31_Depth_Charge/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/31_Depth_Charge/depthcharge.bas ================================================ 2 PRINT TAB(30);"DEPTH CHARGE" 4 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 6 PRINT: PRINT: PRINT 20 INPUT "DIMENSION OF SEARCH AREA";G: PRINT 30 N=INT(LOG(G)/LOG(2))+1 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 110 A=INT(G*RND(1)) : B=INT(G*RND(1)) : C=INT(G*RND(1)) 120 FOR D=1 TO N : PRINT : PRINT "TRIAL #";D; : INPUT X,Y,Z 130 IF ABS(X-A)+ABS(Y-B)+ABS(Z-C)=0 THEN 300 140 GOSUB 500 : PRINT : NEXT D 200 PRINT : PRINT "YOU HAVE BEEN TORPEDOED! ABANDON SHIP!" 210 PRINT "THE SUBMARINE WAS AT";A;",";B;",";C : GOTO 400 300 PRINT : PRINT "B O O M ! ! YOU FOUND IT IN";D;"TRIES!" 400 PRINT : PRINT: INPUT "ANOTHER GAME (Y OR N)";A$ 410 IF A$="Y" THEN 100 420 PRINT "OK. HOPE YOU ENJOYED YOURSELF." : GOTO 600 500 PRINT "SONAR REPORTS SHOT WAS "; 510 IF Y>B THEN PRINT "NORTH"; 520 IF Y<B THEN PRINT "SOUTH"; 530 IF X>A THEN PRINT "EAST"; 540 IF X<A THEN PRINT "WEST"; 550 IF Y<>B OR X<>A THEN PRINT " AND"; 560 IF Z>C THEN PRINT " TOO LOW." 570 IF Z<C THEN PRINT " TOO HIGH." 580 IF Z=C THEN PRINT " DEPTH OK." 590 RETURN 600 END ================================================ FILE: 00_Alternate_Languages/31_Depth_Charge/go/main.go ================================================ package main import ( "bufio" "fmt" "math" "math/rand" "os" "strconv" "strings" "time" ) type Position []int func NewPosition() Position { p := make([]int, 3) return Position(p) } func showWelcome() { fmt.Print("\033[H\033[2J") fmt.Println(" DEPTH CHARGE") fmt.Println(" Creative Computing Morristown, New Jersey") fmt.Println() } func getNumCharges() (int, int) { scanner := bufio.NewScanner(os.Stdin) for { fmt.Println("Dimensions of search area?") scanner.Scan() dim, err := strconv.Atoi(scanner.Text()) if err != nil { fmt.Println("Must enter an integer number. Please try again...") continue } return dim, int(math.Log2(float64(dim))) + 1 } } func askForNewGame() { scanner := bufio.NewScanner(os.Stdin) fmt.Println("Another game (Y or N): ") scanner.Scan() if strings.ToUpper(scanner.Text()) == "Y" { main() } fmt.Println("OK. Hope you enjoyed yourself") os.Exit(1) } func showShotResult(shot, location Position) { result := "Sonar reports shot was " if shot[1] > location[1] { // y-direction result += "north" } else if shot[1] < location[1] { // y-direction result += "south" } if shot[0] > location[0] { // x-direction result += "east" } else if shot[0] < location[0] { // x-direction result += "west" } if shot[1] != location[1] || shot[0] != location[0] { result += " and " } if shot[2] > location[2] { result += "too low." } else if shot[2] < location[2] { result += "too high." } else { result += "depth OK." } fmt.Println(result) } func getShot() Position { scanner := bufio.NewScanner(os.Stdin) for { shotPos := NewPosition() fmt.Println("Enter coordinates: ") scanner.Scan() rawGuess := strings.Split(scanner.Text(), " ") if len(rawGuess) != 3 { goto there } for i := 0; i < 3; i++ { val, err := strconv.Atoi(rawGuess[i]) if err != nil { goto there } shotPos[i] = val } return shotPos there: fmt.Println("Please enter coordinates separated by spaces") fmt.Println("Example: 3 2 1") } } func getRandomPosition(searchArea int) Position { pos := NewPosition() for i := 0; i < 3; i++ { pos[i] = rand.Intn(searchArea) } return pos } func playGame(searchArea, numCharges int) { rand.Seed(time.Now().UTC().UnixNano()) fmt.Println("\nYou are the captain of the destroyer USS Computer.") fmt.Println("An enemy sub has been causing you trouble. Your") fmt.Printf("mission is to destroy it. You have %d shots.\n", numCharges) fmt.Println("Specify depth charge explosion point with a") fmt.Println("trio of numbers -- the first two are the") fmt.Println("surface coordinates; the third is the depth.") fmt.Println("\nGood luck!") fmt.Println() subPos := getRandomPosition(searchArea) for c := 0; c < numCharges; c++ { fmt.Printf("\nTrial #%d\n", c+1) shot := getShot() if shot[0] == subPos[0] && shot[1] == subPos[1] && shot[2] == subPos[2] { fmt.Printf("\nB O O M ! ! You found it in %d tries!\n", c+1) askForNewGame() } else { showShotResult(shot, subPos) } } // out of depth charges fmt.Println("\nYou have been torpedoed! Abandon ship!") fmt.Printf("The submarine was at %d %d %d\n", subPos[0], subPos[1], subPos[2]) askForNewGame() } func main() { showWelcome() searchArea, numCharges := getNumCharges() playGame(searchArea, numCharges) } ================================================ FILE: 00_Alternate_Languages/32_Diamond/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/32_Diamond/MiniScript/diamond.ms ================================================ // 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 ================================================ FILE: 00_Alternate_Languages/32_Diamond/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/32_Diamond/diamond.bas ================================================ 1 PRINT TAB(33);"DIAMOND" 2 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 3 PRINT:PRINT:PRINT 4 PRINT "FOR A PRETTY DIAMOND PATTERN," 5 INPUT "TYPE IN AN ODD NUMBER BETWEEN 5 AND 21";R:PRINT 6 Q=INT(60/R):A$="CC" 8 FOR L=1 TO Q 10 X=1:Y=R:Z=2 20 FOR N=X TO Y STEP Z 25 PRINT TAB((R-N)/2); 28 FOR M=1 TO Q 29 C=1 30 FOR A=1 TO N 32 IF C>LEN(A$) THEN PRINT "!";:GOTO 50 34 PRINT MID$(A$,C,1); 36 C=C+1 50 NEXT A 53 IF M=Q THEN 60 55 PRINT TAB(R*M+(R-N)/2); 56 NEXT M 60 PRINT 70 NEXT N 83 IF X<>1 THEN 95 85 X=R-2:Y=1:Z=-2 90 GOTO 20 95 NEXT L 99 END ================================================ FILE: 00_Alternate_Languages/33_Dice/C/dice.c ================================================ #include <stdio.h> #include <stdlib.h> #include <time.h> float percent(int number, int total){ float percent; percent = (float)number / (float)total * 100; return percent; } int main(){ int dice1,dice2,times,rolls[13] = {0}; srand(time(NULL)); printf("This program simulates the rolling of a pair of dice\n"); printf("How many times do you want to roll the dice?(Higher the number longer the waiting time): "); scanf("%d",×); for(int i = 0; i < times; i++){ dice1 = rand() % 6 + 1; dice2 = rand() % 6 + 1; rolls[dice1 + dice2]+=1; } printf("The number of times each sum was rolled is:\n"); for(int i = 2; i <= 12; i++){ printf("%d: rolled %d times, or %f%c of the times\n",i,rolls[i],percent(rolls[i],times),(char)37); } } ================================================ FILE: 00_Alternate_Languages/33_Dice/Julia/Dice.jl ================================================ #= Port of Dice from BASIC Computer Games (1978) This "game" simulates a given number of dice rolls, and returns the count for each possible total. The only change that has been made from the original program, is that when asking if the user wants to play again, any string starting with y or Y will be accepted, instead of only YES. =# function main() # Array to store the counts for each total. # There are 11 possible totals. counts = [0 for i in 1:11] # Print intro text println("\n Dice") println("Creative Computing Morristown, New Jersey") println("\n\n") println("This program simulates the rolling of a") println("pair of dice.") println("You enter the number of times you want the computer to") println("'roll' the dice. Watch out, very large numbers take") println("a long time. In particular, numbers over 5000.") still_playing = true while still_playing println() print("How many rolls? ") # Get user input for number of dice rolls rolls = readline() rolls = parse(Int64, rolls) # Roll dice the specified number of times and update total count for _ in 1:rolls dice_roll = rand(1:6, 2) dice_sum = sum(dice_roll) # The index is one less than the sum, as a sum of 1 is impossible, # the array will only have 11 values counts[dice_sum-1] += 1 end # Display results println("\nTotal Spots Number of Times") for i in 1:8 print(" ") print(i+1) print(" ") println(counts[i]) end for i in 9:11 print(" ") print(i+1) print(" ") println(counts[i]) end # Ask try again print("\nTry Again? ") input = readline() if length(input) > 0 && uppercase(input)[1] == 'Y' # If game is continued, resets total counts counts = [0 for i in 1:11] else still_playing = false end end end if abspath(PROGRAM_FILE) == @__FILE__ main() end ================================================ FILE: 00_Alternate_Languages/33_Dice/Julia/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Python](https://www.julialang.org/) ================================================ FILE: 00_Alternate_Languages/33_Dice/MiniScript/README.md ================================================ 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. ================================================ FILE: 00_Alternate_Languages/33_Dice/MiniScript/dice.ms ================================================ // 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 ================================================ FILE: 00_Alternate_Languages/33_Dice/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/33_Dice/dice.bas ================================================ 2 PRINT TAB(34);"DICE" 4 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 6 PRINT:PRINT:PRINT 10 DIM F(12) 20 REM DANNY FREIDUS 30 PRINT "THIS PROGRAM SIMULATES THE ROLLING OF A" 40 PRINT "PAIR OF DICE." 50 PRINT "YOU ENTER THE NUMBER OF TIMES YOU WANT THE COMPUTER TO" 60 PRINT "'ROLL' THE DICE. WATCH OUT, VERY LARGE NUMBERS TAKE" 70 PRINT "A LONG TIME. IN PARTICULAR, NUMBERS OVER 5000." 80 FOR Q=1 TO 12 90 F(Q)=0 100 NEXT Q 110 PRINT:PRINT "HOW MANY ROLLS"; 120 INPUT X 130 FOR S=1 TO X 140 A=INT(6*RND(1)+1) 150 B=INT(6*RND(1)+1) 160 R=A+B 170 F(R)=F(R)+1 180 NEXT S 185 PRINT 190 PRINT "TOTAL SPOTS","NUMBER OF TIMES" 200 FOR V=2 TO 12 210 PRINT V,F(V) 220 NEXT V 221 PRINT 222 PRINT:PRINT "TRY AGAIN"; 223 INPUT Z$ 224 IF Z$="YES" THEN 80 240 END ================================================ FILE: 00_Alternate_Languages/33_Dice/go/main.go ================================================ package main import ( "bufio" "fmt" "math/rand" "os" "strconv" "strings" ) func printWelcome() { fmt.Println("\n Dice") fmt.Println("Creative Computing Morristown, New Jersey") fmt.Println() fmt.Println() fmt.Println("This program simulates the rolling of a") fmt.Println("pair of dice.") fmt.Println("You enter the number of times you want the computer to") fmt.Println("'roll' the dice. Watch out, very large numbers take") fmt.Println("a long time. In particular, numbers over 5000.") fmt.Println() } func main() { printWelcome() scanner := bufio.NewScanner(os.Stdin) for { fmt.Println("\nHow many rolls? ") scanner.Scan() numRolls, err := strconv.Atoi(scanner.Text()) if err != nil { fmt.Println("Invalid input, try again...") continue } // We'll track counts of roll outcomes in a 13-element list. // The first two indices (0 & 1) are ignored, leaving just // the indices that match the roll values (2 through 12). results := make([]int, 13) for n := 0; n < numRolls; n++ { d1 := rand.Intn(6) + 1 d2 := rand.Intn(6) + 1 results[d1+d2] += 1 } // Display final results fmt.Println("\nTotal Spots Number of Times") for i := 2; i < 13; i++ { fmt.Printf(" %-14d%d\n", i, results[i]) } fmt.Println("\nTry again? ") scanner.Scan() if strings.ToUpper(scanner.Text()) == "Y" { continue } else { os.Exit(1) } } } ================================================ FILE: 00_Alternate_Languages/33_Dice/nim/dice.nim ================================================ 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") ================================================ FILE: 00_Alternate_Languages/34_Digits/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/34_Digits/MiniScript/digits.ms ================================================ 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." ================================================ FILE: 00_Alternate_Languages/34_Digits/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/34_Digits/digits.bas ================================================ 10 PRINT TAB(33);"DIGITS" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 30 PRINT:PRINT:PRINT 210 PRINT "THIS IS A GAME OF GUESSING." 220 PRINT "FOR INSTRUCTIONS, TYPE '1', ELSE TYPE '0'"; 230 INPUT E 240 IF E=0 THEN 360 250 PRINT 260 PRINT "PLEASE TAKE A PIECE OF PAPER AND WRITE DOWN" 270 PRINT "THE DIGITS '0', '1', OR '2' THIRTY TIMES AT RANDOM." 280 PRINT "ARRANGE THEM IN THREE LINES OF TEN DIGITS EACH." 290 PRINT "I WILL ASK FOR THEN TEN AT A TIME." 300 PRINT "I WILL ALWAYS GUESS THEM FIRST AND THEN LOOK AT YOUR" 310 PRINT "NEXT NUMBER TO SEE IF I WAS RIGHT. BY PURE LUCK," 320 PRINT "I OUGHT TO BE RIGHT TEN TIMES. BUT I HOPE TO DO BETTER" 330 PRINT "THAN THAT *****" 340 PRINT:PRINT 360 READ A,B,C 370 DATA 0,1,3 380 DIM M(26,2),K(2,2),L(8,2) 400 FOR I=0 TO 26: FOR J=0 TO 2: M(I,J)=1: NEXT J: NEXT I 410 FOR I=0 TO 2: FOR J=0 TO 2: K(I,J)=9: NEXT J: NEXT I 420 FOR I=0 TO 8: FOR J=0 TO 2: L(I,J)=3: NEXT J: NEXT I 450 L(0,0)=2: L(4,1)=2: L(8,2)=2 480 Z=26: Z1=8: Z2=2 510 X=0 520 FOR T=1 TO 3 530 PRINT 540 PRINT "TEN NUMBERS, PLEASE"; 550 INPUT N(1),N(2),N(3),N(4),N(5),N(6),N(7),N(8),N(9),N(10) 560 FOR I=1 TO 10 570 W=N(I)-1 580 IF W=SGN(W) THEN 620 590 PRINT "ONLY USE THE DIGITS '0', '1', OR '2'." 600 PRINT "LET'S TRY AGAIN.":GOTO 530 620 NEXT I 630 PRINT: PRINT "MY GUESS","YOUR NO.","RESULT","NO. RIGHT":PRINT 660 FOR U=1 TO 10 670 N=N(U): S=0 690 FOR J=0 TO 2 700 S1=A*K(Z2,J)+B*L(Z1,J)+C*M(Z,J) 710 IF S>S1 THEN 760 720 IF S<S1 THEN 740 730 IF RND(1)<.5 THEN 760 740 S=S1: G=J 760 NEXT J 770 PRINT " ";G," ";N(U), 780 IF G=N(U) THEN 810 790 PRINT " WRONG",X 800 GOTO 880 810 X=X+1 820 PRINT " RIGHT",X 830 M(Z,N)=M(Z,N)+1 840 L(Z1,N)=L(Z1,N)+1 850 K(Z2,N)=K(Z2,N)+1 860 Z=Z-INT(Z/9)*9 870 Z=3*Z+N(U) 880 Z1=Z-INT(Z/9)*9 890 Z2=N(U) 900 NEXT U 910 NEXT T 920 PRINT 930 IF X>10 THEN 980 940 IF X<10 THEN 1010 950 PRINT "I GUESSED EXACTLY 1/3 OF YOUR NUMBERS." 960 PRINT "IT'S A TIE GAME." 970 GOTO 1030 980 PRINT "I GUESSED MORE THAN 1/3 OF YOUR NUMBERS." 990 PRINT "I WIN.": FOR Q=1 TO 10: PRINT CHR$(7);: NEXT Q 1000 GOTO 1030 1010 PRINT "I GUESSED LESS THAN 1/3 OF YOUR NUMBERS." 1020 PRINT "YOU BEAT ME. CONGRATULATIONS *****" 1030 PRINT 1040 PRINT "DO YOU WANT TO TRY AGAIN (1 FOR YES, 0 FOR NO)"; 1060 INPUT X 1070 IF X=1 THEN 400 1080 PRINT:PRINT "THANKS FOR THE GAME." 1090 END ================================================ FILE: 00_Alternate_Languages/34_Digits/go/main.go ================================================ package main import ( "bufio" "fmt" "math/rand" "os" "strconv" "time" ) func printIntro() { fmt.Println(" DIGITS") fmt.Println(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") fmt.Println() fmt.Println() fmt.Println("THIS IS A GAME OF GUESSING.") } func readInteger(prompt string) int { scanner := bufio.NewScanner(os.Stdin) for { fmt.Println(prompt) scanner.Scan() response, err := strconv.Atoi(scanner.Text()) if err != nil { fmt.Println("INVALID INPUT, TRY AGAIN... ") continue } return response } } func printInstructions() { fmt.Println() fmt.Println("PLEASE TAKE A PIECE OF PAPER AND WRITE DOWN") fmt.Println("THE DIGITS '0', '1', OR '2' THIRTY TIMES AT RANDOM.") fmt.Println("ARRANGE THEM IN THREE LINES OF TEN DIGITS EACH.") fmt.Println("I WILL ASK FOR THEN TEN AT A TIME.") fmt.Println("I WILL ALWAYS GUESS THEM FIRST AND THEN LOOK AT YOUR") fmt.Println("NEXT NUMBER TO SEE IF I WAS RIGHT. BY PURE LUCK,") fmt.Println("I OUGHT TO BE RIGHT TEN TIMES. BUT I HOPE TO DO BETTER") fmt.Println("THAN THAT *****") fmt.Println() } func readTenNumbers() []int { numbers := make([]int, 10) numbers[0] = readInteger("FIRST NUMBER: ") for i := 1; i < 10; i++ { numbers[i] = readInteger("NEXT NUMBER:") } return numbers } func printSummary(correct int) { fmt.Println() if correct > 10 { fmt.Println() fmt.Println("I GUESSED MORE THAN 1/3 OF YOUR NUMBERS.") fmt.Println("I WIN.\u0007") } else if correct < 10 { fmt.Println("I GUESSED LESS THAN 1/3 OF YOUR NUMBERS.") fmt.Println("YOU BEAT ME. CONGRATULATIONS *****") } else { fmt.Println("I GUESSED EXACTLY 1/3 OF YOUR NUMBERS.") fmt.Println("IT'S A TIE GAME.") } } func buildArray(val, row, col int) [][]int { a := make([][]int, row) for r := 0; r < row; r++ { b := make([]int, col) for c := 0; c < col; c++ { b[c] = val } a[r] = b } return a } func main() { rand.Seed(time.Now().UnixNano()) printIntro() if readInteger("FOR INSTRUCTIONS, TYPE '1', ELSE TYPE '0' ? ") == 1 { printInstructions() } a := 0 b := 1 c := 3 m := buildArray(1, 27, 3) k := buildArray(9, 3, 3) l := buildArray(3, 9, 3) for { l[0][0] = 2 l[4][1] = 2 l[8][2] = 2 z := float64(26) z1 := float64(8) z2 := 2 runningCorrect := 0 var numbers []int for round := 1; round <= 4; round++ { validNumbers := false for !validNumbers { numbers = readTenNumbers() validNumbers = true for _, n := range numbers { if n < 0 || n > 2 { fmt.Println("ONLY USE THE DIGITS '0', '1', OR '2'.") fmt.Println("LET'S TRY AGAIN.") validNumbers = false break } } } fmt.Printf("\n%-14s%-14s%-14s%-14s\n", "MY GUESS", "YOUR NO.", "RESULT", "NO. RIGHT") for _, n := range numbers { s := 0 myGuess := 0 for j := 0; j < 3; j++ { s1 := a*k[z2][j] + b*l[int(z1)][j] + c*m[int(z)][j] if s < s1 { s = s1 myGuess = j } else if s1 == s && rand.Float64() > 0.5 { myGuess = j } } result := "" if myGuess != n { result = "WRONG" } else { runningCorrect += 1 result = "RIGHT" m[int(z)][n] = m[int(z)][n] + 1 l[int(z1)][n] = l[int(z1)][n] + 1 k[int(z2)][n] = k[int(z2)][n] + 1 z = z - (z/9)*9 z = 3.0*z + float64(n) } fmt.Printf("\n%-14d%-14d%-14s%-14d\n", myGuess, n, result, runningCorrect) z1 = z - (z/9)*9 z2 = n } printSummary(runningCorrect) if readInteger("\nDO YOU WANT TO TRY AGAIN (1 FOR YES, 0 FOR NO) ? ") != 1 { fmt.Println("\nTHANKS FOR THE GAME.") os.Exit(0) } } } } ================================================ FILE: 00_Alternate_Languages/35_Even_Wins/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/35_Even_Wins/MiniScript/evenwins.ms ================================================ 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" ================================================ FILE: 00_Alternate_Languages/35_Even_Wins/MiniScript/gameofevenwins.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/35_Even_Wins/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/35_Even_Wins/evenwins.bas ================================================ 1 PRINT TAB(31);"EVEN WINS" 2 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 3 PRINT:PRINT 4 Y1=0 10 M1=0 20 DIM M(20),Y(20) 30 PRINT " THIS IS A TWO PERSON GAME CALLED 'EVEN WINS.'" 40 PRINT "TO PLAY THE GAME, THE PLAYERS NEED 27 MARBLES OR" 50 PRINT "OTHER OBJECTS ON A TABLE." 60 PRINT 70 PRINT 80 PRINT " THE 2 PLAYERS ALTERNATE TURNS, WITH EACH PLAYER" 90 PRINT "REMOVING FROM 1 TO 4 MARBLES ON EACH MOVE. THE GAME" 100 PRINT "ENDS WHEN THERE ARE NO MARBLES LEFT, AND THE WINNER" 110 PRINT "IS THE ONE WITH AN EVEN NUMBER OF MARBLES." 120 PRINT 130 PRINT 140 PRINT " THE ONLY RULES ARE THAT (1) YOU MUST ALTERNATE TURNS," 150 PRINT "(2) YOU MUST TAKE BETWEEN 1 AND 4 MARBLES EACH TURN," 160 PRINT "AND (3) YOU CANNOT SKIP A TURN." 170 PRINT 180 PRINT 190 PRINT 200 PRINT " TYPE A '1' IF YOU WANT TO GO FIRST, AND TYPE" 210 PRINT "A '0' IF YOU WANT ME TO GO FIRST." 220 INPUT C 225 PRINT 230 IF C=0 THEN 250 240 GOTO 1060 250 T=27 260 M=2 270 PRINT:PRINT "TOTAL=";T:PRINT 280 M1=M1+M 290 T=T-M 300 PRINT "I PICK UP";M;"MARBLES." 310 IF T=0 THEN 880 320 PRINT:PRINT "TOTAL=";T 330 PRINT 340 PRINT " AND WHAT IS YOUR NEXT MOVE, MY TOTAL IS";M1 350 INPUT Y 360 PRINT 370 IF Y<1 THEN 1160 380 IF Y>4 THEN 1160 390 IF Y<=T THEN 430 400 PRINT " YOU HAVE TRIED TO TAKE MORE MARBLES THAN THERE ARE" 410 PRINT "LEFT. TRY AGAIN." 420 GOTO 350 430 Y1=Y1+Y 440 T=T-Y 450 IF T=0 THEN 880 460 PRINT "TOTAL=";T 470 PRINT 480 PRINT "YOUR TOTAL IS";Y1 490 IF T<.5 THEN 880 500 R=T-6*INT(T/6) 510 IF INT(Y1/2)=Y1/2 THEN 700 520 IF T<4.2 THEN 580 530 IF R>3.4 THEN 620 540 M=R+1 550 M1=M1+M 560 T=T-M 570 GOTO 300 580 M=T 590 T=T-M 600 GOTO 830 610 REM 250 IS WHERE I WIN. 620 IF R<4.7 THEN 660 630 IF R>3.5 THEN 660 640 M=1 650 GOTO 670 660 M=4 670 T=T-M 680 M1=M1+M 690 GOTO 300 700 REM I AM READY TO ENCODE THE STRAT FOR WHEN OPP TOT IS EVEN 710 IF R<1.5 THEN 1020 720 IF R>5.3 THEN 1020 730 M=R-1 740 M1=M1+M 750 T=T-M 760 IF T<.2 THEN 790 770 REM IS # ZERO HERE 780 GOTO 300 790 REM IS = ZERO HERE 800 PRINT "I PICK UP";M;"MARBLES." 810 PRINT 820 GOTO 880 830 REM THIS IS WHERE I WIN 840 PRINT "I PICK UP";M;"MARBLES." 850 PRINT 860 PRINT "TOTAL = 0" 870 M1=M1+M 880 PRINT "THAT IS ALL OF THE MARBLES." 890 PRINT 900 PRINT " MY TOTAL IS";M1;", YOUR TOTAL IS";Y1 910 PRINT 920 IF INT(M1/2)=M1/2 THEN 950 930 PRINT " YOU WON. DO YOU WANT TO PLAY" 940 GOTO 960 950 PRINT " I WON. DO YOU WANT TO PLAY" 960 PRINT "AGAIN? TYPE 1 FOR YES AND 0 FOR NO." 970 INPUT A1 980 IF A1=0 THEN 1030 990 M1=0 1000 Y1=0 1010 GOTO 200 1020 GOTO 640 1030 PRINT 1040 PRINT "OK. SEE YOU LATER." 1050 GOTO 1230 1060 T=27 1070 PRINT 1080 PRINT 1090 PRINT 1100 PRINT "TOTAL=";T 1110 PRINT 1120 PRINT 1130 PRINT "WHAT IS YOUR FIRST MOVE"; 1140 INPUT Y 1150 GOTO 360 1160 PRINT 1170 PRINT "THE NUMBER OF MARBLES YOU TAKE MUST BE A POSITIVE" 1180 PRINT "INTEGER BETWEEN 1 AND 4." 1190 PRINT 1200 PRINT " WHAT IS YOUR NEXT MOVE?" 1210 PRINT 1220 GOTO 350 1230 END ================================================ FILE: 00_Alternate_Languages/35_Even_Wins/gameofevenwins.bas ================================================ 1 PRINT TAB(28);"GAME OF EVEN WINS" 2 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 3 PRINT:PRINT 4 INPUT "DO YOU WANT INSTRUCTIONS (YES OR NO)";A$:PRINT 5 IF A$="NO" THEN 20 6 PRINT "THE GAME IS PLAYED AS FOLLOWS:":PRINT 7 PRINT "AT THE BEGINNING OF THE GAME, A RANDOM NUMBER OF CHIPS ARE" 8 PRINT "PLACED ON THE BOARD. THE NUMBER OF CHIPS ALWAYS STARTS" 9 PRINT "AS AN ODD NUMBER. ON EACH TURN, A PLAYER MUST TAKE ONE," 10 PRINT "TWO, THREE, OR FOUR CHIPS. THE WINNER IS THE PLAYER WHO" 11 PRINT "FINISHES WITH A TOTAL NUMBER OF CHIPS THAT IS EVEN." 12 PRINT "THE COMPUTER STARTS OUT KNOWING ONLY THE RULES OF THE" 13 PRINT "GAME. IT GRADUALLY LEARNS TO PLAY WELL. IT SHOULD BE" 14 PRINT "DIFFICULT TO BEAT THE COMPUTER AFTER TWENTY GAMES IN A ROW." 15 PRINT "TRY IT!!!!": PRINT 16 PRINT "TO QUIT AT ANY TIME, TYPE A '0' AS YOUR MOVE.": PRINT 20 DIM R(1,5) 25 L=0: B=0 30 FOR I=0 TO 5 40 R(1,I)=4 50 R(0,I)=4 60 NEXT I 70 A=0: B=0 90 P=INT((13*RND(1)+9)/2)*2+1 100 IF P=1 THEN 530 110 PRINT "THERE ARE";P;"CHIPS ON THE BOARD." 120 E1=E 130 L1=L 140 E=(A/2-INT(A/2))*2 150 L=INT((P/6-INT(P/6))*6+.5) 160 IF R(E,L)>=P THEN 320 170 M=R(E,L) 180 IF M<=0 THEN 370 190 P=P-M 200 IF M=1 THEN 510 210 PRINT "COMPUTER TAKES";M;"CHIPS LEAVING";P;"... YOUR MOVE"; 220 B=B+M 230 INPUT M 240 M=INT(M) 250 IF M<1 THEN 450 260 IF M>4 THEN 460 270 IF M>P THEN 460 280 IF M=P THEN 360 290 P=P-M 300 A=A+M 310 GOTO 100 320 IF P=1 THEN 550 330 PRINT "COMPUTER TAKES";P;"CHIPS." 340 R(E,L)=P 350 B=B+P 360 IF B/2=INT(B/2) THEN 420 370 PRINT "GAME OVER ... YOU WIN!!!": PRINT 390 IF R(E,L)=1 THEN 480 400 R(E,L)=R(E,L)-1 410 GOTO 70 420 PRINT "GAME OVER ... I WIN!!!": PRINT 430 GOTO 70 450 IF M=0 THEN 570 460 PRINT M;"IS AN ILLEGAL MOVE ... YOUR MOVE"; 470 GOTO 230 480 IF R(E1,L1)=1 THEN 70 490 R(E1,L1)=R(E1,L1)-1 500 GOTO 70 510 PRINT "COMPUTER TAKES 1 CHIP LEAVING";P;"... YOUR MOVE"; 520 GOTO 220 530 PRINT "THERE IS 1 CHIP ON THE BOARD." 540 GOTO 120 550 PRINT "COMPUTER TAKES 1 CHIP." 560 GOTO 340 570 END ================================================ FILE: 00_Alternate_Languages/35_Even_Wins/go/evenwins.go ================================================ package main import ( "bufio" "fmt" "os" "strconv" "strings" ) const MAXTAKE = 4 type PlayerType int8 const ( HUMAN PlayerType = iota COMPUTER ) type Game struct { table int human int computer int } func NewGame() Game { g := Game{} g.table = 27 return g } func printIntro() { fmt.Println("Welcome to Even Wins!") fmt.Println("Based on evenwins.bas from Creative Computing") fmt.Println() fmt.Println("Even Wins is a two-person game. You start with") fmt.Println("27 marbles in the middle of the table.") fmt.Println() fmt.Println("Players alternate taking marbles from the middle.") fmt.Println("A player can take 1 to 4 marbles on their turn, and") fmt.Println("turns cannot be skipped. The game ends when there are") fmt.Println("no marbles left, and the winner is the one with an even") fmt.Println("number of marbles.") fmt.Println() } func (g *Game) printBoard() { fmt.Println() fmt.Printf(" marbles in the middle: %d\n", g.table) fmt.Printf(" # marbles you have: %d\n", g.human) fmt.Printf("# marbles computer has: %d\n", g.computer) fmt.Println() } func (g *Game) gameOver() { fmt.Println() fmt.Println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") fmt.Println("!! All the marbles are taken: Game Over!") fmt.Println("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") fmt.Println() g.printBoard() if g.human%2 == 0 { fmt.Println("You are the winner! Congratulations!") } else { fmt.Println("The computer wins: all hail mighty silicon!") } fmt.Println() } func getPlural(count int) string { m := "marble" if count > 1 { m += "s" } return m } func (g *Game) humanTurn() { scanner := bufio.NewScanner(os.Stdin) maxAvailable := MAXTAKE if g.table < MAXTAKE { maxAvailable = g.table } fmt.Println("It's your turn!") for { fmt.Printf("Marbles to take? (1 - %d) --> ", maxAvailable) scanner.Scan() n, err := strconv.Atoi(scanner.Text()) if err != nil { fmt.Printf("\n Please enter a whole number from 1 to %d\n", maxAvailable) continue } if n < 1 { fmt.Println("\n You must take at least 1 marble!") continue } if n > maxAvailable { fmt.Printf("\n You can take at most %d %s\n", maxAvailable, getPlural(maxAvailable)) continue } fmt.Printf("\nOkay, taking %d %s ...\n", n, getPlural(n)) g.table -= n g.human += n return } } func (g *Game) computerTurn() { marblesToTake := 0 fmt.Println("It's the computer's turn ...") r := float64(g.table - 6*int((g.table)/6)) if int(g.human/2) == g.human/2 { if r < 1.5 || r > 5.3 { marblesToTake = 1 } else { marblesToTake = int(r - 1) } } else if float64(g.table) < 4.2 { marblesToTake = 4 } else if r > 3.4 { if r < 4.7 || r > 3.5 { marblesToTake = 4 } } else { marblesToTake = int(r + 1) } fmt.Printf("Computer takes %d %s ...\n", marblesToTake, getPlural(marblesToTake)) g.table -= marblesToTake g.computer += marblesToTake } func (g *Game) play(playersTurn PlayerType) { g.printBoard() for { if g.table == 0 { g.gameOver() return } else if playersTurn == HUMAN { g.humanTurn() g.printBoard() playersTurn = COMPUTER } else { g.computerTurn() g.printBoard() playersTurn = HUMAN } } } func getFirstPlayer() PlayerType { scanner := bufio.NewScanner(os.Stdin) for { fmt.Println("Do you want to play first? (y/n) --> ") scanner.Scan() if strings.ToUpper(scanner.Text()) == "Y" { return HUMAN } else if strings.ToUpper(scanner.Text()) == "N" { return COMPUTER } else { fmt.Println() fmt.Println("Please enter 'y' if you want to play first,") fmt.Println("or 'n' if you want to play second.") fmt.Println() } } } func main() { scanner := bufio.NewScanner(os.Stdin) printIntro() for { g := NewGame() g.play(getFirstPlayer()) fmt.Println("\nWould you like to play again? (y/n) --> ") scanner.Scan() if strings.ToUpper(scanner.Text()) == "Y" { fmt.Println("\nOk, let's play again ...") } else { fmt.Println("\nOk, thanks for playing ... goodbye!") return } } } ================================================ FILE: 00_Alternate_Languages/36_Flip_Flop/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/36_Flip_Flop/MiniScript/flipflop.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/36_Flip_Flop/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/36_Flip_Flop/flipflop.bas ================================================ 2 PRINT TAB(32);"FLIPFLOP" 4 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 6 PRINT 10 REM *** CREATED BY MICHAEL CASS 15 DIM A$(20) 20 PRINT "THE OBJECT OF THIS PUZZLE IS TO CHANGE THIS:" 30 PRINT 40 PRINT "X X X X X X X X X X" 50 PRINT 60 PRINT "TO THIS:" 70 PRINT 80 PRINT "O O O O O O O O O O" 90 PRINT 100 PRINT "BY TYPING THE NUMBER CORRESPONDING TO THE POSITION OF THE" 110 PRINT "LETTER ON SOME NUMBERS, ONE POSITION WILL CHANGE, ON" 120 PRINT "OTHERS, TWO WILL CHANGE. TO RESET LINE TO ALL X'S, TYPE 0" 130 PRINT "(ZERO) AND TO START OVER IN THE MIDDLE OF A GAME, TYPE " 140 PRINT "11 (ELEVEN)." 170 PRINT 180 REM 190 Q=RND(1) 200 PRINT "HERE IS THE STARTING LINE OF X'S." 210 PRINT 220 C=0 230 PRINT "1 2 3 4 5 6 7 8 9 10" 240 PRINT "X X X X X X X X X X" 250 PRINT 260 REM 270 FOR X=1 TO 10 280 A$(X)="X" 290 NEXT X 300 GOTO 320 310 PRINT "ILLEGAL ENTRY--TRY AGAIN." 320 PRINT "INPUT THE NUMBER"; 330 INPUT N 340 IF N<>INT(N) THEN 310 350 IF N=11 THEN 180 360 IF N>11 THEN 310 370 IF N=0 THEN 230 380 IF M=N THEN 510 390 M=N 400 IF A$(N)="O" THEN 480 410 A$(N)="O" 420 R=TAN(Q+N/Q-N)-SIN(Q/N)+336*SIN(8*N) 430 N=R-INT(R) 440 N=INT(10*N) 450 IF A$(N)="O" THEN 480 460 A$(N)="O" 470 GOTO 610 480 A$(N)="X" 490 IF M=N THEN 420 500 GOTO 610 510 IF A$(N)="O" THEN 590 520 A$(N)="O" 530 R=.592*(1/TAN(Q/N+Q))/SIN(N*2+Q)-COS(N) 540 N=R-INT(R) 550 N=INT(10*N) 560 IF A$(N)="O" THEN 590 570 A$(N)="O" 580 GOTO 610 590 A$(N)="X" 600 IF M=N THEN 530 610 PRINT "1 2 3 4 5 6 7 8 9 10" 620 FOR Z=1 TO 10: PRINT A$(Z);" ";: NEXT Z 630 C=C+1 640 PRINT 650 FOR Z=1 TO 10 660 IF A$(Z)<>"O" THEN 320 670 NEXT Z 680 IF C>12 THEN 710 690 PRINT "VERY GOOD. YOU GUESSED IT IN ONLY";C;"GUESSES." 700 GOTO 720 710 PRINT "TRY HARDER NEXT TIME. IT TOOK YOU";C;"GUESSES." 720 PRINT "DO YOU WANT TO TRY ANOTHER PUZZLE"; 730 INPUT X$ 740 IF LEFT$(X$,1)="N" THEN 780 760 PRINT 770 GOTO 180 780 END ================================================ FILE: 00_Alternate_Languages/37_Football/MiniScript/README.md ================================================ 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! ================================================ FILE: 00_Alternate_Languages/37_Football/MiniScript/football.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/37_Football/MiniScript/ftball.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/37_Football/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/37_Football/football.bas ================================================ 1 PRINT TAB(32);"FOOTBALL" 2 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 3 PRINT:PRINT:PRINT 100 REM 120 DIM A(20),B(20),C(40),H(2),T(2),W(2),X(2),Y(2),Z(2) 130 DIM M$(2),D(2),P$(20) 140 PRINT "PRESENTING N.F.U. FOOTBALL (NO FORTRAN USED)" 145 PRINT:PRINT 150 INPUT "DO YOU WANT INSTRUCTIONS";A$ 160 IF A$="NO" THEN 290 165 IF A$<>"YES" THEN 150 170 PRINT "THIS IS A FOOTBALL GAME FOR TWO TEAMS IN WHICH PLAYERS MUST" 180 PRINT "PREPARE A TAPE WITH A DATA STATEMENT (1770 FOR TEAM 1," 190 PRINT "1780 FOR TEAM 2) IN WHICH EACH TEAM SCRAMBLES NOS. 1-20" 195 PRINT "THESE NUMBERS ARE THEN ASSIGNED TO TWENTY GIVEN PLAYS." 200 PRINT"A LIST OF NOS. AND THEIR PLAYS IS PROVIDED WITH" 210 PRINT "BOTH TEAMS HAVING THE SAME PLAYS. THE MORE SIMILAR THE" 220 PRINT "PLAYS THE LESS YARDAGE GAINED. SCORES ARE GIVEN" 223 PRINT "WHENEVER SCORES ARE MADE. SCORES MAY ALSO BE OBTAINED" 225 PRINT "BY INPUTTING 99,99 FOR PLAY NOS. TO PUNT OR ATTEMPT A" 227 PRINT "FIELD GOAL, INPUT 77,77 FOR PLAY NUMBERS. QUESTIONS WILL BE" 230 PRINT "ASKED THEN. ON 4TH DOWN, YOU WILL ALSO BE ASKED WHETHER" 240 PRINT "YOU WANT TO PUNT OR ATTEMPT A FIELD GOAL. IF THE ANSWER TO" 250 PRINT "BOTH QUESTIONS IS NO IT WILL BE ASSUMED YOU WANT TO" 260 PRINT "TRY AND GAIN YARDAGE. ANSWER ALL QUESTIONS YES OR NO." 270 PRINT "THE GAME IS PLAYED UNTIL PLAYERS TERMINATE (CONTROL-C)." 280 PRINT "PLEASE PREPARE A TAPE AND RUN.": STOP 290 PRINT:PRINT "PLEASE INPUT SCORE LIMIT ON GAME";:INPUT E 300 FOR I=1 TO 40: READ N: IF I>20 THEN 350 330 A(N)=I: GOTO 360 350 B(N)=I-20 360 C(I)=N: NEXT I 370 FOR I=1 TO 20: READ P$(I): NEXT I 380 L=0: T=1 410 PRINT "TEAM";T;"PLAY CHART" 420 PRINT "NO. PLAY" 430 FOR I=1 TO 20 440 REM 450 PRINT C(I+L);TAB(6);P$(I) 460 NEXT I 630 L=L+20:T=2 640 PRINT 650 PRINT "TEAR OFF HERE----------------------------------------------" 660 FOR X=1 TO 11: PRINT: NEXT X 670 FOR Z=1 TO 3000: NEXT Z 680 IF L=20 THEN 410 690 D(1)=0: D(2)=3: M$(1)="--->": M$(2)="<---" 700 H(1)=0: H(2)=0: T(1)=2: T(2)=1 710 W(1)=-1: W(2)=1: X(1)=100: X(2)=0 720 Y(1)=1: Y(2)=-1: Z(1)=0: Z(2)=100 725 GOSUB 1910 730 PRINT "TEAM 1 DEFENDS 0 YD GOAL -- TEAM 2 DEFENDS 100 YD GOAL." 740 T=INT(2*RND(1)+1) 760 PRINT: PRINT "THE COIN IS FLIPPED" 765 P=X(T)-Y(T)*40 770 GOSUB 1860: PRINT : PRINT "TEAM";T;"RECEIVES KICK-OFF" 780 K=INT(26*RND(1)+40) 790 P=P-Y(T)*K 794 IF W(T)*P<Z(T)+10 THEN 810 795 PRINT: PRINT "BALL WENT OUT OF ENDZONE --AUTOMATIC TOUCHBACK--" 796 GOTO 870 810 PRINT "BALL WENT";K;"YARDS. NOW ON";P:GOSUB 1900 830 PRINT "TEAM";T;"DO YOU WANT TO RUNBACK";:INPUT A$ 840 IF A$="YES" THEN 1430 845 IF A$<>"NO" THEN 830 850 IF W(T)*P<Z(T) THEN 880 870 P=Z(T)-W(T)*20 880 D=1: S=P 885 FOR I=1 TO 72: PRINT "=";: NEXT I 890 PRINT: PRINT "TEAM";T;"DOWN";D;"ON";P 893 IF D<>1 THEN 900 895 IF Y(T)*(P+Y(T)*10)>=X(T) THEN 898 897 C=4: GOTO 900 898 C=8 900 IF C=8 THEN 904 901 PRINT TAB(27);10-(Y(T)*P-Y(T)*S);"YARDS TO 1ST DOWN" 902 GOTO 910 904 PRINT TAB(27);X(T)-Y(T)*P;"YARDS" 910 GOSUB 1900: IF D=4 THEN 1180 920 REM 930 U=INT(3*RND(0)-1): GOTO 940 936 PRINT "ILLEGAL PLAY NUMBER, CHECK AND" 940 PRINT "INPUT OFFENSIVE PLAY, DEFENSIVE PLAY"; 950 IF T=2 THEN 970 960 INPUT P1,P2: GOTO 975 970 INPUT P2,P1 975 IF P1=77 THEN 1180 980 IF P1>20 THEN 1800 985 IF P1<1 THEN 1800 990 IF P2>20 THEN 1800 992 IF P2<1 THEN 1800 995 P1=INT(P1): P2=INT(P2) 1000 Y=INT(ABS(A(P1)-B(P2))/19*((X(T)-Y(T)*P+25)*RND(1)-15)) 1005 PRINT: IF T=2 THEN 1015 1010 IF A(P1)<11 THEN 1048 1012 GOTO 1020 1015 IF B(P2)<11 THEN 1048 1020 IF U<>0 THEN 1035 1025 PRINT "PASS INCOMPLETE TEAM";T 1030 Y=0: GOTO 1050 1035 G=RND(1): IF G>.025 THEN 1040 1037 IF Y>2 THEN 1045 1040 PRINT "QUARTERBACK SCRAMBLED": GOTO 1050 1045 PRINT "PASS COMPLETED": GOTO 1050 1048 PRINT "THE BALL WAS RUN" 1050 P=P-W(T)*Y 1060 PRINT: PRINT "NET YARDS GAINED ON DOWN";D;"ARE ";Y 1070 G=RND(1): IF G>.025 THEN 1110 1080 PRINT: PRINT "** LOSS OF POSSESSION FROM TEAM";T;"TO TEAM";T(T) 1100 GOSUB 1850: PRINT: T=T(T): GOTO 830 1110 IF Y(T)*P>=X(T) THEN 1320 1120 IF W(T)*P>=Z(T) THEN 1230 1130 IF Y(T)*P-Y(T)*S>=10 THEN 880 1140 D=D+1: IF D<>5 THEN 885 1160 PRINT: PRINT "CONVERSION UNSUCCESSFUL TEAM";T:T=T(T) 1170 GOSUB 1850: GOTO 880 1180 PRINT "DOES TEAM";T;"WANT TO PUNT";: INPUT A$ 1185 IF A$="NO" THEN 1200 1187 IF A$<>"YES" THEN 1180 1190 PRINT:PRINT "TEAM";T;"WILL PUNT": G=RND(1): IF G<.025 THEN 1080 1195 GOSUB 1850: K=INT(25*RND(1)+35): T=T(T): GOTO 790 1200 PRINT "DOES TEAM";T;"WANT TO ATTEMPT A FIELD GOAL";: INPUT A$ 1210 IF A$="YES" THEN 1640 1215 IF A$<>"NO" THEN 1200 1217 GOTO 920 1230 PRINT: PRINT "SAFETY AGAINST TEAM";T;"**********************OH-OH" 1240 H(T(T))=H(T(T))+2: GOSUB 1810 1280 PRINT"TEAM";T;"DO YOU WANT TO PUNT INSTEAD OF A KICKOFF";:INPUT A$ 1290 P=Z(T)-W(T)*20: IF A$="YES" THEN 1190 1320 PRINT: PRINT "TOUCHDOWN BY TEAM";T;"*********************YEA TEAM" 1340 Q=7: G=RND(1): IF G>.1 THEN 1380 1360 Q=6: PRINT "EXTRA POINT NO GOOD": GOTO 1390 1380 PRINT "EXTRA POINT GOOD" 1390 H(T)=H(T)+Q: GOSUB 1810 1420 T=T(T): GOTO 765 1430 K=INT(9*RND(0)+1) 1440 R=INT(((X(T)-Y(T)*P+25)*RND(1)-15)/K) 1460 P=P-W(T)*R 1480 PRINT:PRINT "RUNBACK TEAM";T;R;"YARDS" 1485 G=RND(1): IF G<.025 THEN 1080 1490 IF Y(T)*P>=X(T) THEN 1320 1500 IF W(T)*P>=Z(T) THEN 1230 1510 GOTO 880 1640 PRINT: PRINT "TEAM";T;"WILL ATTEMPT A FIELD GOAL" 1645 G=RND(1): IF G<.025 THEN 1080 1650 F=INT(35*RND(1)+20) 1660 PRINT: PRINT "KICK IS";F;"YARDS LONG" 1680 P=P-W(T)*F: G=RND(1) 1690 IF G<.35 THEN 1735 1700 IF Y(T)*P<X(T) THEN 1740 1710 PRINT "FIELD GOAL GOOD FOR TEAM";T;"*********************YEA" 1720 Q=3: GOTO 1390 1735 PRINT "BALL WENT WIDE" 1740 PRINT "FIELD GOAL UNSUCCESFUL TEAM";T;"-----------------TOO BAD" 1742 GOSUB 1850: IF Y(T)*P<X(T)+10 THEN 1745 1744 T=T(T): GOTO 794 1745 PRINT: PRINT "BALL NOW ON";P 1750 T=T(T): GOSUB 1900: GOTO 830 1770 DATA 17,8,4,14,19,3,10,1,7,11,15,9,5,20,13,18,16,2,12,6 1780 DATA 20,2,17,5,8,18,12,11,1,4,19,14,10,7,9,15,6,13,16,3 1790 DATA "PITCHOUT","TRIPLE REVERSE","DRAW","QB SNEAK","END AROUND" 1792 DATA "DOUBLE REVERSE","LEFT SWEEP","RIGHT SWEEP","OFF TACKLE" 1794 DATA "WISHBONE OPTION","FLARE PASS","SCREEN PASS" 1796 DATA "ROLL OUT OPTION","RIGHT CURL","LEFT CURL","WISHBONE OPTION" 1798 DATA "SIDELINE PASS","HALF-BACK OPTION","RAZZLE-DAZZLE","BOMB!!!!" 1800 IF P1<>99 THEN 936 1810 PRINT: PRINT "TEAM 1 SCORE IS";H(1) 1820 PRINT "TEAM 2 SCORE IS";H(2): PRINT 1825 IF H(T)<E THEN 1830 1827 PRINT "TEAM";T;"WINS*******************": GOTO 2000 1830 IF P1=99 THEN 940 1835 RETURN 1850 PRINT 1860 FOR X=1 TO 72: PRINT "+";: NEXT X: PRINT 1870 RETURN 1900 PRINT TAB(D(T)+5+P/2);M$(T) 1910 PRINT "TEAM 1 [0 10 20 30 40 50 60 70 80 90"; 1915 PRINT " 100] TEAM 2" 1920 PRINT 1930 RETURN 2000 END ================================================ FILE: 00_Alternate_Languages/37_Football/ftball.bas ================================================ 10 PRINT TAB(33);"FTBALL" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 30 PRINT:PRINT 220 PRINT "THIS IS DARTMOUTH CHAMPIONSHIP FOOTBALL.":PRINT 230 PRINT "YOU WILL QUARTERBACK DARTMOUTH. CALL PLAYS AS FOLLOWS:" 240 PRINT "1= SIMPLE RUN; 2= TRICKY RUN; 3= SHORT PASS;" 250 PRINT "4= LONG PASS; 5= PUNT; 6= QUICK KICK; 7= PLACE KICK." 260 PRINT 270 PRINT "CHOOSE YOUR OPPONENT"; 280 INPUT O$(1) 290 O$(0)="DARTMOUTH" 300 PRINT 310 LET S(0)=0: LET S(1)=0 320 REM 330 DIM L$(20) 340 FOR I=1 TO 20: READ L$(I): NEXT I 350 DATA "KICK","RECEIVE"," YARD ","RUN BACK FOR ","BALL ON " 360 DATA "YARD LINE"," SIMPLE RUN"," TRICKY RUN"," SHORT PASS" 370 DATA " LONG PASS","PUNT"," QUICK KICK "," PLACE KICK"," LOSS " 380 DATA " NO GAIN","GAIN "," TOUCHDOWN "," TOUCHBACK ","SAFETY***" 385 DATA "JUNK" 390 LET P=INT(RND(1)*2) 400 PRINT O$(P);" WON THE TOSS" 410 DEF FNF(X)=1-2*P 420 DEF FNG(Z)=P*(X1-X)+(1-P)*(X-X1) 430 IF P=0 THEN 470 440 PRINT O$(1);" ELECTS TO RECEIVE." 450 PRINT 460 GOTO 580 470 PRINT "DO YOU ELECT TO KICK OR RECEIVE"; 480 INPUT A$ 490 PRINT 500 FOR E=1 TO 2 510 IF A$=L$(E) THEN 550 520 NEXT E 530 PRINT "INCORRECT ANSWER. PLEASE TYPE 'KICK' OR 'RECEIVE'"; 540 GOTO 480 550 IF E=2 THEN 580 560 LET P=1 580 LET X=40+(1-P)*20 590 LET Y=INT(200*(RND(1)-.5)^3+55) 600 PRINT Y;L$(3);" KICKOFF" 610 LET X=X-FNF(1)*Y 620 IF ABS(X-50)>=50 THEN 700 630 LET Y=INT(50*RND(1)^2)+(1-P)*INT(50*RND(1)^4) 640 LET X=X+FNF(1)*Y 650 IF ABS(X-50)>=50 THEN 655 651 PRINT Y;L$(3);" RUNBACK" 652 GOTO 720 655 PRINT L$(4); 660 GOTO 2600 700 PRINT "TOUCHBACK FOR ";O$(P);"." 710 LET X=20+P*60 720 REM FIRST DOWN 730 GOSUB 800 740 LET X1=X 750 LET D=1 760 PRINT:PRINT "FIRST DOWN ";O$(P);"***" 770 PRINT 780 PRINT 790 GOTO 860 800 REM PRINT POSITION 810 IF X>50 THEN 840 820 PRINT L$(5);O$(0);X;L$(6) 830 GOTO 850 840 PRINT L$(5);O$(1);100-X;L$(6) 850 RETURN 860 REM NEW PLAY 870 LET T=T+1 880 IF T=30 THEN 1060 890 IF T<50 THEN 940 900 IF RND(1)>.2 THEN 940 910 PRINT "END OF GAME ***" 920 PRINT "FINAL SCORE: ";O$(0);": ";S(0);" ";O$(1);": ";S(1) 930 STOP 940 IF P=1 THEN 1870 950 PRINT "NEXT PLAY"; 960 INPUT Z 970 IF Z<>INT(Z) THEN 990 980 IF ABS(Z-4)<=3 THEN 1010 990 PRINT "ILLEGAL PLAY NUMBER, RETYPE"; 1000 GOTO 960 1010 LET F=0 1020 PRINT L$(Z+6);". "; 1030 LET R=RND(1)*(.98+FNF(1)*.02) 1040 LET R1=RND(1) 1050 ON Z GOTO 1110,1150,1260,1480,1570,1570,1680 1060 REM JEAN'S SPECIAL 1070 IF RND(1)> 1/3 THEN 940 1080 PRINT "GAME DELAYED. DOG ON FIELD." 1090 PRINT 1100 GOTO 940 1110 REM SIMPLE RUN 1120 LET Y=INT(24*(R-.5)^3+3) 1130 IF RND(1)<.05 THEN 1180 1140 GOTO 2190 1150 REM TRICKY RUN 1160 LET Y=INT(20*R-5) 1170 IF RND(1)>.1 THEN 2190 1180 LET F=-1 1190 LET X3=X 1200 LET X=X+FNF(1)*Y 1210 IF ABS(X-50)>=50 THEN 1240 1220 PRINT "*** FUMBLE AFTER "; 1230 GOTO 2230 1240 PRINT "*** FUMBLE." 1250 GOTO 2450 1260 REM SHORT PASS 1270 LET Y=INT(60*(R1-.5)^3+10) 1280 IF R<.05 THEN 1330 1290 IF R<.15 THEN 1390 1300 IF R<.55 THEN 1420 1310 PRINT "COMPLETE. "; 1320 GOTO 2190 1330 IF D=4 THEN 1420 1340 PRINT "INTERCEPTED." 1350 LET F=-1 1360 LET X=X+FNF(1)*Y 1370 IF ABS(X-50)>=50 THEN 2450 1380 GOTO 2300 1390 PRINT "PASSER TACKLED. "; 1400 LET Y=-INT(10*R1) 1410 GOTO 2190 1420 LET Y=0 1430 IF RND(1)<.3 THEN 1460 1440 PRINT "INCOMPLETE. "; 1450 GOTO 2190 1460 PRINT "BATTED DOWN. "; 1470 GOTO 2190 1480 REM LONG PASS 1490 LET Y=INT(160*(R1-.5)^3+30) 1500 IF R<.1 THEN 1330 1510 IF R<.3 THEN 1540 1520 IF R<.75 THEN 1420 1530 GOTO 1310 1540 PRINT "PASSER TACKLED. "; 1550 LET Y=-INT(15*R1+3) 1560 GOTO 2190 1570 REM PUNT OR KICK 1580 LET Y=INT(100*(R-.5)^3+35) 1590 IF D=4 THEN 1610 1600 LET Y=INT(Y*1.3) 1610 PRINT Y;L$(3);" PUNT" 1620 IF ABS(X+Y*FNF(1)-50)>=50 THEN 1670 1630 IF D<4 THEN 1670 1640 LET Y1=INT(R1^2*20) 1650 PRINT Y1;L$(3);" RUN BACK" 1660 LET Y=Y-Y1 1670 GOTO 1350 1680 REM PLACE KICK 1690 LET Y=INT(100*(R-.5)^3+35) 1700 IF R1>.15 THEN 1750 1710 PRINT "KICK IS BLOCKED ***" 1720 LET X=X-5*FNF(1) 1730 LET P=1-P 1740 GOTO 720 1750 LET X=X+FNF(1)*Y 1760 IF ABS(X-50)>=60 THEN 1810 1770 PRINT "KICK IS SHORT." 1780 IF ABS(X-50)>=50 THEN 2710 1790 P=1-P 1800 GOTO 630 1810 IF R1>.5 THEN 1840 1820 PRINT "KICK IS OFF TO THE SIDE." 1830 GOTO 2710 1840 PRINT "FIELD GOAL ***" 1850 LET S(P)=S(P)+3 1860 GOTO 2640 1870 REM OPPONENT'S PLAY 1880 IF D>1 THEN 1940 1890 IF RND(1)>1/3 THEN 1920 1900 LET Z=3 1910 GOTO 1010 1920 LET Z=1 1930 GOTO 1010 1940 IF D=4 THEN 2090 1950 IF 10+X-X1<5 THEN 1890 1960 IF X<5 THEN 1890 1970 IF X<=10 THEN 2160 1980 IF X>X1 THEN 2020 1990 LET A=INT(2*RND(1)) 2000 LET Z=2+A*2 2010 GOTO 1010 2020 IF D<3 THEN 1990 2030 IF X<45 THEN 1990 2040 IF RND(1)>1/4 THEN 2070 2050 LET Z=6 2060 GOTO 1010 2070 LET Z=4 2080 GOTO 1010 2090 IF X>30 THEN 2140 2100 IF 10+X-X1<3 THEN 1890 2110 IF X<3 THEN 1890 2120 LET Z=7 2130 GOTO 1010 2140 LET Z=5 2150 GOTO 1010 2160 LET A=INT(2*RND(1)) 2170 LET Z=2+A 2180 GOTO 1010 2190 REM GAIN OR LOSS 2200 LET X3=X 2210 LET X=X+FNF(1)*Y 2220 IF ABS(X-50)>=50 THEN 2450 2230 IF Y=0 THEN 2250 2240 PRINT ABS(Y);L$(3); 2250 PRINT L$(15+SGN(Y)) 2280 IF ABS(X3-50)>40 THEN 2300 2290 IF RND(1)<.1 THEN 2860 2300 GOSUB 800 2310 IF F=0 THEN 2340 2320 LET P=1-P 2330 GOTO 740 2340 IF FNG(1)>=10 THEN 740 2350 IF D=4 THEN 2320 2360 LET D=D+1 2370 PRINT "DOWN: ";D;" "; 2380 IF (X1-50)*FNF(1)<40 THEN 2410 2390 PRINT "GOAL TO GO" 2400 GOTO 2420 2410 PRINT "YARDS TO GO: ";10-FNG(1) 2420 PRINT 2430 PRINT 2440 GOTO 860 2450 REM BALL IN END-ZONE 2460 IF X>=100 THEN 2490 2470 LET E=0 2480 GOTO 2500 2490 LET E=1 2500 ON 1+E-F*2+P*4 GOTO 2510,2590,2760,2710,2590,2510,2710,2760 2510 REM SAFETY 2520 LET S(1-P)=S(1-P)+2 2530 PRINT L$(19) 2540 GOSUB 2800 2550 PRINT O$(P);" KICKS OFF FROM ITS 20 YARD LINE." 2560 LET X=20+P*60 2570 LET P=1-P 2580 GOTO 590 2590 REM OFFENSIVE TD 2600 PRINT L$(17);"***" 2610 IF RND(1)>.8 THEN 2680 2620 LET S(P)=S(P)+7 2630 PRINT "KICK IS GOOD." 2640 GOSUB 2800 2650 PRINT O$(P);" KICKS OFF" 2660 LET P=1-P 2670 GOTO 580 2680 PRINT "KICK IS OFF TO THE SIDE" 2690 LET S(P)=S(P)+6 2700 GOTO 2640 2710 REM TOUCHBACK 2720 PRINT L$(18) 2730 LET P=1-P 2740 LET X=20+P*60 2750 GOTO 720 2760 REM DEFENSIVE TD 2770 PRINT L$(17);"FOR ";O$(1-P);"***" 2780 LET P=1-P 2790 GOTO 2600 2800 REM SCORE 2810 PRINT 2820 PRINT "SCORE: ";S(0);" TO ";S(1) 2830 PRINT 2840 PRINT 2850 RETURN 2860 REM PENALTY 2870 LET P3=INT(2*RND(1)) 2880 PRINT O$(P3);" OFFSIDES -- PENALTY OF 5 YARDS." 2890 PRINT 2900 PRINT 2910 IF P3=0 THEN 2980 2920 PRINT "DO YOU ACCEPT THE PENALTY"; 2930 INPUT A$ 2940 IF A$="NO" THEN 2300 2950 IF A$="YES" THEN 3110 2960 PRINT "TYPE 'YES' OR 'NO'"; 2970 GOTO 2930 2980 REM OPPONENT'S STRATEGY ON PENALTY 2990 IF P=1 THEN 3040 3000 IF Y<=0 THEN 3080 3010 IF F<0 THEN 3080 3020 IF FNG(1)<3*D-2 THEN 3080 3030 GOTO 3100 3040 IF Y<=5 THEN 3100 3050 IF F<0 THEN 3100 3060 IF D<4 THEN 3080 3070 IF FNG(1)<10 THEN 3100 3080 PRINT "PENALTY REFUSED." 3090 GOTO 2300 3100 PRINT "PENALTY ACCEPTED." 3110 LET F=0 3120 LET D=D-1 3130 IF P<>P3 THEN 3160 3140 LET X=X3-FNF(1)*5 3150 GOTO 2300 3160 LET X=X3+FNF(1)*5 3170 GOTO 2300 3180 END ================================================ FILE: 00_Alternate_Languages/38_Fur_Trader/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/38_Fur_Trader/MiniScript/furtrader.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/38_Fur_Trader/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/38_Fur_Trader/c/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [ANSI-C](https://en.wikipedia.org/wiki/ANSI_C) ##### Translator Notes: I tried to preserve as much of the original layout and flow of the code as possible. However I did use enumerated types for the Fort numbers and Fur types. I think this was certainly a change for the better, and makes the code much easier to read. I also tried to minimise the use of pointers, and stuck with old-school C formatting, because you never know how old the compiler is. Interestingly the code seems to have a bug around the prices of Fox Furs. The commodity-rate for these is stored in the variable `D1`, however some paths through the code do not set this price. So there was a chance of using this uninitialised, or whatever the previous loop set. I don't think this was the original authors intent. So I preserved the original flow of the code (using the previous `D1` value), but also catching the uninitialised path, and assigning a "best guess" value. krt@krt.com.au 2020-10-10 ================================================ FILE: 00_Alternate_Languages/38_Fur_Trader/c/furtrader.c ================================================ /* * Ported from furtrader.bas to ANSI C (C99) by krt@krt.com.au * * compile with: * gcc -g -Wall -Werror furtrader.c -o furtrader */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> /* Constants */ #define FUR_TYPE_COUNT 4 #define FUR_MINK 0 #define FUR_BEAVER 1 #define FUR_ERMINE 2 #define FUR_FOX 3 #define MAX_FURS 190 const char *FUR_NAMES[FUR_TYPE_COUNT] = { "MINK", "BEAVER", "ERMINE", "FOX" }; #define FORT_TYPE_COUNT 3 #define FORT_MONTREAL 1 #define FORT_QUEBEC 2 #define FORT_NEWYORK 3 const char *FORT_NAMES[FORT_TYPE_COUNT] = { "HOCHELAGA (MONTREAL)", "STADACONA (QUEBEC)", "NEW YORK" }; /* Print the words at the specified column */ void printAtColumn( int column, const char *words ) { int i; for ( i=0; i<column; i++ ) printf( " " ); printf( "%s\n", words ); } /* trivial function to output a line with a \n */ void print( const char *words ) { printf( "%s\n", words ); } /* Show the player the introductory message */ void showIntroduction() { 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( "" ); } /* * Prompt the user for input. * When input is given, try to conver it to an integer * return the integer converted value or 0 on error */ int getNumericInput() { int result = -1; char buffer[64]; /* somewhere to store user input */ char *endstr; while ( result == -1 ) { printf( ">> " ); /* prompt the user */ fgets( buffer, sizeof( buffer ), stdin ); /* read from the console into the buffer */ result = (int)strtol( buffer, &endstr, 10 ); /* only simple error checking */ if ( endstr == buffer ) /* was the string -> integer ok? */ result = -1; } return result; } /* * Prompt the user for YES/NO input. * When input is given, try to work out if it's YES, Yes, yes, Y, etc. * And convert to a single upper-case letter * Returns a character of 'Y' or 'N'. */ char getYesOrNo() { char result = '!'; char buffer[64]; /* somewhere to store user input */ while ( !( result == 'Y' || result == 'N' ) ) /* While the answer was not Yes or No */ { print( "ANSWER YES OR NO" ); printf( ">> " ); fgets( buffer, sizeof( buffer ), stdin ); /* read from the console into the buffer */ if ( buffer[0] == 'Y' || buffer[0] == 'y' ) result = 'Y'; else if ( buffer[0] == 'N' || buffer[0] == 'n' ) result = 'N'; } return result; } /* * Show the player the choices of Fort, get their input, if the * input is a valid choice (1,2,3) return it, otherwise keep * prompting the user. */ int getFortChoice() { int result = 0; while ( result == 0 ) { print( "" ); 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." ); print( "ANSWER 1, 2, OR 3." ); result = getNumericInput(); /* get input from the player */ } return result; } /* * Print the description for the fort */ void showFortComment( int which_fort ) { print( "" ); if ( which_fort == FORT_MONTREAL ) { 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 ( which_fort == FORT_QUEBEC ) { 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 ( which_fort == FORT_NEWYORK ) { 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 { printf( "Internal error #1, fort %d does not exist\n", which_fort ); exit( 1 ); /* you have a bug */ } print( "" ); } /* * Prompt the player for how many of each fur type they want. * Accept numeric inputs, re-prompting on incorrect input values */ void getFursPurchase( int *furs ) { int i; printf( "YOUR %d FURS ARE DISTRIBUTED AMONG THE FOLLOWING\n", FUR_TYPE_COUNT ); print( "KINDS OF PELTS: MINK, BEAVER, ERMINE AND FOX." ); print( "" ); for ( i=0; i<FUR_TYPE_COUNT; i++ ) { printf( "HOW MANY %s DO YOU HAVE\n", FUR_NAMES[i] ); furs[i] = getNumericInput(); } } /* * (Re)Set the player's inventory to zero */ void zeroInventory( int *player_fur_count ) { int i; for ( i=0; i<FUR_TYPE_COUNT; i++ ) { player_fur_count[i] = 0; } } /* * Tally the player's inventory */ int sumInventory( int *player_fur_count ) { int result = 0; int i; for ( i=0; i<FUR_TYPE_COUNT; i++ ) { result += player_fur_count[i]; } return result; } /* * Return a random number between a & b * Ref: https://stackoverflow.com/a/686376/1730895 */ float randomAB(float a, float b) { return ((b - a) * ((float)rand() / (float)RAND_MAX)) + a; } /* Random floating point number between 0 and 1 */ float randFloat() { return randomAB( 0, 1 ); } /* States to allow switching in main game-loop */ #define STATE_STARTING 1 #define STATE_CHOOSING_FORT 2 #define STATE_TRAVELLING 3 #define STATE_TRADING 4 int main( void ) { /* variables for storing player's status */ float player_funds = 0; /* no money */ int player_furs[FUR_TYPE_COUNT] = { 0, 0, 0, 0 }; /* no furs */ /* player input holders */ char yes_or_no; int event_picker; int which_fort; /* what part of the game is in play */ int game_state = STATE_STARTING; /* commodity prices */ float mink_price = -1; float beaver_price = -1; float ermine_price = -1; float fox_price = -1; /* sometimes this takes the "last" price (probably this was a bug) */ float mink_value; float beaver_value; float ermine_value; float fox_value; /* for calculating sales results */ srand( time( NULL ) ); /* seed the random number generator */ printAtColumn( 31, "FUR TRADER" ); printAtColumn( 15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" ); printAtColumn( 15, "(Ported to ANSI-C Oct 2012 krt@krt.com.au)" ); print( "\n\n\n" ); /* Loop forever until the player asks to quit */ while ( 1 ) { if ( game_state == STATE_STARTING ) { showIntroduction(); player_funds = 600; /* Initial player start money */ zeroInventory( player_furs ); /* Player fur inventory */ print( "DO YOU WISH TO TRADE FURS?" ); yes_or_no = getYesOrNo(); if ( yes_or_no == 'N' ) exit( 0 ); /* STOP */ game_state = STATE_TRADING; } else if ( game_state == STATE_TRADING ) { print( "" ); printf( "YOU HAVE $ %1.2f IN SAVINGS\n", player_funds ); printf( "AND %d FURS TO BEGIN THE EXPEDITION\n", MAX_FURS ); getFursPurchase( player_furs ); if ( sumInventory( player_furs ) > MAX_FURS ) { print( "" ); print( "YOU MAY NOT HAVE THAT MANY FURS." ); print( "DO NOT TRY TO CHEAT. I CAN ADD." ); print( "YOU MUST START AGAIN." ); print( "" ); game_state = STATE_STARTING; /* T/N: Wow, harsh. */ } else { game_state = STATE_CHOOSING_FORT; } } else if ( game_state == STATE_CHOOSING_FORT ) { which_fort = getFortChoice(); showFortComment( which_fort ); print( "DO YOU WANT TO TRADE AT ANOTHER FORT?" ); yes_or_no = getYesOrNo(); if ( yes_or_no == 'N' ) game_state = STATE_TRAVELLING; } else if ( game_state == STATE_TRAVELLING ) { print( "" ); if ( which_fort == FORT_MONTREAL ) { mink_price = ( ( 0.2 * randFloat() + 0.70 ) * 100 + 0.5 ) / 100; ermine_price = ( ( 0.2 * randFloat() + 0.65 ) * 100 + 0.5 ) / 100; beaver_price = ( ( 0.2 * randFloat() + 0.75 ) * 100 + 0.5 ) / 100; fox_price = ( ( 0.2 * randFloat() + 0.80 ) * 100 + 0.5 ) / 100; print( "SUPPLIES AT FORT HOCHELAGA COST $150.00." ); print( "YOUR TRAVEL EXPENSES TO HOCHELAGA WERE $10.00." ); player_funds -= 160; } else if ( which_fort == FORT_QUEBEC ) { mink_price = ( ( 0.30 * randFloat() + 0.85 ) * 100 + 0.5 ) / 100; ermine_price = ( ( 0.15 * randFloat() + 0.80 ) * 100 + 0.5 ) / 100; beaver_price = ( ( 0.20 * randFloat() + 0.90 ) * 100 + 0.5 ) / 100; fox_price = ( ( 0.25 * randFloat() + 1.10 ) * 100 + 0.5 ) / 100; event_picker = ( 10 * randFloat() ) + 1; if ( event_picker <= 2 ) { 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." ); player_furs[ FUR_BEAVER ] = 0; } else if ( event_picker <= 6 ) { print( "YOU ARRIVED SAFELY AT FORT STADACONA." ); } else if ( event_picker <= 8 ) { print( "YOUR CANOE UPSET IN THE LACHINE RAPIDS. YOU" ); print( "LOST ALL YOUR FURS." ); zeroInventory( player_furs ); } else if ( event_picker <= 10 ) { print( "YOUR FOX PELTS WERE NOT CURED PROPERLY." ); print( "NO ONE WILL BUY THEM." ); player_furs[ FUR_FOX ] = 0; } else { printf( "Internal Error #3, Out-of-bounds event_picker %d\n", event_picker ); exit( 1 ); /* you have a bug */ } print( "" ); print( "SUPPLIES AT FORT STADACONA COST $125.00." ); print( "YOUR TRAVEL EXPENSES TO STADACONA WERE $15.00." ); player_funds -= 140; } else if ( which_fort == FORT_NEWYORK ) { mink_price = ( ( 0.15 * randFloat() + 1.05 ) * 100 + 0.5 ) / 100; ermine_price = ( ( 0.15 * randFloat() + 0.95 ) * 100 + 0.5 ) / 100; beaver_price = ( ( 0.25 * randFloat() + 1.00 ) * 100 + 0.5 ) / 100; if ( fox_price < 0 ) { /* Original Bug? There is no Fox price generated for New York, it will use any previous "D1" price. So if there was no previous value, make one up */ fox_price = ( ( 0.25 * randFloat() + 1.05 ) * 100 + 0.5 ) / 100; /* not in orginal code */ } event_picker = ( 10 * randFloat() ) + 1; if ( event_picker <= 2 ) { print( "YOU WERE ATTACKED BY A PARTY OF IROQUOIS." ); print( "ALL PEOPLE IN YOUR TRADING GROUP WERE" ); print( "KILLED. THIS ENDS THE GAME." ); exit( 0 ); } else if ( event_picker <= 6 ) { print( "YOU WERE LUCKY. YOU ARRIVED SAFELY" ); print( "AT FORT NEW YORK." ); } else if ( event_picker <= 8 ) { print( "YOU NARROWLY ESCAPED AN IROQUOIS RAIDING PARTY." ); print( "HOWEVER, YOU HAD TO LEAVE ALL YOUR FURS BEHIND." ); zeroInventory( player_furs ); } else if ( event_picker <= 10 ) { mink_price /= 2; fox_price /= 2; 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 %d\n" ); exit( 1 ); /* you have a bug */ } print( "" ); print( "SUPPLIES AT NEW YORK COST $85.00." ); print( "YOUR TRAVEL EXPENSES TO NEW YORK WERE $25.00." ); player_funds -= 105; } else { printf( "Internal error #2, fort %d does not exist\n", which_fort ); exit( 1 ); /* you have a bug */ } /* Calculate sales */ beaver_value = beaver_price * player_furs[ FUR_BEAVER ]; fox_value = fox_price * player_furs[ FUR_FOX ]; ermine_value = ermine_price * player_furs[ FUR_ERMINE ]; mink_value = mink_price * player_furs[ FUR_MINK ]; print( "" ); printf( "YOUR BEAVER SOLD FOR $%6.2f\n", beaver_value ); printf( "YOUR FOX SOLD FOR $%6.2f\n", fox_value ); printf( "YOUR ERMINE SOLD FOR $%6.2f\n", ermine_value ); printf( "YOUR MINK SOLD FOR $%6.2f\n", mink_value ); player_funds += beaver_value + fox_value + ermine_value + mink_value; print( "" ); printf( "YOU NOW HAVE $ %1.2f INCLUDING YOUR PREVIOUS SAVINGS\n", player_funds ); print( "" ); print( "DO YOU WANT TO TRADE FURS NEXT YEAR?" ); yes_or_no = getYesOrNo(); if ( yes_or_no == 'N' ) exit( 0 ); /* STOP */ else game_state = STATE_TRADING; } } return 0; /* exit OK */ } ================================================ FILE: 00_Alternate_Languages/38_Fur_Trader/furtrader.bas ================================================ 1 DIM F(4) 2 PRINT TAB(31);"FUR TRADER" 4 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 6 PRINT: PRINT: PRINT 15 GOSUB 1091 16 LET I=600 17 PRINT "DO YOU WISH TO TRADE FURS?" 18 GOSUB 1402 19 IF B$="YES" THEN 100 20 IF B$="YES " THEN 100 21 STOP 100 PRINT 101 PRINT "YOU HAVE $";I " SAVINGS." 102 PRINT "AND 190 FURS TO BEGIN THE EXPEDITION." 261 LET E1=INT((.15*RND(1)+.95)*10^2+.5)/10^2 262 LET B1=INT((.25*RND(1)+1.00)*10^2+.5)/10^2 300 PRINT 301 PRINT "YOUR 190 FURS ARE DISTRIBUTED AMONG THE FOLLOWING" 302 PRINT "KINDS OF PELTS: MINK, BEAVER, ERMINE AND FOX." 310 GOSUB 1430 315 RESTORE 330 FOR J=1 TO 4 332 READ B$ 333 PRINT 335 PRINT "HOW MANY ";B$;" PELTS DO YOU HAVE"; 338 INPUT F(J) 340 LET F(0)=F(1)+F(2)+F(3)+F(4) 342 IF F(0)=190 THEN 1100 344 IF F(0)>190 THEN 500 348 NEXT J 350 GOTO 1100 500 PRINT 501 PRINT "YOU MAY NOT HAVE THAT MANY FURS." 502 PRINT "DO NOT TRY TO CHEAT. I CAN ADD." 503 PRINT "YOU MUST START AGAIN." 504 GOTO 15 508 PRINT 511 PRINT "DO YOU WANT TO TRADE FURS NEXT YEAR?" 513 GOTO 18 1091 PRINT "YOU ARE THE LEADER OF A FRENCH FUR TRADING EXPEDITION IN " 1092 PRINT "1776 LEAVING THE LAKE ONTARIO AREA TO SELL FURS AND GET" 1093 PRINT "SUPPLIES FOR THE NEXT YEAR. YOU HAVE A CHOICE OF THREE" 1094 PRINT "FORTS AT WHICH YOU MAY TRADE. THE COST OF SUPPLIES" 1095 PRINT "AND THE AMOUNT YOU RECEIVE FOR YOUR FURS WILL DEPEND" 1096 PRINT "ON THE FORT THAT YOU CHOOSE." 1099 RETURN 1100 PRINT "YOU MAY TRADE YOUR FURS AT FORT 1, FORT 2," 1102 PRINT "OR FORT 3. FORT 1 IS FORT HOCHELAGA (MONTREAL)" 1103 PRINT "AND IS UNDER THE PROTECTION OF THE FRENCH ARMY." 1104 PRINT "FORT 2 IS FORT STADACONA (QUEBEC) AND IS UNDER THE" 1105 PRINT "PROTECTION OF THE FRENCH ARMY. HOWEVER, YOU MUST" 1106 PRINT "MAKE A PORTAGE AND CROSS THE LACHINE RAPIDS." 1108 PRINT "FORT 3 IS FORT NEW YORK AND IS UNDER DUTCH CONTROL." 1109 PRINT "YOU MUST CROSS THROUGH IROQUOIS LAND." 1110 PRINT "ANSWER 1, 2, OR 3." 1111 INPUT B 1112 IF B=1 THEN 1120 1113 IF B=2 THEN 1135 1115 IF B=3 THEN 1147 1116 GOTO 1110 1120 PRINT "YOU HAVE CHOSEN THE EASIEST ROUTE. HOWEVER, THE FORT" 1121 PRINT "IS FAR FROM ANY SEAPORT. THE VALUE" 1122 PRINT "YOU RECEIVE FOR YOUR FURS WILL BE LOW AND THE COST" 1123 PRINT "OF SUPPLIES HIGHER THAN AT FORTS STADACONA OR NEW YORK." 1125 GOSUB 1400 1129 IF B$="YES" THEN 1110 1130 GOTO 1160 1135 PRINT "YOU HAVE CHOSEN A HARD ROUTE. IT IS, IN COMPARSION," 1136 PRINT "HARDER THAN THE ROUTE TO HOCHELAGA BUT EASIER THAN" 1137 PRINT "THE ROUTE TO NEW YORK. YOU WILL RECEIVE AN AVERAGE VALUE" 1138 PRINT "FOR YOUR FURS AND THE COST OF YOUR SUPPLIES WILL BE AVERAGE." 1141 GOSUB 1400 1144 IF B$="YES" THEN 1110 1145 GOTO 1198 1147 PRINT "YOU HAVE CHOSEN THE MOST DIFFICULT ROUTE. AT" 1148 PRINT "FORT NEW YORK YOU WILL RECEIVE THE HIGHEST VALUE" 1149 PRINT "FOR YOUR FURS. THE COST OF YOUR SUPPLIES" 1150 PRINT "WILL BE LOWER THAN AT ALL THE OTHER FORTS." 1152 GOSUB 1400 1155 IF B$="YES" THEN 1110 1156 GOTO 1250 1160 LET I=I-160 1169 PRINT 1174 LET M1=INT((.2*RND(1)+.7)*10^2+.5)/10^2 1175 LET E1=INT((.2*RND(1)+.65)*10^2+.5)/10^2 1176 LET B1=INT((.2*RND(1)+.75)*10^2+.5)/10^2 1177 LET D1=INT((.2*RND(1)+.8)*10^2+.5)/10^2 1180 PRINT "SUPPLIES AT FORT HOCHELAGA COST $150.00." 1181 PRINT "YOUR TRAVEL EXPENSES TO HOCHELAGA WERE $10.00." 1190 GOTO 1410 1198 LET I=I-140 1201 PRINT 1205 LET M1=INT((.3*RND(1)+.85)*10^2+.5)/10^2 1206 LET E1=INT((.15*RND(1)+.8)*10^2+.5)/10^2 1207 LET B1=INT((.2*RND(1)+.9)*10^2+.5)/10^2 1209 LET P=INT(10*RND(1))+1 1210 IF P<=2 THEN 1216 1212 IF P<=6 THEN 1224 1213 IF P<=8 THEN 1226 1215 IF P<=10 THEN 1235 1216 LET F(2)=0 1218 PRINT "YOUR BEAVER WERE TOO HEAVY TO CARRY ACROSS" 1219 PRINT "THE PORTAGE. YOU HAD TO LEAVE THE PELTS, BUT FOUND" 1220 PRINT "THEM STOLEN WHEN YOU RETURNED." 1221 GOSUB 1244 1222 GOTO 1414 1224 PRINT "YOU ARRIVED SAFELY AT FORT STADACONA." 1225 GOTO 1239 1226 GOSUB 1430 1230 PRINT "YOUR CANOE UPSET IN THE LACHINE RAPIDS. YOU" 1231 PRINT "LOST ALL YOUR FURS." 1232 GOSUB 1244 1233 GOTO 1418 1235 LET F(4)=0 1237 PRINT "YOUR FOX PELTS WERE NOT CURED PROPERLY." 1238 PRINT "NO ONE WILL BUY THEM." 1239 GOSUB 1244 1240 GOTO 1410 1244 PRINT "SUPPLIES AT FORT STADACONA COST $125.00." 1246 PRINT "YOUR TRAVEL EXPENSES TO STADACONA WERE $15.00." 1248 RETURN 1250 LET I=I-105 1254 PRINT 1260 LET M1=INT((.15*RND(1)+1.05)*10^2+.5)/10^2 1263 LET D1=INT((.25*RND(1)+1.1)*10^2+.5)/10^2 1270 LET P=INT(10*RND(1))+1 1271 IF P<=2 THEN 1281 1272 IF P<=6 THEN 1291 1273 IF P<=8 THEN 1295 1274 IF P<=10 THEN 1306 1281 PRINT "YOU WERE ATTACKED BY A PARTY OF IROQUOIS." 1282 PRINT "ALL PEOPLE IN YOUR TRADING GROUP WERE" 1283 PRINT "KILLED. THIS ENDS THE GAME." 1284 STOP 1291 PRINT "YOU WERE LUCKY. YOU ARRIVED SAFELY" 1292 PRINT "AT FORT NEW YORK." 1293 GOTO 1311 1295 GOSUB 1430 1300 PRINT "YOU NARROWLY ESCAPED AN IROQUOIS RAIDING PARTY." 1301 PRINT "HOWEVER, YOU HAD TO LEAVE ALL YOUR FURS BEHIND." 1303 GOSUB 1320 1304 GOTO 1418 1306 LET B1=B1/2 1307 LET M1=M1/2 1308 PRINT "YOUR MINK AND BEAVER WERE DAMAGED ON YOUR TRIP." 1309 PRINT "YOU RECEIVE ONLY HALF THE CURRENT PRICE FOR THESE FURS." 1311 GOSUB 1320 1312 GOTO 1410 1320 PRINT "SUPPLIES AT NEW YORK COST $80.00." 1321 PRINT "YOUR TRAVEL EXPENSES TO NEW YORK WERE $25.00." 1322 RETURN 1400 PRINT "DO YOU WANT TO TRADE AT ANOTHER FORT?" 1402 PRINT "ANSWER YES OR NO", 1403 INPUT B$ 1404 RETURN 1410 PRINT 1412 PRINT "YOUR BEAVER SOLD FOR $";B1*F(2); 1414 PRINT "YOUR FOX SOLD FOR $";D1*F(4) 1416 PRINT "YOUR ERMINE SOLD FOR $";E1*F(3); 1417 PRINT "YOUR MINK SOLD FOR $";M1*F(1) 1418 LET I=M1*F(1)+B1*F(2)+E1*F(3)+D1*F(4)+I 1420 PRINT 1422 PRINT "YOU NOW HAVE $";I;" INCLUDING YOUR PREVIOUS SAVINGS" 1425 GOTO 508 1430 FOR J=1 TO 4 1432 LET F(J)=0 1434 NEXT J 1436 RETURN 2000 DATA "MINK","BEAVER","ERMINE","FOX" 2046 END ================================================ FILE: 00_Alternate_Languages/38_Fur_Trader/go/main.go ================================================ package main import ( "bufio" "fmt" "log" "math/rand" "os" "strconv" "strings" "time" ) const ( MAXFURS = 190 STARTFUNDS = 600 ) type Fur int8 const ( FUR_MINK Fur = iota FUR_BEAVER FUR_ERMINE FUR_FOX ) type Fort int8 const ( FORT_MONTREAL Fort = iota FORT_QUEBEC FORT_NEWYORK ) type GameState int8 const ( STARTING GameState = iota TRADING CHOOSINGFORT TRAVELLING ) func FURS() []string { return []string{"MINK", "BEAVER", "ERMINE", "FOX"} } func FORTS() []string { return []string{"HOCHELAGA (MONTREAL)", "STADACONA (QUEBEC)", "NEW YORK"} } type Player struct { funds float32 furs []int } func NewPlayer() Player { p := Player{} p.funds = STARTFUNDS p.furs = make([]int, 4) return p } func (p *Player) totalFurs() int { f := 0 for _, v := range p.furs { f += v } return f } func (p *Player) lostFurs() { for f := 0; f < len(p.furs); f++ { p.furs[f] = 0 } } func printTitle() { fmt.Println(" FUR TRADER") fmt.Println(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") fmt.Println() fmt.Println() fmt.Println() } func printIntro() { fmt.Println("YOU ARE THE LEADER OF A FRENCH FUR TRADING EXPEDITION IN ") fmt.Println("1776 LEAVING THE LAKE ONTARIO AREA TO SELL FURS AND GET") fmt.Println("SUPPLIES FOR THE NEXT YEAR. YOU HAVE A CHOICE OF THREE") fmt.Println("FORTS AT WHICH YOU MAY TRADE. THE COST OF SUPPLIES") fmt.Println("AND THE AMOUNT YOU RECEIVE FOR YOUR FURS WILL DEPEND") fmt.Println("ON THE FORT THAT YOU CHOOSE.") fmt.Println() } func getFortChoice() Fort { scanner := bufio.NewScanner(os.Stdin) for { fmt.Println() fmt.Println("YOU MAY TRADE YOUR FURS AT FORT 1, FORT 2,") fmt.Println("OR FORT 3. FORT 1 IS FORT HOCHELAGA (MONTREAL)") fmt.Println("AND IS UNDER THE PROTECTION OF THE FRENCH ARMY.") fmt.Println("FORT 2 IS FORT STADACONA (QUEBEC) AND IS UNDER THE") fmt.Println("PROTECTION OF THE FRENCH ARMY. HOWEVER, YOU MUST") fmt.Println("MAKE A PORTAGE AND CROSS THE LACHINE RAPIDS.") fmt.Println("FORT 3 IS FORT NEW YORK AND IS UNDER DUTCH CONTROL.") fmt.Println("YOU MUST CROSS THROUGH IROQUOIS LAND.") fmt.Println("ANSWER 1, 2, OR 3.") fmt.Print(">> ") scanner.Scan() f, err := strconv.Atoi(scanner.Text()) if err != nil || f < 1 || f > 3 { fmt.Println("Invalid input, Try again ... ") continue } return Fort(f) } } func printFortComment(f Fort) { fmt.Println() switch f { case FORT_MONTREAL: fmt.Println("YOU HAVE CHOSEN THE EASIEST ROUTE. HOWEVER, THE FORT") fmt.Println("IS FAR FROM ANY SEAPORT. THE VALUE") fmt.Println("YOU RECEIVE FOR YOUR FURS WILL BE LOW AND THE COST") fmt.Println("OF SUPPLIES HIGHER THAN AT FORTS STADACONA OR NEW YORK.") case FORT_QUEBEC: fmt.Println("YOU HAVE CHOSEN A HARD ROUTE. IT IS, IN COMPARSION,") fmt.Println("HARDER THAN THE ROUTE TO HOCHELAGA BUT EASIER THAN") fmt.Println("THE ROUTE TO NEW YORK. YOU WILL RECEIVE AN AVERAGE VALUE") fmt.Println("FOR YOUR FURS AND THE COST OF YOUR SUPPLIES WILL BE AVERAGE.") case FORT_NEWYORK: fmt.Println("YOU HAVE CHOSEN THE MOST DIFFICULT ROUTE. AT") fmt.Println("FORT NEW YORK YOU WILL RECEIVE THE HIGHEST VALUE") fmt.Println("FOR YOUR FURS. THE COST OF YOUR SUPPLIES") fmt.Println("WILL BE LOWER THAN AT ALL THE OTHER FORTS.") } fmt.Println() } func getYesOrNo() string { scanner := bufio.NewScanner(os.Stdin) for { fmt.Println("ANSWER YES OR NO") scanner.Scan() if strings.ToUpper(scanner.Text())[0:1] == "Y" { return "Y" } else if strings.ToUpper(scanner.Text())[0:1] == "N" { return "N" } } } func getFursPurchase() []int { scanner := bufio.NewScanner(os.Stdin) fmt.Printf("YOUR %d FURS ARE DISTRIBUTED AMONG THE FOLLOWING\n", MAXFURS) fmt.Println("KINDS OF PELTS: MINK, BEAVER, ERMINE AND FOX.") fmt.Println() purchases := make([]int, 4) for i, f := range FURS() { retry: fmt.Printf("HOW MANY %s DO YOU HAVE: ", f) scanner.Scan() count, err := strconv.Atoi(scanner.Text()) if err != nil { fmt.Println("INVALID INPUT, TRY AGAIN ...") goto retry } purchases[i] = count } return purchases } func main() { rand.Seed(time.Now().UnixNano()) printTitle() gameState := STARTING whichFort := FORT_NEWYORK var ( minkPrice int erminePrice int beaverPrice int foxPrice int ) player := NewPlayer() for { switch gameState { case STARTING: printIntro() fmt.Println("DO YOU WISH TO TRADE FURS?") if getYesOrNo() == "N" { os.Exit(0) } gameState = TRADING case TRADING: fmt.Println() fmt.Printf("YOU HAVE $ %1.2f IN SAVINGS\n", player.funds) fmt.Printf("AND %d FURS TO BEGIN THE EXPEDITION\n", MAXFURS) player.furs = getFursPurchase() if player.totalFurs() > MAXFURS { fmt.Println() fmt.Println("YOU MAY NOT HAVE THAT MANY FURS.") fmt.Println("DO NOT TRY TO CHEAT. I CAN ADD.") fmt.Println("YOU MUST START AGAIN.") gameState = STARTING } else { gameState = CHOOSINGFORT } case CHOOSINGFORT: whichFort = getFortChoice() printFortComment(whichFort) fmt.Println("DO YOU WANT TO TRADE AT ANOTHER FORT?") changeFort := getYesOrNo() if changeFort == "N" { gameState = TRAVELLING } case TRAVELLING: switch whichFort { case FORT_MONTREAL: minkPrice = (int((0.2*rand.Float64()+0.70)*100+0.5) / 100) erminePrice = (int((0.2*rand.Float64()+0.65)*100+0.5) / 100) beaverPrice = (int((0.2*rand.Float64()+0.75)*100+0.5) / 100) foxPrice = (int((0.2*rand.Float64()+0.80)*100+0.5) / 100) fmt.Println("SUPPLIES AT FORT HOCHELAGA COST $150.00.") fmt.Println("YOUR TRAVEL EXPENSES TO HOCHELAGA WERE $10.00.") player.funds -= 160 case FORT_QUEBEC: minkPrice = (int((0.30*rand.Float64()+0.85)*100+0.5) / 100) erminePrice = (int((0.15*rand.Float64()+0.80)*100+0.5) / 100) beaverPrice = (int((0.20*rand.Float64()+0.90)*100+0.5) / 100) foxPrice = (int((0.25*rand.Float64()+1.10)*100+0.5) / 100) event := int(10*rand.Float64()) + 1 if event <= 2 { fmt.Println("YOUR BEAVER WERE TOO HEAVY TO CARRY ACROSS") fmt.Println("THE PORTAGE. YOU HAD TO LEAVE THE PELTS, BUT FOUND") fmt.Println("THEM STOLEN WHEN YOU RETURNED.") player.furs[FUR_BEAVER] = 0 } else if event <= 6 { fmt.Println("YOU ARRIVED SAFELY AT FORT STADACONA.") } else if event <= 8 { fmt.Println("YOUR CANOE UPSET IN THE LACHINE RAPIDS. YOU") fmt.Println("LOST ALL YOUR FURS.") player.lostFurs() } else if event <= 10 { fmt.Println("YOUR FOX PELTS WERE NOT CURED PROPERLY.") fmt.Println("NO ONE WILL BUY THEM.") player.furs[FUR_FOX] = 0 } else { log.Fatal("Unexpected error") } fmt.Println() fmt.Println("SUPPLIES AT FORT STADACONA COST $125.00.") fmt.Println("YOUR TRAVEL EXPENSES TO STADACONA WERE $15.00.") player.funds -= 140 case FORT_NEWYORK: minkPrice = (int((0.15*rand.Float64()+1.05)*100+0.5) / 100) erminePrice = (int((0.15*rand.Float64()+0.95)*100+0.5) / 100) beaverPrice = (int((0.25*rand.Float64()+1.00)*100+0.5) / 100) foxPrice = (int((0.25*rand.Float64()+1.05)*100+0.5) / 100) // not in original code event := int(10*rand.Float64()) + 1 if event <= 2 { fmt.Println("YOU WERE ATTACKED BY A PARTY OF IROQUOIS.") fmt.Println("ALL PEOPLE IN YOUR TRADING GROUP WERE") fmt.Println("KILLED. THIS ENDS THE GAME.") os.Exit(0) } else if event <= 6 { fmt.Println("YOU WERE LUCKY. YOU ARRIVED SAFELY") fmt.Println("AT FORT NEW YORK.") } else if event <= 8 { fmt.Println("YOU NARROWLY ESCAPED AN IROQUOIS RAIDING PARTY.") fmt.Println("HOWEVER, YOU HAD TO LEAVE ALL YOUR FURS BEHIND.") player.lostFurs() } else if event <= 10 { minkPrice /= 2 foxPrice /= 2 fmt.Println("YOUR MINK AND BEAVER WERE DAMAGED ON YOUR TRIP.") fmt.Println("YOU RECEIVE ONLY HALF THE CURRENT PRICE FOR THESE FURS.") } else { log.Fatal("Unexpected error") } fmt.Println() fmt.Println("SUPPLIES AT NEW YORK COST $85.00.") fmt.Println("YOUR TRAVEL EXPENSES TO NEW YORK WERE $25.00.") player.funds -= 110 } beaverValue := beaverPrice * player.furs[FUR_BEAVER] foxValue := foxPrice * player.furs[FUR_FOX] ermineValue := erminePrice * player.furs[FUR_ERMINE] minkValue := minkPrice * player.furs[FUR_MINK] fmt.Println() fmt.Printf("YOUR BEAVER SOLD FOR $%6.2f\n", float64(beaverValue)) fmt.Printf("YOUR FOX SOLD FOR $%6.2f\n", float64(foxValue)) fmt.Printf("YOUR ERMINE SOLD FOR $%6.2f\n", float64(ermineValue)) fmt.Printf("YOUR MINK SOLD FOR $%6.2f\n", float64(minkValue)) player.funds += float32(beaverValue + foxValue + ermineValue + minkValue) fmt.Println() fmt.Printf("YOU NOW HAVE $%1.2f INCLUDING YOUR PREVIOUS SAVINGS\n", player.funds) fmt.Println("\nDO YOU WANT TO TRADE FURS NEXT YEAR?") if getYesOrNo() == "N" { os.Exit(0) } else { gameState = TRADING } } } } ================================================ FILE: 00_Alternate_Languages/39_Golf/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/39_Golf/MiniScript/golf.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/39_Golf/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/39_Golf/golf.bas ================================================ 1 PRINT TAB(34);"GOLF" 2 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 3 PRINT:PRINT:PRINT 4 PRINT "WELCOME TO THE CREATIVE COMPUTING COUNTRY CLUB," 5 PRINT "AN EIGHTEEN HOLE CHAMPIONSHIP LAYOUT LOCATED A SHORT" 6 PRINT "DISTANCE FROM SCENIC DOWNTOWN MORRISTOWN. THE" 7 PRINT "COMMENTATOR WILL EXPLAIN THE GAME AS YOU PLAY." 8 PRINT "ENJOY YOUR GAME; SEE YOU AT THE 19TH HOLE..." 9 PRINT:PRINT: DIM L(10) 10 G1=18 20 G2=0 30 G3=0 40 A=0 50 N=.8 60 S2=0 70 F=1 80 PRINT "WHAT IS YOUR HANDICAP"; 90 INPUT H:PRINT 100 IF H>30 THEN 470 110 IF H<0 THEN 470 120 PRINT "DIFFICULTIES AT GOLF INCLUDE:" 130 PRINT "0=HOOK, 1=SLICE, 2=POOR DISTANCE, 4=TRAP SHOTS, 5=PUTTING" 140 PRINT "WHICH ONE (ONLY ONE) IS YOUR WORST"; 150 INPUT T:PRINT 160 IF T>5 THEN 120 170 S1=0 210 REM 230 L(0)=0 240 J=0 245 Q=0 250 S2=S2+1 260 K=0 270 IF F=1 THEN 310 290 PRINT "YOUR SCORE ON HOLE";F-1;"WAS";S1 291 GOTO 1750 292 IF S1>P+2 THEN 297 293 IF S1=P THEN 299 294 IF S1=P-1 THEN 301 295 IF S1=P-2 THEN 303 296 GOTO 310 297 PRINT "KEEP YOUR HEAD DOWN." 298 GOTO 310 299 PRINT "A PAR. NICE GOING." 300 GOTO 310 301 PRINT "A BIRDIE." 302 GOTO 310 303 IF P=3 THEN 306 304 PRINT "A GREAT BIG EAGLE." 305 GOTO 310 306 PRINT "A HOLE IN ONE." 310 IF F=19 THEN 1710 315 S1=0 316 PRINT 320 IF S1=0 THEN 1590 330 IF L(0)<1 THEN 1150 340 X=0 350 IF L(0)>5 THEN 1190 360 PRINT "SHOT WENT";D1;"YARDS. IT'S";D2;"YARDS FROM THE CUP." 362 PRINT "BALL IS";INT(O);"YARDS OFF LINE... IN "; 380 GOSUB 400 390 GOTO 620 400 IF L(X)=1 THEN 480 410 IF L(X)=2 THEN 500 420 IF L(X)=3 THEN 520 430 IF L(X)=4 THEN 540 440 IF L(X)=5 THEN 560 450 IF L(X)=6 THEN 580 460 PRINT "OUT OF BOUNDS." 465 GOTO 1690 470 PRINT "PGA HANDICAPS RANGE FROM 0 TO 30." 472 GOTO 80 480 PRINT "FAIRWAY." 490 GOTO 1690 500 PRINT "ROUGH." 510 GOTO 1690 520 PRINT "TREES." 530 GOTO 1690 540 PRINT "ADJACENT FAIRWAY." 550 GOTO 1690 560 PRINT "TRAP." 570 GOTO 1690 580 PRINT "WATER." 590 GOTO 1690 620 IF A=1 THEN 629 621 PRINT "SELECTION OF CLUBS" 622 PRINT "YARDAGE DESIRED SUGGESTED CLUBS" 623 PRINT "200 TO 280 YARDS 1 TO 4" 624 PRINT "100 TO 200 YARDS 19 TO 13" 625 PRINT " 0 TO 100 YARDS 29 TO 23" 626 A=1 629 PRINT "WHAT CLUB DO YOU CHOOSE"; 630 INPUT C 632 PRINT 635 IF C<1 THEN 690 637 IF C>29 THEN 690 640 IF C>4 THEN 710 650 IF L(0)<=5 THEN 740 660 IF C=14 THEN 740 665 IF C=23 THEN 740 670 GOTO 690 680 S1=S1-1 690 PRINT "THAT CLUB IS NOT IN THE BAG." 693 PRINT 700 GOTO 620 710 IF C<12 THEN 690 720 C=C-6 730 GOTO 650 740 S1=S1+1 741 W=1 742 IF C>13 THEN 960 746 IF INT(F/3)=F/3 THEN 952 752 IF C<4 THEN 756 754 GOTO 760 756 IF L(0)=2 THEN 862 760 IF S1>7 THEN 867 770 D1=INT(((30-H)*2.5+187-((30-H)*.25+15)*C/2)+25*RND(1)) 780 D1=INT(D1*W) 800 IF T=2 THEN 1170 830 O=(RND(1)/.8)*(2*H+16)*ABS(TAN(D1*.0035)) 840 D2=INT(SQR(O^2+ABS(D-D1)^2)) 850 IF D-D1<0 THEN 870 860 GOTO 890 862 PRINT "YOU DUBBED IT." 864 D1=35 866 GOTO 830 867 IF D<200 THEN 1300 868 GOTO 770 870 IF D2<20 THEN 890 880 PRINT "TOO MUCH CLUB. YOU'RE PAST THE HOLE." 890 B=D 900 D=D2 910 IF D2>27 THEN 1020 920 IF D2>20 THEN 1100 930 IF D2>.5 THEN 1120 940 L(0)=9 950 GOTO 1470 952 IF S2+Q+(10*(F-1)/18)<(F-1)*(72+((H+1)/.85))/18 THEN 956 954 GOTO 752 956 Q=Q+1 957 IF S1/2<>INT(S1/2) THEN 1011 958 GOTO 862 960 PRINT "NOW GAUGE YOUR DISTANCE BY A PERCENTAGE (1 TO 100)" 961 PRINT "OF A FULL SWING"; 970 INPUT W: W=W/100 972 PRINT 980 IF W>1 THEN 680 985 IF L(0)=5 THEN 1280 990 IF C=14 THEN 760 1000 C=C-10 1010 GOTO 760 1011 IF D<95 THEN 862 1012 PRINT "BALL HIT TREE - BOUNCED INTO ROUGH";D-75;"YARDS FROM HOLE." 1014 D=D-75 1018 GOTO 620 1020 IF O<30 THEN 1150 1022 IF J>0 THEN 1150 1030 IF T>0 THEN 1070 1035 S9=(S2+1)/15 1036 IF INT(S9)=S9 THEN 1075 1040 PRINT "YOU HOOKED- "; 1050 L(0)=L(2) 1055 IF O>45 THEN 1092 1060 GOTO 320 1070 S9=(S2+1)/15 1071 IF INT(S9)=S9 THEN 1040 1075 PRINT "YOU SLICED- "; 1080 L(0)=L(1) 1090 GOTO 1055 1092 PRINT "BADLY." 1094 GOTO 320 1100 L(0)=5 1110 GOTO 320 1120 L(0)=8 1130 D2=INT(D2*3) 1140 GOTO 1380 1150 L(0)=1 1160 GOTO 320 1170 D1=INT(.85*D1) 1180 GOTO 830 1190 IF L(0)>6 THEN 1260 1200 PRINT "YOUR SHOT WENT INTO THE WATER." 1210 S1=S1+1 1220 PRINT "PENALTY STROKE ASSESSED. HIT FROM PREVIOUS LOCATION." 1230 J=J+1 1240 L(0)=1 1242 D=B 1250 GOTO 620 1260 PRINT "YOUR SHOT WENT OUT OF BOUNDS." 1270 GOTO 1210 1280 IF T=3 THEN 1320 1300 D2=1+(3*INT((80/(40-H))*RND(1))) 1310 GOTO 1380 1320 IF RND(1)>N THEN 1360 1330 N=N*.2 1340 PRINT "SHOT DUBBED, STILL IN TRAP." 1350 GOTO 620 1360 N=.8 1370 GOTO 1300 1380 PRINT "ON GREEN,";D2;"FEET FROM THE PIN." 1381 PRINT "CHOOSE YOUR PUTT POTENCY (1 TO 13):"; 1400 INPUT I 1410 S1=S1+1 1420 IF S1+1-P>(H*.072)+2 THEN 1470 1425 IF K>2 THEN 1470 1428 K=K+1 1430 IF T=4 THEN 1530 1440 D2=D2-I*(4+2*RND(1))+1.5 1450 IF D2<-2 THEN 1560 1460 IF D2>2 THEN 1500 1470 PRINT "YOU HOLED IT." 1472 PRINT 1480 F=F+1 1490 GOTO 230 1500 PRINT "PUTT SHORT." 1505 D2=INT(D2) 1510 GOTO 1380 1530 D2=D2-I*(4+1*RND(1))+1 1550 GOTO 1450 1560 PRINT "PASSED BY CUP." 1570 D2=-D2 1580 GOTO 1505 1590 READ D,P,L(1),L(2) 1595 PRINT 1600 PRINT "YOU ARE AT THE TEE OFF HOLE";F;"DISTANCE";D;"YARDS, PAR";P 1605 G3=G3+P 1620 PRINT "ON YOUR RIGHT IS "; 1630 X=1 1640 GOSUB 400 1650 PRINT "ON YOUR LEFT IS "; 1660 X=2 1670 GOSUB 400 1680 GOTO 620 1690 RETURN 1700 DATA 361,4,4,2,389,4,3,3,206,3,4,2,500,5,7,2 1702 DATA 408,4,2,4,359,4,6,4,424,4,4,2,388,4,4,4 1704 DATA 196,3,7,2,400,4,7,2,560,5,7,2,132,3,2,2 1706 DATA 357,4,4,4,294,4,2,4,475,5,2,3,375,4,4,2 1708 DATA 180,3,6,2,550,5,6,6 1710 PRINT 1750 G2=G2+S1 1760 PRINT "TOTAL PAR FOR";F-1;"HOLES IS";G3;" YOUR TOTAL IS";G2 1761 IF G1=F-1 THEN 1770 1765 GOTO 292 1770 END ================================================ FILE: 00_Alternate_Languages/40_Gomoko/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/40_Gomoko/MiniScript/gomoko.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/40_Gomoko/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/40_Gomoko/gomoko.bas ================================================ 2 PRINT TAB(33);"GOMOKO" 4 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 6 PRINT:PRINT:PRINT 8 DIM A(19,19) 10 PRINT "WELCOME TO THE ORIENTAL GAME OF GOMOKO." 20 PRINT: PRINT "THE GAME IS PLAYED ON AN N BY N GRID OF A SIZE" 30 PRINT "THAT YOU SPECIFY. DURING YOUR PLAY, YOU MAY COVER ONE GRID" 40 PRINT "INTERSECTION WITH A MARKER. THE OBJECT OF THE GAME IS TO GET" 50 PRINT "5 ADJACENT MARKERS IN A ROW -- HORIZONTALLY, VERTICALLY, OR" 60 PRINT "DIAGONALLY. ON THE BOARD DIAGRAM, YOUR MOVES ARE MARKED" 70 PRINT "WITH A '1' AND THE COMPUTER MOVES WITH A '2'." 80 PRINT: PRINT "THE COMPUTER DOES NOT KEEP TRACK OF WHO HAS WON." 90 PRINT "TO END THE GAME, TYPE -1,-1 FOR YOUR MOVE.": PRINT 110 PRINT "WHAT IS YOUR BOARD SIZE (MIN 7/ MAX 19)";: INPUT N 115 IF N>6 THEN 117 116 GOTO 120 117 IF N<20 THEN 210 120 PRINT "I SAID, THE MINIMUM IS 7, THE MAXIMUM IS 19.": GOTO 110 210 FOR I=1 TO N:FOR J=1 TO N: A(I,J)=0: NEXT J: NEXT I 300 PRINT: PRINT "WE ALTERNATE MOVES. YOU GO FIRST...": PRINT 310 PRINT "YOUR PLAY (I,J)";: INPUT I,J 315 PRINT 320 IF I=-1 THEN 980 330 X=I: Y=J: GOSUB 910: IF L=1 THEN 410 340 PRINT "ILLEGAL MOVE. TRY AGAIN...": GOTO 310 410 IF A(I,J)=0 THEN 440 420 PRINT "SQUARE OCCUPIED. TRY AGAIN...": GOTO 310 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 570 IF L=0 THEN 590 580 IF A(X,Y)=1 THEN 710 590 NEXT F: NEXT E 600 REM *** COMPUTER TRIES A RANDOM MOVE *** 610 X=INT(N*RND(1)+1): Y=INT(N*RND(1)+1): GOSUB 910: IF L=0 THEN 610 650 IF A(X,Y)<>0 THEN 610 660 A(X,Y)=2: GOSUB 810: GOTO 310 710 X=I-E: Y=J-F: GOSUB 910 750 IF L=0 THEN 610 760 GOTO 650 800 REM *** PRINT THE BOARD *** 810 FOR I=1 TO N: FOR J=1 TO N: PRINT A(I,J); 840 NEXT J: PRINT: NEXT I: PRINT: RETURN 910 L=1: IF X<1 THEN 970 920 IF X>N THEN 970 930 IF Y<1 THEN 970 940 IF Y>N THEN 970 950 RETURN 970 L=0: RETURN 980 PRINT: PRINT "THANKS FOR THE GAME!!" 985 PRINT "PLAY AGAIN (1 FOR YES, 0 FOR NO)";: INPUT Q 990 IF Q=1 THEN 110 999 END ================================================ FILE: 00_Alternate_Languages/41_Guess/MiniScript/README.md ================================================ 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. ================================================ FILE: 00_Alternate_Languages/41_Guess/MiniScript/guess.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/41_Guess/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/41_Guess/go/main.go ================================================ package main import ( "bufio" "fmt" "math" "math/rand" "os" "strconv" "time" ) func printIntro() { fmt.Println(" Guess") fmt.Println("Creative Computing Morristown, New Jersey") fmt.Println() fmt.Println() fmt.Println() fmt.Println("This is a number guessing game. I'll think") fmt.Println("of a number between 1 and any limit you want.") fmt.Println("Then you have to guess what it is") } func getLimit() (int, int) { scanner := bufio.NewScanner(os.Stdin) for { fmt.Println("What limit do you want?") scanner.Scan() limit, err := strconv.Atoi(scanner.Text()) if err != nil || limit < 0 { fmt.Println("Please enter a number greater or equal to 1") continue } limitGoal := int((math.Log(float64(limit)) / math.Log(2)) + 1) return limit, limitGoal } } func main() { rand.Seed(time.Now().UnixNano()) printIntro() scanner := bufio.NewScanner(os.Stdin) limit, limitGoal := getLimit() guessCount := 1 stillGuessing := true won := false myGuess := int(float64(limit)*rand.Float64() + 1) fmt.Printf("I'm thinking of a number between 1 and %d\n", limit) fmt.Println("Now you try to guess what it is.") for stillGuessing { scanner.Scan() n, err := strconv.Atoi(scanner.Text()) if err != nil { fmt.Println("Please enter a number greater or equal to 1") continue } if n < 0 { break } fmt.Print("\n\n\n") if n < myGuess { fmt.Println("Too low. Try a bigger answer") guessCount += 1 } else if n > myGuess { fmt.Println("Too high. Try a smaller answer") guessCount += 1 } else { fmt.Printf("That's it! You got it in %d tries\n", guessCount) won = true stillGuessing = false } } if won { if guessCount < limitGoal { fmt.Println("Very good.") } else if guessCount == limitGoal { fmt.Println("Good.") } else { fmt.Printf("You should have been able to get it in only %d guesses.\n", limitGoal) } fmt.Print("\n\n\n") } } ================================================ FILE: 00_Alternate_Languages/41_Guess/guess.bas ================================================ 1 PRINT TAB(33);"GUESS" 2 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 3 PRINT:PRINT:PRINT 4 PRINT "THIS IS A NUMBER GUESSING GAME. I'LL THINK" 5 PRINT "OF A NUMBER BETWEEN 1 AND ANY LIMIT YOU WANT." 6 PRINT "THEN YOU HAVE TO GUESS WHAT IT IS." 7 PRINT 8 PRINT "WHAT LIMIT DO YOU WANT"; 9 INPUT L 10 PRINT 11 L1=INT(LOG(L)/LOG(2))+1 12 PRINT "I'M THINKING OF A NUMBER BETWEEN 1 AND";L 13 G=1 14 PRINT "NOW YOU TRY TO GUESS WHAT IT IS." 15 M=INT(L*RND(1)+1) 20 INPUT N 21 IF N>0 THEN 25 22 GOSUB 70 23 GOTO 1 25 IF N=M THEN 50 30 G=G+1 31 IF N>M THEN 40 32 PRINT "TOO LOW. TRY A BIGGER ANSWER." 33 GOTO 20 40 PRINT "TOO HIGH. TRY A SMALLER ANSWER." 42 GOTO 20 50 PRINT "THAT'S IT! YOU GOT IT IN";G;"TRIES." 52 IF G<L1 THEN 58 54 IF G=L1 THEN 60 56 PRINT "YOU SHOULD HAVE BEEN ABLE TO GET IT IN ONLY";L1 57 GOTO 65 58 PRINT "VERY "; 60 PRINT "GOOD." 65 GOSUB 70 66 GOTO 12 70 FOR H=1 TO 5 71 PRINT 72 NEXT H 73 RETURN 99 END ================================================ FILE: 00_Alternate_Languages/42_Gunner/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/42_Gunner/MiniScript/gunner.ms ================================================ 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." ================================================ FILE: 00_Alternate_Languages/42_Gunner/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/42_Gunner/go/main.go ================================================ package main import ( "bufio" "fmt" "math" "math/rand" "os" "strconv" "strings" "time" ) func printIntro() { fmt.Println(" GUNNER") fmt.Println(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") fmt.Print("\n\n\n") fmt.Println("YOU ARE THE OFFICER-IN-CHARGE, GIVING ORDERS TO A GUN") fmt.Println("CREW, TELLING THEM THE DEGREES OF ELEVATION YOU ESTIMATE") fmt.Println("WILL PLACE A PROJECTILE ON TARGET. A HIT WITHIN 100 YARDS") fmt.Println("OF THE TARGET WILL DESTROY IT.") fmt.Println() } func getFloat() float64 { scanner := bufio.NewScanner(os.Stdin) for { scanner.Scan() fl, err := strconv.ParseFloat(scanner.Text(), 64) if err != nil { fmt.Println("Invalid input") continue } return fl } } func play() { gunRange := int(40000*rand.Float64() + 20000) fmt.Printf("\nMAXIMUM RANGE OF YOUR GUN IS %d YARDS\n", gunRange) killedEnemies := 0 S1 := 0 for { targetDistance := int(float64(gunRange) * (0.1 + 0.8*rand.Float64())) shots := 0 fmt.Printf("\nDISTANCE TO THE TARGET IS %d YARDS\n", targetDistance) for { fmt.Print("\n\nELEVATION? ") elevation := getFloat() if elevation > 89 { fmt.Println("MAXIMUM ELEVATION IS 89 DEGREES") continue } if elevation < 1 { fmt.Println("MINIMUM ELEVATION IS 1 DEGREE") continue } shots += 1 if shots < 6 { B2 := 2 * elevation / 57.3 shotImpact := int(float64(gunRange) * math.Sin(B2)) shotProximity := int(targetDistance - shotImpact) if math.Abs(float64(shotProximity)) < 100 { // hit fmt.Printf("*** TARGET DESTROYED *** %d ROUNDS OF AMMUNITION EXPENDED.\n", shots) S1 += shots if killedEnemies == 4 { fmt.Printf("\n\nTOTAL ROUNDS EXPENDED WERE: %d\n", S1) if S1 > 18 { print("BETTER GO BACK TO FORT SILL FOR REFRESHER TRAINING!") return } else { print("NICE SHOOTING !!") return } } else { killedEnemies += 1 fmt.Println("\nTHE FORWARD OBSERVER HAS SIGHTED MORE ENEMY ACTIVITY...") break } } else { // missed if shotProximity > 100 { fmt.Printf("SHORT OF TARGET BY %d YARDS.\n", int(math.Abs(float64(shotProximity)))) } else { fmt.Printf("OVER TARGET BY %d YARDS.\n", int(math.Abs(float64(shotProximity)))) } } } else { fmt.Print("\nBOOM !!!! YOU HAVE JUST BEEN DESTROYED BY THE ENEMY.\n\n\n") fmt.Println("BETTER GO BACK TO FORT SILL FOR REFRESHER TRAINING!") return } } } } func main() { rand.Seed(time.Now().UnixNano()) scanner := bufio.NewScanner(os.Stdin) printIntro() for { play() fmt.Print("TRY AGAIN (Y OR N)? ") scanner.Scan() if strings.ToUpper(scanner.Text())[0:1] != "Y" { fmt.Println("\nOK. RETURN TO BASE CAMP.") break } } } ================================================ FILE: 00_Alternate_Languages/42_Gunner/gunner.bas ================================================ 10 PRINT TAB(30);"GUNNER" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 30 PRINT:PRINT:PRINT 130 PRINT "YOU ARE THE OFFICER-IN-CHARGE, GIVING ORDERS TO A GUN" 140 PRINT "CREW, TELLING THEM THE DEGREES OF ELEVATION YOU ESTIMATE" 150 PRINT "WILL PLACE A PROJECTILE ON TARGET. A HIT WITHIN 100 YARDS" 160 PRINT "OF THE TARGET WILL DESTROY IT." : PRINT 170 R=INT(40000*RND(1)+20000) 180 PRINT "MAXIMUM RANGE OF YOUR GUN IS";R;" YARDS." 185 Z=0 190 PRINT 195 S1=0 200 T=INT(R*(.1+.8*RND(1))) 210 S=0 220 GOTO 370 230 PRINT "MINIMUM ELEVATION IS ONE DEGREE." 240 GOTO 390 250 PRINT "MAXIMUM ELEVATION IS 89 DEGREES." 260 GOTO 390 270 PRINT "OVER TARGET BY ";ABS(E);"YARDS." 280 GOTO 390 290 PRINT "SHORT OF TARGET BY "ABS(E);"YARDS." 300 GOTO 390 320 PRINT "*** TARGET DESTROYED *** ";S;"ROUNDS OF AMMUNITION EXPENDED." 325 S1=S1+S 330 IF Z=4 THEN 490 340 Z=Z+1 345 PRINT 350 PRINT "THE FORWARD OBSERVER HAS SIGHTED MORE ENEMY ACTIVITY..." 360 GOTO 200 370 PRINT "DISTANCE TO THE TARGET IS "T;" YARDS." 380 PRINT 390 PRINT 400 INPUT "ELEVATION";B 420 IF B>89 THEN 250 430 IF B<1 THEN 230 440 S=S+1 442 IF S<6 THEN 450 444 PRINT:PRINT "BOOM !!!! YOU HAVE JUST BEEN DESTROYED "; 446 PRINT "BY THE ENEMY." : PRINT : PRINT : PRINT : GOTO 495 450 B2=2*B/57.3 : I=R*SIN(B2) : X=T-I : E=INT(X) 460 IF ABS(E)<100 THEN 320 470 IF E>100 THEN 290 480 GOTO 270 490 PRINT : PRINT : PRINT "TOTAL ROUNDS EXPENDED WERE:";S1 492 IF S1>18 THEN 495 493 PRINT "NICE SHOOTING !!" : GOTO 500 495 PRINT "BETTER GO BACK TO FORT SILL FOR REFRESHER TRAINING!" 500 PRINT : INPUT "TRY AGAIN (Y OR N)";Z$ 510 IF Z$="Y" THEN 170 520 PRINT:PRINT "OK. RETURN TO BASE CAMP." 999 END ================================================ FILE: 00_Alternate_Languages/43_Hammurabi/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/43_Hammurabi/MiniScript/hammurabi.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/43_Hammurabi/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/43_Hammurabi/hammurabi.bas ================================================ 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) 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." 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 323 GOSUB 710 324 GOTO 320 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 Q < A THEN 350 343 GOSUB 720 344 GOTO 340 350 A = A - Q : S = S + Y * Q : C = 0 400 PRINT 410 PRINT "HOW MANY BUSHELS DO YOU WISH TO FEED YOUR PEOPLE"; 411 INPUT Q 412 IF Q < 0 THEN 850 418 REM *** TRYING TO USE MORE GRAIN THAN IS IN SILOS? 420 IF Q <= S THEN 430 421 GOSUB 710 422 GOTO 410 430 S = S - Q : C = 1 : PRINT 440 PRINT "HOW MANY ACRES DO YOU WISH TO PLANT WITH SEED"; 441 INPUT D : IF D = 0 THEN 511 442 IF D < 0 THEN 850 444 REM *** TRYING TO PLANT MORE ACRES THAN YOU OWN? 445 IF D <= A THEN 450 446 GOSUB 720 447 GOTO 440 449 REM *** ENOUGH GRAIN FOR SEED? 450 IF INT(D / 2) <= S THEN 455 452 GOSUB 710 453 GOTO 440 454 REM *** ENOUGH PEOPLE TO TEND THE CROPS? 455 IF D < 10 * P THEN 510 460 PRINT "BUT YOU HAVE ONLY "; P; " PEOPLE TO TEND THE FIELDS! NOW THEN," 470 GOTO 440 510 S = S - INT(D / 2) 511 GOSUB 800 512 REM *** A BOUNTIFUL HARVEST! 515 Y = C : H = D * Y : E = 0 521 GOSUB 800 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 531 GOSUB 800 532 REM *** LET'S HAVE SOME BABIES 533 I = INT(C *(20 * A + S) / P / 100 + 1) 539 REM *** HOW MANY PEOPLE HAD FULL TUMMIES? 540 C = INT(Q / 20) 541 REM *** HORROS, A 15% CHANCE OF PLAGUE 542 Q = INT(10 *(2 * RND(1) - 0.3)) 550 IF P < C THEN 210 551 REM *** STARVE ENOUGH FOR IMPEACHMENT? 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 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." 855 PRINT "GET YOURSELF ANOTHER STEWARD!!!!!" 857 GOTO 990 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 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 900 PRINT "A FANTASTIC PERFORMANCE!!! CHARLEMANGE, DISRAELI, AND" 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 960 PRINT "YOUR PERFORMANCE COULD HAVE BEEN SOMEWHAT BETTER, BUT" 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 ================================================ FILE: 00_Alternate_Languages/44_Hangman/C/dictionary.txt ================================================ harm retire principle tile biology show reporter profound prestige hardship supplementary abundant firm preparation mother welfare broadcast virgin bloody shaft bird buy passion south slant hesitate leak ride contempt banner hurt disaster ranch damage conceive environmental outside apathy temple arrange hour tone intelligence soup bishop fool chase snub develop domination cry distant poem implication rally assertive anxiety home bear execute century solo cathedral terminal integration mastermind pen X-ray match ceremony stop linger slow desert superior tender debt criticism rehabilitation finish jest scream piece mask approach sequence negotiation to traffic midnight aspect dull concession citizen conception instrument compartment responsibility resist withdraw ================================================ FILE: 00_Alternate_Languages/44_Hangman/C/main.c ================================================ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #define MAX_WORDS 100 //check if windows or linux for the clear screen #ifdef _WIN32 #define CLEAR "cls" #else #define CLEAR "clear" #endif /** * @brief Prints the stage of the hangman based on the number of wrong guesses. * * @param stage Hangman stage. */ void print_hangman(int stage){ switch (stage){ case 0: printf("----------\n"); printf("| |\n"); printf("|\n"); printf("|\n"); printf("|\n"); printf("|\n"); break; case 1: printf("----------\n"); printf("| |\n"); printf("| O\n"); printf("| |\n"); printf("|\n"); printf("|\n"); break; case 2: printf("----------\n"); printf("| |\n"); printf("| o\n"); printf("| /|\n"); printf("|\n"); printf("|\n"); break; case 3: printf("----------\n"); printf("| |\n"); printf("| o\n"); printf("| /|\\\n"); printf("|\n"); printf("|\n"); break; case 4: printf("----------\n"); printf("| |\n"); printf("| o\n"); printf("| /|\\\n"); printf("| /\n"); printf("|\n"); break; case 5: printf("----------\n"); printf("| |\n"); printf("| o\n"); printf("| /|\\\n"); printf("| / \\\n"); printf("|\n"); break; default: break; } } /** * @brief Picks and return a random word from the dictionary. * * @return Random word */ char* random_word_picker(){ //generate a random english word char* word = malloc(sizeof(char) * 100); FILE* fp = fopen("dictionary.txt", "r"); srand(time(NULL)); if (fp == NULL){ printf("Error opening dictionary.txt\n"); exit(1); } int random_number = rand() % MAX_WORDS; for (int j = 0; j < random_number; j++){ fscanf(fp, "%s", word); } fclose(fp); return word; } void main(void){ char* word = malloc(sizeof(char) * 100); word = random_word_picker(); char* hidden_word = malloc(sizeof(char) * 100); for (int i = 0; i < strlen(word); i++){ hidden_word[i] = '_'; } hidden_word[strlen(word)] = '\0'; int stage = 0; int wrong_guesses = 0; int correct_guesses = 0; char* guess = malloc(sizeof(char) * 100); while (wrong_guesses < 6 && correct_guesses < strlen(word)){ CLEAR; print_hangman(stage); printf("%s\n", hidden_word); printf("Enter a guess: "); scanf("%s", guess); for (int i = 0; i < strlen(word); i++){ if (strcmp(guess,word) == 0){ correct_guesses = strlen(word); } else if (guess[0] == word[i]){ hidden_word[i] = guess[0]; correct_guesses++; } } if (strchr(word, guess[0]) == NULL){ wrong_guesses++; } stage = wrong_guesses; } if (wrong_guesses == 6){ printf("You lose! The word was %s\n", word); } else { printf("You win!\n"); } } ================================================ FILE: 00_Alternate_Languages/44_Hangman/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/44_Hangman/MiniScript/hangman.ms ================================================ 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." ================================================ FILE: 00_Alternate_Languages/44_Hangman/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/44_Hangman/hangman.bas ================================================ 10 PRINT TAB(32);"HANGMAN" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 25 PRINT:PRINT:PRINT 30 DIM P$(12,12),L$(20),D$(20),N$(26),U(50) 40 C=1: N=50 50 FOR I=1 TO 20: D$(I)="-": NEXT I: M=0 60 FOR I=1 TO 26: N$(I)="": NEXT I 70 FOR I=1 TO 12: FOR J=1 TO 12: P$(I,J)=" ": NEXT J: NEXT I 80 FOR I=1 TO 12: P$(I,1)="X": NEXT I 90 FOR I=1 TO 7: P$(1,I)="X": NEXT: P$(2,7)="X" 95 IF C<N THEN 100 97 PRINT "YOU DID ALL THE WORDS!!": STOP 100 Q=INT(N*RND(1))+1 110 IF U(Q)=1 THEN 100 115 U(Q)=1: C=C+1: RESTORE: T1=0 150 FOR I=1 TO Q: READ A$: NEXT I 160 L=LEN(A$): FOR I=1 TO LEN(A$): L$(I)=MID$(A$,I,1): NEXT I 170 PRINT "HERE ARE THE LETTERS YOU USED:" 180 FOR I=1 TO 26: PRINT N$(I);: IF N$(I+1)="" THEN 200 190 PRINT ",";: NEXT I 200 PRINT: PRINT: FOR I=1 TO L: PRINT D$(I);: NEXT I: PRINT: PRINT 210 INPUT "WHAT IS YOUR GUESS";G$: R=0 220 FOR I=1 TO 26: IF N$(I)="" THEN 250 230 IF G$=N$(I) THEN PRINT "YOU GUESSED THAT LETTER BEFORE!": GOTO 170 240 NEXT I: PRINT "PROGRAM ERROR. RUN AGAIN.": STOP 250 N$(I)=G$: T1=T1+1 260 FOR I=1 TO L: IF L$(I)=G$ THEN 280 270 NEXT I: IF R=0 THEN 290 275 GOTO 300 280 D$(I)=G$: R=R+1: GOTO 270 290 M=M+1: GOTO 400 300 FOR I=1 TO L: IF D$(I)="-" THEN 320 310 NEXT I: GOTO 390 320 PRINT: FOR I=1 TO L: PRINT D$(I);: NEXT I: PRINT: PRINT 330 INPUT "WHAT IS YOUR GUESS FOR THE WORD";B$ 340 IF A$=B$ THEN 360 350 PRINT "WRONG. TRY ANOTHER LETTER.": PRINT: GOTO 170 360 PRINT "RIGHT!! IT TOOK YOU";T1;"GUESSES!" 370 INPUT "WANT ANOTHER WORD";W$: IF W$="YES" THEN 50 380 PRINT: PRINT "IT'S BEEN FUN! BYE FOR NOW.": GOTO 999 390 PRINT "YOU FOUND THE WORD!": GOTO 370 400 PRINT: PRINT: PRINT"SORRY, THAT LETTER ISN'T IN THE WORD." 410 ON M GOTO 415,420,425,430,435,440,445,450,455,460 415 PRINT "FIRST, WE DRAW A HEAD": GOTO 470 420 PRINT "NOW WE DRAW A BODY.": GOTO 470 425 PRINT "NEXT WE DRAW AN ARM.": GOTO 470 430 PRINT "THIS TIME IT'S THE OTHER ARM.": GOTO 470 435 PRINT "NOW, LET'S DRAW THE RIGHT LEG.": GOTO 470 440 PRINT "THIS TIME WE DRAW THE LEFT LEG.": GOTO 470 445 PRINT "NOW WE PUT UP A HAND.": GOTO 470 450 PRINT "NEXT THE OTHER HAND.": GOTO 470 455 PRINT "NOW WE DRAW ONE FOOT": GOTO 470 460 PRINT "HERE'S THE OTHER FOOT -- YOU'RE HUNG!!" 470 ON M GOTO 480,490,500,510,520,530,540,550,560,570 480 P$(3,6)="-": P$(3,7)="-": P$(3,8)="-": P$(4,5)="(": P$(4,6)="." 481 P$(4,8)=".":P$(4,9)=")":P$(5,6)="-":P$(5,7)="-":P$(5,8)="-":GOTO 580 490 FOR I=6 TO 9: P$(I,7)="X": NEXT I: GOTO 580 500 FOR I=4 TO 7: P$(I,I-1)="\": NEXT I: GOTO 580 510 P$(4,11)="/": P$(5,10)="/": P$(6,9)="/": P$(7,8)="/": GOTO 580 520 P$(10,6)="/": P$(11,5)="/": GOTO 580 530 P$(10,8)="\": P$(11,9)="\": GOTO 580 540 P$(3,11)="\": GOTO 580 550 P$(3,3)="/": GOTO 580 560 P$(12,10)="\": P$(12,11)="-": GOTO 580 570 P$(12,3)="-": P$(12,4)="/" 580 FOR I=1 TO 12: FOR J=1 TO 12: PRINT P$(I,J);: NEXT J 590 PRINT: NEXT I: PRINT: PRINT: IF M<>10 THEN 170 600 PRINT "SORRY, YOU LOSE. THE WORD WAS ";A$ 610 PRINT "YOU MISSED THAT ONE. DO YOU ";: GOTO 370 620 INPUT "TYPE YES OR NO";Y$: IF LEFT$(Y$,1)="Y" THEN 50 700 DATA "GUM","SIN","FOR","CRY","LUG","BYE","FLY" 710 DATA "UGLY","EACH","FROM","WORK","TALK","WITH","SELF" 720 DATA "PIZZA","THING","FEIGN","FIEND","ELBOW","FAULT","DIRTY" 730 DATA "BUDGET","SPIRIT","QUAINT","MAIDEN","ESCORT","PICKAX" 740 DATA "EXAMPLE","TENSION","QUININE","KIDNEY","REPLICA","SLEEPER" 750 DATA "TRIANGLE","KANGAROO","MAHOGANY","SERGEANT","SEQUENCE" 760 DATA "MOUSTACHE","DANGEROUS","SCIENTIST","DIFFERENT","QUIESCENT" 770 DATA "MAGISTRATE","ERRONEOUSLY","LOUDSPEAKER","PHYTOTOXIC" 780 DATA "MATRIMONIAL","PARASYMPATHOMIMETIC","THIGMOTROPISM" 990 PRINT "BYE NOW" 999 END ================================================ FILE: 00_Alternate_Languages/45_Hello/ANSI_C/hello.c ================================================ #include <stdio.h> #include <string.h> #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; } ================================================ FILE: 00_Alternate_Languages/45_Hello/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/45_Hello/MiniScript/hello.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/45_Hello/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/45_Hello/Swift/hello.swift ================================================ 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() ================================================ FILE: 00_Alternate_Languages/45_Hello/go/main.go ================================================ package main import ( "bufio" "fmt" "os" "strings" "time" ) type PROBLEM_TYPE int8 const ( SEX PROBLEM_TYPE = iota HEALTH MONEY JOB UKNOWN ) func getYesOrNo() (bool, bool, string) { scanner := bufio.NewScanner(os.Stdin) scanner.Scan() if strings.ToUpper(scanner.Text()) == "YES" { return true, true, scanner.Text() } else if strings.ToUpper(scanner.Text()) == "NO" { return true, false, scanner.Text() } else { return false, false, scanner.Text() } } func printTntro() { fmt.Println(" HELLO") fmt.Println(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") fmt.Print("\n\n\n") fmt.Println("HELLO. MY NAME IS CREATIVE COMPUTER.") fmt.Println("\nWHAT'S YOUR NAME?") } func askEnjoyQuestion(user string) { fmt.Printf("HI THERE %s, ARE YOU ENJOYING YOURSELF HERE?\n", user) for { valid, value, msg := getYesOrNo() if valid { if value { fmt.Printf("I'M GLAD TO HEAR THAT, %s.\n", user) fmt.Println() } else { fmt.Printf("OH, I'M SORRY TO HEAR THAT, %s. MAYBE WE CAN\n", user) fmt.Println("BRIGHTEN UP YOUR VISIT A BIT.") } break } else { fmt.Printf("%s, I DON'T UNDERSTAND YOUR ANSWER OF '%s'.\n", user, msg) fmt.Println("PLEASE ANSWER 'YES' OR 'NO'. DO YOU LIKE IT HERE?") } } } func promptForProblems(user string) PROBLEM_TYPE { scanner := bufio.NewScanner(os.Stdin) fmt.Println() fmt.Printf("SAY %s, I CAN SOLVE ALL KINDS OF PROBLEMS EXCEPT\n", user) fmt.Println("THOSE DEALING WITH GREECE. WHAT KIND OF PROBLEMS DO") fmt.Println("YOU HAVE? (ANSWER SEX, HEALTH, MONEY, OR JOB)") for { scanner.Scan() switch strings.ToUpper(scanner.Text()) { case "SEX": return SEX case "HEALTH": return HEALTH case "MONEY": return MONEY case "JOB": return JOB default: return UKNOWN } } } func promptTooMuchOrTooLittle() (bool, bool) { scanner := bufio.NewScanner(os.Stdin) scanner.Scan() if strings.ToUpper(scanner.Text()) == "TOO MUCH" { return true, true } else if strings.ToUpper(scanner.Text()) == "TOO LITTLE" { return true, false } else { return false, false } } func solveSexProblem(user string) { fmt.Println("IS YOUR PROBLEM TOO MUCH OR TOO LITTLE?") for { valid, tooMuch := promptTooMuchOrTooLittle() if valid { if tooMuch { fmt.Println("YOU CALL THAT A PROBLEM?!! I SHOULD HAVE SUCH PROBLEMS!") fmt.Printf("IF IT BOTHERS YOU, %s, TAKE A COLD SHOWER.\n", user) } else { fmt.Printf("WHY ARE YOU HERE IN SUFFERN, %s? YOU SHOULD BE\n", user) fmt.Println("IN TOKYO OR NEW YORK OR AMSTERDAM OR SOMEPLACE WITH SOME") fmt.Println("REAL ACTION.") } return } else { fmt.Printf("DON'T GET ALL SHOOK, %s, JUST ANSWER THE QUESTION\n", user) fmt.Println("WITH 'TOO MUCH' OR 'TOO LITTLE'. WHICH IS IT?") } } } func solveHealthProblem(user string) { fmt.Printf("MY ADVICE TO YOU %s IS:\n", user) fmt.Println(" 1. TAKE TWO ASPRIN") fmt.Println(" 2. DRINK PLENTY OF FLUIDS (ORANGE JUICE, NOT BEER!)") fmt.Println(" 3. GO TO BED (ALONE)") } func solveMoneyProblem(user string) { fmt.Printf("SORRY, %s, I'M BROKE TOO. WHY DON'T YOU SELL\n", user) fmt.Println("ENCYCLOPEADIAS OR MARRY SOMEONE RICH OR STOP EATING") fmt.Println("SO YOU WON'T NEED SO MUCH MONEY?") } func solveJobProblem(user string) { fmt.Printf("I CAN SYMPATHIZE WITH YOU %s. I HAVE TO WORK\n", user) fmt.Println("VERY LONG HOURS FOR NO PAY -- AND SOME OF MY BOSSES") fmt.Printf("REALLY BEAT ON MY KEYBOARD. MY ADVICE TO YOU, %s,\n", user) fmt.Println("IS TO OPEN A RETAIL COMPUTER STORE. IT'S GREAT FUN.") } func askQuestionLoop(user string) { for { problem := promptForProblems(user) switch problem { case SEX: solveSexProblem(user) case HEALTH: solveHealthProblem(user) case MONEY: solveMoneyProblem(user) case JOB: solveJobProblem(user) case UKNOWN: fmt.Printf("OH %s, YOUR ANSWER IS GREEK TO ME.\n", user) } for { fmt.Println() fmt.Printf("ANY MORE PROBLEMS YOU WANT SOLVED, %s?\n", user) valid, value, _ := getYesOrNo() if valid { if value { fmt.Println("WHAT KIND (SEX, MONEY, HEALTH, JOB)") break } else { return } } fmt.Printf("JUST A SIMPLE 'YES' OR 'NO' PLEASE, %s\n", user) } } } func goodbyeUnhappy(user string) { fmt.Println() fmt.Printf("TAKE A WALK, %s.\n", user) fmt.Println() fmt.Println() } func goodbyeHappy(user string) { fmt.Printf("NICE MEETING YOU %s, HAVE A NICE DAY.\n", user) } func askForFee(user string) { fmt.Println() fmt.Printf("THAT WILL BE $5.00 FOR THE ADVICE, %s.\n", user) fmt.Println("PLEASE LEAVE THE MONEY ON THE TERMINAL.") time.Sleep(4 * time.Second) fmt.Print("\n\n\n") fmt.Println("DID YOU LEAVE THE MONEY?") for { valid, value, msg := getYesOrNo() if valid { if value { fmt.Printf("HEY, %s, YOU LEFT NO MONEY AT ALL!\n", user) fmt.Println("YOU ARE CHEATING ME OUT OF MY HARD-EARNED LIVING.") fmt.Println() fmt.Printf("WHAT A RIP OFF, %s!!!\n", user) fmt.Println() } else { fmt.Printf("THAT'S HONEST, %s, BUT HOW DO YOU EXPECT\n", user) fmt.Println("ME TO GO ON WITH MY PSYCHOLOGY STUDIES IF MY PATIENTS") fmt.Println("DON'T PAY THEIR BILLS?") } return } else { fmt.Printf("YOUR ANSWER OF '%s' CONFUSES ME, %s.\n", msg, user) fmt.Println("PLEASE RESPOND WITH 'YES' or 'NO'.") } } } func main() { scanner := bufio.NewScanner(os.Stdin) printTntro() scanner.Scan() userName := scanner.Text() fmt.Println() askEnjoyQuestion(userName) askQuestionLoop(userName) askForFee(userName) if false { goodbyeHappy(userName) // unreachable } else { goodbyeUnhappy(userName) } } ================================================ FILE: 00_Alternate_Languages/45_Hello/hello.bas ================================================ 2 PRINT TAB(33);"HELLO" 4 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 6 PRINT: PRINT: PRINT 10 PRINT "HELLO. MY NAME IS CREATIVE COMPUTER." 20 PRINT: PRINT: INPUT "WHAT'S YOUR NAME";N$: PRINT 30 PRINT "HI THERE, ";N$;", ARE YOU ENJOYING YOURSELF HERE"; 40 INPUT B$: PRINT 50 IF B$="YES" THEN 70 55 IF B$="NO" THEN 80 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 70 PRINT "I'M GLAD TO HEAR THAT, ";N$;".": PRINT 75 GOTO 100 80 PRINT "OH, I'M SORRY TO HEAR THAT, ";N$;". MAYBE WE CAN" 85 PRINT "BRIGHTEN UP YOUR VISIT A BIT." 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 130 IF C$="SEX" THEN 200 132 IF C$="HEALTH" THEN 180 134 IF C$="MONEY" THEN 160 136 IF C$="JOB" THEN 145 138 PRINT "OH, ";N$;", YOUR ANSWER OF ";C$;" IS GREEK TO ME." 140 GOTO 250 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 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 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 200 INPUT "IS YOUR PROBLEM TOO MUCH OR TOO LITTLE";D$: PRINT 210 IF D$="TOO MUCH" THEN 220 212 IF D$="TOO LITTLE" THEN 230 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 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 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 255 PRINT "ANY MORE PROBLEMS YOU WANT SOLVED, ";N$; 260 INPUT E$: PRINT 270 IF E$="YES" THEN 280 273 IF E$="NO" THEN 300 275 PRINT "JUST A SIMPLE 'YES' OR 'NO' PLEASE, ";N$;"." 277 GOTO 255 280 PRINT "WHAT KIND (SEX, MONEY, HEALTH, JOB)"; 282 GOTO 125 300 PRINT 302 PRINT "THAT WILL BE $5.00 FOR THE ADVICE, ";N$;"." 305 PRINT "PLEASE LEAVE THE MONEY ON THE TERMINAL." 307 FOR I=1 TO 2000: NEXT I 310 PRINT: PRINT: PRINT 315 PRINT "DID YOU LEAVE THE MONEY"; 320 INPUT G$: PRINT 325 IF G$="YES" THEN 350 330 IF G$="NO" THEN 370 335 PRINT "YOUR ANSWER OF '";G$;"' CONFUSES ME, ";N$;"." 340 PRINT "PLEASE RESPOND WITH 'YES' OR 'NO'.": GOTO 315 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 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 390 PRINT "NICE MEETING YOU, ";N$;", HAVE A NICE DAY." 400 REM 999 END ================================================ FILE: 00_Alternate_Languages/45_Hello/hello.c ================================================ #include <stdio.h> #include <string.h> #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; } ================================================ FILE: 00_Alternate_Languages/46_Hexapawn/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/46_Hexapawn/MiniScript/hexapawn.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/46_Hexapawn/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/46_Hexapawn/hexapawn.bas ================================================ 1 PRINT TAB(32);"HEXAPAWN" 2 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 3 PRINT:PRINT:PRINT 4 REM HEXAPAWN: INTERPRETATION OF HEXAPAWN GAME AS PRESENTED IN 5 REM MARTIN GARDNER'S "THE UNEXPECTED HANGING AND OTHER MATHEMATIC- 6 REM AL DIVERSIONS", CHAPTER EIGHT: A MATCHBOX GAME-LEARNING MACHINE 7 REM ORIGINAL VERSION FOR H-P TIMESHARE SYSTEM BY R.A. KAAPKE 5/5/76 8 REM INSTRUCTIONS BY JEFF DALTON 9 REM CONVERSION TO MITS BASIC BY STEVE NORTH 10 DIM B(19,9),M(19,4),S(9),P$(3) 15 W=0: L=0 20 DEF FNR(X)=-3*(X=1)-(X=3)-4*(X=6)-6*(X=4)-7*(X=9)-9*(X=7)+FNS(X) 25 DEF FNS(X)=-X*(X=2 OR X=5 OR X=8) 30 DEF FNM(Y)=Y-INT(Y/10)*10 35 P$="X.O" 40 FOR I=1 TO 19: FOR J=1 TO 9: READ B(I,J): NEXT J: NEXT I 45 FOR I=1 TO 19: FOR J=1 TO 4: READ M(I,J): NEXT J: NEXT I 50 PRINT "INSTRUCTIONS (Y-N)"; 60 INPUT A$ 70 A$=LEFT$(A$,1) 80 IF A$="Y" THEN 2000 90 IF A$<>"N" THEN 50 100 X=0: Y=0 111 S(4)=0: S(5)=0: S(6)=0 112 S(1)=-1: S(2)=-1: S(3)=-1 113 S(7)=1: S(8)=1: S(9)=1 115 GOSUB 1000 120 PRINT "YOUR MOVE"; 121 INPUT M1,M2 122 IF M1=INT(M1)AND M2=INT(M2)AND M1>0 AND M1<10 AND M2>0 AND M2<10 THEN 130 123 PRINT "ILLEGAL CO-ORDINATES." 124 GOTO 120 130 IF S(M1)=1 THEN 150 140 PRINT "ILLEGAL MOVE.": GOTO 120 150 IF S(M2)=1 THEN 140 160 IF M2-M1<>-3 AND S(M2)<>-1 THEN 140 170 IF M2>M1 THEN 140 180 IF M2-M1=-3 AND (S(M2)<>0) THEN 140 185 IF M2-M1<-4 THEN 140 186 IF M1=7 AND M2=3 THEN 140 190 S(M1)=0 200 S(M2)=1 205 GOSUB 1000 210 IF S(1)=1 OR S(2)=1 OR S(3)=1 THEN 820 220 FOR I=1 TO 9 221 IF S(I)=-1 THEN 230 222 NEXT I 223 GOTO 820 230 FOR I=1 TO 9 240 IF S(I)<>-1 THEN 330 250 IF S(I+3)=0 THEN 350 260 IF FNR(I)=I THEN 320 270 IF I>3 THEN 300 280 IF S(5)=1 THEN 350 290 GOTO 330 300 IF S(8)=1 THEN 350 310 GOTO 330 320 IF S(I+2)=1 OR S(I+4)=1 THEN 350 330 NEXT I 340 GOTO 820 350 FOR I=1 TO 19 360 FOR J=1 TO 3 370 FOR K=3 TO 1 STEP -1 380 T((J-1)*3+K)=B(I,(J-1)*3+4-K) 390 NEXT K 400 NEXT J 410 FOR J=1 TO 9 420 IF S(J)<>B(I,J) THEN 460 430 NEXT J 440 R=0 450 GOTO 540 460 FOR J=1 TO 9 470 IF S(J)<>T(J) THEN 510 480 NEXT J 490 R=1 500 GOTO 540 510 NEXT I 511 REMEMBER THE TERMINATION OF THIS LOOP IS IMPOSSIBLE 512 PRINT "ILLEGAL BOARD PATTERN." 530 STOP 540 X=I 550 FOR I=1 TO 4 560 IF M(X,I)<>0 THEN 600 570 NEXT I 580 PRINT "I RESIGN." 590 GOTO 820 600 Y=INT(RND(1)*4+1) 601 IF M(X,Y)=0 THEN 600 610 IF R<>0 THEN 630 620 PRINT "I MOVE FROM ";STR$(INT(M(X,Y)/10));" TO ";STR$(FNM(M(X,Y))) 622 S(INT(M(X,Y)/10))=0 623 S(FNM(M(X,Y)))=-1 624 GOTO 640 630 PRINT "I MOVE FROM ";STR$(FNR(INT(M(X,Y)/10)));" TO "; 631 PRINT STR$(FNR(FNM(M(X,Y)))) 632 S(FNR(INT(M(X,Y)/10)))=0 633 S(FNR(FNM(M(X,Y))))=-1 640 GOSUB 1000 641 IF S(7)=-1 OR S(8)=-1 OR S(9)=-1 THEN 870 650 FOR I=1 TO 9 660 IF S(I)=1 THEN 690 670 NEXT I 680 GOTO 870 690 FOR I=1 TO 9 700 IF S(I)<>1 THEN 790 710 IF S(I-3)=0 THEN 120 720 IF FNR(I)=I THEN 780 730 IF I<7 THEN 760 740 IF S(5)=-1 THEN 120 750 GOTO 790 760 IF S(2)=-1 THEN 120 770 GOTO 790 780 IF S(I-2)=-1 OR S(I-4)=-1 THEN 120 790 NEXT I 800 PRINT "YOU CAN'T MOVE, SO "; 810 GOTO 870 820 PRINT "YOU WIN." 830 M(X,Y)=0 840 L=L+1 850 PRINT "I HAVE WON";W;"AND YOU";L;"OUT OF";L+W;"GAMES." 851 PRINT 860 GOTO 100 870 PRINT "I WIN." 880 W=W+1 890 GOTO 850 900 DATA -1,-1,-1,1,0,0,0,1,1,-1,-1,-1,0,1,0,1,0,1 905 DATA -1,0,-1,-1,1,0,0,0,1,0,-1,-1,1,-1,0,0,0,1 910 DATA -1,0,-1,1,1,0,0,1,0,-1,-1,0,1,0,1,0,0,1 915 DATA 0,-1,-1,0,-1,1,1,0,0,0,-1,-1,-1,1,1,1,0,0 920 DATA -1,0,-1,-1,0,1,0,1,0,0,-1,-1,0,1,0,0,0,1 925 DATA 0,-1,-1,0,1,0,1,0,0,-1,0,-1,1,0,0,0,0,1 930 DATA 0,0,-1,-1,-1,1,0,0,0,-1,0,0,1,1,1,0,0,0 935 DATA 0,-1,0,-1,1,1,0,0,0,-1,0,0,-1,-1,1,0,0,0 940 DATA 0,0,-1,-1,1,0,0,0,0,0,-1,0,1,-1,0,0,0,0 945 DATA -1,0,0,-1,1,0,0,0,0 950 DATA 24,25,36,0,14,15,36,0,15,35,36,47,36,58,59,0 955 DATA 15,35,36,0,24,25,26,0,26,57,58,0 960 DATA 26,35,0,0,47,48,0,0,35,36,0,0,35,36,0,0 965 DATA 36,0,0,0,47,58,0,0,15,0,0,0 970 DATA 26,47,0,0,47,58,0,0,35,36,47,0,28,58,0,0,15,47,0,0 1000 PRINT 1010 FOR I=1 TO 3 1020 FOR J=1 TO 3 1030 PRINT TAB(10);MID$(P$,S((I-1)*3+J)+2,1); 1040 NEXT J 1050 PRINT 1060 NEXT I 1070 PRINT 1080 RETURN 2000 PRINT: PRINT "THIS PROGRAM PLAYS THE GAME OF HEXAPAWN." 2010 PRINT "HEXAPAWN IS PLAYED WITH CHESS PAWNS ON A 3 BY 3 BOARD." 2020 PRINT "THE PAWNS ARE MOVED AS IN CHESS - ONE SPACE FORWARD TO" 2030 PRINT "AN EMPTY SPACE OR ONE SPACE FORWARD AND DIAGONALLY TO" 2040 PRINT "CAPTURE AN OPPOSING MAN. ON THE BOARD, YOUR PAWNS" 2050 PRINT "ARE 'O', THE COMPUTER'S PAWNS ARE 'X', AND EMPTY " 2060 PRINT "SQUARES ARE '.'. TO ENTER A MOVE, TYPE THE NUMBER OF" 2070 PRINT "THE SQUARE YOU ARE MOVING FROM, FOLLOWED BY THE NUMBER" 2080 PRINT "OF THE SQUARE YOU WILL MOVE TO. THE NUMBERS MUST BE" 2090 PRINT "SEPERATED BY A COMMA.": PRINT 2100 PRINT "THE COMPUTER STARTS A SERIES OF GAMES KNOWING ONLY WHEN" 2105 PRINT "THE GAME IS WON (A DRAW IS IMPOSSIBLE) AND HOW TO MOVE." 2110 PRINT "IT HAS NO STRATEGY AT FIRST AND JUST MOVES RANDOMLY." 2120 PRINT "HOWEVER, IT LEARNS FROM EACH GAME. THUS, WINNING BECOMES" 2130 PRINT "MORE AND MORE DIFFICULT. ALSO, TO HELP OFFSET YOUR" 2140 PRINT "INITIAL ADVANTAGE, YOU WILL NOT BE TOLD HOW TO WIN THE" 2150 PRINT "GAME BUT MUST LEARN THIS BY PLAYING." 2160 PRINT: PRINT "THE NUMBERING OF THE BOARD IS AS FOLLOWS:" 2170 PRINT TAB(10);"123": PRINT TAB(10);"456": PRINT TAB(10);"789" 2180 PRINT: PRINT "FOR EXAMPLE, TO MOVE YOUR RIGHTMOST PAWN FORWARD," 2190 PRINT "YOU WOULD TYPE 9,6 IN RESPONSE TO THE QUESTION" 2200 PRINT "'YOUR MOVE ?'. SINCE I'M A GOOD SPORT, YOU'LL ALWAYS" 2210 PRINT "GO FIRST.": PRINT 2220 GOTO 100 9999 END ================================================ FILE: 00_Alternate_Languages/47_Hi-Lo/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/47_Hi-Lo/MiniScript/hi-lo.ms ================================================ 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!!!" ================================================ FILE: 00_Alternate_Languages/47_Hi-Lo/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/47_Hi-Lo/go/main.go ================================================ package main import ( "bufio" "fmt" "math/rand" "os" "strconv" "strings" "time" ) const MAX_ATTEMPTS = 6 func printIntro() { fmt.Println("HI LO") fmt.Println("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") fmt.Println("\n\n\nTHIS IS THE GAME OF HI LO.") fmt.Println("\nYOU WILL HAVE 6 TRIES TO GUESS THE AMOUNT OF MONEY IN THE") fmt.Println("HI LO JACKPOT, WHICH IS BETWEEN 1 AND 100 DOLLARS. IF YOU") fmt.Println("GUESS THE AMOUNT, YOU WIN ALL THE MONEY IN THE JACKPOT!") fmt.Println("THEN YOU GET ANOTHER CHANCE TO WIN MORE MONEY. HOWEVER,") fmt.Println("IF YOU DO NOT GUESS THE AMOUNT, THE GAME ENDS.") fmt.Println() fmt.Println() } func main() { rand.Seed(time.Now().UnixNano()) scanner := bufio.NewScanner(os.Stdin) printIntro() totalWinnings := 0 for { fmt.Println() secret := rand.Intn(1000) + 1 guessedCorrectly := false for attempt := 0; attempt < MAX_ATTEMPTS; attempt++ { fmt.Println("YOUR GUESS?") scanner.Scan() guess, err := strconv.Atoi(scanner.Text()) if err != nil { fmt.Println("INVALID INPUT") } if guess == secret { fmt.Printf("GOT IT!!!!!!!!!! YOU WIN %d DOLLARS.\n", secret) guessedCorrectly = true break } else if guess > secret { fmt.Println("YOUR GUESS IS TOO HIGH.") } else { fmt.Println("YOUR GUESS IS TOO LOW.") } } if guessedCorrectly { totalWinnings += secret fmt.Printf("YOUR TOTAL WINNINGS ARE NOW $%d.\n", totalWinnings) } else { fmt.Printf("YOU BLEW IT...TOO BAD...THE NUMBER WAS %d\n", secret) } fmt.Println() fmt.Println("PLAYAGAIN (YES OR NO)?") scanner.Scan() if strings.ToUpper(scanner.Text())[0:1] != "Y" { break } } fmt.Println("\nSO LONG. HOPE YOU ENJOYED YOURSELF!!!") } ================================================ FILE: 00_Alternate_Languages/47_Hi-Lo/hi-lo.bas ================================================ 10 PRINT TAB(34);"HI LO" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 30 PRINT:PRINT:PRINT 100 PRINT "THIS IS THE GAME OF HI LO.":PRINT 110 PRINT "YOU WILL HAVE 6 TRIES TO GUESS THE AMOUNT OF MONEY IN THE" 120 PRINT "HI LO JACKPOT, WHICH IS BETWEEN 1 AND 100 DOLLARS. IF YOU" 130 PRINT "GUESS THE AMOUNT, YOU WIN ALL THE MONEY IN THE JACKPOT!" 140 PRINT "THEN YOU GET ANOTHER CHANCE TO WIN MORE MONEY. HOWEVER," 150 PRINT "IF YOU DO NOT GUESS THE AMOUNT, THE GAME ENDS.":PRINT 160 R=0 170 B=0:PRINT 180 Y=INT(100*RND(1)) 200 PRINT "YOUR GUESS"; 210 INPUT A 220 B=B+1 230 IF A=Y THEN 300 240 IF A>Y THEN 270 250 PRINT "YOUR GUESS IS TOO LOW.":GOTO 280 270 PRINT "YOUR GUESS IS TOO HIGH." 280 PRINT:IF B<6 THEN 200 290 PRINT "YOU BLEW IT...TOO BAD...THE NUMBER WAS";Y 295 R=0:GOTO 350 300 PRINT "GOT IT!!!!!!!!!! YOU WIN";Y;"DOLLARS." 310 R=R+Y 320 PRINT "YOUR TOTAL WINNINGS ARE NOW";R;"DOLLARS." 350 PRINT:PRINT "PLAY AGAIN (YES OR NO)"; 360 INPUT A$:IF A$="YES" THEN 170 380 PRINT:PRINT "SO LONG. HOPE YOU ENJOYED YOURSELF!!!" 390 END ================================================ FILE: 00_Alternate_Languages/48_High_IQ/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/48_High_IQ/MiniScript/highiq.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/48_High_IQ/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/48_High_IQ/highiq.bas ================================================ 1 PRINT TAB(33);"H-I-Q" 2 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 3 PRINT:PRINT:PRINT 4 DIM B(70),T(9,9) 5 PRINT "HERE IS THE BOARD:": PRINT 6 PRINT " ! ! !" 7 PRINT " 13 14 15": PRINT 8 PRINT " ! ! !" 9 PRINT " 22 23 24": PRINT 10 PRINT "! ! ! ! ! ! !" 11 PRINT "29 30 31 32 33 34 35": PRINT 12 PRINT "! ! ! ! ! ! !" 13 PRINT "38 39 40 41 42 43 44": PRINT 14 PRINT "! ! ! ! ! ! !" 15 PRINT "47 48 49 50 51 52 53": PRINT 16 PRINT " ! ! !" 17 PRINT " 58 59 60": PRINT 18 PRINT " ! ! !" 19 PRINT " 67 68 69": PRINT 20 PRINT "TO SAVE TYPING TIME, A COMPRESSED VERSION OF THE GAME BOARD" 22 PRINT "WILL BE USED DURING PLAY. REFER TO THE ABOVE ONE FOR PEG" 24 PRINT "NUMBERS. OK, LET'S BEGIN." 28 REM *** SET UP BOARD 29 FOR R=1 TO 9 30 FOR C=1 TO 9 31 IF (R-4)*(R-5)*(R-6)=0 THEN 40 32 IF (C-4)*(C-5)*(C-6)=0 THEN 40 35 T(R,C)=-5 36 GOTO 50 40 IF (R-1)*(C-1)*(R-9)*(C-9)=0 THEN 35 42 T(R,C)=5 50 NEXT C 60 NEXT R 65 T(5,5)=0: GOSUB 500 70 REM *** INPUT MOVE AND CHECK ON LEGALITY 75 FOR W=1 TO 33 77 READ M 79 DATA 13,14,15,22,23,24,29,30,31,32,33,34,35,38,39,40,41 81 DATA 42,43,44,47,48,49,50,51,52,53,58,59,60,67,68,69 83 B(M)=-7: NEXT W 86 B(41)=-3 100 INPUT "MOVE WHICH PIECE";Z 110 IF B(Z)=-7 THEN 140 120 PRINT "ILLEGAL MOVE, TRY AGAIN...": GOTO 100 140 INPUT "TO WHERE";P 150 IF B(P)=0 THEN 120 153 IF B(P)=-7 THEN 120 156 IF Z=P THEN 100 160 IF ((Z+P)/2)=INT((Z+P)/2) THEN 180 170 GOTO 120 180 IF (ABS(Z-P)-2)*(ABS(Z-P)-18)<>0 THEN 120 190 GOSUB 1000 200 GOSUB 500 210 GOSUB 1500 220 GOTO 100 500 REM *** PRINT BOARD 510 FOR X=1 TO 9 520 FOR Y=1 TO 9 525 IF (X-1)*(X-9)*(Y-1)*(Y-9)=0 THEN 550 530 IF (X-4)*(X-5)*(X-6)=0 THEN 570 540 IF (Y-4)*(Y-5)*(Y-6)=0 THEN 570 550 REM 560 GOTO 610 570 IF T(X,Y)<>5 THEN 600 580 PRINT TAB(Y*2);"!"; 590 GOTO 610 600 PRINT TAB(Y*2);"O"; 610 REM 615 NEXT Y 620 PRINT 630 NEXT X 640 RETURN 1000 REM *** UPDATE BOARD 1005 C=1: FOR X=1 TO 9 1020 FOR Y=1 TO 9 1030 IF C<>Z THEN 1220 1040 IF C+2<>P THEN 1080 1045 IF T(X,Y+1)=0 THEN 120 1050 T(X,Y+2)=5 1060 T(X,Y+1)=0: B(C+1)=-3 1070 GOTO 1200 1080 IF C+18<>P THEN 1130 1085 IF T(X+1,Y)=0 THEN 120 1090 T(X+2,Y)=5: T(X+1,Y)=0: B(C+9)=-3 1120 GOTO 1200 1130 IF C-2<>P THEN 1170 1135 IF T(X,Y-1)=0 THEN 120 1140 T(X,Y-2)=5: T(X,Y-1)=0: B(C-1)=-3 1160 GOTO 1200 1170 IF C-18<>P THEN 1220 1175 IF T(X-1,Y)=0 THEN 120 1180 T(X-2,Y)=5: T(X-1,Y)=0: B(C-9)=-3 1200 B(Z)=-3: B(P)=-7 1210 T(X,Y)=0: GOTO 1240 1220 C=C+1 1225 NEXT Y 1230 NEXT X 1240 RETURN 1500 REM*** CHECK IF GAME IS OVER 1505 F=0 1510 FOR R=2 TO 8 1520 FOR C=2 TO 8 1530 IF T(R,C)<>5 THEN 1580 1535 F=F+1 1540 FOR A=R-1 TO R+1 1545 T=0 1550 FOR B=C-1 TO C+1 1560 T=T+T(A,B) 1561 NEXT B 1564 IF T<>10 THEN 1567 1565 IF T(A,C)<>0 THEN 1630 1567 NEXT A 1568 FOR X=C-1 TO C+1 1569 T=0 1570 FOR Y=R-1 TO R+1 1571 T=T+T(Y,X) 1572 NEXT Y 1573 IF T<>10 THEN 1575 1574 IF T(R,X)<>0 THEN 1630 1575 NEXT X 1580 NEXT C 1590 NEXT R 1600 REM *** GAME IS OVER 1605 PRINT "THE GAME IS OVER." 1610 PRINT "YOU HAD";F;"PIECES REMAINING." 1611 IF F<>1 THEN 1615 1612 PRINT "BRAVO! YOU MADE A PERFECT SCORE!" 1613 PRINT "SAVE THIS PAPER AS A RECORD OF YOUR ACCOMPLISHMENT!" 1615 PRINT: INPUT "PLAY AGAIN (YES OR NO)";A$ 1617 IF A$="NO" THEN 2000 1618 RESTORE: GOTO 28 1620 STOP 1630 RETURN 2000 PRINT: PRINT "SO LONG FOR NOW.": PRINT 2010 END ================================================ FILE: 00_Alternate_Languages/49_Hockey/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/49_Hockey/MiniScript/hockey.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/49_Hockey/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/49_Hockey/hockey.bas ================================================ 2 PRINT TAB(33);"HOCKEY" 4 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 6 PRINT:PRINT:PRINT 10 REM ROBERT PUOPOLO ALG. 1 140 MCCOWAN 6/7/73 HOCKEY 30 LET X=1 40 PRINT:PRINT:PRINT 50 PRINT "WOULD YOU LIKE THE INSTRUCTIONS";:INPUT C$ 55 PRINT 60 IF C$="NO" THEN 90 65 IF C$="YES" THEN 80 70 PRINT "ANSWER YES OR NO!!":GOTO 50 80 GOTO 1720 90 DIM A$(7),B$(7),H(20),T(5),T1(5),T2(5),T3(5) 100 PRINT "ENTER THE TWO TEAMS";:INPUT A$(7),B$(7) 105 PRINT 110 PRINT "ENTER THE NUMBER OF MINUTES IN A GAME";:INPUT T6 115 PRINT 120 IF T6<1 THEN 110:PRINT 130 PRINT "WOULD THE " A$(7) " COACH ENTER HIS TEAM" 135 PRINT 140 FOR I=1 TO 6:PRINT "PLAYER"I;:INPUT A$(I):NEXT I:PRINT 150 PRINT "WOULD THE " B$(7) " COACH DO THE SAME" 155 PRINT 160 FOR T=1 TO 6:PRINT "PLAYER"T;:INPUT B$(T):NEXT T:PRINT 170 PRINT "INPUT THE REFEREE FOR THIS GAME";:INPUT R$ 180 PRINT:PRINT TAB(10);A$(7) " STARTING LINEUP" 190 FOR T=1 TO 6:PRINT A$(T):NEXT T 200 PRINT:PRINT TAB(10);B$(7)" STARTING LINEUP" 210 FOR T=1 TO 6:PRINT B$(T):NEXT T:PRINT 220 PRINT "WE'RE READY FOR TONIGHTS OPENING FACE-OFF." 230 PRINT R$ " WILL DROP THE PUCK BETWEEN " A$(2) " AND " B$(2) 240 FOR L=1 TO T6:IF L=1 THEN 260 250 PRINT "AND WE'RE READY FOR THE FACE-OFF" 260 C=INT(2*RND(X))+1:ON C GOTO 270,280 270 PRINT A$(7) " HAS CONTROL OF THE PUCK":GOTO 290 280 PRINT B$(7) " HAS CONTROL." 290 PRINT "PASS";:INPUT P:FOR N=1 TO 3:H(N)=0:NEXT N 300 IF P<0 THEN 290 305 IF P>3 THEN 290 310 FOR J=1 TO (P+2) 320 H(J)=INT(5*RND(X))+1 330 NEXT J:IF H(J-1)=H(J-2) THEN 310 331 IF P+2<3 THEN 350 335 IF H(J-1)=H(J-3) THEN 310 340 IF H(J-2)=H(J-3) THEN 310 350 IF P=0 THEN 360 355 GOTO 490 360 INPUT "SHOT";S:IF S<1 THEN 360 365 IF S>4 THEN 360 370 ON C GOTO 380,480 380 PRINT A$(H(J-1));:G=H(J-1):G1=0:G2=0 390 ON S GOTO 400,420,440,460 400 PRINT " LET'S A BOOMER GO FROM THE RED LINE!!" 410 Z=10:GOTO 890 420 PRINT " FLIPS A WRISTSHOT DOWN THE ICE" 440 PRINT " BACKHANDS ONE IN ON THE GOALTENDER" 450 Z=25:GOTO 890 460 PRINT " SNAPS A LONG FLIP SHOT" 470 Z=17:GOTO 890 480 PRINT B$(H(J-1));:G1=0:G2=0:G=H(J-1):GOTO 390 490 ON C GOTO 500,640 500 ON P GOTO 510,540,570 510 PRINT A$(H(J-2)) " LEADS " A$(H(J-1)) " WITH A PERFECT PASS." 520 PRINT A$(H(J-1)) " CUTTING IN!!!" 530 G=H(J-1):G1=H(J-2):G2=0:Z1=3:GOTO 770 540 PRINT A$(H(J-2)) " GIVES TO A STREAKING " A$(H(J-1)) 550 PRINT A$(H(J-3)) " COMES DOWN ON " B$(5) " AND " B$(4) 560 G=H(J-3):G1=H(J-1):G2=H(J-2):Z1=2:GOTO 770 570 PRINT "OH MY GOD!! A ' 4 ON 2 ' SITUATION" 580 PRINT A$(H(J-3)) " LEADS " A$(H(J-2)) 590 PRINT A$(H(J-2)) " IS WHEEELING THROUGH CENTER." 600 PRINT A$(H(J-2)) " GIVES AND GOES WITH " A$(H(J-1)) 610 PRINT "PRETTY PASSING!" 620 PRINT A$(H(J-1)) " DROPS IT TO " A$(H(J-4)) 630 G=H(J-4):G1=H(J-1):G2=H(J-2):Z1=1:GOTO 770 640 ON P GOTO 650,670,720 650 PRINT B$(H(J-1)) " HITS " B$(H(J-2)) " FLYING DOWN THE LEFT SIDE" 660 G=H(J-2):G1=H(J-1):G2=0:Z1=3:GOTO 770 670 PRINT "IT'S A ' 3 ON 2 '!" 680 PRINT "ONLY " A$(4) " AND " A$(5) " ARE BACK." 690 PRINT B$(H(J-2)) " GIVES OFF TO " B$(H(J-1)) 700 PRINT B$(H(J-1)) " DROPS TO " B$(H(J-3)) 710 G=H(J-3):G1=H(J-1):G2=H(J-2):Z1=2:GOTO 770 720 PRINT " A ' 3 ON 2 ' WITH A ' TRAILER '!" 730 PRINT B$(H(J-4)) " GIVES TO " B$(H(J-2)) " WHO SHUFFLES IT OFF TO" 740 PRINT B$(H(J-1)) " WHO FIRES A WING TO WING PASS TO " 750 PRINT B$(H(J-3)) " AS HE CUTS IN ALONE!!" 760 G=H(J-3):G1=H(J-1):G2=H(J-2):Z1=1:GOTO 770 770 PRINT "SHOT";:INPUT S:IF S>4 THEN 770:IF S<1 THEN 770 780 ON C GOTO 790,880 790 PRINT A$(G);:ON S GOTO 800,820,840,860 800 PRINT " LET'S A BIG SLAP SHOT GO!!" 810 Z=4:Z=Z+Z1:GOTO 890 820 PRINT " RIPS A WRIST SHOT OFF" 830 Z=2:Z=Z+Z1:GOTO 890 840 PRINT " GETS A BACKHAND OFF" 850 Z=3:Z=Z+Z1:GOTO 890 860 PRINT " SNAPS OFF A SNAP SHOT" 870 Z=2:Z=Z+Z1:GOTO 890 880 PRINT B$(G);:ON S GOTO 800,820,840,860 890 PRINT "AREA";:INPUT A:IF A<1 THEN 890 895 IF A>4 THEN 890 900 ON C GOTO 910,920 910 S2=S2+1:GOTO 930 920 S3=S3+1 930 A1=INT(4*RND(X))+1:IF A<>A1 THEN 1200 940 H(20)=INT(100*RND(X))+1 950 IF INT(H(20)/Z)=H(20)/Z THEN 1160 960 ON C GOTO 970,980 970 PRINT "GOAL " A$(7):H(9)=H(9)+1:GOTO 990 980 PRINT "SCORE " B$(7):H(8)=H(8)+1 990 FOR B1=1 TO 25:PRINT CHR$(7);:NEXT B1:PRINT 1000 PRINT "SCORE: ";:IF H(8)>H(9) THEN 1020 1010 PRINT A$(7)":";H(9),B$(7)":";H(8):GOTO 1030 1020 PRINT B$(7)":";H(8),A$(7)":";H(9) 1030 ON C GOTO 1040,1100 1040 PRINT "GOAL SCORED BY: " A$(G):IF G1=0 THEN 1070 1050 IF G2=0 THEN 1080 1060 PRINT " ASSISTED BY: " A$(G1) " AND " A$(G2):GOTO 1090 1070 PRINT " UNASSISTED.":GOTO 1090 1080 PRINT " ASSISTED BY: " A$(G1) 1090 T(G)=T(G)+1:T1(G1)=T1(G1)+1:T1(G2)=T1(G2)+1:GOTO 1540 1100 PRINT "GOAL SCORED BY: " B$(G); 1110 IF G1=0 THEN 1130 1115 IF G2=0 THEN 1140 1120 PRINT " ASSISTED BY: " B$(G1) " AND " B$(G2):GOTO 1150 1130 PRINT " UNASSISTED":GOTO 1150 1140 PRINT " ASSISTED BY: " B$(G1):GOTO 1150 1150 T2(G)=T2(G)+1:T3(G1)=T3(G1)+1:T3(G2)=T3(G2)+1:GOTO 1540 1160 A2=INT(100*RND(X))+1:IF INT(A2/4)=A2/4 THEN 1170 1165 GOTO 1200 1170 ON C GOTO 1180,1190 1180 PRINT "SAVE " B$(6) " -- REBOUND":GOTO 940 1190 PRINT "SAVE " A$(6) " -- FOLLOW UP":GOTO 940 1200 S1=INT(6*RND(X))+1 1210 ON C GOTO 1220,1380 1220 ON S1 GOTO 1230,1260,1290,1300,1330,1350 1230 PRINT "KICK SAVE AND A BEAUTY BY " B$(6) 1240 PRINT "CLEARED OUT BY " B$(3) 1250 GOTO 260 1260 PRINT "WHAT A SPECTACULAR GLOVE SAVE BY " B$(6) 1270 PRINT "AND " B$(6) " GOLFS IT INTO THE CROWD" 1280 GOTO 1540 1290 PRINT "SKATE SAVE ON A LOW STEAMER BY " B$(6):GOTO 260 1300 PRINT "PAD SAVE BY " B$(6) " OFF THE STICK" 1310 PRINT "OF "A$(G) " AND " B$(6) " COVERS UP" 1320 GOTO 1540 1330 PRINT "WHISTLES ONE OVER THE HEAD OF " B$(6) 1340 GOTO 260 1350 PRINT B$(6) " MAKES A FACE SAVE!! AND HE IS HURT" 1360 PRINT "THE DEFENSEMAN " B$(5) " COVERS UP FOR HIM" 1370 GOTO 1540 1380 ON S1 GOTO 1390,1410,1440,1470,1490,1520 1390 PRINT "STICK SAVE BY " A$(6) 1400 PRINT "AND CLEARED OUT BY " A$(4):GOTO 260 1410 PRINT "OH MY GOD!! " B$(G) " RATTLES ONE OFF THE POST" 1420 PRINT "TO THE RIGHT OF " A$(6) " AND " A$(6) " COVERS "; 1430 PRINT "ON THE LOOSE PUCK!":GOTO 1540 1440 PRINT "SKATE SAVE BY " A$(6) 1450 PRINT A$(6) " WHACKS THE LOOSE PUCK INTO THE STANDS" 1460 GOTO 1540 1470 PRINT "STICK SAVE BY " A$(6) " AND HE CLEARS IT OUT HIMSELF" 1480 GOTO 260 1490 PRINT "KICKED OUT BY " A$(6) 1500 PRINT "AND IT REBOUNDS ALL THE WAY TO CENTER ICE" 1510 GOTO 260 1520 PRINT "GLOVE SAVE " A$(6) " AND HE HANGS ON" 1530 GOTO 1540 1540 NEXT L:FOR N=1 TO 30:PRINT CHR$(7);:NEXT N:PRINT "THAT'S THE SIREN" 1550 PRINT:PRINT TAB(15);"FINAL SCORE:" 1560 IF H(8)>H(9) THEN 1580 1570 PRINT A$(7)":";H(9),B$(7)":";H(8):GOTO 1590 1580 PRINT B$(7)":";H(8),A$(7)":";H(9) 1590 PRINT: PRINT TAB(10);"SCORING SUMMARY":PRINT 1600 PRINT TAB(25);A$(7) 1610 PRINT TAB(5);"NAME";TAB(20);"GOALS";TAB(35);"ASSISTS" 1620 PRINT TAB(5);"----";TAB(20);"-----";TAB(35);"-------" 1630 FOR I=1 TO 5:PRINT TAB(5);A$(I);TAB(21);T(I);TAB(36);T1(I) 1640 NEXT I:PRINT 1650 PRINT TAB(25);B$(7) 1660 PRINT TAB(5);"NAME";TAB(20);"GOALS";TAB(35);"ASSISTS" 1670 PRINT TAB(5);"----";TAB(20);"-----";TAB(35);"-------" 1680 FOR T=1 TO 5:PRINT TAB(5);B$(T);TAB(21);T2(T);TAB(36);T3(T) 1690 NEXT T:PRINT 1700 PRINT "SHOTS ON NET":PRINT A$(7)":";S2:PRINT B$(7)":";S3 1710 END 1720 PRINT: PRINT "THIS IS A SIMULATED HOCKEY GAME." 1730 PRINT "QUESTION RESPONSE" 1740 PRINT "PASS TYPE IN THE NUMBER OF PASSES YOU WOULD" 1750 PRINT " LIKE TO MAKE, FROM 0 TO 3." 1760 PRINT "SHOT TYPE THE NUMBER CORRESPONDING TO THE SHOT" 1770 PRINT " YOU WANT TO MAKE. ENTER:" 1780 PRINT " 1 FOR A SLAPSHOT" 1790 PRINT " 2 FOR A WRISTSHOT" 1800 PRINT " 3 FOR A BACKHAND" 1810 PRINT " 4 FOR A SNAP SHOT" 1820 PRINT "AREA TYPE IN THE NUMBER CORRESPONDING TO" 1830 PRINT " THE AREA YOU ARE AIMING AT. ENTER:" 1840 PRINT " 1 FOR UPPER LEFT HAND CORNER" 1850 PRINT " 2 FOR UPPER RIGHT HAND CORNER" 1860 PRINT " 3 FOR LOWER LEFT HAND CORNER" 1870 PRINT " 4 FOR LOWER RIGHT HAND CORNER" 1880 PRINT 1890 PRINT "AT THE START OF THE GAME, YOU WILL BE ASKED FOR THE NAMES" 1900 PRINT "OF YOUR PLAYERS. THEY ARE ENTERED IN THE ORDER: " 1910 PRINT "LEFT WING, CENTER, RIGHT WING, LEFT DEFENSE," 1920 PRINT "RIGHT DEFENSE, GOALKEEPER. ANY OTHER INPUT REQUIRED WILL" 1930 PRINT "HAVE EXPLANATORY INSTRUCTIONS." 1940 GOTO 90 1950 END ================================================ FILE: 00_Alternate_Languages/50_Horserace/MiniScript/README.md ================================================ 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. ================================================ FILE: 00_Alternate_Languages/50_Horserace/MiniScript/horserace.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/50_Horserace/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/50_Horserace/horserace.bas ================================================ 100 PRINT TAB(31);"HORSERACE" 110 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 120 PRINT:PRINT:PRINT 210 DIM S(8) 220 PRINT "WELCOME TO SOUTH PORTLAND HIGH RACETRACK" 230 PRINT " ...OWNED BY LAURIE CHEVALIER" 240 PRINT "DO YOU WANT DIRECTIONS"; 250 INPUT X$ 260 IF X$="NO" THEN 320 270 PRINT"UP TO 10 MAY PLAY. A TABLE OF ODDS WILL BE PRINTED. YOU" 280 PRINT"MAY BET ANY + AMOUNT UNDER 100000 ON ONE HORSE." 290 PRINT "DURING THE RACE, A HORSE WILL BE SHOWN BY ITS" 300 PRINT"NUMBER. THE HORSES RACE DOWN THE PAPER!" 310 PRINT 320 PRINT "HOW MANY WANT TO BET"; 330 INPUT C 340 PRINT "WHEN ? APPEARS,TYPE NAME" 350 FOR A=1 TO C 360 INPUT W$(A) 370 NEXT A 380 PRINT 390 PRINT"HORSE",,"NUMBER","ODDS" 400 PRINT 410 FOR I=1 TO 8: S(I)=0: NEXT I 420 LET R=0 430 FOR A=1 TO 8 440 LET D(A)=INT(10*RND(1)+1) 450 NEXT A 460 FOR A=1 TO 8 470 LET R=R+D(A) 480 NEXT A 490 LET V$(1)="JOE MAW" 500 LET V$(2)="L.B.J." 510 LET V$(3)="MR.WASHBURN" 520 LET V$(4)="MISS KAREN" 530 LET V$(5)="JOLLY" 540 LET V$(6)="HORSE" 550 LET V$(7)="JELLY DO NOT" 560 LET V$(8)="MIDNIGHT" 570 FOR N=1 TO 8 580 PRINT V$(N),,N,R/D(N);":1" 590 NEXT N 600 PRINT"--------------------------------------------------" 610 PRINT "PLACE YOUR BETS...HORSE # THEN AMOUNT" 620 FOR J=1 TO C 630 PRINT W$(J); 640 INPUT Q(J),P(J) 650 IF P(J)<1 THEN 670 660 IF P(J)<100000 THEN 690 670 PRINT" YOU CAN'T DO THAT!" 680 GOTO 630 690 NEXT J 700 PRINT 710 PRINT"1 2 3 4 5 6 7 8" 720 PRINT"XXXXSTARTXXXX" 730 FOR I=1 TO 8 740 LET M=I 750 LET M(I)=M 760 LET Y(M(I))=INT(100*RND(1)+1) 770 IF Y(M(I))<10 THEN 860 780 LET S=INT(R/D(I)+.5) 790 IF Y(M(I))<S+17 THEN 880 800 IF Y(M(I))<S+37 THEN 900 810 IF Y(M(I))<S+57 THEN 920 820 IF Y(M(I))<77+S THEN 940 830 IF Y(M(I))<S+92 THEN 960 840 LET Y(M(I))=7 850 GOTO 970 860 LET Y(M(I))=1 870 GOTO 970 880 LET Y(M(I))=2 890 GOTO 970 900 LET Y(M(I))=3 910 GOTO 970 920 LET Y(M(I))=4 930 GOTO 970 940 LET Y(M(I))=5 950 GOTO 970 960 LET Y(M(I))=6 970 NEXT I 980 LET M=I 990 FOR I=1 TO 8 1000 LET S(M(I))=S(M(I))+Y(M(I)) 1010 NEXT I 1020 LET I=1 1030 FOR L=1 TO 8 1040 FOR I=1 TO 8-L 1050 IF S(M(I))<S(M(I+1))THEN 1090 1060 LET H=M(I) 1070 LET M(I)=M(I+1) 1080 LET M(I+1)=H 1090 NEXT I 1100 NEXT L 1110 LET T=S(M(8)) 1120 FOR I=1 TO 8 1130 LET B=S(M(I))-S(M(I-1)) 1140 IF B=0 THEN 1190 1150 FOR A=1 TO B 1160 PRINT 1170 IF S(M(I))>27 THEN 1240 1180 NEXT A 1190 PRINT M(I); 1200 NEXT I 1210 FOR A=1 TO 28-T 1220 PRINT 1230 NEXT A 1240 PRINT "XXXXFINISHXXXX"; 1242 PRINT 1243 PRINT 1244 PRINT "---------------------------------------------" 1245 PRINT 1250 IF T<28 THEN 720 1270 PRINT "THE RACE RESULTS ARE:" 1272 LET Z9=1 1280 FOR I=8 TO 1 STEP-1 1290 LET F=M(I) 1300 PRINT 1310 PRINT Z9;"PLACE HORSE NO.";F,"AT ";R/D(F);":1" 1312 LET Z9=Z9+1 1320 NEXT I 1330 FOR J=1 TO C 1340 IF Q(J)<>M(8) THEN 1370 1350 LET N=Q(J) 1355 PRINT 1360 PRINT W$(J);" WINS $";(R/D(N))*P(J) 1370 NEXT J 1372 PRINT "DO YOU WANT TO BET ON THE NEXT RACE ?" 1374 INPUT "YES OR NO"; O$ 1376 IF O$="YES" THEN 380 1380 END ================================================ FILE: 00_Alternate_Languages/51_Hurkle/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/51_Hurkle/MiniScript/hurkle.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/51_Hurkle/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/51_Hurkle/hurkle.bas ================================================ 10 PRINT TAB(33);"HURKLE" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 30 PRINT:PRINT:PRINT 110 N=5 120 G=10 210 PRINT 220 PRINT "A HURKLE IS HIDING ON A";G;"BY";G;"GRID. HOMEBASE" 230 PRINT "ON THE GRID IS POINT 0,0 IN THE SOUTHWEST CORNER," 235 PRINT "AND ANY POINT ON THE GRID IS DESIGNATED BY A" 240 PRINT "PAIR OF WHOLE NUMBERS SEPERATED BY A COMMA. THE FIRST" 245 PRINT "NUMBER IS THE HORIZONTAL POSITION AND THE SECOND NUMBER" 246 PRINT "IS THE VERTICAL POSITION. YOU MUST TRY TO" 250 PRINT "GUESS THE HURKLE'S GRIDPOINT. YOU GET";N;"TRIES." 260 PRINT "AFTER EACH TRY, I WILL TELL YOU THE APPROXIMATE" 270 PRINT "DIRECTION TO GO TO LOOK FOR THE HURKLE." 280 PRINT 285 A=INT(G*RND(1)) 286 B=INT(G*RND(1)) 310 FOR K=1 TO N 320 PRINT "GUESS #";K; 330 INPUT X,Y 340 IF ABS(X-A)+ABS(Y-B)=0 THEN 500 350 REM PRINT INFO 360 GOSUB 610 370 PRINT 380 NEXT K 410 PRINT 420 PRINT "SORRY, THAT'S";N;"GUESSES." 430 PRINT "THE HURKLE IS AT ";A;",";B 440 PRINT 450 PRINT "LET'S PLAY AGAIN, HURKLE IS HIDING." 460 PRINT 470 GOTO 285 500 REM 510 PRINT 520 PRINT "YOU FOUND HIM IN";K;"GUESSES!" 540 GOTO 440 610 PRINT "GO "; 620 IF Y=B THEN 670 630 IF Y<B THEN 660 640 PRINT "SOUTH"; 650 GOTO 670 660 PRINT "NORTH"; 670 IF X=A THEN 720 680 IF X<A THEN 710 690 PRINT "WEST"; 700 GOTO 720 710 PRINT "EAST"; 720 PRINT 730 RETURN 999 END ================================================ FILE: 00_Alternate_Languages/52_Kinema/MiniScript/README.md ================================================ 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. ================================================ FILE: 00_Alternate_Languages/52_Kinema/MiniScript/kinema.ms ================================================ // 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 ================================================ FILE: 00_Alternate_Languages/52_Kinema/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/52_Kinema/kinema.bas ================================================ 10 PRINT TAB(33);"KINEMA" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 30 PRINT: PRINT: PRINT 100 PRINT 105 PRINT 106 Q=0 110 V=5+INT(35*RND(1)) 111 PRINT "A BALL IS THROWN UPWARDS AT";V;"METERS PER SECOND." 112 PRINT 115 A=.05*V^2 116 PRINT "HOW HIGH WILL IT GO (IN METERS)"; 117 GOSUB 500 120 A=V/5 122 PRINT "HOW LONG UNTIL IT RETURNS (IN SECONDS)"; 124 GOSUB 500 130 T=1+INT(2*V*RND(1))/10 132 A=V-10*T 134 PRINT "WHAT WILL ITS VELOCITY BE AFTER";T;"SECONDS"; 136 GOSUB 500 140 PRINT 150 PRINT Q;"RIGHT OUT OF 3."; 160 IF Q<2 THEN 100 170 PRINT " NOT BAD." 180 GOTO 100 500 INPUT G 502 IF ABS((G-A)/A)<.15 THEN 510 504 PRINT "NOT EVEN CLOSE...." 506 GOTO 512 510 PRINT "CLOSE ENOUGH." 511 Q=Q+1 512 PRINT "CORRECT ANSWER IS ";A 520 PRINT 530 RETURN 999 END ================================================ FILE: 00_Alternate_Languages/53_King/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/53_King/MiniScript/king.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/53_King/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/53_King/king.bas ================================================ 1 PRINT TAB(34);"KING" 2 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 3 PRINT:PRINT:PRINT 4 PRINT "DO YOU WANT INSTRUCTIONS"; 5 INPUT Z$ 6 N5=8 10 IF LEFT$(Z$,1)="N" THEN 47 11 IF Z$="AGAIN" THEN 1960 12 PRINT:PRINT:PRINT 20 PRINT "CONGRATULATIONS! YOU'VE JUST BEEN ELECTED PREMIER OF SETATS" 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" 30 PRINT "RALLODS PER YEAR TO SURVIVE. YOUR COUNTRY'S INCOME COMES" 32 PRINT "FROM FARM PRODUCE AND TOURISTS VISITING YOUR MAGNIFICENT" 34 PRINT "FORESTS, HUNTING, FISHING, ETC. HALF YOUR LAND IS FARM LAND" 36 PRINT "WHICH ALSO HAS AN EXCELLENT MINERAL CONTENT AND MAY BE SOLD" 38 PRINT "TO FOREIGN INDUSTRY (STRIP MINING) WHO IMPORT AND SUPPORT" 40 PRINT "THEIR OWN WORKERS. CROPS COST BETWEEN 10 AND 15 RALLODS PER" 42 PRINT "SQUARE MILE TO PLANT." 44 PRINT "YOUR GOAL IS TO COMPLETE YOUR";N5;"YEAR TERM OF OFFICE." 46 PRINT "GOOD LUCK!" 47 PRINT 50 A=INT(60000+(1000*RND(1))-(1000*RND(1))) 55 B=INT(500+(10*RND(1))-(10*RND(1))) 65 D=2000 100 W=INT(10*RND(1)+95) 102 PRINT 105 PRINT "YOU NOW HAVE ";A;" RALLODS IN THE TREASURY." 110 PRINT INT(B);:PRINT "COUNTRYMEN, "; 115 V9=INT(((RND(1)/2)*10+10)) 120 IF C=0 THEN 140 130 PRINT INT(C);"FOREIGN WORKERS, "; 140 PRINT "AND";INT(D);"SQ. MILES OF LAND." 150 PRINT "THIS YEAR INDUSTRY WILL BUY LAND FOR";W; 152 PRINT "RALLODS PER SQUARE MILE." 155 PRINT "LAND CURRENTLY COSTS";V9;"RALLODS PER SQUARE MILE TO PLANT." 162 PRINT 200 PRINT "HOW MANY SQUARE MILES DO YOU WISH TO SELL TO INDUSTRY"; 210 INPUT H 215 IF H<0 THEN 200 220 IF H<=D-1000 THEN 300 230 PRINT "*** THINK AGAIN. YOU ONLY HAVE";D-1000;"SQUARE MILES OF FARM LAND." 240 IF X<>0 THEN 200 250 PRINT:PRINT "(FOREIGN INDUSTRY WILL ONLY BUY FARM LAND BECAUSE" 260 PRINT "FOREST LAND IS UNECONOMICAL TO STRIP MINE DUE TO TREES," 270 PRINT "THICKER TOP SOIL, ETC.)" 280 X=1 299 GOTO 200 300 D=INT(D-H) 310 A=INT(A+(H*W)) 320 PRINT "HOW MANY RALLODS WILL YOU DISTRIBUTE AMONG YOUR COUNTRYMEN"; 340 INPUT I 342 IF I<0 THEN 320 350 IF I<A THEN 400 360 IF I=A THEN 380 370 PRINT " THINK AGAIN. YOU'VE ONLY";A;" RALLODS IN THE TREASURY" 375 GOTO 320 380 J=0 390 K=0 395 A=0 399 GOTO 1000 400 A=INT(A-I) 410 PRINT "HOW MANY SQUARE MILES DO YOU WISH TO PLANT"; 420 INPUT J 421 IF J<0 THEN 410 422 IF J<=B*2 THEN 426 423 PRINT " SORRY, BUT EACH COUNTRYMAN CAN ONLY PLANT 2 SQ. MILES." 424 GOTO 410 426 IF J<=D-1000 THEN 430 427 PRINT " SORRY, BUT YOU'VE ONLY";D-1000;"SQ. MILES OF FARM LAND." 428 GOTO 410 430 U1=INT(J*V9) 435 IF U1<A THEN 500 440 IF U1=A THEN 490 450 PRINT " THINK AGAIN. YOU'VE ONLY";A;" RALLODS LEFT IN THE TREASURY." 460 GOTO 410 490 K=0 495 A=0 499 GOTO 1000 500 A=A-U1 510 PRINT "HOW MANY RALLODS DO YOU WISH TO SPEND ON POLLUTION CONTROL"; 520 INPUT K 522 IF K<0 THEN 510 530 IF K<=A THEN 1000 540 PRINT " THINK AGAIN. YOU ONLY HAVE ";A;" RALLODS REMAINING." 550 GOTO 510 600 IF H<>0 THEN 1002 602 IF I<>0 THEN 1002 604 IF J<>0 THEN 1002 606 IF K<>0 THEN 1002 609 PRINT 612 PRINT "GOODBYE." 614 PRINT "(IF YOU WISH TO CONTINUE THIS GAME AT A LATER DATE, ANSWER" 616 PRINT "'AGAIN' WHEN ASKED IF YOU WANT INSTRUCTIONS AT THE START" 617 PRINT "OF THE GAME)." 618 STOP 1000 GOTO 600 1002 PRINT 1003 PRINT 1010 A=INT(A-K) 1020 A4=A 1100 IF INT(I/100-B)>=0 THEN 1120 1105 IF I/100<50 THEN 1700 1110 PRINT INT(B-(I/100));"COUNTRYMEN DIED OF STARVATION" 1120 F1=INT(RND(1)*(2000-D)) 1122 IF K<25 THEN 1130 1125 F1=INT(F1/(K/25)) 1130 IF F1<=0 THEN 1150 1140 PRINT F1;"COUNTRYMEN DIED OF CARBON-MONOXIDE AND DUST INHALATION" 1150 IF INT((I/100)-B)<0 THEN 1170 1160 IF F1>0 THEN 1180 1165 GOTO 1200 1170 PRINT " YOU WERE FORCED TO SPEND";INT((F1+(B-(I/100)))*9); 1172 PRINT "RALLODS ON FUNERAL EXPENSES" 1174 B5=INT(F1+(B-(I/100))) 1175 A=INT(A-((F1+(B-(I/100)))*9)) 1176 GOTO 1185 1180 PRINT " YOU WERE FORCED TO SPEND ";INT(F1*9);"RALLODS ON "; 1181 PRINT "FUNERAL EXPENSES." 1182 B5=F1 1183 A=INT(A-(F1*9)) 1185 IF A>=0 THEN 1194 1187 PRINT " INSUFFICIENT RESERVES TO COVER COST - LAND WAS SOLD" 1189 D=INT(D+(A/W)) 1190 A=0 1194 B=INT(B-B5) 1200 IF H=0 THEN 1250 1220 C1=INT(H+(RND(1)*10)-(RND(1)*20)) 1224 IF C>0 THEN 1230 1226 C1=C1+20 1230 PRINT C1;"WORKERS CAME TO THE COUNTRY AND"; 1250 P1=INT(((I/100-B)/10)+(K/25)-((2000-D)/50)-(F1/2)) 1255 PRINT ABS(P1);"COUNTRYMEN "; 1260 IF P1<0 THEN 1275 1265 PRINT "CAME TO"; 1270 GOTO 1280 1275 PRINT "LEFT"; 1280 PRINT " THE ISLAND." 1290 B=INT(B+P1) 1292 C=INT(C+C1) 1305 U2=INT(((2000-D)*((RND(1)+1.5)/2))) 1310 IF C=0 THEN 1324 1320 PRINT "OF ";INT(J);"SQ. MILES PLANTED,"; 1324 IF J>U2 THEN 1330 1326 U2=J 1330 PRINT " YOU HARVESTED ";INT(J-U2);"SQ. MILES OF CROPS." 1340 IF U2=0 THEN 1370 1344 IF T1>=2 THEN 1370 1350 PRINT " (DUE TO "; 1355 IF T1=0 THEN 1365 1360 PRINT "INCREASED "; 1365 PRINT "AIR AND WATER POLLUTION FROM FOREIGN INDUSTRY.)" 1370 Q=INT((J-U2)*(W/2)) 1380 PRINT "MAKING";INT(Q);"RALLODS." 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." 1420 IF V2=0 THEN 1450 1425 IF V1-V2>=V3 THEN 1450 1430 PRINT " DECREASE BECAUSE "; 1435 G1=10*RND(1) 1440 IF G1<=2 THEN 1460 1442 IF G1<=4 THEN 1465 1444 IF G1<=6 THEN 1470 1446 IF G1<=8 THEN 1475 1448 IF G1<=10 THEN 1480 1450 V3=INT(A+V3) 1451 A=INT(A+V3) 1452 GOTO 1500 1460 PRINT "FISH POPULATION HAS DWINDLED DUE TO WATER POLLUTION." 1462 GOTO 1450 1465 PRINT "AIR POLLUTION IS KILLING GAME BIRD POPULATION." 1467 GOTO 1450 1470 PRINT "MINERAL BATHS ARE BEING RUINED BY WATER POLLUTION." 1472 GOTO 1450 1475 PRINT "UNPLEASANT SMOG IS DISCOURAGING SUN BATHERS." 1477 GOTO 1450 1480 PRINT "HOTELS ARE LOOKING SHABBY DUE TO SMOG GRIT." 1482 GOTO 1450 1500 IF B5>200 THEN 1600 1505 IF B<343 THEN 1700 1510 IF (A4/100)>5 THEN 1800 1515 IF C>B THEN 1550 1520 IF N5-1=X5 THEN 1900 1545 GOTO 2000 1550 PRINT 1552 PRINT 1560 PRINT "THE NUMBER OF FOREIGN WORKERS HAS EXCEEDED THE NUMBER" 1562 PRINT "OF COUNTRYMEN. AS A MINORITY, THEY HAVE REVOLTED AND" 1564 PRINT "TAKEN OVER THE COUNTRY." 1570 IF RND(1)<=.5 THEN 1580 1574 PRINT "YOU HAVE BEEN THROWN OUT OF OFFICE AND ARE NOW" 1576 PRINT "RESIDING IN PRISON." 1578 GOTO 1590 1580 PRINT "YOU HAVE BEEN ASSASSINATED." 1590 PRINT 1592 PRINT 1596 STOP 1600 PRINT 1602 PRINT 1610 PRINT B5;"COUNTRYMEN DIED IN ONE YEAR!!!!!" 1615 PRINT "DUE TO THIS EXTREME MISMANAGEMENT, YOU HAVE NOT ONLY" 1620 PRINT "BEEN IMPEACHED AND THROWN OUT OF OFFICE, BUT YOU" 1622 M6=INT(RND(1)*10) 1625 IF M6<=3 THEN 1670 1630 IF M6<=6 THEN 1680 1635 IF M6<=10 THEN 1690 1670 PRINT "ALSO HAD YOUR LEFT EYE GOUGED OUT!" 1672 GOTO 1590 1680 PRINT "HAVE ALSO GAINED A VERY BAD REPUTATION." 1682 GOTO 1590 1690 PRINT "HAVE ALSO BEEN DECLARED NATIONAL FINK." 1692 GOTO 1590 1700 PRINT 1702 PRINT 1710 PRINT "OVER ONE THIRD OF THE POPULTATION HAS DIED SINCE YOU" 1715 PRINT "WERE ELECTED TO OFFICE. THE PEOPLE (REMAINING)" 1720 PRINT "HATE YOUR GUTS." 1730 GOTO 1570 1800 IF B5-F1<2 THEN 1515 1807 PRINT 1815 PRINT "MONEY WAS LEFT OVER IN THE TREASURY WHICH YOU DID" 1820 PRINT "NOT SPEND. AS A RESULT, SOME OF YOUR COUNTRYMEN DIED" 1825 PRINT "OF STARVATION. THE PUBLIC IS ENRAGED AND YOU HAVE" 1830 PRINT "BEEN FORCED TO EITHER RESIGN OR COMMIT SUICIDE." 1835 PRINT "THE CHOICE IS YOURS." 1840 PRINT "IF YOU CHOOSE THE LATTER, PLEASE TURN OFF YOUR COMPUTER" 1845 PRINT "BEFORE PROCEEDING." 1850 GOTO 1590 1900 PRINT 1902 PRINT 1920 PRINT "CONGRATULATIONS!!!!!!!!!!!!!!!!!!" 1925 PRINT "YOU HAVE SUCCESFULLY COMPLETED YOUR";N5;"YEAR TERM" 1930 PRINT "OF OFFICE. YOU WERE, OF COURSE, EXTREMELY LUCKY, BUT" 1935 PRINT "NEVERTHELESS, IT'S QUITE AN ACHIEVEMENT. GOODBYE AND GOOD" 1940 PRINT "LUCK - YOU'LL PROBABLY NEED IT IF YOU'RE THE TYPE THAT" 1945 PRINT "PLAYS THIS GAME." 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 1965 PRINT " COME ON, YOUR TERM IN OFFICE IS ONLY";N5;"YEARS." 1967 GOTO 1960 1969 PRINT "HOW MUCH DID YOU HAVE IN THE TREASURY"; 1970 INPUT A 1971 IF A<0 THEN 1590 1975 PRINT "HOW MANY COUNTRYMEN"; 1976 INPUT B 1977 IF B<0 THEN 1590 1980 PRINT "HOW MANY WORKERS"; 1981 INPUT C 1982 IF C<0 THEN 1590 1990 PRINT "HOW MANY SQUARE MILES OF LAND"; 1991 INPUT D 1992 IF D<0 THEN 1590 1993 IF D>2000 THEN 1996 1994 IF D>1000 THEN 100 1996 PRINT " COME ON, YOU STARTED WITH 1000 SQ. MILES OF FARM LAND" 1997 PRINT " AND 10,000 SQ. MILES OF FOREST LAND." 1998 GOTO 1990 2000 X5=X5+1 2020 B5=0 2040 GOTO 100 2046 END ================================================ FILE: 00_Alternate_Languages/53_King/king_variable_update.bas ================================================ 1 PRINT TAB(34);"KING" 2 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 3 PRINT:PRINT:PRINT 4 PRINT "DO YOU WANT INSTRUCTIONS"; 5 INPUT Z$ 6 YEARS_REQUIRED=8 10 IF LEFT$(Z$,1)="N" THEN 47 11 IF Z$="AGAIN" THEN 1960 12 PRINT:PRINT:PRINT 20 PRINT "CONGRATULATIONS! YOU'VE JUST BEEN ELECTED PREMIER OF SETATS" 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" 30 PRINT "RALLODS PER YEAR TO SURVIVE. YOUR COUNTRY'S INCOME COMES" 32 PRINT "FROM FARM PRODUCE AND TOURISTS VISITING YOUR MAGNIFICENT" 34 PRINT "FORESTS, HUNTING, FISHING, ETC. HALF YOUR LAND IS FARM LAND" 36 PRINT "WHICH ALSO HAS AN EXCELLENT MINERAL CONTENT AND MAY BE SOLD" 38 PRINT "TO FOREIGN INDUSTRY (STRIP MINING) WHO IMPORT AND SUPPORT" 40 PRINT "THEIR OWN WORKERS. CROPS COST BETWEEN 10 AND 15 RALLODS PER" 42 PRINT "SQUARE MILE TO PLANT." 44 PRINT "YOUR GOAL IS TO COMPLETE YOUR";YEARS_REQUIRED;"YEAR TERM OF OFFICE." 46 PRINT "GOOD LUCK!" 47 PRINT 50 RALLODS=INT(60000+(1000*RND(1))-(1000*RND(1))) 55 COUNTRYMEN=INT(500+(10*RND(1))-(10*RND(1))) 65 LANDAREA=2000 100 LANDPRICE=INT(10*RND(1)+95) 102 PRINT 105 PRINT "YOU NOW HAVE ";RALLODS;" RALLODS IN THE TREASURY." 110 PRINT INT(COUNTRYMEN);:PRINT "COUNTRYMEN, "; 115 COST_TO_PLANT=INT(((RND(1)/2)*10+10)) 120 IF FOREIGN_WORKERS=0 THEN 140 130 PRINT INT(FOREIGN_WORKERS);"FOREIGN WORKERS, "; 140 PRINT "AND";INT(LANDAREA);"SQ. MILES OF LAND." 150 PRINT "THIS YEAR INDUSTRY WILL BUY LAND FOR";LANDPRICE; 152 PRINT "RALLODS PER SQUARE MILE." 155 PRINT "LAND CURRENTLY COSTS";COST_TO_PLANT;"RALLODS PER SQUARE MILE TO PLANT." 162 PRINT 200 PRINT "HOW MANY SQUARE MILES DO YOU WISH TO SELL TO INDUSTRY"; 210 INPUT SELL_TO_INDUSTRY 215 IF SELL_TO_INDUSTRY<0 THEN 200 220 IF SELL_TO_INDUSTRY<=LANDAREA-1000 THEN 300 230 PRINT "*** THINK AGAIN. YOU ONLY HAVE";LANDAREA-1000;"SQUARE MILES OF FARM LAND." 240 IF EXPLANATION_GIVEN THEN 200 250 PRINT:PRINT "(FOREIGN INDUSTRY WILL ONLY BUY FARM LAND BECAUSE" 260 PRINT "FOREST LAND IS UNECONOMICAL TO STRIP MINE DUE TO TREES," 270 PRINT "THICKER TOP SOIL, ETC.)" 280 EXPLANATION_GIVEN=TRUE 299 GOTO 200 300 LANDAREA=INT(LANDAREA-SELL_TO_INDUSTRY) 310 RALLODS=INT(RALLODS+(SELL_TO_INDUSTRY*LANDPRICE)) 320 PRINT "HOW MANY RALLODS WILL YOU DISTRIBUTE AMONG YOUR COUNTRYMEN"; 340 INPUT WELFARE 342 IF WELFARE<0 THEN 320 350 IF WELFARE<RALLODS THEN 400 360 IF WELFARE=RALLODS THEN 380 370 PRINT " THINK AGAIN. YOU'VE ONLY";RALLODS;" RALLODS IN THE TREASURY" 375 GOTO 320 380 PLANTING_AREA=0 390 MONEY_SPENT_ON_POLLUTION_CONTROL=0 395 RALLODS=0 399 GOTO 1000 400 RALLODS=INT(RALLODS-WELFARE) 410 PRINT "HOW MANY SQUARE MILES DO YOU WISH TO PLANT"; 420 INPUT PLANTING_AREA 421 IF PLANTING_AREA<0 THEN 410 422 IF PLANTING_AREA<=COUNTRYMEN*2 THEN 426 423 PRINT " SORRY, BUT EACH COUNTRYMAN CAN ONLY PLANT 2 SQ. MILES." 424 GOTO 410 426 IF PLANTING_AREA<=LANDAREA-1000 THEN 430 427 PRINT " SORRY, BUT YOU'VE ONLY";LANDAREA-1000;"SQ. MILES OF FARM LAND." 428 GOTO 410 430 MONEY_SPENT_ON_PLANTING=INT(PLANTING_AREA*COST_TO_PLANT) 435 IF MONEY_SPENT_ON_PLANTING<RALLODS THEN 500 440 IF MONEY_SPENT_ON_PLANTING=RALLODS THEN 490 450 PRINT " THINK AGAIN. YOU'VE ONLY";RALLODS;" RALLODS LEFT IN THE TREASURY." 460 GOTO 410 490 MONEY_SPENT_ON_POLLUTION_CONTROL=0 495 RALLODS=0 499 GOTO 1000 500 RALLODS=RALLODS-MONEY_SPENT_ON_PLANTING 510 PRINT "HOW MANY RALLODS DO YOU WISH TO SPEND ON POLLUTION CONTROL"; 520 INPUT MONEY_SPENT_ON_POLLUTION_CONTROL 522 IF MONEY_SPENT_ON_POLLUTION_CONTROL<0 THEN 510 530 IF MONEY_SPENT_ON_POLLUTION_CONTROL<=RALLODS THEN 1000 540 PRINT " THINK AGAIN. YOU ONLY HAVE ";RALLODS;" RALLODS REMAINING." 550 GOTO 510 600 IF SELL_TO_INDUSTRY<>0 THEN 1002 602 IF WELFARE<>0 THEN 1002 604 IF PLANTING_AREA<>0 THEN 1002 606 IF MONEY_SPENT_ON_POLLUTION_CONTROL<>0 THEN 1002 609 PRINT 612 PRINT "GOODBYE." 614 PRINT "(IF YOU WISH TO CONTINUE THIS GAME AT A LATER DATE, ANSWER" 616 PRINT "'AGAIN' WHEN ASKED IF YOU WANT INSTRUCTIONS AT THE START" 617 PRINT "OF THE GAME)." 618 STOP 1000 GOTO 600 1002 PRINT 1003 PRINT 1010 RALLODS=INT(RALLODS-MONEY_SPENT_ON_POLLUTION_CONTROL) 1020 ORIGINAL_RALLODS=RALLODS 1100 IF INT(WELFARE/100-COUNTRYMEN)>=0 THEN 1120 1105 IF WELFARE/100<50 THEN 1700 1110 PRINT INT(COUNTRYMEN-(WELFARE/100));"COUNTRYMEN DIED OF STARVATION" 1120 POLLUTION_DEATHS=INT(RND(1)*(2000-LANDAREA)) 1122 IF MONEY_SPENT_ON_POLLUTION_CONTROL<25 THEN 1130 1125 POLLUTION_DEATHS=INT(POLLUTION_DEATHS/(MONEY_SPENT_ON_POLLUTION_CONTROL/25)) 1130 IF POLLUTION_DEATHS<=0 THEN 1150 1140 PRINT POLLUTION_DEATHS;"COUNTRYMEN DIED OF CARBON-MONOXIDE AND DUST INHALATION" 1150 IF INT((WELFARE/100)-COUNTRYMEN)<0 THEN 1170 1160 IF POLLUTION_DEATHS>0 THEN 1180 1165 GOTO 1200 1170 PRINT " YOU WERE FORCED TO SPEND";INT((POLLUTION_DEATHS+(COUNTRYMEN-(WELFARE/100)))*9); 1172 PRINT "RALLODS ON FUNERAL EXPENSES" 1174 DEATHS=INT(POLLUTION_DEATHS+(COUNTRYMEN-(WELFARE/100))) 1175 RALLODS=INT(RALLODS-((POLLUTION_DEATHS+(COUNTRYMEN-(WELFARE/100)))*9)) 1176 GOTO 1185 1180 PRINT " YOU WERE FORCED TO SPEND ";INT(POLLUTION_DEATHS*9);"RALLODS ON "; 1181 PRINT "FUNERAL EXPENSES." 1182 DEATHS=POLLUTION_DEATHS 1183 RALLODS=INT(RALLODS-(POLLUTION_DEATHS*9)) 1185 IF RALLODS>=0 THEN 1194 1187 PRINT " INSUFFICIENT RESERVES TO COVER COST - LAND WAS SOLD" 1189 LANDAREA=INT(LANDAREA+(RALLODS/LANDPRICE)) 1190 RALLODS=0 1194 COUNTRYMEN=INT(COUNTRYMEN-DEATHS) 1200 IF SELL_TO_INDUSTRY=0 THEN 1250 1220 NEW_FOREIGNERS=INT(SELL_TO_INDUSTRY+(RND(1)*10)-(RND(1)*20)) 1224 IF FOREIGN_WORKERS>0 THEN 1230 1226 NEW_FOREIGNERS=NEW_FOREIGNERS+20 1230 PRINT NEW_FOREIGNERS;"WORKERS CAME TO THE COUNTRY AND"; 1250 IMMIGRATION=INT(((WELFARE/100-COUNTRYMEN)/10)+(MONEY_SPENT_ON_POLLUTION_CONTROL/25)-((2000-LANDAREA)/50)-(POLLUTION_DEATHS/2)) 1255 PRINT ABS(IMMIGRATION);"COUNTRYMEN "; 1260 IF IMMIGRATION<0 THEN 1275 1265 PRINT "CAME TO"; 1270 GOTO 1280 1275 PRINT "LEFT"; 1280 PRINT " THE ISLAND." 1290 COUNTRYMEN=INT(COUNTRYMEN+IMMIGRATION) 1292 FOREIGN_WORKERS=INT(FOREIGN_WORKERS+NEW_FOREIGNERS) 1305 CROP_LOSS=INT(((2000-LANDAREA)*((RND(1)+1.5)/2))) 1310 IF FOREIGN_WORKERS=0 THEN 1324 1320 PRINT "OF ";INT(PLANTING_AREA);"SQ. MILES PLANTED,"; 1324 IF PLANTING_AREA>CROP_LOSS THEN 1330 1326 CROP_LOSS=PLANTING_AREA 1330 PRINT " YOU HARVESTED ";INT(PLANTING_AREA-CROP_LOSS);"SQ. MILES OF CROPS." 1340 IF CROP_LOSS=0 THEN 1370 1344 IF T1>=2 THEN 1370 1350 PRINT " (DUE TO "; 1355 IF T1=0 THEN 1365 1360 PRINT "INCREASED "; 1365 PRINT "AIR AND WATER POLLUTION FROM FOREIGN INDUSTRY.)" 1370 AGRICULTURAL_INCOME=INT((PLANTING_AREA-CROP_LOSS)*(LANDPRICE/2)) 1380 PRINT "MAKING";INT(AGRICULTURAL_INCOME);"RALLODS." 1390 RALLODS=INT(RALLODS+AGRICULTURAL_INCOME) REM I think tourism calculations are actually wrong in the original code! 1400 V1=INT(((COUNTRYMEN-IMMIGRATION)*22)+(RND(1)*500)) 1405 V2=INT((2000-LANDAREA)*15) 1410 PRINT " YOU MADE";ABS(INT(V1-V2));"RALLODS FROM TOURIST TRADE." 1420 IF V2=0 THEN 1450 1425 IF V1-V2>=V3 THEN 1450 1430 PRINT " DECREASE BECAUSE "; 1435 G1=10*RND(1) 1440 IF G1<=2 THEN 1460 1442 IF G1<=4 THEN 1465 1444 IF G1<=6 THEN 1470 1446 IF G1<=8 THEN 1475 1448 IF G1<=10 THEN 1480 1450 V3=INT(RALLODS+V3) 1451 RALLODS=INT(RALLODS+V3) 1452 GOTO 1500 1460 PRINT "FISH POPULATION HAS DWINDLED DUE TO WATER POLLUTION." 1462 GOTO 1450 1465 PRINT "AIR POLLUTION IS KILLING GAME BIRD POPULATION." 1467 GOTO 1450 1470 PRINT "MINERAL BATHS ARE BEING RUINED BY WATER POLLUTION." 1472 GOTO 1450 1475 PRINT "UNPLEASANT SMOG IS DISCOURAGING SUN BATHERS." 1477 GOTO 1450 1480 PRINT "HOTELS ARE LOOKING SHABBY DUE TO SMOG GRIT." 1482 GOTO 1450 1500 IF DEATHS>200 THEN 1600 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=YEAR THEN 1900 1545 GOTO 2000 1550 PRINT 1552 PRINT 1560 PRINT "THE NUMBER OF FOREIGN WORKERS HAS EXCEEDED THE NUMBER" 1562 PRINT "OF COUNTRYMEN. AS A MINORITY, THEY HAVE REVOLTED AND" 1564 PRINT "TAKEN OVER THE COUNTRY." 1570 IF RND(1)<=.5 THEN 1580 1574 PRINT "YOU HAVE BEEN THROWN OUT OF OFFICE AND ARE NOW" 1576 PRINT "RESIDING IN PRISON." 1578 GOTO 1590 1580 PRINT "YOU HAVE BEEN ASSASSINATED." 1590 PRINT 1592 PRINT 1596 STOP 1600 PRINT 1602 PRINT 1610 PRINT DEATHS;"COUNTRYMEN DIED IN ONE YEAR!!!!!" 1615 PRINT "DUE TO THIS EXTREME MISMANAGEMENT, YOU HAVE NOT ONLY" 1620 PRINT "BEEN IMPEACHED AND THROWN OUT OF OFFICE, BUT YOU" 1622 M6=INT(RND(1)*10) 1625 IF M6<=3 THEN 1670 1630 IF M6<=6 THEN 1680 1635 IF M6<=10 THEN 1690 1670 PRINT "ALSO HAD YOUR LEFT EYE GOUGED OUT!" 1672 GOTO 1590 1680 PRINT "HAVE ALSO GAINED A VERY BAD REPUTATION." 1682 GOTO 1590 1690 PRINT "HAVE ALSO BEEN DECLARED NATIONAL FINK." 1692 GOTO 1590 1700 PRINT 1702 PRINT 1710 PRINT "OVER ONE THIRD OF THE POPULTATION HAS DIED SINCE YOU" 1715 PRINT "WERE ELECTED TO OFFICE. THE PEOPLE (REMAINING)" 1720 PRINT "HATE YOUR GUTS." 1730 GOTO 1570 1800 IF DEATHS-POLLUTION_DEATHS<2 THEN 1515 1807 PRINT 1815 PRINT "MONEY WAS LEFT OVER IN THE TREASURY WHICH YOU DID" 1820 PRINT "NOT SPEND. AS A RESULT, SOME OF YOUR COUNTRYMEN DIED" 1825 PRINT "OF STARVATION. THE PUBLIC IS ENRAGED AND YOU HAVE" 1830 PRINT "BEEN FORCED TO EITHER RESIGN OR COMMIT SUICIDE." 1835 PRINT "THE CHOICE IS YOURS." 1840 PRINT "IF YOU CHOOSE THE LATTER, PLEASE TURN OFF YOUR COMPUTER" 1845 PRINT "BEFORE PROCEEDING." 1850 GOTO 1590 1900 PRINT 1902 PRINT 1920 PRINT "CONGRATULATIONS!!!!!!!!!!!!!!!!!!" 1925 PRINT "YOU HAVE SUCCESFULLY COMPLETED YOUR";YEARS_REQUIRED;"YEAR TERM" 1930 PRINT "OF OFFICE. YOU WERE, OF COURSE, EXTREMELY LUCKY, BUT" 1935 PRINT "NEVERTHELESS, IT'S QUITE AN ACHIEVEMENT. GOODBYE AND GOOD" 1940 PRINT "LUCK - YOU'LL PROBABLY NEED IT IF YOU'RE THE TYPE THAT" 1945 PRINT "PLAYS THIS GAME." 1950 GOTO 1590 1960 PRINT "HOW MANY YEARS HAD YOU BEEN IN OFFICE WHEN INTERRUPTED"; 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"; 1970 INPUT RALLODS 1971 IF RALLODS<0 THEN 1590 1975 PRINT "HOW MANY COUNTRYMEN"; 1976 INPUT COUNTRYMEN 1977 IF COUNTRYMEN<0 THEN 1590 1980 PRINT "HOW MANY WORKERS"; 1981 INPUT FOREIGN_WORKERS 1982 IF FOREIGN_WORKERS<0 THEN 1590 1990 PRINT "HOW MANY SQUARE MILES OF LAND"; 1991 INPUT LANDAREA 1992 IF LANDAREA<0 THEN 1590 1993 IF LANDAREA>2000 THEN 1996 1994 IF LANDAREA>1000 THEN 100 1996 PRINT " COME ON, YOU STARTED WITH 1000 SQ. MILES OF FARM LAND" 1997 PRINT " AND 10,000 SQ. MILES OF FOREST LAND." 1998 GOTO 1990 2000 YEAR=YEAR+1 2020 DEATHS=0 2040 GOTO 100 2046 END ================================================ FILE: 00_Alternate_Languages/54_Letter/MiniScript/README.md ================================================ 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. ================================================ FILE: 00_Alternate_Languages/54_Letter/MiniScript/letter.ms ================================================ // 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 ================================================ FILE: 00_Alternate_Languages/54_Letter/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/54_Letter/letter.bas ================================================ 10 PRINT TAB(33);"LETTER" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 30 PRINT:PRINT:PRINT 100 PRINT "LETTER GUESSING GAME": PRINT 210 PRINT "I'LL THINK OF A LETTER OF THE ALPHABET, A TO Z." 220 PRINT "TRY TO GUESS MY LETTER AND I'LL GIVE YOU CLUES" 230 PRINT "AS TO HOW CLOSE YOU'RE GETTING TO MY LETTER." 310 L=65+INT(RND(1)*26) 320 G=0 340 PRINT: PRINT "O.K., I HAVE A LETTER. START GUESSING." 410 PRINT: PRINT "WHAT IS YOUR GUESS"; 420 G=G+1 430 INPUT A$: A=ASC(A$): PRINT 440 IF A=L THEN 500 450 IF A>L THEN 480 460 PRINT "TOO LOW. TRY A HIGHER LETTER.": GOTO 410 480 PRINT "TOO HIGH. TRY A LOWER LETTER.": GOTO 410 500 PRINT: PRINT "YOU GOT IT IN";G;"GUESSES!!" 504 IF G<=5 THEN 508 506 PRINT "BUT IT SHOULDN'T TAKE MORE THAN 5 GUESSES!": GOTO 515 508 PRINT "GOOD JOB !!!!!" 510 FOR N=1 TO 15: PRINT CHR$(7);: NEXT N 515 PRINT 520 PRINT "LET'S PLAY AGAIN....." 530 GOTO 310 999 END ================================================ FILE: 00_Alternate_Languages/55_Life/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/55_Life/MiniScript/life.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/55_Life/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/55_Life/life.bas ================================================ 2 PRINT TAB(34);"LIFE" 4 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 6 PRINT: PRINT: PRINT 8 PRINT "ENTER YOUR PATTERN:" 9 X1=1: Y1=1: X2=24: Y2=70 10 DIM A(24,70),B$(24) 20 C=1 30 INPUT B$(C) 40 IF B$(C)="DONE" THEN B$(C)="": GOTO 80 50 IF LEFT$(B$(C),1)="." THEN B$(C)=" "+RIGHT$(B$(C),LEN(B$(C))-1) 60 C=C+1 70 GOTO 30 80 C=C-1: L=0 90 FOR X=1 TO C-1 100 IF LEN(B$(X))>L THEN L=LEN(B$(X)) 110 NEXT X 120 X1=11-C/2 130 Y1=33-L/2 140 FOR X=1 TO C 150 FOR Y=1 TO LEN(B$(X)) 160 IF MID$(B$(X),Y,1)<>" " THEN A(X1+X,Y1+Y)=1:P=P+1 170 NEXT Y 180 NEXT X 200 PRINT:PRINT:PRINT 210 PRINT "GENERATION:";G,"POPULATION:";P;: IF I9 THEN PRINT "INVALID!"; 215 X3=24:Y3=70:X4=1: Y4=1: P=0 220 G=G+1 225 FOR X=1 TO X1-1: PRINT: NEXT X 230 FOR X=X1 TO X2 240 PRINT 250 FOR Y=Y1 TO Y2 253 IF A(X,Y)=2 THEN A(X,Y)=0:GOTO 270 256 IF A(X,Y)=3 THEN A(X,Y)=1:GOTO 261 260 IF A(X,Y)<>1 THEN 270 261 PRINT TAB(Y);"*"; 262 IF X<X3 THEN X3=X 264 IF X>X4 THEN X4=X 266 IF Y<Y3 THEN Y3=Y 268 IF Y>Y4 THEN Y4=Y 270 NEXT Y 290 NEXT X 295 FOR X=X2+1 TO 24: PRINT: NEXT X 299 X1=X3: X2=X4: Y1=Y3: Y2=Y4 301 IF X1<3 THEN X1=3:I9=-1 303 IF X2>22 THEN X2=22:I9=-1 305 IF Y1<3 THEN Y1=3:I9=-1 307 IF Y2>68 THEN Y2=68:I9=-1 309 P=0 500 FOR X=X1-1 TO X2+1 510 FOR Y=Y1-1 TO Y2+1 520 C=0 530 FOR I=X-1 TO X+1 540 FOR J=Y-1 TO Y+1 550 IF A(I,J)=1 OR A(I,J)=2 THEN C=C+1 560 NEXT J 570 NEXT I 580 IF A(X,Y)=0 THEN 610 590 IF C<3 OR C>4 THEN A(X,Y)=2: GOTO 600 595 P=P+1 600 GOTO 620 610 IF C=3 THEN A(X,Y)=3:P=P+1 620 NEXT Y 630 NEXT X 635 X1=X1-1:Y1=Y1-1:X2=X2+1:Y2=Y2+1 640 GOTO 210 650 END ================================================ FILE: 00_Alternate_Languages/56_Life_for_Two/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/56_Life_for_Two/MiniScript/lifefortwo.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/56_Life_for_Two/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/56_Life_for_Two/lifefortwo.bas ================================================ 2 PRINT TAB(33);"LIFE2" 4 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 6 PRINT: PRINT: PRINT 7 DIM N(6,6),K(18),A(16),X(2),Y(2) 8 DATA 3,102,103,120,130,121,112,111,12 9 DATA 21,30,1020,1030,1011,1021,1003,1002,1012 10 FOR M=1 TO 18: READ K(M): NEXT M 13 DATA -1,0,1,0,0,-1,0,1,-1,-1,1,-1,-1,1,1,1 14 FOR O1= 1 TO 16: READ A(O1): NEXT O1 20 GOTO 500 50 FOR J=1 TO 5 51 FOR K=1 TO 5 55 IF N(J,K)>99 THEN GOSUB 200 60 NEXT K 65 NEXT J 90 K=0: M2=0: M3=0 99 FOR J=0 TO 6: PRINT 100 FOR K=0 TO 6 101 IF J<>0 THEN IF J<>6 THEN 105 102 IF K=6 THEN PRINT 0;: GOTO 125 103 PRINT K;: GOTO 120 105 IF K<>0 THEN IF K<>6 THEN 110 106 IF J=6 THEN PRINT 0: GOTO 126 107 PRINT J;: GOTO 120 110 GOSUB 300 120 NEXT K 125 NEXT J 126 RETURN 200 B=1: IF N(J,K)>999 THEN B=10 220 FOR O1= 1 TO 15 STEP 2 230 N(J+A(O1),K+A(O1+1))=N(J+A(O1),K+A(O1+1))+B 231 NEXT O1 239 RETURN 300 IF N(J,K)<3 THEN 399 305 FOR O1=1 TO 18 310 IF N(J,K)=K(O1) THEN 350 315 NEXT O1 320 GOTO 399 350 IF O1>9 THEN 360 351 N(J,K)=100: M2=M2+1: PRINT " * "; 355 RETURN 360 N(J,K)=1000: M3=M3+1: PRINT " # "; 365 RETURN 399 N(J,K)=0: PRINT " ";: RETURN 500 PRINT TAB(10);"U.B. LIFE GAME" 505 M2=0: M3=0 510 FOR J=1 TO 5 511 FOR K=1 TO 5 515 N(J,K)=0 516 NEXT K 517 NEXT J 519 FOR B=1 TO 2: P1=3: IF B=2 THEN P1=30 520 PRINT:PRINT "PLAYER";B;" - 3 LIVE PIECES." 535 FOR K1=1 TO 3: GOSUB 700 540 N(X(B),Y(B))=P1: NEXT K1 542 NEXT B 559 GOSUB 90 560 PRINT: GOSUB 50 570 IF M2=0 THEN IF M3=0 THEN 574 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 580 FOR B=1 TO 2: PRINT: PRINT: PRINT "PLAYER";B;: GOSUB 700 581 IF B=99 THEN 560 582 NEXT B 586 N(X(1),Y(1))=100: N(X(2),Y(2))=1000 596 GOTO 560 700 PRINT "X,Y":PRINT"XXXXXX";CHR$(13);"$$$$$$";CHR$(13);"&&&&&&"; 701 PRINT CHR$(13);: INPUT Y(B),X(B) 705 IF X(B)<=5 THEN IF X(B)>0 THEN 708 706 GOTO 750 708 IF Y(B)<=5 THEN IF Y(B)>0 THEN 715 710 GOTO 750 715 IF N(X(B),Y(B))<>0 THEN 750 720 IF B=1 THEN RETURN 725 IF X(1)=X(2) THEN IF Y(1)=Y(2) THEN 740 730 RETURN 740 PRINT "SAME COORD. SET TO 0" 741 N(X(B)+1,Y(B)+1)=0: B=99: RETURN 750 PRINT "ILLEGAL COORDS. RETYPE": GOTO 700 999 END ================================================ FILE: 00_Alternate_Languages/57_Literature_Quiz/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/57_Literature_Quiz/MiniScript/litquiz.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/57_Literature_Quiz/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/57_Literature_Quiz/litquiz.bas ================================================ 1 PRINT TAB(25);"LITERATURE QUIZ" 2 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 3 PRINT:PRINT:PRINT 5 R=0 10 PRINT "TEST YOUR KNOWLEDGE OF CHILDREN'S LITERATURE." 12 PRINT: PRINT "THIS IS A MULTIPLE-CHOICE QUIZ." 13 PRINT "TYPE A 1, 2, 3, OR 4 AFTER THE QUESTION MARK." 15 PRINT: PRINT "GOOD LUCK!": PRINT: PRINT 40 PRINT "IN PINOCCHIO, WHAT WAS THE NAME OF THE CAT" 42 PRINT "1)TIGGER, 2)CICERO, 3)FIGARO, 4)GUIPETTO"; 43 INPUT A: IF A=3 THEN 46 44 PRINT "SORRY...FIGARO WAS HIS NAME.": GOTO 50 46 PRINT "VERY GOOD! HERE'S ANOTHER." 47 R=R+1 50 PRINT: PRINT 51 PRINT "FROM WHOSE GARDEN DID BUGS BUNNY STEAL THE CARROTS?" 52 PRINT "1)MR. NIXON'S, 2)ELMER FUDD'S, 3)CLEM JUDD'S, 4)STROMBOLI'S"; 53 INPUT A: IF A=2 THEN 56 54 PRINT "TOO BAD...IT WAS ELMER FUDD'S GARDEN.": GOTO 60 56 PRINT "PRETTY GOOD!" 57 R=R+1 60 PRINT: PRINT 61 PRINT "IN THE WIZARD OF OS, DOROTHY'S DOG WAS NAMED" 62 PRINT "1)CICERO, 2)TRIXIA, 3)KING, 4)TOTO"; 63 INPUT A: IF A=4 THEN 66 64 PRINT "BACK TO THE BOOKS,...TOTO WAS HIS NAME.": GOTO 70 66 PRINT "YEA! YOU'RE A REAL LITERATURE GIANT." 67 R=R+1 70 PRINT:PRINT 71 PRINT "WHO WAS THE FAIR MAIDEN WHO ATE THE POISON APPLE" 72 PRINT "1)SLEEPING BEAUTY, 2)CINDERELLA, 3)SNOW WHITE, 4)WENDY"; 73 INPUT A: IF A=3 THEN 76 74 PRINT "OH, COME ON NOW...IT WAS SNOW WHITE." 75 GOTO 80 76 PRINT "GOOD MEMORY!" 77 R=R+1 80 PRINT:PRINT 85 IF R=4 THEN 100 90 IF R<2 THEN 200 92 PRINT "NOT BAD, BUT YOU MIGHT SPEND A LITTLE MORE TIME" 94 PRINT "READING THE NURSERY GREATS." 96 STOP 100 PRINT "WOW! THAT'S SUPER! YOU REALLY KNOW YOUR NURSERY" 110 PRINT "YOUR NEXT QUIZ WILL BE ON 2ND CENTURY CHINESE" 120 PRINT "LITERATURE (HA, HA, HA)" 130 STOP 200 PRINT "UGH. THAT WAS DEFINITELY NOT TOO SWIFT. BACK TO" 205 PRINT "NURSERY SCHOOL FOR YOU, MY FRIEND." 999 END ================================================ FILE: 00_Alternate_Languages/58_Love/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/58_Love/MiniScript/love.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/58_Love/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/58_Love/love.bas ================================================ 2 PRINT TAB(33);"LOVE" 4 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 6 PRINT: PRINT: PRINT 20 PRINT "A TRIBUTE TO THE GREAT AMERICAN ARTIST, ROBERT INDIANA." 30 PRINT "HIS GREATEST WORK WILL BE REPRODUCED WITH A MESSAGE OF" 40 PRINT "YOUR CHOICE UP TO 60 CHARACTERS. IF YOU CAN'T THINK OF" 50 PRINT "A MESSAGE, SIMPLE TYPE THE WORD 'LOVE'": PRINT 60 INPUT "YOUR MESSAGE, PLEASE";A$: L=LEN(A$) 70 DIM T$(120): FOR I=1 TO 10: PRINT: NEXT I 100 FOR J=0 TO INT(60/L) 110 FOR I=1 TO L 120 T$(J*L+I)=MID$(A$,I,1) 130 NEXT I: NEXT J 140 C=0 200 A1=1: P=1: C=C+1: IF C=37 THEN 999 205 PRINT 210 READ A: A1=A1+A: IF P=1 THEN 300 240 FOR I=1 TO A: PRINT " ";: NEXT I: P=1: GOTO 400 300 FOR I=A1-A TO A1-1: PRINT T$(I);: NEXT I: P=0 400 IF A1>60 THEN 200 410 GOTO 210 600 DATA 60,1,12,26,9,12,3,8,24,17,8,4,6,23,21,6,4,6,22,12,5,6,5 610 DATA 4,6,21,11,8,6,4,4,6,21,10,10,5,4,4,6,21,9,11,5,4 620 DATA 4,6,21,8,11,6,4,4,6,21,7,11,7,4,4,6,21,6,11,8,4 630 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 640 DATA 4,6,17,3,1,7,5,13,4,4,6,15,5,2,23,5,1,29,5,17,8 650 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 660 DATA 5,6,11,3,11,6,14,3,1,5,6,11,3,11,6,15,2,1 670 DATA 6,6,9,3,12,6,16,1,1,6,6,9,3,12,6,7,1,10 680 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 690 DATA 8,6,5,3,14,6,7,1,10,9,6,3,3,15,6,16,1,1 700 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 710 DATA 11,8,13,27,1,11,8,13,27,1,60 999 FOR I=1 TO 10: PRINT: NEXT I: END ================================================ FILE: 00_Alternate_Languages/59_Lunar_LEM_Rocket/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/59_Lunar_LEM_Rocket/MiniScript/lem.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/59_Lunar_LEM_Rocket/MiniScript/lunar.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/59_Lunar_LEM_Rocket/MiniScript/rocket.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/59_Lunar_LEM_Rocket/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/59_Lunar_LEM_Rocket/lem.bas ================================================ 2 PRINT TAB(34);"LEM" 4 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 7 REM ROCKT2 IS AN INTERACTIVE GAME THAT SIMULATES A LUNAR 8 REM LANDING IS SIMILAR TO THAT OF THE APOLLO PROGRAM. 9 REM THERE IS ABSOLUTELY NO CHANCE INVOLVED 10 Z$="GO" 15 B1=1 20 M=17.95 25 F1=5.25 30 N=7.5 35 R0=926 40 V0=1.29 45 T=0 50 H0=60 55 R=R0+H0 60 A=-3.425 65 R1=0 70 A1=8.84361E-04 75 R3=0 80 A3=0 85 M1=7.45 90 M0=M1 95 B=750 100 T1=0 105 F=0 110 P=0 115 N=1 120 M2=0 125 S=0 130 C=0 135 IF Z$="YES" THEN 1150 140 PRINT 145 PRINT "LUNAR LANDING SIMULATION" 150 PRINT 155 PRINT "HAVE YOU FLOWN AN APOLLO/LEM MISSION BEFORE"; 160 PRINT " (YES OR NO)"; 165 INPUT Q$ 170 IF Q$="YES" THEN 190 175 IF Q$="NO" THEN 205 180 PRINT "JUST ANSWER THE QUESTION, PLEASE, "; 185 GOTO 160 190 PRINT 195 PRINT "INPUT MEASUREMENT OPTION NUMBER"; 200 GOTO 225 205 PRINT 210 PRINT "WHICH SYSTEM OF MEASUREMENT DO YOU PREFER?" 215 PRINT " 1=METRIC 0=ENGLISH" 220 PRINT "ENTER THE APPROPRIATE NUMBER"; 225 INPUT K 230 PRINT 235 IF K=0 THEN 280 240 IF K=1 THEN 250 245 GOTO 220 250 Z=1852.8 255 M$="METERS" 260 G3=3.6 265 N$=" KILOMETERS" 270 G5=1000 275 GOTO 305 280 Z=6080 285 M$="FEET" 290 G3=.592 295 N$="N.MILES" 300 G5=Z 305 IF B1=3 THEN 670 310 IF Q$="YES" THEN 485 315 PRINT 320 PRINT " YOU ARE ON A LUNAR LANDING MISSION. AS THE PILOT OF" 325 PRINT "THE LUNAR EXCURSION MODULE, YOU WILL BE EXPECTED TO" 330 PRINT "GIVE CERTAIN COMMANDS TO THE MODULE NAVIGATION SYSTEM." 335 PRINT "THE ON-BOARD COMPUTER WILL GIVE A RUNNING ACCOUNT" 340 PRINT "OF INFORMATION NEEDED TO NAVIGATE THE SHIP." 345 PRINT 350 PRINT 355 PRINT "THE ATTITUDE ANGLE CALLED FOR IS DESCRIBED AS FOLLOWS." 360 PRINT "+ OR -180 DEGREES IS DIRECTLY AWAY FROM THE MOON" 365 PRINT "-90 DEGREES IS ON A TANGENT IN THE DIRECTION OF ORBIT" 370 PRINT "+90 DEGREES IS ON A TANGENT FROM THE DIRECTION OF ORBIT" 375 PRINT "0 (ZERO) DEGREES IS DIRECTLY TOWARD THE MOON" 380 PRINT 385 PRINT TAB(30);"-180|+180" 390 PRINT TAB(34);"^" 395 PRINT TAB(27);"-90 < -+- > +90" 400 PRINT TAB(34);"!" 405 PRINT TAB(34);"0" 410 PRINT TAB(21);"<<<< DIRECTION OF ORBIT <<<<" 415 PRINT 420 PRINT TAB(20);"------ SURFACE OF MOON ------" 425 PRINT 430 PRINT 435 PRINT "ALL ANGLES BETWEEN -180 AND +180 DEGREES ARE ACCEPTED." 440 PRINT 445 PRINT "1 FUEL UNIT = 1 SEC. AT MAX THRUST" 450 PRINT "ANY DISCREPANCIES ARE ACCOUNTED FOR IN THE USE OF FUEL" 455 PRINT "FOR AN ATTITUDE CHANGE." 460 PRINT "AVAILABLE ENGINE POWER: 0 (ZERO) AND ANY VALUE BETWEEN" 465 PRINT "10 AND 100 PERCENT." 470 PRINT 475 PRINT"NEGATIVE THRUST OR TIME IS PROHIBITED." 480 PRINT 485 PRINT 490 PRINT "INPUT: TIME INTERVAL IN SECONDS ------ (T)" 495 PRINT " PERCENTAGE OF THRUST ---------- (P)" 500 PRINT " ATTITUDE ANGLE IN DEGREES ----- (A)" 505 PRINT 510 IF Q$="YES" THEN 535 515 PRINT "FOR EXAMPLE:" 520 PRINT "T,P,A? 10,65,-60" 525 PRINT "TO ABORT THE MISSION AT ANY TIME, ENTER 0,0,0" 530 PRINT 535 PRINT "OUTPUT: TOTAL TIME IN ELAPSED SECONDS" 540 PRINT " HEIGHT IN ";M$ 545 PRINT " DISTANCE FROM LANDING SITE IN ";M$ 550 PRINT " VERTICAL VELOCITY IN ";M$;"/SECOND" 555 PRINT " HORIZONTAL VELOCITY IN ";M$;"/SECOND" 560 PRINT " FUEL UNITS REMAINING" 565 PRINT 570 GOTO 670 575 PRINT 580 PRINT "T,P,A"; 585 INPUT T1,F,P 590 F=F/100 595 IF T1<0 THEN 905 600 IF T1=0 THEN 1090 605 IF ABS(F-.05)>1 THEN 945 610 IF ABS(F-.05)<.05 THEN 945 615 IF ABS(P)>180 THEN 925 620 N=20 625 IF T1<400 THEN 635 630 N=T1/20 635 T1=T1/N 640 P=P*3.14159/180 645 S=SIN(P) 650 C=COS(P) 655 M2=M0*T1*F/B 660 R3=-.5*R0*((V0/R)^2)+R*A1*A1 665 A3=-2*R1*A1/R 670 FOR I=1 TO N 675 IF M1=0 THEN 715 680 M1=M1-M2 685 IF M1>0 THEN 725 690 F=F*(1+M1/M2) 695 M2=M1+M2 700 PRINT "YOU ARE OUT OF FUEL." 705 M1=0 710 GOTO 725 715 F=0 720 M2=0 725 M=M-.5*M2 730 R4=R3 735 R3=-.5*R0*((V0/R)^2)+R*A1*A1 740 R2=(3*R3-R4)/2+.00526*F1*F*C/M 745 A4=A3 750 A3=-2*R1*A1/R 755 A2=(3*A3-A4)/2+.0056*F1*F*S/(M*R) 760 X=R1*T1+.5*R2*T1*T1 765 R=R+X 770 H0=H0+X 775 R1=R1+R2*T1 780 A=A+A1*T1+.5*A2*T1*T1 785 A1=A1+A2*T1 790 M=M-.5*M2 795 T=T+T1 800 IF H0<3.287828E-04 THEN 810 805 NEXT I 810 H=H0*Z 815 H1=R1*Z 820 D=R0*A*Z 825 D1=R*A1*Z 830 T2=M1*B/M0 835 PRINT " ";T;TAB(10);H;TAB(23);D; 840 PRINT TAB(37);H1;TAB(49);D1;TAB(60);T2 845 IF H0<3.287828E-04 THEN 880 850 IF R0*A>164.474 THEN 1050 855 IF M1>0 THEN 580 860 T1=20 865 F=0 870 P=0 875 GOTO 620 880 IF R1<-8.21957E-04 THEN 1020 885 IF ABS(R*A1)>4.93174E-04 THEN 1020 890 IF H0<-3.287828E-04 THEN 1020 895 IF ABS(D)>10*Z THEN 1065 900 GOTO 995 905 PRINT 910 PRINT "THIS SPACECRAFT IS NOT ABLE TO VIOLATE THE SPACE-"; 915 PRINT "TIME CONTINUUM." 920 GOTO 575 925 PRINT 930 PRINT "IF YOU WANT TO SPIN AROUND, GO OUTSIDE THE MODULE" 935 PRINT "FOR AN E.V.A." 940 GOTO 575 945 PRINT 950 PRINT "IMPOSSIBLE THRUST VALUE "; 955 IF F<0 THEN 985 960 IF F-.05<.05 THEN 975 965 PRINT "TOO LARGE" 970 GOTO 575 975 PRINT "TOO SMALL" 980 GOTO 575 985 PRINT "NEGATIVE" 990 GOTO 575 995 PRINT 1000 PRINT "TRANQUILITY BASE HERE -- THE EAGLE HAS LANDED." 1005 PRINT "CONGRATULATIONS -- THERE WAS NO SPACECRAFT DAMAGE." 1010 PRINT "YOU MAY NOW PROCEED WITH SURFACE EXPLORATION." 1015 GOTO 1100 1020 PRINT 1025 PRINT "CRASH !!!!!!!!!!!!!!!!" 1030 PRINT "YOUR IMPACT CREATED A CRATER";ABS(H);M$;" DEEP." 1035 X1=SQR(D1*D1+H1*H1)*G3 1040 PRINT "AT CONTACT YOU WERE TRAVELING";X1;N$;"/HR" 1045 GOTO 1100 1050 PRINT 1055 PRINT "YOU HAVE BEEN LOST IN SPACE WITH NO HOPE OF RECOVERY." 1060 GOTO 1100 1065 PRINT "YOU ARE DOWN SAFELY - " 1075 PRINT 1080 PRINT "BUT MISSED THE LANDING SITE BY";ABS(D/G5);N$;"." 1085 GOTO 1100 1090 PRINT 1095 PRINT "MISSION ABENDED" 1100 PRINT 1105 PRINT "DO YOU WANT TO TRY IT AGAIN (YES/NO)?" 1110 INPUT Z$ 1115 IF Z$="YES" THEN 20 1120 IF Z$="NO" THEN 1130 1125 GOTO 1105 1130 PRINT 1135 PRINT "TOO BAD, THE SPACE PROGRAM HATES TO LOSE EXPERIENCED" 1140 PRINT "ASTRONAUTS." 1145 STOP 1150 PRINT 1155 PRINT "OK, DO YOU WANT THE COMPLETE INSTRUCTIONS OR THE INPUT -" 1160 PRINT "OUTPUT STATEMENTS?" 1165 PRINT "1=COMPLETE INSTRUCTIONS" 1170 PRINT "2=INPUT-OUTPUT STATEMENTS" 1175 PRINT "3=NEITHER" 1180 INPUT B1 1185 Q$="NO" 1190 IF B1=1 THEN 205 1195 Q$="YES" 1200 IF B1=2 THEN 190 1205 IF B1=3 THEN 190 1210 GOTO 1165 1215 END ================================================ FILE: 00_Alternate_Languages/59_Lunar_LEM_Rocket/lunar.bas ================================================ 10 PRINT TAB(33);"LUNAR" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 25 PRINT:PRINT:PRINT 30 PRINT "THIS IS A COMPUTER SIMULATION OF AN APOLLO LUNAR" 40 PRINT "LANDING CAPSULE.": PRINT: PRINT 50 PRINT "THE ON-BOARD COMPUTER HAS FAILED (IT WAS MADE BY" 60 PRINT "XEROX) SO YOU HAVE TO LAND THE CAPSULE MANUALLY." 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." 110 PRINT: PRINT: PRINT: PRINT "GOOD LUCK" 120 L=0 130 PRINT: PRINT "SEC","MI + FT","MPH","LB FUEL","BURN RATE":PRINT 140 A=120:V=1:M=33000:N=16500:G=1E-03:Z=1.8 150 PRINT L,INT(A);INT(5280*(A-INT(A))),3600*V,M-N,:INPUT K:T=10 160 IF M-N<1E-03 THEN 240 170 IF T<1E-03 THEN 150 180 S=T: IF M>=N+S*K THEN 200 190 S=(M-N)/K 200 GOSUB 420: IF I<=0 THEN 340 210 IF V<=0 THEN 230 220 IF J<0 THEN 370 230 GOSUB 330: GOTO 160 240 PRINT "FUEL OUT AT";L;"SECONDS":S=(-V+SQR(V*V+2*A*G))/G 250 V=V+G*S: L=L+S 260 W=3600*V: PRINT "ON MOON AT";L;"SECONDS - IMPACT VELOCITY";W;"MPH" 274 IF W<=1.2 THEN PRINT "PERFECT LANDING!": GOTO 440 280 IF W<=10 THEN PRINT "GOOD LANDING (COULD BE BETTER)":GOTO 440 282 IF W>60 THEN 300 284 PRINT "CRAFT DAMAGE... YOU'RE STRANDED HERE UNTIL A RESCUE" 286 PRINT "PARTY ARRIVES. HOPE YOU HAVE ENOUGH OXYGEN!" 288 GOTO 440 300 PRINT "SORRY THERE WERE NO SURVIVORS. YOU BLEW IT!" 310 PRINT "IN FACT, YOU BLASTED A NEW LUNAR CRATER";W*.227;"FEET DEEP!" 320 GOTO 440 330 L=L+S: T=T-S: M=M-S*K: A=I: V=J: RETURN 340 IF S<5E-03 THEN 260 350 D=V+SQR(V*V+2*A*(G-Z*K/M)):S=2*A/D 360 GOSUB 420: GOSUB 330: GOTO 340 370 W=(1-M*G/(Z*K))/2: S=M*V/(Z*K*(W+SQR(W*W+V/Z)))+.05:GOSUB 420 380 IF I<=0 THEN 340 390 GOSUB 330: IF J>0 THEN 160 400 IF V>0 THEN 370 410 GOTO 160 420 Q=S*K/M: J=V+G*S+Z*(-Q-Q*Q/2-Q^3/3-Q^4/4-Q^5/5) 430 I=A-G*S*S/2-V*S+Z*S*(Q/2+Q^2/6+Q^3/12+Q^4/20+Q^5/30):RETURN 440 PRINT:PRINT:PRINT:PRINT "TRY AGAIN??": GOTO 70 ================================================ FILE: 00_Alternate_Languages/59_Lunar_LEM_Rocket/rocket.bas ================================================ 10 PRINT TAB(30); "ROCKET" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 30 PRINT:PRINT:PRINT 70 PRINT "LUNAR LANDING SIMULATION" 80 PRINT "----- ------- ----------": PRINT 100 INPUT "DO YOU WANT INSTRUCTIONS (YES OR NO)";A$ 110 IF A$="NO" THEN 390 160 PRINT 200 PRINT"YOU ARE LANDING ON THE MOON AND AND HAVE TAKEN OVER MANUAL" 210 PRINT"CONTROL 1000 FEET ABOVE A GOOD LANDING SPOT. YOU HAVE A DOWN-" 220 PRINT"WARD VELOCITY OF 50 FEET/SEC. 150 UNITS OF FUEL REMAIN." 225 PRINT 230 PRINT"HERE ARE THE RULES THAT GOVERN YOUR APOLLO SPACE-CRAFT:": PRINT 240 PRINT"(1) AFTER EACH SECOND THE HEIGHT, VELOCITY, AND REMAINING FUEL" 250 PRINT" WILL BE REPORTED VIA DIGBY YOUR ON-BOARD COMPUTER." 260 PRINT"(2) AFTER THE REPORT A '?' WILL APPEAR. ENTER THE NUMBER" 270 PRINT" OF UNITS OF FUEL YOU WISH TO BURN DURING THE NEXT" 280 PRINT" SECOND. EACH UNIT OF FUEL WILL SLOW YOUR DESCENT BY" 290 PRINT" 1 FOOT/SEC." 310 PRINT"(3) THE MAXIMUM THRUST OF YOUR ENGINE IS 30 FEET/SEC/SEC" 320 PRINT" OR 30 UNITS OF FUEL PER SECOND." 330 PRINT"(4) WHEN YOU CONTACT THE LUNAR SURFACE. YOUR DESCENT ENGINE" 340 PRINT" WILL AUTOMATICALLY SHUT DOWN AND YOU WILL BE GIVEN A" 350 PRINT" REPORT OF YOUR LANDING SPEED AND REMAINING FUEL." 360 PRINT"(5) IF YOU RUN OUT OF FUEL THE '?' WILL NO LONGER APPEAR" 370 PRINT" BUT YOUR SECOND BY SECOND REPORT WILL CONTINUE UNTIL" 380 PRINT" YOU CONTACT THE LUNAR SURFACE.":PRINT 390 PRINT"BEGINNING LANDING PROCEDURE..........":PRINT 400 PRINT"G O O D L U C K ! ! !" 420 PRINT:PRINT 430 PRINT"SEC FEET SPEED FUEL PLOT OF DISTANCE" 450 PRINT 455 T=0:H=1000:V=50:F=150 490 PRINT T;TAB(6);H;TAB(16);V;TAB(26);F;TAB(35);"I";TAB(H/15);"*" 500 INPUT B 510 IF B<0 THEN 650 520 IF B>30 THEN B=30 530 IF B>F THEN B=F 540 V1=V-B+5 560 F=F-B 570 H=H- .5*(V+V1) 580 IF H<=0 THEN 670 590 T=T+1 600 V=V1 610 IF F>0 THEN 490 615 IF B=0 THEN 640 620 PRINT"**** OUT OF FUEL ****" 640 PRINT T;TAB(4);H;TAB(12);V;TAB(20);F;TAB(29);"I";TAB(H/12+29);"*" 650 B=0 660 GOTO 540 670 PRINT"***** CONTACT *****" 680 H=H+ .5*(V1+V) 690 IF B=5 THEN 720 700 D=(-V+SQR(V*V+H*(10-2*B)))/(5-B) 710 GOTO 730 720 D=H/V 730 V1=V+(5-B)*D 760 PRINT"TOUCHDOWN AT";T+D;"SECONDS." 770 PRINT"LANDING VELOCITY=";V1;"FEET/SEC." 780 PRINT F;"UNITS OF FUEL REMAINING." 790 IF V1<>0 THEN 810 800 PRINT"CONGRATULATIONS! A PERFECT LANDING!!" 805 PRINT"YOUR LICENSE WILL BE RENEWED.......LATER." 810 IF ABS(V1)<2 THEN 840 820 PRINT"***** SORRY, BUT YOU BLEW IT!!!!" 830 PRINT"APPROPRIATE CONDOLENCES WILL BE SENT TO YOUR NEXT OF KIN." 840 PRINT:PRINT:PRINT 850 INPUT "ANOTHER MISSION";A$ 860 IF A$="YES" THEN 390 870 PRINT: PRINT "CONTROL OUT.": PRINT 999 END ================================================ FILE: 00_Alternate_Languages/60_Mastermind/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/60_Mastermind/MiniScript/mastermind.ms ================================================ // // 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" ================================================ FILE: 00_Alternate_Languages/60_Mastermind/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/60_Mastermind/mastermind.bas ================================================ 2 PRINT TAB(30);"MASTERMIND" 4 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 6 PRINT: PRINT: PRINT 10 REM 20 REM MASTERMIND II 30 REM STEVE NORTH 40 REM CREATIVE COMPUTING 50 REM PO BOX 789-M MORRISTOWN NEW JERSEY 07960 60 REM 70 REM 80 INPUT "NUMBER OF COLORS";C9 90 IF C9>8 THEN PRINT "NO MORE THAN 8, PLEASE!":GOTO 80 100 INPUT "NUMBER OF POSITIONS";P9 110 INPUT "NUMBER OF ROUNDS";R9 120 P=C9^P9 130 PRINT "TOTAL POSSIBILITIES =";P 140 H=0:C=0 150 DIM Q(P9),S(10,2),S$(10),A$(P9),G$(P9),I(P),H$(P9) 160 L$="BWRGOYPT" 170 PRINT 180 PRINT 190 PRINT "COLOR LETTER" 200 PRINT "===== ======" 210 FOR X=1 TO C9 220 READ X$ 230 PRINT X$;TAB(13);MID$(L$,X,1) 240 NEXT X 250 PRINT 260 FOR R=1 TO R9 270 PRINT 280 PRINT "ROUND NUMBER";R;"----" 290 PRINT 300 PRINT "GUESS MY COMBINATION.":PRINT 310 REM GET A COMBINATION 320 A=INT(P*RND(1)+1) 330 GOSUB 3000 340 FOR X=1 TO A 350 GOSUB 3500 360 NEXT X 370 FOR M=1 TO 10 380 PRINT "MOVE # ";M;" GUESS ";:INPUT X$ 390 IF X$="BOARD" THEN 2000 400 IF X$="QUIT" THEN 2500 410 IF LEN(X$)<>P9 THEN PRINT "BAD NUMBER OF POSITIONS.":GOTO 380 420 REM UNPACK X$ INTO G$(1-P9) 430 FOR X=1 TO P9 440 FOR Y=1 TO C9 450 IF MID$(X$,X,1)=MID$(L$,Y,1) THEN 480 460 NEXT Y 470 PRINT "'"; MID$(X$,X,1); "' IS UNRECOGNIZED.":GOTO 380 480 G$(X)=MID$(X$,X,1) 490 NEXT X 500 REM NOW WE CONVERT Q(1-P9) INTO A$(1-P9) [ACTUAL GUESS] 510 GOSUB 4000 520 REM AND GET NUMBER OF BLACKS AND WHITES 530 GOSUB 4500 540 IF B=P9 THEN 630 550 REM TELL HUMAN RESULTS 560 PRINT "YOU HAVE ";B;" BLACKS AND ";W;" WHITES." 570 REM SAVE ALL THIS STUFF FOR BOARD PRINTOUT LATER 580 S$(M)=X$ 590 S(M,1)=B 600 S(M,2)=W 610 NEXT M 620 PRINT "YOU RAN OUT OF MOVES! THAT'S ALL YOU GET!":GOTO 640 622 GOSUB 4000 623 PRINT "THE ACTUAL COMBINATION WAS: "; 624 FOR X=1 TO P9 625 PRINT A$(X); 626 NEXT X 627 PRINT 630 PRINT "YOU GUESSED IT IN ";M;" MOVES!" 640 H=H+M 650 GOSUB 5000 660 REM 670 REM NOW COMPUTER GUESSES 680 REM 690 FOR X=1 TO P 700 I(X)=1 710 NEXT X 720 PRINT "NOW I GUESS. THINK OF A COMBINATION." 730 INPUT "HIT RETURN WHEN READY:";X$ 740 FOR M=1 TO 10 750 GOSUB 3000 760 REM FIND A GUESS 770 G=INT(P*RND(1)+1) 780 IF I(G)=1 THEN 890 790 FOR X=G TO P 800 IF I(X)=1 THEN 880 810 NEXT X 820 FOR X=1 TO G 830 IF I(X)=1 THEN 880 840 NEXT X 850 PRINT "YOU HAVE GIVEN ME INCONSISTENT INFORMATION." 860 PRINT "TRY AGAIN, AND THIS TIME PLEASE BE MORE CAREFUL." 870 GOTO 660 880 G=X 890 REM NOW WE CONVERT GUESS #G INTO G$ 900 FOR X=1 TO G 910 GOSUB 3500 920 NEXT X 930 GOSUB 6000 940 PRINT "MY GUESS IS: "; 950 FOR X=1 TO P9 960 PRINT H$(X); 970 NEXT X 980 INPUT " BLACKS, WHITES ";B1,W1 990 IF B1=P9 THEN 1120 1000 GOSUB 3000 1010 FOR X=1 TO P 1020 GOSUB 3500 1030 IF I(X)=0 THEN 1070 1035 GOSUB 6500 1040 GOSUB 4000 1050 GOSUB 4500 1060 IF B1>B OR W1>W THEN I(X)=0 1070 NEXT X 1080 NEXT M 1090 PRINT "I USED UP ALL MY MOVES!" 1100 PRINT "I GUESS MY CPU IS JUST HAVING AN OFF DAY." 1110 GOTO 1130 1120 PRINT "I GOT IT IN ";M;" MOVES!" 1130 C=C+M 1140 GOSUB 5000 1150 NEXT R 1160 PRINT "GAME OVER" 1170 PRINT "FINAL SCORE:" 1180 GOSUB 5040 1190 STOP 2000 REM 2010 REM BOARD PRINTOUT ROUTINE 2020 REM 2025 PRINT 2030 PRINT "BOARD" 2040 PRINT "MOVE GUESS BLACK WHITE" 2050 FOR Z=1 TO M-1 2060 PRINT Z;TAB(9);S$(Z);TAB(25);S(Z,1);TAB(35);S(Z,2) 2070 NEXT Z 2075 PRINT 2080 GOTO 380 2500 REM 2510 REM QUIT ROUTINE 2520 REM 2530 PRINT "QUITTER! MY COMBINATION WAS: "; 2535 GOSUB 4000 2540 FOR X=1 TO P9 2550 PRINT A$(X); 2560 NEXT X 2565 PRINT 2570 PRINT "GOOD BYE" 2580 STOP 3000 REM 3010 REM INITIALIZE Q(1-P9) TO ZEROS 3020 REM 3030 FOR S=1 TO P9 3040 Q(S)=0 3050 NEXT S 3060 RETURN 3500 REM 3510 REM INCREMENT Q(1-P9) 3520 REM 3522 IF Q(1)>0 THEN 3530 3524 REM IF ZERO, THIS IS OUR FIRST INCREMENT: MAKE ALL ONES 3526 FOR S=1 TO P9 3527 Q(S)=1 3528 NEXT S 3529 RETURN 3530 Q=1 3540 Q(Q)=Q(Q)+1 3550 IF Q(Q)<=C9 THEN RETURN 3560 Q(Q)=1 3570 Q=Q+1 3580 GOTO 3540 4000 REM 4010 REM CONVERT Q(1-P9) TO A$(1-P9) 4020 REM 4030 FOR S=1 TO P9 4040 A$(S)=MID$(L$,Q(S),1) 4050 NEXT S 4060 RETURN 4500 REM 4510 REM GET NUMBER OF BLACKS (B) AND WHITES (W) 4520 REM MASHES G$ AND A$ IN THE PROCESS 4530 REM 4540 B=0:W=0:F=0 4550 FOR S=1 TO P9 4560 IF G$(S)<>A$(S) THEN 4620 4570 B=B+1 4580 G$(S)=CHR$(F) 4590 A$(S)=CHR$(F+1) 4600 F=F+2 4610 GOTO 4660 4620 FOR T=1 TO P9 4630 IF G$(S)<>A$(T) THEN 4650 4640 IF G$(T)=A$(T) THEN 4650 4645 W=W+1:A$(T)=CHR$(F):G$(S)=CHR$(F+1):F=F+2:GOTO 4660 4650 NEXT T 4660 NEXT S 4670 RETURN 5000 REM 5010 REM PRINT SCORE 5020 REM 5030 PRINT "SCORE:" 5040 PRINT " COMPUTER ";C 5050 PRINT " HUMAN ";H 5060 PRINT 5070 RETURN 5500 REM 5510 REM CONVERT Q(1-P9) INTO G$(1-P9) 5520 REM 5530 FOR S=1 TO P9 5540 G$(S)=MID$(L$,Q(S),1) 5550 NEXT S 5560 RETURN 6000 REM 6010 REM CONVERT Q(1-P9) TO H$(1-P9) 6020 REM 6030 FOR S=1 TO P9 6040 H$(S)=MID$(L$,Q(S),1) 6050 NEXT S 6060 RETURN 6500 REM 6510 REM COPY H$ INTO G$ 6520 REM 6530 FOR S=1 TO P9 6540 G$(S)=H$(S) 6550 NEXT S 6560 RETURN 8000 REM PROGRAM DATA FOR COLOR NAMES 8010 DATA BLACK,WHITE,RED,GREEN,ORANGE,YELLOW,PURPLE,TAN 9998 REM ...WE'RE SORRY BUT IT'S TIME TO GO... 9999 END ================================================ FILE: 00_Alternate_Languages/61_Math_Dice/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/61_Math_Dice/MiniScript/mathdice.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/61_Math_Dice/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/61_Math_Dice/mathdice.bas ================================================ 10 PRINT TAB(31);"MATH DICE" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 30 PRINT:PRINT:PRINT 40 PRINT "THIS PROGRAM GENERATES SUCCESSIVE PICTURES OF TWO DICE." 50 PRINT "WHEN TWO DICE AND AN EQUAL SIGN FOLLOWED BY A QUESTION" 60 PRINT "MARK HAVE BEEN PRINTED, TYPE YOUR ANSWER AND THE RETURN KEY." 70 PRINT "TO CONCLUDE THE LESSON, TYPE CONTROL-C AS YOUR ANSWER." 80 PRINT 90 PRINT 100 N=N+1 110 D=INT(6*RND(1)+1) 120 PRINT" ----- " 130 IF D=1 THEN 200 140 IF D=2 THEN 180 150 IF D=3 THEN 180 160 PRINT "I * * I" 170 GOTO 210 180 PRINT "I * I" 190 GOTO 210 200 PRINT "I I" 210 IF D=2 THEN 260 220 IF D=4 THEN 260 230 IF D=6 THEN 270 240 PRINT "I * I" 250 GOTO 280 260 PRINT "I I" 265 GOTO 280 270 PRINT "I * * I" 280 IF D=1 THEN 350 290 IF D=2 THEN 330 300 IF D=3 THEN 330 310 PRINT "I * * I" 320 GOTO 360 330 PRINT "I * I" 340 GOTO 360 350 PRINT "I I" 360 PRINT " ----- " 370 PRINT 375 IF N=2 THEN 500 380 PRINT " +" 381 PRINT 400 A=D 410 GOTO 100 500 T=D+A 510 PRINT " ="; 520 INPUT T1 530 IF T1=T THEN 590 540 PRINT "NO, COUNT THE SPOTS AND GIVE ANOTHER ANSWER." 541 PRINT " ="; 550 INPUT T2 560 IF T2=T THEN 590 570 PRINT "NO, THE ANSWER IS";T 580 GOTO 600 590 PRINT "RIGHT!" 600 PRINT 601 PRINT "THE DICE ROLL AGAIN..." 610 PRINT 615 N=0 620 GOTO 100 999 END ================================================ FILE: 00_Alternate_Languages/61_Math_Dice/pascal/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Pascal](https://en.wikipedia.org/wiki/Pascal_(programming_language)) ##### Translator Notes: I tried to preserve as much of the original layout and flow of the code as possible. I added a procedure for the printing of the die-face; and another to read an integer from the player, as I was unhappy with the runtime error message spat out when a non-number is given to readln(<integer>). I was torn between using the correct singular term "die" instead of "dice". In the end I used a (poor?) combination of both. krt@krt.com.au 2020-10-12 ================================================ FILE: 00_Alternate_Languages/61_Math_Dice/pascal/mathdice.pas ================================================ (* * Ported from mathdice.bas to Pascal by krt@krt.com.au * * Compile with Free Pascal (https://www.freepascal.org/) ~ * fpc mathdice.pas *) program MathDice; procedure printDice( face_value: integer ); (* Prints a box with spots representing a die face for the user *) begin writeln( ' ----- ' ); if ( face_value = 1 ) then writeln( 'I I' ) else if ( ( face_value = 2 ) or ( face_value = 3 ) ) then writeln( 'I * I' ) else writeln( 'I * * I' ); if ( ( face_value = 2 ) or ( face_value = 4 ) ) then writeln( 'I I' ) else if ( face_value = 6 ) then writeln( 'I * * I' ) else writeln( 'I * I' ); if ( face_value = 1 ) then writeln( 'I I' ) else if ( ( face_value = 2 ) or ( face_value = 3 ) ) then writeln( 'I * I' ) else writeln( 'I * * I' ); writeln( ' ----- ' ); end; procedure writeAtColumn( width: integer; words: string ); (* Prints <width> worth of spaces before the <words> to justify the text *) var i: integer; begin for i := 1 to width do write( ' ' ); writeln( words ); end; function inputNumber(): integer; (* Get a number from the player with error checking. If they type a non-number, ask them again *) var player_input: string; (* The string entered by the player *) player_answer: integer; (* The converted value of the text *) input_error: integer; (* The letter's column that caused an error *) begin input_error := 1; while ( input_error <> 0 ) do begin readln( player_input ); val( player_input, player_answer, input_error ); if ( input_error <> 0 ) then write( 'Please input a number: ' ); end; inputNumber := player_answer; end; var dice1: integer; (* die 1 face value *) dice2: integer; (* die 2 face value *) answer: integer; (* the sum of the dice *) player_answer: integer; (* The value entered by the player *) begin writeAtColumn( 31, 'MATH DICE' ); writeAtColumn( 15, 'CREATIVE COMPUTING MORRISTOWN, NEW JERSEY' ); writeAtColumn( 15, '(Ported to Pascal Oct 2012 krt@krt.com.au)' ); writeln( '' ); writeln( '' ); writeln( '' ); writeln( 'THIS PROGRAM GENERATES SUCCESSIVE PICTURES OF TWO DICE.' ); writeln( 'WHEN TWO DICE AND AN EQUAL SIGN FOLLOWED BY A QUESTION' ); writeln( 'MARK HAVE BEEN PRINTED, TYPE YOUR ANSWER AND THE RETURN KEY.' ); writeln( 'TO CONCLUDE THE LESSON, TYPE CONTROL-C AS YOUR ANSWER.' ); writeln( '' ); writeln( '' ); while ( true ) do begin dice1 := Random( 6 ) + 1; (* Random number between 1 and 6 (including) *) dice2 := Random( 6 ) + 1; (* Random number between 1 and 6 (including) *) answer := dice1 + dice2; (* Show the player two dice faces *) printDice( dice1 ); writeln( ' +' ); printDice( dice2 ); write( ' = ' ); player_answer := inputNumber(); if ( player_answer <> answer ) then begin (* Give the player a second chance at the answer... *) writeln( 'NO, COUNT THE SPOTS AND GIVE ANOTHER ANSWER.' ); write( ' = ' ); player_answer := inputNumber(); end; if ( player_answer <> answer ) then writeln( 'NO, THE ANSWER IS ', answer ) else writeln( 'RIGHT!' ); writeln( '' ); writeln( 'THE DICE ROLL AGAIN...' ); end; end. ================================================ FILE: 00_Alternate_Languages/62_Mugwump/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/62_Mugwump/MiniScript/mugwump.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/62_Mugwump/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/62_Mugwump/mugwump.bas ================================================ 1 PRINT TAB(33);"MUGWUMP" 2 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 3 PRINT:PRINT:PRINT 4 REM COURTESY PEOPLE'S COMPUTER COMPANY 10 DIM P(4,2) 20 PRINT "THE OBJECT OF THIS GAME IS TO FIND FOUR MUGWUMPS" 30 PRINT "HIDDEN ON A 10 BY 10 GRID. HOMEBASE IS POSITION 0,0." 40 PRINT "ANY GUESS YOU MAKE MUST BE TWO NUMBERS WITH EACH" 50 PRINT "NUMBER BETWEEN 0 AND 9, INCLUSIVE. FIRST NUMBER" 60 PRINT "IS DISTANCE TO RIGHT OF HOMEBASE AND SECOND NUMBER" 70 PRINT "IS DISTANCE ABOVE HOMEBASE." 80 PRINT 90 PRINT "YOU GET 10 TRIES. AFTER EACH TRY, I WILL TELL" 100 PRINT "YOU HOW FAR YOU ARE FROM EACH MUGWUMP." 110 PRINT 240 GOSUB 1000 250 T=0 260 T=T+1 270 PRINT 275 PRINT 290 PRINT "TURN NO.";T;"-- WHAT IS YOUR GUESS"; 300 INPUT M,N 310 FOR I=1 TO 4 320 IF P(I,1)=-1 THEN 400 330 IF P(I,1)<>M THEN 380 340 IF P(I,2)<>N THEN 380 350 P(I,1)=-1 360 PRINT "YOU HAVE FOUND MUGWUMP";I 370 GOTO 400 380 D=SQR((P(I,1)-M)^2+(P(I,2)-N)^2) 390 PRINT "YOU ARE";(INT(D*10))/10;"UNITS FROM MUGWUMP";I 400 NEXT I 410 FOR J=1 TO 4 420 IF P(J,1)<>-1 THEN 470 430 NEXT J 440 PRINT 450 PRINT "YOU GOT THEM ALL IN";T;"TURNS!" 460 GOTO 580 470 IF T<10 THEN 260 480 PRINT 490 PRINT "SORRY, THAT'S 10 TRIES. HERE IS WHERE THEY'RE HIDING:" 540 FOR I=1 TO 4 550 IF P(I,1)=-1 THEN 570 560 PRINT "MUGWUMP";I;"IS AT (";P(I,1);",";P(I,2);")" 570 NEXT I 580 PRINT 600 PRINT "THAT WAS FUN! LET'S PLAY AGAIN......." 610 PRINT "FOUR MORE MUGWUMPS ARE NOW IN HIDING." 630 GOTO 240 1000 FOR J=1 TO 2 1010 FOR I=1 TO 4 1020 P(I,J)=INT(10*RND(1)) 1030 NEXT I 1040 NEXT J 1050 RETURN 1099 END ================================================ FILE: 00_Alternate_Languages/63_Name/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/63_Name/MiniScript/name.ms ================================================ 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!" ================================================ FILE: 00_Alternate_Languages/63_Name/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/63_Name/name.bas ================================================ 1 PRINT TAB(34);"NAME" 2 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 3 PRINT: PRINT: PRINT 5 DIM B$(40) 10 PRINT "HELLO.": PRINT "MY NAME IS CREATIVE COMPUTER." 20 PRINT "WHAT'S YOUR NAME (FIRST AND LAST";: INPUT A$: L=LEN(A$) 30 PRINT: PRINT "THANK YOU, "; 40 FOR I=1 TO L: B$(I)=MID$(A$,I,1): NEXT I 50 FOR I=L TO 1 STEP -1: PRINT B$(I);: NEXT I 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: "; 100 FOR J=2 TO L: I=J-1: T$=B$(J) 110 IF T$>B$(I) THEN 130 120 B$(I+1)=B$(I): I=I-1: IF I>0 THEN 110 130 B$(I+1)=T$: NEXT J 140 FOR I=1 TO L: PRINT B$(I);: NEXT I: PRINT: PRINT 150 PRINT "DON'T YOU LIKE THAT BETTER";: INPUT D$ 160 IF D$="YES" THEN 180 170 PRINT: PRINT "I'M SORRY YOU DON'T LIKE IT THAT WAY.": GOTO 200 180 PRINT: PRINT "I KNEW YOU'D AGREE!!" 200 PRINT: PRINT "I REALLY ENJOYED MEETING YOU ";A$;"." 210 PRINT "HAVE A NICE DAY!" 999 END ================================================ FILE: 00_Alternate_Languages/64_Nicomachus/MiniScript/README.md ================================================ 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. ================================================ FILE: 00_Alternate_Languages/64_Nicomachus/MiniScript/nicomachus.ms ================================================ // 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 ================================================ FILE: 00_Alternate_Languages/64_Nicomachus/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/64_Nicomachus/nicomachus.bas ================================================ 2 PRINT TAB(33);"NICOMA" 4 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 6 PRINT: PRINT: PRINT 10 PRINT "BOOMERANG PUZZLE FROM ARITHMETICA OF NICOMACHUS -- A.D. 90!" 20 PRINT 30 PRINT "PLEASE THINK OF A NUMBER BETWEEN 1 AND 100." 40 PRINT "YOUR NUMBER DIVIDED BY 3 HAS A REMAINDER OF"; 45 INPUT A 50 PRINT "YOUR NUMBER DIVIDED BY 5 HAS A REMAINDER OF"; 55 INPUT B 60 PRINT "YOUR NUMBER DIVIDED BY 7 HAS A REMAINDER OF"; 65 INPUT C 70 PRINT 80 PRINT "LET ME THINK A MOMENT..." 85 PRINT 90 FOR I=1 TO 1500: NEXT I 100 D=70*A+21*B+15*C 110 IF D<=105 THEN 140 120 D=D-105 130 GOTO 110 140 PRINT "YOUR NUMBER WAS";D;", RIGHT"; 160 INPUT A$ 165 PRINT 170 IF A$="YES" THEN 220 180 IF A$="NO" THEN 240 190 PRINT "EH? I DON'T UNDERSTAND '";A$;"' TRY 'YES' OR 'NO'." 200 GOTO 160 220 PRINT "HOW ABOUT THAT!!" 230 GOTO 250 240 PRINT "I FEEL YOUR ARITHMETIC IS IN ERROR." 250 PRINT 260 PRINT "LET'S TRY ANOTHER." 270 GOTO 20 999 END ================================================ FILE: 00_Alternate_Languages/65_Nim/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/65_Nim/MiniScript/nim.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/65_Nim/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/65_Nim/nim.bas ================================================ 100 PRINT TAB(33);"NIM" 110 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 120 PRINT:PRINT:PRINT 210 DIM A(100),B(100,10),D(2) 220 PRINT "THIS IS THE GAME OF NIM." 230 PRINT "DO YOU WANT INSTRUCTIONS"; 240 INPUT Z$ 250 IF Z$="NO" THEN 440 260 IF Z$="no" THEN 440 270 IF Z$="YES" THEN 310 280 IF Z$="yes" THEN 310 290 PRINT "PLEASE ANSWER YES OR NO" 300 GOTO 240 310 PRINT "THE GAME IS PLAYED WITH A NUMBER OF PILES OF OBJECTS." 320 PRINT "ANY NUMBER OF OBJECTS ARE REMOVED FROM ONE PILE BY YOU AND" 330 PRINT "THE MACHINE ALTERNATELY. ON YOUR TURN, YOU MAY TAKE" 340 PRINT "ALL THE OBJECTS THAT REMAIN IN ANY PILE, BUT YOU MUST" 350 PRINT "TAKE AT LEAST ONE OBJECT, AND YOU MAY TAKE OBJECTS FROM" 360 PRINT "ONLY ONE PILE ON A SINGLE TURN. YOU MUST SPECIFY WHETHER" 370 PRINT "WINNING IS DEFINED AS TAKING OR NOT TAKING THE LAST OBJECT," 380 PRINT "THE NUMBER OF PILES IN THE GAME, AND HOW MANY OBJECTS ARE" 390 PRINT "ORIGINALLY IN EACH PILE. EACH PILE MAY CONTAIN A" 400 PRINT "DIFFERENT NUMBER OF OBJECTS." 410 PRINT "THE MACHINE WILL SHOW ITS MOVE BY LISTING EACH PILE AND THE" 420 PRINT "NUMBER OF OBJECTS REMAINING IN THE PILES AFTER EACH OF ITS" 430 PRINT "MOVES." 440 PRINT 450 PRINT "ENTER WIN OPTION - 1 TO TAKE LAST, 2 TO AVOID LAST"; 460 INPUT W 470 IF W=1 THEN 490 480 IF W<>2 THEN 450 490 PRINT "ENTER NUMBER OF PILES"; 500 INPUT N 510 IF N>100 THEN 490 520 IF N<1 THEN 490 530 IF N<>INT(N) THEN 490 540 PRINT "ENTER PILE SIZES" 550 FOR I=1 TO N 560 PRINT I; 570 INPUT A(I) 580 IF A(I)>2000 THEN 560 590 IF A(I)<1 THEN 560 600 IF A(I)<>INT(A(I)) THEN 560 610 NEXT I 620 PRINT "DO YOU WANT TO MOVE FIRST"; 630 INPUT Q9$ 640 IF Q9$="YES" THEN 1450 650 IF Q9$="yes" THEN 1450 660 IF Q9$="NO" THEN 700 670 IF Q9$="no" THEN 700 680 PRINT "PLEASE ANSWER YES OR NO." 690 GOTO 630 700 IF W=1 THEN 940 710 LET C=0 720 FOR I=1 TO N 730 IF A(I)=0 THEN 770 740 LET C=C+1 750 IF C=3 THEN 840 760 LET D(C)=I 770 NEXT I 780 IF C=2 THEN 920 790 IF A(D(1))>1 THEN 820 800 PRINT "MACHINE LOSES" 810 GOTO 1640 820 PRINT "MACHINE WINS" 830 GOTO 1640 840 LET C=0 850 FOR I=1 TO N 860 IF A(I)>1 THEN 940 870 IF A(I)=0 THEN 890 880 LET C=C+1 890 NEXT I 900 IF C/2<>INT(C/2) THEN 800 910 GOTO 940 920 IF A(D(1))=1 THEN 820 930 IF A(D(2))=1 THEN 820 940 FOR I=1 TO N 950 LET E=A(I) 960 FOR J=0 TO 10 970 LET F=E/2 980 LET B(I,J)=2*(F-INT(F)) 990 LET E=INT(F) 1000 NEXT J 1010 NEXT I 1020 FOR J=10 TO 0 STEP -1 1030 LET C=0 1040 LET H=0 1050 FOR I=1 TO N 1060 IF B(I,J)=0 THEN 1110 1070 LET C=C+1 1080 IF A(I)<=H THEN 1110 1090 LET H=A(I) 1100 LET G=I 1110 NEXT I 1120 IF C/2<>INT(C/2) THEN 1190 1130 NEXT J 1140 LET E=INT(N*RND(1)+1) 1150 IF A(E)=0 THEN 1140 1160 LET F=INT(A(E)*RND(1)+1) 1170 LET A(E)=A(E)-F 1180 GOTO 1380 1190 LET A(G)=0 1200 FOR J=0 TO 10 1210 LET B(G,J)=0 1220 LET C=0 1230 FOR I=1 TO N 1240 IF B(I,J)=0 THEN 1260 1250 LET C=C+1 1260 NEXT I 1270 LET A(G)=A(G)+2*(C/2-INT(C/2))*2^J 1280 NEXT J 1290 IF W=1 THEN 1380 1300 LET C=0 1310 FOR I=1 TO N 1320 IF A(I)>1 THEN 1380 1330 IF A(I)=0 THEN 1350 1340 LET C=C+1 1350 NEXT I 1360 IF C/2<>INT(C/2) THEN 1380 1370 LET A(G)=1-A(G) 1380 PRINT "PILE SIZE" 1390 FOR I=1 TO N 1400 PRINT I;A(I) 1410 NEXT I 1420 IF W=2 THEN 1450 1430 GOSUB 1570 1440 IF Z=1 THEN 820 1450 PRINT "YOUR MOVE - PILE, NUMBER TO BE REMOVED"; 1460 INPUT X,Y 1470 IF X>N THEN 1450 1480 IF X<1 THEN 1450 1490 IF X<>INT(X) THEN 1450 1500 IF Y>A(X) THEN 1450 1510 IF Y<1 THEN 1450 1520 IF Y<>INT(Y) THEN 1450 1530 LET A(X)=A(X)-Y 1540 GOSUB 1570 1550 IF Z=1 THEN 800 1560 GOTO 700 1570 LET Z=0 1580 FOR I=1 TO N 1590 IF A(I)=0 THEN 1610 1600 RETURN 1610 NEXT I 1620 LET Z=1 1630 RETURN 1640 PRINT "do you want to play another game"; 1650 INPUT Q9$ 1660 IF Q9$="YES" THEN 1720 1670 IF Q9$="yes" THEN 1720 1680 IF Q9$="NO" THEN 1730 1690 IF Q9$="no" THEN 1730 1700 PRINT "PLEASE. YES OR NO." 1710 GOTO 1650 1720 GOTO 440 1730 END ================================================ FILE: 00_Alternate_Languages/66_Number/MiniScript/README.md ================================================ 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. ================================================ FILE: 00_Alternate_Languages/66_Number/MiniScript/number.ms ================================================ // 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 ================================================ FILE: 00_Alternate_Languages/66_Number/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/66_Number/number.bas ================================================ 1 PRINT TAB(33);"NUMBER" 2 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 3 PRINT:PRINT:PRINT 4 PRINT "YOU HAVE 100 POINTS. BY GUESSING NUMBERS FROM 1 TO 5, YOU" 5 PRINT "CAN GAIN OR LOSE POINTS DEPENDING UPON HOW CLOSE YOU GET TO" 6 PRINT "A RANDOM NUMBER SELECTED BY THE COMPUTER.": PRINT 7 PRINT "YOU OCCASIONALLY WILL GET A JACKPOT WHICH WILL DOUBLE(!)" 8 PRINT "YOUR POINT COUNT. YOU WIN WHEN YOU GET 500 POINTS." 9 PRINT: P=100 10 DEF FNR(X)=INT(5*RND(1)+1) 12 INPUT "GUESS A NUMBER FROM 1 TO 5";G 15 R=FNR(1) 16 S=FNR(1) 17 T=FNR(1) 18 U=FNR(1) 19 V=FNR(1) 20 IF G=R THEN 30 21 IF G=S THEN 40 22 IF G=T THEN 50 23 IF G=U THEN 60 24 IF G=V THEN 70 25 IF G>5 THEN 12 30 P=P-5 35 GOTO 80 40 P=P+5 45 GOTO 80 50 P=P+P 53 PRINT "YOU HIT THE JACKPOT!!!" 55 GOTO 80 60 P=P+1 65 GOTO 80 70 P=P-(P*.5) 80 IF P>500 THEN 90 82 PRINT "YOU HAVE";P;"POINTS.":PRINT 85 GOTO 12 90 PRINT "!!!!YOU WIN!!!! WITH ";P;"POINTS." 99 END ================================================ FILE: 00_Alternate_Languages/67_One_Check/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/67_One_Check/MiniScript/onecheck.ms ================================================ 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!!" ================================================ FILE: 00_Alternate_Languages/67_One_Check/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/67_One_Check/onecheck.bas ================================================ 2 PRINT TAB(30);"ONE CHECK" 4 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 6 PRINT: PRINT: PRINT 8 DIM A(64) 10 PRINT "SOLITAIRE CHECKER PUZZLE BY DAVID AHL" 15 PRINT 20 PRINT "48 CHECKERS ARE PLACED ON THE 2 OUTSIDE SPACES OF A" 25 PRINT "STANDARD 64-SQUARE CHECKERBOARD. THE OBJECT IS TO" 30 PRINT "REMOVE AS MANY CHECKERS AS POSSIBLE BY DIAGONAL JUMPS" 35 PRINT "(AS IN STANDARD CHECKERS). USE THE NUMBERED BOARD TO" 40 PRINT "INDICATE THE SQUARE YOU WISH TO JUMP FROM AND TO. ON" 45 PRINT "THE BOARD PRINTED OUT ON EACH TURN '1' INDICATES A" 50 PRINT "CHECKER AND '0' AN EMPTY SQUARE. WHEN YOU HAVE NO" 55 PRINT "POSSIBLE JUMPS REMAINING, INPUT A '0' IN RESPONSE TO" 60 PRINT "QUESTION 'JUMP FROM ?'" 62 PRINT 63 PRINT "HERE IS THE NUMERICAL BOARD:" 66 PRINT 70 FOR J=1 TO 57 STEP 8 74 PRINT J;TAB(4);J+1;TAB(8);J+2;TAB(12);J+3;TAB(16);J+4;TAB(20);J+5; 75 PRINT TAB(24);J+6;TAB(28);J+7 76 NEXT J 77 PRINT 78 PRINT "AND HERE IS THE OPENING POSITION OF THE CHECKERS." 79 PRINT 80 FOR J=1 TO 64 82 A(J)=1 84 NEXT J 86 FOR J=19 TO 43 STEP 8 88 FOR I=J TO J+3 90 A(I)=0 92 NEXT I 94 NEXT J 96 M=0 98 GOTO 340 100 INPUT "JUMP FROM";F 105 IF F=0 THEN 500 110 INPUT "TO";T 112 PRINT 118 REM *** CHECK LEGALITY OF MOVE 120 F1=INT((F-1)/8) 130 F2=F-8*F1 140 T1=INT((T-1)/8) 150 T2=T-8*T1 160 IF F1>7 THEN 230 170 IF T1>7 THEN 230 180 IF F2>8 THEN 230 190 IF T2>8 THEN 230 200 IF ABS(F1-T1)<>2 THEN 230 210 IF ABS(F2-T2)<>2 THEN 230 212 IF A((T+F)/2)=0 THEN 230 215 IF A(F)=0 THEN 230 220 IF A(T)=1 THEN 230 225 GOTO 250 230 PRINT "ILLEGAL MOVE. TRY AGAIN..." 240 GOTO 100 245 REM *** UPDATE BOARD 250 A(T)=1 260 A(F)=0 270 A((T+F)/2)=0 290 M=M+1 310 REM *** PRINT BOARD 340 FOR J=1 TO 57 STEP 8 350 FOR I=J TO J+7 360 PRINT A(I); 370 NEXT I 380 PRINT 390 NEXT J 400 PRINT 410 GOTO 100 490 REM *** END GAME SUMMARY 500 S=0 510 FOR I=1 TO 64 520 S=S+A(I) 530 NEXT I 540 PRINT:PRINT "YOU MADE";M;"JUMPS AND HAD";S;"PIECES" 550 PRINT "REMAINING ON THE BOARD." 560 PRINT 562 INPUT "TRY AGAIN";A$ 570 IF A$="YES" THEN 70 575 IF A$="NO" THEN 600 580 PRINT "PLEASE ANSWER 'YES' OR 'NO'." 590 GOTO 562 600 PRINT 610 PRINT "O.K. HOPE YOU HAD FUN!!" 999 END ================================================ FILE: 00_Alternate_Languages/68_Orbit/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/68_Orbit/MiniScript/orbit.ms ================================================ 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." ================================================ FILE: 00_Alternate_Languages/68_Orbit/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/68_Orbit/orbit.bas ================================================ 2 PRINT TAB(33);"ORBIT" 4 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 6 PRINT:PRINT:PRINT 10 PRINT "SOMEWHERE ABOVE YOUR PLANET IS A ROMULAN SHIP." 15 PRINT 20 PRINT "THE SHIP IS IN A CONSTANT POLAR ORBIT. ITS" 25 PRINT "DISTANCE FROM THE CENTER OF YOUR PLANET IS FROM" 30 PRINT "10,000 TO 30,000 MILES AND AT ITS PRESENT VELOCITY CAN" 31 PRINT "CIRCLE YOUR PLANET ONCE EVERY 12 TO 36 HOURS." 35 PRINT 40 PRINT "UNFORTUNATELY, THEY ARE USING A CLOAKING DEVICE SO" 45 PRINT "YOU ARE UNABLE TO SEE THEM, BUT WITH A SPECIAL" 50 PRINT "INSTRUMENT YOU CAN TELL HOW NEAR THEIR SHIP YOUR" 55 PRINT "PHOTON BOMB EXPLODED. YOU HAVE SEVEN HOURS UNTIL THEY" 60 PRINT "HAVE BUILT UP SUFFICIENT POWER IN ORDER TO ESCAPE" 65 PRINT "YOUR PLANET'S GRAVITY." 70 PRINT 75 PRINT "YOUR PLANET HAS ENOUGH POWER TO FIRE ONE BOMB AN HOUR." 80 PRINT 85 PRINT "AT THE BEGINNING OF EACH HOUR YOU WILL BE ASKED TO GIVE AN" 90 PRINT "ANGLE (BETWEEN 0 AND 360) AND A DISTANCE IN UNITS OF" 95 PRINT "100 MILES (BETWEEN 100 AND 300), AFTER WHICH YOUR BOMB'S" 100 PRINT "DISTANCE FROM THE ENEMY SHIP WILL BE GIVEN." 105 PRINT 110 PRINT "AN EXPLOSION WITHIN 5,000 MILES OF THE ROMULAN SHIP" 111 PRINT "WILL DESTROY IT." 114 PRINT 115 PRINT "BELOW IS A DIAGRAM TO HELP YOU VISUALIZE YOUR PLIGHT." 116 PRINT 117 PRINT 168 PRINT " 90" 170 PRINT " 0000000000000" 171 PRINT " 0000000000000000000" 172 PRINT " 000000 000000" 173 PRINT " 00000 00000" 174 PRINT " 00000 XXXXXXXXXXX 00000" 175 PRINT " 00000 XXXXXXXXXXXXX 00000" 176 PRINT " 0000 XXXXXXXXXXXXXXX 0000" 177 PRINT " 0000 XXXXXXXXXXXXXXXXX 0000" 178 PRINT " 0000 XXXXXXXXXXXXXXXXXXX 0000" 179 PRINT "180<== 00000 XXXXXXXXXXXXXXXXXXX 00000 ==>0" 180 PRINT " 0000 XXXXXXXXXXXXXXXXXXX 0000" 181 PRINT " 0000 XXXXXXXXXXXXXXXXX 0000" 182 PRINT " 0000 XXXXXXXXXXXXXXX 0000" 183 PRINT " 00000 XXXXXXXXXXXXX 00000" 184 PRINT " 00000 XXXXXXXXXXX 00000" 185 PRINT " 00000 00000" 186 PRINT " 000000 000000" 187 PRINT " 0000000000000000000" 188 PRINT " 0000000000000" 190 PRINT " 270" 192 PRINT 195 PRINT "X - YOUR PLANET" 196 PRINT "O - THE ORBIT OF THE ROMULAN SHIP" 197 PRINT 198 PRINT "ON THE ABOVE DIAGRAM, THE ROMULAN SHIP IS CIRCLING" 199 PRINT "COUNTERCLOCKWISE AROUND YOUR PLANET. DON'T FORGET THAT" 200 PRINT "WITHOUT SUFFICIENT POWER THE ROMULAN SHIP'S ALTITUDE" 210 PRINT "AND ORBITAL RATE WILL REMAIN CONSTANT." 220 PRINT 230 PRINT "GOOD LUCK. THE FEDERATION IS COUNTING ON YOU." 270 A=INT(360*RND(1)) 280 D=INT(200*RND(1)+200) 290 R=INT(20*RND(1)+10) 300 H=0 310 IF H=7 THEN 490 320 H=H+1 325 PRINT 326 PRINT 330 PRINT "THIS IS HOUR";H;", AT WHAT ANGLE DO YOU WISH TO SEND" 335 PRINT "YOUR PHOTON BOMB"; 340 INPUT A1 350 PRINT "HOW FAR OUT DO YOU WISH TO DETONATE IT"; 360 INPUT D1 365 PRINT 366 PRINT 370 A=A+R 380 IF A<360 THEN 400 390 A=A-360 400 T=ABS(A-A1) 410 IF T<180 THEN 430 420 T=360-T 430 C=SQR(D*D+D1*D1-2*D*D1*COS(T*3.14159/180)) 440 PRINT "YOUR PHOTON BOMB EXPLODED";C;"*10^2 MILES FROM THE" 445 PRINT "ROMULAN SHIP." 450 IF C<=50 THEN 470 460 GOTO 310 470 PRINT "YOU HAVE SUCCESFULLY COMPLETED YOUR MISSION." 480 GOTO 500 490 PRINT "YOU HAVE ALLOWED THE ROMULANS TO ESCAPE." 500 PRINT "ANOTHER ROMULAN SHIP HAS GONE INTO ORBIT." 510 PRINT "DO YOU WISH TO TRY TO DESTROY IT"; 520 INPUT C$ 530 IF C$="YES" THEN 270 540 PRINT "GOOD BYE." 999 END ================================================ FILE: 00_Alternate_Languages/69_Pizza/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/69_Pizza/MiniScript/pizza.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/69_Pizza/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/69_Pizza/pizza.bas ================================================ 5 PRINT TAB(33);"PIZZA" 10 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 15 PRINT: PRINT: PRINT 20 DIM S$(16),M$(4) 30 PRINT "PIZZA DELIVERY GAME": PRINT 50 INPUT "WHAT IS YOUR FIRST NAME";N$: PRINT 80 PRINT "HI, ";N$;". IN THIS GAME YOU ARE TO TAKE ORDERS" 90 PRINT "FOR PIZZAS. THEN YOU ARE TO TELL A DELIVERY BOY" 100 PRINT "WHERE TO DELIVER THE ORDERED PIZZAS.": PRINT: PRINT 140 FOR I=1 TO 16 150 READ S$(I) 160 NEXT I 170 FOR I=1 TO 4 180 READ M$(I) 190 NEXT I 200 DATA "A","B","C","D","E","F","G","H","I","J","K","L","M","N","O" 210 DATA "P","1","2","3","4" 230 PRINT "MAP OF THE CITY OF HYATTSVILLE": PRINT 250 PRINT " -----1-----2-----3-----4-----" 260 K=4 270 FOR I=1 TO 4 280 PRINT "-": PRINT "-": PRINT"-": PRINT "-" 320 PRINT M$(K); 330 S1=16-4*I+1 340 PRINT " ";S$(S1);" ";S$(S1+1);" ";S$(S1+2);" "; 350 PRINT S$(S1+3);" ";M$(K) 380 K=K-1 390 NEXT I 400 PRINT "-": PRINT "-": PRINT "-": PRINT "-" 440 PRINT " -----1-----2-----3-----4-----": PRINT 460 PRINT "THE OUTPUT IS A MAP OF THE HOMES WHERE" 470 PRINT "YOU ARE TO SEND PIZZAS.": PRINT 490 PRINT "YOUR JOB IS TO GIVE A TRUCK DRIVER" 500 PRINT "THE LOCATION OR COORDINATES OF THE" 510 PRINT "HOME ORDERING THE PIZZA.": PRINT 520 INPUT "DO YOU NEED MORE DIRECTIONS";A$ 530 IF A$="YES" THEN 590 540 IF A$="NO" THEN 750 550 PRINT "'YES' OR 'NO' PLEASE, NOW THEN,": GOTO 520 590 PRINT: PRINT "SOMEBODY WILL ASK FOR A PIZZA TO BE" 600 PRINT "DELIVERED. THEN A DELIVERY BOY WILL" 610 PRINT "ASK YOU FOR THE LOCATION.":PRINT " EXAMPLE:" 620 PRINT "THIS IS J. PLEASE SEND A PIZZA." 640 PRINT "DRIVER TO ";N$;". WHERE DOES J LIVE?" 650 PRINT "YOUR ANSWER WOULD BE 2,3": PRINT 660 INPUT "UNDERSTAND";A$ 670 IF A$="YES" THEN 690 680 PRINT "THIS JOB IS DEFINITELY TOO DIFFICULT FOR YOU. THANKS ANYWAY" 685 GOTO 999 690 PRINT "GOOD. YOU ARE NOW READY TO START TAKING ORDERS.": PRINT 700 PRINT "GOOD LUCK!!": PRINT 750 FOR I=1 TO 5 760 S=INT(RND(1)*16+1): PRINT 770 PRINT "HELLO ";N$;"'S PIZZA. THIS IS ";S$(S);"."; 775 PRINT " PLEASE SEND A PIZZA." 780 PRINT " DRIVER TO ";N$;": WHERE DOES ";S$(S);" LIVE"; 790 INPUT A(1),A(2) 870 T=A(1)+(A(2)-1)*4 880 IF T=S THEN 920 890 PRINT "THIS IS ";S$(T);". I DID NOT ORDER A PIZZA." 900 PRINT "I LIVE AT ";A(1);",";A(2) 910 GOTO 780 920 PRINT "HELLO "N$;". THIS IS ";S$(S);", THANKS FOR THE PIZZA." 930 NEXT I 940 PRINT: INPUT "DO YOU WANT TO DELIVER MORE PIZZAS";A$ 960 IF A$="YES" THEN 750 970 PRINT: PRINT "O.K. ";N$;", SEE YOU LATER!":PRINT 999 END ================================================ FILE: 00_Alternate_Languages/70_Poetry/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/70_Poetry/MiniScript/poetry.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/70_Poetry/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/70_Poetry/poetry.bas ================================================ 10 PRINT TAB(30);"POETRY" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 30 PRINT:PRINT:PRINT 90 ON I GOTO 100,101,102,103,104 100 PRINT "MIDNIGHT DREARY";:GOTO 210 101 PRINT "FIERY EYES";:GOTO 210 102 PRINT "BIRD OR FIEND";:GOTO 210 103 PRINT "THING OF EVIL";:GOTO 210 104 PRINT "PROPHET";:GOTO 210 110 ON I GOTO 111,112,113,114,115 111 PRINT "BEGUILING ME";:U=2:GOTO 210 112 PRINT "THRILLED ME";:GOTO 210 113 PRINT "STILL SITTING....";:GOTO 212 114 PRINT "NEVER FLITTING";:U=2:GOTO 210 115 PRINT "BURNED";:GOTO 210 120 ON I GOTO 121,122,123,124,125 121 PRINT "AND MY SOUL";:GOTO 210 122 PRINT "DARKNESS THERE";:GOTO 210 123 PRINT "SHALL BE LIFTED";:GOTO 210 124 PRINT "QUOTH THE RAVEN";:GOTO 210 125 IF U=0 THEN 210 126 PRINT "SIGN OF PARTING";:GOTO 210 130 ON I GOTO 131,132,133,134,135 131 PRINT "NOTHING MORE";:GOTO 210 132 PRINT "YET AGAIN";:GOTO 210 133 PRINT "SLOWLY CREEPING";:GOTO 210 134 PRINT "...EVERMORE";:GOTO 210 135 PRINT "NEVERMORE"; 210 IF U=0 OR RND(1)>.19 THEN 212 211 PRINT ",";:U=2 212 IF RND(1)>.65 THEN 214 213 PRINT " ";:U=U+1:GOTO 215 214 PRINT : U=0 215 I=INT(INT(10*RND(1))/2)+1 220 J=J+1 : K=K+1 230 IF U>0 OR INT(J/2)<>J/2 THEN 240 235 PRINT " "; 240 ON J GOTO 90,110,120,130,250 250 J=0 : PRINT : IF K>20 THEN 270 260 GOTO 215 270 PRINT : U=0 : K=0 : GOTO 110 999 END ================================================ FILE: 00_Alternate_Languages/70_Poetry/poetry.pl ================================================ #!/usr/bin/perl #use strict; # Automatic converted by bas2perl.pl # Too much spaguetti code to be properly converted. print ' 'x 30 . "POETRY\n"; print ' 'x 15 . "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n"; print "\n"; print "\n"; print "\n"; Line90: if ($I==1) { goto Line100; } elsif ($I==2) { goto Line101; } elsif ($I==3) { goto Line102; } elsif ($I==4) { goto Line103; } elsif ($I==5) { goto Line104; } ; Line100: print "MIDNIGHT DREARY"; goto Line210; Line101: print "FIERY EYES"; goto Line210; Line102: print "BIRD OR FIEND"; goto Line210; Line103: print "THING OF EVIL"; goto Line210; Line104: print "PROPHET"; goto Line210; Line110: if ($I==1) { goto Line111; } elsif ($I==2) { goto Line112; } elsif ($I==3) { goto Line113; } elsif ($I==4) { goto Line114; } elsif ($I==5) { goto Line115; } ; Line111: print "BEGUILING ME"; $U=2; goto Line210; Line112: print "THRILLED ME"; goto Line210; Line113: print "STILL SITTING...."; goto Line212; Line114: print "NEVER FLITTING"; $U=2; goto Line210; Line115: print "BURNED"; goto Line210; Line120: if ($I==1) { goto Line121; } elsif ($I==2) { goto Line122; } elsif ($I==3) { goto Line123; } elsif ($I==4) { goto Line124; } elsif ($I==5) { goto Line125; } ; Line121: print "AND MY SOUL"; goto Line210; Line122: print "DARKNESS THERE"; goto Line210; Line123: print "SHALL BE LIFTED"; goto Line210; Line124: print "QUOTH THE RAVEN"; goto Line210; Line125: if ($U==0) { goto Line210; } print "SIGN OF PARTING"; goto Line210; Line130: if ($I==1) { goto Line131; } elsif ($I==2) { goto Line132; } elsif ($I==3) { goto Line133; } elsif ($I==4) { goto Line134; } elsif ($I==5) { goto Line135; } ; Line131: print "NOTHING MORE"; goto Line210; Line132: print "YET AGAIN"; goto Line210; Line133: print "SLOWLY CREEPING"; goto Line210; Line134: print "...EVERMORE"; goto Line210; Line135: print "NEVERMORE"; Line210: if ($U==0 || rand(1)>.19) { goto Line212; } print ","; $U=2; Line212: if (rand(1)>.65) { goto Line214; } print " "; $U=$U+1; goto Line215; Line214: print "\n"; $U=0; Line215: $I=int(int(10*rand(1))/2)+1; $J=$J+1; $K=$K+1; if ($U>0 || int($J/2)!=$J/2) { goto Line240; } print " "; Line240: if ($J==1) { goto Line90; } elsif ($J==2) { goto Line110; } elsif ($J==3) { goto Line120; } elsif ($J==4) { goto Line130; } elsif ($J==5) { goto Line250; } ; Line250: $J=0; print "\n"; if ($K>20) { goto Line270; } goto Line215; Line270: print "\n"; $U=0; $K=0; goto Line110; exit; ================================================ FILE: 00_Alternate_Languages/71_Poker/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/71_Poker/MiniScript/poker.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/71_Poker/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/71_Poker/poker.bas ================================================ 2 PRINT TAB(33);"POKER" 4 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 6 PRINT: PRINT: PRINT 10 DIM A(50),B(15) 20 DEF FNA(X)=INT(10*RND(1)) 30 DEF FNB(X)=X-100*INT(X/100) 40 PRINT "WELCOME TO THE CASINO. WE EACH HAVE $200." 50 PRINT "I WILL OPEN THE BETTING BEFORE THE DRAW; YOU OPEN AFTER." 60 PRINT "TO FOLD BET 0; TO CHECK BET .5." 70 PRINT "ENOUGH TALK -- LET'S GET DOWN TO BUSINESS." 80 PRINT 90 LET O=1 100 LET C=200 110 LET S=200 120 LET P=0 130 REM 140 PRINT 150 IF C<=5 THEN 3670 160 PRINT "THE ANTE IS $5. I WILL DEAL:" 170 PRINT 180 IF S>5 THEN 200 190 GOSUB 3830 200 LET P=P+10 210 LET S=S-5 220 LET C=C-5 230 FOR Z=1 TO 10 240 GOSUB 1740 250 NEXT Z 260 PRINT "YOUR HAND:" 270 N=1 280 GOSUB 1850 290 N=6 300 I=2 310 GOSUB 2170 320 PRINT 330 IF I<>6 THEN 470 340 IF FNA(0)<=7 THEN 370 350 LET X=11100 360 GOTO 420 370 IF FNA(0)<=7 THEN 400 380 LET X=11110 390 GOTO 420 400 IF FNA(0)>=1 THEN 450 410 X=11111 420 I=7 430 Z=23 440 GOTO 580 450 Z=1 460 GOTO 510 470 IF U>=13 THEN 540 480 IF FNA(0)>=2 THEN 500 490 GOTO 420 500 Z=0 510 K=0 520 PRINT "I CHECK." 530 GOTO 620 540 IF U<=16 THEN 570 550 Z=2 560 IF FNA(0)>=1 THEN 580 570 Z=35 580 V=Z+FNA(0) 590 GOSUB 3480 600 PRINT "I'LL OPEN WITH $"V 610 K=V 620 GOSUB 3050 630 GOSUB 650 640 GOTO 820 650 IF I<>3 THEN 760 660 PRINT 670 PRINT "I WIN." 680 C=C+P 690 PRINT "NOW I HAVE $"C"AND YOU HAVE $"S 700 PRINT "DO YOU WISH TO CONTINUE"; 710 INPUT H$ 720 IF H$="YES" THEN 120 730 IF H$="NO" THEN 4100 740 PRINT "ANSWER YES OR NO, PLEASE." 750 GOTO 700 760 IF I<>4 THEN 810 770 PRINT 780 PRINT "YOU WIN." 790 S=S+P 800 GOTO 690 810 RETURN 820 PRINT 830 PRINT "NOW WE DRAW -- HOW MANY CARDS DO YOU WANT"; 840 INPUT T 850 IF T=0 THEN 980 860 Z=10 870 IF T<4 THEN 900 880 PRINT "YOU CAN'T DRAW MORE THAN THREE CARDS." 890 GOTO 840 900 PRINT "WHAT ARE THEIR NUMBERS:" 910 FOR Q=1 TO T 920 INPUT U 930 GOSUB 1730 940 NEXT Q 950 PRINT "YOUR NEW HAND:" 960 N=1 970 GOSUB 1850 980 Z=10+T 990 FOR U=6 TO 10 1000 IF INT(X/10^(U-6))<>10*INT(X/10^(U-5)) THEN 1020 1010 GOSUB 1730 1020 NEXT U 1030 PRINT 1040 PRINT "I AM TAKING"Z-10-T"CARD"; 1050 IF Z=11+T THEN 1090 1060 PRINT "S" 1070 PRINT 1080 GOTO 1100 1090 PRINT 1100 N=6 1110 V=I 1120 I=1 1130 GOSUB 2170 1140 B=U 1150 M=D 1160 IF V<>7 THEN 1190 1170 Z=28 1180 GOTO 1330 1190 IF I<>6 THEN 1220 1200 Z=1 1210 GOTO 1330 1220 IF U>=13 THEN 1270 1230 Z=2 1240 IF FNA(0)<>6 THEN 1260 1250 Z=19 1260 GOTO 1330 1270 IF U>=16 THEN 1320 1280 Z=19 1290 IF FNA(0)<>8 THEN 1310 1300 Z=11 1310 GOTO 1330 1320 Z=2 1330 K=0 1340 GOSUB 3050 1350 IF T<>.5 THEN 1450 1360 IF V=7 THEN 1400 1370 IF I<>6 THEN 1400 1380 PRINT "I'LL CHECK" 1390 GOTO 1460 1400 V=Z+FNA(0) 1410 GOSUB 3480 1420 PRINT "I'LL BET $"V 1430 K=V 1440 GOSUB 3060 1450 GOSUB 650 1460 PRINT 1470 PRINT "NOW WE COMPARE HANDS:" 1480 J$=H$ 1490 K$=I$ 1500 PRINT "MY HAND:" 1510 N=6 1520 GOSUB 1850 1530 N=1 1540 GOSUB 2170 1550 PRINT 1560 PRINT "YOU HAVE "; 1570 K=D 1580 GOSUB 3690 1590 H$=J$ 1600 I$=K$ 1610 K=M 1620 PRINT "AND I HAVE "; 1630 GOSUB 3690 1640 IF B>U THEN 670 1650 IF U>B THEN 780 1660 IF H$="A FLUS" THEN 1700 1662 IF FNB(M)<FNB(D) THEN 780 1664 IF FNB(M)>FNB(D) THEN 670 1670 PRINT "THE HAND IS DRAWN." 1680 PRINT "ALL $"P"REMAINS IN THE POT." 1690 GOTO 140 1700 IF FNB(M)>FNB(D) THEN 670 1710 IF FNB(D)>FNB(M) THEN 780 1720 GOTO 1670 1730 Z=Z+1 1740 A(Z)=100*INT(4*RND(1))+INT(100*RND(1)) 1750 IF INT(A(Z)/100)>3 THEN 1740 1760 IF A(Z)-100*INT(A(Z)/100)>12 THEN 1740 1765 IF Z=1 THEN 1840 1770 FOR K=1 TO Z-1 1780 IF A(Z)=A(K) THEN 1740 1790 NEXT K 1800 IF Z<=10 THEN 1840 1810 N=A(U) 1820 A(U)=A(Z) 1830 A(Z)=N 1840 RETURN 1850 FOR Z=N TO N+4 1860 PRINT Z"-- "; 1870 GOSUB 1950 1880 PRINT " OF"; 1890 GOSUB 2070 1900 IF Z/2<>INT(Z/2) THEN 1920 1910 PRINT 1920 NEXT Z 1930 PRINT 1940 RETURN 1950 K=FNB(A(Z)) 1960 IF K<>9 THEN 1980 1970 PRINT "JACK"; 1980 IF K<>10 THEN 2000 1990 PRINT "QUEEN"; 2000 IF K<>11 THEN 2020 2010 PRINT "KING"; 2020 IF K<>12 THEN 2040 2030 PRINT "ACE"; 2040 IF K>=9 THEN 2060 2050 PRINT K+2; 2060 RETURN 2070 K=INT(A(Z)/100) 2080 IF K<>0 THEN 2100 2090 PRINT " CLUBS", 2100 IF K<>1 THEN 2120 2110 PRINT " DIAMONDS", 2120 IF K<>2 THEN 2140 2130 PRINT " HEARTS", 2140 IF K<>3 THEN 2160 2150 PRINT " SPADES", 2160 RETURN 2170 U=0 2180 FOR Z=N TO N+4 2190 B(Z)=FNB(A(Z)) 2200 IF Z=N+4 THEN 2230 2210 IF INT(A(Z)/100)<>INT(A(Z+1)/100) THEN 2230 2220 U=U+1 2230 NEXT Z 2240 IF U<>4 THEN 2310 2250 X=11111 2260 D=A(N) 2270 H$="A FLUS" 2280 I$="H IN" 2290 U=15 2300 RETURN 2310 FOR Z=N TO N+3 2320 FOR K=Z+1 TO N+4 2330 IF B(Z)<=B(K) THEN 2390 2340 X=A(Z) 2350 A(Z)=A(K) 2360 B(Z)=B(K) 2370 A(K)=X 2380 B(K)=A(K)-100*INT(A(K)/100) 2390 NEXT K 2400 NEXT Z 2410 X=0 2420 FOR Z=N TO N+3 2430 IF B(Z)<>B(Z+1) THEN 2470 2440 X=X+11*10^(Z-N) 2450 D=A(Z) 2460 GOSUB 2760 2470 NEXT Z 2480 IF X<>0 THEN 2620 2490 IF B(N)+3<>B(N+3) THEN 2520 2500 X=1111 2510 U=10 2520 IF B(N+1)+3<>B(N+4) THEN 2620 2530 IF U<>10 THEN 2600 2540 U=14 2550 H$="STRAIG" 2560 I$="HT" 2570 X=11111 2580 D=A(N+4) 2590 RETURN 2600 U=10 2610 X=11110 2620 IF U>=10 THEN 2690 2630 D=A(N+4) 2640 H$="SCHMAL" 2650 I$="TZ, " 2660 U=9 2670 X=11000 2680 GOTO 2740 2690 IF U<>10 THEN 2720 2700 IF I=1 THEN 2740 2710 GOTO 2750 2720 IF U>12 THEN 2750 2730 IF FNB(D)>6 THEN 2750 2740 I=6 2750 RETURN 2760 IF U>=11 THEN 2810 2770 U=11 2780 H$="A PAIR" 2790 I$=" OF " 2800 RETURN 2810 IF U<>11 THEN 2910 2820 IF B(Z)<>B(Z-1) THEN 2870 2830 H$="THREE" 2840 I$=" " 2850 U=13 2860 RETURN 2870 H$="TWO P" 2880 I$="AIR, " 2890 U=12 2900 RETURN 2910 IF U>12 THEN 2960 2920 U=16 2930 H$="FULL H" 2940 I$="OUSE, " 2950 RETURN 2960 IF B(Z)<>B(Z-1) THEN 3010 2970 U=17 2980 H$="FOUR" 2990 I$=" " 3000 RETURN 3010 U=16 3020 H$="FULL H" 3030 I$="OUSE, " 3040 RETURN 3050 G=0 3060 PRINT:PRINT "WHAT IS YOUR BET"; 3070 INPUT T 3080 IF T-INT(T)=0 THEN 3140 3090 IF K<>0 THEN 3120 3100 IF G<>0 THEN 3120 3110 IF T=.5 THEN 3410 3120 PRINT "NO SMALL CHANGE, PLEASE." 3130 GOTO 3060 3140 IF S-G-T>=0 THEN 3170 3150 GOSUB 3830 3160 GOTO 3060 3170 IF T<>0 THEN 3200 3180 I=3 3190 GOTO 3380 3200 IF G+T>=K THEN 3230 3210 PRINT "IF YOU CAN'T SEE MY BET, THEN FOLD." 3220 GOTO 3060 3230 G=G+T 3240 IF G=K THEN 3380 3250 IF Z<>1 THEN 3420 3260 IF G>5 THEN 3300 3270 IF Z>=2 THEN 3350 3280 V=5 3290 GOTO 3420 3300 IF Z=1 THEN 3320 3310 IF T<=25 THEN 3350 3320 I=4 3330 PRINT "I FOLD." 3340 RETURN 3350 IF Z=2 THEN 3430 3360 PRINT "I'LL SEE YOU." 3370 K=G 3380 S=S-G 3390 C=C-K 3400 P=P+G+K 3410 RETURN 3420 IF G>3*Z THEN 3350 3430 V=G-K+FNA(0) 3440 GOSUB 3480 3450 PRINT "I'LL SEE YOU, AND RAISE YOU"V 3460 K=G+V 3470 GOTO 3060 3480 IF C-G-V>=0 THEN 3660 3490 IF G<>0 THEN 3520 3500 V=C 3510 RETURN 3520 IF C-G>=0 THEN 3360 3530 IF (O/2)<>INT(O/2) THEN 3600 3540 PRINT "WOULD YOU LIKE TO BUY BACK YOUR WATCH FOR $50"; 3550 INPUT J$ 3560 IF LEFT$(J$,1)="N" THEN 3600 3570 C=C+50 3580 O=O/2 3590 RETURN 3600 IF O/3<>INT(O/3) THEN 3670 3610 PRINT "WOULD YOU LIKE TO BUY BACK YOUR TIE TACK FOR $50"; 3620 INPUT J$ 3630 IF LEFT$(J$,1)="N" THEN 3670 3640 C=C+50 3650 O=O/3 3660 RETURN 3670 PRINT "I'M BUSTED. CONGRATULATIONS!" 3680 STOP 3690 PRINT H$;I$; 3700 IF H$<>"A FLUS" THEN 3750 3710 K=INT(K/100) 3720 GOSUB 2080 3730 PRINT 3740 RETURN 3750 K=FNB(K) 3760 GOSUB 1960 3770 IF H$="SCHMAL" THEN 3790 3780 IF H$<>"STRAIG" THEN 3810 3790 PRINT " HIGH" 3800 RETURN 3810 PRINT "'S" 3820 RETURN 3830 PRINT 3840 PRINT "YOU CAN'T BET WITH WHAT YOU HAVEN'T GOT." 3850 IF O/2=INT(O/2) THEN 3970 3860 PRINT "WOULD YOU LIKE TO SELL YOUR WATCH"; 3870 INPUT J$ 3880 IF LEFT$(J$,1)="N" THEN 3970 3890 IF FNA(0)>=7 THEN 3930 3900 PRINT "I'LL GIVE YOU $75 FOR IT." 3910 S=S+75 3920 GOTO 3950 3930 PRINT "THAT'S A PRETTY CRUMMY WATCH - I'LL GIVE YOU $25." 3940 S=S+25 3950 O=O*2 3960 RETURN 3970 IF O/3<>INT(O/3) THEN 4090 3980 PRINT "WILL YOU PART WITH THAT DIAMOND TIE TACK": 3990 INPUT J$ 4000 IF LEFT$(J$,1)="N" THEN 4080 4010 IF FNA(0)>=6 THEN 4050 4020 PRINT "YOU ARE NOW $100 RICHER." 4030 S=S+100 4040 GOTO 4070 4050 PRINT "IT'S PASTE. $25." 4060 S=S+25 4070 O=O*3 4080 RETURN 4090 PRINT "YOUR WAD IS SHOT. SO LONG, SUCKER!" 4100 END ================================================ FILE: 00_Alternate_Languages/72_Queen/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/72_Queen/MiniScript/queen.ms ================================================ 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." ================================================ FILE: 00_Alternate_Languages/72_Queen/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/72_Queen/queen.bas ================================================ 1 PRINT TAB(33);"QUEEN" 2 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 3 PRINT:PRINT:PRINT 10 DIM S(64) 11 FOR I=1 TO 64 12 READ S(I) 13 NEXT I 14 DATA 81, 71, 61, 51, 41, 31, 21, 11 15 DATA 92, 82, 72, 62, 52, 42, 32, 22 16 DATA 103, 93, 83, 73, 63, 53, 43, 33 17 DATA 114, 104, 94, 84, 74, 64, 54, 44 18 DATA 125, 115, 105, 95, 85, 75, 65, 55 19 DATA 136, 126, 116, 106, 96, 86, 76, 66 20 DATA 147, 137, 127, 117, 107, 97, 87, 77 21 DATA 158, 148, 138, 128, 118, 108, 98, 88 22 INPUT "DO YOU WANT INSTRUCTIONS";W$ 23 IF W$="NO" THEN 30 24 IF W$="YES" THEN 28 25 PRINT "PLEASE ANSWER 'YES' OR 'NO'." 26 GOTO 22 28 GOSUB 5000 29 GOTO 100 30 GOSUB 5160 90 REM ERROR CHECKS 100 PRINT "WHERE WOULD YOU LIKE TO START"; 110 INPUT M1 115 IF M1=0 THEN 232 120 T1=INT(M1/10) 130 U1=M1-10*T1 140 IF U1=1 THEN 200 150 IF U1=T1 THEN 200 160 PRINT "PLEASE READ THE DIRECTIONS AGAIN." 170 PRINT "YOU HAVE BEGUN ILLEGALLY." 175 PRINT 180 GOTO 100 200 GOSUB 2000 210 PRINT "COMPUTER MOVES TO SQUARE";M 215 IF M=158 THEN 3400 220 PRINT "WHAT IS YOUR MOVE"; 230 INPUT M1 231 IF M1<>0 THEN 239 232 PRINT 233 PRINT "IT LOOKS LIKE I HAVE WON BY FORFEIT." 234 PRINT 235 GOTO 4000 239 IF M1<=M THEN 3200 240 T1=INT(M1/10) 250 U1=M1-10*T1 260 P=U1-U 270 IF P<>0 THEN 300 280 L=T1-T 290 IF L<=0 THEN 3200 295 GOTO 200 300 IF T1-T <>P THEN 320 310 GOTO 200 320 IF T1-T <>2*P THEN 3200 330 GOTO 200 1990 REM LOCATE MOVE FOR COMPUTER 2000 IF M1=41 THEN 2180 2010 IF M1=44 THEN 2180 2020 IF M1=73 THEN 2180 2030 IF M1=75 THEN 2180 2040 IF M1=126 THEN 2180 2050 IF M1=127 THEN 2180 2060 IF M1=158 THEN 3300 2065 C=0 2070 FOR K=7 TO 1 STEP -1 2080 U=U1 2090 T=T1+K 2100 GOSUB 3500 2105 IF C=1 THEN 2160 2110 U=U+K 2120 GOSUB 3500 2125 IF C=1 THEN 2160 2130 T=T+K 2140 GOSUB 3500 2145 IF C=1 THEN 2160 2150 NEXT K 2155 GOTO 2180 2160 C=0 2170 RETURN 2180 GOSUB 3000 2190 RETURN 2990 REM RANDOM MOVE 3000 Z=RND(1) 3010 IF Z>.6 THEN 3110 3020 IF Z>.3 THEN 3070 3030 U=U1 3040 T=T1+1 3050 M=10*T+U 3060 RETURN 3070 U=U1+1 3080 T=T1+2 3090 M=10*T+U 3100 RETURN 3110 U=U1+1 3120 T=T1+1 3130 M=10*T+U 3140 RETURN 3190 REM ILLEGAL MOVE MESSAGE 3200 PRINT 3210 PRINT "Y O U C H E A T . . . TRY AGAIN"; 3220 GOTO 230 3290 REM PLAYER WINS 3300 PRINT 3310 PRINT "C O N G R A T U L A T I O N S . . ." 3320 PRINT 3330 PRINT "YOU HAVE WON--VERY WELL PLAYED." 3340 PRINT "IT LOOKS LIKE I HAVE MET MY MATCH." 3350 PRINT "THANKS FOR PLAYING---I CAN'T WIN ALL THE TIME." 3360 PRINT 3370 GOTO 4000 3390 REM COMPUTER WINS 3400 PRINT 3410 PRINT "NICE TRY, BUT IT LOOKS LIKE I HAVE WON." 3420 PRINT "THANKS FOR PLAYING." 3430 PRINT 3440 GOTO 4000 3490 REM TEST FOR COMPUTER MOVE 3500 M=10*T+U 3510 IF M=158 THEN 3570 3520 IF M=127 THEN 3570 3530 IF M=126 THEN 3570 3540 IF M=75 THEN 3570 3550 IF M=73 THEN 3570 3560 RETURN 3570 C=1 3580 GOTO 3560 3990 REM ANOTHER GAME??? 4000 PRINT "ANYONE ELSE CARE TO TRY"; 4010 INPUT Q$ 4020 PRINT 4030 IF Q$="YES" THEN 30 4040 IF Q$="NO" THEN 4050 4042 PRINT "PLEASE ANSWER 'YES' OR 'NO'." 4045 GOTO 4000 4050 PRINT:PRINT "OK --- THANKS AGAIN." 4060 STOP 4990 REM DIRECTIONS 5000 PRINT "WE ARE GOING TO PLAY A GAME BASED ON ONE OF THE CHESS" 5010 PRINT "MOVES. OUR QUEEN WILL BE ABLE TO MOVE ONLY TO THE LEFT," 5020 PRINT "DOWN, OR DIAGONALLY DOWN AND TO THE LEFT." 5030 PRINT 5040 PRINT "THE OBJECT OF THE GAME IS TO PLACE THE QUEEN IN THE LOWER" 5050 PRINT "LEFT HAND SQUARE BY ALTERNATING MOVES BETWEEN YOU AND THE" 5060 PRINT "COMPUTER. THE FIRST ONE TO PLACE THE QUEEN THERE WINS." 5070 PRINT 5080 PRINT "YOU GO FIRST AND PLACE THE QUEEN IN ANY ONE OF THE SQUARES" 5090 PRINT "ON THE TOP ROW OR RIGHT HAND COLUMN." 5100 PRINT "THAT WILL BE YOUR FIRST MOVE." 5110 PRINT "WE ALTERNATE MOVES." 5120 PRINT "YOU MAY FORFEIT BY TYPING '0' AS YOUR MOVE." 5130 PRINT "BE SURE TO PRESS THE RETURN KEY AFTER EACH RESPONSE." 5140 PRINT 5150 PRINT 5160 PRINT 5170 FOR A=0 TO 7 5180 FOR B=1 TO 8 5185 I=8*A+B 5190 PRINT S(I); 5200 NEXT B 5210 PRINT 5220 PRINT 5230 PRINT 5240 NEXT A 5250 PRINT 5260 RETURN 9999 END ================================================ FILE: 00_Alternate_Languages/73_Reverse/MiniScript/README.md ================================================ 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 ================================================ FILE: 00_Alternate_Languages/73_Reverse/MiniScript/reverse.ms ================================================ 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!!" ================================================ FILE: 00_Alternate_Languages/73_Reverse/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/73_Reverse/reverse.bas ================================================ 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 130 DIM A(20) 140 REM *** N=NUMBER OF NUMBERS 150 N=9 160 PRINT "DO YOU WANT THE RULES"; 170 INPUT A$ 180 IF A$="NO" THEN 210 190 GOSUB 710 200 REM *** MAKE A RANDOM LIST A(1) TO A(N) 210 A(1)=INT((N-1)*RND(1)+2) 220 FOR K=2 TO N 230 A(K)=INT(N*RND(1)+1) 240 FOR J=1 TO K-1 250 IF A(K)=A(J) THEN 230 260 NEXT J:NEXT K 280 REM *** PRINT ORIGINAL LIST AND START GAME 290 PRINT: PRINT "HERE WE GO ... THE LIST IS:" 310 T=0 320 GOSUB 610 330 PRINT "HOW MANY SHALL I REVERSE"; 340 INPUT R 350 IF R=0 THEN 520 360 IF R<=N THEN 390 370 PRINT "OOPS! TOO MANY! I CAN REVERSE AT MOST";N:GOTO 330 390 T=T+1 400 REM *** REVERSE R NUMBERS AND PRINT NEW LIST 410 FOR K=1 TO INT(R/2) 420 Z=A(K) 430 A(K)=A(R-K+1) 440 A(R-K+1)=Z 450 NEXT K 460 GOSUB 610 470 REM *** CHECK FOR A WIN 480 FOR K=1 TO N 490 IF A(K)<>K THEN 330 500 NEXT K 510 PRINT "YOU WON IT IN";T;"MOVES!!!":PRINT 520 PRINT 530 PRINT "TRY AGAIN (YES OR NO)"; 540 INPUT A$ 550 IF A$="YES" THEN 210 560 PRINT: PRINT "O.K. HOPE YOU HAD FUN!!":GOTO 999 600 REM *** SUBROUTINE TO PRINT LIST 610 PRINT:FOR K=1 TO N:PRINT A(K);:NEXT K 650 PRINT:PRINT:RETURN 700 REM *** SUBROUTINE TO PRINT THE RULES 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 999 END ================================================ FILE: 00_Alternate_Languages/74_Rock_Scissors_Paper/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/74_Rock_Scissors_Paper/MiniScript/rockscissors.ms ================================================ 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!!" ================================================ FILE: 00_Alternate_Languages/74_Rock_Scissors_Paper/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/74_Rock_Scissors_Paper/bash/rockscissors.sh ================================================ #!/bin/bash #10 PRINT TAB(21);"GAME OF ROCK, SCISSORS, PAPER" #20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" #25 PRINT:PRINT:PRINT printf "%*s GAME OF ROCK, SCISSORS, PAPER\n" 21 printf "%*s CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n" 15 echo ; echo #30 INPUT "HOW MANY GAMES";Q #40 IF Q<11 THEN 60 #50 PRINT "SORRY, BUT WE AREN'T ALLOWED TO PLAY THAT MANY.": GOTO 30 while true; do printf "HOW MANY GAMES " read NUMBER_OF_GAMES [ $NUMBER_OF_GAMES -ge 11 ] || break; echo "SORRY, BUT WE AREN'T ALLOWED TO PLAY THAT MANY." done COMPUTER_WINS=0 HUMAN_WINS=0 TIES=0 #60 FOR G=1 TO Q for GAME_NUMBER in $( seq 1 $NUMBER_OF_GAMES ) do #70 PRINT: PRINT "GAME NUMBER";G echo printf "GAME NUMBER %s\n" $GAME_NUMBER #80 X=INT(RND(1)*3+1) COMPUTER_PICK=$((RANDOM%3+1)) #90 PRINT "3=ROCK...2=SCISSORS...1=PAPER" #100 INPUT "1...2...3...WHAT'S YOUR CHOICE";K #110 IF (K-1)*(K-2)*(K-3)<>0 THEN PRINT "INVALID.": GOTO 90 while true; do echo "3=ROCK...2=SCISSORS...1=PAPER" printf "1...2...3...WHAT'S YOUR CHOICE " read HUMAN_PICK [ $(( (HUMAN_PICK-1)*(HUMAN_PICK-2)*(HUMAN_PICK-3) )) -eq 0 ] && break; echo "INVALID." done #120 PRINT "THIS IS MY CHOICE..." #130 ON X GOTO 140,150,160 #140 PRINT "...PAPER": GOTO 170 #150 PRINT "...SCISSORS": GOTO 170 #160 PRINT "...ROCK" printf "THIS IS MY CHOICE..." if [ $COMPUTER_PICK -eq 1 ]; then echo "...PAPER" elif [ $COMPUTER_PICK -eq 2 ]; then echo "...SCISSORS" else echo "...ROCK" fi #170 IF X=K THEN 250 #180 IF X>K THEN 230 #190 IF X=1 THEN 210 #200 PRINT "YOU WIN!!!":H=H+1: GOTO 260 #210 IF K<>3 THEN 200 #220 PRINT "WOW! I WIN!!!":C=C+1:GOTO 260 #230 IF K<>1 OR X<>3 THEN 220 #240 GOTO 200 #250 PRINT "TIE GAME. NO WINNER." #260 NEXT G if [ $COMPUTER_PICK -eq $HUMAN_PICK ]; then echo "TIE GAME. NO WINNER." ((TIES+=1)) continue elif ([ $COMPUTER_PICK -gt $HUMAN_PICK ] && ([ $COMPUTER_PICK -ne 3 ] || [ $HUMAN_PICK -ne 1 ])) \ || ([ $COMPUTER_PICK -eq 1 ] && [ $HUMAN_PICK -eq 3 ]); then echo "WOW! I WIN!!!" ((COMPUTER_WINS+=1)) continue else echo "YOU WIN!!!" ((HUMAN_WINS+=1)) continue fi done #270 PRINT: PRINT "HERE IS THE FINAL GAME SCORE:" #280 PRINT "I HAVE WON";C;"GAME(S)." #290 PRINT "YOU HAVE WON";H;"GAME(S)." #300 PRINT "AND";Q-(C+H);"GAME(S) ENDED IN A TIE." #310 PRINT: PRINT "THANKS FOR PLAYING!!" echo echo "HERE IS THE FINAL GAME SCORE:" printf "I HAVE WON %d GAME(S).\n" $COMPUTER_WINS printf "YOU HAVE WON %d GAME(S).\n" $HUMAN_WINS printf "AND %d GAME(S) ENDED IN A TIE.\n" $TIES echo echo "THANKS FOR PLAYING!!" #320 END ================================================ FILE: 00_Alternate_Languages/74_Rock_Scissors_Paper/nim/rockscissors.nim ================================================ 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!!" ================================================ FILE: 00_Alternate_Languages/74_Rock_Scissors_Paper/rockscissors.bas ================================================ 10 PRINT TAB(21);"GAME OF ROCK, SCISSORS, PAPER" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 25 PRINT:PRINT:PRINT 30 INPUT "HOW MANY GAMES";Q 40 IF Q<11 THEN 60 50 PRINT "SORRY, BUT WE AREN'T ALLOWED TO PLAY THAT MANY.": GOTO 30 60 FOR G=1 TO Q 70 PRINT: PRINT "GAME NUMBER";G 80 X=INT(RND(1)*3+1) 90 PRINT "3=ROCK...2=SCISSORS...1=PAPER" 100 INPUT "1...2...3...WHAT'S YOUR CHOICE";K 110 IF (K-1)*(K-2)*(K-3)<>0 THEN PRINT "INVALID.": GOTO 90 120 PRINT "THIS IS MY CHOICE..." 130 ON X GOTO 140,150,160 140 PRINT "...PAPER": GOTO 170 150 PRINT "...SCISSORS": GOTO 170 160 PRINT "...ROCK" 170 IF X=K THEN 250 180 IF X>K THEN 230 190 IF X=1 THEN 210 200 PRINT "YOU WIN!!!":H=H+1: GOTO 260 210 IF K<>3 THEN 200 220 PRINT "WOW! I WIN!!!":C=C+1:GOTO 260 230 IF K<>1 OR X<>3 THEN 220 240 GOTO 200 250 PRINT "TIE GAME. NO WINNER." 260 NEXT G 270 PRINT: PRINT "HERE IS THE FINAL GAME SCORE:" 280 PRINT "I HAVE WON";C;"GAME(S)." 290 PRINT "YOU HAVE WON";H;"GAME(S)." 300 PRINT "AND";Q-(C+H);"GAME(S) ENDED IN A TIE." 310 PRINT: PRINT "THANKS FOR PLAYING!!" 320 END ================================================ FILE: 00_Alternate_Languages/75_Roulette/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/75_Roulette/MiniScript/roulette.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/75_Roulette/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/75_Roulette/roulette.bas ================================================ 10 PRINT TAB(32);"ROULETTE" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 30 PRINT:PRINT:PRINT 40 PRINT "ENTER THE CURRENT DATE (AS IN 'JANUARY 23, 1979') -"; 50 INPUT D$,E$ 1000 REM-ROULETTE 1010 REM-DAVID JOSLIN 1020 PRINT "WELCOME TO THE ROULETTE TABLE" 1030 PRINT 1040 PRINT "DO YOU WANT INSTRUCTIONS"; 1050 INPUT Y$ 1060 IF LEFT$(Y$,1)="N" THEN 1550 1070 PRINT 1080 PRINT "THIS IS THE BETTING LAYOUT" 1090 PRINT " (*=RED)" 1100 PRINT 1110 PRINT " 1* 2 3*" 1120 PRINT " 4 5* 6 " 1130 PRINT " 7* 8 9*" 1140 PRINT "10 11 12*" 1150 PRINT "---------------" 1160 PRINT "13 14* 15 " 1170 PRINT "16* 17 18*" 1180 PRINT "19* 20 21*" 1190 PRINT "22 23* 24 " 1200 PRINT "---------------" 1210 PRINT "25* 26 27*" 1220 PRINT "28 29 30*" 1230 PRINT "31 32* 33 " 1240 PRINT "34* 35 36*" 1250 PRINT "---------------" 1260 PRINT " 00 0 " 1270 PRINT 1280 PRINT "TYPES OF BETS" 1290 PRINT 1300 PRINT "THE NUMBERS 1 TO 36 SIGNIFY A STRAIGHT BET" 1310 PRINT "ON THAT NUMBER." 1320 PRINT "THESE PAY OFF 35:1" 1330 PRINT 1340 PRINT "THE 2:1 BETS ARE:" 1350 PRINT " 37) 1-12 40) FIRST COLUMN" 1360 PRINT " 38) 13-24 41) SECOND COLUMN" 1370 PRINT " 39) 25-36 42) THIRD COLUMN" 1380 PRINT 1390 PRINT "THE EVEN MONEY BETS ARE:" 1400 PRINT " 43) 1-18 46) ODD" 1410 PRINT " 44) 19-36 47) RED" 1420 PRINT " 45) EVEN 48) BLACK" 1430 PRINT 1440 PRINT " 49)0 AND 50)00 PAY OFF 35:1" 1450 PRINT " NOTE: 0 AND 00 DO NOT COUNT UNDER ANY" 1460 PRINT " BETS EXCEPT THEIR OWN." 1470 PRINT 1480 PRINT "WHEN I ASK FOR EACH BET, TYPE THE NUMBER" 1490 PRINT "AND THE AMOUNT, SEPARATED BY A COMMA." 1500 PRINT "FOR EXAMPLE: TO BET $500 ON BLACK, TYPE 48,500" 1510 PRINT "WHEN I ASK FOR A BET." 1520 PRINT 1530 PRINT "THE MINIMUM BET IS $5, THE MAXIMUM IS $500." 1540 PRINT 1550 REM-PROGRAM BEGINS HERE 1560 REM-TYPE OF BET(NUMBER) ODDS 1570 REM DON'T NEED TO DIMENSION STRINGS 1580 DIM B(100),C(100),T(100),X(38) 1590 DIM A(50) 1600 FOR I=1 TO 38: X(I)=0: NEXT I: REM MAT X=ZER 1610 P=1000 1620 D=100000. 1630 PRINT "HOW MANY BETS"; 1640 INPUT Y 1650 IF Y<1 OR Y<>INT(Y) THEN 1630 1660 FOR I=1 TO 50: A(I)=0: NEXT I: REM MAT A=ZER 1670 FOR C=1 TO Y 1680 PRINT "NUMBER";C; 1690 INPUT X,Z 1700 B(C)=Z 1710 T(C)=X 1720 IF X<1 OR X>50 OR X<>INT(X) THEN 1680 1730 IF Z<1 OR Z<>INT(Z) THEN 1680 1740 IF Z<5 OR Z>500 THEN 1680 1750 IF A(X)=0 THEN 1780 1760 PRINT "YOU MADE THAT BET ONCE ALREADY,DUM-DUM" 1770 GOTO 1680 1780 A(X)=1 1790 NEXT C 1800 PRINT "SPINNING" 1810 PRINT 1820 PRINT 1830 S=INT(RND(1)*100) 1840 IF S=0 OR S>38 THEN 1830 1850 X(S)=X(S)+1 1860 IF S<37 THEN 1920 1870 IF S=37 THEN 1900 1880 PRINT "00" 1890 GOTO 2020 1900 PRINT "0" 1910 GOTO 2020 1920 RESTORE 1930 FOR I1=1 TO 18 1940 READ R 1950 IF R=S THEN 2000 1960 NEXT I1 1970 A$="BLACK" 1980 PRINT S;A$ 1990 GOTO 2020 2000 A$="RED" 2010 GOTO 1980 2020 PRINT 2030 FOR C=1 TO Y 2040 IF T(C)<37 THEN 2710 2050 ON T(C)-36 GOTO 2090,2190,2220,2250,2300,2350,2400,2470,2500 2060 ON T(C)-45 GOTO 2530,2560,2630 2070 GOTO 2710 2080 STOP 2090 REM 1-12(37) 2:1 2100 IF S <= 12 THEN 2150 2110 PRINT "YOU LOSE";B(C);"DOLLARS ON BET";C 2120 D=D+B(C) 2130 P=P-B(C) 2140 GOTO 2180 2150 PRINT "YOU WIN";B(C)*2;"DOLLARS ON BET"C 2160 D=D-B(C)*2 2170 P=P+B(C)*2 2180 GOTO 2810 2190 REM 13-24(38) 2:1 2200 IF S>12 AND S<25 THEN 2150 2210 GOTO 2110 2220 REM 25-36(39) 2:1 2230 IF S>24 AND S<37 THEN 2150 2240 GOTO 2110 2250 REM FIRST COLUMN(40) 2:1 2260 FOR I=1 TO 34 STEP 3 2270 IF S=I THEN 2150 2280 NEXT I 2290 GOTO 2110 2300 REM SECOND COLUMN(41) 2:1 2310 FOR I=2 TO 35 STEP 3 2320 IF S=I THEN 2150 2330 NEXT I 2340 GOTO 2110 2350 REM THIRD COLUMN(42) 2:1 2360 FOR I=3 TO 36 STEP 3 2370 IF S=I THEN 2150 2380 NEXT I 2390 GOTO 2110 2400 REM 1-18(43) 1:1 2410 IF S<19 THEN 2430 2420 GOTO 2110 2430 PRINT "YOU WIN";B(C);"DOLLARS ON BET";C 2440 D=D-B(C) 2450 P=P+B(C) 2460 GOTO 2810 2470 REM 19-36(44) 1:1 2480 IF S<37 AND S>18 THEN 2430 2490 GOTO 2110 2500 REM EVEN(45) 1:1 2510 IF S/2=INT(S/2) AND S<37 THEN 2430 2520 GOTO 2110 2530 REM ODD(46) 1:1 2540 IF S/2<>INT(S/2) AND S<37 THEN 2430 2550 GOTO 2110 2560 REM RED(47) 1:1 2570 RESTORE 2580 FOR I=1 TO 18 2590 READ R 2600 IF S=R THEN 2430 2610 NEXT I 2620 GOTO 2110 2630 REM BLACK(48) 1:1 2640 RESTORE 2650 FOR I=1 TO 18 2660 READ R 2670 IF S=R THEN 2110 2680 NEXT I 2690 IF S>36 THEN 2110 2700 GOTO 2430 2710 REM--1TO36,0,00(1-36,49,50)35:1 2720 IF T(C)<49 THEN 2760 2730 IF T(C)=49 AND S=37 THEN 2780 2740 IF T(C)=50 AND S=38 THEN 2780 2750 GOTO 2110 2760 IF T(C)=S THEN 2780 2770 GOTO 2110 2780 PRINT "YOU WIN";B(C)*35;"DOLLARS ON BET";C 2790 D=D-B(C)*35 2800 P=P+B(C)*35 2810 NEXT C 2820 PRINT 2830 PRINT "TOTALS:","ME","YOU" 2840 PRINT " ",D,P 2850 IF P>0 THEN 2880 2860 PRINT "OOPS! YOU JUST SPENT YOUR LAST DOLLAR!" 2870 GOTO 3190 2880 IF D>0 THEN 2920 2890 PRINT "YOU BROKE THE HOUSE!" 2900 P=101000. 2910 GOTO 2960 2920 PRINT "AGAIN"; 2930 INPUT Y$ 2940 IF LEFT$(Y$,1)="Y" THEN 1630 2950 DATA 1,3,5,7,9,12,14,16,18,19,21,23,25,27,30,32,34,36 2960 IF P<1 THEN 3190 2970 PRINT "TO WHOM SHALL I MAKE THE CHECK"; 2980 INPUT B$ 2990 PRINT 3000 FOR I=1 TO 72: PRINT "-";: NEXT I: REM PRINT 72 DASHES 3010 PRINT TAB(50);"CHECK NO. ";INT(RND(1)*100) 3020 PRINT 3030 GOSUB 3230 3040 PRINT TAB(40);M$ 3050 PRINT 3060 PRINT 3070 PRINT "PAY TO THE ORDER OF-----";B$;"-----$ "; 3080 PRINT P 3090 PRINT 3100 PRINT 3110 PRINT TAB(10),"THE MEMORY BANK OF NEW YORK" 3120 PRINT 3130 PRINT TAB(40),"THE COMPUTER" 3140 PRINT TAB(40)"----------X-----" 3150 PRINT 3160 FOR I=1 TO 62: PRINT "-";: NEXT I 3170 PRINT "COME BACK SOON!" 3180 GOTO 3210 3190 PRINT "THANKS FOR YOUR MONEY." 3200 PRINT "I'LL USE IT TO BUY A SOLID GOLD ROULETTE WHEEL" 3210 PRINT 3220 GOTO 3420 3230 REM 3240 REM THIS ROUTINE RETURNS THE CURRENT DATE IN M$ 3250 REM IF YOU HAVE SYSTEM FUNCTIONS TO HANDLE THIS 3260 REM THEY CAN BE USED HERE. HOWEVER IN THIS 3270 REM PROGRAM, WE JUST INPUT THE DATE AT THE START 3280 REM THE GAME 3290 REM 3300 REM THE DATE IS RETURNED IN VARIABLE M$ 3310 M$=D$+", "+E$ 3320 RETURN 3420 END ================================================ FILE: 00_Alternate_Languages/76_Russian_Roulette/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/76_Russian_Roulette/MiniScript/russianroulette.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/76_Russian_Roulette/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/76_Russian_Roulette/russianroulette.bas ================================================ 1 PRINT TAB(28);"RUSSIAN ROULETTE" 2 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 3 PRINT:PRINT:PRINT 5 PRINT "THIS IS A GAME OF >>>>>>>>>>RUSSIAN ROULETTE." 10 PRINT:PRINT "HERE IS A REVOLVER." 20 PRINT "TYPE '1' TO SPIN CHAMBER AND PULL TRIGGER." 22 PRINT "TYPE '2' TO GIVE UP." 23 PRINT "GO"; 25 N=0 30 INPUT I 31 IF I<>2 THEN 35 32 PRINT " CHICKEN!!!!!" 33 GOTO 72 35 N=N+1 40 IF RND(1)>.833333 THEN 70 45 IF N>10 THEN 80 50 PRINT "- CLICK -" 60 PRINT: GOTO 30 70 PRINT " BANG!!!!! YOU'RE DEAD!" 71 PRINT "CONDOLENCES WILL BE SENT TO YOUR RELATIVES." 72 PRINT:PRINT:PRINT 75 PRINT "...NEXT VICTIM...":GOTO 20 80 PRINT "YOU WIN!!!!!" 85 PRINT "LET SOMEONE ELSE BLOW HIS BRAINS OUT." 90 GOTO 10 99 END ================================================ FILE: 00_Alternate_Languages/77_Salvo/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/77_Salvo/MiniScript/salvo.ms ================================================ 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<A>", 1, 2) placeOne Ship.make("Destroyer<B>", 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<A>", 1, 2) placeOne Ship.make("Destroyer<B>", 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 ================================================ FILE: 00_Alternate_Languages/77_Salvo/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/77_Salvo/salvo.bas ================================================ 1000 PRINT TAB(33);"SALVO" 1010 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 1020 PRINT:PRINT:PRINT 1030 REM 1040 DIM A(10,10),B(10,10),C(7),D(7),E(12),F(12),G(12),H(12),K(10,10) 1050 Z8=0 1060 FOR W=1 TO 12 1070 E(W)=-1 1080 H(W)=-1 1090 NEXT W 1100 FOR X=1 TO 10 1110 FOR Y=1 TO 10 1120 B(X,Y)=0 1130 NEXT Y 1140 NEXT X 1150 FOR X=1 TO 12 1160 F(X)=0 1170 G(X)=0 1180 NEXT X 1190 FOR X=1 TO 10 1200 FOR Y=1 TO 10 1210 A(X,Y)=0 1220 NEXT Y 1230 NEXT X 1240 FOR K=4 TO 1 STEP -1 1250 U6=0 1260 GOSUB 2910 1270 DEF FNA(K)=(5-K)*3-2*INT(K/4)+SGN(K-1)-1 1280 DEF FNB(K)=K+INT(K/4)-SGN(K-1) 1290 IF V+V2+V*V2=0 THEN 1260 1300 IF Y+V*FNB(K)>10 THEN 1260 1310 IF Y+V*FNB(K)<1 THEN 1260 1320 IF X+V2*FNB(K)>10 THEN 1260 1330 IF X+V2*FNB(K)<1 THEN 1260 1340 U6=U6+1 1350 IF U6>25 THEN 1190 1360 FOR Z=0 TO FNB(K) 1370 F(Z+FNA(K))=X+V2*Z 1380 G(Z+FNA(K))=Y+V*Z 1390 NEXT Z 1400 U8=FNA(K) 1405 IF U8>U8+FNB(K) THEN 1460 1410 FOR Z2= U8 TO U8+FNB(K) 1415 IF U8<2 THEN 1450 1420 FOR Z3=1 TO U8-1 1430 IF SQR((F(Z3)-F(Z2))^2 + (G(Z3)-G(Z2))^2) < 3.59 THEN 1260 1440 NEXT Z3 1450 NEXT Z2 1460 FOR Z=0 TO FNB(K) 1470 A(F(Z+U8),G(Z+U8))=.5+SGN(K-1)*(K-1.5) 1480 NEXT Z 1490 NEXT K 1500 PRINT "ENTER COORDINATES FOR..." 1510 PRINT "BATTLESHIP" 1520 FOR X=1 TO 5 1530 INPUT Y,Z 1540 B(Y,Z)=3 1550 NEXT X 1560 PRINT "CRUISER" 1570 FOR X=1 TO 3 1580 INPUT Y,Z 1590 B(Y,Z)=2 1600 NEXT X 1610 PRINT "DESTROYER<A>" 1620 FOR X=1 TO 2 1630 INPUT Y,Z 1640 B(Y,Z)=1 1650 NEXT X 1660 PRINT "DESTROYER<B>" 1670 FOR X=1 TO 2 1680 INPUT Y,Z 1690 B(Y,Z)=.5 1700 NEXT X 1710 PRINT "DO YOU WANT TO START"; 1720 INPUT J$ 1730 IF J$<>"WHERE ARE YOUR SHIPS?" THEN 1890 1740 PRINT "BATTLESHIP" 1750 FOR Z=1 TO 5 1760 PRINT F(Z);G(Z) 1770 NEXT Z 1780 PRINT "CRUISER" 1790 PRINT F(6);G(6) 1800 PRINT F(7);G(7) 1810 PRINT F(8);G(8) 1820 PRINT "DESTROYER<A>" 1830 PRINT F(9);G(9) 1840 PRINT F(10);G(10) 1850 PRINT "DESTROYER<B>" 1860 PRINT F(11);G(11) 1870 PRINT F(12);G(12) 1880 GOTO 1710 1890 C=0 1900 PRINT "DO YOU WANT TO SEE MY SHOTS"; 1910 INPUT K$ 1920 PRINT 1930 IF J$<>"YES" THEN 2620 1940 REM*******************START 1950 IF J$<>"YES" THEN 1990 1960 C=C+1 1970 PRINT 1980 PRINT "TURN";C 1990 A=0 2000 FOR W=.5 TO 3 STEP .5 2010 FOR X=1 TO 10 2020 FOR Y=1 TO 10 2030 IF B(X,Y)=W THEN 2070 2040 NEXT Y 2050 NEXT X 2060 GOTO 2080 2070 A=A+INT(W+.5) 2080 NEXT W 2090 FOR W=1 TO 7 2100 C(W)=0 2110 D(W)=0 2120 F(W)=0 2130 G(W)=0 2140 NEXT W 2150 P3=0 2160 FOR X=1 TO 10 2170 FOR Y=1 TO 10 2180 IF A(X,Y)>10 THEN 2200 2190 P3=P3+1 2200 NEXT Y 2210 NEXT X 2220 PRINT "YOU HAVE";A;"SHOTS." 2230 IF P3>=A THEN 2260 2240 PRINT "YOU HAVE MORE SHOTS THAN THERE ARE BLANK SQUARES." 2250 GOTO 2890 2260 IF A<>0 THEN 2290 2270 PRINT "I HAVE WON." 2280 STOP 2290 FOR W=1 TO A 2300 INPUT X,Y 2310 IF X<>INT(X) THEN 2370 2320 IF X>10 THEN 2370 2330 IF X<1 THEN 2370 2340 IF Y<>INT(Y) THEN 2370 2350 IF Y>10 THEN 2370 2360 IF Y>=1 THEN 2390 2370 PRINT "ILLEGAL, ENTER AGAIN." 2380 GOTO 2300 2390 IF A(X,Y)>10 THEN 2440 2400 C(W)=X 2410 D(W)=Y 2420 NEXT W 2430 GOTO 2460 2440 PRINT "YOU SHOT THERE BEFORE ON TURN";A(X,Y)-10 2450 GOTO 2300 2460 FOR W=1 TO A 2470 IF A(C(W),D(W))=3 THEN 2540 2480 IF A(C(W),D(W))=2 THEN 2560 2490 IF A(C(W),D(W))=1 THEN 2580 2500 IF A(C(W),D(W))=.5 THEN 2600 2510 A(C(W),D(W))=10+C 2520 NEXT W 2530 GOTO 2620 2540 PRINT "YOU HIT MY BATTLESHIP." 2550 GOTO 2510 2560 PRINT "YOU HIT MY CRUISER." 2570 GOTO 2510 2580 PRINT "YOU HIT MY DESTROYER<A>." 2590 GOTO 2510 2600 PRINT "YOU HIT MY DESTROYER<B>." 2610 GOTO 2510 2620 A=0 2630 IF J$="YES" THEN 2670 2640 C=C+1 2650 PRINT 2660 PRINT "TURN";C 2670 A=0 2680 FOR W=.5 TO 3 STEP .5 2690 FOR X=1 TO 10 2700 FOR Y=1 TO 10 2710 IF A(X,Y)=W THEN 2750 2720 NEXT Y 2730 NEXT X 2740 GOTO 2760 2750 A=A+INT(W+.5) 2760 NEXT W 2770 P3=0 2780 FOR X=1 TO 10 2790 FOR Y=1 TO 10 2800 IF A(X,Y)>10 THEN 2820 2810 P3=P3+1 2820 NEXT Y 2830 NEXT X 2840 PRINT "I HAVE";A;"SHOTS." 2850 IF P3>A THEN 2880 2860 PRINT "I HAVE MORE SHOTS THAN BLANK SQUARES." 2870 GOTO 2270 2880 IF A<>0 THEN 2960 2890 PRINT "YOU HAVE WON." 2900 STOP 2910 X=INT(RND(1)*10+1) 2920 Y=INT(RND(1)*10+1) 2930 V=INT(3*RND(1)-1) 2940 V2=INT(3*RND(1)-1) 2950 RETURN 2960 FOR W=1 TO 12 2970 IF H(W)>0 THEN 3800 2980 NEXT W 2990 REM*******************RANDOM 3000 W=0 3010 R3=0 3020 GOSUB 2910 3030 RESTORE 3040 R2=0 3050 R3=R3+1 3060 IF R3>100 THEN 3010 3070 IF X>10 THEN 3110 3080 IF X>0 THEN 3120 3090 X=1+INT(RND(1)*2.5) 3100 GOTO 3120 3110 X=10-INT(RND(1)*2.5) 3120 IF Y>10 THEN 3160 3130 IF Y>0 THEN 3270 3140 Y=1+INT(RND(1)*2.5) 3150 GOTO 3270 3160 Y=10-INT(RND(1)*2.5) 3170 GOTO 3270 3180 F(W)=X 3190 G(W)=Y 3200 IF W=A THEN 3380 3210 IF R2=6 THEN 3030 3220 READ X1,Y1 3230 R2=R2+1 3240 DATA 1,1,-1,1,1,-3,1,1,0,2,-1,1 3250 X=X+X1 3260 Y=Y+Y1 3270 IF X>10 THEN 3210 3280 IF X<1 THEN 3210 3290 IF Y>10 THEN 3210 3300 IF Y<1 THEN 3210 3310 IF B(X,Y)>10 THEN 3210 3320 FOR Q9=1 TO W 3330 IF F(Q9)<>X THEN 3350 3340 IF G(Q9)=Y THEN 3210 3350 NEXT Q9 3360 W=W+1 3370 GOTO 3180 3380 IF K$<>"YES" THEN 3420 3390 FOR Z5=1 TO A 3400 PRINT F(Z5);G(Z5) 3410 NEXT Z5 3420 FOR W=1 TO A 3430 IF B(F(W),G(W))=3 THEN 3500 3440 IF B(F(W),G(W))=2 THEN 3520 3450 IF B(F(W),G(W))=1 THEN 3560 3460 IF B(F(W),G(W))=.5 THEN 3540 3470 B(F(W),G(W))=10+C 3480 NEXT W 3490 GOTO 1950 3500 PRINT "I HIT YOUR BATTLESHIP" 3510 GOTO 3570 3520 PRINT "I HIT YOUR CRUISER" 3530 GOTO 3570 3540 PRINT "I HIT YOUR DESTROYER<B>" 3550 GOTO 3570 3560 PRINT "I HIT YOUR DESTROYER<A>" 3570 FOR Q=1 TO 12 3580 IF E(Q)<>-1 THEN 3730 3590 E(Q)=10+C 3600 H(Q)=B(F(W),G(W)) 3610 M3=0 3620 FOR M2=1 TO 12 3630 IF H(M2)<>H(Q) THEN 3650 3640 M3=M3+1 3650 NEXT M2 3660 IF M3<>INT(H(Q)+.5)+1+INT(INT(H(Q)+.5)/3) THEN 3470 3670 FOR M2=1 TO 12 3680 IF H(M2)<>H(Q) THEN 3710 3690 E(M2)=-1 3700 H(M2)=-1 3710 NEXT M2 3720 GOTO 3470 3730 NEXT Q 3740 PRINT "PROGRAM ABORT:" 3750 FOR Q=1 TO 12 3760 PRINT "E(";Q;") =";E(Q) 3770 PRINT "H(";Q;") =";H(Q) 3780 NEXT Q 3790 STOP 3800 REM************************USINGEARRAY 3810 FOR R=1 TO 10 3820 FOR S=1 TO 10 3830 K(R,S)=0 3840 NEXT S 3850 NEXT R 3860 FOR U=1 TO 12 3870 IF E(U)<10 THEN 4020 3880 FOR R=1 TO 10 3890 FOR S=1 TO 10 3900 IF B(R,S)<10 THEN 3930 3910 K(R,S)=-10000000 3920 GOTO 4000 3930 FOR M=SGN(1-R) TO SGN(10-R) 3940 FOR N=SGN(1-S) TO SGN(10-S) 3950 IF N+M+N*M=0 THEN 3980 3960 IF B(R+M,S+N)<>E(U) THEN 3980 3970 K(R,S)=K(R,S)+E(U)-S*INT(H(U)+.5) 3980 NEXT N 3990 NEXT M 4000 NEXT S 4010 NEXT R 4020 NEXT U 4030 FOR R=1 TO A 4040 F(R)=R 4050 G(R)=R 4060 NEXT R 4070 FOR R=1 TO 10 4080 FOR S=1 TO 10 4090 Q9=1 4100 FOR M=1 TO A 4110 IF K(F(M),G(M))>=K(F(Q9),G(Q9)) THEN 4130 4120 Q9=M 4130 NEXT M 4131 IF R>A THEN 4140 4132 IF R=S THEN 4210 4140 IF K(R,S)<K(F(Q9),G(Q9)) THEN 4210 4150 FOR M=1 TO A 4160 IF F(M)<>R THEN 4190 4170 IF G(M)=S THEN 4210 4180 NEXT M 4190 F(Q9)=R 4200 G(Q9)=S 4210 NEXT S 4220 NEXT R 4230 GOTO 3380 4240 END ================================================ FILE: 00_Alternate_Languages/78_Sine_Wave/C++/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [C++17](https://en.wikipedia.org/wiki/C%2B%2B17) ================================================ FILE: 00_Alternate_Languages/78_Sine_Wave/C++/sinewave.cpp ================================================ #include <iostream> // std::cout, std::endl #include <string> // std::string(size_t n, char c) #include <cmath> // std::sin(double x) int main() { std::cout << std::string(30, ' ') << "SINE WAVE" << std::endl; std::cout << std::string(15, ' ') << "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" << std::endl; std::cout << std::string(5, '\n'); bool b = true; for (double t = 0.0; t <= 40.0; t += 0.25) { int a = int(26 + 25 * std::sin(t)); std::cout << std::string(a, ' ') << (b ? "CREATIVE" : "COMPUTING") << std::endl; b = !b; } return 0; } ================================================ FILE: 00_Alternate_Languages/78_Sine_Wave/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/78_Sine_Wave/MiniScript/sinewave.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/78_Sine_Wave/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/78_Sine_Wave/pascal/sinewave.pas ================================================ program sinewave; procedure tabWriteLn(text: string; indent: integer); begin Writeln(text:length(text)+indent); end; var a, t, b: integer; begin tabWriteLn('SINE WAVE', 30); tabWriteLn('CREATIVE COMPUTING MORRISTOWN, NEW JERSEY', 15); Writeln(); Writeln(); Writeln(); Writeln(); Writeln(); // REMARKABLE PROGRAM BY DAVID AHL b := 0; // START LONG LOOP for t := 0 to 40*4 do begin a := Trunc(26+25*Sin(t/4)); if (b = 0) then begin tabWriteLn('CREATIVE', a); b := 1; end else begin tabWriteLn('COMPUTING', a); b := 0; end; end; end. ================================================ FILE: 00_Alternate_Languages/78_Sine_Wave/sinewave.bas ================================================ 10 PRINT TAB(30);"SINE WAVE" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 30 PRINT: PRINT: PRINT: PRINT: PRINT 40 REMARKABLE PROGRAM BY DAVID AHL 50 B=0 100 REM START LONG LOOP 110 FOR T=0 TO 40 STEP .25 120 A=INT(26+25*SIN(T)) 130 PRINT TAB(A); 140 IF B=1 THEN 180 150 PRINT "CREATIVE" 160 B=1 170 GOTO 200 180 PRINT "COMPUTING" 190 B=0 200 NEXT T 999 END ================================================ FILE: 00_Alternate_Languages/79_Slalom/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/79_Slalom/MiniScript/slalom.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/79_Slalom/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/79_Slalom/slalom.bas ================================================ 10 PRINT TAB(33);"SLALOM" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 30 PRINT:PRINT:PRINT 310 PRINT "HOW MANY GATES DOES THIS COURSE HAVE (1 TO 25)"; 320 INPUT V 330 IF V>25 THEN 360 340 IF V<1 THEN 390 350 GOTO 1440 360 PRINT "25 IS THE LIMIT." 370 LET V=25 380 GOTO 1440 390 PRINT "TRY AGAIN," 400 GOTO 310 410 PRINT "RATE YOURSELF AS A SKIER, (1=WORST, 3=BEST)"; 420 INPUT A 430 IF A<1 THEN 460 440 IF A>3 THEN 460 450 GOTO 480 460 PRINT "THE BOUNDS ARE 1-3" 470 GOTO 410 480 PRINT"THE STARTER COUNTS DOWN...5...4...3...2...1...GO!"; 490 REM 500 LET T=0 510 LET S=INT(RND(1)*(18-9)+9) 520 PRINT 525 PRINT "YOU'RE OFF!" 530 FOR O=1 TO V 540 READ Q 550 PRINT 555 PRINT "HERE COMES GATE #";STR$(O);":" 560 PRINT S;"M.P.H." 570 LET S1=S 580 PRINT "OPTION"; 590 INPUT O1 600 IF O1=0 THEN 970 610 IF O1>8 THEN 1420 620 IF O1<1 THEN 1420 630 GOSUB 990 640 IF S<7 THEN 1390 650 LET T=T+(Q-S+1) 660 IF S>Q THEN 1630 670 NEXT O 680 PRINT:PRINT "YOU TOOK";(T+RND(1));"SECONDS." 690 LET M=T 700 LET M=M/V 710 IF M<1.5-(A*.1) THEN 1650 720 IF M<2.9-(A*.1) THEN 1680 730 IF M<4.4-(A*.01) THEN 1710 740 PRINT:PRINT "DO YOU WANT TO RACE AGAIN"; 750 INPUT B$ 760 REM 770 IF B$="NO" THEN 1740 780 IF B$="YES" THEN 480 790 PRINT "PLEASE TYPE 'YES' OR 'NO'" 800 GOTO 740 810 STOP 820 PRINT 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 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." 880 PRINT " 4 -- TYPE THIS IF YOU WANT TO KEEP GOING THE SAME SPEED." 890 PRINT " 5 -- TYPE THIS IF YOU WANT TO CHECK A TEENSY." 900 PRINT " 6 -- TYPE THIS IF YOU WANT TO CHECK A LITTLE." 910 PRINT " 7 -- TYPE THIS IF YOU WANT TO CHECK A LOT." 920 PRINT " 8 -- TYPE THIS IF YOU WANT TO CHEAT AND TRY TO SKIP A GATE." 930 PRINT 935 PRINT " THE PLACE TO USE THESE OPTIONS IS WHEN THE COMPUTER ASKS:" 940 PRINT 945 PRINT "OPTION?" 950 PRINT 955 PRINT " GOOD LUCK!" 957 PRINT 960 GOTO 1470 970 PRINT "YOU'VE TAKEN";(T+RND(1));"SECONDS." 980 GOTO 580 990 ON O1 GOTO 1130,1010,1170,1080,1190,1100,1150,1210 1000 STOP 1010 LET S=S+INT(RND(1)*(5-3)+3) 1020 PRINT S;"M.P.H." 1030 IF S>Q THEN 1290 1040 IF S>Q-1 THEN 1060 1050 RETURN 1060 PRINT "CLOSE ONE!" 1070 RETURN 1080 PRINT S;"M.P.H." 1090 GOTO 1030 1100 LET S=S-INT(RND(1)*(5-3)+3) 1110 PRINT S;"M.P.H." 1120 GOTO 1030 1130 LET S=S+INT(RND(1)*(10-5)+5) 1140 GOTO 1080 1150 LET S=S-INT(RND(1)*(10-5)+5) 1160 GOTO 1110 1170 LET S=S+INT(RND(1)*(4-1)+1) 1180 GOTO 1110 1190 LET S=S-INT(RND(1)*(4-1)+1) 1200 GOTO 1110 1210 PRINT "***CHEAT" 1220 IF RND(1)<.7 THEN 1260 1230 PRINT "YOU MADE IT!" 1240 LET T=T+1.5 1250 RETURN 1260 PRINT "AN OFFICIAL CAUGHT YOU!" 1270 PRINT "YOU TOOK";(T+RND(1));"SECONDS." 1280 GOTO 740 1290 IF RND(1)<((S-Q)*.1)+.2 THEN 1320 1300 PRINT "YOU WENT OVER THE NAXIMUM SPEED AND MADE IT!" 1310 RETURN 1320 PRINT "YOU WENT OVER THE MAXIMUM SPEED AND "; 1330 IF RND(1)<.5 THEN 1370 1340 PRINT "WIPED OUT!" 1350 PRINT "YOU TOOK";(T+RND(1));"SECONDS" 1360 GOTO 740 1370 PRINT "SNAGGED A FLAG!" 1380 GOTO 1350 1390 PRINT "LET'S BE REALISTIC, OK? LET'S GO BACK AND TRY AGAIN..." 1400 LET S=S1 1410 GOTO 550 1420 PRINT "WHAT?" 1430 GOTO 580 1440 PRINT 1445 PRINT "TYPE ";CHR$(34);"INS";CHR$(34);" FOR INSTRUCTIONS" 1450 PRINT "TYPE ";CHR$(34);"MAX";CHR$(34);" FOR APPROXIMATE MAXIMUM SPEEDS" 1460 PRINT "TYPE ";CHR$(34);"RUN";CHR$(34);" FOR THE BEGINNING OF THE RACE" 1470 PRINT "COMMAND--"; 1480 INPUT A$ 1490 REM 1500 IF A$="INS" THEN 820 1510 IF A$="MAX" THEN 1550 1520 IF A$="RUN" THEN 410 1530 PRINT CHR$(34);A$;CHR$(34);" IS AN ILLEGAL COMMAND--RETRY"; 1540 GOTO 1480 1550 PRINT "GATE MAX" 1560 PRINT " # M.P.H." 1570 PRINT"----------" 1580 FOR B=1 TO V 1590 READ Q 1600 PRINT B;" ";Q 1610 NEXT B 1620 GOTO 1470 1630 LET T=T+.5 1640 GOTO 670 1650 PRINT "YOU WON A GOLD MEDAL!" 1660 LET G(1)=G(1)+1 1670 GOTO 1730 1680 PRINT "YOU WON A SILVER MEDAL" 1690 LET S(1)=S(1)+1 1700 GOTO 1730 1710 PRINT "YOU WON A BRONZE MEDAL" 1720 LET B(1)=B(1)+1 1730 GOTO 740 1740 PRINT "THANKS FOR THE RACE" 1750 IF G(1)<1 THEN 1770 1760 PRINT "GOLD MEDALS:";G(1) 1770 IF S(1)<1 THEN 1790 1780 PRINT "SILVER MEDALS:";S(1) 1790 IF B(1)<1 THEN 1830 1800 PRINT "BRONZE MEDALS:";B(1) 1810 DATA 14,18,26,29,18,25,28,32,29,20,29,29,25,21,26,29,20,21,20 1820 DATA 18,26,25,33,31,22 1830 END ================================================ FILE: 00_Alternate_Languages/80_Slots/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/80_Slots/MiniScript/slots.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/80_Slots/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/80_Slots/slots.bas ================================================ 10 PRINT TAB(30);"SLOTS" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 30 PRINT: PRINT: PRINT 100 REM PRODUCED BY FRED MIRABELLE AND BOB HARPER ON JAN 29, 1973 110 REM IT SIMULATES THE SLOT MACHINE. 120 PRINT "YOU ARE IN THE H&M CASINO,IN FRONT OF ONE OF OUR" 130 PRINT "ONE-ARM BANDITS. BET FROM $1 TO $100." 140 PRINT "TO PULL THE ARM, PUNCH THE RETURN KEY AFTER MAKING YOUR BET." 150 LET P=0 160 PRINT: PRINT"YOUR BET"; 170 INPUT M 180 IF M>100 THEN 860 190 IF M<1 THEN 880 200 M=INT(M) 210 GOSUB 1270 220 PRINT 230 LET X=INT(6*RND(1)+1) 240 LET Y=INT(6*RND(1)+1) 250 LET Z=INT(6*RND(1)+1) 260 PRINT 270 IF X=1 THEN 910 280 IF X=2 THEN 930 290 IF X=3 THEN 950 300 IF X=4 THEN 970 310 IF X=5 THEN 990 320 IF X=6 THEN 1010 330 IF Y=1 THEN 1030 340 IF Y=2 THEN 1050 350 IF Y=3 THEN 1070 360 IF Y=4 THEN 1090 370 IF Y=5 THEN 1110 380 IF Y=6 THEN 1130 390 IF Z=1 THEN 1150 400 IF Z=2 THEN 1170 410 IF Z=3 THEN 1190 420 IF Z=4 THEN 1210 430 IF Z=5 THEN 1230 440 IF Z=6 THEN 1250 450 IF X=Y THEN 600 460 IF X=Z THEN 630 470 IF Y=Z THEN 650 480 PRINT 490 PRINT "YOU LOST." 500 LET P=P-M 510 PRINT "YOUR STANDINGS ARE $"P 520 PRINT "AGAIN"; 530 INPUT A$ 540 IF A$="Y" THEN 160 550 PRINT 560 IF P<0 THEN 670 570 IF P=0 THEN 690 580 IF P>0 THEN 710 590 GOTO 1350 600 IF Y=Z THEN 730 610 IF Y=1 THEN 820 620 GOTO 1341 630 IF Z=1 THEN 820 640 GOTO 470 650 IF Z=1 THEN 820 660 GOTO 1341 670 PRINT "PAY UP! PLEASE LEAVE YOUR MONEY ON THE TERMINAL." 680 GOTO 1350 690 PRINT"HEY, YOU BROKE EVEN." 700 GOTO 1350 710 PRINT "COLLECT YOUR WINNINGS FROM THE H&M CASHIER." 720 GOTO 1350 730 IF Z=1 THEN 780 740 PRINT: PRINT"**TOP DOLLAR**" 750 PRINT "YOU WON!" 760 P=(((10*M)+M)+P) 770 GOTO 510 780 PRINT:PRINT"***JACKPOT***" 790 PRINT "YOU WON!" 800 P=(((100*M)+M)+P) 810 GOTO 510 820 PRINT:PRINT"*DOUBLE BAR*" 830 PRINT"YOU WON!" 840 P=(((5*M)+M)+P) 850 GOTO 510 860 PRINT"HOUSE LIMITS ARE $100" 870 GOTO 160 880 PRINT"MINIMUM BET IS $1" 890 GOTO 160 900 GOTO 220 910 PRINT"BAR";:GOSUB 1310 920 GOTO 330 930 PRINT"BELL";:GOSUB 1310 940 GOTO 330 950 PRINT"ORANGE";:GOSUB 1310 960 GOTO 330 970 PRINT"LEMON";:GOSUB 1310 980 GOTO 330 990 PRINT"PLUM";:GOSUB 1310 1000 GOTO 330 1010 PRINT"CHERRY";:GOSUB 1310 1020 GOTO 330 1030 PRINT" BAR";:GOSUB 1310 1040 GOTO 390 1050 PRINT" BELL";:GOSUB 1310 1060 GOTO 390 1070 PRINT" ORANGE";:GOSUB 1310 1080 GOTO 390 1090 PRINT" LEMON";:GOSUB 1310 1100 GOTO 390 1110 PRINT" PLUM";:GOSUB 1310 1120 GOTO 390 1130 PRINT" CHERRY";:GOSUB 1310 1140 GOTO 390 1150 PRINT" BAR" 1160 GOTO 450 1170 PRINT" BELL" 1180 GOTO 450 1190 PRINT" ORANGE" 1200 GOTO 450 1210 PRINT" LEMON" 1220 GOTO 450 1230 PRINT" PLUM" 1240 GOTO 450 1250 PRINT" CHERRY" 1260 GOTO 450 1270 FOR Q4=1 TO 10 1280 PRINT CHR$(7); 1290 NEXT Q4 1300 RETURN 1310 FOR T8=1 TO 5 1320 PRINT CHR$(7); 1330 NEXT T8 1340 RETURN 1341 PRINT: PRINT "DOUBLE!!" 1342 PRINT"YOU WON!" 1343 P=(((2*M)+M)+P) 1344 GOTO 510 1350 STOP 9999 END ================================================ FILE: 00_Alternate_Languages/81_Splat/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/81_Splat/MiniScript/splat.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/81_Splat/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/81_Splat/splat.bas ================================================ 10 PRINT TAB(33);"SPLAT" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 40 PRINT:PRINT:PRINT 50 DIM A(42) 95 PRINT "WELCOME TO 'SPLAT' -- THE GAME THAT SIMULATES A PARACHUTE" 96 PRINT "JUMP. TRY TO OPEN YOUR CHUTE AT THE LAST POSSIBLE" 97 PRINT "MOMENT WITHOUT GOING SPLAT." 118 PRINT:PRINT:D1=0:V=0:A=0:N=0:M=0:D1=INT(9001*RND(1)+1000) 119 PRINT "SELECT YOUR OWN TERMINAL VELOCITY (YES OR NO)";:INPUT A1$ 120 IF A1$="NO" THEN 128 121 IF A1$<>"YES" THEN PRINT "YES OR NO";:INPUT A1$:GOTO 120 123 PRINT "WHAT TERMINAL VELOCITY (MI/HR)";:INPUT V1 125 V1=V1*(5280/3600):V=V1+((V1*RND(1))/20)-((V1*RND(1))/20):GOTO 135 128 V1=INT(1000*RND(1)) 130 PRINT "OK. TERMINAL VELOCITY ="V1"MI/HR" 131 V1=V1*(5280/3600):V=V1+((V1*RND(1))/20)-((V1*RND(1))/20) 135 PRINT "WANT TO SELECT ACCELERATION DUE TO GRAVITY (YES OR NO)"; 136 INPUT B1$ 140 IF B1$="NO" THEN 150 141 IF B1$<>"YES" THEN PRINT "YES OR NO";:INPUT B1$:GOTO 140 143 PRINT "WHAT ACCELERATION (FT/SEC/SEC)";:INPUT A2 145 A=A2+((A2*RND(1))/20)-((A2*RND(1))/20):GOTO 205 150 ON INT(1+(10*RND(1)))GOTO 151,152,153,154,155,156,157,158,159,160 151 PRINT"FINE. YOU'RE ON MERCURY. ACCELERATION=12.2 FT/SEC/SEC.":GOTO 161 152 PRINT"ALL RIGHT. YOU'RE ON VENUS. ACCELERATION=28.3 FT/SEC/SEC.":GOTO 162 153 PRINT "THEN YOU'RE ON EARTH. ACCELERATION=32.16 FT/SEC/SEC.":GOTO 163 154 PRINT"FINE. YOU'RE ON THE MOON. ACCELERATION=5.15 FT/SEC/SEC.":GOTO 164 155 PRINT"ALL RIGHT. YOU'RE ON MARS. ACCELERATION=12.5 FT/SEC/SEC.":GOTO 165 156 PRINT"THEN YOU'RE ON JUPITER. ACCELERATION=85.2 FT/SEC/SEC.":GOTO 166 157 PRINT"FINE. YOU'RE ON SATURN. ACCELERATION=37.6 FT/SEC/SEC.":GOTO 167 158 PRINT"ALL RIGHT. YOU'RE ON URANUS. ACCELERATION=33.8 FT/SEC/SEC.":GOTO 168 159 PRINT"THEN YOU'RE ON NEPTUNE. ACCELERATION=39.6 FT/SEC/SEC.":GOTO 169 160 PRINT"FINE. YOU'RE ON THE SUN. ACCELERATION=896 FT/SEC/SEC.":GOTO 170 161 A2=12.2:GOTO 145 162 A2=28.3:GOTO 145 163 A2=32.16:GOTO 145 164 A2=5.15:GOTO 145 165 A2=12.5:GOTO 145 166 A2=85.2:GOTO 145 167 A2=37.6:GOTO 145 168 A2=33.8 :GOTO 145 169 A2=39.6:GOTO 145 170 A2=896:GOTO 145 205 PRINT 206 PRINT " ALTITUDE ="D1"FT" 207 PRINT " TERM. VELOCITY ="V1"FT/SEC +/-5%" 208 PRINT " ACCELERATION ="A2"FT/SEC/SEC +/-5%" 210 PRINT "SET THE TIMER FOR YOUR FREEFALL." 211 PRINT "HOW MANY SECONDS";:INPUT T 215 PRINT "HERE WE GO." 217 PRINT 218 PRINT "TIME (SEC)","DIST TO FALL (FT)" 219 PRINT "==========","=================" 300 FOR I=0 TO T STEP (T/8) 310 IF I>V/A THEN 400 320 D=D1-((A/2)*I^2) 330 IF D<=0 THEN 1000 340 PRINT I,D 350 NEXT I 360 GOTO 500 400 PRINT "TERMINAL VELOCITY REACHED AT T PLUS"V/A"SECONDS." 405 FOR I=I TO T STEP (T/8) 410 D=D1-((V^2/(2*A))+(V*(I-(V/A)))) 420 IF D<=0 THEN 1010 430 PRINT I,D 440 NEXT I 500 PRINT "CHUTE OPEN" 510 K=0:K1=0 550 FOR J=0 TO 42 555 IF A(J)=0 THEN 620 560 K=K+1 570 IF D>=A(J) THEN 600 580 K1=K1+1 600 NEXT J 610 GOTO 540 620 A(J)=D 630 IF J>2 THEN 650 635 PRINT "AMAZING!!! NOT BAD FOR YOUR "; 636 IF J=0 THEN PRINT "1ST "; 637 IF J=1 THEN PRINT "2ND "; 638 IF J=2 THEN PRINT "3RD "; 639 PRINT "SUCCESSFUL JUMP!!!":GOTO 2000 650 IF K-K1<=.1*K THEN 700 660 IF K-K1<=.25*K THEN 710 670 IF K-K1<=.5*K THEN 720 680 IF K-K1<=.75*K THEN 730 690 IF K-K1<=.9*K THEN 740 695 GOTO 750 700 PRINT "WOW! THAT'S SOME JUMPING. OF THE"K"SUCCESSFUL JUMPS" 701 PRINT "BEFORE YOURS, ONLY"K-K1"OPENED THEIR CHUTES LOWER THAN" 702 PRINT "YOU DID." 703 GOTO 2000 710 PRINT "PRETTY GOOD! " K"SUCCESSFUL JUMPS PRECEDED YOURS AND ONLY" 711 PRINT K-K1" OF THEM GOT LOWER THAN YOU DID BEFORE THEIR CHUTES" 712 PRINT "OPENED." :GOTO 2000 720 PRINT "NOT BAD. THERE HAVE BEEN"K"SUCCESSFUL JUMPS BEFORE YOURS." 721 PRINT"YOU WERE BEATEN OUT BY"K-K1"OF THEM.":GOTO 2000 730 PRINT "CONSERVATIVE, AREN'T YOU? YOU RANKED ONLY"K-K1"IN THE" 731 PRINT K"SUCCESSFUL JUMPS BEFORE YOURS.":GOTO 2000 740 PRINT "HUMPH! DON'T YOU HAVE ANY SPORTING BLOOD? THERE WERE" 741 PRINT K"SUCCESSFUL JUMPS BEFORE YOURS AND YOU CAME IN"K1"JUMPS" 742 PRINT "BETTER THAN THE WORST. SHAPE UP!!!":GOTO 2000 750 PRINT "HEY! YOU PULLED THE RIP CORD MUCH TOO SOON. "K"SUCCESSFUL" 751 PRINT "JUMPS BEFORE YOURS AND YOU CAME IN NUMBER"K-K1"! GET WITH IT!" 752 GOTO 2000 800 PRINT "REQUIESCAT IN PACE.":GOTO 1950 801 PRINT "MAY THE ANGEL OF HEAVEN LEAD YOU INTO PARADISE.":GOTO 1950 802 PRINT "REST IN PEACE.":GOTO 1950 803 PRINT "SON-OF-A-GUN.":GOTO 1950 804 PRINT "#$%&&%!$":GOTO 1950 805 PRINT "A KICK IN THE PANTS IS A BOOST IF YOU'RE HEADED RIGHT.":GOTO 1950 806 PRINT "HMMM. SHOULD HAVE PICKED A SHORTER TIME.":GOTO 1950 807 PRINT "MUTTER. MUTTER. MUTTER.":GOTO 1950 808 PRINT "PUSHING UP DAISIES.":GOTO 1950 809 PRINT "EASY COME, EASY GO.":GOTO 1950 1000 PRINT SQR(2*D1/A),"SPLAT" 1005 ON INT(1+(10*RND(1)))GOTO 800,801,802,803,804,805,806,807,808,809 1010 PRINT (V/A)+((D1-(V^2/(2*A)))/V),"SPLAT" 1020 GOTO 1005 1950 PRINT "I'LL GIVE YOU ANOTHER CHANCE.":GOTO 2000 2000 PRINT "DO YOU WANT TO PLAY AGAIN";:INPUT Z$ 2001 IF Z$="YES" THEN 118 2002 IF Z$="NO" THEN 2005 2003 PRINT "YES OR NO":GOTO 2000 2005 PRINT "PLEASE";:INPUT Z$:IF Z$="YES" THEN 118 2006 IF Z$<>"NO" THEN PRINT "YES OR NO ";:GOTO 2005 2007 PRINT "SSSSSSSSSS.":PRINT:GOTO 2046 2046 END ================================================ FILE: 00_Alternate_Languages/82_Stars/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/82_Stars/MiniScript/stars.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/82_Stars/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/82_Stars/stars.bas ================================================ 10 PRINT TAB(34);"STARS" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 30 PRINT:PRINT:PRINT 100 REM *** STARS - PEOPLE'S COMPUTER CENTER, MENLO PARK, CA 140 REM *** A IS LIMIT ON NUMBER, M IS NUMBER OF GUESSES 150 A=100:M=7 170 INPUT "DO YOU WANT INSTRUCTIONS";A$ 190 IF LEFT$(A$,1)="N" THEN 280 200 REM *** INSTRUCTIONS ON HOW TO PLAY 210 PRINT "I AM THINKING OF A WHOLE NUMBER FROM 1 TO";A 220 PRINT "TRY TO GUESS MY NUMBER. AFTER YOU GUESS, I" 230 PRINT "WILL TYPE ONE OR MORE STARS (*). THE MORE" 240 PRINT "STARS I TYPE, THE CLOSER YOU ARE TO MY NUMBER." 250 PRINT "ONE STAR (*) MEANS FAR AWAY, SEVEN STARS (*******)" 260 PRINT "MEANS REALLY CLOSE! YOU GET";M;"GUESSES." 270 REM *** COMPUTER THINKS OF A NUMBER 280 PRINT 290 PRINT 300 X=INT(A*RND(1)+1) 310 PRINT "OK, I AM THINKING OF A NUMBER, START GUESSING." 320 REM *** GUESSING BEGINS, HUMAN GETS M GUESSES 330 FOR K=1 TO M 340 PRINT 350 PRINT "YOUR GUESS"; 360 INPUT G 370 IF G=X THEN 600 380 D=ABS(G-X) 390 IF D>=64 THEN 510 400 IF D>=32 THEN 500 410 IF D>=16 THEN 490 420 IF D>=8 THEN 480 430 IF D>=4 THEN 470 440 IF D>=2 THEN 460 450 PRINT "*"; 460 PRINT "*"; 470 PRINT "*"; 480 PRINT "*"; 490 PRINT "*"; 500 PRINT "*"; 510 PRINT "*"; 520 PRINT 530 NEXT K 540 REM *** DID NOT GUESS IN M GUESSES 550 PRINT 560 PRINT "SORRY, THAT'S";M;"GUESSES. THE NUMBER WAS";X 580 GOTO 650 590 REM *** WE HAVE A WINNER 600 PRINT:FOR N=1 TO 79 610 PRINT "*"; 620 NEXT N 630 PRINT:PRINT 640 PRINT "YOU GOT IT IN";K;"GUESSES!!! LET'S PLAY AGAIN..." 650 GOTO 280 660 END ================================================ FILE: 00_Alternate_Languages/83_Stock_Market/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/83_Stock_Market/MiniScript/stockmarket.ms ================================================ 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!!" ================================================ FILE: 00_Alternate_Languages/83_Stock_Market/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/83_Stock_Market/stockmarket.bas ================================================ 10 PRINT TAB(30);"STOCK MARKET" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 30 PRINT: PRINT: PRINT 100 REM STOCK MARKET SIMULATION -STOCK- 101 REM REVISED 8/18/70 (D. PESSEL, L. BRAUN, C. LOSIK) 102 REM IMP VRBLS: A-MRKT TRND SLP; B5-BRKRGE FEE; C-TTL CSH ASSTS; 103 REM C5-TTL CSH ASSTS (TEMP); C(I)-CHNG IN STK VAL; D-TTL ASSTS; 104 REM E1,E2-LRG CHNG MISC; I-STCK #; I1,I2-STCKS W LRG CHNG; 105 REM N1,N2-LRG CHNG DAY CNTS; P5-TTL DAYS PRCHSS; P(I)-PRTFL CNTNTS; 106 REM Q9-NEW CYCL?; S4-SGN OF A; S5-TTL DYS SLS; S(I)-VALUE/SHR; 107 REM T-TTL STCK ASSTS; T5-TTL VAL OF TRNSCTNS; 108 REM W3-LRG CHNG; X1-SMLL CHNG(<$1); Z4,Z5,Z6-NYSE AVE.; Z(I)-TRNSCT 110 DIM S(5),P(5),Z(5),C(5) 112 REM SLOPE OF MARKET TREND:A (SAME FOR ALL STOCKS) 113 LET X=1 114 LET A=INT((RND(X)/10)*100+.5)/100 115 LET T5=0 116 LET X9=0 117 LET N1=0 118 LET N2=0 119 LET E1=0 120 LET E2=0 121 REM INTRODUCTION 122 PRINT "DO YOU WANT THE INSTRUCTIONS (YES-TYPE 1, NO-TYPE 0)"; 123 INPUT Z9 124 PRINT 125 PRINT 126 IF Z9<1 THEN 200 130 PRINT "THIS PROGRAM PLAYS THE STOCK MARKET. YOU WILL BE GIVEN" 132 PRINT "$10,000 AND MAY BUY OR SELL STOCKS. THE STOCK PRICES WILL" 134 PRINT "BE GENERATED RANDOMLY AND THEREFORE THIS MODEL DOES NOT" 135 PRINT "REPRESENT EXACTLY WHAT HAPPENS ON THE EXCHANGE. A TABLE" 136 PRINT "OF AVAILABLE STOCKS, THEIR PRICES, AND THE NUMBER OF SHARES" 137 PRINT "IN YOUR PORTFOLIO WILL BE PRINTED. FOLLOWING THIS, THE" 138 PRINT "INITIALS OF EACH STOCK WILL BE PRINTED WITH A QUESTION" 139 PRINT "MARK. HERE YOU INDICATE A TRANSACTION. TO BUY A STOCK" 140 PRINT "TYPE +NNN, TO SELL A STOCK TYPE -NNN, WHERE NNN IS THE" 141 PRINT "NUMBER OF SHARES. A BROKERAGE FEE OF 1% WILL BE CHARGED" 142 PRINT "ON ALL TRANSACTIONS. NOTE THAT IF A STOCK'S VALUE DROPS" 143 PRINT "TO ZERO IT MAY REBOUND TO A POSITIVE VALUE AGAIN. YOU" 144 PRINT "HAVE $10,000 TO INVEST. USE INTEGERS FOR ALL YOUR INPUTS." 145 PRINT "(NOTE: TO GET A 'FEEL' FOR THE MARKET RUN FOR AT LEAST" 146 PRINT "10 DAYS)" 147 PRINT "-----GOOD LUCK!-----" 200 REM GENERATION OF STOCK TABLE; INPUT REQUESTS 210 REM INITIAL STOCK VALUES 220 LET S(1)=100 230 LET S(2)=85 240 LET S(3)=150 250 LET S(4)=140 260 LET S(5)=110 265 REM INITIAL T8 - # DAYS FOR FIRST TREND SLOPE (A) 266 LET T8=INT(4.99*RND(X)+1) 267 REM RANDOMIZE SIGN OF FIRST TREND SLOPE (A) 268 IF RND(X)>.5 THEN 270 269 LET A=-A 270 REM RANDOMIZE INITIAL VALUES 280 GOSUB 830 285 REM INITIAL PORTFOLIO CONTENTS 290 FOR I=1 TO 5 300 LET P(I)=0 305 LET Z(I)=0 310 NEXT I 320 PRINT 330 PRINT 333 REM INITIALIZE CASH ASSETS:C 335 LET C=10000 338 REM PRINT INITIAL PORTFOLIO 340 PRINT "STOCK"," ","INITIALS","PRICE/SHARE" 350 PRINT "INT. BALLISTIC MISSILES"," IBM",S(1) 352 PRINT "RED CROSS OF AMERICA"," RCA",S(2) 354 PRINT "LICHTENSTEIN, BUMRAP & JOKE"," LBJ",S(3) 356 PRINT "AMERICAN BANKRUPT CO."," ABC",S(4) 358 PRINT "CENSURED BOOKS STORE"," CBS",S(5) 360 PRINT 361 REM NYSE AVERAGE:Z5; TEMP. VALUE:Z4; NET CHANGE:Z6 363 LET Z4=Z5 364 LET Z5=0 365 LET T=0 370 FOR I=1 TO 5 375 LET Z5=Z5+S(I) 380 LET T=T+S(I)*P(I) 390 NEXT I 391 LET Z5=INT(100*(Z5/5)+.5)/100 392 LET Z6=INT((Z5-Z4)*100+.5)/100 393 REM TOTAL ASSETS:D 394 LET D=T+C 395 IF X9>0 THEN 398 396 PRINT "NEW YORK STOCK EXCHANGE AVERAGE: "Z5 397 GOTO 399 398 PRINT "NEW YORK STOCK EXCHANGE AVERAGE: "Z5"NET CHANGE"Z6 399 PRINT 400 LET T=INT(100*T+.5)/100 401 PRINT "TOTAL STOCK ASSETS ARE $";T 403 LET C=INT(100*C+.5)/100 405 PRINT "TOTAL CASH ASSETS ARE $";C 407 LET D=INT(100*D+.5)/100 408 PRINT "TOTAL ASSETS ARE $";D 410 PRINT 411 IF X9=0 THEN 416 412 PRINT "DO YOU WISH TO CONTINUE (YES-TYPE 1, NO-TYPE 0)"; 413 INPUT Q9 414 IF Q9<1 THEN 998 416 REM INPUT TRANSACTIONS 420 PRINT "WHAT IS YOUR TRANSACTION IN" 430 PRINT "IBM"; 440 INPUT Z(1) 450 PRINT "RCA"; 460 INPUT Z(2) 470 PRINT "LBJ"; 480 INPUT Z(3) 490 PRINT "ABC"; 500 INPUT Z(4) 510 PRINT "CBS"; 520 INPUT Z(5) 525 PRINT 530 REM TOTAL DAY'S PURCHASES IN $:P5 540 LET P5=0 550 REM TOTAL DAY'S SALES IN $:S5 560 LET S5=0 570 FOR I=1 TO 5 575 LET Z(I)=INT(Z(I)+.5) 580 IF Z(I)<=0 THEN 610 590 LET P5=P5+Z(I)*S(I) 600 GOTO 620 610 LET S5=S5-Z(I)*S(I) 612 IF -Z(I)<=P(I) THEN 620 614 PRINT "YOU HAVE OVERSOLD A STOCK; TRY AGAIN." 616 GOTO 420 620 NEXT I 622 REM TOTAL VALUE OF TRANSACTIONS:T5 625 LET T5=P5+S5 630 REM BROKERAGE FEE:B5 640 LET B5=INT(.01*T5*100+.5)/100 650 REM CASH ASSETS=OLD CASH ASSETS-TOTAL PURCHASES 652 REM -BROKERAGE FEES+TOTAL SALES:C5 654 LET C5=C-P5-B5+S5 656 IF C5>=0 THEN 674 658 PRINT "YOU HAVE USED $"-C5" MORE THAN YOU HAVE." 660 GOTO 420 674 LET C=C5 675 REM CALCULATE NEW PORTFOLIO 680 FOR I=1 TO 5 690 LET P(I)=P(I)+Z(I) 700 NEXT I 710 REM CALCULATE NEW STOCK VALUES 720 GOSUB 830 750 REM PRINT PORTFOLIO 751 REM BELL RINGING-DIFFERENT ON MANY COMPUTERS 755 PRINT 756 PRINT "********** END OF DAY'S TRADING **********" 757 PRINT 758 PRINT 759 IF X9<1 THEN 769 769 PRINT "STOCK","PRICE/SHARE","HOLDINGS", "VALUE", "NET PRICE CHANGE" 770 PRINT "IBM", S(1), P(1), S(1)*P(1), C(1) 771 PRINT "RCA", S(2), P(2), S(2)*P(2), C(2) 772 PRINT "LBJ", S(3), P(3), S(3)*P(3), C(3) 773 PRINT "ABC", S(4), P(4), S(4)*P(4), C(4) 774 PRINT "CBS", S(5), P(5), S(5)*P(5), C(5) 775 LET X9=1 780 PRINT 790 PRINT 810 GOTO 360 829 REM NEW STOCK VALUES - SUBROUTINE 830 REM RANDOMLY PRODUCE NEW STOCK VALUES BASED ON PREVIOUS 831 REM DAY'S VALUES 832 REM N1,N2 ARE RANDOM NUMBERS OF DAYS WHICH RESPECTIVELY 833 REM DETERMINE WHEN STOCK I1 WILL INCREASE 10 PTS. AND STOCK 834 REM I2 WILL DECREASE 10 PTS. 840 REM IF N1 DAYS HAVE PASSED, PICK AN I1, SET E1, DETERMINE NEW N1 841 IF N1>0 THEN 850 845 LET I1=INT(4.99*RND(X)+1) 846 LET N1=INT(4.99*RND(X)+1) 847 LET E1=1 850 REM IF N2 DAYS HAVE PASSED, PICK AN I2, SET E2, DETERMINE NEW N2 851 IF N2>0 THEN 860 855 LET I2=INT(4.99*RND(X)+1) 856 LET N2=INT(4.99*RND(X)+1) 857 LET E2=1 860 REM DEDUCT ONE DAY FROM N1 AND N2 861 LET N1=N1-1 862 LET N2=N2-1 890 REM LOOP THROUGH ALL STOCKS 900 FOR I=1 TO 5 910 LET X1=RND(X) 915 IF X1>.25 THEN 920 916 LET X1=.25 917 GOTO 935 920 IF X1>.5 THEN 925 921 LET X1=.5 922 GOTO 935 925 IF X1>.75 THEN 930 926 LET X1=.75 927 GOTO 935 930 LET X1=0.0 931 REM BIG CHANGE CONSTANT:W3 (SET TO ZERO INITIALLY) 935 LET W3=0 936 IF E1<1 THEN 945 937 IF INT(I1+.5)<>INT(I+.5) THEN 945 938 REM ADD 10 PTS. TO THIS STOCK; RESET E1 939 LET W3=10 943 LET E1=0 945 IF E2<1 THEN 955 947 IF INT(I2+.5)<>INT(I+.5) THEN 955 948 REM SUBTRACT 10 PTS. FROM THIS STOCK; RESET E2 949 LET W3=W3-10 953 LET E2=0 954 REM C(I) IS CHANGE IN STOCK VALUE 955 LET C(I)=INT(A*S(I))+X1+INT(3-6*RND(X)+.5)+W3 956 LET C(I)=INT(100*C(I)+.5)/100 957 LET S(I)=S(I)+C(I) 960 IF S(I)>0 THEN 967 964 LET C(I)=0 965 LET S(I)=0 966 GOTO 970 967 LET S(I)=INT(100*S(I)+.5)/100 970 NEXT I 972 REM AFTER T8 DAYS RANDOMLY CHANGE TREND SIGN AND SLOPE 973 LET T8=T8-1 974 IF T8<1 THEN 985 980 RETURN 985 REM RANDOMLY CHANGE TREND SIGN AND SLOPE (A), AND DURATION 986 REM OF TREND (T8) 990 LET T8=INT(4.99*RND(X)+1) 992 LET A=INT((RND(X)/10)*100+.5)/100 993 LET S4=RND(X) 994 IF S4<=.5 THEN 997 995 LET A=-A 997 RETURN 998 PRINT "HOPE YOU HAD FUN!!" 999 END ================================================ FILE: 00_Alternate_Languages/84_Super_Star_Trek/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/84_Super_Star_Trek/MiniScript/superstartrek.ms ================================================ // 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: <klingons><bases><stars> 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 " <Shields down to " + ship.shields + " units>" 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 "<Shields unchanged>" 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 ================================================ FILE: 00_Alternate_Languages/84_Super_Star_Trek/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/84_Super_Star_Trek/instructions.txt ================================================ ************************************* * * * * * * * SUPER STAR TREK * * * * * * * ************************************* 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), THAN 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 COMANDER OF THE STARSHIP \ENTERPRISE\; YOUR MISSION: TO SEEK AND DESTROY THE FLEET OF KLINGON WARWHIPS 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 TH 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 THAN THE STATUS DISPLAY TOTAL ENERGY INCLUDES SHIELD ENERGY \DAM\ COMMAND = DAMMAGE 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 SHOWES 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. ================================================ FILE: 00_Alternate_Languages/84_Super_Star_Trek/superstartrek.bas ================================================ 10 REM SUPER STARTREK - MAY 16,1978 - REQUIRES 24K MEMORY 30 REM 40 REM **** **** STAR TREK **** **** 50 REM **** SIMULATION OF A MISSION OF THE STARSHIP ENTERPRISE, 60 REM **** AS SEEN ON THE STAR TREK TV SHOW. 70 REM **** ORIGIONAL PROGRAM BY MIKE MAYFIELD, MODIFIED VERSION 80 REM **** PUBLISHED IN DEC'S "101 BASIC GAMES", BY DAVE AHL. 90 REM **** MODIFICATIONS TO THE LATTER (PLUS DEBUGGING) BY BOB 100 REM *** LEEDOM - APRIL & DECEMBER 1974, 110 REM *** WITH A LITTLE HELP FROM HIS FRIENDS . . . 120 REM *** COMMENTS, EPITHETS, AND SUGGESTIONS SOLICITED -- 130 REM *** SEND TO: R. C. LEEDOM 140 REM *** WESTINGHOUSE DEFENSE & ELECTRONICS SYSTEMS CNTR. 150 REM *** BOX 746, M.S. 338 160 REM *** BALTIMORE, MD 21203 170 REM *** 180 REM *** CONVERTED TO MICROSOFT 8 K BASIC 3/16/78 BY JOHN GORDERS 190 REM *** LINE NUMBERS FROM VERSION STREK7 OF 1/12/75 PRESERVED AS 200 REM *** MUCH AS POSSIBLE WHILE USING MULTIPLE STATEMENTS PER LINE 205 REM *** SOME LINES ARE LONGER THAN 72 CHARACTERS; THIS WAS DONE 210 REM *** BY USING "?" INSTEAD OF "PRINT" WHEN ENTERING LINES 215 REM *** 220 PRINT:PRINT:PRINT:PRINT:PRINT:PRINT:PRINT:PRINT:PRINT:PRINT:PRINT 221 PRINT " ,------*------," 222 PRINT " ,------------- '--- ------'" 223 PRINT " '-------- --' / /" 224 PRINT " ,---' '-------/ /--," 225 PRINT " '----------------'":PRINT 226 PRINT " THE USS ENTERPRISE --- NCC-1701" 227 PRINT:PRINT:PRINT:PRINT:PRINT 260 REM CLEAR 600 270 Z$=" " 330 DIM G(8,8),C(9,2),K(3,3),N(3),Z(8,8),D(8) 370 T=INT(RND(1)*20+20)*100:T0=T:T9=25+INT(RND(1)*10):D0=0:E=3000:E0=E 440 P=10:P0=P:S9=200:S=0:B9=2:K9=0:X$="":X0$=" IS " 470 DEF FND(D)=SQR((K(I,1)-S1)^2+(K(I,2)-S2)^2) 475 DEF FNR(R)=INT(RND(R)*7.98+1.01) 480 REM INITIALIZE ENTERPRIZE'S POSITION 490 Q1=FNR(1):Q2=FNR(1):S1=FNR(1):S2=FNR(1) 530 FOR I=1 TO 9:C(I,1)=0:C(I,2)=0:NEXT I 540 C(3,1)=-1:C(2,1)=-1:C(4,1)=-1:C(4,2)=-1:C(5,2)=-1:C(6,2)=-1 600 C(1,2)=1:C(2,2)=1:C(6,1)=1:C(7,1)=1:C(8,1)=1:C(8,2)=1:C(9,2)=1 670 FOR I=1 TO 8:D(I)=0:NEXT I 710 A1$="NAVSRSLRSPHATORSHEDAMCOMXXX" 810 REM SETUP WHAT EXHISTS IN GALAXY . . . 815 REM K3= # KLINGONS B3= # STARBASES S3 = # STARS 820 FOR I=1 TO 8:FOR J=1 TO 8:K3=0:Z(I,J)=0:R1=RND(1) 850 IF R1>.98 THEN K3=3:K9=K9+3:GOTO 980 860 IF R1>.95 THEN K3=2:K9=K9+2:GOTO 980 870 IF R1>.80 THEN K3=1:K9=K9+1 980 B3=0:IF RND(1)>.96 THEN B3=1:B9=B9+1 1040 G(I,J)=K3*100+B3*10+FNR(1):NEXT J:NEXT I:IF K9>T9 THEN T9=K9+1 1100 IF B9<>0 THEN 1200 1150 IF G(Q1,Q2)<200 THEN G(Q1,Q2)=G(Q1,Q2)+120:K9=K9+1 1160 B9=1:G(Q1,Q2)=G(Q1,Q2)+10:Q1=FNR(1):Q2=FNR(1) 1200 K7=K9:IF B9<>1 THEN X$="S":X0$=" ARE " 1230 PRINT "YOUR ORDERS ARE AS FOLLOWS:" 1240 PRINT " DESTROY THE";K9;"KLINGON WARSHIPS WHICH HAVE INVADED" 1252 PRINT " THE GALAXY BEFORE THEY CAN ATTACK FEDERATION HEADQUARTERS" 1260 PRINT " ON STARDATE";T0+T9;" THIS GIVES YOU";T9;"DAYS. THERE";X0$ 1272 PRINT " ";B9;"STARBASE";X$;" IN THE GALAXY FOR RESUPPLYING YOUR SHIP" 1280 PRINT:REM PRINT "HIT ANY KEY EXCEPT RETURN WHEN READY TO ACCEPT COMMAND" 1300 I=RND(1):REM IF INP(1)=13 THEN 1300 1310 REM HERE ANY TIME NEW QUADRANT ENTERED 1320 Z4=Q1:Z5=Q2:K3=0:B3=0:S3=0:G5=0:D4=.5*RND(1):Z(Q1,Q2)=G(Q1,Q2) 1390 IF Q1<1 OR Q1>8 OR Q2<1 OR Q2>8 THEN 1600 1430 GOSUB 9030:PRINT:IF T0<>T THEN 1490 1460 PRINT "YOUR MISSION BEGINS WITH YOUR STARSHIP LOCATED" 1470 PRINT "IN THE GALACTIC QUADRANT, '";G2$;"'.":GOTO 1500 1490 PRINT "NOW ENTERING ";G2$;" QUADRANT . . ." 1500 PRINT:K3=INT(G(Q1,Q2)*.01):B3=INT(G(Q1,Q2)*.1)-10*K3 1540 S3=G(Q1,Q2)-100*K3-10*B3:IF K3=0 THEN 1590 1560 PRINT "COMBAT AREA CONDITION RED":IF S>200 THEN 1590 1580 PRINT " SHIELDS DANGEROUSLY LOW" 1590 FOR I=1 TO 3:K(I,1)=0:K(I,2)=0:NEXT I 1600 FOR I=1 TO 3:K(I,3)=0:NEXT I:Q$=Z$+Z$+Z$+Z$+Z$+Z$+Z$+LEFT$(Z$,17) 1660 REM POSITION ENTERPRISE IN QUADRANT, THEN PLACE "K3" KLINGONS, & 1670 REM "B3" STARBASES, & "S3" STARS ELSEWHERE. 1680 A$="<*>":Z1=S1:Z2=S2:GOSUB 8670:IF K3<1 THEN 1820 1720 FOR I=1 TO K3:GOSUB 8590:A$="+K+":Z1=R1:Z2=R2 1780 GOSUB 8670:K(I,1)=R1:K(I,2)=R2:K(I,3)=S9*(0.5+RND(1)):NEXT I 1820 IF B3<1 THEN 1910 1880 GOSUB 8590:A$=">!<":Z1=R1:B4=R1:Z2=R2:B5=R2:GOSUB 8670 1910 FOR I=1 TO S3:GOSUB 8590:A$=" * ":Z1=R1:Z2=R2:GOSUB 8670:NEXT I 1980 GOSUB 6430 1990 IF S+E>10 THEN IF E>10 OR D(7)=0 THEN 2060 2020 PRINT:PRINT "** FATAL ERROR ** YOU'VE JUST STRANDED YOUR SHIP IN " 2030 PRINT "SPACE":PRINT "YOU HAVE INSUFFICIENT MANEUVERING ENERGY,"; 2040 PRINT " AND SHIELD CONTROL":PRINT "IS PRESENTLY INCAPABLE OF CROSS"; 2050 PRINT "-CIRCUITING TO ENGINE ROOM!!":GOTO 6220 2060 INPUT"COMMAND";A$ 2080 FOR I=1 TO 9:IF LEFT$(A$,3)<>MID$(A1$,3*I-2,3) THEN 2160 2140 ON I GOTO 2300,1980,4000,4260,4700,5530,5690,7290,6270 2160 NEXT I:PRINT "ENTER ONE OF THE FOLLOWING:" 2180 PRINT " NAV (TO SET COURSE)" 2190 PRINT " SRS (FOR SHORT RANGE SENSOR SCAN)" 2200 PRINT " LRS (FOR LONG RANGE SENSOR SCAN)" 2210 PRINT " PHA (TO FIRE PHASERS)" 2220 PRINT " TOR (TO FIRE PHOTON TORPEDOES)" 2230 PRINT " SHE (TO RAISE OR LOWER SHIELDS)" 2240 PRINT " DAM (FOR DAMAGE CONTROL REPORTS)" 2250 PRINT " COM (TO CALL ON LIBRARY-COMPUTER)" 2260 PRINT " XXX (TO RESIGN YOUR COMMAND)":PRINT:GOTO 1990 2290 REM COURSE CONTROL BEGINS HERE 2300 INPUT"COURSE (0-9)";C1:IF C1=9 THEN C1=1 2310 IF C1>=1 AND C1<9 THEN 2350 2330 PRINT " LT. SULU REPORTS, 'INCORRECT COURSE DATA, SIR!'":GOTO 1990 2350 X$="8":IF D(1)<0 THEN X$="0.2" 2360 PRINT "WARP FACTOR (0-";X$;")";:INPUT W1:IF D(1)<0 AND W1>.2 THEN 2470 2380 IF W1>0 AND W1<=8 THEN 2490 2390 IF W1=0 THEN 1990 2420 PRINT " CHIEF ENGINEER SCOTT REPORTS 'THE ENGINES WON'T TAKE"; 2430 PRINT " WARP ";W1;"!'":GOTO 1990 2470 PRINT "WARP ENGINES ARE DAMAGED. MAXIUM SPEED = WARP 0.2":GOTO 1990 2490 N=INT(W1*8+.5):IF E-N>=0 THEN 2590 2500 PRINT "ENGINEERING REPORTS 'INSUFFICIENT ENERGY AVAILABLE" 2510 PRINT " FOR MANEUVERING AT WARP";W1;"!'" 2530 IF S<N-E OR D(7)<0 THEN 1990 2550 PRINT "DEFLECTOR CONTROL ROOM ACKNOWLEDGES";S;"UNITS OF ENERGY" 2560 PRINT " PRESENTLY DEPLOYED TO SHIELDS." 2570 GOTO 1990 2580 REM KLINGONS MOVE/FIRE ON MOVING STARSHIP . . . 2590 FOR I=1 TO K3:IF K(I,3)=0 THEN 2700 2610 A$=" ":Z1=K(I,1):Z2=K(I,2):GOSUB 8670:GOSUB 8590 2660 K(I,1)=Z1:K(I,2)=Z2:A$="+K+":GOSUB 8670 2700 NEXT I:GOSUB 6000:D1=0:D6=W1:IF W1>=1 THEN D6=1 2770 FOR I=1 TO 8:IF D(I)>=0 THEN 2880 2790 D(I)=D(I)+D6:IF D(I)>-.1 AND D(I)<0 THEN D(I)=-.1:GOTO 2880 2800 IF D(I)<0 THEN 2880 2810 IF D1<>1 THEN D1=1:PRINT "DAMAGE CONTROL REPORT: "; 2840 PRINT TAB(8);:R1=I:GOSUB 8790:PRINT G2$;" REPAIR COMPLETED." 2880 NEXT I:IF RND(1)>.2 THEN 3070 2910 R1=FNR(1):IF RND(1)>=.6 THEN 3000 2930 D(R1)=D(R1)-(RND(1)*5+1):PRINT "DAMAGE CONTROL REPORT: "; 2960 GOSUB 8790:PRINT G2$;" DAMAGED":PRINT:GOTO 3070 3000 D(R1)=D(R1)+RND(1)*3+1:PRINT "DAMAGE CONTROL REPORT: "; 3030 GOSUB 8790:PRINT G2$;" STATE OF REPAIR IMPROVED":PRINT 3060 REM BEGIN MOVING STARSHIP 3070 A$=" ":Z1=INT(S1):Z2=INT(S2):GOSUB 8670 3110 X1=C(C1,1)+(C(C1+1,1)-C(C1,1))*(C1-INT(C1)):X=S1:Y=S2 3140 X2=C(C1,2)+(C(C1+1,2)-C(C1,2))*(C1-INT(C1)):Q4=Q1:Q5=Q2 3170 FOR I=1 TO N:S1=S1+X1:S2=S2+X2:IF S1<1 OR S1>=9 OR S2<1 OR S2>=9 THEN 3500 3240 S8=INT(S1)*24+INT(S2)*3-26:IF MID$(Q$,S8,2)=" " THEN 3360 3320 S1=INT(S1-X1):S2=INT(S2-X2):PRINT "WARP ENGINES SHUT DOWN AT "; 3350 PRINT "SECTOR";S1;",";S2;"DUE TO BAD NAVAGATION":GOTO 3370 3360 NEXT I:S1=INT(S1):S2=INT(S2) 3370 A$="<*>":Z1=INT(S1):Z2=INT(S2):GOSUB 8670:GOSUB 3910:T8=1 3430 IF W1<1 THEN T8=.1*INT(10*W1) 3450 T=T+T8:IF T>T0+T9 THEN 6220 3470 REM SEE IF DOCKED, THEN GET COMMAND 3480 GOTO 1980 3490 REM EXCEEDED QUADRANT LIMITS 3500 X=8*Q1+X+N*X1:Y=8*Q2+Y+N*X2:Q1=INT(X/8):Q2=INT(Y/8):S1=INT(X-Q1*8) 3550 S2=INT(Y-Q2*8):IF S1=0 THEN Q1=Q1-1:S1=8 3590 IF S2=0 THEN Q2=Q2-1:S2=8 3620 X5=0:IF Q1<1 THEN X5=1:Q1=1:S1=1 3670 IF Q1>8 THEN X5=1:Q1=8:S1=8 3710 IF Q2<1 THEN X5=1:Q2=1:S2=1 3750 IF Q2>8 THEN X5=1:Q2=8:S2=8 3790 IF X5=0 THEN 3860 3800 PRINT "LT. UHURA REPORTS MESSAGE FROM STARFLEET COMMAND:" 3810 PRINT " 'PERMISSION TO ATTEMPT CROSSING OF GALACTIC PERIMETER" 3820 PRINT " IS HEREBY *DENIED*. SHUT DOWN YOUR ENGINES.'" 3830 PRINT "CHIEF ENGINEER SCOTT REPORTS 'WARP ENGINES SHUT DOWN" 3840 PRINT " AT SECTOR";S1;",";S2;"OF QUADRANT";Q1;",";Q2;".'" 3850 IF T>T0+T9 THEN 6220 3860 IF 8*Q1+Q2=8*Q4+Q5 THEN 3370 3870 T=T+1:GOSUB 3910:GOTO 1320 3900 REM MANEUVER ENERGY S/R ** 3910 E=E-N-10:IF E>=0 THEN RETURN 3930 PRINT "SHIELD CONTROL SUPPLIES ENERGY TO COMPLETE THE MANEUVER." 3940 S=S+E:E=0:IF S<=0 THEN S=0 3980 RETURN 3990 REM LONG RANGE SENSOR SCAN CODE 4000 IF D(3)<0 THEN PRINT "LONG RANGE SENSORS ARE INOPERABLE":GOTO 1990 4030 PRINT "LONG RANGE SCAN FOR QUADRANT";Q1;",";Q2 4040 O1$="-------------------":PRINT O1$ 4060 FOR I=Q1-1TOQ1+1:N(1)=-1:N(2)=-2:N(3)=-3:FOR J=Q2-1TOQ2+1 4120 IF I>0 AND I<9 AND J>0 AND J<9 THEN N(J-Q2+2)=G(I,J):Z(I,J)=G(I,J) 4180 NEXT J:FOR L=1 TO 3:PRINT ": ";:IF N(L)<0 THEN PRINT "*** ";:GOTO 4230 4210 PRINT RIGHT$(STR$(N(L)+1000),3);" "; 4230 NEXT L:PRINT ":":PRINT O1$:NEXT I:GOTO 1990 4250 REM PHASER CONTROL CODE BEGINS HERE 4260 IF D(4)<0 THEN PRINT "PHASERS INOPERATIVE":GOTO 1990 4265 IF K3>0 THEN 4330 4270 PRINT "SCIENCE OFFICER SPOCK REPORTS 'SENSORS SHOW NO ENEMY SHIPS" 4280 PRINT " IN THIS QUADRANT'":GOTO 1990 4330 IF D(8)<0 THEN PRINT "COMPUTER FAILURE HAMPERS ACCURACY" 4350 PRINT "PHASERS LOCKED ON TARGET; "; 4360 PRINT "ENERGY AVAILABLE =";E;"UNITS" 4370 INPUT"NUMBER OF UNITS TO FIRE";X:IF X<=0 THEN 1990 4400 IF E-X<0 THEN 4360 4410 E=E-X:IF D(7)<0 THEN X=X*RND(1) 4450 H1=INT(X/K3):FOR I=1TO3:IF K(I,3)<=0 THEN 4670 4480 H=INT((H1/FND(0))*(RND(1)+2)):IF H>.15*K(I,3) THEN 4530 4500 PRINT "SENSORS SHOW NO DAMAGE TO ENEMY AT ";K(I,1);",";K(I,2):GOTO 4670 4530 K(I,3)=K(I,3)-H:PRINT H;"UNIT HIT ON KLINGON AT SECTOR";K(I,1);","; 4550 PRINT K(I,2):IF K(I,3)<=0 THEN PRINT "*** KLINGON DESTROYED ***":GOTO 4580 4560 PRINT " (SENSORS SHOW";K(I,3);"UNITS REMAINING)":GOTO 4670 4580 K3=K3-1:K9=K9-1:Z1=K(I,1):Z2=K(I,2):A$=" ":GOSUB 8670 4650 K(I,3)=0:G(Q1,Q2)=G(Q1,Q2)-100:Z(Q1,Q2)=G(Q1,Q2):IF K9<=0 THEN 6370 4670 NEXT I:GOSUB 6000:GOTO 1990 4690 REM PHOTON TORPEDO CODE BEGINS HERE 4700 IF P<=0 THEN PRINT "ALL PHOTON TORPEDOES EXPENDED":GOTO 1990 4730 IF D(5)<0 THEN PRINT "PHOTON TUBES ARE NOT OPERATIONAL":GOTO 1990 4760 INPUT"PHOTON TORPEDO COURSE (1-9)";C1:IF C1=9 THEN C1=1 4780 IF C1>=1ANDC1<9 THEN 4850 4790 PRINT "ENSIGN CHEKOV REPORTS, 'INCORRECT COURSE DATA, SIR!'" 4800 GOTO 1990 4850 X1=C(C1,1)+(C(C1+1,1)-C(C1,1))*(C1-INT(C1)):E=E-2:P=P-1 4860 X2=C(C1,2)+(C(C1+1,2)-C(C1,2))*(C1-INT(C1)):X=S1:Y=S2 4910 PRINT "TORPEDO TRACK:" 4920 X=X+X1:Y=Y+X2:X3=INT(X+.5):Y3=INT(Y+.5) 4960 IF X3<1 OR X3>8 OR Y3<1 OR Y3>8 THEN 5490 5000 PRINT " ";X3;",";Y3:A$=" ":Z1=X:Z2=Y:GOSUB 8830 5050 IF Z3<>0 THEN 4920 5060 A$="+K+":Z1=X:Z2=Y:GOSUB 8830:IF Z3=0 THEN 5210 5110 PRINT "*** KLINGON DESTROYED ***":K3=K3-1:K9=K9-1:IF K9<=0 THEN 6370 5150 FOR I=1TO3:IF X3=K(I,1) AND Y3=K(I,2) THEN 5190 5180 NEXT I:I=3 5190 K(I,3)=0:GOTO 5430 5210 A$=" * ":Z1=X:Z2=Y:GOSUB 8830:IF Z3=0 THEN 5280 5260 PRINT "STAR AT";X3;",";Y3;"ABSORBED TORPEDO ENERGY.":GOSUB 6000:GOTO 1990 5280 A$=">!<":Z1=X:Z2=Y:GOSUB 8830:IF Z3=0 THEN 4760 5330 PRINT "*** STARBASE DESTROYED ***":B3=B3-1:B9=B9-1 5360 IF B9>0 OR K9>T-T0-T9 THEN 5400 5370 PRINT "THAT DOES IT, CAPTAIN!! YOU ARE HEREBY RELIEVED OF COMMAND" 5380 PRINT "AND SENTENCED TO 99 STARDATES AT HARD LABOR ON CYGNUS 12!!" 5390 GOTO 6270 5400 PRINT "STARFLEET COMMAND REVIEWING YOUR RECORD TO CONSIDER" 5410 PRINT "COURT MARTIAL!":D0=0 5430 Z1=X:Z2=Y:A$=" ":GOSUB 8670 5470 G(Q1,Q2)=K3*100+B3*10+S3:Z(Q1,Q2)=G(Q1,Q2):GOSUB 6000:GOTO 1990 5490 PRINT "TORPEDO MISSED":GOSUB 6000:GOTO 1990 5520 REM SHIELD CONTROL 5530 IF D(7)<0 THEN PRINT "SHIELD CONTROL INOPERABLE":GOTO 1990 5560 PRINT "ENERGY AVAILABLE =";E+S;:INPUT"NUMBER OF UNITS TO SHIELDS";X 5580 IF X<0 OR S=X THEN PRINT "<SHIELDS UNCHANGED>":GOTO 1990 5590 IF X<=E+S THEN 5630 5600 PRINT "SHIELD CONTROL REPORTS 'THIS IS NOT THE FEDERATION TREASURY.'" 5610 PRINT "<SHIELDS UNCHANGED>":GOTO 1990 5630 E=E+S-X:S=X:PRINT "DEFLECTOR CONTROL ROOM REPORT:" 5660 PRINT " 'SHIELDS NOW AT";INT(S);"UNITS PER YOUR COMMAND.'":GOTO 1990 5680 REM DAMAGE CONTROL 5690 IF D(6)>=0 THEN 5910 5700 PRINT "DAMAGE CONTROL REPORT NOT AVAILABLE":IF D0=0 THEN 1990 5720 D3=0:FOR I=1TO8:IF D(I)<0 THEN D3=D3+.1 5760 NEXT I:IF D3=0 THEN 1990 5780 PRINT:D3=D3+D4:IF D3>=1 THEN D3=.9 5810 PRINT "TECHNICIANS STANDING BY TO EFFECT REPAIRS TO YOUR SHIP;" 5820 PRINT "ESTIMATED TIME TO REPAIR:";.01*INT(100*D3);"STARDATES" 5840 INPUT "WILL YOU AUTHORIZE THE REPAIR ORDER (Y/N)";A$ 5860 IF A$<>"Y" THEN 1990 5870 FOR I=1TO8:IF D(I)<0 THEN D(I)=0 5890 NEXT I:T=T+D3+.1 5910 PRINT:PRINT "DEVICE STATE OF REPAIR":FOR R1=1TO8 5920 GOSUB 8790:PRINT G2$;LEFT$(Z$,25-LEN(G2$));INT(D(R1)*100)*.01 5950 NEXT R1:PRINT:IF D0<>0 THEN 5720 5980 GOTO 1990 5990 REM KLINGONS SHOOTING 6000 IF K3<=0 THEN RETURN 6010 IF D0<>0 THEN PRINT "STARBASE SHIELDS PROTECT THE ENTERPRISE":RETURN 6040 FOR I=1TO3:IF K(I,3)<=0 THEN 6200 6060 H=INT((K(I,3)/FND(1))*(2+RND(1))):S=S-H:K(I,3)=K(I,3)/(3+RND(0)) 6080 PRINT H;"UNIT HIT ON ENTERPRISE FROM SECTOR";K(I,1);",";K(I,2) 6090 IF S<=0 THEN 6240 6100 PRINT " <SHIELDS DOWN TO";S;"UNITS>":IF H<20 THEN 6200 6120 IF RND(1)>.6 OR H/S<=.02 THEN 6200 6140 R1=FNR(1):D(R1)=D(R1)-H/S-.5*RND(1):GOSUB 8790 6170 PRINT "DAMAGE CONTROL REPORTS ";G2$;" DAMAGED BY THE HIT'" 6200 NEXT I:RETURN 6210 REM END OF GAME 6220 PRINT "IT IS STARDATE";T:GOTO 6270 6240 PRINT:PRINT "THE ENTERPRISE HAS BEEN DESTROYED. THEN FEDERATION "; 6250 PRINT "WILL BE CONQUERED":GOTO 6220 6270 PRINT "THERE WERE";K9;"KLINGON BATTLE CRUISERS LEFT AT" 6280 PRINT "THE END OF YOUR MISSION." 6290 PRINT:PRINT:IF B9=0 THEN 6360 6310 PRINT "THE FEDERATION IS IN NEED OF A NEW STARSHIP COMMANDER" 6320 PRINT "FOR A SIMILAR MISSION -- IF THERE IS A VOLUNTEER," 6330 INPUT"LET HIM STEP FORWARD AND ENTER 'AYE'";A$:IF A$="AYE" THEN 10 6360 END 6370 PRINT "CONGRULATION, CAPTAIN! THEN LAST KLINGON BATTLE CRUISER" 6380 PRINT "MENACING THE FDERATION HAS BEEN DESTROYED.":PRINT 6400 PRINT "YOUR EFFICIENCY RATING IS";1000*(K7/(T-T0))^2:GOTO 6290 6420 REM SHORT RANGE SENSOR SCAN & STARTUP SUBROUTINE 6430 FOR I=S1-1TOS1+1:FOR J=S2-1 TO S2+1 6450 IF INT(I+.5)<1 OR INT(I+.5)>8 OR INT(J+.5)<1 OR INT(J+.5)>8 THEN 6540 6490 A$=">!<":Z1=I:Z2=J:GOSUB 8830:IF Z3=1 THEN 6580 6540 NEXT J:NEXT I:D0=0:GOTO 6650 6580 D0=1:C$="DOCKED":E=E0:P=P0 6620 PRINT "SHIELDS DROPPED FOR DOCKING PURPOSES":S=0:GOTO 6720 6650 IF K3>0 THEN C$="*RED*":GOTO 6720 6660 C$="GREEN":IF E<E0*.1 THEN C$="YELLOW" 6720 IF D(2)>=0 THEN 6770 6730 PRINT:PRINT "*** SHORT RANGE SENSORS ARE OUT ***":PRINT:RETURN 6770 O1$="---------------------------------":PRINT O1$:FOR I=1 TO 8 6820 FOR J=(I-1)*24+1 TO (I-1)*24+22STEP3:PRINT " ";MID$(Q$,J,3);:NEXT J 6830 ON I GOTO 6850,6900,6960,7020,7070,7120,7180,7240 6850 PRINT " STARDATE ";INT(T*10)*.1:GOTO 7260 6900 PRINT " CONDITION ";C$:GOTO 7260 6960 PRINT " QUADRANT ";Q1;",";Q2:GOTO 7260 7020 PRINT " SECTOR ";S1;",";S2:GOTO 7260 7070 PRINT " PHOTON TORPEDOES ";INT(P):GOTO 7260 7120 PRINT " TOTAL ENERGY ";INT(E+S):GOTO 7260 7180 PRINT " SHIELDS ";INT(S):GOTO 7260 7240 PRINT " KLINGONS REMAINING";INT(K9) 7260 NEXT I:PRINT O1$:RETURN 7280 REM LIBRARY COMPUTER CODE 7290 IF D(8)<0 THEN PRINT "COMPUTER DISABLED":GOTO 1990 7320 INPUT"COMPUTER ACTIVE AND AWAITING COMMAND";A:IF A<0 THEN 1990 7350 PRINT:H8=1:ON A+1 GOTO 7540,7900,8070,8500,8150,7400 7360 PRINT "FUNCTIONS AVAILABLE FROM LIBRARY-COMPUTER:" 7370 PRINT " 0 = CUMULATIVE GALACTIC RECORD" 7372 PRINT " 1 = STATUS REPORT" 7374 PRINT " 2 = PHOTON TORPEDO DATA" 7376 PRINT " 3 = STARBASE NAV DATA" 7378 PRINT " 4 = DIRECTION/DISTANCE CALCULATOR" 7380 PRINT " 5 = GALAXY 'REGION NAME' MAP":PRINT:GOTO 7320 7390 REM SETUP TO CHANGE CUM GAL RECORD TO GALAXY MAP 7400 H8=0:G5=1:PRINT " THE GALAXY":GOTO 7550 7530 REM CUM GALACTIC RECORD 7540 REM INPUT"DO YOU WANT A HARDCOPY? IS THE TTY ON (Y/N)";A$ 7542 REM IF A$="Y" THEN POKE1229,2:POKE1237,3:NULL1 7543 PRINT:PRINT " "; 7544 PRINT "COMPUTER RECORD OF GALAXY FOR QUADRANT";Q1;",";Q2 7546 PRINT 7550 PRINT " 1 2 3 4 5 6 7 8" 7560 O1$=" ----- ----- ----- ----- ----- ----- ----- -----" 7570 PRINT O1$:FOR I=1 TO 8:PRINT I;:IF H8=0 THEN 7740 7630 FOR J=1 TO 8:PRINT " ";:IF Z(I,J)=0 THEN PRINT "***";:GOTO 7720 7700 PRINT RIGHT$(STR$(Z(I,J)+1000),3); 7720 NEXT J:GOTO 7850 7740 Z4=I:Z5=1:GOSUB 9030:J0=INT(15-.5*LEN(G2$)):PRINT TAB(J0);G2$; 7800 Z5=5:GOSUB 9030:J0=INT(39-.5*LEN(G2$)):PRINT TAB(J0);G2$; 7850 PRINT:PRINT O1$:NEXT I:PRINT:GOTO 1990 7890 REM STATUS REPORT 7900 PRINT " STATUS REPORT:":X$="":IF K9>1 THEN X$="S" 7940 PRINT "KLINGON";X$;" LEFT: ";K9 7960 PRINT "MISSION MUST BE COMPLETED IN";.1*INT((T0+T9-T)*10);"STARDATES" 7970 X$="S":IF B9<2 THEN X$="":IF B9<1 THEN 8010 7980 PRINT "THE FEDERATION IS MAINTAINING";B9;"STARBASE";X$;" IN THE GALAXY" 7990 GOTO 5690 8010 PRINT "YOUR STUPIDITY HAS LEFT YOU ON YOUR ON IN" 8020 PRINT " THE GALAXY -- YOU HAVE NO STARBASES LEFT!":GOTO 5690 8060 REM TORPEDO, BASE NAV, D/D CALCULATOR 8070 IF K3<=0 THEN 4270 8080 X$="":IF K3>1 THEN X$="S" 8090 PRINT "FROM ENTERPRISE TO KLINGON BATTLE CRUSER";X$ 8100 H8=0:FOR I=1 TO 3:IF K(I,3)<=0 THEN 8480 8110 W1=K(I,1):X=K(I,2) 8120 C1=S1:A=S2:GOTO 8220 8150 PRINT "DIRECTION/DISTANCE CALCULATOR:" 8160 PRINT "YOU ARE AT QUADRANT ";Q1;",";Q2;" SECTOR ";S1;",";S2 8170 PRINT "PLEASE ENTER":INPUT" INITIAL COORDINATES (X,Y)";C1,A 8200 INPUT" FINAL COORDINATES (X,Y)";W1,X 8220 X=X-A:A=C1-W1:IF X<0 THEN 8350 8250 IF A<0 THEN 8410 8260 IF X>0 THEN 8280 8270 IF A=0 THEN C1=5:GOTO 8290 8280 C1=1 8290 IF ABS(A)<=ABS(X) THEN 8330 8310 PRINT "DIRECTION =";C1+(((ABS(A)-ABS(X))+ABS(A))/ABS(A)):GOTO 8460 8330 PRINT "DIRECTION =";C1+(ABS(A)/ABS(X)):GOTO 8460 8350 IF A>0 THEN C1=3:GOTO 8420 8360 IF X<>0 THEN C1=5:GOTO 8290 8410 C1=7 8420 IF ABS(A)>=ABS(X) THEN 8450 8430 PRINT "DIRECTION =";C1+(((ABS(X)-ABS(A))+ABS(X))/ABS(X)):GOTO 8460 8450 PRINT "DIRECTION =";C1+(ABS(X)/ABS(A)) 8460 PRINT "DISTANCE =";SQR(X^2+A^2):IF H8=1 THEN 1990 8480 NEXT I:GOTO 1990 8500 IF B3<>0 THEN PRINT "FROM ENTERPRISE TO STARBASE:":W1=B4:X=B5:GOTO 8120 8510 PRINT "MR. SPOCK REPORTS, 'SENSORS SHOW NO STARBASES IN THIS"; 8520 PRINT " QUADRANT.'":GOTO 1990 8580 REM FIND EMPTY PLACE IN QUADRANT (FOR THINGS) 8590 R1=FNR(1):R2=FNR(1):A$=" ":Z1=R1:Z2=R2:GOSUB 8830:IF Z3=0 THEN 8590 8600 RETURN 8660 REM INSERT IN STRING ARRAY FOR QUADRANT 8670 S8=INT(Z2-.5)*3+INT(Z1-.5)*24+1 8675 IF LEN(A$)<>3 THEN PRINT "ERROR":STOP 8680 IF S8=1 THEN Q$=A$+RIGHT$(Q$,189):RETURN 8690 IF S8=190 THEN Q$=LEFT$(Q$,189)+A$:RETURN 8700 Q$=LEFT$(Q$,S8-1)+A$+RIGHT$(Q$,190-S8):RETURN 8780 REM PRINTS DEVICE NAME 8790 ON R1 GOTO 8792,8794,8796,8798,8800,8802,8804,8806 8792 G2$="WARP ENGINES":RETURN 8794 G2$="SHORT RANGE SENSORS":RETURN 8796 G2$="LONG RANGE SENSORS":RETURN 8798 G2$="PHASER CONTROL":RETURN 8800 G2$="PHOTON TUBES":RETURN 8802 G2$="DAMAGE CONTROL":RETURN 8804 G2$="SHIELD CONTROL":RETURN 8806 G2$="LIBRARY-COMPUTER":RETURN 8820 REM STRING COMPARISON IN QUADRANT ARRAY 8830 Z1=INT(Z1+.5):Z2=INT(Z2+.5):S8=(Z2-1)*3+(Z1-1)*24+1:Z3=0 8890 IF MID$(Q$,S8,3)<>A$ THEN RETURN 8900 Z3=1:RETURN 9010 REM QUADRANT NAME IN G2$ FROM Z4,Z5 (=Q1,Q2) 9020 REM CALL WITH G5=1 TO GET REGION NAME ONLY 9030 IF Z5<=4 THEN ON Z4 GOTO 9040,9050,9060,9070,9080,9090,9100,9110 9035 GOTO 9120 9040 G2$="ANTARES":GOTO 9210 9050 G2$="RIGEL":GOTO 9210 9060 G2$="PROCYON":GOTO 9210 9070 G2$="VEGA":GOTO 9210 9080 G2$="CANOPUS":GOTO 9210 9090 G2$="ALTAIR":GOTO 9210 9100 G2$="SAGITTARIUS":GOTO 9210 9110 G2$="POLLUX":GOTO 9210 9120 ON Z4 GOTO 9130,9140,9150,9160,9170,9180,9190,9200 9130 G2$="SIRIUS":GOTO 9210 9140 G2$="DENEB":GOTO 9210 9150 G2$="CAPELLA":GOTO 9210 9160 G2$="BETELGEUSE":GOTO 9210 9170 G2$="ALDEBARAN":GOTO 9210 9180 G2$="REGULUS":GOTO 9210 9190 G2$="ARCTURUS":GOTO 9210 9200 G2$="SPICA" 9210 IF G5<>1 THEN ON Z5 GOTO 9230,9240,9250,9260,9230,9240,9250,9260 9220 RETURN 9230 G2$=G2$+" I":RETURN 9240 G2$=G2$+" II":RETURN 9250 G2$=G2$+" III":RETURN 9260 G2$=G2$+" IV":RETURN ================================================ FILE: 00_Alternate_Languages/84_Super_Star_Trek/superstartrekins.bas ================================================ 10 REM INSTRUCTIONS FOR "SUPER STARTREK" MAR 5, 1978 20 FOR I=1 TO 12:PRINT:NEXT I 21 PRINT TAB(10);"*************************************" 22 PRINT TAB(10);"* *" 23 PRINT TAB(10);"* *" 30 PRINT TAB(10);"* * * SUPER STAR TREK * * *" 31 PRINT TAB(10);"* *" 32 PRINT TAB(10);"* *" 35 PRINT TAB(10);"*************************************" 36 FOR I=1 TO 8:PRINT:NEXT I 40 INPUT "DO YOU NEED INSTRUCTIONS (Y/N)";K$:IF K$="N" THEN 2000 44 PRINT 45 REM PRINT "TURN THE TTY ON-LINE AND HIT ANY KEY EXCEPT RETURN" 46 REM IF INP(1)=13 THEN 46 50 REM POKE 1229,2:POKE 1237,3:NULL 1 90 PRINT" INSTRUCTIONS FOR 'SUPER STAR TREK'" 100 PRINT 110 PRINT"1. WHEN YOU SEE \COMMAND ?\ PRINTED, ENTER ONE OF THE LEGAL" 120 PRINT" COMMANDS (NAV,SRS,LRS,PHA,TOR,SHE,DAM,COM, OR XXX)." 130 PRINT"2. IF YOU SHOULD TYPE IN AN ILLEGAL COMMAND, YOU'LL GET A SHORT" 140 PRINT" LIST OF THE LEGAL COMMANDS PRINTED OUT." 150 PRINT"3. SOME COMMANDS REQUIRE YOU TO ENTER DATA (FOR EXAMPLE, THE" 160 PRINT" 'NAV' COMMAND COMES BACK WITH 'COURSE (1-9) ?'.) IF YOU" 170 PRINT" TYPE IN ILLEGAL DATA (LIKE NEGATIVE NUMBERS), THAN COMMAND" 180 PRINT" WILL BE ABORTED" 190 PRINT 270 PRINT" THE GALAXY IS DIVIDED INTO AN 8 X 8 QUADRANT GRID," 280 PRINT"AND EACH QUADRANT IS FURTHER DIVIDED INTO AN 8 X 8 SECTOR GRID." 290 PRINT 300 PRINT" YOU WILL BE ASSIGNED A STARTING POINT SOMEWHERE IN THE" 310 PRINT"GALAXY TO BEGIN A TOUR OF DUTY AS COMANDER OF THE STARSHIP" 320 PRINT"\ENTERPRISE\; YOUR MISSION: TO SEEK AND DESTROY THE FLEET OF" 330 PRINT"KLINGON WARWHIPS WHICH ARE MENACING THE UNITED FEDERATION OF" 340 PRINT"PLANETS." 360 PRINT 370 PRINT" YOU HAVE THE FOLLOWING COMMANDS AVAILABLE TO YOU AS CAPTAIN" 380 PRINT"OF THE STARSHIP ENTERPRISE:" 385 PRINT 390 PRINT"\NAV\ COMMAND = WARP ENGINE CONTROL --" 400 PRINT" COURSE IS IN A CIRCULAR NUMERICAL 4 3 2" 410 PRINT" VECTOR ARRANGEMENT AS SHOWN . . ." 420 PRINT" INTEGER AND REAL VALUES MAY BE ..." 430 PRINT" USED. (THUS COURSE 1.5 IS HALF- 5 ---*--- 1" 440 PRINT" WAY BETWEEN 1 AND 2 ..." 450 PRINT" . . ." 460 PRINT" VALUES MAY APPROACH 9.0, WHICH 6 7 8" 470 PRINT" ITSELF IS EQUIVALENT TO 1.0" 480 PRINT" COURSE" 490 PRINT" ONE WARP FACTOR IS THE SIZE OF " 500 PRINT" ONE QUADTANT. THEREFORE, TO GET" 510 PRINT" FROM QUADRANT 6,5 TO 5,5, YOU WOULD" 520 PRINT" USE COURSE 3, WARP FACTOR 1." 530 PRINT 540 PRINT"\SRS\ COMMAND = SHORT RANGE SENSOR SCAN" 550 PRINT" SHOWS YOU A SCAN OF YOUR PRESENT QUADRANT." 555 PRINT 560 PRINT" SYMBOLOGY ON YOUR SENSOR SCREEN IS AS FOLLOWS:" 570 PRINT" <*> = YOUR STARSHIP'S POSITION" 580 PRINT" +K+ = KLINGON BATTLE CRUISER" 590 PRINT" >!< = FEDERATION STARBASE (REFUEL/REPAIR/RE-ARM HERE!)" 600 PRINT" * = STAR" 605 PRINT 610 PRINT" A CONDENSED 'STATUS REPORT' WILL ALSO BE PRESENTED." 620 PRINT 640 PRINT"\LRS\ COMMAND = LONG RANGE SENSOR SCAN" 650 PRINT" SHOWS CONDITIONS IN SPACE FOR ONE QUADRANT ON EACH SIDE" 660 PRINT" OF THE ENTERPRISE (WHICH IS IN THE MIDDLE OF THE SCAN)" 670 PRINT" THE SCAN IS CODED IN THE FORM \###\, WHERE TH UNITS DIGIT" 680 PRINT" IS THE NUMBER OF STARS, THE TENS DIGIT IS THE NUMBER OF" 690 PRINT" STARBASES, AND THE HUNDRESDS DIGIT IS THE NUMBER OF" 700 PRINT" KLINGONS." 705 PRINT 706 PRINT" EXAMPLE - 207 = 2 KLINGONS, NO STARBASES, & 7 STARS." 710 PRINT 720 PRINT"\PHA\ COMMAND = PHASER CONTROL." 730 PRINT" ALLOWS YOU TO DESTROY THE KLINGON BATTLE CRUISERS BY " 740 PRINT" ZAPPING THEM WITH SUITABLY LARGE UNITS OF ENERGY TO" 750 PRINT" DEPLETE THEIR SHIELD POWER. (REMEMBER, KLINGONS HAVE" 760 PRINT" PHASERS TOO!)" 770 PRINT 780 PRINT"\TOR\ COMMAND = PHOTON TORPEDO CONTROL" 790 PRINT" TORPEDO COURSE IS THE SAME AS USED IN WARP ENGINE CONTROL" 800 PRINT" IF YOU HIT THE KLINGON VESSEL, HE IS DESTROYED AND" 810 PRINT" CANNOT FIRE BACK AT YOU. IF YOU MISS, YOU ARE SUBJECT TO" 820 PRINT" HIS PHASER FIRE. IN EITHER CASE, YOU ARE ALSO SUBJECT TO " 825 PRINT" THE PHASER FIRE OF ALL OTHER KLINGONS IN THE QUADRANT." 830 PRINT 835 PRINT" THE LIBRARY-COMPUTER (\COM\ COMMAND) HAS AN OPTION TO " 840 PRINT" COMPUTE TORPEDO TRAJECTORY FOR YOU (OPTION 2)" 850 PRINT 860 PRINT"\SHE\ COMMAND = SHIELD CONTROL" 870 PRINT" DEFINES THE NUMBER OF ENERGY UNITS TO BE ASSIGNED TO THE" 880 PRINT" SHIELDS. ENERGY IS TAKEN FROM TOTAL SHIP'S ENERGY. NOTE" 890 PRINT" THAN THE STATUS DISPLAY TOTAL ENERGY INCLUDES SHIELD ENERGY" 900 PRINT 910 PRINT"\DAM\ COMMAND = DAMMAGE CONTROL REPORT" 920 PRINT" GIVES THE STATE OF REPAIR OF ALL DEVICES. WHERE A NEGATIVE" 930 PRINT" 'STATE OF REPAIR' SHOWS THAT THE DEVICE IS TEMPORARILY" 940 PRINT" DAMAGED." 950 PRINT 960 PRINT"\COM\ COMMAND = LIBRARY-COMPUTER" 970 PRINT" THE LIBRARY-COMPUTER CONTAINS SIX OPTIONS:" 980 PRINT" OPTION 0 = CUMULATIVE GALACTIC RECORD" 990 PRINT" THIS OPTION SHOWES COMPUTER MEMORY OF THE RESULTS OF ALL" 1000 PRINT" PREVIOUS SHORT AND LONG RANGE SENSOR SCANS" 1010 PRINT" OPTION 1 = STATUS REPORT" 1020 PRINT" THIS OPTION SHOWS THE NUMBER OF KLINGONS, STARDATES," 1030 PRINT" AND STARBASES REMAINING IN THE GAME." 1040 PRINT" OPTION 2 = PHOTON TORPEDO DATA" 1050 PRINT" WHICH GIVES DIRECTIONS AND DISTANCE FROM THE ENTERPRISE" 1060 PRINT" TO ALL KLINGONS IN YOUR QUADRANT" 1070 PRINT" OPTION 3 = STARBASE NAV DATA" 1080 PRINT" THIS OPTION GIVES DIRECTION AND DISTANCE TO ANY " 1090 PRINT" STARBASE WITHIN YOUR QUADRANT" 1100 PRINT" OPTION 4 = DIRECTION/DISTANCE CALCULATOR" 1110 PRINT" THIS OPTION ALLOWS YOU TO ENTER COORDINATES FOR" 1120 PRINT" DIRECTION/DISTANCE CALCULATIONS" 1130 PRINT" OPTION 5 = GALACTIC /REGION NAME/ MAP" 1140 PRINT" THIS OPTION PRINTS THE NAMES OF THE SIXTEEN MAJOR " 1150 PRINT" GALACTIC REGIONS REFERRED TO IN THE GAME." 1990 REM POKE 1229,0:POKE 1237,1:NULL 0 2000 REM PRINT:PRINT:PRINT 2010 REM PRINT "TURN CASSETTE PLAYER ON AND HIT ANY KEY EXCEPT RETURN" 2020 REM IF INP(1)=13 THEN 2020 2030 REM PRINT 2040 REM PRINT "TURN CASSETTE PLAYER OFF AND " 2050 REM PRINT "TYPE 'RUN' WHEN COMPUTER PRINTS 'OK'" ================================================ FILE: 00_Alternate_Languages/85_Synonym/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/85_Synonym/MiniScript/synonym.ms ================================================ 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." ================================================ FILE: 00_Alternate_Languages/85_Synonym/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/85_Synonym/synonym.bas ================================================ 2 PRINT TAB(33);"SYNONYM" 4 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 6 PRINT: PRINT: PRINT 10 DIM R$(5),W$(10),L(30),R(30) 20 R$(1)="RIGHT": R$(2)="CORRECT": R$(3)="FINE": R$(4)="GOOD!" 30 R$(5)="CHECK" 70 C=0 90 PRINT "A SYNONYM OF A WORD MEANS ANOTHER WORD IN THE ENGLISH" 100 PRINT "LANGUAGE WHICH HAS THE SAME OR VERY NEARLY THE SAME"; 110 PRINT " MEANING." 130 PRINT "I CHOOSE A WORD -- YOU TYPE A SYNONYM." 140 PRINT "IF YOU CAN'T THINK OF A SYNONYM, TYPE THE WORD 'HELP'" 145 PRINT "AND I WILL TELL YOU A SYNONYM.": PRINT 150 RESTORE: C=C+1: READ N 160 IF C>N THEN 420 170 N1=INT(RND(1)*N+1) 174 IF R(N1)=1 THEN 170 176 R(N1)=1 180 FOR I=1 TO N1 190 READ N2 200 FOR J=1 TO N2 210 READ W$(J) 220 NEXT J 230 NEXT I 232 FOR J=1 TO N2: L(J)=J: NEXT J 235 L(0)=N2: G=1: PRINT 237 L(G)=L(L(0)): L(0)=N2-1: PRINT 240 PRINT " WHAT IS A SYNONYM OF ";W$(G);: INPUT A$ 250 IF A$="HELP" THEN 340 260 FOR K=1 TO N2 270 IF G=K THEN 290 280 IF A$=W$(K) THEN 320 290 NEXT K 300 PRINT " TRY AGAIN.": GOTO 240 320 PRINT R$(INT(RND(1)*5+1)): GOTO 150 340 G1=INT(RND(1)*L(0)+1) 360 PRINT "**** A SYNONYM OF ";W$(G);" IS ";W$(L(G1));".": PRINT 370 L(G1)=L(L(0)): L(0)=L(0)-1: GOTO 240 420 PRINT: PRINT "SYNONYM DRILL COMPLETED.": GOTO 999 500 DATA 10 510 DATA 5,"FIRST","START","BEGINNING","ONSET","INITIAL" 520 DATA 5,"SIMILAR","ALIKE","SAME","LIKE","RESEMBLING" 530 DATA 5,"MODEL","PATTERN","PROTOTYPE","STANDARD","CRITERION" 540 DATA 5,"SMALL","INSIGNIFICANT","LITTLE","TINY","MINUTE" 550 DATA 6,"STOP","HALT","STAY","ARREST","CHECK","STANDSTILL" 560 DATA 6,"HOUSE","DWELLING","RESIDENCE","DOMICILE","LODGING" 565 DATA "HABITATION" 570 DATA 7,"PIT","HOLE","HOLLOW","WELL","GULF","CHASM","ABYSS" 580 DATA 7,"PUSH","SHOVE","THRUST","PROD","POKE","BUTT","PRESS" 590 DATA 6,"RED","ROUGE","SCARLET","CRIMSON","FLAME","RUBY" 600 DATA 7,"PAIN","SUFFERING","HURT","MISERY","DISTRESS","ACHE" 605 DATA "DISCOMFORT" 999 END ================================================ FILE: 00_Alternate_Languages/86_Target/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/86_Target/MiniScript/target.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/86_Target/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/86_Target/target.bas ================================================ 10 PRINT TAB(33);"TARGET" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 30 PRINT: PRINT: PRINT 100 R=1: R1=57.296: P=3.14159 110 PRINT "YOU ARE THE WEAPONS OFFICER ON THE STARSHIP ENTERPRISE" 120 PRINT "AND THIS IS A TEST TO SEE HOW ACCURATE A SHOT YOU" 130 PRINT "ARE IN A THREE-DIMENSIONAL RANGE. YOU WILL BE TOLD" 140 PRINT "THE RADIAN OFFSET FOR THE X AND Z AXES, THE LOCATION" 150 PRINT "OF THE TARGET IN THREE DIMENSIONAL RECTANGULAR COORDINATES," 160 PRINT "THE APPROXIMATE NUMBER OF DEGREES FROM THE X AND Z" 170 PRINT "AXES, AND THE APPROXIMATE DISTANCE TO THE TARGET." 180 PRINT "YOU WILL THEN PROCEEED TO SHOOT AT THE TARGET UNTIL IT IS" 190 PRINT "DESTROYED!": PRINT: PRINT "GOOD LUCK!!":PRINT: PRINT 220 A=RND(1)*2*P: B=RND(1)*2*P: Q=INT(A*R1): W=INT(B*R1) 260 PRINT "RADIANS FROM X AXIS =";A;" FROM Z AXIS =";B 280 P1=100000*RND(1)+RND(1): X=SIN(B)*COS(A)*P1: Y=SIN(B)*SIN(A)*P1 290 Z=COS(B)*P1 340 PRINT "TARGET SIGHTED: APPROXIMATE COORDINATES: X=";X;" Y=";Y;" Z=";Z 345 R=R+1: IF R>5 THEN 390 350 ON R GOTO 355,360,365,370,375 355 P3=INT(P1*.05)*20: GOTO 390 360 P3=INT(P1*.1)*10: GOTO 390 365 P3=INT(P1*.5)*2: GOTO 390 370 P3=INT(P1): GOTO 390 375 P3=P1 390 PRINT " ESTIMATED DISTANCE:";P3 400 PRINT:PRINT "INPUT ANGLE DEVIATION FROM X, DEVIATION FROM Z, DISTANCE"; 405 INPUT A1,B1,P2 410 PRINT: IF P2<20 THEN PRINT "YOU BLEW YOURSELF UP!!": GOTO 580 420 A1=A1/R1: B1=B1/R1: PRINT "RADIANS FROM X AXIS =";A1;" "; 425 PRINT "FROM Z AXIS =";B1 480 X1=P2*SIN(B1)*COS(A1): Y1=P2*SIN(B1)*SIN(A1): Z1=P2*COS(B1) 510 D=((X1-X)^2+(Y1-Y)^2+(Z1-Z)^2)^(1/2) 520 IF D>20 THEN 670 530 PRINT: PRINT " * * * HIT * * * TARGET IS NON-FUNCTIONAL": PRINT 550 PRINT "DISTANCE OF EXPLOSION FROM TARGET WAS";D;"KILOMETERS." 570 PRINT: PRINT "MISSION ACCOMPLISHED IN ";R;" SHOTS." 580 R=0: FOR I=1 TO 5: PRINT: NEXT I: PRINT "NEXT TARGET...": PRINT 590 GOTO 220 670 X2=X1-X: Y2=Y1-Y: Z2=Z1-Z: IF X2<0 THEN 730 710 PRINT "SHOT IN FRONT OF TARGET";X2;"KILOMETERS.": GOTO 740 730 PRINT "SHOT BEHIND TARGET";-X2;"KILOMETERS." 740 IF Y2<0 THEN 770 750 PRINT "SHOT TO LEFT OF TARGET";Y2;"KILOMETERS.": GOTO 780 770 PRINT "SHOT TO RIGHT OF TARGET";-Y2;"KILOMETERS." 780 IF Z2<0 THEN 810 790 PRINT "SHOT ABOVE TARGET";Z2;"KILOMETERS.": GOTO 820 810 PRINT "SHOT BELOW TARGET";-Z2;"KILOMETERS." 820 PRINT "APPROX POSITION OF EXPLOSION: X=";X1;" Y=";Y1;" Z=";Z1 830 PRINT " DISTANCE FROM TARGET =";D: PRINT: PRINT: PRINT: GOTO 345 999 END ================================================ FILE: 00_Alternate_Languages/87_3-D_Plot/3dplot.bas ================================================ 1 PRINT TAB(32);"3D PLOT" 2 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 3 PRINT:PRINT:PRINT 5 DEF FNA(Z)=30*EXP(-Z*Z/100) 100 PRINT 110 FOR X=-30 TO 30 STEP 1.5 120 L=0 130 Y1=5*INT(SQR(900-X*X)/5) 140 FOR Y=Y1 TO -Y1 STEP -5 150 Z=INT(25+FNA(SQR(X*X+Y*Y))-.7*Y) 160 IF Z<=L THEN 190 170 L=Z 180 PRINT TAB(Z);"*"; 190 NEXT Y 200 PRINT 210 NEXT X 300 END ================================================ FILE: 00_Alternate_Languages/87_3-D_Plot/MiniScript/3dplot.ms ================================================ // 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 ================================================ FILE: 00_Alternate_Languages/87_3-D_Plot/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/87_3-D_Plot/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/87_3-D_Plot/d/.gitignore ================================================ *.exe *.obj ================================================ FILE: 00_Alternate_Languages/87_3-D_Plot/d/README.md ================================================ Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html) Converted to [D](https://dlang.org/) by [Bastiaan Veelo](https://github.com/veelo). ## Running the code Assuming the reference [dmd](https://dlang.org/download.html#dmd) compiler: ```shell dmd -dip1000 -run threedeeplot.d ``` [Other compilers](https://dlang.org/download.html) also exist. ## On rounding floating point values to integer values The D equivalent of Basic `INT` is [`floor`](https://dlang.org/phobos/std_math_rounding.html#.floor), which rounds towards negative infinity. If you change occurrences of `floor` to [`lrint`](https://dlang.org/phobos/std_math_rounding.html#.lrint), you'll see that the plots show a bit more detail, as is done in the bonus below. ## Bonus: Self-writing programs With a small modification to the source, the program can be extended to **plot a random function**, and **print its formula**. ```shell rdmd -dip1000 threedeeplot_random.d ``` (`rdmd` caches the executable, which results in speedy execution when the source does not change.) ### Example output ``` 3D Plot (After Creative Computing Morristown, New Jersey) f(z) = 30 * sin(z / 10.0) * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * ** * * * * * * * * ** * * * * * * * * * * * * * * * * * * * * * * ** * * ** * * * ** * ** * * * * * ** * * * * * ** * * * * * ** * * * ** * ** * * * ** * * ** * * * * * * * * * * * * * * * * * * * * ** * * * * * * * ** * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * * * * * * * * * * * ** * * * * * * * * * * * * ** * * * * * * * * * * * * * * * * * * * * * * * * * * * ``` ### Breakdown of differences Have a look at the relevant differences between `threedeeplot.d` and `threedeeplot_random.d`. This is the original function with the single expression that is evaluated for the plot: ```d static float fna(float z) { return 30.0 * exp(-z * z / 100.0); } ``` Here `static` means that the nested function does not need acces to its enclosing scope. Now, by inserting the following: ```d enum functions = ["30.0 * exp(-z * z / 100.0)", "sqrt(900.01 - z * z) * .9 - 2", "30 * (cos(z / 16.0) + .5)", "30 - 30 * sin(z / 18.0)", "30 * exp(-cos(z / 16.0)) - 30", "30 * sin(z / 10.0)"]; size_t index = uniform(0, functions.length); writeln(center("f(z) = " ~ functions[index], width), "\n"); ``` and changing the implementation of `fna` to ```d float fna(float z) { final switch (index) { static foreach (i, f; functions) case i: mixin("return " ~ f ~ ";"); } } ``` we unlock some very special abilities of D. Let's break it down: ```d enum functions = ["30.0 * exp(-z * z / 100.0)", /*...*/]; ``` This defines an array of strings, each containing a mathematical expression. Due to the `enum` keyword, this is an array that really only exists at compile-time. ```d size_t index = uniform(0, functions.length); ``` This defines a random index into the array. `functions.length` is evaluated at compile-time, due to D's compile-time function evaluation (CTFE). ```d writeln(center("f(z) = " ~ functions[index], width), "\n"); ``` Unmistakenly, this prints the formula centered on a line. What happens behind the scenes is that `functions` (which only existed at compile-time before now) is pasted in, so that an instance of that array actually exists at run-time at this spot, and is instantly indexed. ```d float fna(float z) { final switch (index) { // ... } } ``` `static` has been dropped from the nested function because we want to evaluate `index` inside it. The function contains an ordinary `switch`, with `final` providing some extra robustness. It disallows a `default` case and produces an error when the switch doesn't handle all cases. The `switch` body is where the magic happens and consists of these three lines: ```d static foreach (i, f; functions) case i: mixin("return " ~ f ~ ";"); ``` The `static foreach` iterates over `functions` at compile-time, producing one `case` for every element in `functions`. `mixin` takes a string, which is constructed at compile-time, and pastes it right into the source. In effect, the implementation of `float fna(float z)` unrolls itself into ```d float fna(float z) { final switch (index) { case 0: return 30.0 * exp(-z * z / 100.0); case 1: return sqrt(900.01 - z * z) * .9 - 2; case 2: return 30 * (cos(z / 16.0) + .5); case 3: return 30 - 30 * sin(z / 18.0); case 4: return 30 * exp(-cos(z / 16.0)) - 30; case 5: return 30 * sin(z / 10.0)"; } } ``` So if you feel like adding another function, all you need to do is append it to the `functions` array, and the rest of the program *rewrites itself...* ================================================ FILE: 00_Alternate_Languages/87_3-D_Plot/d/threedeeplot.d ================================================ @safe: // Make @safe the default for this file, enforcing memory-safety. import std.stdio, std.string, std.math, std.range, std.conv, std.algorithm; void main() { enum width = 80; writeln(center("3D Plot", width)); writeln(center("(After Creative Computing Morristown, New Jersey)\n\n\n", width)); static float fna(float z) { return 30.0 * exp(-z * z / 100.0); } char[] row; for (float x = -30.0; x <= 30.0; x += 1.5) { size_t max_z = 0L; auto y1 = 5 * floor((sqrt(900 - x * x)) / 5.0); for (float y = y1; y >= -y1; y -= 5) { auto z = to!size_t(max(0, floor(25 + fna(sqrt(x * x + y * y)) - .7 * y))); if (z > max_z) // Visible { max_z = z; if (z + 1 > row.length) // row needs to grow row ~= ' '.repeat(z + 1 - row.length).array; row[z] = '*'; } } writeln(row); row = null; } } ================================================ FILE: 00_Alternate_Languages/87_3-D_Plot/d/threedeeplot_random.d ================================================ @safe: // Make @safe the default for this file, enforcing memory-safety. import std.stdio, std.string, std.math, std.range, std.conv, std.random, std.algorithm; void main() { enum width = 80; writeln(center("3D Plot", width)); writeln(center("(After Creative Computing Morristown, New Jersey)\n\n", width)); enum functions = ["30.0 * exp(-z * z / 100.0)", "sqrt(900.01 - z * z) * .9 - 2", "30 * (cos(z / 16.0) + .5)", "30 - 30 * sin(z / 18.0)", "30 * exp(-cos(z / 16.0)) - 30", "30 * sin(z / 10.0)"]; size_t index = uniform(0, functions.length); writeln(center("f(z) = " ~ functions[index], width), "\n"); float fna(float z) { final switch (index) { static foreach (i, f; functions) case i: mixin("return " ~ f ~ ";"); } } char[] row; for (float x = -30.0; x <= 30.0; x += 1.5) { size_t max_z = 0L; auto y1 = 5 * lrint((sqrt(900 - x * x)) / 5.0); for (float y = y1; y >= -y1; y -= 5) { auto z = to!size_t(max(0, lrint(25 + fna(sqrt(x * x + y * y)) - .7 * y))); if (z > max_z) // Visible { max_z = z; if (z + 1 > row.length) // row needs to grow row ~= ' '.repeat(z + 1 - row.length).array; row[z] = '*'; } } writeln(row); row = null; } } ================================================ FILE: 00_Alternate_Languages/88_3-D_Tic-Tac-Toe/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/88_3-D_Tic-Tac-Toe/MiniScript/qubit.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/88_3-D_Tic-Tac-Toe/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/88_3-D_Tic-Tac-Toe/qubit.bas ================================================ 50 PRINT CHR$(26):REM WIDTH 80 100 PRINT TAB(33);"QUBIC":PRINT 110 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 120 PRINT:PRINT:PRINT 210 PRINT "DO YOU WANT INSTRUCTIONS"; 220 INPUT C$ 230 IF LEFT$(C$,1)="N" THEN 315 240 IF LEFT$(C$,1)="Y" THEN 265 250 PRINT "INCORRECT ANSWER. PLEASE TYPE 'YES' OR 'NO'"; 260 GOTO 220 265 PRINT 270 PRINT "THE GAME IS TIC-TAC-TOE IN A 4 X 4 X 4 CUBE." 280 PRINT "EACH MOVE IS INDICATED BY A 3 DIGIT NUMBER, WITH EACH" 290 PRINT "DIGIT BETWEEN 1 AND 4 INCLUSIVE. THE DIGITS INDICATE THE" 300 PRINT "LEVEL, ROW, AND COLUMN, RESPECTIVELY, OF THE OCCUPIED" 305 PRINT "PLACE. " 306 PRINT 307 PRINT "TO PRINT THE PLAYING BOARD, TYPE 0 (ZERO) AS YOUR MOVE." 308 PRINT "THE PROGRAM WILL PRINT THE BOARD WITH YOUR MOVES INDI-" 309 PRINT "CATED WITH A (Y), THE MACHINE'S MOVES WITH AN (M), AND" 310 PRINT "UNUSED SQUARES WITH A ( ). OUTPUT IS ON PAPER." 311 PRINT 312 PRINT "TO STOP THE PROGRAM RUN, TYPE 1 AS YOUR MOVE." 313 PRINT:PRINT 315 DIM X(64),L(76),M(76,4),Y(16) 320 FOR I = 1 TO 16 330 READ Y(I) 340 NEXT I 350 FOR I=1 TO 76 360 FOR J = 1 TO 4 370 READ M(I,J) 380 NEXT J 390 NEXT I 400 FOR I = 1 TO 64 410 LET X (I) =0 420 NEXT I 430 LET Z=1 440 PRINT "DO YOU WANT TO MOVE FIRST"; 450 INPUT S$ 460 IF LEFT$(S$,1)="N" THEN 630 470 IF LEFT$(S$,1)="Y" THEN 500 480 PRINT "INCORRECT ANSWER. PLEASE TYPE 'YES' OR 'NO'."; 490 GOTO 450 500 PRINT " " 510 PRINT "YOUR MOVE"; 520 INPUT J1 521 IF J1=1 THEN 2770 522 IF J1<>0 THEN 525 523 GOSUB 2550 524 GOTO 500 525 IF J1<111 THEN 2750 526 IF J1>444 THEN 2750 530 GOSUB 2500 540 LET K1=INT(J1/100) 550 LET J2=(J1-K1*100) 560 LET K2=INT(J2/10) 570 LET K3= J1 - K1*100 -K2*10 580 LET M=16*K1+4*K2+K3-20 590 IF X(M)=0 THEN 620 600 PRINT "THAT SQUARE IS USED, TRY AGAIN." 610 GOTO 500 620 LET X(M)=1 630 GOSUB 1640 640 J=1 650 I=1 660 IF J=1 THEN 720 670 IF J=2 THEN 790 680 IF J=3 THEN 930 690 I=I+1: IF I<=76 THEN 660 700 J=J+1: IF J<=3 THEN 650 710 GOTO 1300 720 IF L(I)<>4 THEN 690 730 PRINT "YOU WIN AS FOLLOWS"; 740 FOR J=1 TO 4 750 LET M=M(I,J) 760 GOSUB 1570 770 NEXT J 780 GOTO 1490 790 IF L(I)<>15 THEN 690 800 FOR J=1 TO 4 810 LET M=M(I,J) 820 IF X(M)<>0 THEN 860 830 LET X(M)=5 840 PRINT "MACHINE MOVES TO"; 850 GOSUB 1570 860 NEXT J 870 PRINT ", AND WINS AS FOLLOWS" 880 FOR J=1 TO 4 890 LET M=M(I,J) 900 GOSUB 1570 910 NEXT J 920 GOTO 1490 930 IF L(I)<>3 THEN 690 940 PRINT "NICE TRY. MACHINE MOVES TO"; 950 FOR J=1 TO 4 960 LET M=M(I,J) 970 IF X(M)<>0 THEN 1010 980 LET X(M)=5 990 GOSUB 1570 1000 GOTO 500 1010 NEXT J 1020 GOTO 1300 1030 I=1 1040 LET L(I)=X(M(I,1))+X(M(I,2))+X(M(I,3))+X(M(I,4)) 1050 LET L = L(I) 1060 IF L <2 THEN 1130 1070 IF L>=3 THEN 1130 1080 IF L>2 THEN 2230 1090 FOR J = 1 TO 4 1100 IF X(M(I,J))<>0 THEN 1120 1110 LET X(M(I,J))=1/8 1120 NEXT J 1130 I=I+1: IF I<=76 THEN 1040 1140 GOSUB 1640 1150 I=1 1160 IF L(I)=1/2 THEN 2360 1170 IF L(I)=1+3/8 THEN 2360 1180 I=I+1: IF I<=76 THEN 1160 1190 GOTO 1830 1200 LET Z = 1 1210 IF X(Y(Z))=0 THEN 1250 1220 LET Z=Z+1 1230 IF Z<>17 THEN 1210 1240 GOTO 1720 1250 LET M=Y(Z) 1260 LET X(M)=5 1270 PRINT "MACHINE MOVES TO"; 1280 GOSUB 1570 1290 GOTO 500 1300 LET X=X 1310 I=1 1320 LET L(I)=X(M(I,1))+X(M(I,2))+X(M(I,3))+X(M(I,4)) 1330 LET L=L(I) 1340 IF L<10 THEN 1410 1350 IF L>=11 THEN 1410 1360 IF L>10 THEN 2230 1370 FOR J=1 TO 4 1380 IF X(M(I,J))<>0 THEN 1400 1390 LET X(M(I,J))=1/8 1400 NEXT J 1410 I=I+1: IF I<=76 THEN 1320 1420 GOSUB 1640 1430 I=1 1440 IF L(I)=.5 THEN 2360 1450 IF L(I)=5+3/8 THEN 2360 1460 I=I+1: IF I<=76 THEN 1440 1470 GOSUB 2500 1480 GOTO 1030 1490 PRINT " " 1500 PRINT "DO YOU WANT TO TRY ANOTHER GAME"; 1510 INPUT X$ 1520 IF LEFT$(X$,1)="Y" THEN 400 1530 IF LEFT$(X$,1)="N" THEN 1560 1540 PRINT "INCORRECT ANSWER. PLEASE TYPE 'YES' OR 'NO'"; 1550 GOTO 1510 1560 END 1570 LET K1=INT((M-1)/16)+1 1580 LET J2=M-16*(K1-1) 1590 LET K2=INT((J2-1)/4)+1 1600 LET K3=M-(K1-1)*16-(K2-1)*4 1610 LET M=K1*100+K2*10+K3 1620 PRINT M; 1630 RETURN 1640 FOR S=1 TO 76 1650 LET J1 = M(S,1) 1660 LET J2=M(S,2) 1670 LET J3=M(S,3) 1680 LET J4=M(S,4) 1690 LET L(S)=X(J1)+X(J2)+X(J3)+X(J4) 1700 NEXT S 1710 RETURN 1720 FOR I=1 TO 64 1730 IF X(I)<>0 THEN 1800 1740 LET X(I)=5 1750 LET M=I 1760 PRINT "MACHINE LIKES"; 1770 GOSUB 1570 1780 PRINT " " 1790 GOTO 500 1800 NEXT I 1810 PRINT "THE GAME IS A DRAW." 1820 GOTO 1490 1830 FOR K=1 TO 18 1840 LET P=0 1850 FOR I=4*K-3 TO 4*K 1860 FOR J=1 TO 4 1870 LET P=P+X(M(I,J)) 1880 NEXT J 1890 NEXT I 1900 IF P<4 THEN 1940 1910 IF P<5 THEN 1970 1920 IF P<9 THEN 1940 1930 IF P<10 THEN 1970 1940 NEXT K 1950 GOSUB 2500 1960 GOTO 1200 1970 LET S=1/8 1980 FOR I=4*K-3 TO 4*K 1990 GOTO 2370 2000 NEXT I 2010 LET S=0 2020 GOTO 1980 2030 DATA 1,49,52,4,13,61,64,16,22,39,23,38,26,42,27,43 2040 DATA 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20 2050 DATA 21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38 2060 DATA 39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56 2070 DATA 57,58,59,60,61,62,63,64 2080 DATA 1,17,33,49,5,21,37,53,9,25,41,57,13,29,45,61 2090 DATA 2,18,34,50,6,22,38,54,10,26,42,58,14,30,46,62 2100 DATA 3,19,35,51,7,23,39,55,11,27,43,59,15,31,47,63 2110 DATA 4,20,36,52,8,24,40,56,12,28,44,60,16,32,48,64 2120 DATA 1,5,9,13,17,21,25,29,33,37,41,45,49,53,57,61 2130 DATA 2,6,10,14,18,22,26,30,34,38,42,46,50,54,58,62 2140 DATA 3,7,11,15,19,23,27,31,35,39,43,47,51,55,59,63 2150 DATA 4,8,12,16,20,24,28,32,36,40,44,48,52,56,60,64 2160 DATA 1,6,11,16,17,22,27,32,33,38,43,48,49,54,59,64 2170 DATA 13,10,7,4,29,26,23,20,45,42,39,36,61,58,55,52 2180 DATA 1,21,41,61,2,22,42,62,3,23,43,63,4,24,44,64 2190 DATA 49,37,25,13,50,38,26,14,51,39,27,15,52,40,28,16 2200 DATA 1,18,35,52,5,22,39,56,9,26,43,60,13,30,47,64 2210 DATA 49,34,19,4,53,38,23,8,57,42,27,12,61,46,31,16 2220 DATA 1,22,43,64,16,27,38,49,4,23,42,61,13,26,39,52 2230 FOR J=1 TO 4 2240 IF X(M(I,J))<>1/8 THEN 2330 2250 LET X(M(I,J))=5 2260 IF L(I)<5 THEN 2290 2270 PRINT "LET'S SEE YOU GET OUT OF THIS: MACHINE MOVES TO"; 2280 GOTO 2300 2290 PRINT "YOU FOX. JUST IN THE NICK OF TIME, MACHINE MOVES TO"; 2300 LET M=M(I,J) 2310 GOSUB 1570 2320 GOTO 500 2330 NEXT J 2340 PRINT "MACHINE CONCEDES THIS GAME." 2350 GOTO 1490 2360 LET S=1/8 2370 IF I-INT(I/4)*4>1 THEN 2400 2380 LET A=1 2390 GOTO 2410 2400 LET A=2 2410 FOR J=A TO 5-A STEP 5-2*A 2420 IF X(M(I,J))=S THEN 2450 2430 NEXT J 2440 GOTO 2000 2450 LET X(M(I,J))=5 2460 LET M=M(I,J) 2470 PRINT "MACHINE TAKES"; 2480 GOSUB 1570 2490 GOTO 500 2500 FOR I=1 TO 64 2510 IF X(I)<>1/8 THEN 2530 2520 LET X(I)=0 2530 NEXT I 2540 RETURN 2550 FOR XX=1 TO 9:PRINT:NEXT:FOR I=1 TO 4 2560 FOR J=1 TO 4 2562 FOR I1=1 TO J 2564 PRINT" "; 2566 NEXT I1 2570 FOR K=1 TO 4 2600 LET Q=16*I+4*J+K-20 2610 IF X(Q)<>O THEN 2630 2620 PRINT"( ) "; 2630 IF X(Q)<>5 THEN 2650 2640 PRINT"(M) "; 2650 IF X(Q)<>1 THEN 2660 2655 PRINT"(Y) "; 2660 IF X(Q)<>1/8 THEN 2670 2665 PRINT"( ) "; 2670 NEXT K 2680 PRINT 2690 PRINT 2700 NEXT J 2710 PRINT 2720 PRINT 2730 NEXT I 2735 REM PRINT CHR$(12) 2740 RETURN 2750 PRINT"INCORRECT MOVE, RETYPE IT--"; 2760 GOTO 520 2770 END ================================================ FILE: 00_Alternate_Languages/89_Tic-Tac-Toe/MiniScript/README.md ================================================ 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 tictactoe.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 "tictactoe" run ``` ================================================ FILE: 00_Alternate_Languages/89_Tic-Tac-Toe/MiniScript/tictactoe.ms ================================================ // 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 ================================================ FILE: 00_Alternate_Languages/89_Tic-Tac-Toe/MiniScript/tictactoe2.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/89_Tic-Tac-Toe/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/89_Tic-Tac-Toe/go/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Pascal](https://en.wikipedia.org/wiki/Pascal_(programming_language)) ================================================ FILE: 00_Alternate_Languages/89_Tic-Tac-Toe/go/src/tictactoe1.go ================================================ package main import ( "fmt" "strconv" ) func main() { // Print text on the screen with 30 spaces before text fmt.Printf("%30s\n", "TIC TAC TOE") // Print text on screen with 15 spaces before text // And print three lines break on screen fmt.Printf("%15s\n\n\n\n", "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") // THIS PROGRAM PLAYS TIC TAC TOE fmt.Printf("THE GAME BOARD IS NUMBERED:\n\n") fmt.Println("1 2 3") fmt.Println("8 9 4") fmt.Println("7 6 5") // Main program for { var ( a, b, c, d, e int p, q, r, s int ) a = 9 fmt.Printf("\n\n") // THE MACHINE GOES FIRST computerMoves(a) p = readYourMove() b = move(p + 1) computerMoves(b) q = readYourMove() if q == move(b+4) { c = move(b + 2) computerMoves(c) r = readYourMove() if r == move(c+4) { if p%2 != 0 { d = move(c + 3) computerMoves(d) s = readYourMove() if s == move(d+4) { e = move(d + 6) computerMoves(e) fmt.Println("THE GAME IS A DRAW.") } else { e = move(d + 4) computerMoves(e) fmt.Println("AND WINS ********") } } else { d = move(c + 7) computerMoves(d) fmt.Println("AND WINS ********") } } else { d = move(c + 4) computerMoves(d) fmt.Println("AND WINS ********") } } else { c = move(b + 4) computerMoves(c) fmt.Println("AND WINS ********") } } } func computerMoves(move int) { fmt.Printf("COMPUTER MOVES %v\n", move) } func readYourMove() int { for { fmt.Printf("YOUR MOVE?") var input string fmt.Scan(&input) number, err := strconv.Atoi(input) if err == nil { return number } } } func move(number int) int { return number - 8*(int)((number-1)/8) } ================================================ FILE: 00_Alternate_Languages/89_Tic-Tac-Toe/pascal/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Pascal](https://en.wikipedia.org/wiki/Pascal_(programming_language)) ================================================ FILE: 00_Alternate_Languages/89_Tic-Tac-Toe/pascal/tictactoe1.pas ================================================ program tictactoe1; var a, b, c, d, e: integer; p, q, r, s: integer; procedure computerMoves(m: integer); begin write('COMPUTER MOVES '); writeln(m); end; function readYourMove() : integer; var number: integer; begin write('YOUR MOVE?'); readln(number); readYourMove := number; end; function move(number: integer): integer; begin move := number - 8 * trunc((number - 1) / 8); end; function padLeft(m: string; n: integer): string; var tmp: string; begin tmp := ''; repeat tmp := tmp + ' '; n := n - 1; until n = 0; tmp := tmp + m; padLeft := tmp; end; begin writeln(padLeft('TIC TAC TOE', 30)); writeln(padLeft('CREATIVE COMPUTING MORRISTOWN, NEW JERSEY', 15)); writeln(''); writeln(''); writeln(''); writeln('THE GAME BOARD IS NUMBERED:'); writeln(''); writeln('1 2 3'); writeln('8 9 4'); writeln('7 6 5'); while(true) do begin writeln(''); writeln(''); a := 9; computerMoves(a); p := readYourMove(); b := move(p + 1); computerMoves(b); q := readYourMove(); if (q = move(b + 4)) then begin c := move(b + 2); computerMoves(c); r := readYourMove(); if (r = move(c + 4)) then begin if (p mod 2 <> 0) then begin d := move(c + 3); computerMoves(d); s := readYourMove(); if (s = move(d + 4)) then begin e := move(d + 6); computerMoves(e); writeln('THE GAME IS A DRAW.'); end else begin e := move(d + 4); computerMoves(e); writeln('AND WINS ********'); end end else begin d := move(c + 7); computerMoves(d); writeln('AND WINS ********'); end end else begin d := move(c + 4); computerMoves(d); writeln('AND WINS ********'); end end else begin c := move(b + 4); computerMoves(c); writeln('AND WINS ********'); end; end; end. ================================================ FILE: 00_Alternate_Languages/89_Tic-Tac-Toe/tictactoe1.bas ================================================ 10 PRINT TAB(30);"TIC TAC TOE" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 30 PRINT:PRINT:PRINT 50 REM 100 REM THIS PROGRAM PLAYS TIC TAC TOE 110 REM THE MACHINE GOES FIRST 120 PRINT "THE GAME BOARD IS NUMBERED:": PRINT 130 PRINT "1 2 3": PRINT "8 9 4": PRINT "7 6 5" 140 PRINT 150 REM 160 REM 170 REM 180 DEF FNM(X)=X-8*INT((X-1)/8) 190 REM 200 REM MAIN PROGRAM 210 PRINT 220 PRINT 230 A=9 240 M=A 250 GOSUB 650 260 P=M 270 B=FNM(P+1) 280 M=B 290 GOSUB 650 300 Q=M 310 IF Q=FNM(B+4) THEN 360 320 C=FNM(B+4) 330 M=C 340 GOSUB 700 350 GOTO 730 360 C=FNM(B+2) 370 M=C 380 GOSUB 650 390 R=M 400 IF R=FNM(C+4) THEN 450 410 D=FNM(C+4) 420 M=D 430 GOSUB 700 440 GOTO 730 450 IF P/2<>INT(P/2) THEN 500 460 D=FNM(C+7) 470 M=D 480 GOSUB 700 490 GOTO 730 500 D=FNM(C+3) 510 M=D 520 GOSUB 650 530 S=M 540 IF S=FNM(D+4) THEN 590 550 E=FNM(D+4) 560 M=E 570 GOSUB 700 580 REM 590 E=FNM(D+6) 600 M=E 610 GOSUB 700 620 PRINT "THE GAME IS A DRAW." 630 GOTO 210 640 REM 650 GOSUB 700 660 PRINT "YOUR MOVE"; 670 INPUT M 680 RETURN 700 PRINT "COMPUTER MOVES";M 710 RETURN 720 REM 730 PRINT "AND WINS ********" 740 GOTO 210 750 END ================================================ FILE: 00_Alternate_Languages/89_Tic-Tac-Toe/tictactoe2.bas ================================================ 2 PRINT TAB(30);"TIC-TAC-TOE" 4 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 6 PRINT:PRINT:PRINT 8 PRINT "THE BOARD IS NUMBERED:" 10 PRINT " 1 2 3" 12 PRINT " 4 5 6" 14 PRINT " 7 8 9" 16 PRINT:PRINT:PRINT 20 DIM S(9) 50 INPUT"DO YOU WANT 'X' OR 'O'";C$ 55 IF C$="X"THEN 475 60 P$="O":Q$="X" 100 G=-1:H=1:IF S(5)<>0 THEN 103 102 S(5)=-1:GOTO 195 103 IF S(5)<>1 THEN 106 104 IF S(1)<>0 THEN 110 105 S(1)=-1:GOTO 195 106 IF S(2)=1 AND S(1)=0 THEN 181 107 IF S(4)=1 AND S(1)=0 THEN 181 108 IF S(6)=1 AND S(9)=0 THEN 189 109 IF S(8)=1 AND S(9)=0 THEN 189 110 IF G=1 THEN 112 111 GOTO 118 112 J=3*INT((M-1)/3)+1 113 IF 3*INT((M-1)/3)+1=M THEN K=1 114 IF 3*INT((M-1)/3)+2=M THEN K=2 115 IF 3*INT((M-1)/3)+3=M THEN K=3 116 GOTO 120 118 FOR J=1 TO 7 STEP 3:FOR K=1 TO 3 120 IF S(J)<>G THEN 130 122 IF S(J+2)<>G THEN 135 126 IF S(J+1)<>0 THEN 150 128 S(J+1)=-1:GOTO 195 130 IF S(J)=H THEN 150 131 IF S(J+2)<>G THEN 150 132 IF S(J+1)<>G THEN 150 133 S(J)=-1:GOTO 195 135 IF S(J+2)<>0 THEN 150 136 IF S(J+1)<>G THEN 150 138 S(J+2)=-1:GOTO 195 150 IF S(K)<>G THEN 160 152 IF S(K+6)<>G THEN 165 156 IF S(K+3)<>0 THEN 170 158 S(K+3)=-1:GOTO 195 160 IF S(K)=H THEN 170 161 IF S(K+6)<>G THEN 170 162 IF S(K+3)<>G THEN 170 163 S(K)=-1:GOTO 195 165 IF S(K+6)<>0 THEN 170 166 IF S(K+3)<>G THEN 170 168 S(K+6)=-1:GOTO 195 170 GOTO 450 171 IF S(3)=G AND S(7)=0 THEN 187 172 IF S(9)=G AND S(1)=0 THEN 181 173 IF S(7)=G AND S(3)=0 THEN 183 174 IF S(9)=0 AND S(1)=G THEN 189 175 IF G=-1 THEN G=1:H=-1:GOTO 110 176 IF S(9)=1 AND S(3)=0 THEN 182 177 FOR I=2 TO 9:IF S(I)<>0 THEN 179 178 S(I)=-1:GOTO 195 179 NEXT I 181 S(1)=-1:GOTO 195 182 IF S(1)=1 THEN 177 183 S(3)=-1:GOTO 195 187 S(7)=-1:GOTO 195 189 S(9)=-1 195 PRINT:PRINT"THE COMPUTER MOVES TO..." 202 GOSUB 1000 205 GOTO 500 450 IF G=1 THEN 465 455 IF J=7 AND K=3 THEN 465 460 NEXT K,J 465 IF S(5)=G THEN 171 467 GOTO 175 475 P$="X":Q$="O" 500 PRINT:INPUT"WHERE DO YOU MOVE";M 502 IF M=0 THEN PRINT"THANKS FOR THE GAME.":GOTO 2000 503 IF M>9 THEN 506 505 IF S(M)=0 THEN 510 506 PRINT"THAT SQUARE IS OCCUPIED.":PRINT:PRINT:GOTO 500 510 G=1:S(M)=1 520 GOSUB 1000 530 GOTO 100 1000 PRINT:FOR I=1 TO 9:PRINT" ";:IF S(I)<>-1 THEN 1014 1012 PRINT Q$" ";:GOTO 1020 1014 IF S(I)<>0 THEN 1018 1016 PRINT" ";:GOTO 1020 1018 PRINT P$" "; 1020 IF I<>3 AND I<>6 THEN 1050 1030 PRINT:PRINT"---+---+---" 1040 GOTO 1080 1050 IF I=9 THEN 1080 1060 PRINT"!"; 1080 NEXT I:PRINT:PRINT:PRINT 1095 FOR I=1 TO 7 STEP 3 1100 IF S(I)<>S(I+1)THEN 1115 1105 IF S(I)<>S(I+2)THEN 1115 1110 IF S(I)=-1 THEN 1350 1112 IF S(I)=1 THEN 1200 1115 NEXT I:FOR I=1 TO 3:IF S(I)<>S(I+3)THEN 1150 1130 IF S(I)<>S(I+6)THEN 1150 1135 IF S(I)=-1 THEN 1350 1137 IF S(I)=1 THEN 1200 1150 NEXT I:FOR I=1 TO 9:IF S(I)=0 THEN 1155 1152 NEXT I:GOTO 1400 1155 IF S(5)<>G THEN 1170 1160 IF S(1)=G AND S(9)=G THEN 1180 1165 IF S(3)=G AND S(7)=G THEN 1180 1170 RETURN 1180 IF G=-1 THEN 1350 1200 PRINT"YOU BEAT ME!! GOOD GAME.":GOTO 2000 1350 PRINT"I WIN, TURKEY!!!":GOTO 2000 1400 PRINT"IT'S A DRAW. THANK YOU." 2000 END ================================================ FILE: 00_Alternate_Languages/90_Tower/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/90_Tower/MiniScript/tower.ms ================================================ 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!" ================================================ FILE: 00_Alternate_Languages/90_Tower/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/90_Tower/tower.bas ================================================ 10 PRINT TAB(33);"TOWERS" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 30 PRINT:PRINT:PRINT 90 PRINT 100 REM*** INITIALIZE 110 DIM T(7,3) 120 E=0 130 FOR D=1 TO 7 140 FOR N=1 TO 3 150 T(D,N)=0 160 NEXT N 170 NEXT D 180 PRINT "TOWERS OF HANOI PUZZLE.": PRINT 200 PRINT "YOU MUST TRANSFER THE DISKS FROM THE LEFT TO THE RIGHT" 205 PRINT "TOWER, ONE AT A TIME, NEVER PUTTING A LARGER DISK ON A" 210 PRINT "SMALLER DISK.": PRINT 215 INPUT "HOW MANY DISKS DO YOU WANT TO MOVE (7 IS MAX)";S 220 PRINT 230 M=0 240 FOR Q=1 TO 7 250 IF Q=S THEN 350 260 NEXT Q 270 E=E+1 280 IF E>2 THEN 310 290 PRINT "SORRY, BUT I CAN'T DO THAT JOB FOR YOU.": GOTO 215 310 PRINT "ALL RIGHT, WISE GUY, IF YOU CAN'T PLAY THE GAME RIGHT, I'LL" 320 PRINT "JUST TAKE MY PUZZLE AND GO HOME. SO LONG.": STOP 340 REM *** STORE DISKS FROM SMALLEST TO LARGEST 350 PRINT "IN THIS PROGRAM, WE SHALL REFER TO DISKS BY NUMERICAL CODE." 355 PRINT "3 WILL REPRESENT THE SMALLEST DISK, 5 THE NEXT SIZE," 360 PRINT "7 THE NEXT, AND SO ON, UP TO 15. IF YOU DO THE PUZZLE WITH" 365 PRINT "2 DISKS, THEIR CODE NAMES WOULD BE 13 AND 15. WITH 3 DISKS" 370 PRINT "THE CODE NAMES WOULD BE 11, 13 AND 15, ETC. THE NEEDLES" 375 PRINT "ARE NUMBERED FROM LEFT TO RIGHT, 1 TO 3. WE WILL" 380 PRINT "START WITH THE DISKS ON NEEDLE 1, AND ATTEMPT TO MOVE THEM" 385 PRINT "TO NEEDLE 3." 390 PRINT: PRINT "GOOD LUCK!": PRINT 400 Y=7: D=15 420 FOR X=S TO 1 STEP -1 430 T(Y,1)=D: D=D-2: Y=Y-1 460 NEXT X 470 GOSUB 1230 480 PRINT "WHICH DISK WOULD YOU LIKE TO MOVE";:E=0 500 INPUT D 510 IF (D-3)*(D-5)*(D-7)*(D-9)*(D-11)*(D-13)*(D-15)=0 THEN 580 520 PRINT "ILLEGAL ENTRY... YOU MAY ONLY TYPE 3,5,7,9,11,13, OR 15." 530 E=E+1: IF E>1 THEN 560 550 GOTO 500 560 PRINT "STOP WASTING MY TIME. GO BOTHER SOMEONE ELSE.": STOP 580 REM *** CHECK IF REQUESTED DISK IS BELOW ANOTHER 590 FOR R=1 TO 7 600 FOR C=1 TO 3 610 IF T(R,C)=D THEN 640 620 NEXT C: NEXT R 640 FOR Q=R TO 1 STEP -1 645 IF T(Q,C)=0 THEN 660 650 IF T(Q,C)<D THEN 680 660 NEXT Q 670 GOTO 700 680 PRINT "THAT DISK IS BELOW ANOTHER ONE. MAKE ANOTHER CHOICE." 690 GOTO 480 700 E=0 705 INPUT "PLACE DISK ON WHICH NEEDLE";N 730 IF (N-1)*(N-2)*(N-3)=0 THEN 800 735 E=E+1 740 IF E>1 THEN 780 750 PRINT "I'LL ASSUME YOU HIT THE WRONG KEY THIS TIME. BUT WATCH IT," 760 PRINT "I ONLY ALLOW ONE MISTAKE.": GOTO 705 780 PRINT "I TRIED TO WARN YOU, BUT YOU WOULDN'T LISTEN." 790 PRINT "BYE BYE, BIG SHOT.":STOP 800 FOR R=1 TO 7 810 IF T(R,N)<>0 THEN 840 820 NEXT R 830 GOTO 880 835 REM *** CHECK IF DISK TO BE PLACED ON A LARGER ONE 840 IF D<T(R,N) THEN 880 850 PRINT "YOU CAN'T PLACE A LARGER DISK ON TOP OF A SMALLER ONE," 860 PRINT "IT MIGHT CRUSH IT!": PRINT "NOW THEN, ";:GOTO 480 875 REM *** MOVE RELOCATED DISK 880 FOR V=1 TO 7: FOR W=1 TO 3 900 IF T(V,W)=D THEN 930 910 NEXT W: NEXT V 925 REM *** LOCATE EMPTY SPACE ON NEEDLE N 930 FOR U=1 TO 7 940 IF T(U,N)<>0 THEN 970 950 NEXT U 960 U=7: GOTO 980 965 REM *** MOVE DISK AND SET OLD LOCATION TO 0 970 U=U-1 980 T(U,N)=T(V,W): T(V,W)=0 995 REM *** PRINT OUT CURRENT STATUS 1000 GOSUB 1230 1018 REM *** CHECK IF DONE 1020 M=M+1 1030 FOR R=1 TO 7: FOR C=1 TO 2 1050 IF T(R,C)<>0 THEN 1090 1060 NEXT C: NEXT R 1080 GOTO 1120 1090 IF M<=128 THEN 480 1100 PRINT "SORRY, BUT I HAVE ORDERS TO STOP IF YOU MAKE MORE THAN" 1110 PRINT "128 MOVES.": STOP 1120 IF M<>2^S-1 THEN 1140 1130 PRINT:PRINT "CONGRATULATIONS!!":PRINT 1140 PRINT "YOU HAVE PERFORMED THE TASK IN";M;"MOVES." 1150 PRINT: PRINT "TRY AGAIN (YES OR NO)";: INPUT A$ 1160 IF A$="NO" THEN 1390 1170 IF A$="YES" THEN 90 1180 PRINT: PRINT "'YES' OR 'NO' PLEASE";: INPUT A$: GOTO 1160 1230 REM *** PRINT SUBROUTINE 1240 FOR K=1 TO 7 1250 Z=10 1260 FOR J=1 TO 3 1270 IF T(K,J)=0 THEN 1330 1280 PRINT TAB(Z-INT(T(K,J)/2)); 1290 FOR V=1 TO T(K,J) 1300 PRINT "*"; 1310 NEXT V 1320 GOTO 1340 1330 PRINT TAB(Z);"*"; 1340 Z=Z+21 1350 NEXT J 1360 PRINT 1370 NEXT K 1380 RETURN 1390 PRINT: PRINT "THANKS FOR THE GAME!": PRINT: END ================================================ FILE: 00_Alternate_Languages/91_Train/D/README.md ================================================ Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). Conversion to [MiniScript](https://dlang.org). ================================================ FILE: 00_Alternate_Languages/91_Train/D/train.d ================================================ 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; } } } ================================================ FILE: 00_Alternate_Languages/91_Train/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/91_Train/MiniScript/train.ms ================================================ // 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 ================================================ FILE: 00_Alternate_Languages/91_Train/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/91_Train/nim/train.nim ================================================ 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() ================================================ FILE: 00_Alternate_Languages/91_Train/train.bas ================================================ 1 PRINT TAB(33);"TRAIN" 2 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 3 PRINT: PRINT: PRINT 4 PRINT "TIME - SPEED DISTANCE EXERCISE": PRINT 10 C=INT(25*RND(1))+40 15 D=INT(15*RND(1))+5 20 T=INT(19*RND(1))+20 25 PRINT " A CAR TRAVELING";C;"MPH CAN MAKE A CERTAIN TRIP IN" 30 PRINT D;"HOURS LESS THAN A TRAIN TRAVELING AT";T;"MPH." 35 PRINT "HOW LONG DOES THE TRIP TAKE BY CAR"; 40 INPUT A 45 V=D*T/(C-T) 50 E=INT(ABS((V-A)*100/A)+.5) 55 IF E>5 THEN 70 60 PRINT "GOOD! ANSWER WITHIN";E;"PERCENT." 65 GOTO 80 70 PRINT "SORRY. YOU WERE OFF BY";E;"PERCENT." 80 PRINT "CORRECT ANSWER IS";V;"HOURS." 90 PRINT 95 PRINT "ANOTHER PROBLEM (YES OR NO)"; 100 INPUT A$ 105 PRINT 110 IF A$="YES" THEN 10 999 END ================================================ FILE: 00_Alternate_Languages/92_Trap/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/92_Trap/MiniScript/trap.ms ================================================ // 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 ================================================ FILE: 00_Alternate_Languages/92_Trap/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/92_Trap/trap.bas ================================================ 1 PRINT TAB(34);"TRAP" 2 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 3 PRINT:PRINT:PRINT 10 G=6 20 N=100 30 REM-TRAP 40 REM-STEVE ULLMAN, 8-1-72 50 PRINT "INSTRUCTIONS"; 60 INPUT Z$ 70 IF LEFT$(Z$,1)<>"Y" THEN 180 80 PRINT "I AM THINKING OF A NUMBER BETWEEN 1 AND";N 90 PRINT "TRY TO GUESS MY NUMBER. ON EACH GUESS," 100 PRINT "YOU ARE TO ENTER 2 NUMBERS, TRYING TO TRAP" 110 PRINT "MY NUMBER BETWEEN THE TWO NUMBERS. I WILL" 120 PRINT "TELL YOU IF YOU HAVE TRAPPED MY NUMBER, IF MY" 130 PRINT "NUMBER IS LARGER THAN YOUR TWO NUMBERS, OR IF" 140 PRINT "MY NUMBER IS SMALLER THAN YOUR TWO NUMBERS." 150 PRINT "IF YOU WANT TO GUESS ONE SINGLE NUMBER, TYPE" 160 PRINT "YOUR GUESS FOR BOTH YOUR TRAP NUMBERS." 170 PRINT "YOU GET";G;"GUESSES TO GET MY NUMBER." 180 X=INT(N*RND(1))+1 190 FOR Q=1 TO G 200 PRINT 210 PRINT "GUESS #";Q; 220 INPUT A,B 230 IF A=B AND X=A THEN 400 240 IF A <= B THEN 260 250 GOSUB 360 260 IF A <= X AND X <= B THEN 320 270 IF X<A THEN 300 280 PRINT "MY NUMBER IS LARGER THAN YOUR TRAP NUMBERS." 290 GOTO 330 300 PRINT "MY NUMBER IS SMALLER THAN YOUR TRAP NUMBERS." 310 GOTO 330 320 PRINT "YOU HAVE TRAPPED MY NUMBER." 330 NEXT Q 340 PRINT "SORRY, THAT'S";G;"GUESSES. THE NUMBER WAS";X 345 PRINT 350 GOTO 410 360 R=A 370 A=B 380 B=R 390 RETURN 400 PRINT "YOU GOT IT!!!" 410 PRINT 420 PRINT "TRY AGAIN." 430 PRINT 440 GOTO 180 450 END ================================================ FILE: 00_Alternate_Languages/93_23_Matches/23matches.bas ================================================ 20 PRINT TAB(31);"23 MATCHES" 30 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 40 PRINT:PRINT:PRINT 80 PRINT " THIS IS A GAME CALLED '23 MATCHES'." 90 PRINT 100 PRINT "WHEN IT IS YOUR TURN, YOU MAY TAKE ONE, TWO, OR THREE" 110 PRINT "MATCHES. THE OBJECT OF THE GAME IS NOT TO HAVE TO TAKE" 120 PRINT "THE LAST MATCH." 130 PRINT 140 PRINT "LET'S FLIP A COIN TO SEE WHO GOES FIRST." 150 PRINT "IF IT COMES UP HEADS, I WILL WIN THE TOSS." 155 PRINT 160 REM 165 N = 23 170 Q = INT(2*RND(5)) 180 IF Q = 1 THEN 210 190 PRINT "TAILS! YOU GO FIRST. " 195 PRINT 200 GOTO 300 210 PRINT "HEADS! I WIN! HA! HA!" 220 PRINT "PREPARE TO LOSE, MEATBALL-NOSE!!" 230 PRINT 250 PRINT "I TAKE 2 MATCHES" 260 N = N -2 270 PRINT "THE NUMBER OF MATCHES IS NOW" N 280 PRINT 290 PRINT "YOUR TURN -- YOU MAY TAKE 1, 2 OR 3 MATCHES." 300 PRINT "HOW MANY DO YOU WISH TO REMOVE", 310 INPUT K 320 IF K > 3 THEN 430 330 IF K <= 0 THEN 430 340 N = N - K 350 PRINT "THERE ARE NOW";N;"MATCHES REMAINING." 351 IF N = 4 THEN 381 352 IF N = 3 THEN 383 353 IF N = 2 THEN 385 360 IF N <= 1 THEN 530 370 Z = 4 - K 372 GOTO 390 380 PRINT 381 Z = 3 382 GOTO 390 383 Z = 2 384 GOTO 390 385 Z = 1 390 PRINT "MY TURN ! I REMOVE" Z "MATCHES" 400 N = N - Z 410 IF N <= 1 THEN 470 420 GOTO 270 430 PRINT "VERY FUNNY! DUMMY!" 440 PRINT "DO YOU WANT TO PLAY OR GOOF AROUND?" 450 PRINT "NOW, HOW MANY MATCHES DO YOU WANT", 460 GOTO 310 470 PRINT 480 PRINT"YOU POOR BOOB! YOU TOOK THE LAST MATCH! I GOTCHA!!" 490 PRINT "HA ! HA ! I BEAT YOU !!!" 500 PRINT 510 PRINT "GOOD BYE LOSER!" 520 GOTO 560 530 PRINT "YOU WON, FLOPPY EARS !" 540 PRINT "THINK YOU'RE PRETTY SMART !" 550 PRINT "LETS PLAY AGAIN AND I'LL BLOW YOUR SHOES OFF !!" 560 STOP 570 END ================================================ FILE: 00_Alternate_Languages/93_23_Matches/MiniScript/23matches.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/93_23_Matches/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/93_23_Matches/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/94_War/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/94_War/MiniScript/war.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/94_War/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/94_War/d/.gitignore ================================================ *.exe *.obj ================================================ FILE: 00_Alternate_Languages/94_War/d/README.md ================================================ Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html) Converted to [D](https://dlang.org/) by [Bastiaan Veelo](https://github.com/veelo). ## Running the code Assuming the reference [dmd](https://dlang.org/download.html#dmd) compiler: ```shell dmd -preview=dip1000 -run war.d ``` [Other compilers](https://dlang.org/download.html) also exist. ## Specialties explained This game code contains some specialties that you might want to know more about. Here goes. ### Suits Most modern consoles are capable of displaying more than just ASCII, and so I have chosen to display the actual ♠, ♥, ♦ and ♣ instead of substituting them by letters like the BASIC original did. Only the Windows console needs a nudge in the right direction with these instructions: ```d SetConsoleOutputCP(CP_UTF8); // Set code page SetConsoleOutputCP(GetACP); // Restore the default ``` Instead of cluttering the `main()` function with these lesser important details, we can move them into a [module constructor and module destructor](https://dlang.org/spec/module.html#staticorder), which run before and after `main()` respectively. And because order of declaration is irrelevant in a D module, we can push those all the way down to the bottom of the file. This is of course only necessary on Windows (and won't even work anywhere else) so we'll need to wrap this in a `version (Windows)` conditional code block: ```d version (Windows) { import core.sys.windows.windows; shared static this() @trusted { SetConsoleOutputCP(CP_UTF8); } shared static ~this() @trusted { SetConsoleOutputCP(GetACP); } } ``` Although it doesn't matter much in this single-threaded program, the `shared` attribute makes that these constructors/destructors are run once per program invocation; non-shared module constructors and module destructors are run for every thread. The `@trusted` annotation is necessary because these are system API calls; The compiler cannot check these for memory-safety, and so we must indicate that we have reviewed the safety manually. ### Uniform Function Call Syntax In case you wonder why this line works: ```d if ("Do you want instructions?".yes) // ... ``` then it is because this is equivalent to ```d if (yes("Do you want instructions?")) // ... ``` where `yes()` is a Boolean function that is defined below `main()`. This is made possible by the language feature that is called [uniform function call syntax (UFCS)](https://dlang.org/spec/function.html#pseudo-member). UFCS works by passing what is in front of the dot as the first parameter to the function, and it was invented to make it possible to call free functions on objects as if they were member functions. UFCS can also be used to obtain a more natural order of function calls, such as this line inside `yes()`: ```d return trustedReadln.strip.toLower.startsWith("y"); ``` which reads easier than the equivalent ```d return startsWith(toLower(strip(trustedReadln())), "y"); ``` ### Type a lot or not? It would have been straight forward to define the `cards` array explicitly like so: ```d const cards = ["2♠", "2♥", "2♦", "2♣", "3♠", "3♥", "3♦", "3♣", "4♠", "4♥", "4♦", "4♣", "5♠", "5♥", "5♦", "5♣", "6♠", "6♥", "6♦", "6♣", "7♠", "7♥", "7♦", "7♣", "8♠", "8♥", "8♦", "8♣", "9♠", "9♥", "9♦", "9♣", "10♠", "10♥", "10♦", "10♣", "J♥", "J♦", "J♣", "J♣", "Q♠", "Q♥", "Q♦", "Q♣", "K♠", "K♥", "K♦", "K♣", "A♠", "A♥", "A♦", "A♣"]; ``` but that's tedious, difficult to spot errors in (*can you?*) and looks like something a computer can automate. Indeed it can: ```d static const cards = cartesianProduct(["2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"], ["♠", "♥", "♦", "♣"]).map!(a => a.expand.only.join).array; ``` The function [`cartesianProduct`](https://dlang.org/phobos/std_algorithm_setops.html#cartesianProduct) takes two ranges, like the horizontal and vertical headers of a spreadsheet, and fills the table with the combinations that form the coordinates of the cells. But the output of that function is in the form of an array of [`Tuple`](https://dlang.org/phobos/std_typecons.html#Tuple)s, which looks like `[Tuple!(string, string)("2", "♠"), Tuple!(string, string)("2", "♥"), ... etc]`. [`map`](https://dlang.org/phobos/std_algorithm_iteration.html#map) comes to the rescue, converting each Tuple to a string, by calling [`expand`](https://dlang.org/phobos/std_typecons.html#.Tuple.expand), then [`only`](https://dlang.org/phobos/std_range.html#only) and then [`join`](https://dlang.org/phobos/std_array.html#join) on them. The result is a lazily evaluated range of strings. Finally, [`array`](https://dlang.org/phobos/std_array.html#array) turns the range into a random access array. The `static` attribute makes that all this is performed at compile-time, so the result is exactly the same as the manually entered data, but without the typo's. ### Shuffle the cards or not? The original BASIC code works with a constant array of cards, ordered by increasing numerical value, and indexing it with indices that have been shuffled. This is efficient because in comparing who wins, the indices can be compared directly, since a higher index correlates to a card with a higher numerical value (when divided by the number of suits, 4). Some of the other reimplementations in other languages have been written in a lesser efficient way by shuffling the array of cards itself. This then requires the use of a lookup table or searching for equality in an auxiliary array when comparing cards. I find the original more elegant, so that's what you see here: ```d const indices = iota(0, cards.length).array.randomShuffle; ``` [`iota`](https://dlang.org/phobos/std_range.html#iota) produces a range of integers, in this case starting at 0 and increasing up to the number of cards in the deck (exclusive). [`array`](https://dlang.org/phobos/std_array.html#array) turns the range into an array, so that [`randomShuffle`](https://dlang.org/phobos/std_random.html#randomShuffle) can do its work. ================================================ FILE: 00_Alternate_Languages/94_War/d/war.d ================================================ @safe: // Make @safe the default for this file, enforcing memory-safety. import std; void main() { enum width = 50; writeln(center("War", width)); writeln(center("(After Creative Computing Morristown, New Jersey)\n\n", width)); writeln(wrap("This is the card game of war. Each card is given by suit-# " ~ "as 7♠ for seven of spades. ", width)); if ("Do you want instructions?".yes) writeln("\n", wrap("The computer gives you and it a 'card'. The higher card " ~ "(numerically) wins. The game ends when you choose not to " ~ "continue or when you have finished the pack.\n", width)); static const cards = cartesianProduct(["2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"], ["♠", "♥", "♦", "♣"]).map!(a => a.expand.only.join).array; const indices = iota(0, cards.length).array.randomShuffle; int yourScore = 0, compScore = 0, i = 0; while (i < indices.length) { size_t your = indices[i++], comp = indices[i++]; writeln("\nYou: ", cards[your].leftJustify(9), "Computer: ", cards[comp]); your /= 4; comp /= 4; if (your == comp) writeln("Tie. No score change."); else if (your < comp) writeln("The computer wins!!! You have ", yourScore, " and the computer has ", ++compScore, "."); else writeln("You win. You have ", ++yourScore, " and the computer has ", compScore, "."); if (i == indices.length) writeln("\nWe have run out of cards. Final score: You: ", yourScore, ", the computer: ", compScore, "."); else if (!"Do you want to continue?".yes) break; } writeln("\nThanks for playing. It was fun."); } /// Returns whether question was answered positively. bool yes(string question) { writef!"%s "(question); try return trustedReadln.strip.toLower.startsWith("y"); catch (Exception) // readln throws on I/O and Unicode errors, which we handle here. return false; } /** An @trusted wrapper around readln. * * This is the only function that formally requires manual review for memory-safety. * [Arguably readln should be safe already](https://forum.dlang.org/post/rab398$1up$1@digitalmars.com) * which would remove the need to have any @trusted code in this program. */ string trustedReadln() @trusted { return readln; } version (Windows) { // Make the Windows console do a better job at printing UTF-8 strings, // and restore the default upon termination. import core.sys.windows.windows; shared static this() @trusted { SetConsoleOutputCP(CP_UTF8); } shared static ~this() @trusted { SetConsoleOutputCP(GetACP); } } ================================================ FILE: 00_Alternate_Languages/94_War/war.bas ================================================ 10 PRINT TAB(33);"WAR" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 30 PRINT: PRINT: PRINT 100 PRINT "THIS IS THE CARD GAME OF WAR. EACH CARD IS GIVEN BY SUIT-#" 110 PRINT "AS S-7 FOR SPADE 7. "; 120 PRINT "DO YOU WANT DIRECTIONS"; 130 INPUT B$ 140 IF B$="NO" THEN 210 150 IF B$="YES" THEN 180 160 PRINT "YES OR NO, PLEASE. "; 170 GOTO 120 180 PRINT "THE COMPUTER GIVES YOU AND IT A 'CARD'. THE HIGHER CARD" 190 PRINT "(NUMERICALLY) WINS. THE GAME ENDS WHEN YOU CHOOSE NOT TO" 200 PRINT "CONTINUE OR WHEN YOU HAVE FINISHED THE PACK." 210 PRINT 220 PRINT 230 DIM A$(52),L(54) 240 FOR I=1 TO 52 250 READ A$(I) 260 NEXT I 270 REM 280 FOR J=1 TO 52 290 LET L(J)=INT(52*RND(1))+1 295 IF J=1 THEN 350 300 FOR K=1 TO J-1 310 IF L(K)<>L(J) THEN 340 320 REM 330 GOTO 290 340 NEXT K 350 NEXT J 360 P=P+1 370 M1=L(P) 380 P=P+1 390 M2=L(P) 400 PRINT 420 PRINT "YOU: ";A$(M1),"COMPUTER: ";A$(M2) 430 N1=INT((M1-.5)/4) 440 N2=INT((M2-.5)/4) 450 IF N1>=N2 THEN 490 460 A1=A1+1 470 PRINT "THE COMPUTER WINS!!! YOU HAVE";B1;"AND THE COMPUTER HAS";A1 480 GOTO 540 490 IF N1=N2 THEN 530 500 B1=B1+1 510 PRINT "YOU WIN. YOU HAVE";B1;"AND THE COMPUTER HAS";A1 520 GOTO 540 530 PRINT "TIE. NO SCORE CHANGE." 540 IF L(P+1)=0 THEN 610 550 PRINT "DO YOU WANT TO CONTINUE"; 560 INPUT V$ 570 IF V$="YES" THEN 360 580 IF V$="NO" THEN 650 590 PRINT "YES OR NO, PLEASE. "; 600 GOTO 540 610 PRINT 620 PRINT 630 PRINT "WE HAVE RUN OUT OF CARDS. FINAL SCORE: YOU: ";B1; 640 PRINT " THE COMPUTER: ";A1:PRINT 650 PRINT "THANKS FOR PLAYING. IT WAS FUN." 655 PRINT 660 DATA "S-2","H-2","C-2","D-2","S-3","H-3","C-3","D-3" 670 DATA "S-4","H-4","C-4","D-4","S-5","H-5","C-5","D-5" 680 DATA "S-6","H-6","C-6","D-6","S-7","H-7","C-7","D-7" 690 DATA "S-8","H-8","C-8","D-8","S-9","H-9","C-9","D-9" 700 DATA "S-10","H-10","C-10","D-10","S-J","H-J","C-J","D-J" 710 DATA "S-Q","H-Q","C-Q","D-Q","S-K","H-K","C-K","D-K" 720 DATA "S-A","H-A","C-A","D-A" 999 END ================================================ FILE: 00_Alternate_Languages/95_Weekday/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/95_Weekday/MiniScript/weekday.ms ================================================ 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) + " ***" ================================================ FILE: 00_Alternate_Languages/95_Weekday/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/95_Weekday/weekday.bas ================================================ 10 PRINT TAB(32);"WEEKDAY" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 30 PRINT:PRINT:PRINT 100 PRINT "WEEKDAY IS A COMPUTER DEMONSTRATION THAT" 110 PRINT"GIVES FACTS ABOUT A DATE OF INTEREST TO YOU." 120 PRINT 130 PRINT "ENTER TODAY'S DATE IN THE FORM: 3,24,1979 "; 140 INPUT M1,D1,Y1 150 REM THIS PROGRAM DETERMINES THE DAY OF THE WEEK 160 REM FOR A DATE AFTER 1582 170 DEF FNA(A)=INT(A/4) 180 DIM T(12) 190 DEF FNB(A)=INT(A/7) 200 REM SPACE OUTPUT AND READ IN INITIAL VALUES FOR MONTHS. 210 FOR I= 1 TO 12 220 READ T(I) 230 NEXT I 240 PRINT"ENTER DAY OF BIRTH (OR OTHER DAY OF INTEREST)"; 250 INPUT M,D,Y 260 PRINT 270 LET I1 = INT((Y-1500)/100) 280 REM TEST FOR DATE BEFORE CURRENT CALENDAR. 290 IF Y-1582 <0 THEN 1300 300 LET A = I1*5+(I1+3)/4 310 LET I2=INT(A-FNB(A)*7) 320 LET Y2=INT(Y/100) 330 LET Y3 =INT(Y-Y2*100) 340 LET A =Y3/4+Y3+D+T(M)+I2 350 LET B=INT(A-FNB(A)*7)+1 360 IF M > 2 THEN 470 370 IF Y3 = 0 THEN 440 380 LET T1=INT(Y-FNA(Y)*4) 390 IF T1 <> 0 THEN 470 400 IF B<>0 THEN 420 410 LET B=6 420 LET B = B-1 430 GOTO 470 440 LET A = I1-1 450 LET T1=INT(A-FNA(A)*4) 460 IF T1 = 0 THEN 400 470 IF B <>0 THEN 490 480 LET B = 7 490 IF (Y1*12+M1)*31+D1<(Y*12+M)*31+D THEN 550 500 IF (Y1*12+M1)*31+D1=(Y*12+M)*31+D THEN 530 510 PRINT M;"/";D;"/";Y;" WAS A "; 520 GOTO 570 530 PRINT M;"/";D;"/";Y;" IS A "; 540 GOTO 570 550 PRINT M;"/";D;"/";Y;" WILL BE A "; 560 REM PRINT THE DAY OF THE WEEK THE DATE FALLS ON. 570 IF B <>1 THEN 590 580 PRINT "SUNDAY." 590 IF B<>2 THEN 610 600 PRINT "MONDAY." 610 IF B<>3 THEN 630 620 PRINT "TUESDAY." 630 IF B<>4 THEN 650 640 PRINT "WEDNESDAY." 650 IF B<>5 THEN 670 660 PRINT "THURSDAY." 670 IF B<>6 THEN 690 680 GOTO 1250 690 IF B<>7 THEN 710 700 PRINT "SATURDAY." 710 IF (Y1*12+M1)*31+D1=(Y*12+M)*31+D THEN 1120 720 LET I5=Y1-Y 730 PRINT 740 LET I6=M1-M 750 LET I7=D1-D 760 IF I7>=0 THEN 790 770 LET I6= I6-1 780 LET I7=I7+30 790 IF I6>=0 THEN 820 800 LET I5=I5-1 810 LET I6=I6+12 820 IF I5<0 THEN 1310 830 IF I7 <> 0 THEN 850 835 IF I6 <> 0 THEN 850 840 PRINT"***HAPPY BIRTHDAY***" 850 PRINT " "," ","YEARS","MONTHS","DAYS" 855 PRINT " "," ","-----","------","----" 860 PRINT "YOUR AGE (IF BIRTHDATE) ",I5,I6,I7 870 LET A8 = (I5*365)+(I6*30)+I7+INT(I6/2) 880 LET K5 = I5 890 LET K6 = I6 900 LET K7 = I7 910 REM CALCULATE RETIREMENT DATE. 920 LET E = Y+65 930 REM CALCULATE TIME SPENT IN THE FOLLOWING FUNCTIONS. 940 LET F = .35 950 PRINT "YOU HAVE SLEPT ", 960 GOSUB 1370 970 LET F = .17 980 PRINT "YOU HAVE EATEN ", 990 GOSUB 1370 1000 LET F = .23 1010 IF K5 > 3 THEN 1040 1020 PRINT "YOU HAVE PLAYED", 1030 GOTO 1080 1040 IF K5 > 9 THEN 1070 1050 PRINT "YOU HAVE PLAYED/STUDIED", 1060 GOTO 1080 1070 PRINT "YOU HAVE WORKED/PLAYED", 1080 GOSUB 1370 1085 GOTO 1530 1090 PRINT "YOU HAVE RELAXED ",K5,K6,K7 1100 PRINT 1110 PRINT TAB(16);"*** YOU MAY RETIRE IN";E;" ***" 1120 PRINT 1140 PRINT 1200 PRINT 1210 PRINT 1220 PRINT 1230 PRINT 1240 END 1250 IF D=13 THEN 1280 1260 PRINT "FRIDAY." 1270 GOTO 710 1280 PRINT "FRIDAY THE THIRTEENTH---BEWARE!" 1290 GOTO 710 1300 PRINT "NOT PREPARED TO GIVE DAY OF WEEK PRIOR TO MDLXXXII. " 1310 GOTO 1140 1320 REM TABLE OF VALUES FOR THE MONTHS TO BE USED IN CALCULATIONS. 1330 DATA 0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5 1340 REM THIS IS THE CURRENT DATE USED IN THE CALCULATIONS. 1350 REM THIS IS THE DATE TO BE CALCULATED ON. 1360 REM CALCULATE TIME IN YEARS, MONTHS, AND DAYS 1370 LET K1=INT(F*A8) 1380 LET I5 = INT(K1/365) 1390 LET K1 = K1- (I5*365) 1400 LET I6 = INT(K1/30) 1410 LET I7 = K1 -(I6*30) 1420 LET K5 = K5-I5 1430 LET K6 =K6-I6 1440 LET K7 = K7-I7 1450 IF K7>=0 THEN 1480 1460 LET K7=K7+30 1470 LET K6=K6-1 1480 IF K6>0 THEN 1510 1490 LET K6=K6+12 1500 LET K5=K5-1 1510 PRINT I5,I6,I7 1520 RETURN 1530 IF K6=12 THEN 1550 1540 GOTO 1090 1550 LET K5=K5+1 1560 LET K6=0 1570 GOTO 1090 1580 REM 1590 END ================================================ FILE: 00_Alternate_Languages/96_Word/MiniScript/README.md ================================================ 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 ``` ================================================ FILE: 00_Alternate_Languages/96_Word/MiniScript/word.ms ================================================ 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 ================================================ FILE: 00_Alternate_Languages/96_Word/README.md ================================================ Please refer to the `readme.md` in the parent folder. Each subfolder represents a port of this program to a language which is _not_ one of the agreed upon 10 languages, which are intended to meet these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language We welcome additional ports, but these additional ports are for educational purposes only. ================================================ FILE: 00_Alternate_Languages/96_Word/word.bas ================================================ 2 PRINT TAB(33);"WORD" 3 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 4 PRINT: PRINT: PRINT 5 DIM S(7),A(7),L(7),D(7),P(7) 10 PRINT "I AM THINKING OF A WORD -- YOU GUESS IT. I WILL GIVE YOU" 15 PRINT "CLUES TO HELP YOU GET IT. GOOD LUCK!!": PRINT: PRINT 20 REM 30 PRINT: PRINT: PRINT "YOU ARE STARTING A NEW GAME..." 35 RESTORE 40 READ N 50 C=INT(RND(1)*N+1) 60 FOR I=1 TO C 70 READ S$ 80 NEXT I 90 G=0 95 S(0)=LEN(S$) 100 FOR I=1 TO LEN(S$): S(I)=ASC(MID$(S$,I,1)): NEXT I 110 FOR I=1 TO 5 120 A(I)=45 130 NEXT I 140 FOR J=1 TO 5 144 P(J)=0 146 NEXT J 150 PRINT "GUESS A FIVE LETTER WORD"; 160 INPUT L$ 170 G=G+1 172 IF S$=L$ THEN 500 173 FOR I=1 TO 7: P(I)=0: NEXT I 175 L(0)=LEN(L$) 180 FOR I=1 TO LEN(L$): L(I)=ASC(MID$(L$,I,1)): NEXT I 190 IF L(1)=63 THEN 300 200 IF L(0)<>5 THEN 400 205 M=0: Q=1 210 FOR I=1 TO 5 220 FOR J=1 TO 5 230 IF S(I)<>L(J) THEN 260 231 P(Q)=L(J) 232 Q=Q+1 233 IF I<>J THEN 250 240 A(J)=L(J) 250 M=M+1 260 NEXT J 265 NEXT I 270 A(0)=5 272 P(0)=M 275 A$="": FOR I=1 TO A(0): A$=A$+CHR$(A(I)): NEXT I 277 P$="": FOR I=1 TO P(0): P$=P$+CHR$(P(I)): NEXT I 280 PRINT "THERE WERE";M;"MATCHES AND THE COMMON LETTERS WERE...";P$ 285 PRINT "FROM THE EXACT LETTER MATCHES, YOU KNOW................";A$ 286 IF A$=S$ THEN 500 287 IF M>1 THEN 289 288 PRINT: PRINT "IF YOU GIVE UP, TYPE '?' FOR YOUR NEXT GUESS." 289 PRINT 290 GOTO 150 300 S$="": FOR I=1 TO 7: S$=S$+CHR$(S(I)): NEXT I 310 PRINT "THE SECRET WORD IS ";S$: PRINT 320 GOTO 30 400 PRINT "YOU MUST GUESS A 5 LETTER WORD. START AGAIN." 410 PRINT: G=G-1: GOTO 150 500 PRINT "YOU HAVE GUESSED THE WORD. IT TOOK";G;"GUESSES!": PRINT 510 INPUT "WANT TO PLAY AGAIN";Q$ 520 IF Q$="YES" THEN 30 530 DATA 12,"DINKY","SMOKE","WATER","GRASS","TRAIN","MIGHT","FIRST" 540 DATA "CANDY","CHAMP","WOULD","CLUMP","DOPEY" 999 END ================================================ FILE: 00_Alternate_Languages/README.md ================================================ #### Alternate Languages This folder contains implementations of each program in alternate languages which are _not_ one of the agreed upon 10 languages. Implementations here are NOT bound to these three criteria: 1. Popular (by TIOBE index) 2. Memory safe 3. Generally considered a 'scripting' language So for example, (here only) C or PASCAL are allowed. Please still remain faithful to original look-and-feel (console applications). Try to keep your code portable (unless it is not possible, and then be very explicit about this limitation in your README and your folder naming). We welcome additional ports in whatever language you prefer, but these additional ports are for educational purposes only, and do not count towards the donation total at the end of the project. ================================================ FILE: 00_Common/BASIC_Tests/InputTest.bas ================================================ 10 INPUT "Enter 3 numbers";A,B,C 20 PRINT "You entered: ";A;B;C 30 PRINT "--------------------------" 40 GOTO 10 ================================================ FILE: 00_Common/BASIC_Tests/OutputTest.bas ================================================ 10 A=1: B=-2: C=0.7: D=123456789: E=-0.0000000001 20 PRINT "|";A;"|";B;"|";C;"|";D;"|";E;"|" ================================================ FILE: 00_Common/BASIC_Tests/RndTest.bas ================================================ 10 PRINT "1: ";RND(1);RND(1);RND(0);RND(0);RND(1) 20 PRINT "2: ";RND(-2);RND(1);RND(1);RND(1) 30 PRINT "3: ";RND(-5);RND(1);RND(1);RND(1) 40 PRINT "4: ";RND(-2);RND(1);RND(1);RND(1) ================================================ FILE: 00_Common/README.md ================================================ # Common Library ## Purpose The primary purpose of this library is to implement common behaviours of the BASIC interpreter that impact gameplay, to free coders porting the games to concentrate on the explicit game logic. The behaviours implemented by this library are: * Complex interactions involved in text input. * Formatting of number in text output. * Behaviour of the BASIC `RND(float)` PRNG function. A secondary purpose is to provide common services that, with dependency injection, would allow a ported game to be driven programmatically to permit full-game acceptance tests to be written. The library is **NOT** intended to be: * a repository for common game logic that is implemented in the BASIC code of the games; or * a DSL allowing the BASIC code to be compiled in a different language environment with minimal changes. This implies that implementations of the above behaviours should use method names, etc, that are idiomatic of the specific language's normal routines for that behaviour. ## Text Input The behaviour of the BASIC interpreter when accepting text input from the user is a major element of the original gameplay experience that is seen to be valuable to maintain. This behaviour is complex and non-trivial to implement, and is better implemented once for other developers to use so they can concentrate on the explicit game logic. The text input/output behaviour can be investigated using a basic program such as: **`BASIC_Tests/InputTest.bas`** ```basic 10 INPUT "Enter 3 numbers";A,B,C 20 PRINT "You entered: ";A;B;C 30 PRINT "--------------------------" 40 GOTO 10 ``` The following transcript shows the use of this program, and some interesting behaviours of the BASIC interpreter INPUT routine. There are some other behaviours which can be seen in the unit tests for the C# library implementation. ```dos Enter 3 numbers? -1,2,3.141 <-- multiple numbers are separated by commas You entered: -1 2 3.141 -------------------------- Enter 3 numbers? 1 <-- ... or entered on separate lines ?? 2 ?? 3 You entered: 1 2 3 -------------------------- Enter 3 numbers? 1,2 <-- ... or both ?? 3 You entered: 1 2 3 -------------------------- Enter 3 numbers? 1,-2,3,4 <-- Extra input is ignore with a warning !EXTRA INPUT IGNORED You entered: 1 -2 3 -------------------------- Enter 3 numbers? 5 , 6.7, -8 ,10 <-- Whitespace around values is ignored !EXTRA INPUT IGNORED You entered: 5 6.7 -8 -------------------------- Enter 3 numbers? abcd,e,f <-- Non-numeric entries must be retried !NUMBER EXPECTED - RETRY INPUT LINE ? 1,2,abc <-- A single non-numeric invalidates the whole line !NUMBER EXPECTED - RETRY INPUT LINE ? 1de,2.3f,10k,abcde <-- ... except for trailing non-digit chars and extra input !EXTRA INPUT IGNORED You entered: 1 2.3 10 -------------------------- Enter 3 numbers? 1,"2,3",4 <-- Double-quotes enclose a single parsed value You entered: 1 2 4 -------------------------- Enter 3 numbers? 1,2,"3 <-- An unmatched double-quote crashes the interpreter vintbas.exe: Mismatched inputbuf in inputVars CallStack (from HasCallStack): error, called at src\Language\VintageBasic\Interpreter.hs:436:21 in main:Language.VintageBasic.Interpreter ``` I propose to ignore this last behaviour - the interpreter crash - and instead treat the end of the input line as the end of a quoted value. There are some additional behaviours to those shown above which can be seen in the unit tests for the C# implementation of the library. Note also that BASIC numeric variables store a single-precision floating point value, so numeric input functions should return a value of that type. ### Implementation Notes The usage of the `INPUT` command in the BASIC code of the games was analysed, with the variables used designated as `num`, for a numeric variable (eg. `M1`), or `str`, for a string variable (eg. `C$`). The result of this usage analysis across all game programs is: Variable number and type|Count ---|--- str|137 str str|2 num|187 num num|27 num num num|7 num num num num|1 num num num num num num num num num num|1 The usage count is interesting, but not important. What is important is the variable type and number for each usage. Implementers if the above behaviours do not need to cater for mixed variable types in their input routines (although the BASIC interpreter does support this). Input routines also need to cater only for 1 or 2 string values, and 1, 2, 3, 4, or 10 numeric values. ## Numbers in Text Output As seen in the transcript above, the BASIC interpreter has some particular rules for formatting numbers in text output. Negative numbers are printed with a leading negative sign (`-`) and a trailing space. Positive numbers are printed also with the trailing space, but with a leading space in place of the sign character. Additional formatting rules can be seen by running this program: **`BASIC_Tests/OutputTest.bas`** ```basic 10 A=1: B=-2: C=0.7: D=123456789: E=-0.0000000001 20 PRINT "|";A;"|";B;"|";C;"|";D;"|";E;"|" ``` The output is: ```dos | 1 |-2 | .7 | 1.2345679E+8 |-1.E-10 | ``` This clearly shows the leading and trailing spaces, but also shows that: * numbers without an integer component are printed without a leading zero to the left of the decimal place; * numbers above and below a certain magnitude are printed in scientific notation. <!-- markdownlint-disable MD024 --> ### Implementation Notes <!-- markdownlint-enable MD024 --> I think the important piece of this number formatting behaviour, in terms of its impact on replicating the text output of the original games, is the leading and trailing spaces. This should be the minimum behaviour supported for numeric output. Additional formatting behaviours may be investigated and supported by library implementers as they choose. ## The BASIC `RND(float)` function The [Vintage BASIC documentation](http://vintage-basic.net/downloads/Vintage_BASIC_Users_Guide.html) describes the behaviour of the `RND(float)` function: > Psuedorandom number generator. The behavior is different depending on the value passed. If the value is positive, the > result will be a new random value between 0 and 1 (including 0 but not 1). If the value is zero, the result will be a > repeat of the last random number generated. If the value is negative, it will be rounded down to the nearest integer > and used to reseed the random number generator. Pseudorandom sequences can be repeated by reseeding with the same > number. This behaviour can be shown by the program: **`BASIC_Tests/RndTest.bas`** ```basic 10 PRINT "1: ";RND(1);RND(1);RND(0);RND(0);RND(1) 20 PRINT "2: ";RND(-2);RND(1);RND(1);RND(1) 30 PRINT "3: ";RND(-5);RND(1);RND(1);RND(1) 40 PRINT "4: ";RND(-2);RND(1);RND(1);RND(1) ``` The output of this program is: ```dos 1: .97369444 .44256502 .44256502 .44256502 .28549057 <-- Repeated values due to RND(0) 2: .4986506 4.4510484E-2 .96231 .35997057 3: .8113741 .13687313 6.1034977E-2 .7874807 4: .4986506 4.4510484E-2 .96231 .35997057 <-- Same sequence as line 2 due to same seed ``` <!-- markdownlint-disable MD024 --> ### Implementation Notes <!-- markdownlint-enable MD024 --> While the BASIC `RND(x)` function always returns a number between 0 (inclusive) and 1 (exclusive) for positive non-zero values of `x`, game porters would find it convenient for the library to include functions returning a random float or integer in a range from an inclusive minimum to an exclusive maximum. As one of the games, "Football", makes use of `RND(0)` with different scaling applied than the previous use of `RND(1)`, a common library implementation should always generate a value between 0 and 1, and scale that for a function with a range, so that a call to the equivalent of the `RND(0)` function can return the previous value between 0 and 1. ================================================ FILE: 00_Common/dotnet/Directory.Build.props ================================================ <Project> <PropertyGroup> <Nullable>enable</Nullable> <LangVersion>10.0</LangVersion> </PropertyGroup> </Project> ================================================ FILE: 00_Common/dotnet/Games.Common/Games.Common.csproj ================================================ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net6.0</TargetFramework> <LangVersion>10</LangVersion> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> </Project> ================================================ FILE: 00_Common/dotnet/Games.Common/IO/ConsoleIO.cs ================================================ using System; namespace Games.Common.IO; /// <summary> /// An implementation of <see cref="IReadWrite" /> with input begin read for STDIN and output being written to /// STDOUT. /// </summary> public sealed class ConsoleIO : TextIO { public ConsoleIO() : base(Console.In, Console.Out) { } public override char ReadCharacter() => Console.ReadKey(intercept: true).KeyChar; } ================================================ FILE: 00_Common/dotnet/Games.Common/IO/IReadWrite.cs ================================================ using Games.Common.Numbers; namespace Games.Common.IO; /// <summary> /// Provides for input and output of strings and numbers. /// </summary> public interface IReadWrite { /// <summary> /// Reads a character from input. /// </summary> /// <returns>The character read.</returns> char ReadCharacter(); /// <summary> /// Reads a <see cref="float" /> value from input. /// </summary> /// <param name="prompt">The text to display to prompt for the value.</param> /// <returns>A <see cref="float" />, being the value entered.</returns> float ReadNumber(string prompt); /// <summary> /// Reads 2 <see cref="float" /> values from input. /// </summary> /// <param name="prompt">The text to display to prompt for the values.</param> /// <returns>A <see cref="ValueTuple{float, float}" />, being the values entered.</returns> (float, float) Read2Numbers(string prompt); /// <summary> /// Reads 3 <see cref="float" /> values from input. /// </summary> /// <param name="prompt">The text to display to prompt for the values.</param> /// <returns>A <see cref="ValueTuple{float, float, float}" />, being the values entered.</returns> (float, float, float) Read3Numbers(string prompt); /// <summary> /// Reads 4 <see cref="float" /> values from input. /// </summary> /// <param name="prompt">The text to display to prompt for the values.</param> /// <returns>A <see cref="ValueTuple{float, float, float, float}" />, being the values entered.</returns> (float, float, float, float) Read4Numbers(string prompt); /// <summary> /// Read numbers from input to fill an array. /// </summary> /// <param name="prompt">The text to display to prompt for the values.</param> /// <param name="values">A <see cref="float[]" /> to be filled with values from input.</param> void ReadNumbers(string prompt, float[] values); /// <summary> /// Reads a <see cref="string" /> value from input. /// </summary> /// <param name="prompt">The text to display to prompt for the value.</param> /// <returns>A <see cref="string" />, being the value entered.</returns> string ReadString(string prompt); /// <summary> /// Reads 2 <see cref="string" /> values from input. /// </summary> /// <param name="prompt">The text to display to prompt for the values.</param> /// <returns>A <see cref="ValueTuple{string, string}" />, being the values entered.</returns> (string, string) Read2Strings(string prompt); /// <summary> /// Writes a <see cref="string" /> to output. /// </summary> /// <param name="message">The <see cref="string" /> to be written.</param> void Write(string message); /// <summary> /// Writes a <see cref="string" /> to output, followed by a new-line. /// </summary> /// <param name="message">The <see cref="string" /> to be written.</param> void WriteLine(string message = ""); /// <summary> /// Writes a <see cref="Number" /> to output. /// </summary> /// <param name="value">The <see cref="Number" /> to be written.</param> void Write(Number value); /// <summary> /// Writes a <see cref="Number" /> to output. /// </summary> /// <param name="value">The <see cref="Number" /> to be written.</param> void WriteLine(Number value); /// <summary> /// Writes an <see cref="object" /> to output. /// </summary> /// <param name="value">The <see cref="object" /> to be written.</param> void Write(object value); /// <summary> /// Writes an <see cref="object" /> to output. /// </summary> /// <param name="value">The <see cref="object" /> to be written.</param> void WriteLine(object value); /// <summary> /// Writes a formatted string to output. /// </summary> /// <param name="format">The format <see cref="string" /> to be written.</param> /// <param name="value">The values to be inserted into the format.</param> void Write(string format, params object[] values); /// <summary> /// Writes a formatted string to output followed by a new-line. /// </summary> /// <param name="format">The format <see cref="string" /> to be written.</param> /// <param name="value">The values to be inserted into the format.</param> void WriteLine(string format, params object[] values); /// <summary> /// Writes the contents of a <see cref="Stream" /> to output. /// </summary> /// <param name="stream">The <see cref="Stream" /> to be written.</param> void Write(Stream stream, bool keepOpen = false); } ================================================ FILE: 00_Common/dotnet/Games.Common/IO/InsufficientInputException.cs ================================================ namespace Games.Common.IO; public class InsufficientInputException : Exception { public InsufficientInputException() : base("Insufficient input was supplied") { } } ================================================ FILE: 00_Common/dotnet/Games.Common/IO/Strings.cs ================================================ namespace Games.Common.IO; internal static class Strings { internal const string NumberExpected = "!Number expected - retry input line"; internal const string ExtraInput = "!Extra input ignored"; } ================================================ FILE: 00_Common/dotnet/Games.Common/IO/TextIO.cs ================================================ using Games.Common.Numbers; namespace Games.Common.IO; /// <inheritdoc /> /// <summary> /// Implements <see cref="IReadWrite" /> with input read from a <see cref="TextReader" /> and output written to a /// <see cref="TextWriter" />. /// </summary> /// <remarks> /// This implementation reproduces the Vintage BASIC input experience, prompting multiple times when partial input /// supplied, rejecting non-numeric input as needed, warning about extra input being ignored, etc. /// </remarks> public class TextIO : IReadWrite { private readonly TextReader _input; private readonly TextWriter _output; private readonly TokenReader _stringTokenReader; private readonly TokenReader _numberTokenReader; public TextIO(TextReader input, TextWriter output) { _input = input ?? throw new ArgumentNullException(nameof(input)); _output = output ?? throw new ArgumentNullException(nameof(output)); _stringTokenReader = TokenReader.ForStrings(this); _numberTokenReader = TokenReader.ForNumbers(this); } public virtual char ReadCharacter() { while(true) { var ch = _input.Read(); if (ch != -1) { return (char)ch; } } } public float ReadNumber(string prompt) => ReadNumbers(prompt, 1)[0]; public (float, float) Read2Numbers(string prompt) { var numbers = ReadNumbers(prompt, 2); return (numbers[0], numbers[1]); } public (float, float, float) Read3Numbers(string prompt) { var numbers = ReadNumbers(prompt, 3); return (numbers[0], numbers[1], numbers[2]); } public (float, float, float, float) Read4Numbers(string prompt) { var numbers = ReadNumbers(prompt, 4); return (numbers[0], numbers[1], numbers[2], numbers[3]); } public void ReadNumbers(string prompt, float[] values) { if (values.Length == 0) { throw new ArgumentException($"'{nameof(values)}' must have a non-zero length.", nameof(values)); } var numbers = _numberTokenReader.ReadTokens(prompt, (uint)values.Length).Select(t => t.Number).ToArray(); numbers.CopyTo(values.AsSpan()); } private IReadOnlyList<float> ReadNumbers(string prompt, uint quantity) => (quantity > 0) ? _numberTokenReader.ReadTokens(prompt, quantity).Select(t => t.Number).ToList() : throw new ArgumentOutOfRangeException( nameof(quantity), $"'{nameof(quantity)}' must be greater than zero."); public string ReadString(string prompt) { return ReadStrings(prompt, 1)[0]; } public (string, string) Read2Strings(string prompt) { var values = ReadStrings(prompt, 2); return (values[0], values[1]); } private IReadOnlyList<string> ReadStrings(string prompt, uint quantityRequired) => _stringTokenReader.ReadTokens(prompt, quantityRequired).Select(t => t.String).ToList(); internal string ReadLine(string prompt) { Write(prompt + "? "); return _input.ReadLine() ?? throw new InsufficientInputException(); } public void Write(string value) => _output.Write(value); public void WriteLine(string value = "") => _output.WriteLine(value); public void Write(Number value) => _output.Write(value.ToString()); public void WriteLine(Number value) => _output.WriteLine(value.ToString()); public void Write(object value) => _output.Write(value.ToString()); public void WriteLine(object value) => _output.WriteLine(value.ToString()); public void Write(string format, params object[] values) => _output.Write(format, values); public void WriteLine(string format, params object[] values) => _output.WriteLine(format, values); public void Write(Stream stream, bool keepOpen = false) { using var reader = new StreamReader(stream); while (!reader.EndOfStream) { _output.WriteLine(reader.ReadLine()); } if (!keepOpen) { stream?.Dispose(); } } private string GetString(float value) => value < 0 ? $"{value} " : $" {value} "; } ================================================ FILE: 00_Common/dotnet/Games.Common/IO/Token.cs ================================================ using System.Text; using System.Text.RegularExpressions; namespace Games.Common.IO; internal class Token { private static readonly Regex _numberPattern = new(@"^[+\-]?\d*(\.\d*)?([eE][+\-]?\d*)?"); internal Token(string value) { String = value; var match = _numberPattern.Match(String); IsNumber = float.TryParse(match.Value, out var number); Number = (IsNumber, number) switch { (false, _) => float.NaN, (true, float.PositiveInfinity) => float.MaxValue, (true, float.NegativeInfinity) => float.MinValue, (true, _) => number }; } public string String { get; } public bool IsNumber { get; } public float Number { get; } public override string ToString() => String; internal class Builder { private readonly StringBuilder _builder = new(); private bool _isQuoted; private int _trailingWhiteSpaceCount; public Builder Append(char character) { _builder.Append(character); _trailingWhiteSpaceCount = char.IsWhiteSpace(character) ? _trailingWhiteSpaceCount + 1 : 0; return this; } public Builder SetIsQuoted() { _isQuoted = true; return this; } public Token Build() { if (!_isQuoted) { _builder.Length -= _trailingWhiteSpaceCount; } return new Token(_builder.ToString()); } } } ================================================ FILE: 00_Common/dotnet/Games.Common/IO/TokenReader.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using static Games.Common.IO.Strings; namespace Games.Common.IO; /// <summary> /// Reads from input and assembles a given number of values, or tokens, possibly over a number of input lines. /// </summary> internal class TokenReader { private readonly TextIO _io; private readonly Predicate<Token> _isTokenValid; private TokenReader(TextIO io, Predicate<Token> isTokenValid) { _io = io; _isTokenValid = isTokenValid ?? (t => true); } /// <summary> /// Creates a <see cref="TokenReader" /> which reads string tokens. /// </summary> /// <param name="io">A <see cref="TextIO" /> instance.</param> /// <returns>The new <see cref="TokenReader" /> instance.</returns> public static TokenReader ForStrings(TextIO io) => new(io, t => true); /// <summary> /// Creates a <see cref="TokenReader" /> which reads tokens and validates that they can be parsed as numbers. /// </summary> /// <param name="io">A <see cref="TextIO" /> instance.</param> /// <returns>The new <see cref="TokenReader" /> instance.</returns> public static TokenReader ForNumbers(TextIO io) => new(io, t => t.IsNumber); /// <summary> /// Reads valid tokens from one or more input lines and builds a list with the required quantity. /// </summary> /// <param name="prompt">The string used to prompt the user for input.</param> /// <param name="quantityNeeded">The number of tokens required.</param> /// <returns>The sequence of tokens read.</returns> public IEnumerable<Token> ReadTokens(string prompt, uint quantityNeeded) { if (quantityNeeded == 0) { throw new ArgumentOutOfRangeException( nameof(quantityNeeded), $"'{nameof(quantityNeeded)}' must be greater than zero."); } var tokens = new List<Token>(); while (tokens.Count < quantityNeeded) { tokens.AddRange(ReadValidTokens(prompt, quantityNeeded - (uint)tokens.Count)); prompt = "?"; } return tokens; } /// <summary> /// Reads a line of tokens, up to <paramref name="maxCount" />, and rejects the line if any are invalid. /// </summary> /// <param name="prompt">The string used to prompt the user for input.</param> /// <param name="maxCount">The maximum number of tokens to read.</param> /// <returns>The sequence of tokens read.</returns> private IEnumerable<Token> ReadValidTokens(string prompt, uint maxCount) { while (true) { var tokensValid = true; var tokens = new List<Token>(); foreach (var token in ReadLineOfTokens(prompt, maxCount)) { if (!_isTokenValid(token)) { _io.WriteLine(NumberExpected); tokensValid = false; prompt = ""; break; } tokens.Add(token); } if (tokensValid) { return tokens; } } } /// <summary> /// Lazily reads up to <paramref name="maxCount" /> tokens from an input line. /// </summary> /// <param name="prompt">The string used to prompt the user for input.</param> /// <param name="maxCount">The maximum number of tokens to read.</param> /// <returns></returns> private IEnumerable<Token> ReadLineOfTokens(string prompt, uint maxCount) { var tokenCount = 0; foreach (var token in Tokenizer.ParseTokens(_io.ReadLine(prompt))) { if (++tokenCount > maxCount) { _io.WriteLine(ExtraInput); break; } yield return token; } } } ================================================ FILE: 00_Common/dotnet/Games.Common/IO/Tokenizer.cs ================================================ using System; using System.Collections.Generic; namespace Games.Common.IO; /// <summary> /// A simple state machine which parses tokens from a line of input. /// </summary> internal class Tokenizer { private const char Quote = '"'; private const char Separator = ','; private readonly Queue<char> _characters; private Tokenizer(string input) => _characters = new Queue<char>(input); public static IEnumerable<Token> ParseTokens(string input) { if (input is null) { throw new ArgumentNullException(nameof(input)); } return new Tokenizer(input).ParseTokens(); } private IEnumerable<Token> ParseTokens() { while (true) { var (token, isLastToken) = Consume(_characters); yield return token; if (isLastToken) { break; } } } public (Token, bool) Consume(Queue<char> characters) { var tokenBuilder = new Token.Builder(); var state = ITokenizerState.LookForStartOfToken; while (characters.TryDequeue(out var character)) { (state, tokenBuilder) = state.Consume(character, tokenBuilder); if (state is AtEndOfTokenState) { return (tokenBuilder.Build(), false); } } return (tokenBuilder.Build(), true); } private interface ITokenizerState { public static ITokenizerState LookForStartOfToken { get; } = new LookForStartOfTokenState(); (ITokenizerState, Token.Builder) Consume(char character, Token.Builder tokenBuilder); } private struct LookForStartOfTokenState : ITokenizerState { public (ITokenizerState, Token.Builder) Consume(char character, Token.Builder tokenBuilder) => character switch { Separator => (new AtEndOfTokenState(), tokenBuilder), Quote => (new InQuotedTokenState(), tokenBuilder.SetIsQuoted()), _ when char.IsWhiteSpace(character) => (this, tokenBuilder), _ => (new InTokenState(), tokenBuilder.Append(character)) }; } private struct InTokenState : ITokenizerState { public (ITokenizerState, Token.Builder) Consume(char character, Token.Builder tokenBuilder) => character == Separator ? (new AtEndOfTokenState(), tokenBuilder) : (this, tokenBuilder.Append(character)); } private struct InQuotedTokenState : ITokenizerState { public (ITokenizerState, Token.Builder) Consume(char character, Token.Builder tokenBuilder) => character == Quote ? (new ExpectSeparatorState(), tokenBuilder) : (this, tokenBuilder.Append(character)); } private struct ExpectSeparatorState : ITokenizerState { public (ITokenizerState, Token.Builder) Consume(char character, Token.Builder tokenBuilder) => character == Separator ? (new AtEndOfTokenState(), tokenBuilder) : (new IgnoreRestOfLineState(), tokenBuilder); } private struct IgnoreRestOfLineState : ITokenizerState { public (ITokenizerState, Token.Builder) Consume(char character, Token.Builder tokenBuilder) => (this, tokenBuilder); } private struct AtEndOfTokenState : ITokenizerState { public (ITokenizerState, Token.Builder) Consume(char character, Token.Builder tokenBuilder) => throw new InvalidOperationException(); } } ================================================ FILE: 00_Common/dotnet/Games.Common/Numbers/Number.cs ================================================ namespace Games.Common.Numbers; /// <summary> /// A single-precision floating-point number with string formatting equivalent to the BASIC interpreter. /// </summary> public struct Number { private readonly float _value; public Number (float value) { _value = value; } public static implicit operator float(Number value) => value._value; public static implicit operator Number(float value) => new Number(value); public override string ToString() => _value < 0 ? $"{_value} " : $" {_value} "; } ================================================ FILE: 00_Common/dotnet/Games.Common/Randomness/IRandom.cs ================================================ namespace Games.Common.Randomness; /// <summary> /// Provides access to a random number generator /// </summary> public interface IRandom { /// <summary> /// Gets a random <see cref="float" /> such that 0 <= n < 1. /// </summary> /// <returns>The random number.</returns> float NextFloat(); /// <summary> /// Gets the <see cref="float" /> returned by the previous call to <see cref="NextFloat" />. /// </summary> /// <returns>The previous random number.</returns> float PreviousFloat(); /// <summary> /// Reseeds the random number generator. /// </summary> /// <param name="seed">The seed.</param> void Reseed(int seed); } ================================================ FILE: 00_Common/dotnet/Games.Common/Randomness/IRandomExtensions.cs ================================================ using System; namespace Games.Common.Randomness; /// <summary> /// Provides extension methods to <see cref="IRandom" /> providing random numbers in a given range. /// </summary> /// <value></value> public static class IRandomExtensions { /// <summary> /// Gets a random <see cref="float" /> such that 0 <= n < exclusiveMaximum. /// </summary> /// <returns>The random number.</returns> public static float NextFloat(this IRandom random, float exclusiveMaximum) => Scale(random.NextFloat(), exclusiveMaximum); /// <summary> /// Gets a random <see cref="float" /> such that inclusiveMinimum <= n < exclusiveMaximum. /// </summary> /// <returns>The random number.</returns> public static float NextFloat(this IRandom random, float inclusiveMinimum, float exclusiveMaximum) => Scale(random.NextFloat(), inclusiveMinimum, exclusiveMaximum); /// <summary> /// Gets a random <see cref="int" /> such that 0 <= n < exclusiveMaximum. /// </summary> /// <returns>The random number.</returns> public static int Next(this IRandom random, int exclusiveMaximum) => ToInt(random.NextFloat(exclusiveMaximum)); /// <summary> /// Gets a random <see cref="int" /> such that inclusiveMinimum <= n < exclusiveMaximum. /// </summary> /// <returns>The random number.</returns> public static int Next(this IRandom random, int inclusiveMinimum, int exclusiveMaximum) => ToInt(random.NextFloat(inclusiveMinimum, exclusiveMaximum)); /// <summary> /// Gets the previous unscaled <see cref="float" /> (between 0 and 1) scaled to a new range: /// 0 <= x < <paramref name="exclusiveMaximum" />. /// </summary> /// <returns>The random number.</returns> public static float PreviousFloat(this IRandom random, float exclusiveMaximum) => Scale(random.PreviousFloat(), exclusiveMaximum); /// <summary> /// Gets the previous unscaled <see cref="float" /> (between 0 and 1) scaled to a new range: /// <paramref name="inclusiveMinimum" /> <= n < <paramref name="exclusiveMaximum" />. /// </summary> /// <returns>The random number.</returns> public static float PreviousFloat(this IRandom random, float inclusiveMinimum, float exclusiveMaximum) => Scale(random.PreviousFloat(), inclusiveMinimum, exclusiveMaximum); /// <summary> /// Gets the previous unscaled <see cref="float" /> (between 0 and 1) scaled to an <see cref="int" /> in a new /// range: 0 <= n < <paramref name="exclusiveMaximum" />. /// </summary> /// <returns>The random number.</returns> public static int Previous(this IRandom random, int exclusiveMaximum) => ToInt(random.PreviousFloat(exclusiveMaximum)); /// <summary> /// Gets the previous unscaled <see cref="float" /> (between 0 and 1) scaled to an <see cref="int" /> in a new /// range: <paramref name="inclusiveMinimum" /> <= n < <paramref name="exclusiveMaximum" />. /// <returns>The random number.</returns> public static int Previous(this IRandom random, int inclusiveMinimum, int exclusiveMaximum) => ToInt(random.PreviousFloat(inclusiveMinimum, exclusiveMaximum)); private static float Scale(float zeroToOne, float exclusiveMaximum) { if (exclusiveMaximum <= 0) { throw new ArgumentOutOfRangeException(nameof(exclusiveMaximum), "Must be greater than 0."); } return Scale(zeroToOne, 0, exclusiveMaximum); } private static float Scale(float zeroToOne, float inclusiveMinimum, float exclusiveMaximum) { if (exclusiveMaximum <= inclusiveMinimum) { throw new ArgumentOutOfRangeException(nameof(exclusiveMaximum), "Must be greater than inclusiveMinimum."); } var range = exclusiveMaximum - inclusiveMinimum; return zeroToOne * range + inclusiveMinimum; } private static int ToInt(float value) => (int)Math.Floor(value); } ================================================ FILE: 00_Common/dotnet/Games.Common/Randomness/RandomNumberGenerator.cs ================================================ using System; namespace Games.Common.Randomness; /// <inheritdoc /> public class RandomNumberGenerator : IRandom { private Random _random; private float _previous; public RandomNumberGenerator() { // The BASIC RNG is seeded based on time with a 1 second resolution _random = new Random((int)(DateTime.UtcNow.Ticks / TimeSpan.TicksPerSecond)); } public float NextFloat() => _previous = (float)_random.NextDouble(); public float PreviousFloat() => _previous; public void Reseed(int seed) => _random = new Random(seed); } ================================================ FILE: 00_Common/dotnet/Games.Common/_InternalsVisibleTo.cs ================================================ using System.Runtime.CompilerServices; [assembly:InternalsVisibleTo("Games.Common.Test")] ================================================ FILE: 00_Common/dotnet/Games.Common.Test/Games.Common.Test.csproj ================================================ <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net6.0</TargetFramework> <IsPackable>false</IsPackable> </PropertyGroup> <ItemGroup> <PackageReference Include="FluentAssertions" Version="6.4.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.11.0" /> <PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> <PackageReference Include="coverlet.collector" Version="3.1.0"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> </ItemGroup> <ItemGroup> <ProjectReference Include="..\Games.Common\Games.Common.csproj" /> </ItemGroup> </Project> ================================================ FILE: 00_Common/dotnet/Games.Common.Test/IO/TextIOTests/NumberFormatTests.cs ================================================ using System; using System.IO; using FluentAssertions; using Xunit; namespace Games.Common.IO.TextIOTests; public class NumberFormatTests { [Theory] [MemberData(nameof(WriteFloatTestCases))] public void Write_Float_FormatsNumberSameAsBasic(float value, string basicString) { var outputWriter = new StringWriter(); var io = new TextIO(new StringReader(""), outputWriter); io.Write(value); outputWriter.ToString().Should().BeEquivalentTo(basicString); } [Theory] [MemberData(nameof(WriteFloatTestCases))] public void WriteLine_Float_FormatsNumberSameAsBasic(float value, string basicString) { var outputWriter = new StringWriter(); var io = new TextIO(new StringReader(""), outputWriter); io.WriteLine(value); outputWriter.ToString().Should().BeEquivalentTo(basicString + Environment.NewLine); } public static TheoryData<float, string> WriteFloatTestCases() => new() { { 1000F, " 1000 " }, { 3.1415927F, " 3.1415927 " }, { 1F, " 1 " }, { 0F, " 0 " }, { -1F, "-1 " }, { -3.1415927F, "-3.1415927 " }, { -1000F, "-1000 " }, }; [Theory] [MemberData(nameof(WriteIntTestCases))] public void Write_Int_FormatsNumberSameAsBasic(int value, string basicString) { var outputWriter = new StringWriter(); var io = new TextIO(new StringReader(""), outputWriter); io.Write(value); outputWriter.ToString().Should().BeEquivalentTo(basicString); } [Theory] [MemberData(nameof(WriteIntTestCases))] public void WriteLine_Int_FormatsNumberSameAsBasic(int value, string basicString) { var outputWriter = new StringWriter(); var io = new TextIO(new StringReader(""), outputWriter); io.WriteLine(value); outputWriter.ToString().Should().BeEquivalentTo(basicString + Environment.NewLine); } public static TheoryData<int, string> WriteIntTestCases() => new() { { 1000, " 1000 " }, { 1, " 1 " }, { 0, " 0 " }, { -1, "-1 " }, { -1000, "-1000 " }, }; } ================================================ FILE: 00_Common/dotnet/Games.Common.Test/IO/TextIOTests/ReadMethodTests.cs ================================================ using System; using System.Collections.Generic; using System.IO; using FluentAssertions; using FluentAssertions.Execution; using Xunit; using TwoStrings = System.ValueTuple<string, string>; using TwoNumbers = System.ValueTuple<float, float>; using ThreeNumbers = System.ValueTuple<float, float, float>; using FourNumbers = System.ValueTuple<float, float, float, float>; using static System.Environment; using static Games.Common.IO.Strings; namespace Games.Common.IO.TextIOTests; public class ReadMethodTests { [Theory] [MemberData(nameof(ReadStringTestCases))] [MemberData(nameof(Read2StringsTestCases))] [MemberData(nameof(ReadNumberTestCases))] [MemberData(nameof(Read2NumbersTestCases))] [MemberData(nameof(Read3NumbersTestCases))] [MemberData(nameof(Read4NumbersTestCases))] [MemberData(nameof(ReadNumbersTestCases))] public void ReadingValuesHasExpectedPromptsAndResults<T>( Func<IReadWrite, T> read, string input, string expectedOutput, T expectedResult) { var inputReader = new StringReader(input + Environment.NewLine); var outputWriter = new StringWriter(); var io = new TextIO(inputReader, outputWriter); var result = read.Invoke(io); var output = outputWriter.ToString(); using var _ = new AssertionScope(); output.Should().Be(expectedOutput); result.Should().BeEquivalentTo(expectedResult); } [Fact] public void ReadNumbers_ArrayEmpty_ThrowsArgumentException() { var io = new TextIO(new StringReader(""), new StringWriter()); Action readNumbers = () => io.ReadNumbers("foo", Array.Empty<float>()); readNumbers.Should().Throw<ArgumentException>() .WithMessage("'values' must have a non-zero length.*") .WithParameterName("values"); } public static TheoryData<Func<IReadWrite, string>, string, string, string> ReadStringTestCases() { static Func<IReadWrite, string> ReadString(string prompt) => io => io.ReadString(prompt); return new() { { ReadString("Name"), "", "Name? ", "" }, { ReadString("prompt"), " foo ,bar", $"prompt? {ExtraInput}{NewLine}", "foo" } }; } public static TheoryData<Func<IReadWrite, TwoStrings>, string, string, TwoStrings> Read2StringsTestCases() { static Func<IReadWrite, TwoStrings> Read2Strings(string prompt) => io => io.Read2Strings(prompt); return new() { { Read2Strings("2 strings"), ",", "2 strings? ", ("", "") }, { Read2Strings("Input please"), $"{NewLine}x,y", $"Input please? ?? {ExtraInput}{NewLine}", ("", "x") } }; } public static TheoryData<Func<IReadWrite, float>, string, string, float> ReadNumberTestCases() { static Func<IReadWrite, float> ReadNumber(string prompt) => io => io.ReadNumber(prompt); return new() { { ReadNumber("Age"), $"{NewLine}42,", $"Age? {NumberExpected}{NewLine}? {ExtraInput}{NewLine}", 42 }, { ReadNumber("Guess"), "3,4,5", $"Guess? {ExtraInput}{NewLine}", 3 } }; } public static TheoryData<Func<IReadWrite, TwoNumbers>, string, string, TwoNumbers> Read2NumbersTestCases() { static Func<IReadWrite, TwoNumbers> Read2Numbers(string prompt) => io => io.Read2Numbers(prompt); return new() { { Read2Numbers("Point"), "3,4,5", $"Point? {ExtraInput}{NewLine}", (3, 4) }, { Read2Numbers("Foo"), $"x,4,5{NewLine}4,5,x", $"Foo? {NumberExpected}{NewLine}? {ExtraInput}{NewLine}", (4, 5) } }; } public static TheoryData<Func<IReadWrite, ThreeNumbers>, string, string, ThreeNumbers> Read3NumbersTestCases() { static Func<IReadWrite, ThreeNumbers> Read3Numbers(string prompt) => io => io.Read3Numbers(prompt); return new() { { Read3Numbers("Point"), "3.2, 4.3, 5.4, 6.5", $"Point? {ExtraInput}{NewLine}", (3.2F, 4.3F, 5.4F) }, { Read3Numbers("Bar"), $"x,4,5{NewLine}4,5,x{NewLine}6,7,8,y", $"Bar? {NumberExpected}{NewLine}? {NumberExpected}{NewLine}? {ExtraInput}{NewLine}", (6, 7, 8) } }; } public static TheoryData<Func<IReadWrite, FourNumbers>, string, string, FourNumbers> Read4NumbersTestCases() { static Func<IReadWrite, FourNumbers> Read4Numbers(string prompt) => io => io.Read4Numbers(prompt); return new() { { Read4Numbers("Point"), "3,4,5,6,7", $"Point? {ExtraInput}{NewLine}", (3, 4, 5, 6) }, { Read4Numbers("Baz"), $"x,4,5,6{NewLine} 4, 5 , 6,7 ,x", $"Baz? {NumberExpected}{NewLine}? {ExtraInput}{NewLine}", (4, 5, 6, 7) } }; } public static TheoryData<Func<IReadWrite, IReadOnlyList<float>>, string, string, float[]> ReadNumbersTestCases() { static Func<IReadWrite, IReadOnlyList<float>> ReadNumbers(string prompt) => io => { var numbers = new float[6]; io.ReadNumbers(prompt, numbers); return numbers; }; return new() { { ReadNumbers("Primes"), "2, 3, 5, 7, 11, 13", $"Primes? ", new float[] { 2, 3, 5, 7, 11, 13 } }, { ReadNumbers("Qux"), $"42{NewLine}3.141, 2.718{NewLine}3.0e8, 6.02e23{NewLine}9.11E-28", $"Qux? ?? ?? ?? ", new[] { 42, 3.141F, 2.718F, 3.0e8F, 6.02e23F, 9.11E-28F } } }; } } ================================================ FILE: 00_Common/dotnet/Games.Common.Test/IO/TokenReaderTests.cs ================================================ using System; using System.IO; using System.Linq; using FluentAssertions; using FluentAssertions.Execution; using Xunit; using static System.Environment; using static Games.Common.IO.Strings; namespace Games.Common.IO; public class TokenReaderTests { private readonly StringWriter _outputWriter; public TokenReaderTests() { _outputWriter = new StringWriter(); } [Fact] public void ReadTokens_QuantityNeededZero_ThrowsArgumentException() { var sut = TokenReader.ForStrings(new TextIO(new StringReader(""), _outputWriter)); Action readTokens = () => sut.ReadTokens("", 0); readTokens.Should().Throw<ArgumentOutOfRangeException>() .WithMessage("'quantityNeeded' must be greater than zero.*") .WithParameterName("quantityNeeded"); } [Theory] [MemberData(nameof(ReadTokensTestCases))] public void ReadTokens_ReadingValuesHasExpectedPromptsAndResults( string prompt, uint tokenCount, string input, string expectedOutput, string[] expectedResult) { var sut = TokenReader.ForStrings(new TextIO(new StringReader(input + NewLine), _outputWriter)); var result = sut.ReadTokens(prompt, tokenCount); var output = _outputWriter.ToString(); using var _ = new AssertionScope(); output.Should().Be(expectedOutput); result.Select(t => t.String).Should().BeEquivalentTo(expectedResult); } [Theory] [MemberData(nameof(ReadNumericTokensTestCases))] public void ReadTokens_Numeric_ReadingValuesHasExpectedPromptsAndResults( string prompt, uint tokenCount, string input, string expectedOutput, float[] expectedResult) { var sut = TokenReader.ForNumbers(new TextIO(new StringReader(input + NewLine), _outputWriter)); var result = sut.ReadTokens(prompt, tokenCount); var output = _outputWriter.ToString(); using var _ = new AssertionScope(); output.Should().Be(expectedOutput); result.Select(t => t.Number).Should().BeEquivalentTo(expectedResult); } public static TheoryData<string, uint, string, string, string[]> ReadTokensTestCases() { return new() { { "Name", 1, "Bill", "Name? ", new[] { "Bill" } }, { "Names", 2, " Bill , Bloggs ", "Names? ", new[] { "Bill", "Bloggs" } }, { "Names", 2, $" Bill{NewLine}Bloggs ", "Names? ?? ", new[] { "Bill", "Bloggs" } }, { "Foo", 6, $"1,2{NewLine}\" a,b \"{NewLine},\"\"c,d{NewLine}d\"x,e,f", $"Foo? ?? ?? ?? {ExtraInput}{NewLine}", new[] { "1", "2", " a,b ", "", "", "d\"x" } } }; } public static TheoryData<string, uint, string, string, float[]> ReadNumericTokensTestCases() { return new() { { "Age", 1, "23", "Age? ", new[] { 23F } }, { "Constants", 2, " 3.141 , 2.71 ", "Constants? ", new[] { 3.141F, 2.71F } }, { "Answer", 1, $"Forty-two{NewLine}42 ", $"Answer? {NumberExpected}{NewLine}? ", new[] { 42F } }, { "Foo", 6, $"1,2{NewLine}\" a,b \"{NewLine}3, 4 {NewLine}5.6,7,a, b", $"Foo? ?? {NumberExpected}{NewLine}? ?? {ExtraInput}{NewLine}", new[] { 1, 2, 3, 4, 5.6F, 7 } } }; } } ================================================ FILE: 00_Common/dotnet/Games.Common.Test/IO/TokenTests.cs ================================================ using FluentAssertions; using Xunit; namespace Games.Common.IO; public class TokenTests { [Theory] [MemberData(nameof(TokenTestCases))] public void Ctor_PopulatesProperties(string value, bool isNumber, float number) { var expected = new { String = value, IsNumber = isNumber, Number = number }; var token = new Token(value); token.Should().BeEquivalentTo(expected); } public static TheoryData<string, bool, float> TokenTestCases() => new() { { "", false, float.NaN }, { "abcde", false, float.NaN }, { "123 ", true, 123 }, { "+42 ", true, 42 }, { "-42 ", true, -42 }, { "+3.14159 ", true, 3.14159F }, { "-3.14159 ", true, -3.14159F }, { " 123", false, float.NaN }, { "1.2e4", true, 12000 }, { "2.3e-5", true, 0.000023F }, { "1e100", true, float.MaxValue }, { "-1E100", true, float.MinValue }, { "1E-100", true, 0 }, { "-1e-100", true, 0 }, { "100abc", true, 100 }, { "1,2,3", true, 1 }, { "42,a,b", true, 42 }, { "1.2.3", true, 1.2F }, { "12e.5", false, float.NaN }, { "12e0.5", true, 12 } }; } ================================================ FILE: 00_Common/dotnet/Games.Common.Test/IO/TokenizerTests.cs ================================================ using System.Linq; using FluentAssertions; using Xunit; namespace Games.Common.IO; public class TokenizerTests { [Theory] [MemberData(nameof(TokenizerTestCases))] public void ParseTokens_SplitsStringIntoExpectedTokens(string input, string[] expected) { var result = Tokenizer.ParseTokens(input); result.Select(t => t.ToString()).Should().BeEquivalentTo(expected); } public static TheoryData<string, string[]> TokenizerTestCases() => new() { { "", new[] { "" } }, { "aBc", new[] { "aBc" } }, { " Foo ", new[] { "Foo" } }, { " \" Foo \" ", new[] { " Foo " } }, { " \" Foo ", new[] { " Foo " } }, { "\"\"abc", new[] { "" } }, { "a\"\"bc", new[] { "a\"\"bc" } }, { "\"\"", new[] { "" } }, { ",", new[] { "", "" } }, { " foo ,bar", new[] { "foo", "bar" } }, { "\"a\"bc,de", new[] { "a" } }, { "a\"b,\" c,d\", f ,,g", new[] { "a\"b", " c,d", "f", "", "g" } } }; } ================================================ FILE: 00_Common/dotnet/Games.Common.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30114.105 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Games.Common", "Games.Common\Games.Common.csproj", "{005F2A3E-4E45-418B-8D19-E735B3BD4535}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Games.Common.Test", "Games.Common.Test\Games.Common.Test.csproj", "{8369DA66-0414-4A14-B5BE-73B0159498A2}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {005F2A3E-4E45-418B-8D19-E735B3BD4535}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {005F2A3E-4E45-418B-8D19-E735B3BD4535}.Debug|Any CPU.Build.0 = Debug|Any CPU {005F2A3E-4E45-418B-8D19-E735B3BD4535}.Release|Any CPU.ActiveCfg = Release|Any CPU {005F2A3E-4E45-418B-8D19-E735B3BD4535}.Release|Any CPU.Build.0 = Release|Any CPU {8369DA66-0414-4A14-B5BE-73B0159498A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8369DA66-0414-4A14-B5BE-73B0159498A2}.Debug|Any CPU.Build.0 = Debug|Any CPU {8369DA66-0414-4A14-B5BE-73B0159498A2}.Release|Any CPU.ActiveCfg = Release|Any CPU {8369DA66-0414-4A14-B5BE-73B0159498A2}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal ================================================ FILE: 00_Common/dotnet/README.md ================================================ # Games.Common This is the common library for C# and VB.Net ports of the games. ## Overview ### Game Input/Output * `TextIO` is the main class which manages text input and output for a game. It take a `TextReader` and a `TextWriter` in its constructor so it can be wired up in unit tests to test gameplay scenarios. * `ConsoleIO` derives from `TextIO` and binds it to `System.Console.In` and `System.Console.Out`. * `IReadWrite` is an interface implemented by `TextIO` which may be useful in some test scenarios. ```csharp public interface IReadWrite { // Reads a float value from input. float ReadNumber(string prompt); // Reads 2 float values from input. (float, float) Read2Numbers(string prompt); // Reads 3 float values from input. (float, float, float) Read3Numbers(string prompt); // Reads 4 float values from input. (float, float, float, float) Read4Numbers(string prompt); // Read numbers from input to fill an array. void ReadNumbers(string prompt, float[] values); // Reads a string value from input. string ReadString(string prompt); // Reads 2 string values from input. (string, string) Read2Strings(string prompt); // Writes a string to output. void Write(string message); // Writes a string to output, followed by a new-line. void WriteLine(string message = ""); // Writes a float to output, formatted per the BASIC interpreter, with leading and trailing spaces. void Write(float value); // Writes a float to output, formatted per the BASIC interpreter, with leading and trailing spaces, // followed by a new-line. void WriteLine(float value); // Writes the contents of a Stream to output. void Write(Stream stream);} ``` ### Random Number Generation * `IRandom` is an interface that provides basic methods that parallel the 3 uses of BASIC's `RND(float)` function. * `RandomNumberGenerator` is an implementation of `IRandom` built around `System.Random`. * `IRandomExtensions` provides convenience extension methods for obtaining random numbers as `int` and also within a given range. ```csharp public interface IRandom { // Like RND(1), gets a random float such that 0 <= n < 1. float NextFloat(); // Like RND(0), Gets the float returned by the previous call to NextFloat. float PreviousFloat(); // Like RND(-x), Reseeds the random number generator. void Reseed(int seed); } ``` Extension methods on `IRandom`: ```csharp // Gets a random float such that 0 <= n < exclusiveMaximum. float NextFloat(this IRandom random, float exclusiveMaximum); // Gets a random float such that inclusiveMinimum <= n < exclusiveMaximum. float NextFloat(this IRandom random, float inclusiveMinimum, float exclusiveMaximum); // Gets a random int such that 0 <= n < exclusiveMaximum. int Next(this IRandom random, int exclusiveMaximum); // Gets a random int such that inclusiveMinimum <= n < exclusiveMaximum. int Next(this IRandom random, int inclusiveMinimum, int exclusiveMaximum); // Gets the previous unscaled float (between 0 and 1) scaled to a new range: // 0 <= x < exclusiveMaximum. float PreviousFloat(this IRandom random, float exclusiveMaximum); // Gets the previous unscaled float (between 0 and 1) scaled to a new range: // inclusiveMinimum <= n < exclusiveMaximum. float PreviousFloat(this IRandom random, float inclusiveMinimum, float exclusiveMaximum); // Gets the previous unscaled float (between 0 and 1) scaled to an int in a new range: // 0 <= n < exclusiveMaximum. int Previous(this IRandom random, int exclusiveMaximum); // Gets the previous unscaled float (between 0 and 1) scaled to an int in a new range: // inclusiveMinimum <= n < exclusiveMaximum. int Previous(this IRandom random, int inclusiveMinimum, int exclusiveMaximum); ``` ## C\# Usage ### Add Project Reference Add the `Games.Common` project as a reference to the game project. For example, here's the reference from the C\# port of `86_Target`: ```xml <ItemGroup> <ProjectReference Include="..\..\00_Common\dotnet\Games.Common\Games.Common.csproj" /> </ItemGroup> ``` ### C# Game Input/Output usage A game can be encapsulated in a class which takes a `TextIO` instance in it's constructor: ```csharp public class Game { private readonly TextIO _io; public Game(TextIO io) => _io = io; public void Play() { var name = _io.ReadString("What is your name"); var (cats, dogs) = _io.Read2Number($"Hello, {name}, how many pets do you have (cats, dogs)"); _io.WriteLine($"So, {cats + dogs} pets in total, huh?"); } } ``` Then the entry point of the game program would look something like: ```csharp var game = new Game(new ConsoleIO()); game.Play(); ``` ### C# Random Number Generator usage ```csharp var io = new ConsoleIO(); var rng = new RandomNumberGenerator(); io.WriteLine(rng.NextFloat()); // 0.1234, for example io.WriteLine(rng.NextFloat()); // 0.6, for example io.WriteLine(rng.PreviousFloat()); // 0.6, repeats previous io.WriteLine(rng.PreviousFloat(0, 10)); // 6, repeats previous value, but scaled to new range ``` ### C# Unit Test usage `TextIO` can be initialised with a `StringReader` and `StringWriter` to enable testing. For example, given the `Game` class above: ```csharp var reader = new StringReader("Joe Bloggs\r\n4\n\r5"); var writer = new StringWriter(); var game = new Game(new TextIO(reader, writer)) game.Play(); writer.ToString().Should().BeEquivalentTo( "What is your name? Hello, Joe Bloggs, how many pets do you have (cats, dogs)? ?? So, 9 pets in total, huh?"); ``` Note the lack of line breaks in the expected output, because during game play the line breaks come from the text input. Of course, `IReadWrite` can also be mocked for simple test scenarios. ## VB.Net Usage *To be provided* ================================================ FILE: 00_Common/javascript/WebTerminal/HtmlTerminal.css ================================================ :root { --terminal-font: 1rem "Lucida Console", "Courier New", monospace; --background-color: transparent; --text-color: var(--text); --prompt-char: '$ '; --cursor-char: '_'; } /* Basic terminal style. * If you wan t to overwrite them use custom properties (variables). */ .terminal { display: block; font: var(--terminal-font); background-color: var(--background-color); color: var(--text-color); overflow-y: scroll; width: 100%; max-width: 640px; margin: 0 auto; } /* The terminal consits of multiple "line" elements * Because sometimes we want to add a simulates "prompt" at the end of a line * we need to make it an "inline" element and handle line-breaks * by adding <br> elements */ .terminal pre.line { display: inline-block; font: var(--terminal-font); margin: 0; padding: 0; } /* The "terminal" has one "prompt" input-element. */ @keyframes prompt-blink { 100% { opacity: 0; } } .terminal #prompt { display: inline-block; } .terminal #prompt:before { display: inline-block; content: var(--prompt-char); font: var(--terminal-font); } .terminal #prompt:after { display: inline-block; content: var(--cursor-char); background: var(--text); animation: prompt-blink 1s steps(2) infinite; width: 0.75rem; opacity: 1; } .terminal input#prompt { text-transform: uppercase; background: none; border: none; outline: none; caret-color: var(--text); color: var(--text); font: var(--terminal-font); } /* Terminal scrollbar */ ::-webkit-scrollbar { width: 3px; height: 3px; } ::-webkit-scrollbar-track { background: var(--background-color); } ::-webkit-scrollbar-thumb { background: var(--text-color); } ================================================ FILE: 00_Common/javascript/WebTerminal/HtmlTerminal.js ================================================ /** * @class HtmlTerminal * * This class is a very basic implementation of a "terminal" in the browser. * It provides simple functions like "write" and an "input" Callback. * * @license AGPL-2.0 * @author Alexaner Wunschik <https://github.com/mojoaxel> */ class HtmlTerminal { /** * Input callback. * If the prompt is activated by calling the input function * a callback is defined. If this member is not set this means * the prompt is not active. * * @private * @type {function} */ #inputCallback = undefined; /** * A html element to show a "prompt". * * @private * @type {HTMLElement} */ #$prompt; /** * Constructor * Creates a basic terminal simulation on the provided HTMLElement. * * @param {HTMLElement} $output - a dom element */ constructor($output) { // Store the output DOM element in a local variable. this.$output = $output; // Clear terminal. this.clear(); // Add the call "terminal" to the $output element. this.$output.classList.add('terminal'); // Create a prompt element. // This element gets added if input is needed. this.#$prompt = document.createElement("input"); this.#$prompt.setAttribute("id", "prompt"); this.#$prompt.setAttribute("type", "text"); this.#$prompt.setAttribute("length", "50"); this.#$prompt.addEventListener("keydown", this.#handleKey.bind(this)); // Force focus on the promt on each click. // This is needed for mobile support. document.body.addEventListener('click', () => { this.#$prompt.focus(); }); } /** * Creates a new HTMLElement with the given text content. * This element than gets added to the $output as a new "line". * * @private * @memberof MinimalTerminal * @param {String} text - text that should be displayed in the new "line". * @returns {HTMLElement} return a new DOM Element <pre class="line"></pre> */ #newLine(text) { const $lineNode = document.createElement("pre"); $lineNode.classList.add("line"); $lineNode.innerText = text; return $lineNode; } /** * TODO * * @private * @param {*} e */ #handleKey(e) { // if no input-callback is defined just return if (!this.#inputCallback) { return; } if (e.keyCode == 13) { const text = this.#$prompt.value; this.#$prompt.value = ''; this.#$prompt.remove(); this.#inputCallback(text + '\n'); } } /** * Clear the terminal. * Remove all lines. * * @public */ clear() { this.$output.innerText = ""; } /** * Create a new div and add html content. * * @public * @param {*} htmlContent */ inserHtml(htmlContent) { const $htmlNode = document.createElement("div"); $htmlNode.innerHTML = htmlContent; this.$output.appendChild($htmlNode); document.body.scrollTo(0, document.body.scrollHeight); } /** * Write a text to the terminal. * By default there is no linebreak at the end of a new line * except the line ensd with a "\n". * If the given text has multible linebreaks, multibe lines are inserted. * * @public * @param {string} text */ write(text) { if (!text || text.length <= 0) { // empty line this.$output.appendChild(document.createElement("br")); } else if (text.endsWith("\n")) { // single line with linebrank const $lineNode = this.#newLine(text); this.$output.appendChild(this.#newLine(text)); this.$output.appendChild(document.createElement("br")); } else if (text.includes("\n")) { // multible lines const lines = text.split("\n"); lines.forEach((line) => { this.write(line); }); } else { // single line this.$output.appendChild(this.#newLine(text)); } // scroll to the buttom of the page document.body.scrollTo(0, document.body.scrollHeight); } /** * Like "write" but with a newline at the end. * * @public * @param {*} text */ writeln(text) { this.write(text + "\n"); } /** * Query from user input. * This is done by adding a input-element at the end of the terminal, * that showes a prompt and a blinking cursor. * If a key is pressed the input is added to the prompt element. * The input ends with a linebreak. * * @public * @param {*} callback */ input(callback) { // show prompt with a blinking prompt this.#inputCallback = callback; this.$output.appendChild(this.#$prompt); this.#$prompt.focus(); } } ================================================ FILE: 00_Common/javascript/WebTerminal/terminal.html ================================================ <html> <head> <title>Minimal node.js terminal

BASIC Computer Games

================================================ FILE: 00_Common/javascript/WebTerminal/terminal_tests.mjs ================================================ #!/usr/bin/env node import { print, println, tab, input } from '../common.mjs'; async function main() { println(tab(30), "Minimal node.js terminal emulator"); println(); println(tab(0), "tab 0"); println(tab(5), "tab 5"); println(tab(10), "tab 10"); println(tab(15), "tab 15"); println(tab(20), "tab 20"); println(tab(25), "tab 25"); println(); println("1234567890", " _ ", "ABCDEFGHIJKLMNOPRSTUVWXYZ"); println(); print("\nHallo"); print(" "); print("Welt!\n"); println(""); println("Line 1\nLine 2\nLine 3\nLine 4"); println("----------------------------------------------"); const value = await input("input"); println(`input value was "${value}"`); println("End of script"); // 320 END process.exit(0); } main(); ================================================ FILE: 00_Common/javascript/common.mjs ================================================ /** * Print multible strings to the terminal. * Strings get concatinated (add together) without any space betweent them. * There will be no newline at the end! * If you want a linebrak at the end use `println`. * * This function is normally used if you want to put something on the screen * and later add some content to the same line. * For normal output (similar to `console.log`) use `println`! * * @param {...string} messages - the strings to print to the terminal. */ export function print(...messages) { process.stdout.write(messages.join("")); } /** * Add multible strings as a new line to the terminal. * Strings get concatinated (add together) without any space betweent them. * There will be a newline at the end! * If you want the terminal to stay active on the current line use `print`. * * @param {...any} messages - the strings to print to the terminal. */ export function println(...messages) { process.stdout.write(messages.join("") + "\n"); } /** * Create an empty string with a given length * * @param {number} length - the length of the string in space-characters. * @returns {string} returns a string containing only ampty spaces with a length of `count`. */ export function tab(length) { return " ".repeat(length); } /** * Read input from the keyboard and return it as a string. * TODO: to would be very helpfull to only allow a certain class of input (numbers, letters) * TODO: also we could convert all inputs to uppercase (where it makes sence). * * @param {string=''} message - a message or question to print befor the input. * @returns {Promise} - returns the entered text as a string * @async */ export async function input(message = '') { /* First we need to print the mesage * We append a space by default to seperate the message from the imput. * TODO: If the message already contains a space at the end this is not needed! */ process.stdout.write(message + ' '); return new Promise(resolve => { process.stdin.on('data', (input) => { /* onData returns a Buffer. * First we need to convert it into a string. */ const data = input.toString(); /* add input to terminal * The data should end with a newline! */ process.stdout.write(data); /* The result fo onData is a string ending with an `\n`. * We just need the actual content so let's remove the newline at the end: */ const content = data.endsWith('\n') ? data.slice(0, -1) : data; resolve(content); }); }); } ================================================ FILE: 00_Utilities/DotnetUtils/.editorconfig ================================================ root = true [*.{cs,vb}] indent_size = 4 indent_style = space end_of_line = crlf insert_final_newline = true dotnet_separate_import_directive_groups = false dotnet_sort_system_directives_first = false dotnet_style_qualification_for_event = false:suggestion dotnet_style_qualification_for_field = false:suggestion dotnet_style_qualification_for_method = false:suggestion dotnet_style_qualification_for_property = false:suggestion dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion dotnet_style_predefined_type_for_member_access = true:suggestion dotnet_style_parentheses_in_arithmetic_binary_operators = never_if_unnecessary:silent dotnet_style_parentheses_in_other_binary_operators = never_if_unnecessary:silent dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent dotnet_style_parentheses_in_relational_binary_operators = never_if_unnecessary:silent dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent dotnet_style_coalesce_expression = true:suggestion dotnet_style_collection_initializer = true:suggestion dotnet_style_explicit_tuple_names = true:suggestion dotnet_style_null_propagation = true:suggestion dotnet_style_object_initializer = true:suggestion dotnet_style_operator_placement_when_wrapping = end_of_line dotnet_style_prefer_auto_properties = true:suggestion dotnet_style_prefer_compound_assignment = true:suggestion dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion dotnet_style_prefer_conditional_expression_over_return = true:suggestion dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion dotnet_style_prefer_inferred_tuple_names = true:suggestion dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion dotnet_style_prefer_simplified_boolean_expressions = true:suggestion dotnet_style_prefer_simplified_interpolation = true:suggestion # Naming rules dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion dotnet_naming_rule.types_should_be_pascal_case.symbols = types dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case dotnet_naming_rule.non_private_members_should_be_pascal_case.severity = suggestion dotnet_naming_rule.non_private_members_should_be_pascal_case.symbols = non_private_members dotnet_naming_rule.non_private_members_should_be_pascal_case.style = pascal_case dotnet_naming_rule.private_members_should_be_pascal_case.severity = suggestion dotnet_naming_rule.private_members_should_be_pascal_case.symbols = private_members dotnet_naming_rule.private_members_should_be_pascal_case.style = camel_case # Symbols for use with naming rules dotnet_naming_symbols.interface.applicable_kinds = interface dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected dotnet_naming_symbols.interface.required_modifiers = dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum, delegate dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected dotnet_naming_symbols.types.required_modifiers = dotnet_naming_symbols.non_private_members.applicable_kinds = property, method, field, event dotnet_naming_symbols.non_private_members.applicable_accessibilities = public, internal, protected, protected_internal, private_protected dotnet_naming_symbols.private_members.applicable_kinds = property, method, field, event dotnet_naming_symbols.private_members.applicable_accessibilities = private # Naming styles dotnet_naming_style.pascal_case.required_prefix = dotnet_naming_style.pascal_case.required_suffix = dotnet_naming_style.pascal_case.word_separator = dotnet_naming_style.pascal_case.capitalization = pascal_case dotnet_naming_style.begins_with_i.required_prefix = I dotnet_naming_style.begins_with_i.required_suffix = dotnet_naming_style.begins_with_i.word_separator = dotnet_naming_style.begins_with_i.capitalization = pascal_case dotnet_naming_style.camel_case.required_prefix = dotnet_naming_style.camel_case.required_suffix = dotnet_naming_style.camel_case.word_separator = dotnet_naming_style.camel_case.capitalization = camel_case [*.cs] csharp_new_line_before_catch = false csharp_new_line_before_else = false csharp_new_line_before_finally = false csharp_new_line_before_members_in_anonymous_types = true csharp_new_line_before_members_in_object_initializers = true csharp_new_line_before_open_brace = none csharp_new_line_between_query_expression_clauses = true csharp_indent_block_contents = true csharp_indent_braces = false csharp_indent_case_contents = true csharp_indent_case_contents_when_block = true csharp_indent_labels = one_less_than_current csharp_indent_switch_labels = true csharp_space_after_cast = false csharp_space_after_colon_in_inheritance_clause = true csharp_space_after_comma = true csharp_space_after_dot = false csharp_space_after_keywords_in_control_flow_statements = true csharp_space_after_semicolon_in_for_statement = true csharp_space_around_binary_operators = before_and_after csharp_space_around_declaration_statements = false csharp_space_before_colon_in_inheritance_clause = true csharp_space_before_comma = false csharp_space_before_dot = false csharp_space_before_open_square_brackets = false csharp_space_before_semicolon_in_for_statement = false csharp_space_between_empty_square_brackets = false csharp_space_between_method_call_empty_parameter_list_parentheses = false csharp_space_between_method_call_name_and_opening_parenthesis = false csharp_space_between_method_call_parameter_list_parentheses = false csharp_space_between_method_declaration_empty_parameter_list_parentheses = false csharp_space_between_method_declaration_name_and_open_parenthesis = false csharp_space_between_method_declaration_parameter_list_parentheses = false csharp_space_between_parentheses = false csharp_space_between_square_brackets = false csharp_preserve_single_line_blocks = true csharp_preserve_single_line_statements = true csharp_prefer_braces = true:warning csharp_style_expression_bodied_constructors = true:suggestion csharp_style_expression_bodied_methods = true:suggestion csharp_style_expression_bodied_properties = true:suggestion csharp_prefer_simple_default_expression = true:suggestion dotnet_style_prefer_inferred_tuple_names = true:suggestion csharp_style_var_elsewhere = true:suggestion csharp_style_var_for_built_in_types = true:suggestion csharp_style_var_when_type_is_apparent = true:suggestion csharp_preferred_modifier_order = internal,protected,public,private,static,readonly,abstract,override,sealed,virtual:suggestion csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion csharp_style_pattern_matching_over_as_with_null_check = true:suggestion csharp_style_inlined_variable_declaration = true:suggestion csharp_style_deconstructed_variable_declaration = true:suggestion csharp_style_pattern_local_over_anonymous_function = true:suggestion csharp_style_throw_expression = true:suggestion csharp_style_conditional_delegate_call = true:suggestion [*.vb] visual_basic_preferred_modifier_order = partial,default,private,protected,public,friend,notoverridable,overridable,mustoverride,overloads,overrides,mustinherit,notinheritable,static,shared,shadows,readonly,writeonly,dim,const,withevents,widening,narrowing,custom,async,iterator:silent visual_basic_style_unused_value_assignment_preference = unused_local_variable:suggestion visual_basic_style_unused_value_expression_statement_preference = unused_local_variable:silent ================================================ FILE: 00_Utilities/DotnetUtils/DotnetUtils/DotnetUtils.csproj ================================================ Exe net6.0 enable enable ================================================ FILE: 00_Utilities/DotnetUtils/DotnetUtils/Extensions.cs ================================================ using System.Diagnostics.CodeAnalysis; using static System.IO.Path; namespace DotnetUtils; public static class Extensions { public static IEnumerable SelectT(this IEnumerable<(T1, T2)> src, Func selector) => src.Select(x => selector(x.Item1, x.Item2)); public static IEnumerable SelectT(this IEnumerable<(T1, T2, T3)> src, Func selector) => src.Select(x => selector(x.Item1, x.Item2, x.Item3)); public static IEnumerable<(T1, T2, int)> WithIndex(this IEnumerable<(T1, T2)> src) => src.Select((x, index) => (x.Item1, x.Item2, index)); public static bool None(this IEnumerable src, Func? predicate = null) => predicate is null ? !src.Any() : !src.Any(predicate); public static bool IsNullOrWhitespace([NotNullWhen(false)] this string? s) => string.IsNullOrWhiteSpace(s); [return: NotNullIfNotNull("path")] public static string? RelativePath(this string? path, string? rootPath) { if ( path.IsNullOrWhitespace() || rootPath.IsNullOrWhitespace() ) { return path; } path = path.TrimEnd('\\'); // remove trailing backslash, if present return GetRelativePath(rootPath, path.TrimEnd('\\')); } } ================================================ FILE: 00_Utilities/DotnetUtils/DotnetUtils/Functions.cs ================================================ using System.Xml.Linq; using static System.Console; namespace DotnetUtils; public static class Functions { public static string? getValue(string path, params string[] names) { if (names.Length == 0) { throw new InvalidOperationException(); } var parent = XDocument.Load(path).Element("Project")?.Element("PropertyGroup"); return getValue(parent, names); } public static string? getValue(XElement? parent, params string[] names) { if (names.Length == 0) { throw new InvalidOperationException(); } XElement? elem = null; foreach (var name in names) { elem = parent?.Element(name); if (elem != null) { break; } } return elem?.Value; } public static int getChoice(int maxValue) => getChoice(0, maxValue); public static int getChoice(int minValue, int maxValue) { int result; do { Write("? "); } while (!int.TryParse(ReadLine(), out result) || result < minValue || result > maxValue); //WriteLine(); return result; } } ================================================ FILE: 00_Utilities/DotnetUtils/DotnetUtils/Globals.cs ================================================ namespace DotnetUtils; public static class Globals { public static readonly Dictionary LangData = new() { { "csharp", ("cs", "csproj") }, { "vbnet", ("vb", "vbproj") } }; } ================================================ FILE: 00_Utilities/DotnetUtils/DotnetUtils/Methods.cs ================================================ using System.Diagnostics; namespace DotnetUtils; public static class Methods { public static ProcessResult RunProcess(string filename, string arguments) { var process = new Process() { StartInfo = { FileName = filename, Arguments = arguments, UseShellExecute = false, CreateNoWindow = true, RedirectStandardOutput = true, RedirectStandardError = true, }, EnableRaisingEvents = true }; return RunProcess(process); } public static ProcessResult RunProcess(Process process, string input = "") { var (output, error) = ("", ""); var (redirectOut, redirectErr) = ( process.StartInfo.RedirectStandardOutput, process.StartInfo.RedirectStandardError ); if (redirectOut) { process.OutputDataReceived += (s, ea) => output += ea.Data + "\n"; } if (redirectErr) { process.ErrorDataReceived += (s, ea) => error += ea.Data + "\n"; } if (!process.Start()) { throw new InvalidOperationException(); }; if (redirectOut) { process.BeginOutputReadLine(); } if (redirectErr) { process.BeginErrorReadLine(); } if (!string.IsNullOrEmpty(input)) { process.StandardInput.WriteLine(input); process.StandardInput.Close(); } process.WaitForExit(); return new ProcessResult(process.ExitCode, output, error); } public static Task RunProcessAsync(Process process, string input = "") { var tcs = new TaskCompletionSource(); var (output, error) = ("", ""); var (redirectOut, redirectErr) = ( process.StartInfo.RedirectStandardOutput, process.StartInfo.RedirectStandardError ); process.Exited += (s, e) => tcs.SetResult(new ProcessResult(process.ExitCode, output, error)); if (redirectOut) { process.OutputDataReceived += (s, ea) => output += ea.Data + "\n"; } if (redirectErr) { process.ErrorDataReceived += (s, ea) => error += ea.Data + "\n"; } if (!process.Start()) { // what happens to the Exited event if process doesn't start successfully? throw new InvalidOperationException(); } if (redirectOut) { process.BeginOutputReadLine(); } if (redirectErr) { process.BeginErrorReadLine(); } if (!string.IsNullOrEmpty(input)) { process.StandardInput.WriteLine(input); process.StandardInput.Close(); } return tcs.Task; } } public sealed record ProcessResult(int ExitCode, string StdOut, string StdErr) { public override string? ToString() => StdOut + (StdOut is not (null or "") && ExitCode > 0 ? "\n" : "") + (ExitCode != 0 ? $"{ExitCode}\n{StdErr}" : ""); } ================================================ FILE: 00_Utilities/DotnetUtils/DotnetUtils/PortInfo.cs ================================================ using static System.IO.Directory; using static System.IO.Path; using static DotnetUtils.Globals; namespace DotnetUtils; public record PortInfo( string GamePath, string FolderName, int Index, string GameName, string LangPath, string Lang, string Ext, string ProjExt, string[] CodeFiles, string[] Slns, string[] Projs ) { private static readonly EnumerationOptions enumerationOptions = new() { RecurseSubdirectories = true, MatchType = MatchType.Simple, MatchCasing = MatchCasing.CaseInsensitive }; // .NET namespaces cannot have a digit as the first character // For games whose name starts with a digit, we map the name to a specific string private static readonly Dictionary specialGameNames = new() { { "3-D_Plot", "Plot" }, { "3-D_Tic-Tac-Toe", "ThreeDTicTacToe" }, { "23_Matches", "TwentyThreeMatches"} }; public static PortInfo? Create(string gamePath, string langKeyword) { var folderName = GetFileName(gamePath); var parts = folderName.Split('_', 2); if (parts.Length <= 1) { return null; } var (index, gameName) = ( int.TryParse(parts[0], out var n) && n > 0 ? // ignore utilities folder n : (int?)null, specialGameNames.TryGetValue(parts[1], out var specialName) ? specialName : parts[1].Replace("_", "").Replace("-", "") ); if (index is null || gameName is null) { return null; } var (ext, projExt) = LangData[langKeyword]; var langPath = Combine(gamePath, langKeyword); var codeFiles = GetFiles(langPath, $"*.{ext}", enumerationOptions) .Where(x => !x.Contains("\\bin\\") && !x.Contains("\\obj\\")) .ToArray(); return new PortInfo( gamePath, folderName, index.Value, gameName, langPath, langKeyword, ext, projExt, codeFiles, GetFiles(langPath, "*.sln", enumerationOptions), GetFiles(langPath, $"*.{projExt}", enumerationOptions) ); } } ================================================ FILE: 00_Utilities/DotnetUtils/DotnetUtils/PortInfos.cs ================================================ using System.Reflection; using static System.IO.Directory; using static DotnetUtils.Globals; namespace DotnetUtils; public static class PortInfos { public static readonly string Root; static PortInfos() { Root = GetParent(Assembly.GetEntryAssembly()!.Location)!.FullName; Root = Root[..Root.IndexOf(@"\00_Utilities")]; Get = GetDirectories(Root) .SelectMany(gamePath => LangData.Keys.Select(keyword => (gamePath, keyword))) .SelectT((gamePath, keyword) => PortInfo.Create(gamePath, keyword)) .Where(x => x is not null) .ToArray()!; } public static readonly PortInfo[] Get; } ================================================ FILE: 00_Utilities/DotnetUtils/DotnetUtils/Program.cs ================================================ using System.Xml.Linq; using DotnetUtils; using static System.Console; using static System.IO.Path; using static DotnetUtils.Methods; using static DotnetUtils.Functions; var infos = PortInfos.Get; var actions = new (Action action, string description)[] { (printInfos, "Output information -- solution, project, and code files"), (missingSln, "Output missing sln"), (unexpectedSlnName, "Output misnamed sln"), (multipleSlns, "Output multiple sln files"), (missingProj, "Output missing project file"), (unexpectedProjName, "Output misnamed project files"), (multipleProjs, "Output multiple project files"), (checkProjects, "Check .csproj/.vbproj files for target framework, nullability etc."), (checkExecutableProject, "Check that there is at least one executable project per port"), (noCodeFiles, "Output ports without any code files"), (printPortInfo, "Print info about a single port"), (generateMissingSlns, "Generate solution files when missing"), (generateMissingProjs, "Generate project files when missing") }; foreach (var (_, description, index) in actions.WithIndex()) { WriteLine($"{index}: {description}"); } WriteLine(); actions[getChoice(actions.Length - 1)].action(); void printSlns(PortInfo pi) { switch (pi.Slns.Length) { case 0: WriteLine("No sln"); break; case 1: WriteLine($"Solution: {pi.Slns[0].RelativePath(pi.LangPath)}"); break; case > 1: WriteLine("Solutions:"); foreach (var sln in pi.Slns) { Write(sln.RelativePath(pi.LangPath)); WriteLine(); } break; } } void printProjs(PortInfo pi) { switch (pi.Projs.Length) { case 0: WriteLine("No project"); break; case 1: WriteLine($"Project: {pi.Projs[0].RelativePath(pi.LangPath)}"); break; case > 1: WriteLine("Projects:"); foreach (var proj in pi.Projs) { Write(proj.RelativePath(pi.LangPath)); WriteLine(); } break; } } void printInfos() { foreach (var item in infos) { WriteLine(item.LangPath); WriteLine(); printSlns(item); WriteLine(); printProjs(item); WriteLine(); // get code files foreach (var file in item.CodeFiles) { WriteLine(file.RelativePath(item.LangPath)); } WriteLine(new string('-', 50)); } } void missingSln() { var data = infos.Where(x => x.Slns.None()).ToArray(); foreach (var item in data) { WriteLine(item.LangPath); } WriteLine(); WriteLine($"Count: {data.Length}"); } void unexpectedSlnName() { var counter = 0; foreach (var item in infos) { if (item.Slns.None()) { continue; } var expectedSlnName = $"{item.GameName}.sln"; if (item.Slns.Contains(Combine(item.LangPath, expectedSlnName), StringComparer.InvariantCultureIgnoreCase)) { continue; } counter += 1; WriteLine(item.LangPath); WriteLine($"Expected: {expectedSlnName}"); printSlns(item); WriteLine(); } WriteLine($"Count: {counter}"); } void multipleSlns() { var data = infos.Where(x => x.Slns.Length > 1).ToArray(); foreach (var item in data) { WriteLine(item.LangPath); printSlns(item); } WriteLine(); WriteLine($"Count: {data.Length}"); } void missingProj() { var data = infos.Where(x => x.Projs.None()).ToArray(); foreach (var item in data) { WriteLine(item.LangPath); } WriteLine(); WriteLine($"Count: {data.Length}"); } void unexpectedProjName() { var counter = 0; foreach (var item in infos) { if (item.Projs.None()) { continue; } var expectedProjName = $"{item.GameName}.{item.ProjExt}"; if (item.Projs.Contains(Combine(item.LangPath, expectedProjName))) { continue; } counter += 1; WriteLine(item.LangPath); WriteLine($"Expected: {expectedProjName}"); printProjs(item); WriteLine(); } WriteLine($"Count: {counter}"); } void multipleProjs() { var data = infos.Where(x => x.Projs.Length > 1).ToArray(); foreach (var item in data) { WriteLine(item.LangPath); WriteLine(); printProjs(item); } WriteLine(); WriteLine($"Count: {data.Length}"); } void generateMissingSlns() { foreach (var item in infos.Where(x => x.Slns.None())) { var result = RunProcess("dotnet", $"new sln -n {item.GameName} -o {item.LangPath}"); WriteLine(result); var slnFullPath = Combine(item.LangPath, $"{item.GameName}.sln"); foreach (var proj in item.Projs) { result = RunProcess("dotnet", $"sln {slnFullPath} add {proj}"); WriteLine(result); } } } void generateMissingProjs() { foreach (var item in infos.Where(x => x.Projs.None())) { // We can't use the dotnet command to create a new project using the built-in console template, because part of that template // is a Program.cs / Program.vb file. If there already are code files, there's no need to add a new empty one; and // if there's already such a file, it might try to overwrite it. var projText = item.Lang switch { "csharp" => @" Exe net6.0 10 enable enable ", "vbnet" => @$" Exe {item.GameName} net6.0 16.9 ", _ => throw new InvalidOperationException() }; var projFullPath = Combine(item.LangPath, $"{item.GameName}.{item.ProjExt}"); File.WriteAllText(projFullPath, projText); if (item.Slns.Length == 1) { var result = RunProcess("dotnet", $"sln {item.Slns[0]} add {projFullPath}"); WriteLine(result); } } } void checkProjects() { foreach (var info in infos) { WriteLine(info.LangPath); printProjectWarnings(info); } } void printProjectWarnings(PortInfo info) { foreach (var proj in info.Projs) { var warnings = new List(); var parent = XDocument.Load(proj).Element("Project")?.Element("PropertyGroup"); var ( framework, nullable, implicitUsing, rootNamespace, langVersion, optionStrict ) = ( getValue(parent, "TargetFramework", "TargetFrameworks"), getValue(parent, "Nullable"), getValue(parent, "ImplicitUsings"), getValue(parent, "RootNamespace"), getValue(parent, "LangVersion"), getValue(parent, "OptionStrict") ); if (framework != "net6.0") { warnings.Add($"Target: {framework}"); } if (info.Lang == "csharp") { if (nullable != "enable") { warnings.Add($"Nullable: {nullable}"); } if (implicitUsing != "enable") { warnings.Add($"ImplicitUsings: {implicitUsing}"); } if (rootNamespace != null && rootNamespace != info.GameName) { warnings.Add($"RootNamespace: {rootNamespace}"); } if (langVersion != "10") { warnings.Add($"LangVersion: {langVersion}"); } } if (info.Lang == "vbnet") { if (rootNamespace != info.GameName) { warnings.Add($"RootNamespace: {rootNamespace}"); } if (langVersion != "16.9") { warnings.Add($"LangVersion: {langVersion}"); } if (optionStrict != "On") { warnings.Add($"OptionStrict: {optionStrict}"); } } if (warnings.Any()) { WriteLine(proj.RelativePath(info.LangPath)); WriteLine(string.Join("\n", warnings)); WriteLine(); } } } void checkExecutableProject() { foreach (var item in infos) { if (item.Projs.All(proj => getValue(proj, "OutputType") != "Exe")) { WriteLine($"{item.LangPath}"); } } } void noCodeFiles() { var qry = infos .Where(x => x.CodeFiles.None()) .OrderBy(x => x.Lang); foreach (var item in qry) { WriteLine(item.LangPath); } } void tryBuild() { // if has code files, try to build } void printPortInfo() { // prompt for port number Write("Enter number from 1 to 96 "); var index = getChoice(1, 96); Write("Enter 0 for C#, 1 for VB "); var lang = getChoice(1) switch { 0 => "csharp", 1 => "vbnet", _ => throw new InvalidOperationException() }; WriteLine(); var info = infos.Single(x => x.Index == index && x.Lang == lang); WriteLine(info.LangPath); WriteLine(new string('-', 50)); // print solutions printSlns(info); // mismatched solution name/location? (expected x) var expectedSlnName = Combine(info.LangPath, $"{info.GameName}.sln"); if (!info.Slns.Contains(expectedSlnName)) { WriteLine($"Expected name/path: {expectedSlnName.RelativePath(info.LangPath)}"); } // has executable project? if (info.Projs.All(proj => getValue(proj, "OutputType") != "Exe")) { WriteLine("No executable project"); } WriteLine(); // print projects printProjs(info); // mimsatched project name/location? (expected x) var expectedProjName = Combine(info.LangPath, $"{info.GameName}.{info.ProjExt}"); if (info.Projs.Length < 2 && !info.Projs.Contains(expectedProjName)) { WriteLine($"Expected name/path: {expectedProjName.RelativePath(info.LangPath)}"); } WriteLine(); // verify project properties printProjectWarnings(info); WriteLine("Code files:"); // list code files foreach (var codeFile in info.CodeFiles) { WriteLine(codeFile.RelativePath(info.LangPath)); } // try build } ================================================ FILE: 00_Utilities/DotnetUtils/DotnetUtils.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.32014.148 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotnetUtils", "DotnetUtils\DotnetUtils.csproj", "{BFDF93C2-4FB7-4838-AFDF-E7B5F83C3F00}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {BFDF93C2-4FB7-4838-AFDF-E7B5F83C3F00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BFDF93C2-4FB7-4838-AFDF-E7B5F83C3F00}.Debug|Any CPU.Build.0 = Debug|Any CPU {BFDF93C2-4FB7-4838-AFDF-E7B5F83C3F00}.Release|Any CPU.ActiveCfg = Release|Any CPU {BFDF93C2-4FB7-4838-AFDF-E7B5F83C3F00}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {30FCF56E-4E83-42F8-AB43-A52C86C7C9B4} EndGlobalSection EndGlobal ================================================ FILE: 00_Utilities/README.md ================================================ #### Utilities These are global helper / utility programs to assist us in maintaining all the ports. ================================================ FILE: 00_Utilities/TODO.md ================================================ # TODO list game | C# | Java | JS | Kotlin | Lua | Perl | Python | Ruby | Rust | VB.NET ------------------------------ | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- 01_Acey_Ducey | ✅ | ✅ | ✅ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ [02_Amazing](../02_Amazing) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ✅ [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) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ✅ | ⬜️ [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) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ✅ | ⬜️ [19_Bunny](../19_Bunny) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ [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) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ [27_Civil_War](../27_Civil_War) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ [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) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ⬜️ [37_Football](../37_Football) | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ [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) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ✅ | ⬜️ [44_Hangman](../44_Hangman) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ [45_Hello](../45_Hello) | ⬜️ | ✅ | ✅ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ [46_Hexapawn](../46_Hexapawn) | ✅ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ [47_Hi-Lo](../47_Hi-Lo) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ⬜️ [48_High_IQ](../48_High_IQ) | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ [49_Hockey](../49_Hockey) | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ [50_Horserace](../50_Horserace) | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ✅ | ⬜️ [51_Hurkle](../51_Hurkle) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ⬜️ [52_Kinema](../52_Kinema) | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ [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) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ⬜️ [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) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ✅ | ⬜️ [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) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ✅ | ⬜️ [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) | ✅ | ✅ | ✅ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ✅ | ⬜️ [79_Slalom](../79_Slalom) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ [80_Slots](../80_Slots) | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ [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) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ [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) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ⬜️ [94_War](../94_War) | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ⬜️ [95_Weekday](../95_Weekday) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ✅ | ⬜️ [96_Word](../96_Word) | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ✅ ------------------------------ | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- Sum of 96 | 79 | 83 | 96 | 9 | 16 | 75 | 95 | 51 | 58 | 7 ================================================ FILE: 00_Utilities/VintageBASIC.xml ================================================ 00REM 01 02 03 04 - + ^ * / = <> < > <= >= DATA DEF FN DIM END FOR GOSUB GOTO IF THEN INPUT LET NEXT ON PRINT RANDOMIZE REM RESTORE RETURN STOP TO ABS ASC ATN CHR$ COS EXP INT LEFT$ LEN LOG MID$ RIGHT$ RND SGN SIN SPC SQR STR TAB TAN VAL NOT AND OR 00" 01 02" 03( 04 05) 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 ================================================ FILE: 00_Utilities/bas2perl.pl ================================================ #!/usr/bin/perl use strict; my $Mode= lc($ARGV[0]); #trace #convert my $File= $ARGV[1]; my $LN= "Line"; my $Pedantic= 0; my $Indent= 0; my %Vars; # num | str | anm | ast my @Data; my %Code; open(FH, $File); while (my $Line = ) { chomp $Line; my $Space= index($Line, " "); my $Key= substr($Line, 0, $Space); my $Code= substr($Line, $Space+1); $Code{$Key}=$Code; } close(FH); foreach my $Lin (sort {$a<=>$b} keys %Code) { if ($Mode eq "trace") { print "==> $Lin $Code{$Lin}\n"; } my $Ret= &PROCLINE($Code{$Lin}); if ($Mode eq "trace") { print " $Ret\n"; } $Code{$Lin}= $Ret; } if ($Mode eq "convert") { $Code{'0.1'}= "#!/usr/bin/perl"; $Code{'0.2'}= "#use strict;"; $Code{'0.3'}= "# Automatic converted by bas2perl.pl"; $Code{'0.4'}= ""; foreach my $Lin (sort {$a<=>$b} keys %Code) { print "$Code{$Lin}\n"; } } if (@Data) { &DATAIL(); } print "\n\n\n"; exit; sub PROCLINE { my ($Line)= @_; my @Sente= &SMARPLIT($Line, ":", "\""); my $Perl; foreach my $Sen (@Sente) { my $Flag=0; if ($Pedantic==0) { #REM: Resolves some ugly syntaxis... $Sen=~ s/\bPRINT"/PRINT "/g; # PRINT"Hello" $Sen=~ s/"([A-Z])\$/"; $1\$/g; # PRINT "Hello "N$ } $Sen= &TRIM($Sen); if ($Sen>0) { $Sen= "GOTO $Sen"; } if ($Sen=~ /^DATA\b/) { $Sen= &DATA($Sen); $Flag=1; } if ($Sen=~ /^DIM\b/) { $Sen= &DIM($Sen); $Flag=1; } if ($Sen=~ /^END\b/) { $Sen= &ENDD($Sen); $Flag=1; } if ($Sen=~ /^FOR\b/) { $Sen= &FOR($Sen); $Flag=1; } if ($Sen=~ /^GOTO\b/) { $Sen= &GOTO($Sen); $Flag=1; } if ($Sen=~ /^GOSUB\b/) { $Sen= &GOSUB($Sen); $Flag=1; } if ($Sen=~ /^IF\b/) { $Sen= &IF($Sen); $Flag=1; } if ($Sen=~ /^INPUT\b/) { $Sen= &INPUT($Sen); $Flag=1; } if ($Sen=~ /^NEXT\b/) { $Sen= &NEXT($Sen); $Flag=1; } if ($Sen=~ /^ON\b/ && $Sen=~ / GOTO /) { $Sen= &ONGOTO($Sen); $Flag=1; } if ($Sen=~ /^PRINT\b/) { $Sen= &PRINT($Sen); $Flag=1; } if ($Sen=~ /^READ\b/) { $Sen= &READ($Sen); $Flag=1; } if ($Sen=~ /^REM\b/) { $Sen= &REM($Sen); $Flag=1; } if ($Sen=~ /^RETURN\b/) { $Sen= &RETURN($Sen); $Flag=1; } if ($Sen=~ /^STOP\b/) { $Sen= &ENDD($Sen); $Flag=1; } if ($Flag==0) { $Sen= &FORMULA($Sen); } # LET $Sen.=";"; $Sen=~ s/\{;$/\{/g; $Sen=~ s/\};$/\}/g; $Perl.= "$Sen "; } $Perl= &TRIM($Perl); my $Adj= 0; if ($Perl=~ /^for\b/) { $Adj--; } if ($Perl eq "}") { $Adj++; } $Perl= "\t"x ($Indent+$Adj) . $Perl; return $Perl; } #################### # BASIC STATEMENTS # #################### sub DATA { my ($Str)= @_; $Str=~ s/DATA //; push @Data, $Str; return "# TO DATA SEGMENT"; } sub DIM { my ($Str)= @_; $Str=~ s/DIM //; my @Parts= split(/\,(?![^\(]*\))/, $Str); my $Out; foreach my $Par (@Parts) { my $Type= $Par=~ /\$/ ? "ast" : "anm"; $Par=~ s/\$//g; $Par=~ s/\(.*\)//; $Vars{$Par}= "anm"; $Out.= "my \@$Par; "; } chop $Out; chop $Out; return $Out; } sub ENDD { return "exit"; } sub FOR { my ($Str)= @_; $Str=~ s/= /=/g; my @Parts= split(" ", $Str); $Parts[1]= &FORMULA($Parts[1]); my $Var=substr($Parts[1],0,index($Parts[1],"=")); $Parts[3]= "$Var<=".&FORMULA($Parts[3]); if ($Parts[5]<0) { $Parts[3]=~ s//; } $Parts[5]= $Parts[5] eq "" ? "$Var++" : "$Var+=".&FORMULA($Parts[5]); $Str= "for ($Parts[1]; $Parts[3]; $Parts[5]) {"; $Indent++; return $Str; } sub GOTO { # The birth of spaguetti code! # Dijkstra would not like this... my ($Str)= @_; my @Parts= split(" ", $Str); my $Label= "$LN$Parts[1]"; $Str= lc($Parts[0])." $Label"; $Code{($Parts[1]-.2)}=""; $Code{($Parts[1]-.1)}="$Label:"; return $Str; } sub GOSUB { my ($Str)= @_; my @Parts= split(" ", $Str); my $Label= "$LN$Parts[1]"; $Str= "\&$Label()"; $Code{($Parts[1]-.2)}=""; $Code{($Parts[1]-.1)}="sub $Label {"; return $Str; } sub IF { my ($Str)= @_; $Str=~ s/^IF //g; my @Parts= split(" THEN ", $Str); $Parts[0]= &FORMULA($Parts[0], 1); $Parts[1]= &PROCLINE($Parts[1]); my $Str= "if ($Parts[0]) { $Parts[1] }"; return $Str; } sub INPUT { my ($Str)= @_; $Str=~ s/INPUT //; $Str=~ s/"(.*)"//g; my $Txt= qq|print "$1\? "; |; my @Parts= split(/,/, $Str); my @Multi; foreach my $Par (@Parts) { my $Type= "num"; if ($Par=~ /\$/) { $Type= "str"; $Par=~ s/\$//g; } if ($Par=~ /\(/) { if ($Type eq "num") { $Type= "anm"; } if ($Type eq "str") { $Type= "ast"; } $Par=~ s/\(/\[/g; $Par=~ s/\)/\]/g; } $Par=~ s/\;//g; push @Multi, "\$$Par"; my $Name= $Par; $Name=~ s/\[.*\]//; $Vars{$Name}= $Type; } $Str= join(",", @Multi); my $Spl= ""; if (scalar @Parts>1) { $Spl= "; ($Str)= split(/,/, \$Inp_)"; $Str= "\$Inp_"; } my $Inp= qq|chomp($Str = uc())$Spl|; if ($Str=~ /,/) { $Str= "\$$Str"; $Str=~ s/,/,\$/g; $Str= "Inp"; } return $Txt.$Inp; } sub NEXT { $Indent--; return "}"; } sub ONGOTO { # Base 1, if not match it will be skipped. my ($Str)= @_; my @Parts= split(" ", $Str); my $Var= $Parts[1]; my @Lines= split(",", $Parts[3]); my $Count=0; my $Text; foreach my $Lin (@Lines) { $Count++; my $This= "\telsif (\$$Var==$Count) "; if ($Count==1) { $This= "if (\$$Var==1) "; } my $Goto= &GOTO("GOTO $Lin"); $This.="{ $Goto; }\n"; $Text.= $This; } return $Text; } sub PRINT { my ($Str)= @_; if ($Str eq "PRINT") { return 'print "\n"' }; $Str=~ s/^PRINT //; my $Enter= 1; if ($Str=~ /;$/) { $Enter= 0; } my @Parts= &SMARPLIT($Str, ";", "\""); my @Out; foreach my $Par (@Parts) { if ($Par=~ /"/) { push @Out, $Par; next; } if ($Par=~ /TAB\((.*?)\)/) { push @Out, "' 'x".&FORMULA($1)." "; next; } $Par= &FORMULA($Par); push @Out, $Par; } my $Out= join(". ", @Out); if ($Enter) { $Out.= qq|. "\\n"|; } return "print ".$Out; } sub READ { my ($Str)= @_; $Str=~ s/READ //; $Str= &FORMULA($Str); $Str.="= ; chomp($Str)"; return $Str; } sub REM { my ($Str)= @_; return "#".$Str; } sub RETURN { return "return; }"; } ########### # HELPERS # ########### sub TRIM { my ($Str)= @_; #$Str=~ s/\s+/ /g; $Str=~ s/^\s+//; $Str=~ s/\s+$//; return $Str; } sub DATAIL { print "\n\n\n"; print "__DATA__\n"; foreach my $Dat (@Data) { $Dat=~ s/"//g; $Dat=~ s/,/\n/g; print "$Dat\n"; } } sub FORMULA { my ($Str, $Cond)= @_; $Str=~ s/\$//g; $Str=~ s/ABS\(/abs\(/g; $Str=~ s/COS\(/cos\(/g; $Str=~ s/LEN\(/length\(/g; $Str=~ s/INT\(/int\(/g; $Str=~ s/MID\$?\(/substr\(/g; $Str=~ s/RND\(/rand\(/g; $Str=~ s/SIN\(/sin\(/g; $Str=~ s/SQR\(/sqr\(/g; $Str=~ s/(\b[A-Z][0-9]?\b)/\$$&/g; #==> Check for arrays... foreach my $Key (keys %Vars) { if ($Vars{$Key}!~ /^a/) { next; } $Str=~ s/\$$Key\((.*?)\)/\$$Key\[$1\]/g; } if ($Cond==1) { $Str=~ s/<>/ ne /g; $Str=~ s/=/ eq /g; } return $Str; } sub SMARPLIT { my ($Str, $Sep, $Nin)= @_; my @Parts; my $Text= ""; my $Flag= 0; my $Prev; foreach my $Char (split('', $Str)) { if ($Char eq $Nin) { $Flag= !$Flag; } if ($Char eq $Sep && $Flag==0) { push @Parts, &TRIM($Text); $Text= ""; next; } $Prev= $Char; $Text.= $Char; } if ($Text) { push @Parts, &TRIM($Text); } return @Parts; } ================================================ FILE: 00_Utilities/build-index.js ================================================ #!/usr/bin/env node /** * This script creates an "index.html" file in the root of the directory. * * Call this from the root of the project with * `node ./00_Utilities/build-index.js` * * @author Alexander Wunschik */ const fs = require('fs'); const path = require('path'); const TITLE = 'BASIC Computer Games'; const JAVASCRIPT_FOLDER = 'javascript'; const IGNORE_FOLDERS_START_WITH = ['.', '00_', 'buildJvm', 'Sudoku']; const IGNORE_FILES = [ // "84 Super Star Trek" has it's own node/js implementation (using xterm) 'cli.mjs', 'superstartrek.mjs' ]; function createGameLinks(game) { const creatFileLink = (file, name = path.basename(file)) => { if (file.endsWith('.html')) { return `
  • ${name.replace('.html', '')}
  • `; } else if (file.endsWith('.mjs')) { return `
  • ${name.replace('.mjs', '')} (node.js)
  • `; } else { throw new Error(`Unknown file-type found: ${file}`); } } if (game.files.length > 1) { const entries = game.files.map(file => { return creatFileLink(file); }); return `
  • ${game.name}
      ${entries.map(e => `\t\t\t${e}`).join('\n')}
  • `; } else { return creatFileLink(game.files[0], game.name); } } function createIndexHtml(title, games) { const listEntries = games.map(game => createGameLinks(game) ).map(entry => `\t\t\t${entry}`).join('\n'); const head = ` ${title} `; const body = `

    ${title}

      ${listEntries}
    `; return ` ${head} ${body} `.trim().replace(/\s\s+/g, ''); } function findJSFilesInFolder(folder) { // filter folders that do not include a subfolder called "javascript" const hasJavascript = fs.existsSync(`${folder}/${JAVASCRIPT_FOLDER}`); if (!hasJavascript) { throw new Error(`Game "${folder}" is missing a javascript folder`); } // get all files in the javascript folder const files = fs.readdirSync(`${folder}/${JAVASCRIPT_FOLDER}`); // filter files only allow .html files const htmlFiles = files.filter(file => file.endsWith('.html')); const mjsFiles = files.filter(file => file.endsWith('.mjs')); const entries = [ ...htmlFiles, ...mjsFiles ].filter(file => !IGNORE_FILES.includes(file)); if (entries.length == 0) { throw new Error(`Game "${folder}" is missing a HTML or node.js file in the folder "${folder}/${JAVASCRIPT_FOLDER}"`); } return entries.map(file => path.join(folder, JAVASCRIPT_FOLDER, file)); } function main() { // Get the list of all folders in the current director let folders = fs.readdirSync(process.cwd()); // filter files only allow folders folders = folders.filter(folder => fs.statSync(folder).isDirectory()); // filter out the folders that start with a dot or 00_ folders = folders.filter(folder => { return !IGNORE_FOLDERS_START_WITH.some(ignore => folder.startsWith(ignore)); }); // sort the folders alphabetically (by number) folders = folders.sort(); // get name and javascript file from folder const games = folders.map(folder => { const name = folder.replace('_', ' '); let files; try { files = findJSFilesInFolder(folder); } catch (error) { console.warn(`Game "${name}" is missing a javascript implementation: ${error.message}`); return null; } return { name, files } }).filter(game => game !== null); // create a index.html file with a list of all games const htmlContent = createIndexHtml(TITLE, games); fs.writeFileSync('index.html', htmlContent); console.log(`index.html successfully created!`); } main(); ================================================ FILE: 00_Utilities/find-missing-implementations.js ================================================ /** * Program to find games that are missing solutions in a given language * * Scan each game folder, check for a folder for each language, and also make * sure there's at least one file of the expected extension and not just a * readme or something */ const fs = require("fs"); const glob = require("glob"); // relative path to the repository root const ROOT_PATH = "../."; 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" }, ]; const getFilesRecursive = async (path, extension) => { return new Promise((resolve, reject) => { glob(`${path}/**/*.${extension}`, (err, matches) => { if (err) { reject(err); } resolve(matches); }); }); }; const getPuzzleFolders = () => { return fs .readdirSync(ROOT_PATH, { withFileTypes: true }) .filter((dirEntry) => dirEntry.isDirectory()) .filter( (dirEntry) => ![".git", "node_modules", "00_Utilities"].includes(dirEntry.name) ) .map((dirEntry) => dirEntry.name); }; (async () => { let missingGames = {}; let missingLanguageCounts = {}; languages.forEach((l) => (missingLanguageCounts[l.name] = 0)); const puzzles = getPuzzleFolders(); for (const puzzle of puzzles) { for (const { name: language, extension } of languages) { const files = await getFilesRecursive( `${ROOT_PATH}/${puzzle}/${language}`, extension ); if (files.length === 0) { if (!missingGames[puzzle]) missingGames[puzzle] = []; missingGames[puzzle].push(language); missingLanguageCounts[language]++; } } } const missingCount = Object.values(missingGames).flat().length; if (missingCount === 0) { console.log("All games have solutions for all languages"); } else { console.log(`Missing ${missingCount} implementations:`); Object.entries(missingGames).forEach( ([p, ls]) => (missingGames[p] = ls.join(", ")) ); console.log(`\nMissing languages by game:`); console.table(missingGames); console.log(`\nBy language:`); console.table(missingLanguageCounts); } })(); return; ================================================ FILE: 00_Utilities/find-unimplemented.js ================================================ /** * Program to show unimplemented games by language, optionally filtered by * language * * Usage: node find-unimplemented.js [[[lang1] lang2] ...] * * Adapted from find-missing-implementtion.js */ const fs = require("fs"); const glob = require("glob"); // relative path to the repository root const ROOT_PATH = "../."; 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) => { return new Promise((resolve, reject) => { glob(`${path}/**/*.${extension}`, (err, matches) => { if (err) { reject(err); } resolve(matches); }); }); }; const getPuzzleFolders = () => { return fs .readdirSync(ROOT_PATH, { withFileTypes: true }) .filter((dirEntry) => dirEntry.isDirectory()) .filter( (dirEntry) => ![".git", "node_modules", "00_Utilities", "buildJvm"].includes(dirEntry.name) ) .map((dirEntry) => dirEntry.name); }; (async () => { const result = {}; if (process.argv.length > 2) { languages = languages.filter((language) => process.argv.slice(2).includes(language.name)); } for (const { name: language } of languages) { result[language] = []; } const puzzleFolders = getPuzzleFolders(); for (const puzzleFolder of puzzleFolders) { for (const { name: language, extension } of languages) { const files = await getFilesRecursive( `${ROOT_PATH}/${puzzleFolder}/${language}`, extension ); if (files.length === 0) { result[language].push(puzzleFolder); } } } console.log('Unimplementation by language:') console.dir(result); })(); return; ================================================ FILE: 00_Utilities/javascript/style_terminal.css ================================================ /** * Old school terminal style * * @see https://codepen.io/cvan/pen/Zarmry * @see http://aleclownes.com/2017/02/01/crt-display.html */ /* define the main color theme */ :root { --background: rgb(1, 24, 1); --text: rgb(51, 251, 51); --input: yellow; --font: 600 1rem/1.3 Consolas, Andale Mono, monospace; } /* reset some styles */ * { box-sizing: border-box; margin: 0; padding: 0; } html { font-size: 16px; } header { margin-bottom: 2rem; } body { background-color: var(--background); color: var(--text); font: var(--font); margin: 0; } #output { padding: 1rem; } /* format input fields */ /* TODO: a simple blinking cursor would even better than this! */ input { display: inline-block; position: relative; caret-color: var(--input); background: none; border: none; outline: none; border-bottom: 1px solid var(--input); border-style: none none dashed none; color: var(--input); font: 900 1rem/1.3 Consolas, Andale Mono, monospace; animation: textShadow 1.6s infinite; text-transform: uppercase; } /* format the link list */ ul { margin: 0; list-style: none; } li { margin: 0; } /* format the sub-lists */ ul > li > ul { margin-left: 1.75rem; } ul > li > ul > li::before { content: "├── "; } ul > li > ul > li:last-child::before { content: "└── "; } /* overwrite the default (blue) link styles */ a, a:visited { color: var(--text); text-decoration: none; } a:hover { text-decoration: underline; } /* add all the face flicker effects (only on desktop) */ @media not screen and (max-width: 960px) and (prefers-reduced-motion) { main { padding: 3rem; } #output::before { content: " "; display: block; position: fixed; top: 0; left: 0; bottom: 0; right: 0; background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.25) 50%), linear-gradient(90deg, rgba(255, 0, 0, 0.06), rgba(0, 255, 0, 0.02), rgba(0, 0, 255, 0.06)); z-index: 2; background-size: 100% 2px, 3px 100%; pointer-events: none; } #output::after { content: " "; display: block; position: fixed; top: 0; left: 0; bottom: 0; right: 0; background: rgba(18, 16, 16, 0.1); opacity: 0; z-index: 2; pointer-events: none; } } ================================================ FILE: 00_Utilities/jvmTestUtils/kotlin/test/ConsoleTest.kt ================================================ package com.pcholt.console.testutils import com.google.common.truth.Truth import org.junit.Rule import org.junit.contrib.java.lang.system.SystemOutRule import org.junit.contrib.java.lang.system.TextFromStandardInputStream abstract class ConsoleTest { @get:Rule val inputRule = TextFromStandardInputStream.emptyStandardInputStream() @get:Rule val systemOutRule = SystemOutRule().enableLog() val regexInputCommand = "\\{(.*)}".toRegex() fun assertConversation(conversation: String, runMain: () -> Unit) { inputRule.provideLines(*regexInputCommand .findAll(conversation) .map { it.groupValues[1] } .toList().toTypedArray()) runMain() Truth.assertThat( systemOutRule.log.trimWhiteSpace() ) .isEqualTo( regexInputCommand .replace(conversation, "").trimWhiteSpace() ) } private fun String.trimWhiteSpace() = replace("[\\s]+".toRegex(), " ") } ================================================ FILE: 00_Utilities/markdown_todo.py ================================================ import os from typing import Dict, List def has_implementation(lang: str, file_list: List[str], subdir_list: List[str]) -> bool: if lang == "csharp": return any(file.endswith(".cs") for file in file_list) elif lang == "vbnet": return any(file.endswith(".vb") for file in file_list) else: return len(file_list) > 1 or len(subdir_list) > 0 def get_data(checklist_orig: List[str], root_dir: str = "..") -> List[List[str]]: """ Parameters ---------- root_dir : str The root directory you want to start from. """ lang_pos: Dict[str, int] = { lang: i for i, lang in enumerate(checklist_orig[1:], start=1) } strings_done: List[List[str]] = [] ignore_folders = [ ".git", "00_Utilities", ".github", ".mypy_cache", ".pytest_cache", "00_Alternate_Languages", "00_Common", "buildJvm", "htmlcov", ] prev_game = "" empty_boxes = ["⬜️" for _ in checklist_orig] checklist = empty_boxes[:] for dir_name, subdir_list, file_list in sorted(os.walk(root_dir)): # split_dir[1] is the game # split_dir[2] is the language split_dir = dir_name.split(os.path.sep) if len(split_dir) == 2 and split_dir[1] not in ignore_folders: if prev_game == "": prev_game = split_dir[1] checklist[0] = f"{split_dir[1]:<30}" if prev_game != split_dir[1]: # it's a new dir strings_done.append(checklist) checklist = [ f"{f'[{split_dir[1]}](../{split_dir[1]})':<30}", ] + empty_boxes[1:] prev_game = split_dir[1] elif ( len(split_dir) == 3 and split_dir[1] != ".git" and split_dir[2] in lang_pos ): out = ( "✅" if has_implementation(split_dir[2], file_list, subdir_list) else "⬜️" ) if split_dir[2] not in lang_pos or lang_pos[split_dir[2]] >= len(checklist): print(f"Could not find {split_dir[2]}: {dir_name}") checklist[lang_pos[split_dir[2]]] = "⬜️" continue checklist[lang_pos[split_dir[2]]] = out return strings_done def write_file(path: str, languages: List[str], strings_done: List[List[str]]) -> None: dashes_arr = ["---"] * (len(languages) + 1) dashes_arr[0] = "-" * 30 dashes = " | ".join(dashes_arr) write_string = f"# TODO list\n {'game':<30}| {' | '.join(languages)}\n{dashes}\n" sorted_strings = list( map(lambda l: " | ".join(l) + "\n", sorted(strings_done, key=lambda x: x[0])) ) write_string += "".join(sorted_strings) write_string += f"{dashes}\n" language_indices = range(1, len(languages) + 1) nb_games = len(strings_done) write_string += ( f"{f'Sum of {nb_games}':<30} | " + " | ".join( [ f"{sum(row[lang] == '✅' for row in strings_done)}" for lang in language_indices ] ) + "\n" ) with open(path, "w", encoding="utf-8") as f: f.write(write_string) if __name__ == "__main__": languages = { "csharp": "C#", "java": "Java", "javascript": "JS", "kotlin": "Kotlin", "lua": "Lua", "perl": "Perl", "python": "Python", "ruby": "Ruby", "rust": "Rust", "vbnet": "VB.NET", } strings_done = get_data(["game"] + list(languages.keys())) write_file("TODO.md", list(languages.values()), strings_done) ================================================ FILE: 00_Utilities/markdown_todo_rust/Cargo.toml ================================================ [package] name = "markdown_todo_rust" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] ================================================ FILE: 00_Utilities/markdown_todo_rust/src/main.rs ================================================ use std::ffi::OsStr; use std::{fs, io}; use std::fs::metadata; use std::path::{Path, PathBuf}; /** * todo list generator for this repository, coded in rust * * @author Anthony Rubick */ //DATA const ROOT_DIR: &str = "../../"; const LANGUAGES: [(&str,&str); 10] = [ //first element of tuple is the language name, second element is the file extension ("csharp", "cs"), ("java", "java"), ("javascript", "html"), ("kotlin", "kt"), ("lua", "lua"), ("perl", "pl"), ("python", "py"), ("ruby", "rb"), ("rust", "rs"), ("vbnet", "vb") ]; const OUTPUT_PATH: &str = "../../todo.md"; fn main() { //DATA let mut root_folders:Vec; let mut output_string: String = String::new(); let format_game_first: bool; let ingore: [PathBuf;5] = [ PathBuf::from(r"../../.git"), PathBuf::from(r"../../.github"), PathBuf::from(r"../../00_Alternate_Languages"), PathBuf::from(r"../../00_Utilities"), PathBuf::from(r"../../00_Common"), ]; //folders to ignore //print welcome message println!(" Markdown TODO list maker by Anthony Rubick for the basic-computer-games repo "); //ask user how they want the todo list formatted format_game_first = get_yn_from_user("\n\t---====FORMATS====---\ngame first:\n\tGame\n\t\tLanguage ✅/⬜️\n\nlang first:\n\tLanguage\n\t\tGame ✅/⬜️\n\nmake todo list using the game first format? (y/n | default No) "); //get all folders in ROOT_DIR root_folders = Vec::new(); match fs::read_dir(ROOT_DIR) { Err(why) => { println!("! {:?}", why.kind()); panic!("error"); }, Ok(paths) => { for path in paths { let full_path = path.unwrap().path(); if metadata(&full_path).unwrap().is_dir() { root_folders.push(full_path); } } }, } //for i in root_folders {println!("> {:?}", &i);} //for all folders, search for the languages and extensions root_folders = root_folders.into_iter().filter(|path| { //not one of the ignored folders !ingore.contains(path) }).collect(); root_folders.sort(); //create todo list if format_game_first { //being forming output string // for every game // every language + ✅/⬜️ for g in root_folders.into_iter() { //DATA let mut num_done:u8 = 0; //number of languages implemented let mut total:u8 = 0; //number of languages let game = g.clone().into_os_string().into_string().unwrap().chars().filter(|c| !(*c=='.' || *c=='/')).collect::();//get the game name //find all the lanuages the game is coded in let languages_list_for_game = { let mut s:String = String::new(); //every language + ✅/⬜️ LANGUAGES.iter().for_each(|lang| { s+="- "; s += lang.0; // + ✅/⬜️ let paths:Vec<_> = list_files(&g).into_iter().map(|path| path.into_os_string().into_string().unwrap()).collect(); let paths:Vec = paths.into_iter().filter_map(|s| { match Path::new(s.as_str()).extension().and_then(OsStr::to_str) { None => None, Some(s) => Some(s.to_string()), } }).collect(); //get all the extensions if paths.into_iter().any(|f| f.eq(lang.1)) {//list_files(&game).iter().map(|path| path.into_os_string().into_string().unwrap()).any(|s| LANGUAGES.iter().map(|tup| Some(tup.1)).collect::>().contains(&s.split('.').next())) { s+="✅"; num_done += 1; //increment number done } else { s += "⬜️"; } total += 1; //increment total s += "\n"; }); s }; //format message output_string += format!( "### {}\t{}\n\n{}\n", //message format game, format!("coded in {}/{} languages", num_done, total), languages_list_for_game, ).as_str(); } //print the whole list println!("\n\n{}", output_string); } else { //figure out what langauges the user wants printed //print language - extension table println!("\n\t---====LANGUAGES TO PRINT====---\na complete todo list will be output to todo-list.md\n"); print_lang_extension_table(); //prompt user to input the file extensions of the languages they want the todo-lists for printed to console //parse input for valid languages, store them in langs_to_print let langs_to_print = get_str_from_user("\nenter the file extensions, from the table above,\nfor the languages you want printed to standard output\nFile extensions: (separated by spaces)") .chars().filter(|c| c.is_ascii_alphabetic() || c.is_whitespace()).collect::();//filter out everything but ascii letters and spaces let langs_to_print: Vec<_> = langs_to_print.split(' ')//create an iterator with all the words .filter(|s| LANGUAGES.iter().any(|tup| tup.1.eq_ignore_ascii_case(*s))) //filter out words that aren't valid extensions (in languages) .collect(); //collect words into vector println!("\nwill print: {:#?}\n\n", langs_to_print); //being forming output string // for every language // every game + ✅/⬜️ for lang in LANGUAGES.iter() { //DATA let mut num_done = 0; let mut total = 0; let games_list_for_language = { //list //DATA let mut s:String = String::new(); //go through every game and check if it's coded in the language for g in (&root_folders).into_iter() { //data let game = g.clone(); let game_name = game.into_os_string().into_string().unwrap().chars().filter(|c| !(*c=='.' || *c=='/')).collect::(); //get the game name let paths:Vec<_> = list_files(g).into_iter().map(|path| path.into_os_string().into_string().unwrap()).collect(); //all subpaths of game let paths:Vec = paths.into_iter().filter_map(|s| { match Path::new(s.as_str()).extension().and_then(OsStr::to_str) { None => None, Some(s) => Some(s.to_string()), } }).collect(); //get all the extensions //add game name s+="- "; s+= game_name.as_str(); //every game + ✅/⬜️ if paths.into_iter().any(|f| f.eq(lang.1)) { s+="✅"; num_done += 1; //increment num done } else { s += "⬜️"; } total += 1; //increment total //new line s += "\n"; } //print desired languages only, what's output to std_out, also not markdown formatted if langs_to_print.contains(&lang.1) { print!("### {}\t{} ###\n{}\n\n", lang.0, format!("{}/{} games ported", num_done, total), s); } s }; //format message, what's output to the file output_string += format!( "### {}\t{}\n\n{}\n\n", //message format lang.0, //language format!("{}/{} games ported", num_done, total),//number done / total number games_list_for_language, ).as_str(); } } //write output to file fs::write(OUTPUT_PATH, output_string).expect("failed to write todo list"); } /** * print language - extension table */ fn print_lang_extension_table() { println!("LANGUAGE\tFILE EXTENSION"); println!("========\t=============="); for tup in LANGUAGES.iter() { match tup.0 { "javascript" => println!("{}\t{}", tup.0,tup.1), //long ones _ => println!("{}\t\t{}", tup.0,tup.1), }; } } /** * gets a string from user input */ fn get_str_from_user(prompt:&str) -> String { //DATA let mut raw_input = String::new(); //print prompt println!("{}",prompt); //get input and trim whitespaces io::stdin().read_line(&mut raw_input).expect("Failed to read input"); //return raw input return raw_input.trim().to_string(); } /** * gets a boolean from user */ fn get_yn_from_user(prompt:&str) -> bool { //DATA let input = get_str_from_user(prompt); //default in case of error if input.is_empty() {return false;} //get and parse input match &input[0..1] { //get first character "y" | "Y" => return true, _ => return false, } } /** * returns a vector containing paths to all files in path and subdirectories of path */ fn list_files(path: &Path) -> Vec { let mut vec = Vec::new(); _list_files(&mut vec,&path); vec } fn _list_files(vec: &mut Vec, path: &Path) { if metadata(&path).unwrap().is_dir() { let paths = fs::read_dir(&path).unwrap(); for path_result in paths { let full_path = path_result.unwrap().path(); if metadata(&full_path).unwrap().is_dir() { _list_files(vec, &full_path); } else { vec.push(full_path); } } } } ================================================ FILE: 00_Utilities/python/ci-requirements.in ================================================ flake8 flake8-bugbear flake8-comprehensions flake8_implicit_str_concat mypy ================================================ FILE: 00_Utilities/python/ci-requirements.txt ================================================ # # This file is autogenerated by pip-compile with python 3.10 # To update, run: # # pip-compile ci-requirements.in # attrs==20.3.0 # via # flake8-bugbear # flake8-implicit-str-concat flake8==4.0.1 # via # -r ci-requirements.in # flake8-bugbear # flake8-comprehensions flake8-bugbear==22.1.11 # via -r ci-requirements.in flake8-comprehensions==3.8.0 # via -r ci-requirements.in flake8-implicit-str-concat==0.2.0 # via -r ci-requirements.in mccabe==0.6.1 # via flake8 more-itertools==8.12.0 # via flake8-implicit-str-concat mypy==0.931 # via -r ci-requirements.in mypy-extensions==0.4.3 # via mypy pycodestyle==2.8.0 # via flake8 pyflakes==2.4.0 # via flake8 tomli==2.0.1 # via mypy typing-extensions==4.1.1 # via mypy ================================================ FILE: 00_Utilities/yatol.pl ================================================ #!/usr/bin/perl #YATOL: Yet Another TOdo List use strict; #REM: Get list of basic files ordered by number of lines. #REM: This way you can do the easier ones first. my @Ret=`find .. -iname '*.bas' -exec wc -l \{\} \\; | sort -h`; my @Langs= qw(PL JS VB PAS RB C# JAVA PY); my @Dirs= qw(perl javascript vbnet pascal ruby csharp java python); my %Sum; my $Row=25; my $Tab=7; printf("%-$Row\s", "PATH"); printf("%$Tab\s", "BAS"); foreach my $Dir (@Langs) { printf("%$Tab\s", $Dir); } print "\n"; my $Count; foreach my $Lin (@Ret) { $Count++; chomp $Lin; my ($Num, $File)= split (" ", $Lin); my @Parts= split(/\//, $File); my $Base= $Parts[1]; printf("%-$Row\s", $Base); printf("%$Tab\s", "$Num"); foreach my $Dir (@Dirs) { my $Path= "../$Base/$Dir/"; my $Ret= `ls $Path | wc -l`; if ($Ret>1) { printf("%$Tab\s", "YES"); $Sum{$Dir}++; } else { printf("%$Tab\s", " ");} } print "\n"; } printf("%$Row\s", "FILES:"); printf("%$Tab\s", " "); foreach my $Dir (@Dirs) { printf("%$Tab\s", "$Sum{$Dir}"); } print "\n"; printf("%$Row\s", "ADVANCE:"); printf("%$Tab\s", " "); foreach my $Dir (@Dirs) { my $Per= int($Sum{$Dir}/$Count*100)."%"; printf("%$Tab\s", "$Per"); } print "\n"; ================================================ FILE: 01_Acey_Ducey/README.md ================================================ ### Acey Ducey This is a simulation of the Acey Ducey card game. In the game, the dealer (the computer) deals two cards face up. You have an option to bet or not to bet depending on whether or not you feel the next card dealt will have a value between the first two. Your initial money is set to $100; you may want to alter this value if you want to start with more or less than $100. The game keeps going on until you lose all your money or interrupt the program. The original program author was Bill Palmby of Prairie View, Illinois. --- As published in Basic Computer Games (1978): - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=2) - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=17) Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html #### Known Bugs - Entering a negative bet allows you to gain arbitrarily large amounts of money upon losing the round. #### Porting Notes - 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 - PowerShell: https://github.com/eweilnau/basic-computer-games-powershell/blob/main/AceyDucey.ps1 ================================================ FILE: 01_Acey_Ducey/aceyducey.bas ================================================ 10 PRINT TAB(26);"ACEY DUCEY CARD GAME" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 21 PRINT 22 PRINT 30 PRINT"ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER " 40 PRINT"THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP" 50 PRINT"YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING" 60 PRINT"ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE" 70 PRINT"A VALUE BETWEEN THE FIRST TWO." 80 PRINT"IF YOU DO NOT WANT TO BET, INPUT A 0" 100 N=100 110 Q=100 120 PRINT "YOU NOW HAVE ";Q;" DOLLARS." 130 PRINT 140 GOTO 260 210 Q=Q+M 220 GOTO 120 240 Q=Q-M 250 GOTO 120 260 PRINT"HERE ARE YOUR NEXT TWO CARDS: " 270 A=INT(14*RND(1))+2 280 IF A<2 THEN 270 290 IF A>14 THEN 270 300 B=INT(14*RND(1))+2 310 IF B<2 THEN 300 320 IF B>14 THEN 300 330 IF A>=B THEN 270 350 IF A<11 THEN 400 360 IF A=11 THEN 420 370 IF A=12 THEN 440 380 IF A=13 THEN 460 390 IF A=14 THEN 480 400 PRINT A 410 GOTO 500 420 PRINT"JACK" 430 GOTO 500 440 PRINT"QUEEN" 450 GOTO 500 460 PRINT"KING" 470 GOTO 500 480 PRINT"ACE" 500 IF B<11 THEN 550 510 IF B=11 THEN 570 520 IF B=12 THEN 590 530 IF B=13 THEN 610 540 IF B=14 THEN 630 550 PRINT B 560 GOTO 650 570 PRINT"JACK" 580 GOTO 650 590 PRINT"QUEEN" 600 GOTO 650 610 PRINT"KING" 620 GOTO 650 630 PRINT"ACE" 640 PRINT 650 PRINT 660 INPUT"WHAT IS YOUR BET";M 670 IF M<>0 THEN 680 675 PRINT"CHICKEN!!" 676 PRINT 677 GOTO 260 680 IF M<=Q THEN 730 690 PRINT"SORRY, MY FRIEND, BUT YOU BET TOO MUCH." 700 PRINT"YOU HAVE ONLY ";Q;" DOLLARS TO BET." 710 GOTO 650 730 C=INT(14*RND(1))+2 740 IF C<2 THEN 730 750 IF C>14 THEN 730 760 IF C<11 THEN 810 770 IF C=11 THEN 830 780 IF C=12 THEN 850 790 IF C=13 THEN 870 800 IF C=14 THEN 890 810 PRINT C 820 GOTO 910 830 PRINT"JACK" 840 GOTO 910 850 PRINT"QUEEN" 860 GOTO 910 870 PRINT"KING" 880 GOTO 910 890 PRINT "ACE" 900 PRINT 910 IF C>A THEN 930 920 GOTO 970 930 IF C>=B THEN 970 950 PRINT"YOU WIN!!!" 960 GOTO 210 970 PRINT"SORRY, YOU LOSE" 980 IF M Exe netcoreapp3.1 ================================================ FILE: 01_Acey_Ducey/csharp/AceyDucey.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30709.132 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AceyDucey", "AceyDucey.csproj", "{A8FE2F04-3DFE-4254-BD5B-281DC8E30B1C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {A8FE2F04-3DFE-4254-BD5B-281DC8E30B1C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A8FE2F04-3DFE-4254-BD5B-281DC8E30B1C}.Debug|Any CPU.Build.0 = Debug|Any CPU {A8FE2F04-3DFE-4254-BD5B-281DC8E30B1C}.Release|Any CPU.ActiveCfg = Release|Any CPU {A8FE2F04-3DFE-4254-BD5B-281DC8E30B1C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {F620B340-EB7A-48C1-B12F-9D3D94AE7771} EndGlobalSection EndGlobal ================================================ FILE: 01_Acey_Ducey/csharp/Game.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace AceyDucey { /// /// The main class that implements all the game logic /// internal class Game { /// /// Our Random number generator object /// private Random Rnd { get; } = new Random(); /// /// A line of underscores that we'll print between turns to separate them from one another on screen /// private string SeparatorLine { get; } = new string('_', 70); /// /// Main game loop function. This will play the game endlessly until the player chooses to quit. /// internal void GameLoop() { // First display instructions to the player DisplayIntroText(); // We'll loop for each game until the player decides not to continue do { // Play a game! PlayGame(); // Play again? } while (TryAgain()); } /// /// Play the game /// private void PlayGame() { GameState state = new GameState(); // Clear the display Console.Clear(); // Keep looping until the player has no money left do { // Play the next turn. Pass in our state object so the turn can see the money available, // can update it after the player makes a bet, and can update the turn count. PlayTurn(state); // Keep looping until the player runs out of money } while (state.Money > 0); // Looks like the player is bankrupt, let them know how they did. Console.ForegroundColor = ConsoleColor.White; Console.WriteLine(""); Console.WriteLine($"Sorry, friend, but you blew your wad. Your game is over after {state.TurnCount} {(state.TurnCount == 1 ? "turn" : "turns")}. Your highest balance was ${state.MaxMoney}."); } /// /// Play a turn /// /// The current game state private void PlayTurn(GameState state) { // Let the player know what's happening Console.WriteLine(""); Console.WriteLine(SeparatorLine); Console.WriteLine(""); Console.ForegroundColor = ConsoleColor.White; Console.WriteLine(""); Console.WriteLine("Here are your next two cards:"); // Generate two random cards int firstCard = GetCard(); int secondCard = GetCard(); // If the second card is lower than the first card, swap them over if (secondCard < firstCard) { (firstCard, secondCard) = (secondCard, firstCard); } // Display the cards DisplayCard(firstCard); DisplayCard(secondCard); // Ask the player what they want to do Console.ForegroundColor = ConsoleColor.White; Console.WriteLine(""); Console.Write("You currently have "); Console.ForegroundColor = ConsoleColor.Yellow; Console.Write($"${state.Money}"); Console.ForegroundColor = ConsoleColor.White; Console.WriteLine(". How much would you like to bet?"); // Read the bet amount int betAmount = PlayTurn_GetBetAmount(state.Money); // Display a summary of their inpout Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine(""); Console.WriteLine($"You choose to {(betAmount == 0 ? "pass" : $"bet {betAmount}")}."); // Generate and display the final card Console.ForegroundColor = ConsoleColor.White; Console.WriteLine(""); Console.WriteLine("The next card is:"); int thirdCard = GetCard(); DisplayCard(thirdCard); Console.WriteLine(""); // Was the third card between the first two cards? if (thirdCard > firstCard && thirdCard < secondCard) { // It was! Inform the player and add to their money Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine("You win!"); if (betAmount == 0) { Console.WriteLine("(It's just a shame you chose not to bet!)"); } else { state.Money += betAmount; // If their money exceeds the MaxMoney, update that too state.MaxMoney = Math.Max(state.Money, state.MaxMoney); } } else { // Oh dear, the player lost. Let them know the bad news and take their bet from their money Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine("You lose!"); if (betAmount == 0) { Console.WriteLine("(It's lucky you chose not to bet!)"); } else { state.Money -= betAmount; } } Console.ForegroundColor = ConsoleColor.White; Console.Write("You now have "); Console.ForegroundColor = ConsoleColor.Yellow; Console.Write($"${state.Money}"); Console.ForegroundColor = ConsoleColor.White; Console.WriteLine("."); // Update the turn count now that another turn has been played state.TurnCount += 1; // Ready for the next turn... Console.ForegroundColor = ConsoleColor.DarkGreen; Console.WriteLine(""); Console.WriteLine("Press any key to continue..."); Console.ReadKey(true); } /// /// Prompt the user for their bet amount and validate their input /// /// The player's current money /// Returns the amount the player chooses to bet private int PlayTurn_GetBetAmount(int currentMoney) { int betAmount; // Loop until the user enters a valid value do { // Move this to a separate function... Console.ForegroundColor = ConsoleColor.Yellow; Console.Write("> $"); string input = Console.ReadLine(); // Is this a valid number? if (!int.TryParse(input, out betAmount)) { // No Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("Sorry, I didn't understand. Please enter how much you would like to bet."); // Continue looping continue; } // If the amount between 0 and their available money? if (betAmount < 0 || betAmount > currentMoney) { // No Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine($"Please enter a bet amount between $0 and ${currentMoney}."); // Continue looping continue; } // We have a valid bet, stop looping break; } while (true); // Return whatever the player entered return betAmount; } /// /// Generate a new random card. /// /// Will return a value between 2 and 14, inclusive. /// Values 2 to 10 are their face values. 11 represents a Jack, 12 is a Queen, 13 a King and 14 an Ace. /// Even though this is a slightly offset sequence, it allows us to perform a simple greater-than/less-than /// comparison with the card values, treating an Ace as a high card. private int GetCard() { return Rnd.Next(2, 15); } /// /// Display the card number on screen, translating values 11 through to 14 into their named equivalents. /// /// private void DisplayCard(int card) { string cardText; switch (card) { case 11: cardText = "Jack"; break; case 12: cardText = "Queen"; break; case 13: cardText = "King"; break; case 14: cardText = "Ace"; break; default: cardText = card.ToString(); break; } // Format as black text on a white background Console.Write(" "); Console.BackgroundColor = ConsoleColor.White; Console.ForegroundColor = ConsoleColor.Black; Console.Write($" {cardText} "); Console.BackgroundColor = ConsoleColor.Black; Console.ForegroundColor = ConsoleColor.White; Console.WriteLine(""); } /// /// Display instructions on how to play the game and wait for the player to press a key. /// private void DisplayIntroText() { Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine("Acey Ducey Gard Game."); Console.WriteLine("Creating Computing, Morristown, New Jersey."); Console.WriteLine(""); Console.ForegroundColor = ConsoleColor.DarkGreen; Console.WriteLine("Originally published in 1978 in the book 'Basic Computer Games' by David Ahl."); Console.WriteLine("Modernised and converted to C# in 2021 by Adam Dawes (@AdamDawes575)."); Console.WriteLine(""); Console.ForegroundColor = ConsoleColor.Gray; Console.WriteLine("Acey Ducey is played in the following manner:"); Console.WriteLine(""); Console.WriteLine("The dealer (computer) deals two cards, face up."); Console.WriteLine(""); Console.WriteLine("You have an option to bet or pass, depending on whether or not you feel the next card will have a value between the"); Console.WriteLine("first two."); Console.WriteLine(""); Console.WriteLine("If the card is between, you will win your stake, otherwise you will lose it. Ace is 'high' (higher than a King)."); Console.WriteLine(""); Console.WriteLine("If you want to pass, enter a bet amount of $0."); Console.WriteLine(""); Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine("Press any key start the game."); Console.ReadKey(true); } /// /// Prompt the player to try again, and wait for them to press Y or N. /// /// Returns true if the player wants to try again, false if they have finished playing. private bool TryAgain() { Console.ForegroundColor = ConsoleColor.White; Console.WriteLine("Would you like to try again? (Press 'Y' for yes or 'N' for no)"); Console.ForegroundColor = ConsoleColor.Yellow; Console.Write("> "); char pressedKey; // Keep looping until we get a recognised input do { // Read a key, don't display it on screen ConsoleKeyInfo key = Console.ReadKey(true); // Convert to upper-case so we don't need to care about capitalisation pressedKey = Char.ToUpper(key.KeyChar); // Is this a key we recognise? If not, keep looping } while (pressedKey != 'Y' && pressedKey != 'N'); // Display the result on the screen Console.WriteLine(pressedKey); // Return true if the player pressed 'Y', false for anything else. return (pressedKey == 'Y'); } } } ================================================ FILE: 01_Acey_Ducey/csharp/GameState.cs ================================================ using System; using System.Collections.Generic; using System.Text; namespace AceyDucey { /// /// The GameState class keeps track of all the game variables while the game is being played /// internal class GameState { /// /// How much money does the player have at the moment? /// internal int Money { get; set; } /// /// What's the highest amount of money they had at any point in the game? /// internal int MaxMoney { get; set; } /// /// How many turns have they played? /// internal int TurnCount { get; set; } /// /// Class constructor -- initialise all values to their defaults. /// internal GameState() { // Setting Money to 100 gives the player their starting balance. Changing this will alter how much they have to begin with. Money = 100; MaxMoney = Money; TurnCount = 0; } } } ================================================ FILE: 01_Acey_Ducey/csharp/Program.cs ================================================ using System; using System.Threading; namespace AceyDucey { /// /// The application's entry point /// class Program { /// /// This function will be called automatically when the application begins /// /// static void Main(string[] args) { // Create an instance of our main Game class Game game = new Game(); // Call its GameLoop function. This will play the game endlessly in a loop until the player chooses to quit. game.GameLoop(); } } } ================================================ FILE: 01_Acey_Ducey/csharp/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/) by Adam Dawes (@AdamDawes575, https://adamdawes.com). ================================================ FILE: 01_Acey_Ducey/elixir/acey_ducey.exs ================================================ ######################################################## # # 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}) ================================================ FILE: 01_Acey_Ducey/java/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Oracle Java](https://openjdk.java.net/) Two versions of Acey Ducey have been contributed. The original upload supported JDK 8/JDK 11 and uses multiple files and the second uses features in JDK 17 and is implemented in a single file AceyDucey17.java. Both are in the src folder. ================================================ FILE: 01_Acey_Ducey/java/src/AceyDucey.java ================================================ import java.util.Scanner; /** * Game of AceyDucey *

    * Based on the Basic game of AceyDucey here * https://github.com/coding-horror/basic-computer-games/blob/main/01%20Acey%20Ducey/aceyducey.bas * Note: The idea was to create a version of the 1970's Basic game in Java, without introducing * new features - no additional text, error checking, etc has been added. */ public class AceyDucey { // Current amount of players cash private int playerAmount; // First drawn dealer card private Card firstCard; // Second drawn dealer card private Card secondCard; // Players drawn card private Card playersCard; // User to display game intro/instructions private boolean firstTimePlaying = true; // game state to determine if game over private boolean gameOver = false; // Used for keyboard input private final Scanner kbScanner; // Constant value for cards from a deck - 2 lowest, 14 (Ace) highest public static final int LOW_CARD_RANGE = 2; public static final int HIGH_CARD_RANGE = 14; public AceyDucey() { // Initialise players cash playerAmount = 100; // Initialise kb scanner kbScanner = new Scanner(System.in); } // Play again method - public method called from class invoking game // If player enters YES then the game can be played again (true returned) // otherwise not (false) public boolean playAgain() { System.out.println(); System.out.println("SORRY, FRIEND, BUT YOU BLEW YOUR WAD."); System.out.println(); System.out.println(); System.out.print("TRY AGAIN (YES OR NO) "); String playAgain = kbScanner.next().toUpperCase(); System.out.println(); System.out.println(); if (playAgain.equals("YES")) { return true; } else { System.out.println("O.K., HOPE YOU HAD FUN!"); return false; } } // game loop method public void play() { // Keep playing hands until player runs out of cash do { if (firstTimePlaying) { intro(); firstTimePlaying = false; } displayBalance(); drawCards(); displayCards(); int betAmount = getBet(); playersCard = randomCard(); displayPlayerCard(); if (playerWon()) { System.out.println("YOU WIN!!"); playerAmount += betAmount; } else { System.out.println("SORRY, YOU LOSE"); playerAmount -= betAmount; // Player run out of money? if (playerAmount <= 0) { gameOver = true; } } } while (!gameOver); // Keep playing until player runs out of cash } // Method to determine if player won (true returned) or lost (false returned) // to win a players card has to be in the range of the first and second dealer // drawn cards inclusive of first and second cards. private boolean playerWon() { // winner return (playersCard.getValue() >= firstCard.getValue()) && playersCard.getValue() <= secondCard.getValue(); } private void displayPlayerCard() { System.out.println(playersCard.getName()); } // Get the players bet, and return the amount // 0 is considered a valid bet, but better more than the player has available is not // method will loop until a valid bet is entered. private int getBet() { boolean validBet = false; int amount; do { System.out.print("WHAT IS YOUR BET "); amount = kbScanner.nextInt(); if (amount == 0) { System.out.println("CHICKEN!!"); validBet = true; } else if (amount > playerAmount) { System.out.println("SORRY, MY FRIEND, BUT YOU BET TOO MUCH."); System.out.println("YOU HAVE ONLY " + playerAmount + " DOLLARS TO BET."); } else { validBet = true; } } while (!validBet); return amount; } private void displayBalance() { System.out.println("YOU NOW HAVE " + playerAmount + " DOLLARS."); } private void displayCards() { System.out.println("HERE ARE YOUR NEXT TWO CARDS: "); System.out.println(firstCard.getName()); System.out.println(secondCard.getName()); } // Draw two dealer cards, and save them for later use. // ensure that the first card is a smaller value card than the second one private void drawCards() { do { firstCard = randomCard(); secondCard = randomCard(); } while (firstCard.getValue() >= secondCard.getValue()); } // Creates a random card private Card randomCard() { return new Card((int) (Math.random() * (HIGH_CARD_RANGE - LOW_CARD_RANGE + 1) + LOW_CARD_RANGE)); } public void intro() { System.out.println("ACEY DUCEY CARD GAME"); System.out.println("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println(); System.out.println(); System.out.println("ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER"); System.out.println("THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP"); System.out.println("YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING"); System.out.println("ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE"); System.out.println("A VALUE BETWEEN THE FIRST TWO."); System.out.println("IF YOU DO NOT WANT TO BET, INPUT: 0"); } } ================================================ FILE: 01_Acey_Ducey/java/src/AceyDucey17.java ================================================ import java.util.Random; import java.util.Scanner; /** * A modern version (JDK17) of ACEY DUCEY using post Java 8 features. Notes * regarding new java features or differences in the original basic * implementation are numbered and at the bottom of this code. * The goal is to recreate the exact look and feel of the original program * minus a large glaring bug in the original code that lets you cheat. */ public class AceyDucey17 { public static void main(String[] args) { // notes [1] System.out.println(""" ACEY DUCEY CARD GAME CREATIVE COMPUTING MORRISTOWN, NEW JERSEY 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"""); do { playGame(); } while (stillInterested()); System.out.println("O.K., HOPE YOU HAD FUN!"); } public static void playGame() { int cashOnHand = 100; // our only mutable variable note [11] System.out.println("YOU NOW HAVE "+ cashOnHand +" DOLLARS.");// note [6] while (cashOnHand > 0) { System.out.println(); System.out.println("HERE ARE YOUR NEXT TWO CARDS:"); final Card lowCard = Card.getRandomCard(2, Card.KING); //note [3] System.out.println(lowCard); final Card highCard = Card.getRandomCard(lowCard.rank() + 1, Card.ACE); System.out.println(highCard); final int bet = getBet(cashOnHand); final int winnings = determineWinnings(lowCard,highCard,bet); cashOnHand += winnings; if(winnings != 0 || cashOnHand != 0){ //note [2] System.out.println("YOU NOW HAVE "+ cashOnHand +" DOLLARS.");//note [6] } } } public static int determineWinnings(Card lowCard, Card highCard, int bet){ if (bet <= 0) { // note [5] System.out.println("CHICKEN!!"); return 0; } Card nextCard = Card.getRandomCard(2, Card.ACE); System.out.println(nextCard); if(nextCard.between(lowCard,highCard)){ System.out.println("YOU WIN!!!"); return bet; } System.out.println("SORRY, YOU LOSE"); return -bet; } public static boolean stillInterested(){ System.out.println(); System.out.println(); System.out.println("SORRY, FRIEND, BUT YOU BLEW YOUR WAD."); System.out.println(); System.out.println(); System.out.print("TRY AGAIN (YES OR NO)? "); Scanner input = new Scanner(System.in); boolean playAgain = input.nextLine() .toUpperCase() .startsWith("Y"); // note [9] System.out.println(); System.out.println(); return playAgain; } public static int getBet(int cashOnHand){ int bet; do{ System.out.println(); System.out.print("WHAT IS YOUR BET? "); bet = inputNumber(); if (bet > cashOnHand) { System.out.println("SORRY, MY FRIEND, BUT YOU BET TOO MUCH."); System.out.println("YOU HAVE ONLY "+cashOnHand+" DOLLARS TO BET."); } }while(bet > cashOnHand); return bet; } public static int inputNumber() { final Scanner input = new Scanner(System.in); // set to negative to mark as not entered yet in case of input error. int number = -1; while (number < 0) { try { number = input.nextInt(); } catch(Exception ex) { // note [7] System.out.println("!NUMBER EXPECTED - RETRY INPUT LINE"); System.out.print("? "); try{ input.nextLine(); } catch(Exception ns_ex){ // received EOF (ctrl-d or ctrl-z if windows) System.out.println("END OF INPUT, STOPPING PROGRAM."); System.exit(1); } } } return number; } record Card(int rank){ // Some constants to describe face cards. public static final int JACK = 11, QUEEN = 12, KING = 13, ACE = 14; private static final Random random = new Random(); public static Card getRandomCard(int from, int to){ return new Card(random.nextInt(from, to+1)); // note [4] } public boolean between(Card lower, Card higher){ return lower.rank() < this.rank() && this.rank() < higher.rank(); } @Override public String toString() { // note [13] return switch (rank) { case JACK -> "JACK"; case QUEEN -> "QUEEN"; case KING -> "KING"; case ACE -> "ACE\n"; // note [10] default -> " "+rank+" "; // note [6] }; } } /* Notes: 1. Multiline strings, a.k.a. text blocks, were added in JDK15. 2. The original game only displays the players balance if it changed, which it does not when the player chickens out and bets zero. It also doesn't display the balance when it becomes zero because it has a more appropriate message: Sorry, You Lose. 3. To pick two cards to show, the original BASIC implementation has a bug that could cause a race condition if the RND function never chose a lower number first and higher number second. It loops infinitely re-choosing random numbers until the condition is met of the first one being lower. The logic is changed a bit here so that the first card picked is anything but an ACE, the highest possible card, and then the second card is between the just picked first card upto and including the ACE. 4. Random.nextInt(origin, bound) was added in JDK17, and allows to directly pick a range for a random integer to be generated. The second parameter is exclusive of the range and thus why they are stated with +1's to the face card. 5. The original BASIC implementation has a bug that allows negative value bets. Since you can't bet MORE cash than you have you can always bet less including a very, very large negative value. You would do this when the chances of winning are slim or zero since losing a hand SUBTRACTS your bet from your cash; subtracting a negative number actually ADDS to your cash, potentially making you an instant billionaire. This loophole is now closed. 6. The subtle behavior of the BASIC PRINT command causes a space to be printed before all positive numbers as well as a trailing space. Any place a non-face card or the players balance is printed has extra space to mimic this behavior. 7. Errors on input were probably specific to the interpreter. This program tries to match the Vintage Basic interpreter's error messages. The final input.nextLine() command exists to clear the blockage of whatever non-number input was entered. But even that could fail if the user types Ctrl-D (windows Ctrl-Z), signifying an EOF (end of file) and thus the closing of STDIN channel. The original program on an EOF signal prints "END OF INPUT IN LINE 660" and thus we cover it roughly the same way. All of this is necessary to avoid a messy stack trace from being printed as the program crashes. 9. The original game only accepted a full upper case "YES" to continue playing if bankrupted. This program is more lenient and will accept any input that starts with the letter 'y', uppercase or not. 10. The original game prints an extra blank line if the card is an ACE. There is seemingly no rationale for this. 11. Modern java best practices are edging toward a more functional paradigm and as such, mutating state is discouraged. All other variables besides the cashOnHand are final and initialized only once. 12. Refactoring of the concept of a card is done with a record. Records were introduced in JDK14. Card functionality is encapsulated in this example of a record. An enum could be a better alternative since there are technically only 13 cards possible. 13. Switch expressions were introduced as far back as JDK12 but continue to be refined for clarity, exhaustiveness. As of JDK17 pattern matching for switch expressions can be accessed by enabling preview features. */ } ================================================ FILE: 01_Acey_Ducey/java/src/AceyDuceyGame.java ================================================ /** * This class is used to invoke the game. * */ public class AceyDuceyGame { public static void main(String[] args) { boolean keepPlaying; AceyDucey game = new AceyDucey(); // Keep playing game until infinity or the player loses do { game.play(); System.out.println(); System.out.println(); System.out.println(); keepPlaying = game.playAgain(); } while (keepPlaying); } } ================================================ FILE: 01_Acey_Ducey/java/src/Card.java ================================================ /** * A card from a deck - the value is between 2-14 to cover * cards with a face value of 2-9 and then a Jack, Queen, King, and Ace */ public class Card { private int value; private String name; Card(int value) { init(value); } private void init(int value) { this.value = value; if (value < 11) { this.name = String.valueOf(value); } else { switch (value) { case 11: this.name = "Jack"; break; case 12: this.name = "Queen"; break; case 13: this.name = "King"; break; case 14: this.name = "Ace"; break; default: this.name = "Unknown"; } } } public int getValue() { return value; } public String getName() { return name; } } ================================================ FILE: 01_Acey_Ducey/javascript/.prettierrc.json ================================================ { "trailingComma": "es5", "tabWidth": 4, "semi": true, "singleQuote": true } ================================================ FILE: 01_Acey_Ducey/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 01_Acey_Ducey/javascript/aceyducey.html ================================================ ACEY DUCEY

    
    
    
    
    ================================================
    FILE: 01_Acey_Ducey/javascript/aceyducey.js
    ================================================
    // UTILITY VARIABLES
    
    // By default:
    // — Browsers have a window object
    // — Node.js does not
    // Checking for an undefined window object is a loose check
    // to enable browser and Node.js support
    const isRunningInBrowser = typeof window !== 'undefined';
    
    // To easily validate input strings with utility functions
    const validLowerCaseYesStrings = ['yes', 'y'];
    const validLowerCaseNoStrings = ['no', 'n'];
    const validLowerCaseYesAndNoStrings = [
        ...validLowerCaseYesStrings,
        ...validLowerCaseNoStrings,
    ];
    // UTILITY VARIABLES
    
    // Function to get a random number (card) 2-14 (ACE is 14)
    function getRandomCard() {
        // In our game, the value of ACE is greater than face cards;
        // instead of having the value of ACE be 1, we’ll have it be 14.
        // So, we want to shift the range of random numbers from 1-13 to 2-14
        let min = 2;
        let max = 14;
        // Return random integer between two values, inclusive
        return Math.floor(Math.random() * (max - min + 1) + min);
    }
    
    function newGameCards() {
        let cardOne = getRandomCard();
        let cardTwo = getRandomCard();
        let cardThree = getRandomCard();
        // We want:
        // 1. cardOne and cardTwo to be different cards
        // 2. cardOne to be lower than cardTwo
        // So, while cardOne is greater than or equal too cardTwo
        // we will continue to generate random cards.
        while (cardOne >= cardTwo) {
            cardOne = getRandomCard();
            cardTwo = getRandomCard();
        }
        return [cardOne, cardTwo, cardThree];
    }
    
    // Function to get card value
    function getCardValue(card) {
        let faceOrAce = {
            11: 'JACK',
            12: 'QUEEN',
            13: 'KING',
            14: 'ACE',
        };
        // If card value matches a key in faceOrAce, use faceOrAce value;
        // Else, return undefined and handle with the Nullish Coalescing Operator (??)
        // and default to card value.
        let cardValue = faceOrAce[card] ?? card;
        return cardValue;
    }
    
    print(spaces(26) + 'ACEY DUCEY CARD GAME');
    print(spaces(15) + 'CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n\n');
    print('ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER');
    print('THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP');
    print('YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING');
    print('ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE');
    print('A VALUE BETWEEN THE FIRST TWO.');
    print("IF YOU DO NOT WANT TO BET, INPUT '0'");
    
    main();
    
    async function main() {
        let bet;
        let availableDollars = 100;
    
        // Loop game forever
        while (true) {
            let [cardOne, cardTwo, cardThree] = newGameCards();
    
            print(`YOU NOW HAVE ${availableDollars} DOLLARS.\n`);
    
            print('HERE ARE YOUR NEXT TWO CARDS: ');
            print(getCardValue(cardOne));
            print(getCardValue(cardTwo));
            print('');
    
            // Loop until receiving a valid bet
            let validBet = false;
            while (!validBet) {
                print('\nWHAT IS YOUR BET? ');
                bet = parseInt(await input(), 10);
                let minimumRequiredBet = 0;
                if (bet >= minimumRequiredBet) {
                    if (bet > availableDollars) {
                        print('SORRY, MY FRIEND, BUT YOU BET TOO MUCH.');
                        print(`YOU HAVE ONLY ${availableDollars} DOLLARS TO BET.`);
                    } else {
                        validBet = true;
                    }
                }
            }
            if (bet == 0)
            {
                // User chose not to bet.
                print('CHICKEN!!');
                print('');
                // Don't draw a third card, draw a new set of 2 cards.
                continue;
            }
    
            print('\n\nHERE IS THE CARD WE DREW: ');
            print(getCardValue(cardThree));
    
            // Determine if player won or lost
            if (cardThree > cardOne && cardThree < cardTwo) {
                print('YOU WIN!!!');
                availableDollars = availableDollars + bet;
            } else {
                print('SORRY, YOU LOSE');
    
                if (bet >= availableDollars) {
                    print('');
                    print('');
                    print('SORRY, FRIEND, BUT YOU BLEW YOUR WAD.');
                    print('');
                    print('');
                    print('TRY AGAIN (YES OR NO)');
    
                    let tryAgainInput = await input();
    
                    print('');
                    print('');
    
                    if (isValidYesString(tryAgainInput)) {
                        availableDollars = 100;
                    } else {
                        print('O.K., HOPE YOU HAD FUN!');
                        break;
                    }
                } else {
                    availableDollars = availableDollars - bet;
                }
            }
        }
    }
    
    // UTILITY FUNCTIONS
    function isValidYesNoString(string) {
        return validLowerCaseYesAndNoStrings.includes(string.toLowerCase());
    }
    
    function isValidYesString(string) {
        return validLowerCaseYesStrings.includes(string.toLowerCase());
    }
    
    function isValidNoString(string) {
        return validLowerCaseNoStrings.includes(string.toLowerCase());
    }
    
    function print(string) {
        if (isRunningInBrowser) {
            // Adds trailing newline to match console.log behavior
            document
                .getElementById('output')
                .appendChild(document.createTextNode(string + '\n'));
        } else {
            console.log(string);
        }
    }
    
    function input() {
        if (isRunningInBrowser) {
            // Accept input from the browser DOM input
            return new Promise((resolve) => {
                const outputElement = document.querySelector('#output');
                const inputElement = document.createElement('input');
                outputElement.append(inputElement);
                inputElement.focus();
    
                inputElement.addEventListener('keydown', (event) => {
                    if (event.key === 'Enter') {
                        const result = inputElement.value;
                        inputElement.remove();
                        print(result);
                        print('');
                        resolve(result);
                    }
                });
            });
        } else {
            // Accept input from the command line in Node.js
            // See: https://nodejs.dev/learn/accept-input-from-the-command-line-in-nodejs
            return new Promise(function (resolve) {
                const readline = require('readline').createInterface({
                    input: process.stdin,
                    output: process.stdout,
                });
                readline.question('', function (input) {
                    resolve(input);
                    readline.close();
                });
            });
        }
    }
    
    function printInline(string) {
        if (isRunningInBrowser) {
            document
                .getElementById('output')
                .appendChild(document.createTextNode(string));
        } else {
            process.stdout.write(string);
        }
    }
    
    function spaces(numberOfSpaces) {
        return ' '.repeat(numberOfSpaces);
    }
    
    // UTILITY FUNCTIONS
    
    
    ================================================
    FILE: 01_Acey_Ducey/kotlin/aceyducey.kt
    ================================================
    import java.util.Random
    
    fun printCard(a: Int) {
        if (a < 11) println(a)
        if (a == 11) println("JACK")
        if (a == 12) println("QUEEN")
        if (a == 13) println("KING")
        if (a == 14) println("ACE")
    }
    
    fun main() {
        println("ACEY DUCEY CARD GAME")
        println("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY")
        println()
        println()
        println("ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER ")
        println("THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP")
        println("YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING")
        println("ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE")
        println("A VALUE BETWEEN THE FIRST TWO.")
        println("IF YOU DO NOT WANT TO BET, INPUT A 0")
        var random = Random()
        do {
            var q = 100
            var a : Int
            var b : Int
            var m : Int
            println("YOU NOW HAVE " + q + " DOLLARS.")
            println()
            do {
                do {
                    do {
                        println("HERE ARE YOUR NEXT TWO CARDS: ")
                        do {
                            a = random.nextInt(12) + 2
                            b = random.nextInt(12) + 2
                        } while (a >= b);
                        printCard(a)
                        printCard(b)
                        println()
                        println()
                        print("WHAT IS YOUR BET")
                        m = readLine()!!.toInt()
                        if (m == 0) {
                            println("CHICKEN!!")
                            println()
                        }
                    } while (m == 0);
                    if (m > q) {
                        println("SORRY, MY FRIEND, BUT YOU BET TOO MUCH.")
                        println("YOU HAVE ONLY " + q + " DOLLARS TO BET.")
                    }
                } while (m > q);
                var c = random.nextInt(12) + 2
                printCard(c)
                println()
                if (c > a && c < b) {
                    println("YOU WIN!!!")
                    q += m
                }
                else {
                    println("SORRY, YOU LOSE")
                    if (m < q) q -= m
                }
            } while (m < q);
            println()
            println()
            println("SORRY, FRIEND, BUT YOU BLEW YOUR WAD.")
            println()
            println()
            println("TRY AGAIN (YES OR NO)")
        } while (readLine() == "YES");
        println("O.K., HOPE YOU HAD FUN!")
    }
    
    
    ================================================
    FILE: 01_Acey_Ducey/lua/README.md
    ================================================
    As published in Basic Computer Games (1978), as found at Annarchive:
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=17)
    
    
    Conversion to Lua
    - [Lua.org](https://www.lua.org)
    
    
    ================================================
    FILE: 01_Acey_Ducey/lua/acey_ducey.lua
    ================================================
    print [[
                ACEY DUCEY CARD GAME
     CREATIVE COMPUTING  MORRISTOWN, NEW JERSY
    
    
    
    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 OF NOT YOU FEEL THE CARD WILL HAVE
    A VALUE BETWEEN THE FIRST TWO.
    IF YOU DO NOT WANT TO BET INPUT A 0]]
    
    local starting_cash = 100
    
    local card_names = {[11] = "JACK", [12] = "QUEEN", [13] = "KING", [14] = "ACE"}
    for i = 2, 10 do
      card_names[i] = string.format(" %d", i)
    end
    
    function play_round(player_cash, skip_total)
      assert(player_cash > 0, "can't play with nothing to bet")
    
      if not skip_total then
        print(string.format("YOU NOW HAVE %u DOLLARS", player_cash))
      end
    
      print "\nHERE ARE YOUR NEXT TWO CARDS"
    
      local first_card, second_card;
    
      repeat
        first_card = math.random(2, 14)
        second_card = math.random(2, 14)
      until first_card < second_card
    
      print(card_names[first_card])
      print(card_names[second_card])
      print("")
    
      local bet = get_bet(player_cash)
    
      if bet == 0 then
        print "CHICKEN!!"
        return play_round(player_cash, "skip total")
      end
    
      local third_card = math.random(2, 14)
      print(card_names[third_card])
    
      if first_card < third_card and third_card < second_card then
        print "YOU WIN!!!"
        return play_round(player_cash + bet)
      end
    
      print "SORRY, YOU LOSE"
      if bet < player_cash then
        return play_round(player_cash - bet)
      end
    
      print "SORRY, FRIEND BUT YOU BLEW YOUR WAD"
      io.write "TRY AGAIN (YES OR NO)? "
      local keep_playing = io.read("l")
      if keep_playing:upper():match("%f[%a]YES%f[^%a]") then
        return play_round(starting_cash)
      end
    
      print "OK HOPE YOU HAD FUN"
    end
    
    function get_bet(player_cash)
      assert(player_cash > 0, "can't play with nothing to bet")
    
      io.write("WHAT IS YOUR BET? ")
      local input = io.read("l")
      if not input then
        print "GOODBYE"
        os.exit(0)
      end
    
      local digits = input:match("(%d+)")
      if not digits then
        print "I DON'T UNDERSTAND THAT NUMBER"
        return get_bet(player_cash)
      end
    
      local bet = tonumber(digits)
      assert(bet, "pattern matched something that doesn't convert to number")
    
      if bet > player_cash then
        print "SORRY, MY FRIEND BUT YOU BET TOO MUCH"
        print(string.format("YOU HAVE ONLY %u DOLLARS TO BET", player_cash))
        return get_bet(player_cash)
      end
    
      assert(0 <= bet and bet <= player_cash, "invalid bet")
      return bet
    end
    
    play_round(starting_cash)
    
    
    ================================================
    FILE: 01_Acey_Ducey/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 01_Acey_Ducey/perl/aceyducey.pl
    ================================================
    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    # The List::Util module is part of the core Perl distribution.  Using this
    # means we don't need to re-invent the wheel and create a way to shuffle
    # a list.
    use List::Util qw(shuffle);
    
    # Rather than put in a number of print (or say) statements here, we use a
    # "here document".  This is very useful for long strings of text.  In this
    # case, everything between the end of the "print" line and the line with
    # "END_INSTRUCTIONS" on it will be printed verbatim.
    print << 'END_INSTRUCTIONS';
    
    Acey-Ducey
    Adapted from Creative Computing, Morristown, New Jersey
    
    
    Acey-Ducey is played as follows. The dealer (computer) deals two cards face up.
    You have an option to bet or not bet, depending on whether or not you feel that
    the next card drawn will have a value between the first two.  Aces are low.
    
    Bets must be in whole-dollar amounts only.
    
    If you do not want to bet, input a 0.  If you want to quit, input a -1.
    
    END_INSTRUCTIONS
    
    my @cards       = ( 1 .. 13 );    # That is, Ace through King.
    my $keepPlaying = 1;
    
    GAME:
    while ($keepPlaying) {
        my $playerBalance = 100;      # The player starts with $100
    
        HAND:
        while (1) {
            print "\nYou now have $playerBalance dollars.\n\n";
    
            # We'll create a new array that is a shuffled version of the deck.
            my @shuffledDeck = shuffle(@cards);
    
            # Then, by taking the two "top cards" off the deck, we're guaranteed
            # that those will be unique.  This way we don't have to keep drawing
            # if we get, say, two queens.  We sort them as we pull them to make
            # sure that the first card is lower than the second one.
            my ( $firstCard, $secondCard ) = sort { $a <=> $b } @shuffledDeck[ 0 .. 1 ];
    
            print "I drew ", nameOfCard($firstCard), " and ", nameOfCard($secondCard), ".\n";
    
            my $bet = getValidBet($playerBalance);
            if ( $bet == 0 ) {
                print "Chicken!\n\n";
                next HAND;
            }
    
            if ( $bet < 0 ) {
                last GAME;
            }
    
            # Now we re-shuffle the whole deck again and choose a third card.
            # (Note: This is how the odds get stacked in favor of the dealer since
            # the third card can be exactly the same as the first or second.)
            @shuffledDeck = shuffle(@cards);
            my $thirdCard = $shuffledDeck[0];
    
            print "I drew ", nameOfCard($thirdCard), "!\n";
    
            if ( ( $firstCard < $thirdCard ) && ( $thirdCard < $secondCard ) ) {
                print "You win!\n\n";
                $playerBalance += $bet;
            }
            else {
                print "You lose!\n\n";
                $playerBalance -= $bet;
            }
    
            if ( $playerBalance <= 0 ) {
                print "Sorry, buddy, you blew your wad!\n\n";
                last HAND;
            }
        }
    
        $keepPlaying = promptUserToKeepPlaying();
    }
    
    print "Thanks for playing!\n";
    
    ###############
    sub getValidBet {
        my $maxBet = shift;
    
        INPUT:
        {
            print "\nWhat's your bet? ";
    
            chomp( my $input =  );
    
            # This regular expression will validate that the player entered an integer.
            # The !~ match operate *negates* the match, so if the player did NOT enter
            # an integer, they'll be given an error and prompted again.
            if (
                $input !~ /^        # Match the beginning of the string
                        [+-]?    # Optional plus or minus...
                        \d+      # followed by one more more digits...
                        $        # and then the end of the string
                        /x    # The x modifier ignores whitespace in this regex...
              )
            {
                print "Sorry, numbers only!\n";
                redo INPUT;
            }
    
            if ( $input > $maxBet ) {
                print "Sorry, my friend, you can't bet more money than you have.\n";
                print "You only have $maxBet dollars to spend!\n";
                redo INPUT;
            }
    
            return $input;
        }
    }
    
    # Since arrays in Perl are 0-based, we need to convert the value that we drew from
    # the array to its proper position in the deck.
    sub nameOfCard {
        my $value = shift;
    
        # Note that the Joker isn't used in this game, but since arrays in Perl are
        # 0-based, it's useful to have something there to represent the "0th"
        # position.  This way the rest of the elements match their expected values
        # (e.g., element 1 is Ace, element 7 is 7, and element 12 is Queen).
    
        my @cardlist = qw(Joker Ace 2 3 4 5 6 7 8 9 10 Jack Queen King);
        return $cardlist[$value];
    }
    
    sub promptUserToKeepPlaying {
        YESNO:
        {
            print "Try again (Y/N)? ";
    
            chomp( my $input = uc  );
    
            if ( $input eq 'Y' ) {
                return 1;
            }
            elsif ( $input eq 'N' ) {
                return 0;
            }
            else {
                redo YESNO;
            }
        }
    }
    
    
    ================================================
    FILE: 01_Acey_Ducey/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    Propose using pylint and black to format python files so that it conforms to some standards
    
    
    ================================================
    FILE: 01_Acey_Ducey/python/acey_ducey.py
    ================================================
    #!/usr/bin/env python3
    """
    Play the Acey-Ducey game
    https://www.atariarchives.org/basicgames/showpage.php?page=2
    """
    
    import random
    
    cards = {
        2: "2",
        3: "3",
        4: "4",
        5: "5",
        6: "6",
        7: "7",
        8: "8",
        9: "9",
        10: "10",
        11: "Jack",
        12: "Queen",
        13: "King",
        14: "Ace",
    }
    
    
    def play_game() -> None:
        cash = 100
        while cash > 0:
            print(f"You now have {cash} dollars\n")
            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")
            while True:
                try:
                    bet = int(input("What is your bet? "))
                    if bet < 0:
                        raise ValueError("Bet must be more than zero")
                    if bet == 0:
                        print("CHICKEN!!\n")
                    if bet > cash:
                        print("Sorry, my friend but you bet too much")
                        print(f"You only have {cash} dollars to bet")
                        continue
                    cash -= bet
                    break
    
                except ValueError:
                    print("Please enter a positive number")
            print(f" {cards[card_c]}")
            if bet > 0:
                if card_a <= card_c <= card_b:
                    print("You win!!!")
                    cash += bet * 2
                else:
                    print("Sorry, you lose")
    
        print("Sorry, friend, but you blew your wad")
    
    
    def main() -> None:
        print(
            """
    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
      """
        )
        keep_playing = True
    
        while keep_playing:
            play_game()
            keep_playing = input("Try again? (yes or no) ").lower().startswith("y")
        print("Ok hope you had fun")
    
    
    if __name__ == "__main__":
        random.seed()
        main()
    
    
    ================================================
    FILE: 01_Acey_Ducey/python/acey_ducey_oo.py
    ================================================
    """
    AceyDuchy
    From: BASIC Computer Games (1978)
          Edited by David Ahl
    "The original BASIC program author was Bill Palmby
     of Prairie View, Illinois."
    Python port by Aviyam Fischer, 2022
    """
    
    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]
    
    
    class Card(NamedTuple):
        suit: Suit
        rank: Rank
    
        def __str__(self) -> str:
            r = str(self.rank)
            r = {"11": "J", "12": "Q", "13": "K", "14": "A"}.get(r, r)
            return f"{r}{self.suit}"
    
    
    class Deck:
        def __init__(self) -> None:
            self.cards: List[Card] = []
            self.build()
    
        def build(self) -> None:
            for suit in get_args(Suit):
                for rank in get_args(Rank):
                    self.cards.append(Card(suit, rank))
    
        def shuffle(self) -> None:
            random.shuffle(self.cards)
    
        def deal(self) -> Card:
            return self.cards.pop()
    
    
    class Game:
        def __init__(self) -> None:
            self.deck = Deck()
            self.deck.shuffle()
            self.card_a = self.deck.deal()
            self.card_b = self.deck.deal()
            self.money = 100
            self.not_done = True
    
        def play(self) -> None:
            while self.not_done:
                while self.money > 0:
                    card_a = self.card_a
                    card_b = self.card_b
    
                    if card_a.rank > card_b.rank:
                        card_a, card_b = card_b, card_a
    
                    if card_a.rank == card_b.rank:
                        self.card_b = self.deck.deal()
                        card_b = self.card_b
    
                    print(f"You have:\t ${self.money} ")
                    print(f"Your cards:\t {card_a} {card_b}")
    
                    bet = int(input("What is your bet? "))
                    player_card = self.deck.deal()
                    if 0 < bet <= self.money:
    
                        print(f"Your deal:\t {player_card}")
                        if card_a.rank <= player_card.rank <= card_b.rank:
                            print("You Win!")
                            self.money += bet
                        else:
                            print("You Lose!")
                            self.money -= bet
                            self.not_done = False
                    else:
                        print("Chicken!")
                        print(f"Your deal should have been: {player_card}")
                        if card_a.rank < player_card.rank < card_b.rank:
                            print("You could have won!")
                        else:
                            print("You would lose, so it was wise of you to chicken out!")
    
                    if len(self.deck.cards) <= 3:
                        print("You ran out of cards. Game over.")
                        self.not_done = False
                        break
    
                    self.card_a = self.deck.deal()
                    self.card_b = self.deck.deal()
    
            if self.money == 0:
                self.not_done = False
    
    
    def game_loop() -> None:
        game_over = False
    
        while not game_over:
            game = Game()
            game.play()
            print(f"You have ${game.money} left")
            print("Would you like to play again? (y/n)")
            if input() == "n":
                game_over = True
    
    
    def main() -> None:
        print(
            """
        Acey Ducey is a card game where you play against the computer.
        The Dealer(computer) will deal two cards facing up.
        You have an option to bet or not bet depending on whether or not you
        feel the card will have a value between the first two.
        If you do not want to bet input a 0
        """
        )
        game_loop()
        print("\nThanks for playing!")
    
    
    if __name__ == "__main__":
        random.seed()
        main()
    
    
    ================================================
    FILE: 01_Acey_Ducey/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/) by Christopher Özbek [coezbek@github](https://github.com/coezbek).
    
    
    ================================================
    FILE: 01_Acey_Ducey/ruby/aceyducey.rb
    ================================================
    ########################################################
    #
    # 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."
    #
    # Ruby port by Christopher Oezbek, 2021
    #
    # This uses the following techniques:
    #
    #  - The programm largely consists of a GAME LOOP,
    #    which is used to represent one round.
    #  - The Kernel function rand(Range) is used to get an
    #    Integer in the (inclusive) Range of 2 to 14.
    #  - To ensure the user enters a proper Integer
    #    via the console, an inline 'rescue' statement is
    #    used.
    #  - To capture the long text in the introduction, a
    #    squiggly HEREDOC string declaration <<~ is used.
    #
    #
    ########################################################
    
    puts <<~INSTRUCTIONS
               🂡  ACEY DUCEY CARD GAME 🂱
       CREATIVE COMPUTING - MORRISTOWN, NEW JERSEY
    
      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 IN A ROUND, ENTER 0
    INSTRUCTIONS
    
    # The player starts with 100$
    stake = 100
    
    while true # Game loop
      puts
      puts "YOU NOW HAVE #{stake} DOLLARS."
      puts
      puts "HERE ARE YOUR NEXT TWO CARDS:"
    
      # Randomly draw two cards and make sure the first card is lower in value than the second
      # Using array destructuring, this sorted array can be assigned to `first_card` and `second_card`
      first_card, second_card = (2...14).to_a.shuffle.pop(2).sort
    
      # Helper method to convert a numeric card into a String for printing
      def card_name(card)
        case card
        when 11
          "JACK"
        when 12
          "QUEEN"
        when 13
          "KING"
        when 14
          "ACE"
        else
          card
        end
      end
    
      puts card_name(first_card)
      puts card_name(second_card)
      puts
      puts
    
      # Loop until the user enters something sensible
      while true
        puts "WHAT IS YOUR BET? ENTER 0 IF YOU DON'T WANT TO BET (CTRL+C TO EXIT)"
        your_bet = Integer(gets.chomp) rescue nil
    
        if your_bet == nil || your_bet < 0
          puts "PLEASE ENTER A POSITIVE NUMBER"
          puts
          next
        end
    
        if your_bet > stake
          puts "SORRY, MY FRIEND, BUT YOU BET TOO MUCH."
          puts "YOU HAVE ONLY #{stake} DOLLARS TO BET."
          puts
          next
        end
    
        break
      end
    
      if your_bet == 0
        puts "CHICKEN!!"
        next
      end
    
      puts "THANK YOU! YOUR BET IS #{your_bet} DOLLARS."
      puts ""
      puts "THE THIRD CARD IS:"
      third_card = rand(2..14)
      puts card_name(third_card)
      puts
    
      if first_card <= third_card && third_card <= second_card
        puts "YOU WIN!!!"
        stake += your_bet
      else
        puts "SORRY, YOU LOSE"
        stake -= your_bet
      end
    
      if stake == 0
        puts
        puts "SORRY, FRIEND, BUT YOU BLEW YOUR WAD."
        puts
    
        puts "TRY AGAIN? (YES OR NO)"
        if gets.chomp.downcase.start_with? 'y'
          # Reset cash to 100
          stake = 100
        else
          puts "O.K., HOPE YOU HAD FUN!"
          exit
        end
      end
    end
    
    
    ================================================
    FILE: 01_Acey_Ducey/rust/Cargo.toml
    ================================================
    [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"
    
    
    ================================================
    FILE: 01_Acey_Ducey/rust/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Rust](https://www.rust-lang.org/) by Alex Kotov [mur4ik18@github](https://github.com/mur4ik18).
    
    Further edits by
    
    - Berker Şal [berkersal@github](https://github.com/berkersal)
    
    
    ================================================
    FILE: 01_Acey_Ducey/rust/src/main.rs
    ================================================
    use rand::{prelude::ThreadRng, Rng};
    use std::{fmt, io, mem};
    
    #[derive(PartialEq, Eq, PartialOrd, Ord)]
    struct Card(u8);
    
    impl Card {
        fn new_random(rng: &mut ThreadRng) -> Card {
            Card(rng.gen_range(2..15))
        }
    }
    
    impl fmt::Display for Card {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            write!(
                f,
                "{}",
                match self.0 {
                    11 => String::from("JACK"),
                    12 => String::from("QUEEN"),
                    13 => String::from("KING"),
                    14 => String::from("ACE"),
                    otherwise => otherwise.to_string(),
                }
            )
        }
    }
    
    struct CardsPool(Card, Card, Card);
    
    impl CardsPool {
        fn new() -> CardsPool {
            let mut rng = rand::thread_rng();
            let mut first = Card::new_random(&mut rng);
            let mut second = Card::new_random(&mut rng);
            let third = Card::new_random(&mut rng);
    
            if first > second {
                mem::swap(&mut first, &mut second);
            }
    
            CardsPool(first, second, third)
        }
    
        fn is_in_win_range(&self) -> bool {
            self.0 <= self.2 && self.2 <= self.1
        }
    }
    
    fn main() {
        hello();
        // user start bank
        let mut user_bank: u16 = 100;
    
        loop {
            println!("YOU NOW HAVE {} DOLLARS.", &mut user_bank);
            println!("\nHERE ARE YOUR NEXT TWO CARDS:");
            // get new random cards
            let cards = CardsPool::new();
    
            println!("{}", cards.0);
            println!("{}", cards.1);
    
            let user_bet: u16 = get_bet(user_bank);
    
            if user_bet == 0 {
                println!("CHICKEN!!!\n");
                continue;
            } else {
                println!("THANK YOU! YOUR BET IS {} DOLLARS.", user_bet);
            }
    
            println!("\nTHE THIRD CARD IS:");
            println!("{}", cards.2);
    
            if cards.is_in_win_range() {
                println!("\nYOU WIN!!!");
                user_bank += user_bet;
            } else {
                println!("\nSORRY, YOU LOSE");
                user_bank -= user_bet;
            }
    
            if user_bank == 0 {
                println!("\nSORRY, FRIEND, BUT YOU BLEW YOUR WAD.");
                println!("\nTRY AGAIN? (YES OR NO)");
                let mut input = String::new();
                io::stdin().read_line(&mut input).expect("Incorrect input");
    
                if input.trim().to_lowercase() == "yes" {
                    user_bank = 100;
                    println!();
                } else {
                    println!("\nO.K., HOPE YOU HAD FUN!");
                    break;
                }
            }
        }
    }
    
    fn hello() {
        println!("         🂡  ACEY DUCEY CARD GAME 🂱");
        println!("CREATIVE COMPUTING - MORRISTOWN, NEW JERSEY");
        println!("ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER");
        println!("THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP");
        println!("YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING");
        println!("ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE");
        println!("A VALUE BETWEEN THE FIRST TWO.");
        println!("IF YOU DO NOT WANT TO BET IN A ROUND, ENTER 0");
        println!("\n\n");
    }
    
    fn get_bet(user_bank: u16) -> u16 {
        loop {
            println!("\nWHAT IS YOUR BET? ENTER 0 IF YOU DON'T WANT TO BET (CTRL+C TO EXIT)");
            let bet: u16;
            let mut input = String::new();
    
            io::stdin()
                .read_line(&mut input)
                .expect("CANNOT READ INPUT!");
    
            match input.trim().parse::() {
                Ok(i) => bet = i,
                Err(e) => {
                    println!("CHECK YOUR INPUT! {}!", e.to_string().to_uppercase());
                    continue;
                }
            };
    
            match bet {
                bet if bet <= user_bank => return bet,
                _ => {
                    println!("\nSORRY, MY FRIEND, BUT YOU BET TOO MUCH.");
                    println!("YOU HAVE ONLY {} DOLLARS TO BET.", user_bank);
                }
            };
        }
    }
    
    
    ================================================
    FILE: 01_Acey_Ducey/vbnet/AceyDucey.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 17
    VisualStudioVersion = 17.0.31903.59
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "AceyDucey", "AceyDucey.vbproj", "{54C05475-238D-4A82-A67B-B6C1BF2CA337}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{54C05475-238D-4A82-A67B-B6C1BF2CA337}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{54C05475-238D-4A82-A67B-B6C1BF2CA337}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{54C05475-238D-4A82-A67B-B6C1BF2CA337}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{54C05475-238D-4A82-A67B-B6C1BF2CA337}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {C01D9DAE-644C-455F-8365-E14E49074BC3}
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 01_Acey_Ducey/vbnet/AceyDucey.vb
    ================================================
    Public Class AceyDucey
        ''' 
        ''' Create a single instance of the Random class to be used
        ''' throughout the program.
        ''' 
        Private ReadOnly Property Rnd As New Random()
    
        ''' 
        ''' Define a varaible to store the the player balance. 
    ''' Defaults to 0 '''
    ''' ''' Since is a value type, and no value ''' has been explicitly set, the default value of the type is used. ''' Private _balance As Integer Public Sub New() DisplayIntroduction() End Sub ''' ''' Play multiple games of Acey Ducey until the player chooses to quit. ''' Public Sub Play() Do PlayGame() Loop While TryAgain() 'Loop (play again) based on the Boolean value returned by TryAgain Console.WriteLine("O.K., HOPE YOU HAD FUN!") End Sub ''' ''' Play a game of Acey Ducey, which ends when the player balance reaches 0 ''' Private Sub PlayGame() _balance = 100 'At the start of the game, set the player balance to 100 Console.WriteLine() Console.WriteLine($"YOU NOW HAVE {_balance} DOLLARS.") Do PlayTurn() Loop While _balance > 0 'Continue playing while the user has a balance Console.WriteLine() Console.WriteLine("SORRY, FRIEND, BUT YOU BLEW YOUR WAD.") End Sub ''' ''' Play one turn of Acey Ducey ''' ''' ''' A turn consists of displaying to cards, making a wager ''' and determining the result (win/lose) ''' Private Sub PlayTurn() Console.WriteLine() Console.WriteLine("HERE ARE YOUR NEXT TWO CARDS: ") Dim cards = GetOrderedCards() For Each card In cards DisplayCard(card) Next Dim wager As Integer = GetWager() Dim finalCard As Integer = GetCard() If wager = 0 Then Console.WriteLine("CHICKEN!!") Return End If DisplayCard(finalCard) Console.WriteLine() '''Check if the value of the final card is between the first and second cards. ''' '''The use of AndAlso is used to short-circuit the evaluation of the IF condition. '''Short-circuiting means that both sides of the condition do not need to be '''evaluated. In this case, if the left criteria returns FALSE, the right criteria '''is ignored and the evaluation result is returned as FALSE. ''' '''This works because AndAlso requires both condition to return TRUE in order to be '''evaluated as TRUE. If the first condition is FALSE we already know the evaluation result. If finalCard >= cards.First() AndAlso finalCard <= cards.Last() Then Console.WriteLine("YOU WIN!!!") _balance += wager 'Condensed version of _balance = _balance + wager Else Console.WriteLine("SORRY, YOU LOSE.") _balance -= wager 'Condensed version of _balance = _balance - wager End If End Sub ''' ''' Get two cards in ascending order ''' ''' ''' The original version generates two cards (A and B) ''' If A is greater than or equal to B, both cards are regenerated. '''

    ''' This version generates the two cards, but only regenerates A ''' if A is equal to B. The cards are then returned is ascending order, ''' ensuring that A is less than B (maintaining the original end result) '''
    Private Function GetOrderedCards() As Integer() '''When declaring fixed size arrays in VB.NET you declare the MAX INDEX of the array '''and NOT the SIZE (number of elements) of the array. '''As such, card(1) gives you and array with index 0 and index 1, which means '''the array stores two elements and not one Dim cards(1) As Integer cards(0) = GetCard() cards(1) = GetCard() 'Perform this action as long as the first card is equal to the second card While cards(0) = cards(1) cards(0) = GetCard() End While Array.Sort(cards) 'Sort the values in ascending order Return cards End Function ''' ''' Get a random number (card) ranked 2 to 14 ''' Private Function GetCard() As Integer Return Rnd.Next(2, 15) End Function ''' ''' Display the face value of the card ''' Private Sub DisplayCard(card As Integer) Dim output As String Select Case card Case 2 To 10 output = card.ToString() Case 11 output = "JACK" Case 12 output = "QUEEN" Case 13 output = "KING" Case 14 output = "ACE" Case Else Throw New ArgumentOutOfRangeException(NameOf(card), "Value must be between 2 and 14") End Select Console.WriteLine(output) End Sub ''' ''' Prompt the user to make a bet ''' ''' ''' The function will not return until a valid bet is made.
    ''' is used to validate that the user input is a valid '''
    Private Function GetWager() As Integer Dim wager As Integer Do Console.WriteLine() Console.Write("WHAT IS YOUR BET? ") Dim input As String = Console.ReadLine() '''Determine if the user input is an Integer '''If it is an Integer, store the value in the variable wager If Not Integer.TryParse(input, wager) Then Console.WriteLine("SORRY, I DID'T QUITE GET THAT.") Continue Do 'restart the loop End If 'Prevent the user from betting more than their current balance If _balance < wager Then Console.WriteLine("SORRY, MY FRIEND, BUT YOU BET TOO MUCH.") Console.WriteLine($"YOU HAVE ONLY {_balance} DOLLARS TO BET.") Continue Do 'restart the loop End If 'Prevent the user from betting negative values If wager < 0 Then Console.WriteLine("FUNNY GUY! YOU CANNOT MAKE A NEGATIVE BET.") Continue Do 'restart the loop End If Exit Do 'If we get to this line, exit the loop as all above validations passed Loop Return wager End Function ''' ''' Prompt the user to try again ''' ''' ''' This function will not return until a valid reponse is given ''' Private Function TryAgain() As Boolean Dim response As String Do Console.Write("TRY AGAIN (YES OR NO) ") response = Console.ReadLine() If response.Equals("YES", StringComparison.OrdinalIgnoreCase) Then Return True If response.Equals("NO", StringComparison.OrdinalIgnoreCase) Then Return False Console.WriteLine("SORRY, I DID'T QUITE GET THAT.") Loop End Function ''' ''' Display the opening title and instructions ''' ''' ''' Refer to ''' ''' Interpolated Strings ''' documentation for the use of $ and { } with strings ''' Private Sub DisplayIntroduction() Console.WriteLine($"{Space((Console.WindowWidth \ 2) - 10)}ACEY DUCEY CARD GAME") Console.WriteLine($"{Space((Console.WindowWidth \ 2) - 21)}CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") Console.WriteLine("") Console.WriteLine("") Console.WriteLine("ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER") Console.WriteLine("THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP") Console.WriteLine("YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING") Console.WriteLine("ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE") Console.WriteLine("A VALUE BETWEEN THE FIRST TWO.") Console.WriteLine("IF YOU DO NOT WANT TO BET, INPUT A 0") Console.WriteLine("") End Sub End Class ================================================ FILE: 01_Acey_Ducey/vbnet/AceyDucey.vbproj ================================================ Exe AceyDucey net6.0 16.9 ================================================ FILE: 01_Acey_Ducey/vbnet/Program.vb ================================================ Imports System ''' ''' This is a modern adapation of Acey Ducey from BASIC Computer Games. ''' ''' The structural changes primarily consist of replacing the many GOTOs with ''' Do/Loop constructs to force the continual execution of the program. ''' ''' Some modern improvements were added, primarily the inclusion of a multiple ''' subroutines and functions, which eliminates repeated logic and reduces ''' then need for nested loops. ''' ''' The archaic RND function is greatly simplified with the .NET Framework's Random class. ''' ''' Elementary comments are provided for non-programmers or novices. ''' Module Program Sub Main() Call New AceyDucey().Play() End Sub End Module ================================================ FILE: 01_Acey_Ducey/vbnet/README.md ================================================ Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET) ================================================ FILE: 02_Amazing/README.md ================================================ ### Amazing This program will print out a different maze every time it is run and guarantees only one path through. You can choose the dimensions of the maze — i.e. the number of squares wide and long. The original program author was Jack Hauber of Windsor, Connecticut. --- As published in Basic Computer Games (1978): - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=3) - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=18) Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html #### 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. ================================================ FILE: 02_Amazing/amazing.bas ================================================ 10 PRINT TAB(28);"AMAZING PROGRAM" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 30 PRINT:PRINT:PRINT:PRINT 100 INPUT "WHAT ARE YOUR WIDTH AND LENGTH";H,V 102 IF H<>1 AND V<>1 THEN 110 104 PRINT "MEANINGLESS DIMENSIONS. TRY AGAIN.":GOTO 100 110 DIM W(H,V),V(H,V) 120 PRINT 130 PRINT 140 PRINT 150 PRINT 160 Q=0:Z=0:X=INT(RND(1)*H+1) 165 FOR I=1 TO H 170 IF I=X THEN 173 171 PRINT ".--";:GOTO 180 173 PRINT ". "; 180 NEXT I 190 PRINT "." 195 C=1:W(X,1)=C:C=C+1 200 R=X:S=1:GOTO 260 210 IF R<>H THEN 240 215 IF S<>V THEN 230 220 R=1:S=1:GOTO 250 230 R=1:S=S+1:GOTO 250 240 R=R+1 250 IF W(R,S)=0 THEN 210 260 IF R-1=0 THEN 530 265 IF W(R-1,S)<>0 THEN 530 270 IF S-1=0 THEN 390 280 IF W(R,S-1)<>0 THEN 390 290 IF R=H THEN 330 300 IF W(R+1,S)<>0 THEN 330 310 X=INT(RND(1)*3+1) 320 ON X GOTO 790,820,860 330 IF S<>V THEN 340 334 IF Z=1 THEN 370 338 Q=1:GOTO 350 340 IF W(R,S+1)<>0 THEN 370 350 X=INT(RND(1)*3+1) 360 ON X GOTO 790,820,910 370 X=INT(RND(1)*2+1) 380 ON X GOTO 790,820 390 IF R=H THEN 470 400 IF W(R+1,S)<>0 THEN 470 405 IF S<>V THEN 420 410 IF Z=1 THEN 450 415 Q=1:GOTO 430 420 IF W(R,S+1)<>0 THEN 450 430 X=INT(RND(1)*3+1) 440 ON X GOTO 790,860,910 450 X=INT(RND(1)*2+1) 460 ON X GOTO 790,860 470 IF S<>V THEN 490 480 IF Z=1 THEN 520 485 Q=1:GOTO 500 490 IF W(R,S+1)<>0 THEN 520 500 X=INT(RND(1)*2+1) 510 ON X GOTO 790,910 520 GOTO 790 530 IF S-1=0 THEN 670 540 IF W(R,S-1)<>0 THEN 670 545 IF R=H THEN 610 547 IF W(R+1,S)<>0 THEN 610 550 IF S<>V THEN 560 552 IF Z=1 THEN 590 554 Q=1:GOTO 570 560 IF W(R,S+1)<>0 THEN 590 570 X=INT(RND(1)*3+1) 580 ON X GOTO 820,860,910 590 X=INT(RND(1)*2+1) 600 ON X GOTO 820,860 610 IF S<>V THEN 630 620 IF Z=1 THEN 660 625 Q=1:GOTO 640 630 IF W(R,S+1)<>0 THEN 660 640 X=INT(RND(1)*2+1) 650 ON X GOTO 820,910 660 GOTO 820 670 IF R=H THEN 740 680 IF W(R+1,S)<>0 THEN 740 685 IF S<>V THEN 700 690 IF Z=1 THEN 730 695 Q=1:GOTO 710 700 IF W(R,S+1)<>0 THEN 730 710 X=INT(RND(1)*2+1) 720 ON X GOTO 860,910 730 GOTO 860 740 IF S<>V THEN 760 750 IF Z=1 THEN 780 755 Q=1:GOTO 770 760 IF W(R,S+1)<>0 THEN 780 770 GOTO 910 780 GOTO 1000 790 W(R-1,S)=C 800 C=C+1:V(R-1,S)=2:R=R-1 810 IF C=H*V+1 THEN 1010 815 Q=0:GOTO 260 820 W(R,S-1)=C 830 C=C+1 840 V(R,S-1)=1:S=S-1:IF C=H*V+1 THEN 1010 850 Q=0:GOTO 260 860 W(R+1,S)=C 870 C=C+1:IF V(R,S)=0 THEN 880 875 V(R,S)=3:GOTO 890 880 V(R,S)=2 890 R=R+1 900 IF C=H*V+1 THEN 1010 905 GOTO 530 910 IF Q=1 THEN 960 920 W(R,S+1)=C:C=C+1:IF V(R,S)=0 THEN 940 930 V(R,S)=3:GOTO 950 940 V(R,S)=1 950 S=S+1:IF C=H*V+1 THEN 1010 955 GOTO 260 960 Z=1 970 IF V(R,S)=0 THEN 980 975 V(R,S)=3:Q=0:GOTO 1000 980 V(R,S)=1:Q=0:R=1:S=1:GOTO 250 1000 GOTO 210 1010 IF Z=1 THEN 1015 1011 X=INT(RND(1)*H+1) 1012 IF V(X,V)=0 THEN 1014 1013 V(X,V)=3: GOTO 1015 1014 V(X,V)=1 1015 FOR J=1 TO V 1016 PRINT "I"; 1017 FOR I=1 TO H 1018 IF V(I,J)<2 THEN 1030 1020 PRINT " "; 1021 GOTO 1040 1030 PRINT " I"; 1040 NEXT I 1041 PRINT 1043 FOR I=1 TO H 1045 IF V(I,J)=0 THEN 1060 1050 IF V(I,J)=2 THEN 1060 1051 PRINT ": "; 1052 GOTO 1070 1060 PRINT ":--"; 1070 NEXT I 1071 PRINT "." 1072 NEXT J 1073 END ================================================ FILE: 02_Amazing/csharp/Amazing.cs ================================================ using System; using System.Collections.Generic; namespace Amazing { class AmazingGame { private const int FIRST_COL = 0; private const int FIRST_ROW = 0; private const int EXIT_UNSET = 0; private const int EXIT_DOWN = 1; private const int EXIT_RIGHT = 2; private static int GetDelimitedValue(String text, int pos) { String[] tokens = text.Split(","); int val; if (Int32.TryParse(tokens[pos], out val)) { return val; } return 0; } private static String Tab(int spaces) { return new String(' ', spaces); } public static int Random(int min, int max) { Random random = new Random(); return random.Next(max - min) + min; } public void Play() { Console.WriteLine(Tab(28) + "AMAZING PROGRAM"); Console.WriteLine(Tab(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); Console.WriteLine(); int width = 0; int length = 0; do { String range = DisplayTextAndGetInput("WHAT ARE YOUR WIDTH AND LENGTH"); if (range.IndexOf(",") > 0) { width = GetDelimitedValue(range, 0); length = GetDelimitedValue(range, 1); } } while (width < 1 || length < 1); Grid grid = new Grid(length, width); int enterCol = grid.SetupEntrance(); int totalWalls = width * length + 1; int count = 2; Cell cell = grid.StartingCell(); while (count != totalWalls) { List possibleDirs = GetPossibleDirs(grid, cell); if (possibleDirs.Count != 0) { cell = SetCellExit(grid, cell, possibleDirs); cell.Count = count++; } else { cell = grid.GetFirstUnset(cell); } } grid.SetupExit(); WriteMaze(width, grid, enterCol); } private Cell SetCellExit(Grid grid, Cell cell, List possibleDirs) { Direction direction = possibleDirs[Random(0, possibleDirs.Count)]; if (direction == Direction.GO_LEFT) { cell = grid.GetPrevCol(cell); cell.ExitType = EXIT_RIGHT; } else if (direction == Direction.GO_UP) { cell = grid.GetPrevRow(cell); cell.ExitType = EXIT_DOWN; } else if (direction == Direction.GO_RIGHT) { cell.ExitType = cell.ExitType + EXIT_RIGHT; cell = grid.GetNextCol(cell); } else if (direction == Direction.GO_DOWN) { cell.ExitType = cell.ExitType + EXIT_DOWN; cell = grid.GetNextRow(cell); } return cell; } private void WriteMaze(int width, Grid grid, int enterCol) { // top line for (int i = 0; i < width; i++) { if (i == enterCol) Console.Write(". "); else Console.Write(".--"); } Console.WriteLine("."); for (int i = 0; i < grid.Length; i++) { Console.Write("I"); for (int j = 0; j < grid.Width; j++) { if (grid.Cells[i,j].ExitType == EXIT_UNSET || grid.Cells[i, j].ExitType == EXIT_DOWN) Console.Write(" I"); else Console.Write(" "); } Console.WriteLine(); for (int j = 0; j < grid.Width; j++) { if (grid.Cells[i,j].ExitType == EXIT_UNSET || grid.Cells[i, j].ExitType == EXIT_RIGHT) Console.Write(":--"); else Console.Write(": "); } Console.WriteLine("."); } } private List GetPossibleDirs(Grid grid, Cell cell) { var possibleDirs = new List(); foreach (var val in Enum.GetValues(typeof(Direction))) { possibleDirs.Add((Direction)val); } if (cell.Col == FIRST_COL || grid.IsPrevColSet(cell)) { possibleDirs.Remove(Direction.GO_LEFT); } if (cell.Row == FIRST_ROW || grid.IsPrevRowSet(cell)) { possibleDirs.Remove(Direction.GO_UP); } if (cell.Col == grid.LastCol || grid.IsNextColSet(cell)) { possibleDirs.Remove(Direction.GO_RIGHT); } if (cell.Row == grid.LastRow || grid.IsNextRowSet(cell)) { possibleDirs.Remove(Direction.GO_DOWN); } return possibleDirs; } private String DisplayTextAndGetInput(String text) { Console.WriteLine(text); return Console.ReadLine(); } private enum Direction { GO_LEFT, GO_UP, GO_RIGHT, GO_DOWN, } public class Cell { public int ExitType { get; set; } public int Count { get; set; } public int Col { get; set; } public int Row { get; set; } public Cell(int row, int col) { ExitType = EXIT_UNSET; Row = row; Col = col; } } public class Grid { public Cell[,] Cells { get; private set; } public int LastCol { get; set; } public int LastRow { get; set; } public int Width { get; private set; } public int Length { get; private set; } private int enterCol; public Grid(int length, int width) { LastCol = width - 1; LastRow = length - 1; Width = width; Length = length; Cells = new Cell[length,width]; for (int i = 0; i < length; i++) { for (int j = 0; j < width; j++) { this.Cells[i,j] = new Cell(i, j); } } } public int SetupEntrance() { this.enterCol = Random(0, Width); Cells[0, enterCol].Count = 1; return this.enterCol; } public void SetupExit() { int exit = Random(0, Width - 1); Cells[LastRow, exit].ExitType += 1; } public Cell StartingCell() { return Cells[0, enterCol]; } public bool IsPrevColSet(Cell cell) { return 0 != Cells[cell.Row, cell.Col - 1].Count; } public bool IsPrevRowSet(Cell cell) { return 0 != Cells[cell.Row - 1, cell.Col].Count; } public bool IsNextColSet(Cell cell) { return 0 != Cells[cell.Row, cell.Col + 1].Count; } public bool IsNextRowSet(Cell cell) { return 0 != Cells[cell.Row + 1, cell.Col].Count; } public Cell GetPrevCol(Cell cell) { return Cells[cell.Row, cell.Col - 1]; } public Cell GetPrevRow(Cell cell) { return Cells[cell.Row - 1, cell.Col]; } public Cell GetNextCol(Cell cell) { return Cells[cell.Row, cell.Col + 1]; } public Cell GetNextRow(Cell cell) { return Cells[cell.Row + 1, cell.Col]; } public Cell GetFirstUnset(Cell cell) { int col = cell.Col; int row = cell.Row; Cell newCell; do { if (col != this.LastCol) { col++; } else if (row != this.LastRow) { row++; col = 0; } else { row = 0; col = 0; } } while ((newCell = Cells[row, col]).Count == 0); return newCell; } } } class Program { static void Main(string[] args) { new AmazingGame().Play(); } } } ================================================ FILE: 02_Amazing/csharp/Amazing.csproj ================================================ Exe netcoreapp3.1 ================================================ FILE: 02_Amazing/csharp/Amazing.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.808.10 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Amazing", "Amazing.csproj", "{DD3483B4-F366-493C-8BFE-BAFBFE6F3016}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {DD3483B4-F366-493C-8BFE-BAFBFE6F3016}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DD3483B4-F366-493C-8BFE-BAFBFE6F3016}.Debug|Any CPU.Build.0 = Debug|Any CPU {DD3483B4-F366-493C-8BFE-BAFBFE6F3016}.Release|Any CPU.ActiveCfg = Release|Any CPU {DD3483B4-F366-493C-8BFE-BAFBFE6F3016}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {F8762606-E467-47C1-A28F-BD5C4F7BFF5A} EndGlobalSection EndGlobal ================================================ FILE: 02_Amazing/csharp/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/) ================================================ FILE: 02_Amazing/java/Amazing.java ================================================ import java.util.ArrayList; import java.util.Arrays; import java.util.Random; import java.util.Scanner; import static java.lang.System.in; import static java.lang.System.out; /** * Core algorithm copied from amazing.py */ public class Amazing { final static int FIRST_COL = 0; final static int FIRST_ROW = 0; final static int EXIT_UNSET = 0; final static int EXIT_DOWN = 1; final static int EXIT_RIGHT = 2; private final Scanner kbScanner; public Amazing() { kbScanner = new Scanner(in); } private static int getDelimitedValue(String text, int pos) { String[] tokens = text.split(","); try { return Integer.parseInt(tokens[pos]); } catch (Exception ex) { return 0; } } private static String tab(int spaces) { char[] spacesTemp = new char[spaces]; Arrays.fill(spacesTemp, ' '); return new String(spacesTemp); } public static int random(int min, int max) { Random random = new Random(); return random.nextInt(max - min) + min; } public void play() { out.println(tab(28) + "AMAZING PROGRAM"); out.println(tab(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); out.println(); int width = 0; int length = 0; do { String range = displayTextAndGetInput("WHAT ARE YOUR WIDTH AND LENGTH"); if (range.indexOf(",") > 0) { width = getDelimitedValue(range, 0); length = getDelimitedValue(range, 1); } } while (width < 1 || length < 1); Grid grid = new Grid(length, width); int enterCol = grid.setupEntrance(); int totalWalls = width * length + 1; int count = 2; Cell cell = grid.startingCell(); while (count != totalWalls) { ArrayList possibleDirs = getPossibleDirs(grid, cell); if (possibleDirs.size() != 0) { cell = setCellExit(grid, cell, possibleDirs); cell.count = count++; } else { cell = grid.getFirstUnset(cell); } } grid.setupExit(); writeMaze(width, grid, enterCol); } private Cell setCellExit(Grid grid, Cell cell, ArrayList possibleDirs) { Direction direction = possibleDirs.get(random(0, possibleDirs.size())); if (direction == Direction.GO_LEFT) { cell = grid.getPrevCol(cell); cell.exitType = EXIT_RIGHT; } else if (direction == Direction.GO_UP) { cell = grid.getPrevRow(cell); cell.exitType = EXIT_DOWN; } else if (direction == Direction.GO_RIGHT) { cell.exitType = cell.exitType + EXIT_RIGHT; cell = grid.getNextCol(cell); } else if (direction == Direction.GO_DOWN) { cell.exitType = cell.exitType + EXIT_DOWN; cell = grid.getNextRow(cell); } return cell; } private void writeMaze(int width, Grid grid, int enterCol) { // top line for (int i = 0; i < width; i++) { if (i == enterCol) { out.print(". "); } else { out.print(".--"); } } out.println('.'); for (Cell[] rows : grid.cells) { out.print("I"); for (Cell cell : rows) { if (cell.exitType == EXIT_UNSET || cell.exitType == EXIT_DOWN) { out.print(" I"); } else { out.print(" "); } } out.println(); for (Cell cell : rows) { if (cell.exitType == EXIT_UNSET || cell.exitType == EXIT_RIGHT) { out.print(":--"); } else { out.print(": "); } } out.println("."); } } private ArrayList getPossibleDirs(Grid grid, Cell cell) { ArrayList possibleDirs = new ArrayList<>(Arrays.asList(Direction.values())); if (cell.col == FIRST_COL || grid.isPrevColSet(cell)) { possibleDirs.remove(Direction.GO_LEFT); } if (cell.row == FIRST_ROW || grid.isPrevRowSet(cell)) { possibleDirs.remove(Direction.GO_UP); } if (cell.col == grid.lastCol || grid.isNextColSet(cell)) { possibleDirs.remove(Direction.GO_RIGHT); } if (cell.row == grid.lastRow || grid.isNextRowSet(cell)) { possibleDirs.remove(Direction.GO_DOWN); } return possibleDirs; } private String displayTextAndGetInput(String text) { out.print(text); return kbScanner.next(); } enum Direction { GO_LEFT, GO_UP, GO_RIGHT, GO_DOWN, } public static class Cell { int exitType = EXIT_UNSET; int count = 0; int col; int row; public Cell(int row, int col) { this.row = row; this.col = col; } } public static class Grid { Cell[][] cells; int lastCol; int lastRow; int width; int enterCol; public Grid(int length, int width) { this.lastCol = width - 1; this.lastRow = length - 1; this.width = width; this.cells = new Cell[length][width]; for (int i = 0; i < length; i++) { this.cells[i] = new Cell[width]; for (int j = 0; j < width; j++) { this.cells[i][j] = new Cell(i, j); } } } public int setupEntrance() { this.enterCol = random(0, this.width); cells[0][this.enterCol].count = 1; return this.enterCol; } public void setupExit() { int exit = random(0, width - 1); cells[lastRow][exit].exitType += 1; } public Cell startingCell() { return cells[0][enterCol]; } public boolean isPrevColSet(Cell cell) { return 0 != cells[cell.row][cell.col - 1].count; } public boolean isPrevRowSet(Cell cell) { return 0 != cells[cell.row - 1][cell.col].count; } public boolean isNextColSet(Cell cell) { return 0 != cells[cell.row][cell.col + 1].count; } public boolean isNextRowSet(Cell cell) { return 0 != cells[cell.row + 1][cell.col].count; } public Cell getPrevCol(Cell cell) { return cells[cell.row][cell.col - 1]; } public Cell getPrevRow(Cell cell) { return cells[cell.row - 1][cell.col]; } public Cell getNextCol(Cell cell) { return cells[cell.row][cell.col + 1]; } public Cell getNextRow(Cell cell) { return cells[cell.row + 1][cell.col]; } public Cell getFirstUnset(Cell cell) { int col = cell.col; int row = cell.row; Cell newCell; do { if (col != this.lastCol) { col++; } else if (row != this.lastRow) { row++; col = 0; } else { row = 0; col = 0; } } while ((newCell = cells[row][col]).count == 0); return newCell; } } } ================================================ FILE: 02_Amazing/java/AmazingGame.java ================================================ public class AmazingGame { public static void main(String[] args) { Amazing amazing = new Amazing(); amazing.play(); } } ================================================ FILE: 02_Amazing/java/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Oracle Java](https://openjdk.java.net/) ================================================ FILE: 02_Amazing/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 02_Amazing/javascript/amazing.html ================================================ AMAZING
    
    
    
    
    
    
    ================================================
    FILE: 02_Amazing/javascript/amazing.js
    ================================================
    // AMAZING
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    print(tab(28) + "AMAZING PROGRAM\n");
    print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
    print("\n");
    print("\n");
    print("\n");
    print("FOR EXAMPLE TYPE 10,10 AND PRESS ENTER\n");
    print("\n");
    
    // Main program
    async function main()
    {
        while (1) {
            print("WHAT ARE YOUR WIDTH AND LENGTH");
            a = await input();
            h = parseInt(a);
            v2 = parseInt(a.substr(a.indexOf(",") + 1));
            if (h > 1 && v2 > 1)
                break;
            print("MEANINGLESS DIMENSIONS.  TRY AGAIN.\n");
        }
        w = [];
        v = [];
        for (i = 1; i <= h; i++) {
            w[i] = [];
            v[i] = [];
            for (j = 1; j <= v2; j++) {
                w[i][j] = 0;
                v[i][j] = 0;
            }
        }
        print("\n");
        print("\n");
        print("\n");
        print("\n");
        q = 0;
        z = 0;
        x = Math.floor(Math.random() * h + 1);
        for (i = 1; i <= h; i++) {
            if (i == x)
                print(".  ");
            else
                print(".--");
        }
        print(".\n");
        c = 1;
        w[x][1] = c;
        c++;
        r = x;
        s = 1;
        entry = 0;
        while (1) {
            if (entry == 2) {	// Search for a non-explored cell
                do {
                    if (r < h) {
                        r++;
                    } else if (s < v2) {
                        r = 1;
                        s++;
                    } else {
                        r = 1;
                        s = 1;
                    }
                } while (w[r][s] == 0) ;
            }
            if (entry == 0 && r - 1 > 0 && w[r - 1][s] == 0) {	// Can go left?
                if (s - 1 > 0 && w[r][s - 1] == 0) {	// Can go up?
                    if (r < h && w[r + 1][s] == 0) {	// Can go right?
                        // Choose left/up/right
                        x = Math.floor(Math.random() * 3 + 1);
                    } else if (s < v2) {
                        if (w[r][s + 1] == 0) {	// Can go down?
                            // Choose left/up/down
                            x = Math.floor(Math.random() * 3 + 1);
                            if (x == 3)
                                x = 4;
                        } else {
                            x = Math.floor(Math.random() * 2 + 1);
                        }
                    } else if (z == 1) {
                        x = Math.floor(Math.random() * 2 + 1);
                    } else {
                        q = 1;
                        x = Math.floor(Math.random() * 3 + 1);
                        if (x == 3)
                            x = 4;
                    }
                } else if (r < h && w[r + 1][s] == 0) {	// Can go right?
                    if (s < v2) {
                        if (w[r][s + 1] == 0) {	// Can go down?
                            // Choose left/right/down
                            x = Math.floor(Math.random() * 3 + 1);
                        } else {
                            x = Math.floor(Math.random() * 2 + 1);
                        }
                        if (x >= 2)
                            x++;
                    } else if (z == 1) {
                        x = Math.floor(Math.random() * 2 + 1);
                        if (x >= 2)
                            x++;
                    } else {
                        q = 1;
                        x = Math.floor(Math.random() * 3 + 1);
                        if (x >= 2)
                            x++;
                    }
                } else if (s < v2) {
                    if (w[r][s + 1] == 0) {	// Can go down?
                        // Choose left/down
                        x = Math.floor(Math.random() * 2 + 1);
                        if (x == 2)
                            x = 4;
                    } else {
                        x = 1;
                    }
                } else if (z == 1) {
                    x = 1;
                } else {
                    q = 1;
                    x = Math.floor(Math.random() * 2 + 1);
                    if (x == 2)
                        x = 4;
                }
            } else if (s - 1 > 0 && w[r][s - 1] == 0) {	// Can go up?
                if (r < h && w[r + 1][s] == 0) {
                    if (s < v2) {
                        if (w[r][s + 1] == 0)
                            x = Math.floor(Math.random() * 3 + 2);
                        else
                            x = Math.floor(Math.random() * 2 + 2);
                    } else if (z == 1) {
                        x = Math.floor(Math.random() * 2 + 2);
                    } else {
                        q = 1;
                        x = Math.floor(Math.random() * 3 + 2);
                    }
                } else if (s < v2) {
                    if (w[r][s + 1] == 0) {
                        x = Math.floor(Math.random() * 2 + 2);
                        if (x == 3)
                            x = 4;
                    } else {
                        x = 2;
                    }
                } else if (z == 1) {
                    x = 2;
                } else {
                    q = 1;
                    x = Math.floor(Math.random() * 2 + 2);
                    if (x == 3)
                        x = 4;
                }
            } else if (r < h && w[r + 1][s] == 0) {	// Can go right?
                if (s < v2) {
                    if (w[r][s + 1] == 0)
                        x = Math.floor(Math.random() * 2 + 3);
                    else
                        x = 3;
                } else if (z == 1) {
                    x = 3;
                } else {
                    q = 1;
                    x = Math.floor(Math.random() * 2 + 3);
                }
            } else if (s < v2) {
                if (w[r][s + 1] == 0) 	// Can go down?
                    x = 4;
                else {
                    entry = 2;	// Blocked!
                    continue;
                }
            } else if (z == 1) {
                entry = 2;	// Blocked!
                continue;
            } else {
                q = 1;
                x = 4;
            }
            if (x == 1) {	// Left
                w[r - 1][s] = c;
                c++;
                v[r - 1][s] = 2;
                r--;
                if (c == h * v2 + 1)
                    break;
                q = 0;
                entry = 0;
            } else if (x == 2) {	// Up
                w[r][s - 1] = c;
                c++;
                v[r][s - 1] = 1;
                s--;
                if (c == h * v2 + 1)
                    break;
                q = 0;
                entry = 0;
            } else if (x == 3) {	// Right
                w[r + 1][s] = c;
                c++;
                if (v[r][s] == 0)
                    v[r][s] = 2;
                else
                    v[r][s] = 3;
                r++;
                if (c == h * v2 + 1)
                    break;
                entry = 1;
            } else if (x == 4) {	// Down
                if (q != 1) {	// Only if not blocked
                    w[r][s + 1] = c;
                    c++;
                    if (v[r][s] == 0)
                        v[r][s] = 1;
                    else
                        v[r][s] = 3;
                    s++;
                    if (c == h * v2 + 1)
                        break;
                    entry = 0;
                } else {
                    z = 1;
                    if (v[r][s] == 0) {
                        v[r][s] = 1;
                        q = 0;
                        r = 1;
                        s = 1;
                        while (w[r][s] == 0) {
                            if (r < h) {
                                r++;
                            } else if (s < v2) {
                                r = 1;
                                s++;
                            } else {
                                r = 1;
                                s = 1;
                            }
                        }
                        entry = 0;
                    } else {
                        v[r][s] = 3;
                        q = 0;
                        entry = 2;
                    }
                }
            }
        }
        for (j = 1; j <= v2; j++) {
            str = "I";
            for (i = 1; i <= h; i++) {
                if (v[i][j] < 2)
                    str += "  I";
                else
                    str += "   ";
            }
            print(str + "\n");
            str = "";
            for (i = 1; i <= h; i++) {
                if (v[i][j] == 0 || v[i][j] == 2)
                    str += ":--";
                else
                    str += ":  ";
            }
            print(str + ".\n");
        }
    // If you want to see the order of visited cells
    //    for (j = 1; j <= v2; j++) {
    //        str = "I";
    //        for (i = 1; i <= h; i++) {
    //            str += w[i][j] + " ";
    //        }
    //        print(str + "\n");
    //    }
    }
    
    main();
    
    
    ================================================
    FILE: 02_Amazing/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 02_Amazing/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 02_Amazing/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 02_Amazing/perl/amazing.pl
    ================================================
    #! /usr/bin/perl
    use strict;
    use warnings;
    
    # Translated from BASIC by Alex Kapranoff
    
    use feature qw/say/;
    
    # width and height of the maze
    my ($width, $height) = input_dimensions();
    
    # wall masks for all cells
    my @walls;
    
    # flags of previous visitation for all cells
    my %is_visited;
    
    # was the path out of the maze found?
    my $path_found = 0;
    
    # column of entry to the maze in the top line
    my $entry_col = int(rand($width));
    
    # cell coordinates for traversal
    my $col = $entry_col;
    my $row = 0;
    
    $is_visited{$row, $col} = 1;
    
    # looping until we visit every cell
    while (keys %is_visited < $width * $height) {
        if (my @dirs = get_possible_directions()) {
            my $dir = $dirs[rand @dirs];
    
            # modify current cell wall if needed
            $walls[$row]->[$col] |= $dir->[2];
    
            # move the position
            $row += $dir->[0];
            $col += $dir->[1];
    
            # we found the exit!
            if ($row == $height) {
                $path_found = 1;
                --$row;
    
                if ($walls[$row]->[$col] == 1) {
                    ($row, $col) = get_next_branch(0, 0);
                }
            }
            else {
                # modify the new cell wall if needed
                $walls[$row]->[$col] |= $dir->[3];
    
                $is_visited{$row, $col} = 1;
            }
        }
        else {
            ($row, $col) = get_next_branch($row, $col);
        }
    }
    
    unless ($path_found) {
        $walls[-1]->[rand $width] |= 1;
    }
    
    print_maze();
    
    sub input_dimensions {
        # Print the banner and returns the dimensions as two integers > 1.
        # The integers are parsed from the first line of standard input.
        say ' ' x 28, 'AMAZING PROGRAM';
        say ' ' x 15, 'CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY';
        print "\n" x 4;
    
        my ($w, $h) = (0, 0);
    
        while ($w <= 1 || $h <= 1) {
            print 'WHAT ARE YOUR WIDTH AND LENGTH? ';
    
            ($w, $h) =  =~ / \d+ /xg;
    
            if ($w < 1 || $h < 1) {
                say "MEANINGLESS DIMENSIONS.  TRY AGAIN."
            }
        }
    
        print "\n" x 4;
    
        return ($w, $h);
    }
    
    sub get_possible_directions {
        # Returns a list of all directions that are available to go to
        # from the current coordinates. "Down" is available on the last line
        # until we go there once and mark it as the path through the maze.
        #
        # Each returned direction element contains changes to the coordindates and to
        # the wall masks of the previous and next cell after the move.
    
        my @rv;
        # up
        if ($row > 0 && !$is_visited{$row - 1, $col}) {
            push @rv, [-1, 0, 0, 1];
        }
        # left
        if ($col > 0 && !$is_visited{$row, $col - 1}) {
            push @rv, [0, -1, 0, 2];
        }
        # right
        if ($col < $width - 1 && !$is_visited{$row, $col + 1}) {
            push @rv, [0, 1, 2, 0];
        }
        # down
        if ($row < $height - 1 && !$is_visited{$row + 1, $col}
            || $row == $height - 1 && !$path_found
        ) {
            push @rv, [1, 0, 1, 0];
        }
    
        return @rv;
    }
    
    sub get_next_branch {
        # Returns the cell coordinates to start a new maze branch from.
        # It looks for a visited cell starting from passed position and
        # going down in the natural traversal order incrementing column and
        # rows with a rollover to start at the bottom right corner.
        my ($y, $x) = @_;
        do {
            if ($x < $width - 1) {
                ++$x;
            } elsif ($y < $height - 1) {
                ($y, $x) = ($y + 1, 0);
            } else {
                ($y, $x) = (0, 0);
            }
        } while (!$is_visited{$y, $x});
    
        return ($y, $x);
    }
    
    sub print_maze {
        # Print the full maze based on wall masks.
        # For each cell, we mark the absense of the wall to the right with
        # bit 2 and the absense of the wall down with bit 1. Full table:
        # 0 -> both walls are present
        # 1 -> wall down is absent
        # 2 -> wall to the right is absent
        # 3 -> both walls are absent
        say join('.', '', map { $_ == $entry_col ? '  ' : '--' } 0 .. $width - 1), '.';
    
        for my $row (@walls) {
            say join('  ', map { $_ & 2 ? ' ' : 'I' } 0, @$row);
            say join(':', '', map { $_ & 1 ? '  ' : '--' } @$row), '.';
        }
    
        return;
    }
    
    
    ================================================
    FILE: 02_Amazing/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 02_Amazing/python/amazing.py
    ================================================
    import enum
    import random
    from dataclasses import dataclass
    from typing import List, Tuple
    
    # Python translation by Frank Palazzolo - 2/2021
    
    
    class Maze:
        def __init__(self, width: int, length: int) -> None:
            assert width >= 2 and length >= 2
            used: List[List[int]] = []
            walls: List[List[int]] = []
            for _ in range(length):
                used.append([0] * width)
                walls.append([0] * width)
    
            # Pick a random entrance, mark as used
            enter_col = random.randint(0, width - 1)
            used[0][enter_col] = 1
    
            self.used = used
            self.walls = walls
            self.enter_col = enter_col
            self.width = width
            self.length = length
    
        def add_exit(self) -> None:
            """Modifies 'walls' to add an exit to the maze."""
            col = random.randint(0, self.width - 1)
            row = self.length - 1
            self.walls[row][col] = self.walls[row][col] + 1
    
        def display(self) -> None:
            for col in range(self.width):
                if col == self.enter_col:
                    print(".  ", end="")
                else:
                    print(".--", end="")
            print(".")
            for row in range(self.length):
                print("I", end="")
                for col in range(self.width):
                    if self.walls[row][col] < 2:
                        print("  I", end="")
                    else:
                        print("   ", end="")
                print()
                for col in range(self.width):
                    if self.walls[row][col] in [0, 2]:
                        print(":--", end="")
                    else:
                        print(":  ", end="")
                print(".")
    
    
    class Direction(enum.Enum):
        LEFT = 0
        UP = 1
        RIGHT = 2
        DOWN = 3
    
    
    @dataclass
    class Position:
        row: int
        col: int
    
    
    # Give Exit directions nice names
    EXIT_DOWN = 1
    EXIT_RIGHT = 2
    
    
    def main() -> None:
        print_intro()
        width, length = get_maze_dimensions()
        maze = build_maze(width, length)
        maze.display()
    
    
    def print_intro() -> None:
        print(" " * 28 + "AMAZING PROGRAM")
        print(" " * 15 + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n\n\n")
    
    
    def build_maze(width: int, length: int) -> Maze:
        """Build two 2D arrays."""
        #
        # used:
        #   Initially set to zero, unprocessed cells
        #   Filled in with consecutive non-zero numbers as cells are processed
        #
        # walls:
        #   Initially set to zero, (all paths blocked)
        #   Remains 0 if there is no exit down or right
        #   Set to 1 if there is an exit down
        #   Set to 2 if there is an exit right
        #   Set to 3 if there are exits down and right
        assert width >= 2 and length >= 2
    
        maze = Maze(width, length)
        position = Position(row=0, col=maze.enter_col)
        count = 2
    
        while count != width * length + 1:
            possible_dirs = get_possible_directions(maze, position)
    
            # If we can move in a direction, move and make opening
            if len(possible_dirs) != 0:
                position, count = make_opening(maze, possible_dirs, position, count)
            # otherwise, move to the next used cell, and try again
            else:
                while True:
                    if position.col != width - 1:
                        position.col += 1
                    elif position.row != length - 1:
                        position.row, position.col = position.row + 1, 0
                    else:
                        position.row, position.col = 0, 0
                    if maze.used[position.row][position.col] != 0:
                        break
    
        maze.add_exit()
        return maze
    
    
    def make_opening(
        maze: Maze,
        possible_dirs: List[Direction],
        pos: Position,
        count: int,
    ) -> Tuple[Position, int]:
        """
        Attention! This modifies 'used' and 'walls'
        """
        direction = random.choice(possible_dirs)
        if direction == Direction.LEFT:
            pos.col = pos.col - 1
            maze.walls[pos.row][pos.col] = EXIT_RIGHT
        elif direction == Direction.UP:
            pos.row = pos.row - 1
            maze.walls[pos.row][pos.col] = EXIT_DOWN
        elif direction == Direction.RIGHT:
            maze.walls[pos.row][pos.col] = maze.walls[pos.row][pos.col] + EXIT_RIGHT
            pos.col = pos.col + 1
        elif direction == Direction.DOWN:
            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 += 1
        return pos, count
    
    
    def get_possible_directions(maze: Maze, pos: Position) -> List[Direction]:
        """
        Get a list of all directions that are not blocked.
    
        Also ignore hit cells that we have already processed
        """
        possible_dirs = list(Direction)
        if pos.col == 0 or maze.used[pos.row][pos.col - 1] != 0:
            possible_dirs.remove(Direction.LEFT)
        if pos.row == 0 or maze.used[pos.row - 1][pos.col] != 0:
            possible_dirs.remove(Direction.UP)
        if pos.col == maze.width - 1 or maze.used[pos.row][pos.col + 1] != 0:
            possible_dirs.remove(Direction.RIGHT)
        if pos.row == maze.length - 1 or maze.used[pos.row + 1][pos.col] != 0:
            possible_dirs.remove(Direction.DOWN)
        return possible_dirs
    
    
    def get_maze_dimensions() -> Tuple[int, int]:
        while True:
            input_str = input("What are your width and length?")
            if input_str.count(",") == 1:
                width_str, length_str = input_str.split(",")
                width = int(width_str)
                length = int(length_str)
                if width > 1 and length > 1:
                    break
            print("Meaningless dimensions. Try again.")
        return width, length
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 02_Amazing/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    Converted to Ruby (with tons of inspiration from the Python version) by @marcheiligers
    
    Run `ruby amazing.rb`.
    
    Run `DEBUG=1 ruby amazing.ruby` to see how it works (requires at least Ruby 2.7).
    
    
    ================================================
    FILE: 02_Amazing/ruby/amazing.rb
    ================================================
    # frozen_string_literal: true
    
    DEBUG = !ENV['DEBUG'].nil?
    
    require 'io/console' if DEBUG
    
    # BASIC arrays are 1-based, unlike Ruby 0-based arrays,
    # and this class simulates that. BASIC arrays are zero-filled,
    # which is also done here. While we could easily update the
    # algorithm to work with zero-based arrays, this class makes
    # the problem easier to reason about, row or col 1 are the
    # first row or column.
    class BasicArrayTwoD
      def initialize(rows, cols)
        @val = Array.new(rows) { Array.new(cols, 0) }
      end
    
      def [](row, col = nil)
        if col
          @val[row - 1][col - 1]
        else
          @val[row - 1]
        end
      end
    
      def []=(row, col, n)
        @val[row - 1][col - 1] = n
      end
    
      def to_s(width: max_width, row_hilite: nil, col_hilite: nil)
        @val.map.with_index do |row, row_index|
          row.map.with_index do |val, col_index|
            if row_hilite == row_index + 1 && col_hilite == col_index + 1
              "[#{val.to_s.center(width)}]"
            else
              val.to_s.center(width + 2)
            end
          end.join
        end.join("\n")
      end
    
      def max_width
        @val.flat_map { |row| row.map { |val| val.to_s.length } }.sort.last
      end
    end
    
    class Maze
      EXIT_DOWN = 1
      EXIT_RIGHT = 2
    
      # Set up a constant hash for directions
      # The values represent the direction of the move as changes to row, col
      # and the type of exit when moving in that direction
      DIRECTIONS = {
        left: { row: 0, col: -1, exit: EXIT_RIGHT },
        up: { row: -1, col: 0, exit: EXIT_DOWN },
        right: { row: 0, col: 1, exit: EXIT_RIGHT },
        down: { row: 1, col: 0, exit: EXIT_DOWN }
      }.freeze
    
      attr_reader :width, :height, :used, :walls, :entry
    
      def initialize(width, height)
        @width = width
        @height = height
    
        @used = BasicArrayTwoD.new(height, width)
        @walls = BasicArrayTwoD.new(height, width)
    
        create
      end
    
      def draw
        # Print the maze
        draw_top(entry, width)
        (1..height - 1).each do |row|
          draw_row(walls[row])
        end
        draw_bottom(walls[height])
      end
    
      private
    
      def create
        # entry represents the location of the opening
        @entry = (rand * width).round + 1
    
        # Set up our current row and column, starting at the top and the locations of the opening
        row = 1
        col = entry
        c = 1
        used[row, col] = c # This marks the opening in the first row
        c += 1
    
        while c != width * height + 1 do
          debug walls, row, col
          # remove possible directions that are blocked or
          # hit cells that we have already processed
          possible_dirs = DIRECTIONS.reject do |dir, change|
            nrow = row + change[:row]
            ncol = col + change[:col]
            nrow < 1 || nrow > height || ncol < 1 || ncol > width || used[nrow, ncol] != 0
          end.keys
    
          # If we can move in a direction, move and make opening
          if possible_dirs.size != 0
            direction = possible_dirs.sample
            change = DIRECTIONS[direction] # pick a random direction
            if %i[left up].include?(direction)
              row += change[:row]
              col += change[:col]
              walls[row, col] = change[:exit]
            else
              walls[row, col] += change[:exit]
              row += change[:row]
              col += change[:col]
            end
            used[row, col] = c
            c = c + 1
          # otherwise, move to the next used cell, and try again
          else
            loop do
              if col != width
                col += 1
              elsif row != height
                row += 1
                col = 1
              else
                row = col = 1
              end
              break if used[row, col] != 0
              debug walls, row, col
            end
          end
        end
    
        # Add a random exit
        walls[height, (rand * width).round] += 1
      end
    
      def draw_top(entry, width)
        (1..width).each do |i|
          if i == entry
            print i == 1 ? '┏  ' : '┳  '
          else
            print i == 1 ? '┏━━' : '┳━━'
          end
        end
    
        puts '┓'
      end
    
      def draw_row(row)
        print '┃'
        row.each.with_index do |val, col|
          print val < 2 ? '  ┃' : '   '
        end
        puts
        row.each.with_index do |val, col|
          print val == 0 || val == 2 ? (col == 0 ? '┣━━' : '╋━━') : (col == 0 ? '┃  ' : '┫  ')
        end
        puts '┫'
      end
    
      def draw_bottom(row)
        print '┃'
        row.each.with_index do |val, col|
          print val < 2 ? '  ┃' : '   '
        end
        puts
        row.each.with_index do |val, col|
          print val == 0 || val == 2 ? (col == 0 ? '┗━━' : '┻━━') : (col == 0 ? '┗  ' : '┻  ')
        end
        puts '┛'
      end
    
      def debug(walls, row, col)
        return unless DEBUG
    
        STDOUT.clear_screen
        puts walls.to_s(row_hilite: row, col_hilite: col)
        sleep 0.1
      end
    end
    
    class Amazing
      def run
        draw_header
    
        width, height = ask_dimensions
        while width <= 1 || height <= 1
          puts "MEANINGLESS DIMENSIONS.  TRY AGAIN."
          width, height = ask_dimensions
        end
    
        maze = Maze.new(width, height)
        puts "\n" * 3
        maze.draw
      end
    
      def draw_header
        puts ' ' * 28 + 'AMAZING PROGRAM'
        puts ' ' * 15 + 'CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY'
        puts "\n" * 3
      end
    
      def ask_dimensions
        print 'WHAT ARE YOUR WIDTH AND HEIGHT? '
        width = gets.to_i
        print '?? '
        height = gets.to_i
        [width, height]
      end
    end
    
    Amazing.new.run
    
    
    ================================================
    FILE: 02_Amazing/rust/Cargo.toml
    ================================================
    [package]
    name = "rust"
    version = "0.1.0"
    edition = "2018"
    
    # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
    
    [dependencies]
    rand = "0.8.5"
    
    
    ================================================
    FILE: 02_Amazing/rust/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Rust](https://www.rust-lang.org/) by Anthony Rubick [AnthonyMichaelTDM](https://github.com/AnthonyMichaelTDM)
    
    
    ================================================
    FILE: 02_Amazing/rust/src/main.rs
    ================================================
    use rand::{Rng, thread_rng, prelude::SliceRandom};
    use  std::{io, collections::HashSet};
    
    fn main() {
        //DATA
        enum Direction {
            LEFT=0,
            UP=1,
            RIGHT=2,
            DOWN=3,
        }
        impl Direction {
            fn val(&self) -> usize {
                match self {
                    Direction::LEFT=>0,
                    Direction::UP=>1,
                    Direction::RIGHT=>2,
                    Direction::DOWN=>3,
                }
            }
        }
        const EXIT_DOWN:usize = 1;
        const EXIT_RIGHT:usize = 2;
        let mut rng = thread_rng(); //rng
        /*
        vector of:
            vectors of:
                integers
                    Initially set to 0, unprocessed cells.
                    Filled in with consecutive non-zero numbers as cells are processed
        */
        let mut used; //2d vector
        /*
        vector of:
            vectors of:
                integers
                    Remains 0 if there is no exit down or right
                    Set to 1 if there is an exit down
                    Set to 2 if there is an exit right
                    Set to 3 if there are exits down and right
        */
        let mut walls; //2d vector
        let width;
        let height;
        let entrance_column; //rng, column of entrance
        let mut row;
        let mut col;
        let mut count;
    
    
    
        //print welcome message
        println!("
                     AMAZING PROGRAM
        CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n\n\n\n");
    
        //prompt for input
        width = get_user_input("What is your width?");
        print!("\n"); //one blank line below
        height = get_user_input("What is your height?");
        print!("\n\n\n\n");//4 blank lines below
    
        //generate maze
        //initialize used and wall vectors
        //2d vectors when you don't know the sizes at compile time are wierd, but here's how it's done :)
        used = vec![0; (width * height) as usize];
        let mut used: Vec<_> = used.as_mut_slice().chunks_mut(width as usize).collect();
        let used = used.as_mut_slice(); //accessible as used[][]
        //2d vectors when you don't know the sizes at compile time are wierd, but here's how it's done :)
        walls = vec![0; (width * height) as usize];
        let mut walls: Vec<_> = walls.as_mut_slice().chunks_mut(width as usize).collect();
        let walls = walls.as_mut_slice(); //accessible as walls[][]
    
        entrance_column=rng.gen_range(0..width-1);
        row = 0;
        col = entrance_column;
        count = 1;
        used[row][col] = count;
        count += 1;
    
        while count != width*height + 1 {
            //remove possible directions that are blocked or
            //hit cells already processed
            let mut possible_directions: HashSet = vec![Direction::LEFT.val(),Direction::UP.val(),Direction::RIGHT.val(),Direction::DOWN.val()].into_iter().collect(); //create it as a vector bc that's easy, then convert it to a hashset
            if col==0 || used[row][col-1]!=0 {
                possible_directions.remove(&Direction::LEFT.val());
            }
            if row==0 || used[row-1][col]!=0 {
                possible_directions.remove(&Direction::UP.val());
            }
            if col==width-1 || used[row][col+1]!=0 {
                possible_directions.remove(&Direction::RIGHT.val());
            }
            if row==height-1 || used[row+1][col]!=0 {
                possible_directions.remove(&Direction::DOWN.val());
            }
    
            //If we can move in a direction, move and make opening
            if possible_directions.len() != 0 { //all values in possible_directions are not NONE
                let pos_dir_vec: Vec<_> = possible_directions.into_iter().collect(); // convert the set to a vector to get access to the choose method
                //select a random direction
                match pos_dir_vec.choose(&mut rng).expect("error") {
                    0=> {
                        col -= 1;
                        walls[row][col] = EXIT_RIGHT;
                    },
                    1=> {
                        row -= 1;
                        walls[row][col] = EXIT_DOWN;
                    },
                    2=>{
                        walls[row][col] = walls[row][col] + EXIT_RIGHT;
                        col += 1;
                    },
                    3=>{
                        walls[row][col] = walls[row][col] + EXIT_DOWN;
                        row += 1;
                    },
                    _=>{},
                }
                used[row][col]=count;
                count += 1;
            }
            //otherwise, move to the next used cell, and try again
            else {
                loop {
                    if col != width-1 {col += 1;}
                    else if row != height-1 {row+=1; col=0;}
                    else {row=0;col=0;}
    
                    if used[row][col] != 0 {break;}
                }
            }
        }
        // Add a random exit
        col=rng.gen_range(0..width);
        row=height-1;
        walls[row][col]+=1;
    
        //print maze
        //first line
        for c in 0..width {
            if c == entrance_column {
                print!(".  ");
            }
            else {
                print!(".--");
            }
        }
        println!(".");
        //rest of maze
        for r in 0..height {
            print!("I");
            for c in 0..width {
                if walls[r][c]<2 {print!("  I");}
                else {print!("   ");}
            }
            println!();
            for c in 0..width {
                if walls[r][c] == 0 || walls[r][c]==2 {print!(":--");}
                else {print!(":  ");}
            }
            println!(".");
        }
    
        //stops the program from ending until you give input, useful when running a compiled .exe
        println!("\n\npress ENTER to exit");
        io::stdin().read_line(&mut String::new()).expect("closing");
    }
    
    fn get_user_input(prompt: &str) -> usize {
        //DATA
        let mut raw_input = String::new(); // temporary variable for user input that can be parsed later
    
        //input loop
        return loop {
    
            //print prompt
            println!("{}", prompt);
    
            //read user input from standard input, and store it to raw_input
            raw_input.clear(); //clear input
            io::stdin().read_line(&mut raw_input).expect( "CANNOT READ INPUT!");
    
            //from input, try to read a number
            match raw_input.trim().parse::() {
                Ok(i) => {
                    if i>1 { //min size 1
                        break i; // this escapes the loop, returning i
                    }
                    else {
                        println!("INPUT OUT OF RANGE.  TRY AGAIN.");
                        continue;// run the loop again
                    }
                }
                Err(e) => {
                    println!("MEANINGLESS DIMENSION.  TRY AGAIN.  {}", e.to_string().to_uppercase());
                    continue; // run the loop again
                }
            };
        }
    }
    
    
    ================================================
    FILE: 02_Amazing/vbnet/Amazing.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Amazing", "Amazing.vbproj", "{FB9DF301-CB34-4C9A-8823-F034303F5DA3}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{FB9DF301-CB34-4C9A-8823-F034303F5DA3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{FB9DF301-CB34-4C9A-8823-F034303F5DA3}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{FB9DF301-CB34-4C9A-8823-F034303F5DA3}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{FB9DF301-CB34-4C9A-8823-F034303F5DA3}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 02_Amazing/vbnet/Amazing.vbproj
    ================================================
    
      
        Exe
        Amazing
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 02_Amazing/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 02_Amazing/vbnet/program.vb
    ================================================
    Imports System
    
    Module Program
    
        Enum Directions
            SolveAndReset = 0
            Left = 1
            Up = 2
            Right = 3
            Down = 4
        End Enum
    
        'Program State
        Dim Width As Integer = 0, Height As Integer = 0, Q As Integer = 0, CellsVisited As Integer = 2, curCol As Integer, curRow As Integer = 1
        Dim SolutionCompleted As Boolean = False
        Dim CellVisitHistory(,) As Integer
        Dim CellState(,) As Integer
    
        Dim rnd As New Random()
    
        Public ReadOnly Property BlockedLeft As Boolean
            Get
                Return curCol - 1 = 0 OrElse CellVisitHistory(curCol - 1, curRow) <> 0
            End Get
        End Property
        Public ReadOnly Property BlockedAbove As Boolean
            Get
                Return curRow - 1 = 0 OrElse CellVisitHistory(curCol, curRow - 1) <> 0
            End Get
        End Property
        Public ReadOnly Property BlockedRight As Boolean
            Get
                Return curCol = Width OrElse CellVisitHistory(curCol + 1, curRow) <> 0
            End Get
        End Property
        'Note: "BlockedBelow" does NOT include checking if we have a solution!
        Public ReadOnly Property BlockedBelow As Boolean
            Get
                Return curRow = Height OrElse CellVisitHistory(curCol, curRow + 1) <> 0
            End Get
        End Property
        Public ReadOnly Property OnBottomRow As Boolean
            Get
                Return curRow.Equals(Height)
            End Get
        End Property
    
        Sub Main(args As String())
            Const header As String =
    "             AMAZING PROGRAM
     CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY
    
    
    
    "
            Console.WriteLine(header)
    
            While Width <= 1 OrElse Height <= 1
                Console.Write("WHAT ARE YOUR WIDTH AND LENGTH? ")
    
                'We no longer have the old convenient INPUT command, so need to parse out the inputs
                Dim parts = Console.ReadLine().Split(","c).Select(Function(s) Convert.ToInt32(s.Trim())).ToList()
                Width = parts(0)
                Height = parts(1)
    
                If Width <= 1 OrElse Height <= 1 Then Console.WriteLine($"MEANINGLESS DIMENSIONS.  TRY AGAIN.{vbCrLf}")
            End While
    
            ReDim CellVisitHistory(Width, Height), CellState(Width, Height)
    
            Console.WriteLine("
    
    ")
    
            curCol = rnd.Next(1, Width + 1) 'Starting X position
            CellVisitHistory(curCol, 1) = 1
            Dim startXPos As Integer = curCol 'we need to know this at the end to print opening line
    
            Dim keepGoing As Boolean = True
            While keepGoing
                If BlockedLeft Then
                    keepGoing = ChoosePath_BlockedToTheLeft()
                ElseIf BlockedAbove Then
                    keepGoing = ChoosePath_BlockedAbove()
                ElseIf BlockedRight Then
                    keepGoing = ChoosePath_BlockedToTheRight()
                Else
                    keepGoing = SelectRandomDirection(Directions.Left, Directions.Up, Directions.Right) 'Go anywhere but down
                End If
            End While
    
            PrintFinalResults(startXPos)
        End Sub
    
        Public Sub ResetCurrentPosition()
            Do
                If curCol <> Width Then 'not at the right edge
                    curCol += 1
                ElseIf curRow <> Height Then 'not at the bottom
                    curCol = 1
                    curRow += 1
                Else
                    curCol = 1
                    curRow = 1
                End If
            Loop While CellVisitHistory(curCol, curRow) = 0
        End Sub
    
        Dim methods() As Func(Of Boolean) = {AddressOf MarkSolvedAndResetPosition, AddressOf GoLeft, AddressOf GoUp, AddressOf GoRight, AddressOf GoDown}
        Public Function SelectRandomDirection(ParamArray possibles() As Directions) As Boolean
            Dim x As Integer = rnd.Next(0, possibles.Length)
            Return methods(possibles(x))()
        End Function
    
        Public Function ChoosePath_BlockedToTheLeft() As Boolean
            If BlockedAbove Then
                If BlockedRight Then
                    If curRow <> Height Then
                        If CellVisitHistory(curCol, curRow + 1) <> 0 Then ' Can't go down, but not at the edge...blocked. Reset and try again
                            ResetCurrentPosition()
                            Return True
                        Else
                            Return GoDown()
                        End If
                    ElseIf SolutionCompleted Then 'Can't go Down (there's already another solution)
                        ResetCurrentPosition()
                        Return True
                    Else 'Can't go LEFT, UP, RIGHT, or DOWN, but we're on the bottom and there's no solution yet
                        Return MarkSolvedAndResetPosition()
                    End If
                ElseIf BlockedBelow Then
                    Return GoRight()
                ElseIf Not OnBottomRow Then
                    Return SelectRandomDirection(Directions.Right, Directions.Down)
                ElseIf SolutionCompleted Then 'Can only go right, and we're at the bottom
                    Return GoRight()
                Else 'Can only go right, we're at the bottom, and there's not a solution yet
                    Return SelectRandomDirection(Directions.Right, Directions.SolveAndReset)
                End If
                '== Definitely can go Up ==
            ElseIf BlockedRight Then
                If BlockedBelow Then
                    Return GoUp()
                ElseIf Not OnBottomRow Then
                    Return SelectRandomDirection(Directions.Up, Directions.Down)
                ElseIf SolutionCompleted Then 'We're on the bottom row, can only go up
                    Return GoUp()
                Else 'We're on the bottom row, can only go up, but there's no solution
                    Return SelectRandomDirection(Directions.Up, Directions.SolveAndReset)
                End If
                '== Definitely can go Up and Right ==
            ElseIf BlockedBelow Then
                Return SelectRandomDirection(Directions.Up, Directions.Right)
            ElseIf Not OnBottomRow Then
                Return SelectRandomDirection(Directions.Up, Directions.Right, Directions.Down)
            ElseIf SolutionCompleted Then 'at the bottom, but already have a solution
                Return SelectRandomDirection(Directions.Up, Directions.Right)
            Else
                Return SelectRandomDirection(Directions.Up, Directions.Right, Directions.SolveAndReset)
            End If
        End Function
    
        Public Function ChoosePath_BlockedAbove() As Boolean
            'No need to check the left side, only called from the "keepGoing" loop where LEFT is already cleared
            If BlockedRight Then
                If BlockedBelow Then
                    Return GoLeft()
                ElseIf Not OnBottomRow Then
                    Return SelectRandomDirection(Directions.Left, Directions.Down)
                ElseIf SolutionCompleted Then 'Can't go down because there's already a solution
                    Return GoLeft()
                Else 'At the bottom, no solution yet...
                    Return SelectRandomDirection(Directions.Left, Directions.SolveAndReset)
                End If
            ElseIf BlockedBelow Then
                Return SelectRandomDirection(Directions.Left, Directions.Right)
            ElseIf Not OnBottomRow Then
                Return SelectRandomDirection(Directions.Left, Directions.Right, Directions.Down)
            ElseIf SolutionCompleted Then
                Return SelectRandomDirection(Directions.Left, Directions.Right)
            Else
                Return SelectRandomDirection(Directions.Left, Directions.Right, Directions.SolveAndReset)
            End If
        End Function
    
        Public Function ChoosePath_BlockedToTheRight() As Boolean
            'No need to check Left or Up, only called from the "keepGoing" loop where LEFT and UP are already cleared
            If BlockedRight Then 'Can't go Right -- why? we knew this when calling the function
                If BlockedBelow Then
                    Return SelectRandomDirection(Directions.Left, Directions.Up)
                ElseIf Not OnBottomRow Then
                    Return SelectRandomDirection(Directions.Left, Directions.Up, Directions.Down)
                ElseIf SolutionCompleted Then
                    Return SelectRandomDirection(Directions.Left, Directions.Up)
                Else
                    Return SelectRandomDirection(Directions.Left, Directions.Up, Directions.SolveAndReset)
                End If
            Else 'Should never get here
                Return SelectRandomDirection(Directions.Left, Directions.Up, Directions.Right) 'Go Left, Up, or Right (but path is blocked?)
            End If
        End Function
    
        Public Sub PrintFinalResults(startPos As Integer)
            For i As Integer = 0 To Width - 1
                If i = startPos Then Console.Write(".  ") Else Console.Write(".--")
            Next
            Console.WriteLine(".")
    
            If Not SolutionCompleted Then 'Pick a random exit
                Dim X As Integer = rnd.Next(1, Width + 1)
                If CellState(X, Height) = 0 Then
                    CellState(X, Height) = 1
                Else
                    CellState(X, Height) = 3
                End If
            End If
    
            For j As Integer = 1 To Height
                Console.Write("I")
                For i As Integer = 1 To Width
                    If CellState(i, j) < 2 Then
                        Console.Write("  I")
                    Else
                        Console.Write("   ")
                    End If
                Next
                Console.WriteLine()
    
                For i As Integer = 1 To Width
                    If CellState(i, j) = 0 OrElse CellState(i, j) = 2 Then
                        Console.Write(":--")
                    Else
                        Console.Write(":  ")
                    End If
                Next
                Console.WriteLine(".")
            Next
        End Sub
    
        Public Function GoLeft() As Boolean
            curCol -= 1
            CellVisitHistory(curCol, curRow) = CellsVisited
            CellsVisited += 1
            CellState(curCol, curRow) = 2
            If CellsVisited > Width * Height Then Return False
            Q = 0
            Return True
        End Function
    
        Public Function GoUp() As Boolean
            curRow -= 1
            CellVisitHistory(curCol, curRow) = CellsVisited
            CellsVisited += 1
            CellState(curCol, curRow) = 1
            If CellsVisited > Width * Height Then Return False
            Q = 0
            Return True
        End Function
    
        Public Function GoRight() As Boolean
            CellVisitHistory(curCol + 1, curRow) = CellsVisited
            CellsVisited += 1
            If CellState(curCol, curRow) = 0 Then CellState(curCol, curRow) = 2 Else CellState(curCol, curRow) = 3
            curCol += 1
            If CellsVisited > Width * Height Then Return False
            Return ChoosePath_BlockedToTheLeft()
        End Function
    
        Public Function GoDown() As Boolean
            If Q = 1 Then Return MarkSolvedAndResetPosition()
    
            CellVisitHistory(curCol, curRow + 1) = CellsVisited
            CellsVisited += 1
            If CellState(curCol, curRow) = 0 Then CellState(curCol, curRow) = 1 Else CellState(curCol, curRow) = 3
            curRow += 1
            If CellsVisited > Width * Height Then Return False
            Return True
        End Function
    
        Public Function MarkSolvedAndResetPosition() As Boolean
            ' AlWAYS returns true
            SolutionCompleted = True
            Q = 1
            If CellState(curCol, curRow) = 0 Then
                CellState(curCol, curRow) = 1
                curCol = 1
                curRow = 1
                If CellVisitHistory(curCol, curRow) = 0 Then ResetCurrentPosition()
            Else
                CellState(curCol, curRow) = 3
                ResetCurrentPosition()
            End If
            Return True
        End Function
    End Module
    
    
    ================================================
    FILE: 03_Animal/README.md
    ================================================
    ### Animal
    
    Unlike other computer games in which the computer picks a number or letter and you must guess what it is, in this game _you_ think of an animal and the _computer_ asks you questions and tries to guess the name of your animal. If the computer guesses incorrectly, it will ask you for a question that differentiates the animal you were thinking of. In this way the computer “learns” new animals. Questions to differentiate new animals should be input without a question mark.
    
    This version of the game does not have a SAVE feature. If your system allows, you may modify the program to save and reload the array when you want to play the game again. This way you can save what the computer learns over a series of games.
    
    At any time if you reply “LIST” to the question “ARE YOU THINKING OF AN ANIMAL,” the computer will tell you all the animals it knows so far.
    
    The program starts originally by knowing only FISH and BIRD. As you build up a file of animals you should use broad, general questions first and then narrow down to more specific ones with later animals. For example, if an elephant was to be your first animal, the computer would ask for a question to distinguish an elephant from a bird. Naturally, there are hundreds of possibilities, however, if you plan to build a large file of animals a good question would be “IS IT A MAMMAL.”
    
    This program can be easily modified to deal with categories of things other than animals by simply modifying the initial data and the dialogue references to animals. In an educational environment, this would be a valuable program to teach the distinguishing characteristics of many classes of objects — rock formations, geography, marine life, cell structures, etc.
    
    Originally developed by Arthur Luehrmann at Dartmouth College, Animal was subsequently shortened and modified by Nathan Teichholtz at DEC and Steve North at Creative Computing.
    
    ---
    
    As published in Basic Computer Games (1978)
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=4)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=19)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 03_Animal/animal.bas
    ================================================
    10 PRINT TAB(32);"ANIMAL"
    20 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    30 PRINT: PRINT: PRINT
    40 PRINT "PLAY 'GUESS THE ANIMAL'"
    45 PRINT
    50 PRINT "THINK OF AN ANIMAL AND THE COMPUTER WILL TRY TO GUESS IT."
    60 PRINT
    70 DIM A$(200)
    80 FOR I=0 TO 3
    90 READ A$(I)
    100 NEXT I
    110 N=VAL(A$(0))
    120 REM          MAIN CONTROL SECTION
    130 INPUT "ARE YOU THINKING OF AN ANIMAL";A$
    140 IF A$="LIST" THEN 600
    150 IF LEFT$(A$,1)<>"Y" THEN 120
    160 K=1
    170 GOSUB 390
    180 IF LEN(A$(K))=0 THEN 999
    190 IF LEFT$(A$(K),2)="\Q" THEN 170
    200 PRINT "IS IT A ";RIGHT$(A$(K),LEN(A$(K))-2);
    210 INPUT A$
    220 A$=LEFT$(A$,1)
    230 IF LEFT$(A$,1)="Y" THEN PRINT "WHY NOT TRY ANOTHER ANIMAL?": GOTO 120
    240 INPUT "THE ANIMAL YOU WERE THINKING OF WAS A ";V$
    250 PRINT "PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A"
    260 PRINT V$;" FROM A ";RIGHT$(A$(K),LEN(A$(K))-2)
    270 INPUT X$
    280 PRINT "FOR A ";V$;" THE ANSWER WOULD BE ";
    290 INPUT A$
    300 A$=LEFT$(A$,1): IF A$<>"Y" AND A$<>"N" THEN 280
    310 IF A$="Y" THEN B$="N"
    320 IF A$="N" THEN B$="Y"
    330 Z1=VAL(A$(0))
    340 A$(0)=STR$(Z1+2)
    350 A$(Z1)=A$(K)
    360 A$(Z1+1)="\A"+V$
    370 A$(K)="\Q"+X$+"\"+A$+STR$(Z1+1)+"\"+B$+STR$(Z1)+"\"
    380 GOTO 120
    390 REM     SUBROUTINE TO PRINT QUESTIONS
    400 Q$=A$(K)
    410 FOR Z=3 TO LEN(Q$)
    415 IF MID$(Q$,Z,1)<>"\" THEN PRINT MID$(Q$,Z,1);: NEXT Z
    420 INPUT C$
    430 C$=LEFT$(C$,1)
    440 IF C$<>"Y" AND C$<>"N" THEN 410
    450 T$="\"+C$
    455 FOR X=3 TO LEN(Q$)-1
    460 IF MID$(Q$,X,2)=T$ THEN 480
    470 NEXT X
    475 STOP
    480 FOR Y=X+1 TO LEN(Q$)
    490 IF MID$(Q$,Y,1)="\" THEN 510
    500 NEXT Y
    505 STOP
    510 K=VAL(MID$(Q$,X+2,Y-X-2))
    520 RETURN
    530 DATA "4","\QDOES IT SWIM\Y2\N3\","\AFISH","\ABIRD"
    600 PRINT:PRINT "ANIMALS I ALREADY KNOW ARE:"
    605 X=0
    610 FOR I=1 TO 200
    620 IF LEFT$(A$(I),2)<>"\A" THEN 650
    624 PRINT TAB(15*X);
    630 FOR Z=3 TO LEN(A$(I))
    640 IF MID$(A$(I),Z,1)<>"\" THEN PRINT MID$(A$(I),Z,1);: NEXT Z
    645 X=X+1: IF X=4 THEN X=0: PRINT
    650 NEXT I
    660 PRINT
    670 PRINT
    680 GOTO 120
    999 END
    
    
    ================================================
    FILE: 03_Animal/csharp/Animal.csproj
    ================================================
    
    
      
        Exe
        net5.0
      
    
    
    
    
    ================================================
    FILE: 03_Animal/csharp/Animal.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30907.101
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Animal", "Animal.csproj", "{D9A8DAB5-1B29-44A9-B13F-CED17B5A22B9}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{D9A8DAB5-1B29-44A9-B13F-CED17B5A22B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{D9A8DAB5-1B29-44A9-B13F-CED17B5A22B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{D9A8DAB5-1B29-44A9-B13F-CED17B5A22B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{D9A8DAB5-1B29-44A9-B13F-CED17B5A22B9}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {064879D3-A288-4980-8DC4-59653D5EE2DD}
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 03_Animal/csharp/Branch.cs
    ================================================
    namespace Animal
    {
        public class Branch
        {
            public string Text { get; set; }
    
            public bool IsEnd => Yes == null && No == null;
    
            public Branch Yes { get; set; }
    
            public Branch No { get; set; }
    
            public override string ToString()
            {
                return $"{Text} : IsEnd {IsEnd}";
            }
        }
    }
    
    
    ================================================
    FILE: 03_Animal/csharp/Program.cs
    ================================================
    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    using Animal;
    
    Console.WriteLine(new string(' ', 32) + "ANIMAL");
    Console.WriteLine(new string(' ', 15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
    Console.WriteLine();
    Console.WriteLine();
    Console.WriteLine();
    Console.WriteLine("PLAY 'GUESS THE ANIMAL'");
    Console.WriteLine();
    Console.WriteLine("THINK OF AN ANIMAL AND THE COMPUTER WILL TRY TO GUESS IT.");
    Console.WriteLine();
    
    // Root of the question and answer tree
    Branch rootBranch = new Branch
    {
        Text = "DOES IT SWIM",
        Yes = new Branch { Text = "FISH" },
        No = new Branch { Text = "BIRD" }
    };
    
    string[] TRUE_INPUTS = { "Y", "YES", "T", "TRUE" };
    string[] FALSE_INPUTS = { "N", "NO", "F", "FALSE" };
    
    
    while (true)
    {
        MainGameLoop();
    }
    
    void MainGameLoop()
    {
        // Wait fora YES or LIST command
        string input = null;
        while (true)
        {
            input = GetInput("ARE YOU THINKING OF AN ANIMAL");
            if (IsInputListCommand(input))
            {
                ListKnownAnimals(rootBranch);
            }
            else if (IsInputYes(input))
            {
                break;
            }
        }
    
        // Walk through the tree following the YES and NO
        // branches based on user input.
        Branch currentBranch = rootBranch;
        while (!currentBranch.IsEnd)
        {
            while (true)
            {
                input = GetInput(currentBranch.Text);
                if (IsInputYes(input))
                {
                    currentBranch = currentBranch.Yes;
                    break;
                }
                else if (IsInputNo(input))
                {
                    currentBranch = currentBranch.No;
                    break;
                }
            }
        }
    
        // Was the answer correct?
        input = GetInput($"IS IT A {currentBranch.Text}");
        if (IsInputYes(input))
        {
            Console.WriteLine("WHY NOT TRY ANOTHER ANIMAL?");
            return;
        }
    
        // Interview the user to add a new question and answer
        // branch to the tree
        string newAnimal = GetInput("THE ANIMAL YOU WERE THINKING OF WAS A");
        string newQuestion = GetInput($"PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A {newAnimal} FROM A {currentBranch.Text}");
        string newAnswer = null;
        while (true)
        {
            newAnswer = GetInput($"FOR A {newAnimal} THE ANSWER WOULD BE");
            if (IsInputNo(newAnswer))
            {
                currentBranch.No = new Branch { Text = newAnimal };
                currentBranch.Yes = new Branch { Text = currentBranch.Text };
                currentBranch.Text = newQuestion;
                break;
            }
            else if (IsInputYes(newAnswer))
            {
                currentBranch.Yes = new Branch { Text = newAnimal };
                currentBranch.No = new Branch { Text = currentBranch.Text };
                currentBranch.Text = newQuestion;
                break;
            }
        }
    }
    
    string GetInput(string prompt)
    {
        Console.Write($"{prompt}? ");
        string result = Console.ReadLine();
        if (string.IsNullOrWhiteSpace(result))
        {
            return GetInput(prompt);
        }
    
        return result.Trim().ToUpper();
    }
    
    bool IsInputYes(string input) => TRUE_INPUTS.Contains(input.ToUpperInvariant().Trim());
    
    bool IsInputNo(string input) => FALSE_INPUTS.Contains(input.ToUpperInvariant().Trim());
    
    bool IsInputListCommand(string input) => input.ToUpperInvariant().Trim() == "LIST";
    
    string[] GetKnownAnimals(Branch branch)
    {
        List result = new List();
        if (branch.IsEnd)
        {
            return new[] { branch.Text };
        }
        else
        {
            result.AddRange(GetKnownAnimals(branch.Yes));
            result.AddRange(GetKnownAnimals(branch.No));
            return result.ToArray();
        }
    }
    
    void ListKnownAnimals(Branch branch)
    {
        string[] animals = GetKnownAnimals(branch);
        for (int x = 0; x < animals.Length; x++)
        {
            int column = (x % 4);
            if (column == 0)
            {
                Console.WriteLine();
            }
    
            Console.Write(new string(' ', column == 0 ? 0 : 15) + animals[x]);
        }
        Console.WriteLine();
    }
    
    
    ================================================
    FILE: 03_Animal/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 03_Animal/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 03_Animal/java/src/Animal.java
    ================================================
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Locale;
    import java.util.Scanner;
    import java.util.stream.Collectors;
    
    /**
     * ANIMAL
     * 

    * Converted from BASIC to Java by Aldrin Misquitta (@aldrinm) * The original BASIC program uses an array to maintain the questions and answers and to decide which question to * ask next. Updated this Java implementation to use a tree instead of the earlier faulty one based on a list (thanks @patimen). * * Bonus option: TREE --> prints the game decision data as a tree to visualize/debug the state of the game */ public class Animal { public static void main(String[] args) { printIntro(); Scanner scan = new Scanner(System.in); Node root = new QuestionNode("DOES IT SWIM", new AnimalNode("FISH"), new AnimalNode("BIRD")); boolean stopGame = false; while (!stopGame) { String choice = readMainChoice(scan); switch (choice) { case "TREE": printTree(root); break; case "LIST": printKnownAnimals(root); break; case "Q": case "QUIT": stopGame = true; break; default: if (choice.toUpperCase(Locale.ROOT).startsWith("Y")) { Node current = root; //where we are in the question tree Node previous; //keep track of parent of current in order to place new questions later on. while (current instanceof QuestionNode) { var currentQuestion = (QuestionNode) current; var reply = askQuestionAndGetReply(currentQuestion, scan); previous = current; current = reply ? currentQuestion.getTrueAnswer() : currentQuestion.getFalseAnswer(); if (current instanceof AnimalNode) { //We have reached a animal node, so offer it as the guess var currentAnimal = (AnimalNode) current; System.out.printf("IS IT A %s ? ", currentAnimal.getAnimal()); var animalGuessResponse = readYesOrNo(scan); if (animalGuessResponse) { //we guessed right! end this round System.out.println("WHY NOT TRY ANOTHER ANIMAL?"); } else { //we guessed wrong :(, ask for feedback //cast previous to QuestionNode since we know at this point that it is not a leaf node askForInformationAndSave(scan, currentAnimal, (QuestionNode) previous, reply); } } } } } } } /** * Prompt for information about the animal we got wrong * @param current The animal that we guessed wrong * @param previous The root of current * @param previousToCurrentDecisionChoice Whether it was a Y or N answer that got us here. true = Y, false = N */ private static void askForInformationAndSave(Scanner scan, AnimalNode current, QuestionNode previous, boolean previousToCurrentDecisionChoice) { //Failed to get it right and ran out of questions //Let's ask the user for the new information System.out.print("THE ANIMAL YOU WERE THINKING OF WAS A ? "); String animal = scan.nextLine(); System.out.printf("PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A %s FROM A %s ? ", animal, current.getAnimal()); String newQuestion = scan.nextLine(); System.out.printf("FOR A %s THE ANSWER WOULD BE ? ", animal); boolean newAnswer = readYesOrNo(scan); //Add it to our question store addNewAnimal(current, previous, animal, newQuestion, newAnswer, previousToCurrentDecisionChoice); } private static void addNewAnimal(Node current, QuestionNode previous, String animal, String newQuestion, boolean newAnswer, boolean previousToCurrentDecisionChoice) { var animalNode = new AnimalNode(animal); var questionNode = new QuestionNode(newQuestion, newAnswer ? animalNode : current, !newAnswer ? animalNode : current); if (previous != null) { if (previousToCurrentDecisionChoice) { previous.setTrueAnswer(questionNode); } else { previous.setFalseAnswer(questionNode); } } } private static boolean askQuestionAndGetReply(QuestionNode questionNode, Scanner scanner) { System.out.printf("%s ? ", questionNode.question); return readYesOrNo(scanner); } private static boolean readYesOrNo(Scanner scanner) { boolean validAnswer = false; Boolean choseAnswer = null; while (!validAnswer) { String answer = scanner.nextLine(); if (answer.toUpperCase(Locale.ROOT).startsWith("Y")) { validAnswer = true; choseAnswer = true; } else if (answer.toUpperCase(Locale.ROOT).startsWith("N")) { validAnswer = true; choseAnswer = false; } } return choseAnswer; } private static void printKnownAnimals(Node root) { System.out.println("\nANIMALS I ALREADY KNOW ARE:"); List leafNodes = collectLeafNodes(root); String allAnimalsString = leafNodes.stream().map(AnimalNode::getAnimal).collect(Collectors.joining("\t\t")); System.out.println(allAnimalsString); } //Traverse the tree and collect all the leaf nodes, which basically have all the animals. private static List collectLeafNodes(Node root) { List collectedNodes = new ArrayList<>(); if (root instanceof AnimalNode) { collectedNodes.add((AnimalNode) root); } else { var q = (QuestionNode) root; collectedNodes.addAll(collectLeafNodes(q.getTrueAnswer())); collectedNodes.addAll(collectLeafNodes(q.getFalseAnswer())); } return collectedNodes; } private static String readMainChoice(Scanner scan) { System.out.print("ARE YOU THINKING OF AN ANIMAL ? "); return scan.nextLine(); } private static void printIntro() { System.out.println(" ANIMAL"); System.out.println(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println("\n\n"); System.out.println("PLAY 'GUESS THE ANIMAL'"); System.out.println("\n"); System.out.println("THINK OF AN ANIMAL AND THE COMPUTER WILL TRY TO GUESS IT."); } //Based on https://stackoverflow.com/a/8948691/74057 private static void printTree(Node root) { StringBuilder buffer = new StringBuilder(50); print(root, buffer, "", ""); System.out.println(buffer); } private static void print(Node root, StringBuilder buffer, String prefix, String childrenPrefix) { buffer.append(prefix); buffer.append(root.toString()); buffer.append('\n'); if (root instanceof QuestionNode) { var questionNode = (QuestionNode) root; print(questionNode.getTrueAnswer(), buffer, childrenPrefix + "├─Y─ ", childrenPrefix + "│ "); print(questionNode.getFalseAnswer(), buffer, childrenPrefix + "└─N─ ", childrenPrefix + " "); } } /** * Base interface for all nodes in our question tree */ private interface Node { } private static class QuestionNode implements Node { private final String question; private Node trueAnswer; private Node falseAnswer; public QuestionNode(String question, Node trueAnswer, Node falseAnswer) { this.question = question; this.trueAnswer = trueAnswer; this.falseAnswer = falseAnswer; } public String getQuestion() { return question; } public Node getTrueAnswer() { return trueAnswer; } public void setTrueAnswer(Node trueAnswer) { this.trueAnswer = trueAnswer; } public Node getFalseAnswer() { return falseAnswer; } public void setFalseAnswer(Node falseAnswer) { this.falseAnswer = falseAnswer; } @Override public String toString() { return "Question{'" + question + "'}"; } } private static class AnimalNode implements Node { private final String animal; public AnimalNode(String animal) { this.animal = animal; } public String getAnimal() { return animal; } @Override public String toString() { return "Animal{'" + animal + "'}"; } } } ================================================ FILE: 03_Animal/java/test/AnimalJavaTest.kt ================================================ import com.pcholt.console.testutils.ConsoleTest import org.junit.Test class AnimalJavaTest : ConsoleTest() { @Test fun `given a standard setup, find the fish`() { assertConversation( """ $title ARE YOU THINKING OF AN ANIMAL ? {YES} DOES IT SWIM ? {YES} IS IT A FISH ? {YES} WHY NOT TRY ANOTHER ANIMAL? ARE YOU THINKING OF AN ANIMAL ? {QUIT} """ ) { Animal.main(emptyArray()) } } @Test fun `given a standard setup, create a cow, and verify`() { assertConversation( """ $title ARE YOU THINKING OF AN ANIMAL ? {YES} DOES IT SWIM ? {NO} IS IT A BIRD ? {NO} THE ANIMAL YOU WERE THINKING OF WAS A ? {COW} PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A COW FROM A BIRD ? {DOES IT EAT GRASS} FOR A COW THE ANSWER WOULD BE ? {YES} ARE YOU THINKING OF AN ANIMAL ? {YES} DOES IT SWIM ? {NO} DOES IT EAT GRASS ? {YES} IS IT A COW ? {YES} WHY NOT TRY ANOTHER ANIMAL? ARE YOU THINKING OF AN ANIMAL ? {QUIT} """ ) { Animal.main(emptyArray()) } } private val title = """ ANIMAL CREATIVE COMPUTING MORRISTOWN, NEW JERSEY PLAY 'GUESS THE ANIMAL' THINK OF AN ANIMAL AND THE COMPUTER WILL TRY TO GUESS IT. """ } ================================================ FILE: 03_Animal/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 03_Animal/javascript/animal.html ================================================ ANIMAL

    
    
    
    
    
    
    
    ================================================
    FILE: 03_Animal/javascript/animal.js
    ================================================
    // ANIMAL
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                            input_str = input_element.value;
                                                            document.getElementById("output").removeChild(input_element);
                                                            print(input_str);
                                                            print("\n");
                                                            resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    print(tab(32) + "ANIMAL\n");
    print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
    print("\n");
    print("\n");
    print("\n");
    print("PLAY 'GUESS THE ANIMAL'\n");
    print("\n");
    print("THINK OF AN ANIMAL AND THE COMPUTER WILL TRY TO GUESS IT.\n");
    print("\n");
    
    var k;
    var n;
    var str;
    var q;
    var z;
    var c;
    var t;
    
    var animals = [
                   "\\QDOES IT SWIM\\Y1\\N2\\",
                   "\\AFISH",
                   "\\ABIRD",
                   ];
    
    n = animals.length;
    
    function show_animals() {
        var x;
    
        print("\n");
        print("ANIMALS I ALREADY KNOW ARE:\n");
        str = "";
        x = 0;
        for (var i = 0; i < n; i++) {
            if (animals[i].substr(0, 2) == "\\A") {
                while (str.length < 15 * x)
                    str += " ";
                for (var z = 2; z < animals[i].length; z++) {
                    if (animals[i][z] == "\\")
                        break;
                    str += animals[i][z];
                }
                x++;
                if (x == 4) {
                    x = 0;
                    print(str + "\n");
                    str = "";
                }
            }
        }
        if (str != "")
            print(str + "\n");
    }
    
    // Main control section
    async function main()
    {
        while (1) {
            while (1) {
                print("ARE YOU THINKING OF AN ANIMAL");
                str = await input();
                if (str == "LIST")
                    show_animals();
                if (str[0] == "Y")
                    break;
            }
    
            k = 0;
            do {
                // Subroutine to print questions
                q = animals[k];
                while (1) {
                    str = "";
                    for (z = 2; z < q.length; z++) {
                        if (q[z] == "\\")
                            break;
                        str += q[z];
                    }
                    print(str);
                    c = await input();
                    if (c[0] == "Y" || c[0] == "N")
                        break;
                }
                t = "\\" + c[0];
                x = q.indexOf(t);
                k = parseInt(q.substr(x + 2));
            } while (animals[k].substr(0,2) == "\\Q") ;
    
            print("IS IT A " + animals[k].substr(2));
            a = await input();
            if (a[0] == "Y") {
                print("WHY NOT TRY ANOTHER ANIMAL?\n");
                continue;
            }
            print("THE ANIMAL YOU WERE THINKING OF WAS A ");
            v = await input();
            print("PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A\n");
            print(v + " FROM A " + animals[k].substr(2) + "\n");
            x = await input();
            while (1) {
                print("FOR A " + v + " THE ANSWER WOULD BE ");
                a = await input();
                a = a.substr(0, 1);
                if (a == "Y" || a == "N")
                    break;
            }
            if (a == "Y")
                b = "N";
            if (a == "N")
                b = "Y";
            z1 = animals.length;
            animals[z1] = animals[k];
            animals[z1 + 1] = "\\A" + v;
            animals[k] = "\\Q" + x + "\\" + a + (z1 + 1) + "\\" + b + z1 + "\\";
        }
    }
    
    main();
    
    
    ================================================
    FILE: 03_Animal/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 03_Animal/kotlin/src/Animal.kt
    ================================================
    /**
     * ANIMAL
     *
     *
     * Converted from BASIC to Kotlin by John Long (@patimen)
     *
     * Animal is basically a perfect example of a binary tree. Implement it
     * as such, with the QuestionNode either having an answer if it is a terminal node
     * or a Question
     */
    
    fun main() {
        printIntro()
        val rootQuestionNode =
            QuestionOrAnswer(question = Question("DOES IT SWIM", QuestionOrAnswer("FISH"), QuestionOrAnswer("BIRD")))
        while (true) {
            val choice = ask("ARE YOU THINKING OF AN ANIMAL")
            when {
                choice == "LIST" -> printKnownAnimals(rootQuestionNode)
                choice.startsWith("Q") -> return
                choice.startsWith("Y") -> {
                    // A wrong answer means it's a new animal!
                    val wrongAnswer = rootQuestionNode.getWrongAnswer()
                    if (wrongAnswer == null) {
                        // The computer got the right answer!
                        println("WHY NOT TRY ANOTHER ANIMAL?")
                    } else {
                        // Get a new question to ask next time
                        wrongAnswer.askForInformationAndSave()
                    }
                }
            }
        }
    }
    
    // Takes care of asking a question (on the same line) and getting
    // an answer or a blank string
    fun ask(question: String): String {
        print("$question? ")
        return readln().uppercase() ?: ""
    }
    
    // Special case for a "yes or no" question, returns true of yes
    fun askYesOrNo(question: String): Boolean {
        return generateSequence {
            print("$question? ")
            readln()
        }.firstNotNullOf { yesOrNo(it) }
    }
    
    // If neither Y (true) or N (false), return null, so the above sequence
    // will just keep executing until it gets the answer
    private fun yesOrNo(string: String): Boolean? =
        when (string.uppercase().firstOrNull()) {
            'Y' -> true
            'N' -> false
            else -> null
        }
    
    private fun printKnownAnimals(question: QuestionOrAnswer) {
        println("\nANIMALS I ALREADY KNOW ARE:")
        val animals = question.getAnswers().chunked(4)
        animals.forEach { line ->
            // The '*' in front of line.toTypedArray() "spreads" the array as a list of parameters instead
            System.out.printf("%-15s".repeat(line.size), *line.toTypedArray())
            println()
        }
    }
    
    private fun printIntro() {
        println("                                ANIMAL")
        println("              CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY")
        println("\n\n")
        println("PLAY 'GUESS THE ANIMAL'")
        println("\n")
        println("THINK OF AN ANIMAL AND THE COMPUTER WILL TRY TO GUESS IT.")
    }
    
    class QuestionOrAnswer(private var answer: String? = null, var question: Question? = null) {
        fun getAnswers(): List = answer?.let { listOf(it) } ?: question!!.getAnswers()
        fun getWrongAnswer(): QuestionOrAnswer? {
            if (answer != null) {
                // "takeUnless" will return null if the answer is "yes". In this case
                // we will return the "wrong answer", aka the terminal answer that was incorrect
                return this.takeUnless { askYesOrNo("IS IT A $answer") }
            }
            return question?.getWrongAnswer()
        }
    
        fun askForInformationAndSave() {
            //Failed to get it right and ran out of questions
            //Let's ask the user for the new information
            val newAnimal = ask("THE ANIMAL YOU WERE THINKING OF WAS A")
            val newQuestion = ask("PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A \n$newAnimal FROM A $answer\n")
            val newAnswer = askYesOrNo("FOR A $newAnimal THE ANSWER WOULD BE")
    
            val trueAnswer = if (newAnswer) newAnimal else answer
            val falseAnswer = if (newAnswer) answer else newAnimal
            // Replace our answer with null  and set the question with the data we just got
            // This makes it a question instead of an answer
            this.answer = null
            this.question = Question(newQuestion, QuestionOrAnswer(trueAnswer), QuestionOrAnswer(falseAnswer))
        }
    }
    
    class Question(
        private val question: String,
        private val trueAnswer: QuestionOrAnswer,
        private val falseAnswer: QuestionOrAnswer
    ) {
        fun getAnswers(): List = trueAnswer.getAnswers() + falseAnswer.getAnswers()
    
        fun getWrongAnswer(): QuestionOrAnswer? =
            if (askYesOrNo(question)) {
                trueAnswer.getWrongAnswer()
            } else {
                falseAnswer.getWrongAnswer()
            }
    }
    
    
    ================================================
    FILE: 03_Animal/kotlin/test/AnimalKtTest.kt
    ================================================
    import com.pcholt.console.testutils.ConsoleTest
    import org.junit.Test
    
    class AnimalKtTest : ConsoleTest() {
    
        @Test
        fun `given a standard setup, find the fish`() {
            assertConversation(
                """
                $title
                ARE YOU THINKING OF AN ANIMAL? {YES}
                DOES IT SWIM? {YES}
                IS IT A FISH? {YES}
                WHY NOT TRY ANOTHER ANIMAL?
                ARE YOU THINKING OF AN ANIMAL? {QUIT}
                """
            ) {
                main()
            }
        }
    
        @Test
        fun `given a standard setup, create a cow, and verify`() {
            assertConversation(
                """
                $title
                ARE YOU THINKING OF AN ANIMAL? {YES}
                DOES IT SWIM? {NO}
                IS IT A BIRD? {NO}
                THE ANIMAL YOU WERE THINKING OF WAS A? {COW}
                PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A
                COW FROM A BIRD
                ? {DOES IT EAT GRASS}
                FOR A COW THE ANSWER WOULD BE? {YES}
                ARE YOU THINKING OF AN ANIMAL? {YES}
                DOES IT SWIM? {NO}
                DOES IT EAT GRASS? {YES}
                IS IT A COW? {YES}
                WHY NOT TRY ANOTHER ANIMAL?
                ARE YOU THINKING OF AN ANIMAL? {QUIT}
            """
            ) {
                main()
            }
        }
    
        private val title = """
                                            ANIMAL
                          CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY
    
            PLAY 'GUESS THE ANIMAL'
            THINK OF AN ANIMAL AND THE COMPUTER WILL TRY TO GUESS IT.
        """
    }
    
    
    ================================================
    FILE: 03_Animal/lua/Animal.lua
    ================================================
    --
    -- 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
    
    
    ================================================
    FILE: 03_Animal/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 03_Animal/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 03_Animal/perl/animal.pl
    ================================================
    #!/usr/bin/env perl
    
    use 5.010;      # To get 'state' and 'say'
    
    use strict;     # Require explicit declaration of variables
    use warnings;   # Enable optional compiler warnings
    
    use English;    # Use more friendly names for Perl's magic variables
    use Term::ReadLine;     # Prompt and return user input
    
    our $VERSION = '0.000_01';
    
    # The Perl ref() built-in returns 'HASH' for a hash reference. But we
    # make it a manifest constant just to avoid typos.
    use constant REF_HASH   => ref {};
    
    print <<'EOD';
                                    ANIMAL
                   Creative Computing  Morristown, New Jersey
    
    
    
    Play 'Guess the Animal'
    Think of an animal and the computer will try to guess it.
    
    EOD
    
    # We keep the accumulated data in a tree structure, initialized here. As
    # we accumulate animals, we replace the 'yes' or 'no' keys with new hash
    # references.
    my $database = {
        question    => 'Does it swim',  # Initial question
        yes         => 'fish',          # Result of answering 'y'
        no          => 'bird',          # Result of answering 'n'
    };
    
    while ( 1 ) {
    
        my $resp = get_input(
            'Are you thinking of an an animal? [y/n/list]: '
        );
    
        if ( $resp =~ m/ \A y /smxi ) {
            # If we got an answer beginning with 'y', walk the database
            walk_tree( $database );
        } elsif ( $resp =~ m/ \A list \z /smxi ) {
            # If we got 'list', list the currently-known animals.
            say '';
            say 'Animals I already know are:';
            say "    $_" for sort( list_animals( $database ) );
        }
    }
    
    # Get input from the user. The arguments are:
    # * The prompt
    # * A reference to validation code. This code receives the response in
    #   $ARG and returns true for a valid response.
    # * A warning to print if the response is not valid. This must end in a
    #   return.
    # The first valid response is returned. An end-of-file terminates the
    # script.
    sub get_input {
        my ( $prompt, $validate, $warning ) = @ARG;
    
        # If no validator is passed, default to one that always returns
        # true.
        $validate ||= sub { 1 };
    
        # Create the readline object. The 'state' causes the variable to be
        # initialized only once, no matter how many times this subroutine is
        # called. The do { ... } is a compound statement used because we
        # need to tweak the created object before we store it.
        state $term = do {
            my $obj = Term::ReadLine->new( 'animal' );
            $obj->ornaments( 0 );
            $obj;
        };
    
        while ( 1 ) {   # Iterate indefinitely
    
            # Read the input into the topic variable, localized to prevent
            # Spooky Action at a Distance. We exit on undef, which signals
            # end-of-file.
            exit unless defined( local $ARG = $term->readline( $prompt ) );
    
            # Return the input if it is valid.
            return $ARG if $validate->();
    
            # Issue the warning, and go around the merry-go-round again.
            warn $warning;
        }
    }
    
    # Get a yes-or-no answer. The argument is the prompt, which will have
    # '? [y/n]: ' appended. The donkey work is done by get_input(), which is
    # requested to validate the response as beginning with 'y' or 'n',
    # case-insensitive. The return is a true value for 'y' and a false value
    # for 'n'.
    sub get_yes_no {
        my ( $prompt ) = @ARG;
        state $map_answer = {
            n   => 0,
            y   => 1,
        };
        my $resp = lc get_input(
            "$prompt? [y/n]: ",
            sub { m/ \A [yn] /smxi },
            "Please respond 'y' or 'n'\n",
        );
        return $map_answer->{ substr $resp, 0, 1 };
    }
    
    # Recurse through the database, returning the names of all animals in
    # it, in an undefined order.
    sub list_animals {
        my ( $node ) = @ARG;
        return $node unless REF_HASH eq ref $node;
        return( map { list_animals( $node->{$_} ) } qw{ yes no } );
    }
    
    # Find or create the desired animal.
    # Ask the question stored in the node given in its argument. If the key
    # selected by the answer ('yes' or 'no') is another node, recurse. If it
    # is an animal name, confirm it, or add a new animal as appropriate.
    sub walk_tree {
        my ( $node ) = @ARG;
    
        # Ask the question associated with this node. Turn the true/false
        # response into 'yes' or 'no', since those are the names of the
        # respective keys.
        my $resp = get_yes_no ( $node->{question} ) ? 'yes' : 'no';
    
        # Chose the datum for the response.
        my $choice = $node->{ $resp };
    
        # If the datum is a hash reference
        if ( REF_HASH eq ref $choice ) {
    
            # Recurse into it
            walk_tree( $choice );
    
        # Otherwise it is an actual animal (i.e. terminal node). Check it.
        } else {
    
            # If this is not the animal the player was thinking of
            unless ( get_yes_no( "Is it a $choice" ) ) {
    
                # Find out what animal the player was thinking of
                my $animal = lc get_input(
                    'The animal you were thinking of was a ',
                );
    
                # Get a yes/no question that distinguishes the animal the
                # player was thinking of from the animal we found in the
                # tree.
                say 'Please type in a question that would distinguish a';
                my $question = get_input( "$animal from a $choice: " );
    
                # Find out whether the new animal is selected by 'yes' or
                # 'no'. If 'no', swap the original animal with the new one
                # for convenience.
                ( $choice, $animal ) = ( $animal, $choice ) if get_yes_no(
                    "For a $animal the answer would be",
                );
    
                # Replace the animal we originally found by a new node
                # giving the original animal, the new animal, and the
                # question that distinguishes them.
                $node->{ $resp } = {
                    question    => $question,
                    no          => $animal,
                    yes         => $choice,
                };
            }
    
            # Find out if the player wants to play again. If not, exit. If
            # so, just return.
            say '';
            exit unless get_yes_no( 'Why not try another animal' );
            return;
        }
    }
    
    __END__
    
    =head1 TITLE
    
    animal.pl - Play the game 'animal' from Basic Computer Games
    
    =head1 SYNOPSIS
    
     animal.pl
    
    =head1 DETAILS
    
    This Perl script is a port of C, which is the 3ed entry in Basic
    Computer Games.
    
    The original BASIC was greatly complicated by the need to emulate a
    binary tree with an array. The implementation using hashes as nodes in
    an actual binary tree is much simpler.
    
    =head1 PORTED BY
    
    Thomas R. Wyant, III F
    
    =head1 COPYRIGHT AND LICENSE
    
    Copyright (C) 2022 by Thomas R. Wyant, III
    
    This program is free software; you can redistribute it and/or modify it
    under the same terms as Perl 5.10.0. For more details, see the Artistic
    License 1.0 at
    L, and/or the
    Gnu GPL at L.
    
    This program is distributed in the hope that it will be useful, but
    without any warranty; without even the implied warranty of
    merchantability or fitness for a particular purpose.
    
    =cut
    
    # ex: set expandtab tabstop=4 textwidth=72 :
    
    
    ================================================
    FILE: 03_Animal/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 03_Animal/python/animal.py
    ================================================
    """
    Animal
    
    From: Basic computer Games(1978)
    
       Unlike other computer games in which the computer
      picks a number or letter and you must guess what it is,
      in this game you think of an animal and the computer asks
      you questions and tries to guess the name of your animal.
      If the computer guesses incorrectly, it will ask you for a
      question that differentiates the animal it guessed
      from the one you were thinking of. In this way the
      computer "learns" new animals. Questions to differentiate
      new animals should be input without a question mark.
       This version of the game does not have a SAVE feature.
      If your sistem allows, you may modify the program to
      save array A$, then reload the array  when you want
      to play the game again. This way you can save what the
      computer learns over a series of games.
       At any time if you reply 'LIST' to the question "ARE YOU
      THINKING OF AN ANIMAL", the computer will tell you all the
      animals it knows so far.
       The program starts originally by knowing only FISH and BIRD.
      As you build up a file of animals you should use broad,
      general questions first and then narrow down to more specific
      ones with later animals. For example, If an elephant was to be
      your first animal, the computer would ask for a question to distinguish
      an elephant from a bird. Naturally there are hundreds of possibilities,
      however, if you plan to build a large file of animals a good question
      would be "IS IT A MAMAL".
       This program can be easily modified to deal with categories of
      things other than animals by simply modifying the initial data
      in Line 530 and the dialogue references to animal in Lines 10,
      40, 50, 130, 230, 240 and 600. In an educational environment, this
      would be a valuable program to teach the distinguishing chacteristics
      of many classes of objects -- rock formations, geography, marine life,
      cell structures, etc.
       Originally developed by Arthur Luehrmann at Dartmouth College,
      Animal was subsequently shortened and modified by Nathan Teichholtz at
      DEC and Steve North at Creative Computing
    """
    
    from typing import Optional
    
    
    class Node:
        """
        Node of the binary tree of questions.
        """
    
        def __init__(
            self, text: str, yes_node: Optional["Node"], no_node: Optional["Node"]
        ):
            # the nodes that are leafs have as text the animal's name, otherwise
            # a yes/no question
            self.text = text
            self.yes_node = yes_node
            self.no_node = no_node
    
        def update_node(
            self, new_question: str, answer_new_ques: str, new_animal: str
        ) -> None:
            # update the leaf with a question
            old_animal = self.text
            # we replace the animal with a new question
            self.text = new_question
    
            if answer_new_ques == "y":
                self.yes_node = Node(new_animal, None, None)
                self.no_node = Node(old_animal, None, None)
            else:
                self.yes_node = Node(old_animal, None, None)
                self.no_node = Node(new_animal, None, None)
    
        # the leafs have as children None
        def is_leaf(self) -> bool:
            return self.yes_node is None and self.no_node is None
    
    
    def list_known_animals(root_node: Optional[Node]) -> None:
        """Traversing the tree by recursion until we reach the leafs."""
        if root_node is None:
            return
    
        if root_node.is_leaf():
            print(root_node.text, end=" " * 11)
            return
    
        if root_node.yes_node:
            list_known_animals(root_node.yes_node)
    
        if root_node.no_node:
            list_known_animals(root_node.no_node)
    
    
    def parse_input(message: str, check_list: bool, root_node: Optional[Node]) -> str:
        """only accepts yes or no inputs and recognizes list operation"""
        token = ""
        while token not in ["y", "n"]:
            inp = input(message)
    
            if check_list and inp.lower() == "list":
                print("Animals I already know are:")
                list_known_animals(root_node)
                print("\n")
    
            token = inp[0].lower() if len(inp) > 0 else ""
        return token
    
    
    def avoid_void_input(message: str) -> str:
        answer = ""
        while not answer:
            answer = input(message)
        return answer
    
    
    def print_intro() -> None:
        print(" " * 32 + "Animal")
        print(" " * 15 + "Creative Computing Morristown, New Jersey\n")
        print("Play ´Guess the Animal´")
        print("Think of an animal and the computer will try to guess it.\n")
    
    
    def main() -> None:
        # Initial tree
        yes_child = Node("Fish", None, None)
        no_child = Node("Bird", None, None)
        root = Node("Does it swim?", yes_child, no_child)
    
        # Main loop of game
        print_intro()
        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
    
            while keep_asking:
    
                if not actual_node.is_leaf():
    
                    # we have to keep asking i.e. traversing nodes
                    answer = parse_input(actual_node.text, False, None)
    
                    # As this is an inner node, both children are not None
                    if answer == "y":
                        assert actual_node.yes_node is not None
                        actual_node = actual_node.yes_node
                    else:
                        assert actual_node.no_node is not None
                        actual_node = actual_node.no_node
                else:
                    # we have reached a possible answer
                    answer = parse_input(f"Is it a {actual_node.text}? ", False, None)
                    if answer == "n":
                        # add the new animal to the tree
                        new_animal = avoid_void_input(
                            "The animal you were thinking of was a ? "
                        )
                        new_question = avoid_void_input(
                            "Please type in a question that would distinguish a "
                            f"{new_animal} from a {actual_node.text}: "
                        )
                        answer_new_question = parse_input(
                            f"for a {new_animal} the answer would be: ", False, None
                        )
    
                        actual_node.update_node(f"{new_question}?", answer_new_question, new_animal)
    
                    else:
                        print("Why not try another animal?")
    
                    keep_asking = False
    
    
    ########################################################
    # Porting Notes
    #
    #   The data structure used for storing questions and
    #   animals is a binary tree where each non-leaf node
    #   has a question, while the leafs store the animals.
    #
    #   As the original program, this program doesn't store
    #   old questions and animals. A good modification would
    #   be to add a database to store the tree.
    #    Also as the original program, this one can be easily
    #   modified to not only make guesses about animals, by
    #   modyfing the initial data of the tree, the questions
    #   that are asked to the user and the initial message
    #   function  (Lines 120 to 130, 135, 158, 160, 168, 173)
    
    ########################################################
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 03_Animal/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 03_Animal/ruby/animal.rb
    ================================================
    require 'set'
    
    def intro
      puts "                                ANIMAL
                   CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY
    
    
    
    PLAY 'GUESS THE ANIMAL'
    
    THINK OF AN ANIMAL AND THE COMPUTER WILL TRY TO GUESS IT.
    
    "
    end
    
    def ask(question)
      print "#{question} "
      (gets || '').chomp.upcase
    end
    
    Feature = Struct.new(:question, :yes_guess, :no_guess)
    
    def add_guess(animals, guess)
      guess.is_a?(Struct) ? get_all_animals(guess, animals) : animals.add(guess)
    end
    
    def get_all_animals(feature, animals = Set.new)
      add_guess(animals, feature.yes_guess)
      add_guess(animals, feature.no_guess)
      animals
    end
    
    def create_feature(current_animal)
      new_animal = ask('THE ANIMAL YOU WERE THINKING OF WAS A ?')
      puts "PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A #{new_animal} FROM A #{current_animal}"
      question = ask('?')
      loop do
        yes_no = ask("FOR A #{new_animal} THE ANSWER WOULD BE ?")
        next unless ['Y', 'N'].include?(yes_no[0])
        guesses = yes_no[0] == 'Y' ? [new_animal, current_animal] : [current_animal, new_animal]
        return Feature.new(question, *guesses)
      end
    end
    
    def guess_loop(feature)
      loop do
        answer = ask(feature.question)
        next unless ['Y', 'N'].include?(answer[0])
        answer_is_yes = answer[0] == 'Y'
    
        name = answer_is_yes ? feature.yes_guess : feature.no_guess
        if name.is_a?(Struct)
          feature = name
          next
        end
    
        guess = ask("IS IT A #{name}?")
        correct_guess = guess[0] == 'Y'
    
        if correct_guess
          puts "WHY NOT TRY ANOTHER ANIMAL?"
          break
        end
    
        if answer_is_yes
          feature.yes_guess = create_feature(name)
        else
          feature.no_guess = create_feature(name)
        end
        break
      end
    end
    
    def main
      intro
      feature = Feature.new('DOES IT SWIM?', 'FISH', 'BIRD')
    
      while true do
        option = ask("ARE YOU THINKING OF AN ANIMAL?")
        if option == 'LIST'
          puts
          puts "ANIMALS I ALREADY KNOW ARE:"
          puts get_all_animals(feature).to_a.join(" " * 15)
          puts
        elsif option[0] == 'Y'
          guess_loop(feature)
        elsif option == ''
          puts
        end
      end
    end
    
    trap "SIGINT" do puts; exit 130 end
    
    main
    
    
    ================================================
    FILE: 03_Animal/rust/Cargo.toml
    ================================================
    [package]
    name = "animal"
    version = "0.1.0"
    edition = "2021"
    
    # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
    
    [dependencies]
    
    
    ================================================
    FILE: 03_Animal/rust/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Rust](https://www.rust-lang.org/) by [Anton Kaiukov](https://github.com/batk0)
    
    
    ================================================
    FILE: 03_Animal/rust/src/main.rs
    ================================================
    /*******************************************************************************
     * Animal
     * 
     * From: Basic computer Games(1978)
     * 
     *    Unlike other computer games in which the computer
     *   picks a number or letter and you must guess what it is,
     *   in this game you think of an animal and the computer asks
     *   you questions and tries to guess the name of your animal.
     *   If the computer guesses incorrectly, it will ask you for a
     *   question that differentiates the animal it guessed
     *   from the one you were thinking of. In this way the
     *   computer "learns" new animals. Questions to differentiate
     *   new animals should be input without a question mark.
     *    This version of the game does not have a SAVE feature.
     *   If your sistem allows, you may modify the program to
     *   save array A$, then reload the array  when you want
     *   to play the game again. This way you can save what the
     *   computer learns over a series of games.
     *    At any time if you reply 'LIST' to the question "ARE YOU
     *   THINKING OF AN ANIMAL", the computer will tell you all the
     *   animals it knows so far.
     *    The program starts originally by knowing only FISH and BIRD.
     *   As you build up a file of animals you should use broad,
     *   general questions first and then narrow down to more specific
     *   ones with later animals. For example, If an elephant was to be
     *   your first animal, the computer would ask for a question to distinguish
     *   an elephant from a bird. Naturally there are hundreds of possibilities,
     *   however, if you plan to build a large file of animals a good question
     *   would be "IS IT A MAMAL".
     *    This program can be easily modified to deal with categories of
     *   things other than animals by simply modifying the initial data
     *   in Line 530 and the dialogue references to animal in Lines 10,
     *   40, 50, 130, 230, 240 and 600. In an educational environment, this
     *   would be a valuable program to teach the distinguishing chacteristics
     *   of many classes of objects -- rock formations, geography, marine life,
     *   cell structures, etc.
     *    Originally developed by Arthur Luehrmann at Dartmouth College,
     *   Animal was subsequently shortened and modified by Nathan Teichholtz at
     *   DEC and Steve North at Creative Computing
     ******************************************************************************/
    /*******************************************************************************
     * Porting notes:
     * 
     * The data structure used for the game is B-Tree where each leaf is an animal
     * and non-leaf node is a question.
     * 
     * B-Tree is implemented in non-traditional way. It uses HashMap for string
     * nodes data and use determenistic method to calculate keys for left (yes) and 
     * right (no) nodes. (See comments in the code.)
     * 
     * The logic of the game mostly kept in `main` function with the use of some
     * helper functions.
     ******************************************************************************/
    use std::collections::HashMap;
    use std::io;
    
    /// Main function that contains all the logic.
    fn main() {
        println!("{: ^80}", "Animal");
        println!("{: ^80}\n", "Creative Computing Morristown, New Jersey");
        println!("Play ´Guess the Animal´");
        println!("Think of an animal and the computer will try to guess it.\n");
    
        // Initial game data
        let mut animal = BTree::new(
            "Does it swim".to_string(),
            "Fish".to_string(),
            "Bird".to_string(),
        );
        
        // Main game loop
        while keep_playing() {
            animal.restart();
            // Ask questions until player reaches an animal.
            while ! animal.is_leaf() {
                println!("{}? ", animal.get());
                if yes_no() {
                    animal.yes();
                } else {
                    animal.no();
                }
            }
            // Ask if this is the animal player is thinking of.
            println!("Is it a {}? ", animal.get());
            if ! yes_no() {
                // Add a new animal if it's not, and distiguish it from the existing
                // one with a question.
                println!("The animal you were thinking of was a ? ");
                let new_animal = read_input();
                let old_animal = animal.get();
                println!("Please type in a question that would distinguish a {} from a {}: ",
                    new_animal, old_animal );
                let new_question = read_input();
                    println!("for a {} the answer would be: ", new_animal);
                if yes_no() {
                    animal.set(new_question, new_animal, old_animal)
                } else {
                    animal.set(new_question, old_animal, new_animal)
                }
            }
            println!("Why not try another animal?");
        }
    }
    
    /// Reads the input line from [`io::stdin`] and returns it as a [`String`].
    fn read_input() -> String {
        let mut input = String::new();
        io::stdin().read_line(&mut input).unwrap();
        input.trim().parse::().unwrap()
    }
    
    /// Asks the player whether the player wants to continue playing. Returns 
    /// [`true`] if the answer is yes.
    fn keep_playing() -> bool {
        println!("Are you thinking of an animal? ");
        yes_no()
    }
    
    /// Checks whether given answer is `yes` or `no`, returns [`true`] if yes.
    fn yes_no() -> bool {
        loop {
            let answer = read_input().to_lowercase();
            if answer != "y" && answer != "yes" && answer != "n" && answer != "no" {
                println!("Please type `yes` or `no`");
                continue;
            }
            return answer == "y" || answer == "yes";
        }
    }
    
    /// Binary Tree data structure. Implemented the similar way to Max/Min Heap.
    struct BTree {
        /// contains all nodes data (questions and animals).
        nodes: HashMap,
        /// contains the key to current node in the game.
        cursor: usize,
    }
    
    impl BTree {
        /// Creates new [`BTree`] with one root node (question) and two childs 
        /// (animals).
        fn new(value: String, yes: String, no: String) -> BTree {
            let nodes: HashMap = HashMap::from([
                (0, value),
                (1, yes),
                (2, no),
            ]);
            BTree { nodes: nodes, cursor: 0 }
        }
        
        /// Returns the key for left node (yes) based on the postition of the 
        /// [`BTree::cursor`].
        fn get_yes_key(&self) -> usize {
            &self.cursor * 2 + 1
        }
    
        /// Returns the key for right node (no) based on the postition of the 
        /// [`BTree::cursor`].
        fn get_no_key(&self) -> usize {
            &self.cursor * 2 + 2
        }
        
        /// Check if current node is a leaf (has no children).
        fn is_leaf(&self) -> bool {
            ! ( self.nodes.contains_key(&self.get_yes_key()) || 
                self.nodes.contains_key(&self.get_no_key()) )
        }
    
        /// Moves cursor to `yes` (left node) if the node exists.
        fn yes(&mut self) {
            if self.nodes.contains_key(&self.get_yes_key()) {
                self.cursor = self.get_yes_key();   
            }
        }
    
        /// Moves cursor to `no` (right node) if the node exists.
        fn no(&mut self) {
            if self.nodes.contains_key(&self.get_no_key()) {
                self.cursor = self.get_no_key();   
            }
        }
        
        /// Sets new value (question) and two children (animals) at current position
        /// of the [`BTree::cursor`].
        fn set(&mut self, value: String, yes: String, no: String) {
            if let Some(v) = self.nodes.get_mut(&self.cursor) {
                *v = value;
            }
            self.nodes.insert(self.get_yes_key(), yes);
            self.nodes.insert(self.get_no_key(), no);
        }
        
        /// Returns the value (question or animal) of the current node.
        fn get(&self) -> String {
            if let Some(t) = self.nodes.get(&self.cursor) {
                return t.to_string();
            }
            "".to_string()
        }
    
        /// Reset cursor to 0 (root node).
        fn restart(&mut self) {
            self.cursor = 0;
        }
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
        
        #[test]
        fn test_new() {
            let got = BTree::new("root".to_string(), "yes".to_string(), "no".to_string());
            let want = BTree{nodes: HashMap::from([
                (0, "root".to_string()),
                (1, "yes".to_string()),
                (2, "no".to_string()),
            ]), cursor: 0};
            assert_eq!(got.nodes, want.nodes);
            assert_eq!(got.cursor, want.cursor);
        }
    
        #[test]
        fn test_get() {
            let got = BTree::new("root".to_string(), "yes".to_string(), "no".to_string());
            let want = "root".to_string();
            assert_eq!(got.get(), want);
        }
    
        #[test]
        fn test_set() {
            let mut got = BTree::new("root".to_string(), "yes".to_string(), "no".to_string());
            got.set("Root".to_string(), "Yes".to_string(), "No".to_string());
            let want = BTree{nodes: HashMap::from([
                    (0, "Root".to_string()),
                    (1, "Yes".to_string()),
                    (2, "No".to_string()),
                ]), cursor: 0};
            assert_eq!(got.nodes, want.nodes);
            assert_eq!(got.cursor, want.cursor);
        }
    
        #[test]
        fn test_yes() {
            let mut got = BTree::new("root".to_string(), "yes".to_string(), "no".to_string());
            let want = "yes".to_string();
            let want_cursor = 1;
            got.yes();
            assert_eq!(got.get(), want);
            assert_eq!(got.cursor, want_cursor);
        }
    
        #[test]
        fn test_no() {
            let mut got = BTree::new("root".to_string(), "yes".to_string(), "no".to_string());
            let want = "no".to_string();
            let want_cursor = 2;
            got.no();
            assert_eq!(got.get(), want);
            assert_eq!(got.cursor, want_cursor);
        }
    
        #[test]
        fn test_is_leaf() {
            let mut got = BTree::new("root".to_string(), "yes".to_string(), "no".to_string());
            assert!(!got.is_leaf(), "should not be leaf");
            got.yes();
            assert!(got.is_leaf(), "should be leaf");
        }
    
        #[test]
        fn test_get_key() {
            let mut got = BTree::new("root".to_string(), "yes".to_string(), "no".to_string());
            assert_eq!(got.get_yes_key(), 1);
            assert_eq!(got.get_no_key(), 2);
            got.yes();
            assert_eq!(got.get_yes_key(), 3);
            assert_eq!(got.get_no_key(), 4);
        }
    
        #[test]
        fn test_restart() {
            let mut got = BTree::new("root".to_string(), "yes".to_string(), "no".to_string());
            assert_eq!(got.cursor, 0);
            got.yes();
            assert_eq!(got.cursor, 1);
            got.restart();
            assert_eq!(got.cursor, 0);
        }
    }
    
    
    ================================================
    FILE: 03_Animal/vbnet/Animal/Animal.vbproj
    ================================================
    
      
        Exe
        Animal
        net6.0
        16.9
        On
      
    
    
    
    ================================================
    FILE: 03_Animal/vbnet/Animal/Branch.vb
    ================================================
    Public Class Branch
        Public Property Text As String
    
        Public ReadOnly Property IsEnd As Boolean
            Get
                Return Yes Is Nothing AndAlso No Is Nothing
            End Get
        End Property
    
        Public Property Yes As Branch
        Public Property No As Branch
    
        ' Allows walking all the descendants recursively
        Public Iterator Function DescendantTexts() As IEnumerable(Of String)
            If Yes IsNot Nothing Then
                Yield Yes.Text
                For Each childText In Yes.DescendantTexts
                    Yield childText
                Next
            End If
    
            If No IsNot Nothing Then
                Yield No.Text
                For Each childText In No.DescendantTexts
                    Yield childText
                Next
            End If
        End Function
    End Class
    
    
    ================================================
    FILE: 03_Animal/vbnet/Animal/Game.vb
    ================================================
    Option Compare Text
    
    Public Class Game
        ' This Dictionary holds the corresponding value for each of the variants of "YES" and "NO" we accept
        ' Note that the Dictionary is case-insensitive, meaning it maps "YES", "yes" and even "yEs" to True
        Private Shared ReadOnly YesNoResponses As New Dictionary(Of String, Boolean)(StringComparer.InvariantCultureIgnoreCase) From {
            {"yes", True},
            {"y", True},
            {"true", True},
            {"t", True},
            {"1", True},
            {"no", False},
            {"n", False},
            {"false", False},
            {"f", False},
            {"0", False}
        }
    
        ReadOnly console As ConsoleAdapterBase
    
        ' The pre-initialized root branch
        ReadOnly root As New Branch With {
            .Text = "DOES IT SWIM?",
            .Yes = New Branch With {.Text = "FISH"},
            .No = New Branch With {.Text = "BIRD"}
        }
    
        ''' Reduces a string or console input to True, False or Nothing. Case-insensitive.
        ''' Optional String to reduce via the same logic. If not passed in, will use console.ReadLine
        ''' 
        ''' Returns True for a "yes" response (yes, y, true, t, 1) and False for a "no" response (no, n, false, f, 0).
    ''' Returns Nothing if the response doesn't match any of these. '''
    Private Function GetYesNo(Optional s As String = Nothing) As Boolean? s = If(s, console.ReadLine) Dim ret As Boolean If YesNoResponses.TryGetValue(s, ret) Then Return ret Return Nothing End Function Sub New(console As ConsoleAdapterBase) If console Is Nothing Then Throw New ArgumentNullException(NameOf(console)) Me.console = console End Sub Sub BeginLoop() ' Print the program heading console.WriteCenteredLines( "ANIMAL CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") ' Print the program description console.Write( " PLAY 'GUESS THE ANIMAL' THINK OF AN ANIMAL AND THE COMPUTER WILL TRY TO GUESS IT. ") Do console.Write("ARE YOU THINKING OF AN ANIMAL? ") Dim response = console.ReadLine If response = "list" Then ' List all the stored animals console.Write( " ANIMALS I ALREADY KNOW ARE: ") ' We're using a ForEach extension method instead of the regular For Each loop to provide the index alongside the text root.DescendantTexts.ForEach(Sub(text, index) ' We want to move to the next line after every four animals ' But for the first animal, where the index is 0, 0 Mod 4 will also return 0 ' So we have to explicitly exclude the first animal If index > 0 AndAlso index Mod 4 = 0 Then console.WriteLine() console.Write($"{text.MaxLength(15),-15}") End Sub) console.Write( " ") Continue Do End If Dim ynResponse = GetYesNo(response) If ynResponse Is Nothing OrElse Not ynResponse Then Continue Do Dim currentBranch = root Do While Not currentBranch.IsEnd ' Branches can either be questions, or end branches ' We have to walk the questions, prompting each time for "yes" or "no" console.Write($"{currentBranch.Text} ") Do ynResponse = GetYesNo() Loop While ynResponse Is Nothing ' Depending on the answer, we'll follow either the branch at "Yes" or "No" currentBranch = If( ynResponse, currentBranch.Yes, currentBranch.No ) Loop ' Now we're at an end branch console.Write($"IS IT A {currentBranch.Text}? ") ynResponse = GetYesNo() If ynResponse Then ' Only if ynResponse = True will we go into this If Then block ' The computer guessed the animal; we can go back to the beginning of the game console.WriteLine("WHY NOT TRY ANOTHER ANIMAL?") Continue Do End If ' Get the new animal from the user console.Write("THE ANIMAL YOU WERE THINKING OF WAS A ? ") Dim newAnimal = console.ReadLine.ToUpperInvariant ' Get the question used to distinguish the new animal from the current end branch console.Write( $"PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A {newAnimal} FROM A {currentBranch.Text} ") Dim newQuestion = console.ReadLine.ToUpperInvariant ' Get the answer to that question, for the new animal ' for the old animal, the answer will be the opposite console.Write($"FOR A {newAnimal} THE ANSWER WOULD BE? ") Do ynResponse = GetYesNo() Loop While ynResponse Is Nothing ' Create the new end branch for the new animal Dim newBranch = New Branch With {.Text = newAnimal} ' Copy over the current animal to another new end branch Dim currentBranchCopy = New Branch With {.Text = currentBranch.Text} ' Make the current branch into the distinguishing question currentBranch.Text = newQuestion ' Set the Yes and No branches of the current branch according to the answer If ynResponse Then currentBranch.Yes = newBranch currentBranch.No = currentBranchCopy Else currentBranch.No = newBranch currentBranch.Yes = currentBranchCopy End If ' TODO how do we exit? Loop End Sub End Class ================================================ FILE: 03_Animal/vbnet/Animal/Program.vb ================================================ Module Program Sub Main() Dim game As New Game(New ConsoleAdapter) game.BeginLoop() End Sub End Module ================================================ FILE: 03_Animal/vbnet/Animal/Shared/ConsoleAdapter.vb ================================================ Public Class ConsoleAdapter Inherits ConsoleAdapterBase Public Overrides Sub Write(value As Object) Console.Write(value) End Sub Public Overrides Sub WriteLine() Console.WriteLine() End Sub Public Overrides Sub WriteCenteredLines(value As Object) If Console.CursorLeft <> 0 Then WriteLine() Dim toWrite = If(value?.ToString, "") For Each line In toWrite.Split(Environment.NewLine) Write($"{Space((Console.WindowWidth - line.Length) \ 2)}{line}") WriteLine() Next End Sub Public Overrides Function ReadLine() As String Dim response As String Do response = Console.ReadLine Loop While response Is Nothing Return response.Trim End Function End Class ================================================ FILE: 03_Animal/vbnet/Animal/Shared/ConsoleAdapterBase.vb ================================================ Public MustInherit Class ConsoleAdapterBase Public MustOverride Sub Write(value As Object) Public MustOverride Sub WriteLine() Public MustOverride Sub WriteCenteredLines(value As Object) ''' Implementations should always return a String without leading or trailing whitespace, never Nothng Public MustOverride Function ReadLine() As String Public Sub WriteLine(value As Object) Write(value) WriteLine() End Sub End Class ================================================ FILE: 03_Animal/vbnet/Animal/Shared/Extensions.vb ================================================ Imports System.Runtime.CompilerServices Public Module Extensions Public Sub ForEach(Of T)(src As IEnumerable(Of T), action As Action(Of T)) For Each x In src action(x) Next End Sub Public Sub ForEach(Of T)(src As IEnumerable(Of T), action As Action(Of T, Integer)) Dim index As Integer For Each x In src action(x, index) index += 1 Next End Sub Public Function MaxLength(s As String, value As Integer) As String If s Is Nothing Then Return Nothing Return s.Substring(0, Math.Min(s.Length, value)) End Function Public Function ForceEndsWith(s As String, toAppend As String) As String If Not s.EndsWith(toAppend, StringComparison.OrdinalIgnoreCase) Then s += toAppend Return s End Function Public Function ToTitleCase(s As String) As String If s Is Nothing Then Return Nothing Return Char.ToUpperInvariant(s(0)) + s.Substring(1).ToUpperInvariant End Function ' https://stackoverflow.com/a/3681580/111794 Public Function ToReverseCase(s As String) As String If s Is Nothing Then Return Nothing Return New String(s.Select(Function(c) If( Not Char.IsLetter(c), c, If( Char.IsUpper(c), Char.ToLowerInvariant(c), Char.ToUpperInvariant(c) ) )).ToArray) End Function ' https://stackoverflow.com/a/58132204/111794 Public Function Slice(Of T)(lst As IList(Of T), start As Integer, [end] As Integer) As T() start = If(start >= 0, start, lst.Count + start) [end] = If([end] > 0, [end], lst.Count + [end]) Return lst.Skip(start).Take([end] - start).ToArray End Function End Module ================================================ FILE: 03_Animal/vbnet/Animal.Tests/Animal.Tests.vbproj ================================================ Animal.Tests net6.0 false On runtime; build; native; contentfiles; analyzers; buildtransitive all runtime; build; native; contentfiles; analyzers; buildtransitive all ================================================ FILE: 03_Animal/vbnet/Animal.Tests/EndOfInputsException.vb ================================================ ''' ''' Indicates that there are no more inputs in the MockConsole. ''' We need this while testing, because otherwise the game loop will continue forever, waiting for a nonexistent input. ''' Public Class EndOfInputsException Inherits Exception End Class ================================================ FILE: 03_Animal/vbnet/Animal.Tests/MockConsole.vb ================================================ Imports System.IO Public Class MockConsole Inherits ConsoleAdapterBase Private inputs As Queue(Of String) Public ReadOnly Lines As New List(Of (line As String, centered As Boolean)) From { ("", False) } ' TODO it's possible to clear all the lines, and we'd have to check once again in WriteString and WriteCenteredLine if there are any lines Sub New(Inputs As IEnumerable(Of String)) Me.inputs = New Queue(Of String)(Inputs) End Sub Private Sub CheckLinesInitialized() If Lines.Count = 0 Then Lines.Add(("", False)) End Sub Private Sub WriteString(s As String, Optional centered As Boolean = False) If s Is Nothing Then Return CheckLinesInitialized() s.Split(Environment.NewLine).ForEach(Sub(line, index) If index = 0 Then Dim currentLast = Lines(Lines.Count - 1) ' centered should never come from the current last line ' if WriteCenteredLine is called, it immediately creates a new line Lines(Lines.Count - 1) = (currentLast.line + line, centered) Else Lines.Add((line, centered)) End If End Sub) End Sub Public Overrides Sub Write(value As Object) WriteString(value?.ToString) End Sub Public Overrides Sub WriteLine() Lines.Add(("", False)) End Sub Public Overrides Sub WriteCenteredLines(value As Object) If Lines.Count = 0 Then Lines.Add(("", False)) Dim currentLast = Lines(Lines.Count - 1).line If currentLast.Length > 0 Then Lines.Add(("", False)) WriteString(value?.ToString, True) WriteLine() End Sub Public Overrides Function ReadLine() As String ' Indicates the end of a test run, for programs which loop endlessly If inputs.Count = 0 Then Throw New EndOfInputsException Dim nextInput = inputs.Dequeue.Trim WriteLine(nextInput) Return nextInput End Function End Class ================================================ FILE: 03_Animal/vbnet/Animal.Tests/TestContainer.vb ================================================ Imports Xunit Imports Animal Imports System.IO Public Class TestContainer Private Shared Function ResponseVariantExpander(src As IEnumerable(Of String)) As TheoryData(Of String) Dim theoryData = New TheoryData(Of String) src. SelectMany(Function(x) {x, x.Substring(0, 1)}). SelectMany(Function(x) { x, x.ToUpperInvariant, x.ToLowerInvariant, x.ToTitleCase, x.ToReverseCase }). Distinct. ForEach(Sub(x) theoryData.Add(x)) Return theoryData End Function Private Shared YesVariantsThepryData As TheoryData(Of String) = ResponseVariantExpander({"yes", "true", "1"}) Private Shared Function YesVariants() As TheoryData(Of String) Return YesVariantsThepryData End Function Private Shared NoVariantsThepryData As TheoryData(Of String) = ResponseVariantExpander({"no", "false", "0"}) Private Shared Function NoVariants() As TheoryData(Of String) Return NoVariantsThepryData End Function ''' Test LIST variants Sub List(listResponse As String) Dim console As New MockConsole({listResponse}) Dim game As New Game(console) Assert.Throws(Of EndOfInputsException)(Sub() game.BeginLoop()) Assert.Equal( { "ANIMALS I ALREADY KNOW ARE:", "FISH BIRD " }, console.Lines.Slice(-4, -2).Select(Function(x) x.line) ) End Sub '' Test YES variants Sub YesVariant(yesVariant As String) Dim console As New MockConsole({yesVariant}) Dim game As New Game(console) Assert.Throws(Of EndOfInputsException)(Sub() game.BeginLoop()) Assert.Equal( { $"ARE YOU THINKING OF AN ANIMAL? {yesVariant}", "DOES IT SWIM? " }, console.Lines.Slice(-2, 0).Select(Function(x) x.line) ) End Sub '' Test NO variants Sub NoVariant(noVariant As String) Dim console As New MockConsole({"y", noVariant}) Dim game As New Game(console) Assert.Throws(Of EndOfInputsException)(Sub() game.BeginLoop()) Assert.Equal( { $"DOES IT SWIM? {noVariant}", "IS IT A BIRD? " }, console.Lines.Slice(-2, 0).Select(Function(x) x.line) ) End Sub ''' Test adding a new animal and using the new animal in the game Sub TestAddedAnimal() Dim console As New MockConsole({ "y", "y", "n", "whale", "is it a mammal?", "y", "y", "y", "y", "y" }) Dim game As New Game(console) Assert.Throws(Of EndOfInputsException)(Sub() game.BeginLoop()) Assert.Equal( { "ARE YOU THINKING OF AN ANIMAL? y", "DOES IT SWIM? y", "IS IT A FISH? n", "THE ANIMAL YOU WERE THINKING OF WAS A ? whale", "PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A", "WHALE FROM A FISH", "is it a mammal?", "FOR A WHALE THE ANSWER WOULD BE? y", "ARE YOU THINKING OF AN ANIMAL? y", "DOES IT SWIM? y", "IS IT A MAMMAL? y", "IS IT A WHALE? y", "WHY NOT TRY ANOTHER ANIMAL?", "ARE YOU THINKING OF AN ANIMAL? " }, console.Lines.Slice(9, 100).Select(Function(x) x.line) ) End Sub End Class ================================================ FILE: 03_Animal/vbnet/Animal.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.32112.339 MinimumVisualStudioVersion = 10.0.40219.1 Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "Animal", "Animal\Animal.vbproj", "{5517E4CE-BCF9-4D1F-9A17-B620C1B96B0D}" EndProject Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Animal.Tests", "Animal.Tests\Animal.Tests.vbproj", "{3986C6A2-77D4-4F00-B3CF-F5736C623B1E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {5517E4CE-BCF9-4D1F-9A17-B620C1B96B0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5517E4CE-BCF9-4D1F-9A17-B620C1B96B0D}.Debug|Any CPU.Build.0 = Debug|Any CPU {5517E4CE-BCF9-4D1F-9A17-B620C1B96B0D}.Release|Any CPU.ActiveCfg = Release|Any CPU {5517E4CE-BCF9-4D1F-9A17-B620C1B96B0D}.Release|Any CPU.Build.0 = Release|Any CPU {3986C6A2-77D4-4F00-B3CF-F5736C623B1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3986C6A2-77D4-4F00-B3CF-F5736C623B1E}.Debug|Any CPU.Build.0 = Debug|Any CPU {3986C6A2-77D4-4F00-B3CF-F5736C623B1E}.Release|Any CPU.ActiveCfg = Release|Any CPU {3986C6A2-77D4-4F00-B3CF-F5736C623B1E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {88469A47-E30C-4763-A325-074101D16608} EndGlobalSection EndGlobal ================================================ FILE: 03_Animal/vbnet/README.md ================================================ Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET) This takes some inspiration from the [C# port of Animal](https://github.com/zspitz/basic-computer-games/tree/main/03_Animal/csharp). The `Game` class takes a console abstraction (`ConsoleAdapterBase`), which could also be used for different UIs, such as WinForms or a web page. This solution also has an xUnit tests project. Responses can be entered in any capitalization, but animals and the distinguishing question will be converted to uppercase. ================================================ FILE: 04_Awari/README.md ================================================ ### Awari Awari is an ancient African game played with seven sticks and thirty-six stones or beans laid out as shown above. The board is divided into six compartments or pits on each side. In addition, there are two special home pits at the ends. A move is made by taking all the beans from any (non-empty) pit on your own side. Starting from the pit to the right of this one, these beans are ‘sown’ one in each pit working around the board anticlockwise. A turn consists of one or two moves. If the last bean of your move is sown in your own home you may take a second move. If the last bean sown in a move lands in an empty pit, provided that the opposite pit is not empty, all the beans in the opposite pit, together with the last bean sown are ‘captured’ and moved to the player’s home. When either side is empty, the game is finished. The player with the most beans in his home has won. In the computer version, the board is printed as 14 numbers representing the 14 pits. ``` 3 3 3 3 3 3 0 0 3 3 3 3 3 3 ``` The pits on your (lower) side are numbered 1-6 from left to right. The pits on my (the computer’s) side are numbered from my left (your right). To make a move you type in the number of a pit. If the last bean lands in your home, the computer types ‘AGAIN?’ and then you type in your second move. The computer’s move is typed, followed by a diagram of the board in its new state. The computer always offers you the first move. This is considered to be a slight advantage. There is a learning mechanism in the program that causes the play of the computer to improve as it playes more games. The original version of Awari is adopted from one originally written by Geoff Wyvill of Bradford, Yorkshire, England. --- As published in Basic Computer Games (1978) - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=6) - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=21) Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html #### Porting Notes (please note any difficulties or challenges in porting here) ================================================ FILE: 04_Awari/awari.bas ================================================ 5 PRINT TAB(34);"AWARI" 7 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 10 DATA 0 15 DIM B(13),G(13),F(50):READ N 20 PRINT:PRINT:E=0 25 FOR I=0 TO 12:B(I)=3:NEXT I 30 C=0:F(N)=0:B(13)=0:B(6)=0 35 GOSUB 500 40 PRINT "YOUR MOVE";:GOSUB 110 45 IF E=0 THEN 80 50 IF M=H THEN GOSUB 100 55 IF E=0 THEN 80 60 PRINT "MY MOVE IS ";:GOSUB 800 65 IF E=0 THEN 80 70 IF M=H THEN PRINT ",";:GOSUB 800 75 IF E>0 THEN 35 80 PRINT:PRINT"GAME OVER" 85 D=B(6)-B(13):IF D<0 THEN PRINT "I WIN BY";-D;"POINTS":GOTO 20 90 N=N+1:IF D=0 THEN PRINT "DRAWN GAME":GOTO 20 95 PRINT "YOU WIN BY";D;"POINTS":GOTO 20 100 PRINT "AGAIN"; 110 INPUT M:IF M<7 THEN IF M>0 THEN M=M-1:GOTO 130 120 PRINT "ILLEGAL MOVE":GOTO 100 130 IF B(M)=0 THEN 120 140 H=6:GOSUB 200 150 GOTO 500 200 K=M:GOSUB 600 205 E=0:IF K>6 THEN K=K-7 210 C=C+1:IF C<9 THEN F(N)=F(N)*6+K 215 FOR I=0 TO 5:IF B(I)<>0 THEN 230 220 NEXT I 225 RETURN 230 FOR I=7 TO 12:IF B(I)<>0 THEN E=1:RETURN 235 GOTO 220 500 PRINT:PRINT" "; 505 FOR I=12 TO 7 STEP -1:GOSUB 580 510 NEXT I 515 PRINT:I=13:GOSUB 580 520 PRINT " ";:PRINT B(6):PRINT " "; 525 FOR I=0 TO 5:GOSUB 580 530 NEXT I 535 PRINT:PRINT:RETURN 580 IF B(I)<10 THEN PRINT " "; 585 PRINT B(I);:RETURN 600 P=B(M):B(M)=0 605 FOR P=P TO 1 STEP -1:M=M+1:IF M>13 THEN M=M-14 610 B(M)=B(M)+1:NEXT P 615 IF B(M)=1 THEN IF M<>6 THEN IF M<>13 THEN IF B(12-M)<>0 THEN 625 620 RETURN 625 B(H)=B(H)+B(12-M)+1:B(M)=0:B(12-M)=0:RETURN 800 D=-99:H=13 805 FOR I=0 TO 13:G(I)=B(I):NEXT I 810 FOR J=7 TO 12:IF B(J)=0 THEN 885 815 Q=0:M=J:GOSUB 600 820 FOR I=0 TO 5:IF B(I)=0 THEN 845 825 L=B(I)+I:R=0 830 IF L>13 THEN L=L-14:R=1:GOTO 830 835 IF B(L)=0 THEN IF L<>6 THEN IF L<>13 THEN R=B(12-L)+R 840 IF R>Q THEN Q=R 845 NEXT I 850 Q=B(13)-B(6)-Q:IF C>8 THEN 875 855 K=J:IF K>6 THEN K=K-7 860 FOR I=0 TO N-1:IF F(N)*6+K=INT(F(I)/6^(7-C)+.1) THEN Q=Q-2 870 NEXT I 875 FOR I=0 TO 13:B(I)=G(I):NEXT I 880 IF Q>=D THEN A=J:D=Q 885 NEXT J 890 M=A:PRINT CHR$(42+M);:GOTO 200 900 FOR I=0 TO N-1:PRINT B(I):NEXT I 999 END ================================================ FILE: 04_Awari/csharp/Awari.csproj ================================================ Exe net6.0 enable enable Awari ================================================ FILE: 04_Awari/csharp/Awari.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.32014.148 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Awari", "Awari.csproj", "{DD161F58-D90F-481A-8275-96E01D229A70}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {DD161F58-D90F-481A-8275-96E01D229A70}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DD161F58-D90F-481A-8275-96E01D229A70}.Debug|Any CPU.Build.0 = Debug|Any CPU {DD161F58-D90F-481A-8275-96E01D229A70}.Release|Any CPU.ActiveCfg = Release|Any CPU {DD161F58-D90F-481A-8275-96E01D229A70}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {7F5C288A-A6C6-4AC0-96E3-6A3B482A0947} EndGlobalSection EndGlobal ================================================ FILE: 04_Awari/csharp/Game.cs ================================================ namespace Awari; public class Game { public int[] PlayerPits => _beans[0..6]; public int[] ComputerPits => _beans[7..13]; public int PlayerHome => _beans[_playerHome]; public int ComputerHome => _beans[_computerHome]; private bool IsDone => PlayerPits.All(b => b == 0) // if all the player's pits are empty || ComputerPits.All(b => b == 0); // or if all the computer's pits are empty public GameState State { get; private set; } public void Reset() { State = GameState.PlayerMove; Array.Fill(_beans, _initialPitValue); _beans[_playerHome] = 0; _beans[_computerHome] = 0; _moveCount = 0; _notWonGameMoves[^1] = 0; } public bool IsLegalPlayerMove(int move) => move is > 0 and < 7 && _beans[move - 1] > 0; // arrays are zero-based, but moves are one-based public void PlayerMove(int move) => MoveAndRegister(move - 1, _playerHome); public List ComputerTurn() { // keep a list of moves made by the computer in a single turn (1 or 2) List moves = new(); moves.Add(ComputerMove()); // ComputerMove() returns the move made // only if a second move is possible, do it if (State == GameState.ComputerSecondMove) moves.Add(ComputerMove()); return moves; } public GameOutcome GetOutcome() { if (State != GameState.Done) throw new InvalidOperationException("Game is not yet done."); int difference = _beans[_playerHome] - _beans[_computerHome]; var winner = difference switch { < 0 => GameWinner.Computer, 0 => GameWinner.Draw, > 0 => GameWinner.Player, }; return new GameOutcome(winner, Math.Abs(difference)); } private void MoveAndRegister(int pit, int homePosition) { int lastMovedBean = Move(_beans, pit, homePosition); // encode moves by player and computer into a 'base 6' number // e.g. if the player moves 5, the computer moves 2, and the player moves 4, // that would be encoded as ((5 * 6) * 6) + (2 * 6) + 4 = 196 if (pit > 6) pit -= 7; _moveCount++; if (_moveCount < 9) _notWonGameMoves[^1] = _notWonGameMoves[^1] * 6 + pit; // determine next state based on current state, whether the game's done, and whether the last moved bean moved // into the player's home position State = (State, IsDone, lastMovedBean == homePosition) switch { (_, true, _) => GameState.Done, (GameState.PlayerMove, _, true) => GameState.PlayerSecondMove, (GameState.PlayerMove, _, false) => GameState.ComputerMove, (GameState.PlayerSecondMove, _, _) => GameState.ComputerMove, (GameState.ComputerMove, _, true) => GameState.ComputerSecondMove, (GameState.ComputerMove, _, false) => GameState.PlayerMove, (GameState.ComputerSecondMove, _, _) => GameState.PlayerMove, _ => throw new InvalidOperationException("Unexpected game state"), }; // do some bookkeeping if the game is done, but not won by the computer if (State == GameState.Done && _beans[_playerHome] >= _beans[_computerHome]) // add an entry for the next game _notWonGameMoves.Add(0); } private static int Move(int[] beans, int pit, int homePosition) { int beansToMove = beans[pit]; beans[pit] = 0; // add the beans that were in the pit to other pits, moving clockwise around the board for (; beansToMove >= 1; beansToMove--) { // wrap around if pit exceeds 13 pit = (pit + 1) % 14; beans[pit]++; } if (beans[pit] == 1 // if the last bean was sown in an empty pit && pit is not _playerHome and not _computerHome // which is not either player's home && beans[12 - pit] != 0) // and the pit opposite is not empty { // move the last pit sown and the _beans in the pit opposite to the player's home beans[homePosition] = beans[homePosition] + beans[12 - pit] + 1; beans[pit] = 0; beans[12 - pit] = 0; } return pit; } private int ComputerMove() { int move = DetermineComputerMove(); MoveAndRegister(move, homePosition: _computerHome); // the result is only used to return it to the application, so translate it from an array index (between 7 and // 12) to a pit number (between 1 and 6) return move - 6; } private int DetermineComputerMove() { int bestScore = -99; int move = 0; // for each of the computer's possible moves, simulate them to calculate a score and pick the best one for (int j = 7; j < 13; j++) { if (_beans[j] <= 0) continue; int score = SimulateMove(j); if (score >= bestScore) { move = j; bestScore = score; } } return move; } private int SimulateMove(int move) { // make a copy of the current state, so we can safely mess with it var hypotheticalBeans = new int[14]; _beans.CopyTo(hypotheticalBeans, 0); // simulate the move in our copy Move(hypotheticalBeans, move, homePosition: _computerHome); // determine the 'best' move the player could make after this (best for them, not for the computer) int score = ScoreBestNextPlayerMove(hypotheticalBeans); // score this move by calculating how far ahead we would be after the move, and subtracting the player's next // move score score = hypotheticalBeans[_computerHome] - hypotheticalBeans[_playerHome] - score; // have we seen the current set of moves before in a drawn/lost game? after 8 moves it's unlikely we'll find any // matches, since games will have diverged. also we don't have space to store that many moves. if (_moveCount < 8) { int translatedMove = move - 7; // translate from 7 through 12 to 0 through 5 // if the first two moves in this game were 1 and 2, and this hypothetical third move would be a 3, // movesSoFar would be (1 * 36) + (2 * 6) + 3 = 51 int movesSoFar = _notWonGameMoves[^1] * 6 + translatedMove; // since we store moves as a 'base 6' number, we need to divide stored moves by a power of 6 // let's say we've a stored lost game where the moves were, in succession, 1 through 8, the value stored // would be: // 8 + (7 * 6) + (6 * 36) + (5 * 216) + (4 * 1296) + (3 * 7776) + (2 * 46656) + (1 * 279936) = 403106 // to figure out the first three moves, we'd need to divide by 7776, resulting in 51.839... double divisor = Math.Pow(6.0, 7 - _moveCount); foreach (int previousGameMoves in _notWonGameMoves) // if this combination of moves so far ultimately resulted in a draw/loss, give it a lower score // note that this can happen multiple times if (movesSoFar == (int) (previousGameMoves / divisor + 0.1)) score -= 2; } return score; } private static int ScoreBestNextPlayerMove(int[] hypotheticalBeans) { int bestScore = 0; for (int i = 0; i < 6; i++) { if (hypotheticalBeans[i] <= 0) continue; int score = ScoreNextPlayerMove(hypotheticalBeans, i); if (score > bestScore) bestScore = score; } return bestScore; } private static int ScoreNextPlayerMove(int[] hypotheticalBeans, int move) { // figure out where the last bean will land int target = hypotheticalBeans[move] + move; int score = 0; // if it wraps around, that means the player is adding to his own pits, which is good if (target > 13) { // prevent overrunning the number of pits we have target %= 14; score = 1; } // if the player's move ends up in an empty pit, add the value of the pit on the opposite side to the score if (hypotheticalBeans[target] == 0 && target is not _playerHome and not _computerHome) score += hypotheticalBeans[12 - target]; return score; } private const int _playerHome = 6; private const int _computerHome = 13; private const int _initialPitValue = 3; private readonly int[] _beans = new int[14]; private readonly List _notWonGameMoves = new() { 0 }; // not won means draw or lose private int _moveCount; } public enum GameState { PlayerMove, PlayerSecondMove, ComputerMove, ComputerSecondMove, Done, } public enum GameWinner { Player, Computer, Draw, } public record struct GameOutcome(GameWinner Winner, int Difference); ================================================ FILE: 04_Awari/csharp/Program.cs ================================================ using Awari; Console.WriteLine(Tab(34) + "AWARI"); Console.WriteLine(Tab(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); Game game = new(); while (true) { game.Reset(); DisplayGame(); while (game.State != GameState.Done) { switch (game.State) { case GameState.PlayerMove: PlayerMove(second: false); break; case GameState.PlayerSecondMove: PlayerMove(second: true); break; case GameState.ComputerMove: ComputerTurn(); break; } DisplayGame(); } var outcome = game.GetOutcome(); string outcomeLabel = outcome.Winner switch { GameWinner.Computer => $"I WIN BY {outcome.Difference} POINTS", GameWinner.Draw => "DRAWN GAME", GameWinner.Player => $"YOU WIN BY {outcome.Difference} POINTS", _ => throw new InvalidOperationException($"Unexpected winner {outcome.Winner}."), }; Console.WriteLine(outcomeLabel); Console.WriteLine(); } void DisplayGame() { // display the computer's pits Console.Write(" "); foreach (var pit in game.ComputerPits.Reverse()) Console.Write($"{pit,2} "); Console.WriteLine(); // display both homes Console.WriteLine($"{game.ComputerHome,2}{Tab(19)}{game.PlayerHome,2}"); // display the player's pits Console.Write(" "); foreach (var pit in game.PlayerPits) Console.Write($"{pit,2} "); Console.WriteLine(); Console.WriteLine(); } void PlayerMove(bool second = false) { int move = GetMove(second); game.PlayerMove(move); } int GetMove(bool second) { string prompt = second ? "AGAIN? " : "YOUR MOVE? "; while (true) { Console.Write(prompt); string input = Console.ReadLine() ?? ""; // input must be a number between 1 and 6, and the pit must have > 0 beans if (int.TryParse(input, out int move) && game.IsLegalPlayerMove(move)) return move; Console.WriteLine("ILLEGAL MOVE"); } } void ComputerTurn() { var moves = game.ComputerTurn(); string movesString = string.Join(",", moves); Console.WriteLine($"MY MOVE IS {movesString}"); } string Tab(int n) => new(' ', n); ================================================ FILE: 04_Awari/csharp/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/) ================================================ FILE: 04_Awari/java/Awari.java ================================================ import java.util.Scanner; import java.util.Random; public class Awari{ int []board; private final int playerPits; private final int computerPits; private final int playerHome; private final int computerHome; Scanner input; int sumPlayer; int sumComputer; Awari(){ input = new Scanner(System.in); playerPits = 0; computerPits = 7; playerHome = 6; computerHome = 13; sumPlayer = 18; sumComputer = 18; board = new int [14]; for (int i=0;i<6;i++){ board[playerPits+i]=3; board[computerPits+i]=3; } System.out.println(" AWARI"); System.out.println("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); printBoard(); playerMove(true); } private void printBoard(){ System.out.print("\n "); for (int i=0;i<6;i++){ System.out.print(String.format("%2d",board[12-i])); System.out.print(" "); } System.out.println(""); System.out.print(String.format("%2d",board[computerHome])); System.out.print(" "); System.out.println(String.format("%2d",board[playerHome])); System.out.print(" "); for(int i=0;i<6;i++){ System.out.print(String.format("%2d",board[playerPits+i])); System.out.print(" "); } System.out.println(""); } private void playerMove(boolean val){ System.out.println("\nComputerSum PlayerSum"+sumComputer+" "+sumPlayer); if(val == true) System.out.print("YOUR MOVE? "); else System.out.print("AGAIN? "); int move = input.nextInt(); while(move<1||move>6||board[move-1]==0){ System.out.print("INVALID MOVE!!! TRY AGAIN "); move = input.nextInt(); } int seeds = board[move-1]; board[move-1] = 0; sumPlayer -= seeds; int last_pos = distribute(seeds,move); if(last_pos == playerHome){ printBoard(); if(isGameOver(true)){ System.exit(0); } playerMove(false); } else if(board[last_pos] == 1&&last_pos != computerHome){ int opp = calculateOpposite(last_pos); if(last_pos<6){ sumPlayer+=board[opp]; sumComputer-=board[opp]; } else{ sumComputer+=board[opp]; sumPlayer-=board[opp]; } board[last_pos]+=board[opp]; board[opp] = 0; printBoard(); if(isGameOver(false)){ System.exit(0); } computerMove(true); } else{ printBoard(); if(isGameOver(false)){ System.exit(0); } computerMove(true); } } private void computerMove(boolean value){ int val=-1; System.out.println("\nComputerSum PlayerSum"+sumComputer+" "+sumPlayer); for(int i=0;i<6;i++){ if(6-i == board[computerPits+i]) val = i; } int move ; if(val == -1) { Random random = new Random(); move = random.nextInt(6)+computerPits; while(board[move] == 0){ move = random.nextInt(6)+computerPits; } if(value == true) System.out.println(String.format("MY MOVE IS %d ",move-computerPits+1)); else System.out.println(String.format(",%d",move-computerPits+1)); int seeds = board[move]; board[move] = 0; sumComputer-=seeds; int last_pos = distribute(seeds,move+1); if(board[last_pos] == 1&&last_pos != playerHome){ int opp = calculateOpposite(last_pos); if(last_pos<6){ sumPlayer+=board[opp]; sumComputer-=board[opp]; } else{ sumComputer+=board[opp]; sumPlayer-=board[opp]; } board[last_pos]+=board[opp]; board[opp] = 0; printBoard(); if(isGameOver(false)){ System.exit(0); } } else{ printBoard(); if(isGameOver(false)){ System.exit(0); } } playerMove(true); } else { move = val+computerPits; if(value == true) System.out.print(String.format("MY MOVE IS %d",move-computerPits+1)); else System.out.print(String.format(",%d",move-computerPits+1)); int seeds = board[move]; board[move] = 0; sumComputer-=seeds; int last_pos = distribute(seeds,move+1); if(last_pos == computerHome){ if(isGameOver(true) ){ System.exit(0); } computerMove(false); } } } private int distribute(int seeds, int pos){ while(seeds!=0){ if(pos==14) pos=0; if(pos<6) sumPlayer++; else if(pos>6&&pos<13) sumComputer++; board[pos]++; pos++; seeds--; } return pos-1; } private int calculateOpposite(int pos){ return 12-pos; } private boolean isGameOver(boolean show){ if(sumPlayer == 0 || sumComputer == 0){ if(show) printBoard(); System.out.println("GAME OVER"); if(board[playerHome]>board[computerHome]){ System.out.println(String.format("YOU WIN BY %d POINTS",board[playerHome]-board[computerHome])); } else if(board[playerHome] AWARI
    
    
    
    
    
    
    ================================================
    FILE: 04_Awari/javascript/awari.js
    ================================================
    // AWARI
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    print(tab(34) + "AWARI\n");
    print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
    
    n = 0;
    
    b = [0,0,0,0,0,0,0,0,0,0,0,0,0,0];
    g = [0,0,0,0,0,0,0,0,0,0,0,0,0,0];
    f = [];
    for (i = 0; i <= 50; i++) {
        f[i] = 0;
    }
    
    function show_number(number)
    {
        if (number < 10)
            print("  " + number + " ");
        else
            print(" " + number + " ");
    }
    
    function show_board()
    {
        var i;
    
        print("\n");
        print("   ");
        for (i = 12; i >= 7; i--)
            show_number(b[i]);
        print("\n");
        i = 13;
        show_number(b[i]);
        print("                       " + b[6] + "\n");
        print("   ");
        for (i = 0; i <= 5; i++)
            show_number(b[i]);
        print("\n");
        print("\n");
    }
    
    function do_move()
    {
        k = m;
        adjust_board();
        e = 0;
        if (k > 6)
            k -= 7;
        c++;
        if (c < 9)
            f[n] = f[n] * 6 + k
            for (i = 0; i <= 5; i++) {
                if (b[i] != 0) {
                    for (i = 7; i <= 12; i++) {
                        if (b[i] != 0) {
                            e = 1;
                            return;
                        }
                    }
                }
            }
    }
    
    function adjust_board()
    {
        p = b[m];
        b[m] = 0;
        while (p >= 1) {
            m++;
            if (m > 13)
                m -= 14;
            b[m]++;
            p--;
        }
        if (b[m] == 1) {
            if (m != 6 && m != 13) {
                if (b[12 - m] != 0) {
                    b[h] += b[12 - m] + 1;
                    b[m] = 0;
                    b[12 - m] = 0;
                }
            }
        }
    }
    
    function computer_move()
    {
        d = -99;
        h = 13;
        for (i = 0; i<= 13; i++)	// Backup board
            g[i] = b[i];
        for (j = 7; j <= 12; j++) {
            if (b[j] == 0)
                continue;
            q = 0;
            m = j;
            adjust_board();
            for (i = 0; i <= 5; i++) {
                if (b[i] == 0)
                    continue;
                l = b[i] + i;
                r = 0;
                while (l > 13) {
                    l -= 14;
                    r = 1;
                }
                if (b[l] == 0) {
                    if (l != 6 && l != 13)
                        r = b[12 - l] + r;
                }
                if (r > q)
                    q = r;
            }
            q = b[13] - b[6] - q;
            if (c < 8) {
                k = j;
                if (k > 6)
                    k -= 7;
                for (i = 0; i <= n - 1; i++) {
                    if (f[n] * 6 + k == Math.floor(f[i] / Math.pow(7 - c, 6) + 0.1))
                        q -= 2;
                }
            }
            for (i = 0; i <= 13; i++)	// Restore board
                b[i] = g[i];
            if (q >= d) {
                a = j;
                d = q;
            }
        }
        m = a;
        print(m - 6);
        do_move();
    }
    
    // Main program
    async function main()
    {
        while (1) {
            print("\n");
            print("\n");
            e = 0;
            for (i = 0; i <= 12; i++)
                b[i] = 3;
    
            c = 0;
            f[n] = 0;
            b[13] = 0;
            b[6] = 0;
    
            while (1) {
                show_board();
                print("YOUR MOVE");
                while (1) {
                    m = parseInt(await input());
                    if (m < 7) {
                        if (m > 0) {
                            m--;
                            if (b[m] != 0)
                                break;
                        }
                    }
                    print("ILLEGAL MOVE\n");
                    print("AGAIN");
                }
                h = 6;
                do_move();
                show_board();
                if (e == 0)
                    break;
                if (m == h) {
                    print("AGAIN");
                    while (1) {
                        m = parseInt(await input());
                        if (m < 7) {
                            if (m > 0) {
                                m--;
                                if (b[m] != 0)
                                    break;
                            }
                        }
                        print("ILLEGAL MOVE\n");
                        print("AGAIN");
                    }
                    h = 6;
                    do_move();
                    show_board();
                }
                if (e == 0)
                    break;
                print("MY MOVE IS ");
                computer_move();
                if (e == 0)
                    break;
                if (m == h) {
                    print(",");
                    computer_move();
                }
                if (e == 0)
                    break;
            }
            print("\n");
            print("GAME OVER\n");
            d = b[6] - b[13];
            if (d < 0)
                print("I WIN BY " + -d + " POINTS\n");
            else if (d == 0) {
                n++;
                print("DRAWN GAME\n");
            } else {
                n++;
                print("YOU WIN BY " + d + " POINTS\n");
            }
        }
    }
    
    main();
    
    
    ================================================
    FILE: 04_Awari/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 04_Awari/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 04_Awari/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 04_Awari/perl/awari.pl
    ================================================
    #!/usr/bin/env perl
    use v5.24;
    use warnings;
    use experimental 'signatures';
    no warnings 'experimental::signatures';
    use List::Util 'none';
    
    # our board will be represented with an array of 14 slots, from 0 to 13.
    # Positions 6 and 13 represent the "home pit" for the human and the
    # computer, respectively.
    use constant PLAYER_HOME => 6;
    use constant COMPUTER_HOME => 13;
    
    use constant FIRST => 0;
    use constant AGAIN => 1;
    
    exit main(@ARGV);
    
    sub main {
       $|++; # disable buffering on standard output, every print will be
             # done immediately
    
       welcome(); # startup message
    
       # this array will keep track of computer-side failures, defined as
       # "the computer did not win". Whenever the computer loses or draws, the
       # specific sequence of moves will be saved and then used to drive
       # the search for a (hopefully) optimal move.
       my $failures = [];
       while ('enjoying') {
    
          # a new game starts, let's reset the board to the initial condition
          my $board = [ (3) x 6, 0, (3) x 6, 0 ];
    
          # this string will keep track of all moves performed
          my $moves = '/';
    
          # the human player starts
          my $turn = 'player';
    
          say "\n";
          print_board($board);
    
          while (not is_game_over($board)) {
    
             my $move; # this will collect the move in this turn
    
             if ($turn eq 'player') { # "first" move for player
    
                # player_move(...) does the move selected by the player,
                # returning both the selected move as well as the pit id
                # where the last seed landed
                ($move, my $landing) = player_move($board);
    
                # if we landed on the Player's Home Pit we get another move
                $turn = $landing == PLAYER_HOME ? 'player-again' : 'computer';
             }
             elsif ($turn eq 'player-again') { # "second" move for player
    
                # here we call player_move making it clear that it's the
                # second move, to get the right prompt eventually. We only
                # care for the $move as the result, so we ignore the other.
                ($move) = player_move($board, AGAIN);
                $turn = 'computer';
             }
             else {
    
                # the computer_move(...) function analyzes the $board as well
                # as adapting the strategy based on past "failures" (i.e.
                # matches where the computer did not win). For this it's
                # important to pass the log of these failures, as well as the
                # full record of moves in this specific match.
                ($move, my $landing) = computer_move($board, $failures, $moves);
                print "\nMY MOVE IS ", $move - 6;
    
                # do the second move in the turn if conditions apply
                if ($landing == COMPUTER_HOME && ! is_game_over($board)) {
    
                   # save the first move before doing the second one!
                   $moves .= "$move/";
    
                   my ($move) = computer_move($board, $failures, $moves);
                   print ',', $move - 6;
                }
                $turn = 'player';
             }
    
             # append the last selected move by either party, to track this
             # specific match (useful for computer's AI and ML)
             $moves .= "$move/";
             print_board($board);
          }
    
          # assess_victory() returns the difference between player's and
          # computer's seeds, so a negative value is a win for the computer.
          my $computer_won = assess_victory($board) < 0;
    
          # if this last match was a "failure" (read: not a win for the
          # computer), then record it for future memory.
          push $failures->@*, $moves unless $computer_won;
       }
    
       return 0;
    }
    
    # calculate the difference between the two home pits. Negative values mean
    # that the computer won, 0 is a draw, positive values is a player's win.
    # The difference is also returned back, in case of need.
    sub assess_victory ($board) {
       say "\nGAME OVER";
       my $difference = $board->[PLAYER_HOME] - $board->[COMPUTER_HOME];
       if ($difference < 0) {
          say 'I WIN BY ', -$difference, ' POINTS';
       }
       else {
          say $difference ? "YOU WIN BY $difference POINTS" : 'DRAWN GAME';
       }
       return $difference;
    }
    
    # move the seeds from $pit and take into account possible bonuses
    sub move_seeds ($board, $pit) {
    
       # get the seeds from the selected pit $pit
       my $seeds = $board->[$pit];
       $board->[$pit] = 0;
    
       # $landing will be our "moving cursor" to place seeds around
       my $landing = $pit;
       while ($seeds > 0) {
          $landing = ($landing + 1) % 14; # 12 --> 13 -[wrap]-> 0 --> 1
          --$seeds;
          ++$board->[$landing];
       }
    
       # check for "stealing seeds" condition. This cannot happen in home pits
       if ($landing != PLAYER_HOME && $landing != COMPUTER_HOME
           && $board->[$landing] == 1 && $board->[12 - $landing] > 0) {
          my $home = $pit < 7 ? PLAYER_HOME : COMPUTER_HOME;
          $board->[$home] += 1 + $board->[12 - $landing];
          $board->@[$landing, 12 - $landing] = (0, 0);
       }
    
       return ($pit, $landing);
    }
    
    sub get_player_move ($board, $prompt) {
       print "\n$prompt? ";
       while (defined(my $move = )) {
          chomp($move); # remove newline
          return $move - 1 if $move =~ m{\A[1-6]\z}mxs && $board->[$move - 1];
          print 'ILLEGAL MOVE\nAGAIN? ';
       }
       die "goodbye\n";
    }
    
    sub player_move ($board, $stage = FIRST) {
       my $prompt = $stage == FIRST ? 'YOUR MOVE' : 'AGAIN';
       my $selected_move = get_player_move($board, $prompt);
       return move_seeds($board, $selected_move);
    }
    
    sub computer_move ($board, $failures, $moves) {
    
       # we will go through all possible moves for the computer and all
       # possible responses by the player, collecting the "best" move in terms
       # of reasonable outcome (assuming that each side wants to maximize their
       # outcome. $best_move will eventually contain the best move for the
       # computer, and $best_difference the best difference in scoring (as
       # seen from the computer).
       my ($best_move, $best_difference);
       for my $c_move (7 .. 12) {
          next unless $board->[$c_move]; # only consider pits with seeds inside
    
          # we work on a copy of the board to do all our trial-and-errors
          my $copy = [ $board->@* ];
          move_seeds($copy, $c_move);
    
          # it's time to "think like a player" and see what's the "best" move
          # for the player in this situation. This heuristic is "not perfect"
          # but it seems OK anyway.
          my $best_player_score = 0;
          for my $p_move (0 .. 5) {
             next unless $copy->[$p_move]; # only pits with seeds inside
             my $landing = $copy->[$p_move] + $p_move;
    
             # the player's score for this move, calculated as additional seeds
             # placed in the player's pit. The original algorithm sets this to
             # 1 only if the $landing position is greater than 13, which can
             # be obtained by setting the ORIGINAL environment variable to a
             # "true" value (in Perl terms). Otherwise it is calculated
             # according to the real rules for the game.
             my $p_score = $ENV{ORIGINAL} ? $landing > 13
                : ($landing + 1) % 14 > 6;
    
             # whatever, the landing position must be within the bounds
             $landing %= 14;
    
             # if the conditions apply, the player's move might win additional
             # seeds, which we have to to take into account.
             $p_score += $copy->[12 - $landing]
                if $copy->[$landing] == 0
                && $landing != PLAYER_HOME && $landing != COMPUTER_HOME;
    
             # let's compare this move's score against the best collected
             # so far (as a response to a specific computer's move).
             $best_player_score = $p_score if $p_score > $best_player_score;
          }
    
          # the overall score for the player is the additional seeds we just
          # calculated into $best_player_score plus the seeds that were already
          # in the player's pit
          $best_player_score += $copy->[PLAYER_HOME];
    
          # the best difference we can aim for with this computer's move must
          # assume that the player will try its best
          my $difference = $copy->[COMPUTER_HOME] - $best_player_score;
    
          # now it's time to check this computer's move against the history
          # of failed matches. $candidate_moves will be the "candidate" list
          # of moves if we accept this one.
          my $candidate_moves = $moves . $c_move . '/';
          for my $failure ($failures->@*) {
    
             # index(.) returns 0 if and only if $candidate_moves appears at
             # the very beginning of $failure, i.e. it matches a previous
             # behaviour.
             next if index($failure, $candidate_moves) != 0;
    
             # same sequence of moves as before... assign a penalty
             $difference -= 2;
          }
    
          # update $best_move and $best_difference if they need to
          ($best_move, $best_difference) = ($c_move, $difference)
             if (! defined $best_move) || ($best_difference < $difference);
       }
    
       # apply the selected move and return
       return move_seeds($board, $best_move);
    }
    
    sub welcome {
       say ' ' x 34, 'AWARI';
       say ' ' x 15, 'CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY';
    }
    
    sub print_board ($board) {
       my $template = '
        %2d  %2d  %2d  %2d  %2d  %2d
     %2d                         %d
        %2d  %2d  %2d  %2d  %2d  %2d
    ';
       printf $template, $board->@[12, 11, 10, 9, 8 , 7, 13, 6, 0 .. 5];
       return;
    }
    
    sub is_game_over ($board) {
    
       # game over if the player's side is empty
       return 1 if none { $_ } $board->@[0 ..  5];
    
       # game over if the computers' side is empty
       return 1 if none { $_ } $board->@[7 .. 12];
    
       # not game over
       return 0;
    }
    
    
    ================================================
    FILE: 04_Awari/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 04_Awari/python/awari.py
    ================================================
    """
    AWARI
    
    An ancient African game (see also Kalah, Mancala).
    
    Ported by Dave LeCompte
    """
    
    # PORTING NOTES
    #
    # This game started out as 70 lines of BASIC, and I have ported it
    # before. I find it somewhat amazing how efficient (densely packed) the
    # original code is. Of course, the original code has fairly cryptic
    # variable names (as was forced by BASIC's limitation on long (2+
    # character) variable names). I have done my best here to interpret what
    # each variable is doing in context, and rename them appropriately.
    #
    # I have endeavored to leave the logic of the code in place, as it's
    # interesting to see a 2-ply game tree evaluation written in BASIC,
    # along with what a reader in 2021 would call "machine learning".
    #
    # As each game is played, the move history is stored as base-6
    # digits stored losing_book[game_number]. If the human player wins or
    # draws, the computer increments game_number, effectively "recording"
    # that loss to be referred to later. As the computer evaluates moves, it
    # checks the potential game state against these losing game records, and
    # if the potential move matches with the losing game (up to the current
    # number of moves), that move is evaluated at a two point penalty.
    #
    # Compare this, for example with MENACE, a mechanical device for
    # "learning" tic-tac-toe:
    # https://en.wikipedia.org/wiki/Matchbox_Educable_Noughts_and_Crosses_Engine
    #
    # The base-6 representation allows game history to be VERY efficiently
    # represented. I considered whether to rewrite this representation to be
    # easier to read, but I elected to TRY to document it, instead.
    #
    # Another place where I have made a difficult decision between accuracy
    # and correctness is inside the "wrapping" code where it considers
    # "while human_move_end > 13". The original BASIC code reads:
    #
    # 830 IF L>13 THEN L=L-14:R=1:GOTO 830
    #
    # I suspect that the intention is not to assign 1 to R, but to increment
    # R. I discuss this more in a porting note comment next to the
    # translated code. If you wish to play a more accurate version of the
    # game as written in the book, you can convert the increment back to an
    # assignment.
    #
    # I continue to be impressed with this jewel of a game; as soon as I had
    # the AI playing against me, it was beating me. I've been able to score
    # a few wins against the computer, but even at its 2-ply lookahead, it
    # beats me nearly always. I would like to become better at this game to
    # explore the effectiveness of the "losing book" machine learning.
    #
    #
    # EXERCISES FOR THE READER
    # One could go many directions with this game:
    # - change the initial number of stones in each pit
    # - change the number of pits
    # - only allow capturing if you end on your side of the board
    # - don't allow capturing at all
    # - don't drop a stone into the enemy "home"
    # - go clockwise, instead
    # - allow the player to choose to go clockwise or counterclockwise
    # - instead of a maximum of two moves, allow each move that ends on the
    #   "home" to be followed by a free move.
    # - increase the AI lookahead
    # - make the scoring heuristic a little more nuanced
    # - store history to a file on disk (or in the cloud!) to allow the AI
    #   to learn over more than a single session
    
    from typing import Dict, List, Tuple
    
    game_number: int = 0
    move_count: int = 0
    losing_book: List[int] = []
    n = 0
    
    MAX_HISTORY = 9
    LOSING_BOOK_SIZE = 50
    
    
    def draw_pit(line: str, board, pit_index) -> str:
        val = board[pit_index]
        line += " "
        if val < 10:
            line += " "
        return line + str(val) + " "
    
    
    def draw_board(board) -> None:
        print()
    
        # Draw the top (computer) pits
        line = "   "
        for i in range(12, 6, -1):
            line = draw_pit(line, board, i)
        print(line)
    
        # Draw the side (home) pits
        line = draw_pit("", board, 13)
        line += " " * 24
        line = draw_pit(line, board, 6)
        print(line)
    
        # Draw the bottom (player) pits
        line = "   "
        for i in range(0, 6):
            line = draw_pit(line, board, i)
        print(line)
        print()
        print()
    
    
    def play_game(board: List[int]) -> None:
        # Place the beginning stones
        for i in range(0, 13):
            board[i] = 3
    
        # Empty the home pits
        board[6] = 0
        board[13] = 0
    
        global move_count
        move_count = 0
    
        # clear the history record for this game
        losing_book[game_number] = 0
    
        while True:
            draw_board(board)
    
            print("YOUR MOVE")
            landing_spot, is_still_going, home = player_move(board)
            if not is_still_going:
                break
            if landing_spot == home:
                landing_spot, is_still_going, home = player_move_again(board)
            if not is_still_going:
                break
    
            print("MY MOVE")
            landing_spot, is_still_going, home, msg = computer_move("", board)
    
            if not is_still_going:
                print(msg)
                break
            if landing_spot == home:
                landing_spot, is_still_going, home, msg = computer_move(f"{msg} , ", board)
            if not is_still_going:
                print(msg)
                break
            print(msg)
    
        game_over(board)
    
    
    def computer_move(msg: str, board) -> Tuple[int, bool, int, str]:
        # This function does a two-ply lookahead evaluation; one computer
        # move plus one human move.
        #
        # To do this, it makes a copy (temp_board) of the board, plays
        # each possible computer move and then uses math to work out what
        # the scoring heuristic is for each possible human move.
        #
        # Additionally, if it detects that a potential move puts it on a
        # series of moves that it has recorded in its "losing book", it
        # penalizes that move by two stones.
    
        best_quality = -99
    
        # Make a copy of the board, so that we can experiment. We'll put
        # everything back, later.
        temp_board = board[:]
    
        # For each legal computer move 7-12
        for computer_move in range(7, 13):
            if board[computer_move] == 0:
                continue
            do_move(computer_move, 13, board)  # try the move (1 move lookahead)
    
            best_player_move_quality = 0
            # for all legal human moves 0-5 (responses to computer move computer_move)
            for human_move_start in range(0, 6):
                if board[human_move_start] == 0:
                    continue
    
                human_move_end = board[human_move_start] + human_move_start
                this_player_move_quality = 0
    
                # If this move goes around the board, wrap backwards.
                #
                # PORTING NOTE: The careful reader will note that I am
                # incrementing this_player_move_quality for each wrap,
                # while the original code only set it equal to 1.
                #
                # I expect this was a typo or oversight, but I also
                # recognize that you'd have to go around the board more
                # than once for this to be a difference, and even so, it
                # would be a very small difference; there are only 36
                # stones in the game, and going around the board twice
                # requires 24 stones.
    
                while human_move_end > 13:
                    human_move_end = human_move_end - 14
                    this_player_move_quality += 1
    
                if (
                    (board[human_move_end] == 0)
                    and (human_move_end != 6)
                    and (human_move_end != 13)
                ):
                    # score the capture
                    this_player_move_quality += board[12 - human_move_end]
    
                if this_player_move_quality > best_player_move_quality:
                    best_player_move_quality = this_player_move_quality
    
            # This is a zero sum game, so the better the human player's
            # move is, the worse it is for the computer player.
            computer_move_quality = board[13] - board[6] - best_player_move_quality
    
            if move_count < MAX_HISTORY:
                move_digit = computer_move
                if move_digit > 6:
                    move_digit = move_digit - 7
    
                # Calculate the base-6 history representation of the game
                # with this move. If that history is in our "losing book",
                # penalize that move.
                for prev_game_number in range(game_number):
                    if losing_book[game_number] * 6 + move_digit == int(
                        losing_book[prev_game_number] / 6 ^ (7 - move_count) + 0.1  # type: ignore
                    ):
                        computer_move_quality -= 2
    
            # Copy back from temporary board
            for i in range(14):
                board[i] = temp_board[i]
    
            if computer_move_quality >= best_quality:
                best_move = computer_move
                best_quality = computer_move_quality
    
        selected_move = best_move
    
        move_str = chr(42 + selected_move)
        if msg:
            msg += f", {move_str}"
        else:
            msg = move_str
    
        move_number, is_still_going, home = execute_move(selected_move, 13, board)
    
        return move_number, is_still_going, home, msg
    
    
    def game_over(board) -> None:
        print()
        print("GAME OVER")
    
        pit_difference = board[6] - board[13]
        if pit_difference < 0:
            print(f"I WIN BY {-pit_difference} POINTS")
    
        else:
            global n
            n = n + 1
    
            if pit_difference == 0:
                print("DRAWN GAME")
            else:
                print(f"YOU WIN BY {pit_difference} POINTS")
    
    
    def do_capture(m, home, board) -> None:
        board[home] += board[12 - m] + 1
        board[m] = 0
        board[12 - m] = 0
    
    
    def do_move(m, home, board) -> int:
        move_stones = board[m]
        board[m] = 0
    
        for _stones in range(move_stones, 0, -1):
            m = m + 1
            if m > 13:
                m = m - 14
            board[m] += 1
        if board[m] == 1 and (m != 6) and (m != 13) and (board[12 - m] != 0):
            do_capture(m, home, board)
        return m
    
    
    def player_has_stones(board) -> bool:
        return any(board[i] > 0 for i in range(6))
    
    
    def computer_has_stones(board: Dict[int, int]) -> bool:
        return any(board[i] > 0 for i in range(7, 13))
    
    
    def execute_move(move, home: int, board) -> Tuple[int, bool, int]:
        move_digit = move
        last_location = do_move(move, home, board)
    
        if move_digit > 6:
            move_digit = move_digit - 7
    
        global move_count
        move_count += 1
        if move_count < MAX_HISTORY:
            # The computer keeps a chain of moves in losing_book by
            # storing a sequence of moves as digits in a base-6 number.
            #
            # game_number represents the current game,
            # losing_book[game_number] records the history of the ongoing
            # game.  When the computer evaluates moves, it tries to avoid
            # moves that will lead it into paths that have led to previous
            # losses.
            losing_book[game_number] = losing_book[game_number] * 6 + move_digit
    
        is_still_going = bool(player_has_stones(board) and computer_has_stones(board))
        return last_location, is_still_going, home
    
    
    def player_move_again(board) -> Tuple[int, bool, int]:
        print("AGAIN")
        return player_move(board)
    
    
    def player_move(board) -> Tuple[int, bool, int]:
        while True:
            print("SELECT MOVE 1-6")
            m = int(input()) - 1
    
            if m > 5 or m < 0 or board[m] == 0:
                print("ILLEGAL MOVE")
                continue
    
            break
    
        ending_spot, is_still_going, home = execute_move(m, 6, board)
    
        draw_board(board)
    
        return ending_spot, is_still_going, home
    
    
    def main() -> None:
        print(" " * 34 + "AWARI")
        print(" " * 15 + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n\n")
    
        board = [0] * 14  # clear the board representation
        global losing_book
        losing_book = [0] * LOSING_BOOK_SIZE  # clear the "machine learning" state
    
        while True:
            play_game(board)
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 04_Awari/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/) by [Alex Scown](https://github.com/TheScown)
    
    
    ================================================
    FILE: 04_Awari/ruby/awari.rb
    ================================================
    require 'strscan'
    
    # Prints a number according to Vintage Basic's PRINT statement
    # @param n The number to print
    def print_number(n)
      # PRINT adds padding after a number and before a positive number
      print ' ' if n >= 0
      print n.to_s
      print ' '
    end
    
    # Mimic the INPUT statement using Vintage Basic as a reference
    # @param prompt The prompt to show to the user
    # @return An array of strings representing the inputted values
    def input(prompt)
      prompt_suffix = '? '
      print "#{prompt}#{prompt_suffix}"
    
      input = gets.chomp.strip
      scanner = StringScanner.new(input)
      input_values = []
    
      until scanner.eos?
        scanner.scan(/\s+/)
    
        if scanner.check(/"/)
          scanner.scan(/"/)
          next_string = scanner.scan_until(/"/)
    
          if next_string
            # Remove the trailing close quote
            next_string.chomp!('"')
          else
            # No close quote – Vintage Basic crashes in this case
            raise 'Unmatched quotes in input'
          end
        elsif scanner.exist?(/,/)
          next_string = scanner.scan_until(/,/).chomp(',')
        else
          next_string = scanner.scan_until(/\s+|$/).rstrip
        end
    
        input_values << next_string
      end
    
      input_values << '' if input_values.empty?
    
      input_values
    end
    
    class Game
      def initialize(history, non_win_count)
        @beans = Array.new(13, 3)
        @beans[6] = 0
        @beans[13] = 0
    
        @turn_counter = 0
    
        @history = history
        @non_win_count = non_win_count
      end
    
      # @return [Boolean] True if the computer did not win the game
      def play
        while true
          print_beans
    
          move = get_move("YOUR MOVE")
          home_pit = 6
          computer_home_pit = 13
    
          last_pit = perform_move(move, home_pit)
    
          print_beans
    
          break if game_over
    
          if home_pit == last_pit
            second_move = get_move("AGAIN")
    
            perform_move(second_move, home_pit)
    
            print_beans
    
            break if game_over
          end
    
          computer_move, computer_last_pit = get_computer_move
          print "MY MOVE IS #{computer_move - 6}"
    
          break if game_over
    
          if computer_last_pit == computer_home_pit
            second_computer_move, _ = get_computer_move
            print ",#{second_computer_move - 6}"
    
            break if game_over
          end
        end
    
        end_game
      end
    
      private
    
      def game_over
        @beans[0...6].all? { |b| b == 0 } || @beans[7...13].all? { |b| b == 0 }
      end
    
      # @return [Boolean] True if the computer did not win
      def end_game
        puts
        puts "GAME OVER"
    
        difference = @beans[6] - @beans[13]
    
        if difference < 0
          puts "I WIN BY #{-difference} POINTS"
    
          return
        end
    
        puts "YOU WIN BY #{difference} POINTS" if difference > 0
        puts "DRAWN GAME" if difference == 0
    
        difference >= 0
      end
    
      # @param [Integer] move
      # @param [Integer] home_pit
      def perform_move(move, home_pit)
        last_pit = distribute_beans(move, home_pit)
    
        update_history(move)
    
        last_pit
      end
    
      def update_history(current_move)
        k = current_move % 7
        @turn_counter += 1
    
        # Add the move to the history
        @history[@non_win_count] = @history[@non_win_count] * 6 + k if @turn_counter < 9
      end
    
      def print_beans
        puts
    
        # Print computer beans
        print ' ' * 3
        @beans[7...13].reverse.each { |bean_count| print_bean(bean_count) }
        puts
    
        # Print home beans
        print_bean(@beans[13])
        print ' ' * 23
        print_number(@beans[6]) # This is not print_bean in line with the original version
        puts
    
        # Print player beans
        print ' ' * 3
        @beans[0...6].each { |bean_count| print_bean(bean_count) }
        puts
    
        puts
      end
    
      def get_move(prompt)
        move = get_integer_input(prompt)
    
        while move < 1 || move > 6 || @beans[move - 1] == 0
          puts "ILLEGAL MOVE"
          move = get_integer_input("AGAIN")
        end
    
        move - 1
      end
    
      def distribute_beans(start_pit, home_pit, beans = @beans)
        beans_to_distribute = beans[start_pit]
        beans[start_pit] = 0
    
        current_pit = start_pit
    
        (0...beans_to_distribute).each do
          current_pit = (current_pit + 1) % beans.size
          beans[current_pit] += 1
        end
    
        # If the last pit was empty before we put a bean in it (and it's not a scoring pit), add beans to score
        if beans[current_pit] == 1 && current_pit != 6 && current_pit != 13 && beans[12 - current_pit] != 0
          beans[home_pit] = beans[home_pit] + beans[12 - current_pit] + 1
          beans[current_pit] = 0
          beans[12 - current_pit] = 0
        end
    
        current_pit
      end
    
      def print_bean(bean_count)
        print ' ' if bean_count < 10
        print_number(bean_count)
      end
    
      def get_integer_input(prompt)
        integer_value = nil
    
        input_values = input(prompt)
    
        while integer_value.nil?
          print '!EXTRA INPUT IGNORED' if (input_values.size > 1)
    
          value = input_values.first
    
          begin
            integer_value = Integer(value)
          rescue
            puts '!NUMBER EXPECTED - RETRY INPUT LINE'
            input_values = input('')
          end
        end
    
        integer_value
      end
    
      def get_computer_move
        d = -99
        home_pit = 13
    
        chosen_move = 7
    
        # Test all possible moves
        (7...13).each do |move_under_test|
          # Create a copy of the beans to test against
          beans_copy = @beans.dup
    
          # If the move is not legal, skip it
          next if beans_copy[move_under_test] == 0
    
          # Determine the best response the player may make to this move
          player_max_score = 0
    
          # Make the move under test against the copy
          distribute_beans(move_under_test, home_pit, beans_copy)
    
          # Test every player response
          (0...6).each do |i|
            # Skip the move if it would be illegal
            next if beans_copy[i] == 0
    
            # Determine the last
            landing_with_overflow = beans_copy[i] + i
            # If landing > 13 the player has put a bean in both home pits
            player_move_score = (landing_with_overflow > 14) ? 1 : 0
            # Find the actual pit
            landing = landing_with_overflow % 14
    
            # If the landing pit is empty, the player will steal beans
            if beans_copy[landing] == 0 && landing != 6 && landing != 13
              player_move_score = beans_copy[12 - landing] + player_move_score
            end
    
            # Update the max score if this move is the best player move
            player_max_score = player_move_score if player_move_score > player_max_score
          end
    
          # Final score for move is computer score, minus the player's score and any player gains from their best move
          final_score = beans_copy[13] - beans_copy[6] - player_max_score
    
          if @turn_counter < 9
            k = move_under_test % 7
    
            (0...@non_win_count).each do |i|
              # Penalise move if it was used in a losing game
              final_score = final_score - 2 if @history[@non_win_count] * 6 + k == ((Float(@history[i]) / 6 ** (7 - @turn_counter)) + 0.1).floor
            end
          end
    
          # Choose the move if it is the best move found so far
          if final_score >= d
            chosen_move = move_under_test
            d = final_score
          end
        end
    
        last_pit = perform_move(chosen_move, home_pit)
    
        [chosen_move, last_pit]
      end
    end
    
    puts 'AWARI'.center(80)
    puts 'CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY'.center(80)
    
    # Initialise stable variables
    history = Array.new(50)
    non_win_count = 0
    
    # APPLICATION LOOP
    while true
      puts
      puts
    
      history[non_win_count] = 0
    
      game = Game.new(history, non_win_count)
    
      computer_didnt_win = game.play
      non_win_count += 1 if computer_didnt_win
    end
    
    
    ================================================
    FILE: 04_Awari/rust/Cargo.toml
    ================================================
    [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"
    
    ================================================
    FILE: 04_Awari/rust/src/main.rs
    ================================================
    use std::{thread, time::Duration};
    
    use rand::Rng;
    
    // AI "learning" is not implemented. Don't have the time. - Ugur
    
    fn main() {
        loop {
            let mut game = Game::default();
    
            loop {
                game.draw();
                if game.play_turn(false) {
                    break;
                }
            }
        }
    }
    
    enum DistributeResult {
        Normal,
        // Leftover beans
        EndOnHomePit(bool),
        // "true" if ended on Player Home Pit
        EndOnEmptyPit(usize),
        // "index" of the empty pit within the Row
        ChosenEmpty,
    }
    
    struct Game {
        pits: [u8; 14],
        player_turn: bool,
    }
    
    impl Default for Game {
        fn default() -> Self {
            println!("\n\n\t\t AWARI");
            println!("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
    
            Self {
                pits: [3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 0],
                player_turn: true,
            }
        }
    }
    
    impl Game {
        fn step_through(&mut self, mut index: usize) -> usize {
            let mut bean_amount = self.pits[index];
            self.pits[index] = 0;
    
            loop {
                index += 1;
    
                if index > self.pits.len() - 1 {
                    index = 0;
                }
    
                self.pits[index] += 1;
    
                bean_amount -= 1;
                if bean_amount == 0 {
                    return index;
                }
            }
        }
    
        fn play_turn(&mut self, is_repeat: bool) -> bool {
            use DistributeResult::*;
    
            if self.is_game_over() {
                println!("\nGame Over!");
                let (player_beans, ai_beans) = (self.pits[6], self.pits[13]);
                if player_beans == ai_beans {
                    println!("It's a draw")
                } else if player_beans > ai_beans {
                    println!("You win by {}", player_beans - ai_beans);
                } else {
                    println!("I win by {}", ai_beans - player_beans);
                }
                return true;
            }
    
            let chosen_index = if self.player_turn {
                player_prompt(if is_repeat { "Again?" } else { "Your move?" }) - 1
            } else {
                println!("========================");
    
                thread::sleep(Duration::from_secs(1));
    
                let non_empty_pits: Vec = self
                    .pits
                    .iter()
                    .enumerate()
                    .filter(|&(i, p)| (7..13).contains(&i) && *p > 0)
                    .map(|(i, _)| i)
                    .collect();
                let random_index = rand::thread_rng().gen_range(0..non_empty_pits.len());
                let ai_move = non_empty_pits[random_index];
    
                println!("My move is {}", ai_move - 6);
    
                println!("========================");
                ai_move
            };
    
            match self.process_choice(chosen_index) {
                Normal => (),
                EndOnHomePit(player) => {
                    self.draw();
    
                    if player == self.player_turn && !is_repeat {
                        self.play_turn(true);
                    }
                }
                EndOnEmptyPit(last_index) => {
                    let opposite_index = 12 - last_index;
                    let home_index = if self.player_turn { 6 } else { 13 };
                    let won_beans = 1 + self.pits[opposite_index];
    
                    self.pits[last_index] = 0;
                    self.pits[opposite_index] = 0;
                    self.pits[home_index] += won_beans;
                }
                ChosenEmpty => {
                    println!("Chosen pit is empty");
                    return self.play_turn(is_repeat);
                }
            }
    
            if !is_repeat {
                self.player_turn = !self.player_turn;
            }
    
            false
        }
    
        pub fn process_choice(&mut self, index: usize) -> DistributeResult {
            use DistributeResult::*;
    
            if self.pits[index] == 0 {
                return ChosenEmpty;
            }
    
            let last_index = self.step_through(index);
    
            if last_index == 6 && self.player_turn {
                return EndOnHomePit(true);
            } else if last_index == 13 && !self.player_turn {
                return EndOnHomePit(false);
            } else if self.pits[last_index] == 1 {
                return EndOnEmptyPit(last_index);
            }
    
            Normal
        }
    
        fn is_game_over(&self) -> bool {
            let player_empty = !(0..6).any(|i| self.pits[i] > 0);
            let ai_empty = !(7..13).any(|i| self.pits[i] > 0);
            player_empty || ai_empty
        }
    
        fn draw(&self) {
            let row_as_string = |player: bool| -> String {
                let mut row_as_string = String::new();
    
                let range = if player { 0..6 } else { 7..13 };
    
                range.for_each(|i| {
                    let mut bean_amount_as_string = self.pits[i].to_string();
                    bean_amount_as_string.push_str("  ");
    
                    if player {
                        row_as_string.push_str(&bean_amount_as_string);
                    } else {
                        row_as_string.insert_str(0, &bean_amount_as_string);
                    }
                });
    
                row_as_string
            };
    
            println!(
                "\n  {}\n{}                  {}\n  {}\n",
                row_as_string(false),
                self.pits[13].to_string(),
                self.pits[6].to_string(),
                row_as_string(true)
            );
        }
    }
    
    pub fn player_prompt(message: &str) -> usize {
        loop {
            let mut input = String::new();
            println!("{}", message);
    
            if let Ok(_) = std::io::stdin().read_line(&mut input) {
                match input.trim().parse::() {
                    Ok(n) => {
                        if (1..=6).contains(&n) {
                            return n;
                        } else {
                            println!("Enter a number between 1 and 6")
                        }
                    }
                    Err(e) => {
                        println!("{}", e);
                    }
                }
            }
        }
    }
    
    
    ================================================
    FILE: 04_Awari/vbnet/Awari.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Awari", "Awari.vbproj", "{718AECEB-CC24-49C8-B620-B2F93E021C51}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{718AECEB-CC24-49C8-B620-B2F93E021C51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{718AECEB-CC24-49C8-B620-B2F93E021C51}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{718AECEB-CC24-49C8-B620-B2F93E021C51}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{718AECEB-CC24-49C8-B620-B2F93E021C51}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 04_Awari/vbnet/Awari.vbproj
    ================================================
    
      
        Exe
        Awari
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 04_Awari/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 05_Bagels/README.md
    ================================================
    ### Bagels
    
    In this game, the computer picks a 3-digit secret number using the digits 0 to 9 and you attempt to guess what it is. You are allowed up to twenty guesses. No digit is repeated. After each guess the computer will give you clues about your guess as follows:
    
    - PICO    One digit is correct, but in the wrong place
    - FERMI    One digit is in the correct place
    - BAGELS   No digit is correct
    
    You will learn to draw inferences from the clues and, with practice, you’ll learn to improve your score. There are several good strategies for playing Bagels. After you have found a good strategy, see if you can improve it. Or try a different strategy altogether to see if it is any better. While the program allows up to twenty guesses, if you use a good strategy it should not take more than eight guesses to get any number.
    
    The original authors of this program are D. Resek and P. Rowe of the Lawrence Hall of Science, Berkeley, California.
    
    ---
    
    As published in Basic Computer Games (1978)
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=9)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=21)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 05_Bagels/bagels.bas
    ================================================
    5 PRINT TAB(33);"BAGELS"
    10 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY":PRINT:PRINT
    15 REM *** BAGLES NUMBER GUESSING GAME
    20 REM *** ORIGINAL SOURCE UNKNOWN BUT SUSPECTED TO BE
    25 REM *** LAWRENCE HALL OF SCIENCE, U.C. BERKELY
    30 DIM A1(3),A(3),B(3)
    40 Y=0:T=255
    50 PRINT:PRINT:PRINT
    70 INPUT "WOULD YOU LIKE THE RULES (YES OR NO)";A$
    90 IF LEFT$(A$,1)="N" THEN 150
    100 PRINT:PRINT "I AM THINKING OF A THREE-DIGIT NUMBER.  TRY TO GUESS"
    110 PRINT "MY NUMBER AND I WILL GIVE YOU CLUES AS FOLLOWS:"
    120 PRINT "   PICO   - ONE DIGIT CORRECT BUT IN THE WRONG POSITION"
    130 PRINT "   FERMI  - ONE DIGIT CORRECT AND IN THE RIGHT POSITION"
    140 PRINT "   BAGELS - NO DIGITS CORRECT"
    150 FOR I=1 TO 3
    160 A(I)=INT(10*RND(1))
    165 IF I-1=0 THEN 200
    170 FOR J=1 TO I-1
    180 IF A(I)=A(J) THEN 160
    190 NEXT J
    200 NEXT I
    210 PRINT:PRINT "O.K.  I HAVE A NUMBER IN MIND."
    220 FOR I=1 TO 20
    230 PRINT "GUESS #";I,
    240 INPUT A$
    245 IF LEN(A$)<>3 THEN 630
    250 FOR Z=1 TO 3:A1(Z)=ASC(MID$(A$,Z,1)):NEXT Z
    260 FOR J=1 TO 3
    270 IF A1(J)<48 THEN 300
    280 IF A1(J)>57 THEN 300
    285 B(J)=A1(J)-48
    290 NEXT J
    295 GOTO 320
    300 PRINT "WHAT?"
    310 GOTO 230
    320 IF B(1)=B(2) THEN 650
    330 IF B(2)=B(3) THEN 650
    340 IF B(3)=B(1) THEN 650
    350 C=0:D=0
    360 FOR J=1 TO 2
    370 IF A(J)<>B(J+1) THEN 390
    380 C=C+1
    390 IF A(J+1)<>B(J) THEN 410
    400 C=C+1
    410 NEXT J
    420 IF A(1)<>B(3) THEN 440
    430 C=C+1
    440 IF A(3)<>B(1) THEN 460
    450 C=C+1
    460 FOR J=1 TO 3
    470 IF A(J)<>B(J) THEN 490
    480 D=D+1
    490 NEXT J
    500 IF D=3 THEN 680
    505 IF C=0 THEN 545
    520 FOR J=1 TO C
    530 PRINT "PICO ";
    540 NEXT J
    545 IF D=0 THEN 580
    550 FOR J=1 TO D
    560 PRINT "FERMI ";
    570 NEXT J
    580 IF C+D<>0 THEN 600
    590 PRINT "BAGELS";
    600 PRINT
    605 NEXT I
    610 PRINT "OH WELL."
    615 PRINT "THAT'S TWENTY GUESSES.  MY NUMBER WAS";100*A(1)+10*A(2)+A(3)
    620 GOTO 700
    630 PRINT "TRY GUESSING A THREE-DIGIT NUMBER.":GOTO 230
    650 PRINT "OH, I FORGOT TO TELL YOU THAT THE NUMBER I HAVE IN MIND"
    660 PRINT "HAS NO TWO DIGITS THE SAME.":GOTO 230
    680 PRINT "YOU GOT IT!!!":PRINT
    690 Y=Y+1
    700 INPUT "PLAY AGAIN (YES OR NO)";A$
    720 IF LEFT$(A$,1)="Y" THEN 150
    730 IF Y=0 THEN 750
    740 PRINT:PRINT "A";Y;"POINT BAGELS BUFF!!"
    750 PRINT "HOPE YOU HAD FUN.  BYE."
    999 END
    
    
    ================================================
    FILE: 05_Bagels/csharp/BagelNumber.cs
    ================================================
    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace BasicComputerGames.Bagels
    {
    	public enum BagelValidation
    	{
    		Valid,
    		WrongLength,
    		NotUnique,
    		NonDigit
    	};
    	public class BagelNumber
    	{
    		private static readonly Random Rnd = new Random();
    
    		private readonly int[] _digits;
    		public override string ToString()
    		{
    			return String.Join('-', _digits);
    		}
    
    		public static BagelNumber CreateSecretNumber(int numDigits)
    		{
    			if (numDigits < 3 || numDigits > 9)
    				throw new ArgumentOutOfRangeException(nameof(numDigits),
    					"Number of digits must be between 3 and 9, inclusive");
    
    			var digits = GetDigits(numDigits);
    			return new BagelNumber(digits);
    		}
    
    
    
    		public static BagelValidation IsValid(string number, int length)
    		{
    			if (number.Length != length)
    				return BagelValidation.WrongLength;
    
    			if (!number.All(Char.IsDigit))
    				return BagelValidation.NonDigit;
    
    			if (new HashSet(number).Count != length)
    				return BagelValidation.NotUnique;
    
    			return BagelValidation.Valid;
    		}
    
    		public BagelNumber(string number)
    		{
    			if (number.Any(d => !Char.IsDigit(d)))
    				throw new ArgumentException("Number must be all unique digits", nameof(number));
    
    			_digits = number.Select(d => d - '0').ToArray();
    		}
    
    		//public BagelNumber(long number)
    		//{
    		//	var digits = new List();
    		//	if (number >= 1E10)
    		//		throw new ArgumentOutOfRangeException(nameof(number), "Number can be no more than 9 digits");
    
    		//	while (number > 0)
    		//	{
    		//		long num = number / 10;
    		//		int digit = (int)(number - (num * 10));
    		//		number = num;
    		//		digits.Add(digit);
    		//	}
    
    		//	_digits = digits.ToArray();
    		//}
    
    		public BagelNumber(int[] digits)
    		{
    			_digits = digits;
    		}
    
    		private static  int[] GetDigits(int numDigits)
    		{
    			int[] digits = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    			Shuffle(digits);
    			return digits.Take(numDigits).ToArray();
    
    		}
    
    		private static void Shuffle(int[] digits)
    		{
    			for (int i = digits.Length - 1; i > 0; --i)
    			{
    				int pos = Rnd.Next(i);
    				var t = digits[i];
    				digits[i] = digits[pos];
    				digits[pos] = t;
    			}
    
    		}
    
    		public (int pico, int fermi) CompareTo(BagelNumber other)
    		{
    			int pico = 0;
    			int fermi = 0;
    			for (int i = 0; i < _digits.Length; i++)
    			{
    				for (int j = 0; j < other._digits.Length; j++)
    				{
    					if (_digits[i] == other._digits[j])
    					{
    						if (i == j)
    							++fermi;
    						else
    							++pico;
    					}
    				}
    			}
    
    			return (pico, fermi);
    		}
    	}
    }
    
    
    ================================================
    FILE: 05_Bagels/csharp/Bagels.csproj
    ================================================
    
    
      
        Exe
        net5.0
      
    
      
        
      
    
    
    
    
    ================================================
    FILE: 05_Bagels/csharp/Bagels.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bagels", "Bagels.csproj", "{2FC5F33F-2C4B-4707-94E5-3C9B2B633EFE}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{2FC5F33F-2C4B-4707-94E5-3C9B2B633EFE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{2FC5F33F-2C4B-4707-94E5-3C9B2B633EFE}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{2FC5F33F-2C4B-4707-94E5-3C9B2B633EFE}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{2FC5F33F-2C4B-4707-94E5-3C9B2B633EFE}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 05_Bagels/csharp/Game.cs
    ================================================
    using System;
    using System.Collections.Generic;
    using System.Runtime.CompilerServices;
    
    namespace BasicComputerGames.Bagels
    {
    	public class Game : GameBase
    	{
    		public void GameLoop()
    		{
    			DisplayIntroText();
    			int points = 0;
    			do
    			{
    				var result =PlayRound();
    				if (result)
    					++points;
    			} while (TryAgain());
    
    			Console.WriteLine();
    			Console.WriteLine($"A {points} point Bagels buff!!");
    			Console.WriteLine("Hope you had fun. Bye.");
    		}
    
    		private const int Length = 3;
    		private const int MaxGuesses = 20;
    
    		private bool  PlayRound()
    		{
    			var secret = BagelNumber.CreateSecretNumber(Length);
    			Console.WriteLine("O.K. I have a number in mind.");
    			for (int guessNo = 1; guessNo <= MaxGuesses; ++guessNo)
    			{
    				string strGuess;
    				BagelValidation isValid;
    				do
    				{
    					Console.WriteLine($"Guess #{guessNo}");
    					strGuess = Console.ReadLine();
    					isValid = BagelNumber.IsValid(strGuess, Length);
    					PrintError(isValid);
    				} while (isValid != BagelValidation.Valid);
    
    				var guess = new BagelNumber(strGuess);
    				var fermi = 0;
    				var pico = 0;
    				(pico, fermi) = secret.CompareTo(guess);
    				if(pico + fermi == 0)
    					Console.Write("BAGELS!");
    				else if (fermi == Length)
    				{
    					Console.WriteLine("You got it!");
    					return true;
    				}
    				else
    				{
    					PrintList("Pico ", pico);
    					PrintList("Fermi ", fermi);
    				}
    				Console.WriteLine();
    			}
    
    			Console.WriteLine("Oh, well.");
    			Console.WriteLine($"That's {MaxGuesses} guesses.  My Number was {secret}");
    
    			return false;
    
    		}
    
    		private void PrintError(BagelValidation isValid)
    		{
    			switch (isValid)
    			{
    				case BagelValidation.NonDigit:
    					Console.WriteLine("What?");
    					break;
    
    				case BagelValidation.NotUnique:
    					Console.WriteLine("Oh, I forgot to tell you that the number I have in mind has no two digits the same.");
    					break;
    
    				case BagelValidation.WrongLength:
    					Console.WriteLine($"Try guessing a {Length}-digit number.");
    					break;
    
    				case BagelValidation.Valid:
    					break;
    			}
    		}
    
    		private void PrintList(string msg, int repeat)
    		{
    			for(int i=0; i
    		/// Prompt the player to try again, and wait for them to press Y or N.
    		/// 
    		/// Returns true if the player wants to try again, false if they have finished playing.
    		protected bool TryAgain()
    		{
    			Console.ForegroundColor = ConsoleColor.White;
    			Console.WriteLine("Would you like to try again? (Press 'Y' for yes or 'N' for no)");
    
    			Console.ForegroundColor = ConsoleColor.Yellow;
    			Console.Write("> ");
    
    			char pressedKey;
    			// Keep looping until we get a recognised input
    			do
    			{
    				// Read a key, don't display it on screen
    				ConsoleKeyInfo key = Console.ReadKey(true);
    				// Convert to upper-case so we don't need to care about capitalisation
    				pressedKey = Char.ToUpper(key.KeyChar);
    				// Is this a key we recognise? If not, keep looping
    			} while (pressedKey != 'Y' && pressedKey != 'N');
    			// Display the result on the screen
    			Console.WriteLine(pressedKey);
    
    			// Return true if the player pressed 'Y', false for anything else.
    			return (pressedKey == 'Y');
    		}
    
    	}
    }
    
    
    ================================================
    FILE: 05_Bagels/csharp/Program.cs
    ================================================
    namespace BasicComputerGames.Bagels
    {
    	public class Program
    	{
    		public static void Main(string[] args)
    		{
    			// Create an instance of our main Game class
    			var game = new Game();
    
    			// Call its GameLoop function. This will play the game endlessly in a loop until the player chooses to quit.
    			game.GameLoop();
    		}
    	}
    }
    
    
    ================================================
    FILE: 05_Bagels/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 05_Bagels/java/BagelGame.java
    ================================================
    /******************************************************************************
    *
    * Encapsulates all the state and game logic for one single game of Bagels
    *
    * Used by Bagels.java
    *
    * Jeff Jetton, 2020
    *
    ******************************************************************************/
    
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.HashSet;
    import java.util.List;
    import java.util.Random;
    import java.util.Set;
    
    public class BagelGame {
    
      public static final String CORRECT = "FERMI FERMI FERMI";
      public static final int MAX_GUESSES = 20;
    
      enum GameState {
          RUNNING,
          WON,
          LOST
        }
    
      private GameState      state = GameState.RUNNING;
      private List  secretNum;
      private int            guessNum = 1;
    
      public BagelGame() {
        // No-arg constructor for when you don't need to set the seed
        this(new Random());
      }
    
      public BagelGame(long seed) {
        // Setting the seed as a long value
        this(new Random(seed));
      }
    
      public BagelGame(Random rand) {
        // This is the "real" constructor, which expects an instance of
        // Random to use for shuffling the digits of the secret number.
    
        // Since the digits cannot repeat in our "number", we can't just
        // pick three random 0-9 integers. Instead, we'll treat it like
        // a deck of ten cards, numbered 0-9.
        List digits = new ArrayList(10);
        // The 10 specified initial allocation, not actual size,
        // which is why we add rather than set each element...
        for (int i = 0; i < 10; i++) {
          digits.add(i);
        }
        // Collections offers a handy-dandy shuffle method. Normally it
        // uses a fresh Random class PRNG, but we're supplying our own
        // to give us controll over whether or not we set the seed
        Collections.shuffle(digits, rand);
    
        // Just take the first three digits
        secretNum = digits.subList(0, 3);
      }
    
      public boolean isOver() {
        return state != GameState.RUNNING;
      }
    
      public boolean isWon() {
        return state == GameState.WON;
      }
    
      public int getGuessNum() {
        return guessNum;
      }
    
      public String getSecretAsString() {
        // Convert the secret number to a three-character string
        String secretString = "";
        for (int n : secretNum) {
          secretString += n;
        }
        return secretString;
      }
    
      @Override
      public String toString() {
        // Quick report of game state for debugging purposes
        String s = "Game is " + state + "\n";
        s += "Current Guess Number: " + guessNum + "\n";
        s += "Secret Number: " + secretNum;
        return s;
      }
    
      public String validateGuess(String guess) {
        // Checks the passed string and returns null if it's a valid guess
        // (i.e., exactly three numeric characters)
        // If not valid, returns an "error" string to display to user.
        String error = "";
    
        if (guess.length() == 3) {
          // Correct length. Are all the characters numbers?
          try {
            Integer.parseInt(guess);
          } catch (NumberFormatException ex) {
            error = "What?";
          }
          if (error == "") {
            // Check for unique digits by placing each character in a set
            Set uniqueDigits = new HashSet();
            for (int i = 0; i < guess.length(); i++){
              uniqueDigits.add(guess.charAt(i));
            }
            if (uniqueDigits.size() != guess.length()) {
              error = "Oh, I forgot to tell you that the number I have in mind\n";
              error += "has no two digits the same.";
            }
          }
        } else {
          error = "Try guessing a three-digit number.";
        }
    
        return error;
      }
    
      public String makeGuess(String s) throws IllegalArgumentException {
        // Processes the passed guess string (which, ideally, should be
        // validated by previously calling validateGuess)
        // Return a response string (PICO, FERMI, etc.) if valid
        // Also sets game state accordingly (sets win state or increments
        // number of guesses)
    
        // Convert string to integer list, just to keep things civil
        List guess = new ArrayList(3);
        for (int i = 0; i < 3; i++) {
          guess.add((int)s.charAt(i) - 48);
        }
    
        // Build response string...
        String response = "";
        // Correct digit, but in wrong place?
        for (int i = 0; i < 2; i++) {
          if (secretNum.get(i) == guess.get(i+1)) {
            response += "PICO ";
          }
          if (secretNum.get(i+1) == guess.get(i)) {
            response += "PICO ";
          }
        }
        if (secretNum.get(0) == guess.get(2)) {
          response += "PICO ";
        }
        if (secretNum.get(2) == guess.get(0)) {
          response += "PICO ";
        }
        // Correct digits in right place?
        for (int i = 0; i < 3; i++) {
          if (secretNum.get(i) == guess.get(i)) {
            response += "FERMI ";
          }
        }
        // Nothin' right?
        if (response == "") {
          response = "BAGELS";
        }
        // Get rid of any space that might now be at the end
        response = response.trim();
        // If correct, change state
        if (response.equals(CORRECT)) {
          state = GameState.WON;
        } else {
          // If not, increment guess counter and check for game over
          guessNum++;
          if (guessNum > MAX_GUESSES) {
            state = GameState.LOST;
          }
        }
        return response;
      }
    
    }
    
    
    ================================================
    FILE: 05_Bagels/java/Bagels.java
    ================================================
    /******************************************************************************
    *
    * Bagels
    *
    * From: BASIC Computer Games (1978)
    *       Edited by David H. Ahl
    *
    * "In this game, the computer picks a 3-digit secret number using
    *  the digits 0 to 9 and you attempt to guess what it is.  You are
    *  allowed up to twenty guesses.  No digit is repeated.  After
    *  each guess the computer will give you clues about your guess
    *  as follows:
    *
    *  PICO     One digit is correct, but in the wrong place
    *  FERMI    One digit is in the correct place
    *  BAGELS   No digit is correct
    *
    * "You will learn to draw inferences from the clues and, with
    *  practice, you'll learn to improve your score.  There are several
    *  good strategies for playing Bagels.  After you have found a good
    *  strategy, see if you can improve it.  Or try a different strategy
    *  altogether and see if it is any better.  While the program allows
    *  up to twenty guesses, if you use a good strategy it should not
    *  take more than eight guesses to get any number.
    *
    * "The original authors of this program are D. Resek and P. Rowe of
    *  the Lawrence Hall of Science, Berkeley, California."
    *
    * Java port by Jeff Jetton, 2020, based on an earlier Python port
    *
    ******************************************************************************/
    
    import java.util.Scanner;
    
    public class Bagels {
    
      public static void main(String[] args) {
    
        int gamesWon = 0;
    
        // Intro text
        System.out.println("\n\n                Bagels");
        System.out.println("Creative Computing  Morristown, New Jersey");
        System.out.println("\n\n");
        System.out.print("Would you like the rules (Yes or No)? ");
    
        // Need instructions?
        Scanner scan = new Scanner(System.in);
        String s = scan.nextLine();
        if (s.length() == 0 || s.toUpperCase().charAt(0) != 'N') {
          System.out.println();
          System.out.println("I am thinking of a three-digit number.  Try to guess");
          System.out.println("my number and I will give you clues as follows:");
          System.out.println("   PICO   - One digit correct but in the wrong position");
          System.out.println("   FERMI  - One digit correct and in the right position");
          System.out.println("   BAGELS - No digits correct");
        }
    
        // Loop for playing multiple games
        boolean stillPlaying = true;
        while(stillPlaying) {
    
          // Set up a new game
          BagelGame game = new BagelGame();
          System.out.println("\nO.K.  I have a number in mind.");
    
          // Loop guess and responsses until game is over
          while (!game.isOver()) {
            String guess = getValidGuess(game);
            String response = game.makeGuess(guess);
            // Don't print a response if the game is won
            if (!game.isWon()) {
              System.out.println(response);
            }
          }
    
          // Game is over. But did we win or lose?
          if (game.isWon()) {
            System.out.println("You got it!!!\n");
            gamesWon++;
          } else {
            System.out.println("Oh well");
            System.out.print("That's " + BagelGame.MAX_GUESSES + " guesses.  ");
            System.out.println("My number was " + game.getSecretAsString());
          }
    
          stillPlaying = getReplayResponse();
        }
    
        // Print goodbye message
        if (gamesWon > 0) {
          System.out.println("\nA " + gamesWon + " point Bagels buff!!");
        }
        System.out.println("Hope you had fun.  Bye.\n");
      }
    
      private static String getValidGuess(BagelGame game) {
        // Keep asking for a guess until valid
        Scanner scan = new Scanner(System.in);
        boolean valid = false;
        String guess = "";
        String error;
    
        while (!valid) {
          System.out.print("Guess # " + game.getGuessNum() + "     ? ");
          guess = scan.nextLine().trim();
          error = game.validateGuess(guess);
          if (error == "") {
            valid = true;
          } else {
            System.out.println(error);
          }
        }
        return guess;
      }
    
      private static boolean getReplayResponse() {
        // keep asking for response until valid
        Scanner scan = new Scanner(System.in);
        // Keep looping until a non-zero-length string is entered
        while (true) {
          System.out.print("Play again (Yes or No)? ");
          String response = scan.nextLine().trim();
          if (response.length() > 0) {
            return response.toUpperCase().charAt(0) == 'Y';
          }
        }
      }
    
    }
    
    
    ================================================
    FILE: 05_Bagels/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 05_Bagels/javascript/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells)
    
    
    ================================================
    FILE: 05_Bagels/javascript/bagels.html
    ================================================
    
    
    BAGELS
    
    
    
    
    
    
    
    
    
    
    ================================================
    FILE: 05_Bagels/javascript/bagels.js
    ================================================
    // BAGELS
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    print(tab(33) + "BAGELS\n");
    print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
    
    // *** Bagles number guessing game
    // *** Original source unknown but suspected to be
    // *** Lawrence Hall of Science, U.C. Berkeley
    
    a1 = [0,0,0,0];
    a = [0,0,0,0];
    b = [0,0,0,0];
    
    y = 0;
    t = 255;
    
    print("\n");
    print("\n");
    print("\n");
    
    // Main program
    async function main()
    {
        while (1) {
            print("WOULD YOU LIKE THE RULES (YES OR NO)");
            str = await input();
            if (str.substr(0, 1) != "N") {
                print("\n");
                print("I AM THINKING OF A THREE-DIGIT NUMBER.  TRY TO GUESS\n");
                print("MY NUMBER AND I WILL GIVE YOU CLUES AS FOLLOWS:\n");
                print("   PICO   - ONE DIGIT CORRECT BUT IN THE WRONG POSITION\n");
                print("   FERMI  - ONE DIGIT CORRECT AND IN THE RIGHT POSITION\n");
                print("   BAGELS - NO DIGITS CORRECT\n");
            }
            for (i = 1; i <= 3; i++) {
                do {
                    a[i] = Math.floor(Math.random() * 10);
                    for (j = i - 1; j >= 1; j--) {
                        if (a[i] == a[j])
                            break;
                    }
                } while (j >= 1) ;
            }
            print("\n");
            print("OK.  I HAVE A NUMBER IN MIND.\n");
            for (i = 1; i <= 20; i++) {
                while (1) {
                    print("GUESS #" + i);
                    str = await input();
                    if (str.length != 3) {
                        print("TRY GUESSING A THREE-DIGIT NUMBER.\n");
                        continue;
                    }
                    for (z = 1; z <= 3; z++)
                        a1[z] = str.charCodeAt(z - 1);
                    for (j = 1; j <= 3; j++) {
                        if (a1[j] < 48 || a1[j] > 57)
                            break;
                        b[j] = a1[j] - 48;
                    }
                    if (j <= 3) {
                        print("WHAT?");
                        continue;
                    }
                    if (b[1] == b[2] || b[2] == b[3] || b[3] == b[1]) {
                        print("OH, I FORGOT TO TELL YOU THAT THE NUMBER I HAVE IN MIND\n");
                        print("HAS NO TWO DIGITS THE SAME.\n");
                        continue;
                    }
                    break;
                }
                c = 0;
                d = 0;
                for (j = 1; j <= 2; j++) {
                    if (a[j] == b[j + 1])
                        c++;
                    if (a[j + 1] == b[j])
                        c++;
                }
                if (a[1] == b[3])
                    c++;
                if (a[3] == b[1])
                    c++;
                for (j = 1; j <= 3; j++) {
                    if (a[j] == b[j])
                        d++;
                }
                if (d == 3)
                    break;
                for (j = 0; j < c; j++)
                    print("PICO ");
                for (j = 0; j < d; j++)
                    print("FERMI ");
                if (c + d == 0)
                    print("BAGELS");
                print("\n");
            }
            if (i <= 20) {
                print("YOU GOT IT!!!\n");
                print("\n");
            } else {
                print("OH WELL.\n");
                print("THAT'S YOUR TWENTIETH GUESS.  MY NUMBER WAS " + a[1] + a[2] + a[3]);
            }
            y++;
            print("PLAY AGAIN (YES OR NO)");
            str = await input();
            if (str.substr(0, 1) != "Y")
                break;
        }
        if (y == 0)
            print("HOPE YOU HAD FUN.  BYE.\n");
        else
            print("\nA " + y + " POINT BAGELS BUFF!!\n");
    
    }
    
    main();
    
    
    ================================================
    FILE: 05_Bagels/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 05_Bagels/kotlin/bagels.kt
    ================================================
    import java.util.Random
    
    fun main() {
        println("BAGELS")
        println("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY")
        var a = mutableListOf(0, 0, 0)
        var y = 0
        var t = 255
        println()
        println()
        println()
        println("WOULD YOU LIKE THE RULES (YES OR NO)? ")
        var astring = readLine()
        if (astring?.substring(0, 1) != "N") {
            println()
            println()
            println("I AM THINKING OF A THREE-DIGIT NUMBER.  TRY TO GUESS")
            println("MY NUMBER AND I WILL GIVE YOU CLUES AS FOLLOWS:")
            println("   PICO   - ONE DIGIT CORRECT BUT IN THE WRONG POSITION")
            println("   FERMI  - ONE DIGIT CORRECT AND IN THE RIGHT POSITION")
            println("   BAGELS - NO DIGITS CORRECT")
        }
        var i = 0
        var random = Random()
        while (i < 3) {
            a[i] = random.nextInt(9) + 1
            if (i > 0) {
                for (j in 0 until i) {
                    if (a[i] == a[j])
                    {
                        a[i] = random.nextInt(9) + 1
                        i--
                    }
                }
            }
            i++
        }
        println()
        println("O.K.  I HAVE A NUMBER IN MIND.")
        do {
            var i = 1
            while (i <= 20) {
                do {
                    var repeatGuess = false
                    println("GUESS #" + i)
                    astring = readLine()
                    if (astring?.length != 3) {
                        println("TRY GUESSING A THREE-DIGIT NUMBER.")
                        repeatGuess = true
                    } else {
                        for (j in 0..2) {
                            if (astring[j].toInt() < 48 || astring[j].toInt() > 57) {
                                println("WHAT?")
                                repeatGuess = true
                            }
                        }
    
                        if (astring[0] == astring[1] ||
                            astring[1] == astring[2] ||
                            astring[2] == astring[0]
                        ) {
                            println("OH, I FORGOT TO TELL YOU THAT THE NUMBER I HAVE IN MIND")
                            println("HAS NO TWO DIGITS THE SAME.")
                            repeatGuess = true
                        }
                    }
                } while (repeatGuess);
    
                var c = 0
                var d = 0
                for (j in 0..2) {
                    if (astring!![j]?.toInt() - 48 == a[j]) {
                        c++
                        d++
                    }
                    if (j < 2 && astring!![j]?.toInt() - 48 == a[j + 1]) c++
                }
                if (astring!![2]?.toInt() - 48 == a[0]) c++
                if (astring!![0]?.toInt() - 48 == a[2]) c++
    
                if (d == 3) {
                    break;
                }
                if (c != 0) {
                    for (j in 1..c) {
                        print("PICO ")
                    }
                }
                if (d != 0) {
                    for (j in 1..d) {
                        print("FERMI ")
                    }
                }
                if (c + d == 0) {
                    print("BAGELS ")
                }
                println()
                i++
            }
    
            if (i == 21) {
                println("OH WELL.")
                println("THAT'S TWENTY GUESSES.  MY NUMBER WAS " + a[0] + a[1] + a[2])
            }
            else {
                println("YOU GOT IT!!!")
                println()
                y++
            }
            println("PLAY AGAIN (YES OR NO)?")
            astring = readLine()
        } while (astring?.substring(0, 1) == "Y");
        if (y != 0) {
            println()
            println("A $y POINT BAGELS BUFF!!")
        }
        println ("HOPE YOU HAD FUN.  BYE.")
    }
    
    
    ================================================
    FILE: 05_Bagels/lua/Bagels.lua
    ================================================
    ---
    --- Bagels
    --- Ported by Joe Nellis.
    --- Text displayed is altered slightly from the original program to allow for
    --- more (or less) than three digits to be guessed. Change the difficulty to
    --- the number of digits you wish in the secret code.
    ---
    
    --- difficult is number of digits to use
    local difficulty = 3
    
    print [[
                                    BAGELS
                  CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY
    
    ]]
    
    function getInput(prompt)
        io.write(prompt)
        io.flush()
        local input = io.read("l") 
        if not input then  --- test for EOF
            print("GOODBYE")
            os.exit(0)
        end
        return input
    end
    
    local needsRules = getInput("WOULD YOU LIKE THE RULES? (YES OR NO) ")
    print()
    if needsRules:match("[yY].*") then
        print(string.format( [[
    I AM THINKING OF A %u DIGIT NUMBER.  TRY TO GUESS
    MY NUMBER AND I WILL GIVE YOU CLUES AS FOLLOWS:
       PICO   - ONE DIGIT CORRECT BUT IN THE WRONG POSITION
       FERMI  - ONE DIGIT CORRECT AND IN THE RIGHT POSITION
       BAGELS - NO DIGITS
       ]], difficulty))
    end
    
    function play(numDigits, score)
        local digits = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "0" }
        --- secret number must not have duplicate digits
        --- randomly swap numDigits at the head of this list to create secret number
        for i = 1, numDigits do
            local j = math.random(1, 10)
            digits[i], digits[j] = digits[j], digits[i]
        end
    
        print "O.K.  I HAVE A NUMBER IN MIND."
        for guessNum = 1, 20 do
            :: GUESS_AGAIN :: --- 0) then
                print("A", score, "POINT BAGELS BUFF!!")
            end
            print "HOPE YOU HAD FUN. BYE."
        end
    end
    
    play(difficulty, 0) --- default is numDigits=3, score=0
    
    
    
    ================================================
    FILE: 05_Bagels/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 05_Bagels/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 05_Bagels/perl/bagels.pl
    ================================================
    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    # global variable declaration (just the user's score)
    my($Y) = 0;
    
    
    # yes_input is a subroutine that returns a true value
    # if the first character of the user's input from STDIN
    # is a 'Y' (checking case-insensitively via regex)
    sub yes_input {
        chomp(my $A = );
        return $A =~ m/^Y/i;
    }
    
    # Main code starts here.
    
    print ' 'x32; print "Bagels\n";
    print ' 'x14; print "Creative Computing  Morristown, New Jersey\n\n";
    
    # Provide instructions if requested
    print "Would you like the rules (yes or no)? ";
    if (yes_input()) {
    
        # Print out the instructions using a here doc
        # (useful for large blocks of text)
        print <);
    
                # Use a regex to check if the user entered three digits,
                # and complain if they did not.
                if ($A !~ m{^(\d)(\d)(\d)$}) {
                    print "What?\n";
                    # Program execution will now pass through the rest
                    # of the logic below and loop back to the start
                    # of the CHECK loop.
                } else {
    
                    # As a side effect of the regex match above, the
                    # $1, $2, and $3 variables are each of the digits
                    # of the user's guess. Perl treats numbers and
                    # strings interchangably, so we will not have to
                    # use the ASC() conversion functions required
                    # by the BASIC program.
                    my @B = ($1,$2,$3);
    
                    # Check for duplicate digits in the user's guess
                    if ($B[0] == $B[1] || $B[0] == $B[2] || $B[1] == $B[2]) {
                        print "Oh, I forgot to tell you that the number I have in mind\n";
                        print "has no two digits the same.\n";
                        # Again, no further action is required here
                        # because we want to loop back to the start
                        # of the CHECK loop.
                    } else {
    
                        # This code block is the actual game logic, so
                        # it's executed only if the user's input has
                        # passed all the above checks.
                        my($C,$D);
                        $C = 0; $D = 0;
    
                        # As a replacement for the original BASIC logic,
                        # this for loop works over an anonymous array of
                        # pairs of digits to compare the computer's and
                        # the user's digits to see how many similar ones
                        # there are. Keep in mind that Perl arrays are
                        # zero-indexed, so we're comparing items numbered
                        # 0, 1, and 2, instead of 1, 2, and 3 in BASIC.
    
                        for my $PAIR ( [0,1], [1,0], [1,2], [2,1], [0,2], [2,0] ) {
                            if ($A[$PAIR->[0]] == $B[$PAIR->[1]]) {
                                ++$C;
                            }
                        }
    
                        # Check for digits that are correctly guessed
                        for my $i (0..2) {
                            if ($A[$i] == $B[$i]) {
                                ++$D;
                            }
                        }
    
                        # If the user guessed all 3 digits they get
                        # a point, and the 'PLAY' loop is restarted
                        # (see the 'continue' loop below)
                        if ($D == 3) {
                            print "You got it!!!\n\n";
                            ++$Y;
                            next PLAY;
                        }
    
                        # Print out the clues. The 'x' operator
                        # prints out the string the indicated number
                        # of times. The "BAGELS" line uses Perl's
                        # ternary operator to print the word if
                        # the expression ($C + $D) is equal to 0.
    
                        printf("%s%s%s\n",
                            "PICO " x$C,
                            "FERMI "x$D,
                            ($C+$D==0 ? "BAGELS" : '')
                        );
    
                        # Program execution leaves the CHECK loop and
                        # goes to the next iteration of the $i loop.
                        last CHECK;
    
                    } # end of game logic else block
                } # end of regex match else block
    
                # If program execution reaches this particular point,
                # then the user's input has not been accepted (the
                # only ways out of this loop are the "next PLAY" statement
                # when the user wins, and the "last CHECK" statement
                # when the user's input is successfully parsed).
                # So the program execution goes back to the top of the
                # CHECK loop, printing the request for user input
                # again.
    
            } # end of CHECK loop
    
            # This location is reached by the "last CHECK" statement,
            # and it's another execution of the $i loop.
    
        } # end of $i loop
    
        # If program execution reaches here, the user has guessed 20
        # times and not won.
    
        print "Oh well.\n";
        printf("That's twenty guesses. My number was %s\n", join('',@A));
    
    } # end of the PLAY loop
    
    # This 'continue' block is executed before the conditional part of the
    # PLAY loop is evaluated, so we can ask if the user wants another game
    # (i.e., if we should restart the PLAY loop).
    
    continue {
    
        # This 'continue' loop is reached either when the PLAY loop has completed
        # or via the 'next PLAY' statement when the user wins a game. In either
        # case we ask if the player wants to go again, and use the 'last'
        # statement to exit the loop if the response is not yes.
        print "Play again (yes or no) ? ";
        last unless yes_input();
    }
    
    # And as in the original BASIC program, print out
    # the user's score only if it is > 0.
    printf("A %d point bagels buff!\n", $Y) if $Y > 0;
    print "Hope you had fun.  Bye.\n";
    
    
    ================================================
    FILE: 05_Bagels/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 05_Bagels/python/bagels.py
    ================================================
    """
    Bagels
    
    From: BASIC Computer Games (1978)
          Edited by David H. Ahl
    
    "In this game, the computer picks a 3-digit secret number using
     the digits 0 to 9 and you attempt to guess what it is.  You are
     allowed up to twenty guesses.  No digit is repeated.  After
     each guess the computer will give you clues about your guess
     as follows:
    
     PICO     One digit is correct, but in the wrong place
     FERMI    One digit is in the correct place
     BAGELS   No digit is correct
    
    "You will learn to draw inferences from the clues and, with
     practice, you'll learn to improve your score.  There are several
     good strategies for playing Bagels.  After you have found a good
     strategy, see if you can improve it.  Or try a different strategy
     altogether and see if it is any better.  While the program allows
     up to twenty guesses, if you use a good strategy it should not
     take more than eight guesses to get any number.
    
    "The original authors of this program are D. Resek and P. Rowe of
     the Lawrence Hall of Science, Berkeley, California."
    
    
    Python port by Jeff Jetton, 2019
    """
    
    
    import random
    from typing import List
    
    MAX_GUESSES = 20
    
    
    def print_rules() -> None:
        print("\nI 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")
    
    
    def pick_number() -> List[str]:
        # Note that this returns a list of individual digits
        # as separate strings, not a single integer or string
        numbers = list(range(10))
        random.shuffle(numbers)
        num = numbers[:3]
        return [str(i) for i in num]
    
    
    def get_valid_guess(guesses: int) -> str:
        valid = False
        while not valid:
            guess = input(f"Guess # {guesses}     ? ")
            guess = guess.strip()
            # Guess must be three characters
            if len(guess) == 3:
                # And they should be numbers
                if guess.isnumeric():
                    # And the numbers must be unique
                    if len(set(guess)) == 3:
                        valid = True
                    else:
                        print("Oh, I forgot to tell you that the number I have in mind")
                        print("has no two digits the same.")
                else:
                    print("What?")
            else:
                print("Try guessing a three-digit number.")
    
        return guess
    
    
    def build_result_string(num: List[str], guess: str) -> str:
        result = ""
    
        # Correct digits in wrong place
        for i in range(2):
            if num[i] == guess[i + 1]:
                result += "PICO "
            if num[i + 1] == guess[i]:
                result += "PICO "
        if num[0] == guess[2]:
            result += "PICO "
        if num[2] == guess[0]:
            result += "PICO "
    
        # Correct digits in right place
        for i in range(3):
            if num[i] == guess[i]:
                result += "FERMI "
    
        # Nothing right?
        if result == "":
            result = "BAGELS"
    
        return result
    
    
    def main() -> None:
        # Intro text
        print("\n                Bagels")
        print("Creative Computing  Morristown, New Jersey\n\n")
    
        # Anything other than N* will show the rules
        response = input("Would you like the rules (Yes or No)? ")
        if len(response) > 0:
            if response.upper()[0] != "N":
                print_rules()
        else:
            print_rules()
    
        games_won = 0
        still_running = True
        while still_running:
    
            # New round
            num = pick_number()
            num_str = "".join(num)
            guesses = 1
    
            print("\nO.K.  I have a number in mind.")
            guessing = True
            while guessing:
    
                guess = get_valid_guess(guesses)
    
                if guess == num_str:
                    print("You got it!!!\n")
                    games_won += 1
                    guessing = False
                else:
                    print(build_result_string(num, guess))
                    guesses += 1
                    if guesses > MAX_GUESSES:
                        print("Oh well")
                        print(f"That's {MAX_GUESSES} guesses.  My number was {num_str}")
                        guessing = False
    
            valid_response = False
            while not valid_response:
                response = input("Play again (Yes or No)? ")
                if len(response) > 0:
                    valid_response = True
                    if response.upper()[0] != "Y":
                        still_running = False
    
        if games_won > 0:
            print(f"\nA {games_won} point Bagels buff!!")
    
        print("Hope you had fun.  Bye.\n")
    
    
    if __name__ == "__main__":
        main()
    
    ######################################################################
    #
    # Porting Notes
    #
    #   The original program did an unusually good job of validating the
    #   player's input (compared to many of the other programs in the
    #   book). Those checks and responses have been exactly reproduced.
    #
    #
    # Ideas for Modifications
    #
    #   It should probably mention that there's a maximum of MAX_NUM
    #   guesses in the instructions somewhere, shouldn't it?
    #
    #   Could this program be written to use anywhere from, say 2 to 6
    #   digits in the number to guess? How would this change the routine
    #   that creates the "result" string?
    #
    ######################################################################
    
    
    ================================================
    FILE: 05_Bagels/ruby/README.md
    ================================================
    ## BAGELS
    
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/) by [Tom Armitage](https://github.com/infovore)
    
    ## Translator's notes:
    
    This is a highly imperative port. As such, it's very much, in the spirit of David Ahl's original version, and also highly un-Rubyish.
    
    A few decisions I made:
    
    * the main loop is a 'while' loop. Most games are a main loop that runs until it doesn't, and I felt that "while the player wished to keep playing, the game should run" was an appropriate structure.
    * lots of puts and gets; that feels appropriate to the Ahl implementation. No clever cli or curses libraries here.
    * the number in question, and the player's answer, are stored as numbers. They're only converted into arrays for the purpose of `puts_clue_for` - ie, when comparison is need. The original game stored them as arrays, which made sense, but given the computer says "I have a number in mind", I decided to store what was in its 'mind' as a number.
    * the `String#center` method from Ruby 2.5~ sure is handy.
    
    
    ================================================
    FILE: 05_Bagels/ruby/bagels.rb
    ================================================
    # Bagels
    # Number guessing game.
    # Original source unknown but suspected to be
    # Lawrence Hall of Science, U.C. Berkeley
    
    def print_instructions
      puts
      puts 'I am thinking of a three-digit number. Try to guess'
      puts 'my number and i will give you clues as follows:'
      puts '   PICO   - one digit correct but in the wrong position'
      puts '   FERMI  - one digit correct and in the right position'
      puts '   BAGELS - no digits correct'
    end
    
    def generate_target
      # pick a three digit number with no repeating numbers.
      # gien that, 102-987 is a reasonable starting point!
      target = rand(102..987) while target.to_s.split('').uniq.size < 3
      target
    end
    
    def puts_clue_for(guess_number, target_number)
      guess_array = guess_number.to_s.split('')
      target_array = target_number.to_s.split('')
    
      clues = [].tap do |clue|
        guess_array.each_with_index do |n, i|
          if target_array[i] == n
            clue << 'FERMI'
          elsif target_array.include?(n)
            clue << 'PICO'
          end
        end
      end
    
      # sort clues so that FERMIs come before PICOs, but
      # you don't know which response responds to which number
      if clues.length > 0
        puts clues.sort.join
      else
        puts 'BAGELS'
      end
    end
    
    player_points = 0
    desire_to_play = true
    
    puts 'Bagels'.center(72)
    puts 'Creative Computing  Morristown, New Jersey'.center(72)
    5.times { puts }
    
    puts 'Would you like to the rules? [Y]es or [N]o.'
    instructions_request = gets.chomp.downcase
    
    print_instructions if %w[yes y].include?(instructions_request)
    
    while desire_to_play
      target = generate_target
    
      2.times { puts }
      puts 'OK. I have a number in mind.'
    
      guess_count = 0
      guess = 0
    
      while (guess != target) && guess_count < 20
        guess_count += 1
    
        puts "Guess ##{guess_count}:"
    
        raw_guess = gets.chomp
    
        ## if the guess isn't numerical, sound confused
        if raw_guess =~ /[^0-9]/
          puts 'What?'
          next
        end
    
        ## if the guess is more ore less than three digits, prompt player
        if raw_guess.length != 3
          puts 'Try guessing a three digit number'
          next
        end
    
        ## if the guess contains duplicate numbers, give player a clue
        if raw_guess.split('').uniq.size < 3
          puts 'Oh, I forgot to tell you: the number I have in mind has no two digits the same.'
          next
        end
    
        guess = raw_guess.to_i
    
        if guess != target
          puts_clue_for(guess, target)
          puts
        end
      end
    
      if guess == target
        player_points += 1
    
        puts 'You got it!!!'
        puts
      else
        puts 'Oh well.'
        puts "That's twenty guesses. My number was #{target_number_array.join('')}."
      end
    
      puts
      puts 'Would you like to play again? [Y]es or [N]o'
    
      play_again_request = gets.chomp
      desire_to_play = %w[yes y].include?(play_again_request)
    end
    
    puts "A #{player_points} point bagels buff!!"
    puts 'Hope you had fun. Bye.'
    
    
    ================================================
    FILE: 05_Bagels/rust/Cargo.toml
    ================================================
    [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"
    
    
    ================================================
    FILE: 05_Bagels/rust/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Rust](https://www.rust-lang.org/) by Till Klister [tikste@github](https://github.com/tikste).
    
    
    ================================================
    FILE: 05_Bagels/rust/src/main.rs
    ================================================
    use std::io;
    use std::io::{stdin, stdout, Write};
    
    use rand::prelude::SliceRandom;
    
    type Digits = [u8; 3];
    
    fn main() -> io::Result<()> {
        print_header();
        if !read_lowercase_input()?.starts_with('n') {
            print_rules();
        }
        let mut win_count = 0;
        loop {
            let solution = generate_random_digits();
            println!("\nO.K. I have a number in mind.");
            if guess(solution)? {
                win_count += 1;
                println!("You got it!!!");
            } else {
                println!(
                    "\nOh well\nThat's twenty guesses: My number was {}{}{}",
                    solution[0], solution[1], solution[2]
                );
            }
            println!("\nPlay again (yes or no)?");
            if read_lowercase_input()? != "yes" {
                println!();
                if win_count > 0 {
                    println!("A {win_count} point bagels buff!!");
                }
                println!("Hope you had fun. Bye.");
                return Ok(());
            }
        }
    }
    
    fn print_header() {
        println!("                  Bagels");
        println!("Creative-Computing  Morristown, New Jersey");
        println!();
        println!("Would you like the rules (yes or no)?");
    }
    
    fn print_rules() {
        println!();
        println!("I am thinking of a three-digit number. Try to guess");
        println!("my number and I will give you clues as follows:");
        println!("   Pico   - one digit correct but in the wrong position");
        println!("   Fermi  - one digit correct and in the right position");
        println!("   Bagles - no digits correct");
    }
    
    fn read_lowercase_input() -> io::Result {
        let mut input = String::new();
        stdin().read_line(&mut input)?;
        Ok(input.trim().to_lowercase())
    }
    
    fn generate_random_digits() -> Digits {
        let range = 0..10;
        let mut numbers = range.into_iter().collect::>();
        numbers.shuffle(&mut rand::thread_rng());
        let mut digits = Digits::default();
        let len = digits.len();
        digits.copy_from_slice(&numbers[..len]);
        digits
    }
    
    fn guess(solution: Digits) -> io::Result {
        for round in 1..21 {
            let guess = read_valid_guess(round)?;
            if guess == solution {
                return Ok(true);
            } else {
                let mut pico = 0;
                let mut fermi = 0;
                for i in 0..guess.len() {
                    if guess[i] == solution[i] {
                        fermi += 1;
                    } else if solution.contains(&guess[i]) {
                        pico += 1;
                    }
                }
                let mut status = String::new();
                if pico == 0 && fermi == 0 {
                    status += "Bagels";
                } else {
                    for _ in 0..pico {
                        status += "Pico ";
                    }
                    for _ in 0..fermi {
                        status += "Fermi ";
                    }
                };
                println!("{}", status.trim_end());
            }
        }
        Ok(false)
    }
    
    fn read_valid_guess(round: u8) -> io::Result {
        let mut guess = Digits::default();
        loop {
            let space = " ".repeat(if round < 10 { 5 } else { 4 });
            print!("Guess #{round}{space}? ");
            stdout().flush()?;
            let input = read_lowercase_input()?;
            if input.len() == guess.len() {
                let mut i = 0;
                for c in input.chars() {
                    if let Ok(digit) = c.to_string().parse::() {
                        if guess[..i].contains(&digit) {
                            println!("\nOh, I forgot to tell you that the number I have in mind\nhas no two digits the same.");
                            break;
                        }
                        guess[i] = digit;
                        i += 1;
                        if i == guess.len() {
                            return Ok(guess);
                        }
                    } else {
                        println!("What?");
                        break;
                    }
                }
            } else {
                println!("Try guessing a three-digit number.");
            }
        }
    }
    
    
    ================================================
    FILE: 05_Bagels/vbnet/Bagels.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Bagels", "Bagels.vbproj", "{913FE7A8-B481-4EB3-8938-566BEAD5B0F2}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{913FE7A8-B481-4EB3-8938-566BEAD5B0F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{913FE7A8-B481-4EB3-8938-566BEAD5B0F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{913FE7A8-B481-4EB3-8938-566BEAD5B0F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{913FE7A8-B481-4EB3-8938-566BEAD5B0F2}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 05_Bagels/vbnet/Bagels.vbproj
    ================================================
    
      
        Exe
        Bagels
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 05_Bagels/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 06_Banner/README.md
    ================================================
    ### Banner
    
    This program creates a large banner on a terminal of any message you input. The letters may be any dimension of you wish although the letter height plus distance from left-hand side should not exceed 6 inches. Experiment with the height and width until you get a pleasing effect on whatever terminal you are using.
    
    This program was written by Leonard Rosendust of Brooklyn, New York.
    
    ---
    
    As published in Basic Computer Games (1978)
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=10)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=25)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### 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)
    
    
    
    ================================================
    FILE: 06_Banner/banner.bas
    ================================================
    10 INPUT "HORIZONTAL";X
    20 INPUT "VERTICAL";Y
    21 INPUT "CENTERED";L$
    22 G1=0:IF L$>"P" THEN G1=1
    23 INPUT "CHARACTER (TYPE 'ALL' IF YOU WANT CHARACTER BEING PRINTED)";M$
    29 PRINT "STATEMENT";
    30 INPUT A$
    35 INPUT "SET PAGE";O$
    40 A=ASC(LEFT$(A$,1))
    50 REM
    60 REM
    70 FOR T=1 TO LEN(A$)
    80 P$=MID$(A$,T,1)
    90 FOR O=1 TO 50
    95 READ S$,S(1),S(2),S(3),S(4),S(5),S(6),S(7)
    96 IF P$=" " THEN 812
    100 IF P$=S$ THEN 200
    120 NEXT O
    200 RESTORE
    201 X$=M$
    202 IF M$="ALL" THEN X$=S$
    205 FOR U=1 TO 7
    210 FOR K=8 TO 0 STEP -1
    230 IF 2^K letters = new Dictionary()
            {
                {' ', new int[] { 0, 0, 0, 0, 0, 0, 0 } },
                {'A', new int[] {505, 37, 35, 34, 35, 37, 505} },
                {'B', new int[] {512, 274, 274, 274, 274, 274, 239} },
                {'C', new int[] {125, 131, 258, 258, 258, 131, 69} },
                {'D', new int[] {512, 258, 258, 258, 258, 131, 125} },
                {'E', new int[] {512, 274, 274, 274, 274, 258, 258} },
                {'F', new int[] {512, 18, 18, 18, 18, 2, 2} },
                {'G', new int[] {125, 131, 258, 258, 290, 163, 101} },
                {'H', new int[] {512, 17, 17, 17, 17, 17, 512} },
                {'I', new int[] {258, 258, 258, 512, 258, 258, 258} },
                {'J', new int[] {65, 129, 257, 257, 257, 129, 128} },
                {'K', new int[] {512, 17, 17, 41, 69, 131, 258} },
                {'L', new int[] {512, 257, 257, 257, 257, 257, 257} },
                {'M', new int[] {512, 7, 13, 25, 13, 7, 512} },
                {'N', new int[] {512, 7, 9, 17, 33, 193, 512} },
                {'O', new int[] {125, 131, 258, 258, 258, 131, 125} },
                {'P', new int[] {512, 18, 18, 18, 18, 18, 15} },
                {'Q', new int[] {125, 131, 258, 258, 322, 131, 381} },
                {'R', new int[] {512, 18, 18, 50, 82, 146, 271} },
                {'S', new int[] {69, 139, 274, 274, 274, 163, 69} },
                {'T', new int[] {2, 2, 2, 512, 2, 2, 2} },
                {'U', new int[] {128, 129, 257, 257, 257, 129, 128} },
                {'V', new int[] {64, 65, 129, 257, 129, 65, 64} },
                {'W', new int[] {256, 257, 129, 65, 129, 257, 256} },
                {'X', new int[] {388, 69, 41, 17, 41, 69, 388} },
                {'Y', new int[] {8, 9, 17, 481, 17, 9, 8} },
                {'Z', new int[] {386, 322, 290, 274, 266, 262, 260} },
                {'0', new int[] {57, 69, 131, 258, 131, 69, 57} },
                {'1', new int[] {0, 0, 261, 259, 512, 257, 257} },
                {'2', new int[] {261, 387, 322, 290, 274, 267, 261} },
                {'3', new int[] {66, 130, 258, 274, 266, 150, 100} },
                {'4', new int[] {33, 49, 41, 37, 35, 512, 33} },
                {'5', new int[] {160, 274, 274, 274, 274, 274, 226} },
                {'6', new int[] {194, 291, 293, 297, 305, 289, 193} },
                {'7', new int[] {258, 130, 66, 34, 18, 10, 8} },
                {'8', new int[] {69, 171, 274, 274, 274, 171, 69} },
                {'9', new int[] {263, 138, 74, 42, 26, 10, 7} },
                {'?', new int[] {5, 3, 2, 354, 18, 11, 5} },
                {'*', new int[] {69, 41, 17, 512, 17, 41, 69} },
                {'=', new int[] {41, 41, 41, 41, 41, 41, 41} },
                {'!', new int[] {1, 1, 1, 384, 1, 1, 1} },
                {'.', new int[] {1, 1, 129, 449, 129, 1, 1} }
            };
    
    
            /// 
            /// This displays the provided text on the screen and then waits for the user
            /// to enter a integer value greater than 0.
            /// 
            /// Text to display on the screen asking for the input
            /// The integer value entered by the user
            private int GetNumber(string DisplayText)
            {
                Console.Write(DisplayText);
                string TempStr = Console.ReadLine();
    
                Int32.TryParse(TempStr, out int TempInt);
    
                if (TempInt <= 0)
                {
                    throw new ArgumentException($"{DisplayText} must be greater than zero");
                }
    
                return TempInt;
            }
    
            /// 
            /// This displays the provided text on the screen and then waits for the user
            /// to enter a Y or N.  It cheats by just looking for a 'y' and returning that
            /// as true.  Anything else that the user enters is returned as false.
            /// 
            /// Text to display on the screen asking for the input
            /// Returns true or false
            private bool GetBool(string DisplayText)
            {
                Console.Write(DisplayText);
                return (Console.ReadLine().StartsWith("y", StringComparison.InvariantCultureIgnoreCase));
            }
    
            /// 
            /// This displays the provided text on the screen and then waits for the user
            /// to enter an arbitrary string.  That string is then returned 'as-is'.
            /// 
            /// Text to display on the screen asking for the input
            /// The string entered by the user.
            private string GetString(string DisplayText)
            {
                Console.Write(DisplayText);
                return (Console.ReadLine().ToUpper());
            }
    
            /// 
            /// This queries the user for the various inputs needed by the program.
            /// 
            private void GetInput()
            {
                Horizontal = GetNumber("Horizontal ");
                Vertical = GetNumber("Vertical ");
                Centered = GetBool("Centered ");
                Character = GetString("Character (type 'ALL' if you want character being printed) ");
                Statement = GetString("Statement ");
                // We don't care about what the user enters here.  This is just telling them
                // to set the page in the printer.
                _ = GetString("Set page ");
            }
    
            /// 
            /// This prints out a single character of the banner - adding
            /// a few blanks lines as a spacer between characters.
            /// 
            private void PrintChar(char ch)
            {
                // In the trivial case (a space character), just print out the spaces
                if (ch.Equals(' '))
                {
                    Console.WriteLine(new string('\n', 7 * Horizontal));
                    return;
                }
    
                // If a specific character to be printed was provided by the user,
                // then user that as our ouput character - otherwise take the
                // current character
                char outCh = Character == "ALL" ? ch : Character[0];
                int[] letter = new int[7];
                try
                {
                    letters[outCh].CopyTo(letter, 0);
                }
                catch (KeyNotFoundException)
                {
                    throw new KeyNotFoundException($"The provided letter {outCh} was not found in the letters list");
                }
    
                // This iterates through each of the parts that make up
                // each letter.  Each part represents 1 * Horizontal lines
                // of actual output.
                for (int idx = 0; idx < 7; idx++)
                {
                    // New int array declarations default to zeros
                    // numSections decides how many 'sections' need to be printed
                    // for a given line of each character
                    int[] numSections = new int[7];
                    // fillInSection decides whether each 'section' of the
                    // character gets filled in with the character or with blanks
                    int[] fillInSection = new int[9];
    
                    // This uses the value in each part to decide which
                    // sections are empty spaces in the letter or filled in
                    // spaces.  For each section marked with 1 in fillInSection,
                    // that will correspond to 1 * Vertical characters actually
                    // being output.
                    for (int exp = 8; exp >= 0; exp--)
                    {
                        if (Math.Pow(2, exp) < letter[idx])
                        {
                            fillInSection[8 - exp] = 1;
                            letter[idx] -= (int)Math.Pow(2, exp);
                            if (letter[idx] == 1)
                            {
                                // Once we've exhausted all of the sections
                                // defined in this part of the letter, then
                                // we marked that number and break out of this
                                // for loop.
                                numSections[idx] = 8 - exp;
                                break;
                            }
                        }
                    }
    
                    // Now that we know which sections of this part of the letter
                    // are filled in or spaces, we can actually create the string
                    // to print out.
                    string lineStr = "";
    
                    if (Centered)
                        lineStr += new string(' ', (int)(63 - 4.5 * Vertical) * 1 / 1 + 1);
    
                    for (int idx2 = 0; idx2 <= numSections[idx]; idx2++)
                    {
                        lineStr = lineStr + new string(fillInSection[idx2] == 0 ? ' ' : outCh, Vertical);
                    }
    
                    // Then we print that string out 1 * Horizontal number of times
                    for (int lineidx = 1; lineidx <= Horizontal; lineidx++)
                    {
                        Console.WriteLine(lineStr);
                    }
                }
    
                // Finally, add a little spacer after each character for readability.
                Console.WriteLine(new string('\n', 2 * Horizontal - 1));
            }
    
            /// 
            /// This prints the entire banner based in the parameters
            /// the user provided.
            /// 
            private void PrintBanner()
            {
                // Iterate through each character in the statement
                foreach (char ch in Statement)
                {
                    PrintChar(ch);
                }
    
                // In the original version, it would print an additional 75 blank
                // lines in order to feed the printer paper...don't really need this
                // since we're not actually printing.
                // Console.WriteLine(new string('\n', 75));
            }
    
            /// 
            /// Main entry point into the banner class and handles the main loop.
            /// 
            public void Play()
            {
                GetInput();
                PrintBanner();
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                new Banner().Play();
            }
        }
    }
    
    
    ================================================
    FILE: 06_Banner/csharp/banner.csproj
    ================================================
    
    
      
        Exe
        netcoreapp3.1
      
    
    
    
    
    ================================================
    FILE: 06_Banner/csharp/banner.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.31321.278
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Banner", "Banner.csproj", "{7E8612AB-AFFD-4F72-855F-8172786F28FD}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{7E8612AB-AFFD-4F72-855F-8172786F28FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{7E8612AB-AFFD-4F72-855F-8172786F28FD}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{7E8612AB-AFFD-4F72-855F-8172786F28FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{7E8612AB-AFFD-4F72-855F-8172786F28FD}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {FF2A268C-9C4A-457F-8733-2E8931E07A1D}
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 06_Banner/java/Banner.java
    ================================================
    import java.util.Scanner;
    import java.util.HashMap;
    import java.util.Map;
    import java.lang.Math;
    
    /**
     * Game of Banner
     * 

    * Based on the BASIC game of Banner here * https://github.com/coding-horror/basic-computer-games/blob/main/06%20Banner/banner.bas *

    * Note: The idea was to create a version of the 1970's BASIC game in Java, without introducing * new features - no additional text, error checking, etc has been added. * * Converted from BASIC to Java by Darren Cardenas. */ public class Banner { private final Scanner scan; // For user input public Banner() { scan = new Scanner(System.in); } // End of constructor Banner public void play() { int bitIndex = 0; int centerFlag = 0; int dataIndex = 0; int hIndex = 0; int horizontal = 0; int index = 0; int letterIndex = 0; int tempVal = 0; int vertical = 0; int vIndex = 0; int writeIndex = 0; int[] writerMap = new int[10]; int[] writeLimit = new int[10]; String centerResponse = ""; String characters = ""; String letter = ""; String lineContent = ""; String setPage = ""; String statement = ""; String token = ""; // Print token Map symbolData = new HashMap(); symbolData.put(" ", new int[]{0,0,0,0,0,0,0,0 }); symbolData.put("A", new int[]{0,505,37,35,34,35,37,505 }); symbolData.put("G", new int[]{0,125,131,258,258,290,163,101}); symbolData.put("E", new int[]{0,512,274,274,274,274,258,258}); symbolData.put("T", new int[]{0,2,2,2,512,2,2,2 }); symbolData.put("W", new int[]{0,256,257,129,65,129,257,256 }); symbolData.put("L", new int[]{0,512,257,257,257,257,257,257}); symbolData.put("S", new int[]{0,69,139,274,274,274,163,69 }); symbolData.put("O", new int[]{0,125,131,258,258,258,131,125}); symbolData.put("N", new int[]{0,512,7,9,17,33,193,512 }); symbolData.put("F", new int[]{0,512,18,18,18,18,2,2 }); symbolData.put("K", new int[]{0,512,17,17,41,69,131,258 }); symbolData.put("B", new int[]{0,512,274,274,274,274,274,239}); symbolData.put("D", new int[]{0,512,258,258,258,258,131,125}); symbolData.put("H", new int[]{0,512,17,17,17,17,17,512 }); symbolData.put("M", new int[]{0,512,7,13,25,13,7,512 }); symbolData.put("?", new int[]{0,5,3,2,354,18,11,5 }); symbolData.put("U", new int[]{0,128,129,257,257,257,129,128}); symbolData.put("R", new int[]{0,512,18,18,50,82,146,271 }); symbolData.put("P", new int[]{0,512,18,18,18,18,18,15 }); symbolData.put("Q", new int[]{0,125,131,258,258,322,131,381}); symbolData.put("Y", new int[]{0,8,9,17,481,17,9,8 }); symbolData.put("V", new int[]{0,64,65,129,257,129,65,64 }); symbolData.put("X", new int[]{0,388,69,41,17,41,69,388 }); symbolData.put("Z", new int[]{0,386,322,290,274,266,262,260}); symbolData.put("I", new int[]{0,258,258,258,512,258,258,258}); symbolData.put("C", new int[]{0,125,131,258,258,258,131,69 }); symbolData.put("J", new int[]{0,65,129,257,257,257,129,128 }); symbolData.put("1", new int[]{0,0,0,261,259,512,257,257 }); symbolData.put("2", new int[]{0,261,387,322,290,274,267,261}); symbolData.put("*", new int[]{0,69,41,17,512,17,41,69 }); symbolData.put("3", new int[]{0,66,130,258,274,266,150,100 }); symbolData.put("4", new int[]{0,33,49,41,37,35,512,33 }); symbolData.put("5", new int[]{0,160,274,274,274,274,274,226}); symbolData.put("6", new int[]{0,194,291,293,297,305,289,193}); symbolData.put("7", new int[]{0,258,130,66,34,18,10,8 }); symbolData.put("8", new int[]{0,69,171,274,274,274,171,69 }); symbolData.put("9", new int[]{0,263,138,74,42,26,10,7 }); symbolData.put("=", new int[]{0,41,41,41,41,41,41,41 }); symbolData.put("!", new int[]{0,1,1,1,384,1,1,1 }); symbolData.put("0", new int[]{0,57,69,131,258,131,69,57 }); symbolData.put(".", new int[]{0,1,1,129,449,129,1,1 }); System.out.print("HORIZONTAL? "); horizontal = Integer.parseInt(scan.nextLine()); System.out.print("VERTICAL? "); vertical = Integer.parseInt(scan.nextLine()); System.out.print("CENTERED? "); centerResponse = scan.nextLine().toUpperCase(); centerFlag = 0; // Lexicographical comparison if (centerResponse.compareTo("P") > 0) { centerFlag = 1; } System.out.print("CHARACTER (TYPE 'ALL' IF YOU WANT CHARACTER BEING PRINTED)? "); characters = scan.nextLine().toUpperCase(); System.out.print("STATEMENT? "); statement = scan.nextLine().toUpperCase(); // Initiates the print System.out.print("SET PAGE? "); setPage = scan.nextLine(); // Begin loop through statement letters for (letterIndex = 1; letterIndex <= statement.length(); letterIndex++) { // Extract a letter letter = String.valueOf(statement.charAt(letterIndex - 1)); // Begin loop through all symbol data for (String symbolString: symbolData.keySet()) { // Begin letter handling if (letter.equals(" ")) { for (index = 1; index <= (7 * horizontal); index++) { System.out.println(""); } break; } else if (letter.equals(symbolString)) { token = characters; if (characters.equals("ALL")) { token = symbolString; } for (dataIndex = 1; dataIndex <= 7; dataIndex++) { // Avoid overwriting symbol data tempVal = symbolData.get(symbolString)[dataIndex]; for (bitIndex = 8; bitIndex >= 0; bitIndex--) { if (Math.pow(2, bitIndex) < tempVal) { writerMap[9 - bitIndex] = 1; tempVal -= Math.pow(2, bitIndex); if (tempVal == 1) { writeLimit[dataIndex] = 9 - bitIndex; break; } } else { writerMap[9 - bitIndex] = 0; } } // End of bitIndex loop for (hIndex = 1; hIndex <= horizontal; hIndex++) { // Add whitespace for centering lineContent = " ".repeat((int)((63 - 4.5 * vertical) * centerFlag / token.length())); for (writeIndex = 1; writeIndex <= writeLimit[dataIndex]; writeIndex++) { if (writerMap[writeIndex] == 0) { for (vIndex = 1; vIndex <= vertical; vIndex++) { for (index = 1; index <= token.length(); index++) { lineContent += " "; } } } else { for (vIndex = 1; vIndex <= vertical; vIndex++) { lineContent += token; } } } // End of writeIndex loop System.out.println(lineContent); } // End of hIndex loop } // End of dataIndex loop // Add padding between letters for (index = 1; index <= 2 * horizontal; index++) { System.out.println(""); } } // End letter handling } // End loop through all symbol data } // End loop through statement letters // Add extra length to the banner for (index = 1; index <= 75; index++) { System.out.println(""); } } // End of method play public static void main(String[] args) { Banner game = new Banner(); game.play(); } // End of method main } // End of class Banner ================================================ FILE: 06_Banner/java/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Oracle Java](https://openjdk.java.net/) ================================================ FILE: 06_Banner/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 06_Banner/javascript/banner.html ================================================ BANNER

    
    
    
    
    
    
    ================================================
    FILE: 06_Banner/javascript/banner.js
    ================================================
    // BANNER
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    var letters = [" ",0,0,0,0,0,0,0,
                   "A",505,37,35,34,35,37,505,
                   "G",125,131,258,258,290,163,101,
                   "E",512,274,274,274,274,258,258,
                   "T",2,2,2,512,2,2,2,
                   "W",256,257,129,65,129,257,256,
                   "L",512,257,257,257,257,257,257,
                   "S",69,139,274,274,274,163,69,
                   "O",125,131,258,258,258,131,125,
                   "N",512,7,9,17,33,193,512,
                   "F",512,18,18,18,18,2,2,
                   "K",512,17,17,41,69,131,258,
                   "B",512,274,274,274,274,274,239,
                   "D",512,258,258,258,258,131,125,
                   "H",512,17,17,17,17,17,512,
                   "M",512,7,13,25,13,7,512,
                   "?",5,3,2,354,18,11,5,
                   "U",128,129,257,257,257,129,128,
                   "R",512,18,18,50,82,146,271,
                   "P",512,18,18,18,18,18,15,
                   "Q",125,131,258,258,322,131,381,
                   "Y",8,9,17,481,17,9,8,
                   "V",64,65,129,257,129,65,64,
                   "X",388,69,41,17,41,69,388,
                   "Z",386,322,290,274,266,262,260,
                   "I",258,258,258,512,258,258,258,
                   "C",125,131,258,258,258,131,69,
                   "J",65,129,257,257,257,129,128,
                   "1",0,0,261,259,512,257,257,
                   "2",261,387,322,290,274,267,261,
                   "*",69,41,17,512,17,41,69,
                   "3",66,130,258,274,266,150,100,
                   "4",33,49,41,37,35,512,33,
                   "5",160,274,274,274,274,274,226,
                   "6",194,291,293,297,305,289,193,
                   "7",258,130,66,34,18,10,8,
                   "8",69,171,274,274,274,171,69,
                   "9",263,138,74,42,26,10,7,
                   "=",41,41,41,41,41,41,41,
                   "!",1,1,1,384,1,1,1,
                   "0",57,69,131,258,131,69,57,
                   ".",1,1,129,449,129,1,1];
    
    f = [];
    j = [];
    s = [];
    
    // Main program
    async function main()
    {
        print("HORIZONTAL");
        x = parseInt(await input());
        print("VERTICAL");
        y = parseInt(await input());
        print("CENTERED");
        ls = await input();
        g1 = 0;
        if (ls > "P")
            g1 = 1;
        print("CHARACTER (TYPE 'ALL' IF YOU WANT CHARACTER BEING PRINTED)");
        ms = await input();
        print("STATEMENT");
        as = await input();
        print("SET PAGE");	// This means to prepare printer, just press Enter
        os = await input();
    
        for (t = 0; t < as.length; t++) {
            ps = as.substr(t, 1);
            for (o = 0; o < 50 * 8; o += 8) {
                if (letters[o] == ps) {
                    for (u = 1; u <= 7; u++)
                        s[u] = letters[o + u];
                    break;
                }
            }
            if (o == 50 * 8) {
                ps = " ";
                o = 0;
            }
    //      print("Doing " + o + "\n");
            if (o == 0) {
                for (h = 1; h <= 7 * x; h++)
                    print("\n");
            } else {
                xs = ms;
                if (ms == "ALL")
                    xs = ps;
                for (u = 1; u <= 7; u++) {
                    // An inefficient way of extracting bits
                    // but good enough in BASIC because there
                    // aren't bit shifting operators.
                    for (k = 8; k >= 0; k--) {
                        if (Math.pow(2, k) >= s[u]) {
                            j[9 - k] = 0;
                        } else {
                            j[9 - k] = 1;
                            s[u] -= Math.pow(2, k);
                            if (s[u] == 1) {
                                f[u] = 9 - k;
                                break;
                            }
                        }
                    }
                    for (t1 = 1; t1 <= x; t1++) {
                        str = tab((63 - 4.5 * y) * g1 / xs.length + 1);
                        for (b = 1; b <= f[u]; b++) {
                            if (j[b] == 0) {
                                for (i = 1; i <= y; i++)
                                    str += tab(xs.length);
                            } else {
                                for (i = 1; i <= y; i++)
                                    str += xs;
                            }
                        }
                        print(str + "\n");
                    }
                }
                for (h = 1; h <= 2 * x; h++)
                    print("\n");
            }
        }
    }
    
    main();
    
    
    ================================================
    FILE: 06_Banner/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 06_Banner/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 06_Banner/pascal/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Pascal](https://en.wikipedia.org/wiki/Pascal_(programming_language))
    
    
    ================================================
    FILE: 06_Banner/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 06_Banner/perl/banner.pl
    ================================================
    #!/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);
    
    
    ================================================
    FILE: 06_Banner/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 06_Banner/python/banner.py
    ================================================
    #!/usr/bin/env python3
    
    """
    BANNER
    
    Converted from BASIC to Python by Trevor Hobson
    """
    
    letters = {
        " ": [0, 0, 0, 0, 0, 0, 0],
        "A": [505, 37, 35, 34, 35, 37, 505],
        "G": [125, 131, 258, 258, 290, 163, 101],
        "E": [512, 274, 274, 274, 274, 258, 258],
        "T": [2, 2, 2, 512, 2, 2, 2],
        "W": [256, 257, 129, 65, 129, 257, 256],
        "L": [512, 257, 257, 257, 257, 257, 257],
        "S": [69, 139, 274, 274, 274, 163, 69],
        "O": [125, 131, 258, 258, 258, 131, 125],
        "N": [512, 7, 9, 17, 33, 193, 512],
        "F": [512, 18, 18, 18, 18, 2, 2],
        "K": [512, 17, 17, 41, 69, 131, 258],
        "B": [512, 274, 274, 274, 274, 274, 239],
        "D": [512, 258, 258, 258, 258, 131, 125],
        "H": [512, 17, 17, 17, 17, 17, 512],
        "M": [512, 7, 13, 25, 13, 7, 512],
        "?": [5, 3, 2, 354, 18, 11, 5],
        "U": [128, 129, 257, 257, 257, 129, 128],
        "R": [512, 18, 18, 50, 82, 146, 271],
        "P": [512, 18, 18, 18, 18, 18, 15],
        "Q": [125, 131, 258, 258, 322, 131, 381],
        "Y": [8, 9, 17, 481, 17, 9, 8],
        "V": [64, 65, 129, 257, 129, 65, 64],
        "X": [388, 69, 41, 17, 41, 69, 388],
        "Z": [386, 322, 290, 274, 266, 262, 260],
        "I": [258, 258, 258, 512, 258, 258, 258],
        "C": [125, 131, 258, 258, 258, 131, 69],
        "J": [65, 129, 257, 257, 257, 129, 128],
        "1": [0, 0, 261, 259, 512, 257, 257],
        "2": [261, 387, 322, 290, 274, 267, 261],
        "*": [69, 41, 17, 512, 17, 41, 69],
        "3": [66, 130, 258, 274, 266, 150, 100],
        "4": [33, 49, 41, 37, 35, 512, 33],
        "5": [160, 274, 274, 274, 274, 274, 226],
        "6": [194, 291, 293, 297, 305, 289, 193],
        "7": [258, 130, 66, 34, 18, 10, 8],
        "8": [69, 171, 274, 274, 274, 171, 69],
        "9": [263, 138, 74, 42, 26, 10, 7],
        "=": [41, 41, 41, 41, 41, 41, 41],
        "!": [1, 1, 1, 384, 1, 1, 1],
        "0": [57, 69, 131, 258, 131, 69, 57],
        ".": [1, 1, 129, 449, 129, 1, 1],
    }
    
    
    def print_banner() -> None:
        f = [0] * 7
        j = [0] * 9
    
        while True:
            try:
                horizontal = int(input("Horizontal "))
                if horizontal < 1:
                    raise ValueError("Horizontal must be greater than zero")
                break
    
            except ValueError:
                print("Please enter a number greater than zero")
        while True:
            try:
                vertical = int(input("Vertical "))
                if vertical < 1:
                    raise ValueError("Vertical must be greater than zero")
                break
    
            except ValueError:
                print("Please enter a number greater than zero")
        g1 = 1 if input("Centered ").lower().startswith("y") else 0
        character = input(
            "Character (type 'ALL' if you want character being printed) "
        ).upper()
        statement = input("Statement ")
    
        input("Set page ")  # This means to prepare printer, just press Enter
    
        for statement_char in statement:
            s = letters[statement_char].copy()
            x_str = character
            if x_str == "ALL":
                x_str = statement_char
            if x_str == " ":
                print("\n" * (7 * horizontal))
            else:
                for u in range(0, 7):
                    for k in range(8, -1, -1):
                        if 2**k >= s[u]:
                            j[8 - k] = 0
                        else:
                            j[8 - k] = 1
                            s[u] = s[u] - 2**k
                            if s[u] == 1:
                                f[u] = 8 - k
                                break
                    for _t1 in range(1, horizontal + 1):
                        line_str = " " * int((63 - 4.5 * vertical) * g1 / len(x_str) + 1)
                        for b in range(0, f[u] + 1):
                            if j[b] == 0:
                                for _ in range(1, vertical + 1):
                                    line_str = line_str + " " * len(x_str)
                            else:
                                line_str = line_str + x_str * vertical
                        print(line_str)
                print("\n" * (2 * horizontal - 1))
        # print("\n" * 75)  # Feed some more paper from the printer
    
    
    if __name__ == "__main__":
        print_banner()
    
    
    ================================================
    FILE: 06_Banner/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 06_Banner/ruby/banner.rb
    ================================================
    #!/usr/bin/env ruby
    
    # Banner
    # reinterpreted from BASIC by stephan.com
    
    # this implementation diverges from the original in some notable
    # ways, but maintains the same font definition as before as well
    # as the same somewhat bizarre way of interpreting it.  It would
    # be more efficient to redesign the font to allow `"%09b" % row`
    # and then some substitutions.
    
    FONT = {
      ' ' => [0, 0, 0, 0, 0, 0, 0].freeze,
      '!' => [1, 1, 1, 384, 1, 1, 1].freeze,
      '*' => [69, 41, 17, 512, 17, 41, 69].freeze,
      '.' => [1, 1, 129, 449, 129, 1, 1].freeze,
      '0' => [57, 69, 131, 258, 131, 69, 57].freeze,
      '1' => [0, 0, 261, 259, 512, 257, 257].freeze,
      '2' => [261, 387, 322, 290, 274, 267, 261].freeze,
      '3' => [66, 130, 258, 274, 266, 150, 100].freeze,
      '4' => [33, 49, 41, 37, 35, 512, 33].freeze,
      '5' => [160, 274, 274, 274, 274, 274, 226].freeze,
      '6' => [194, 291, 293, 297, 305, 289, 193].freeze,
      '7' => [258, 130, 66, 34, 18, 10, 8].freeze,
      '8' => [69, 171, 274, 274, 274, 171, 69].freeze,
      '9' => [263, 138, 74, 42, 26, 10, 7].freeze,
      '=' => [41, 41, 41, 41, 41, 41, 41].freeze,
      '?' => [5, 3, 2, 354, 18, 11, 5].freeze,
      'a' => [505, 37, 35, 34, 35, 37, 505].freeze,
      'b' => [512, 274, 274, 274, 274, 274, 239].freeze,
      'c' => [125, 131, 258, 258, 258, 131, 69].freeze,
      'd' => [512, 258, 258, 258, 258, 131, 125].freeze,
      'e' => [512, 274, 274, 274, 274, 258, 258].freeze,
      'f' => [512, 18, 18, 18, 18, 2, 2].freeze,
      'g' => [125, 131, 258, 258, 290, 163, 101].freeze,
      'h' => [512, 17, 17, 17, 17, 17, 512].freeze,
      'i' => [258, 258, 258, 512, 258, 258, 258].freeze,
      'j' => [65, 129, 257, 257, 257, 129, 128].freeze,
      'k' => [512, 17, 17, 41, 69, 131, 258].freeze,
      'l' => [512, 257, 257, 257, 257, 257, 257].freeze,
      'm' => [512, 7, 13, 25, 13, 7, 512].freeze,
      'n' => [512, 7, 9, 17, 33, 193, 512].freeze,
      'o' => [125, 131, 258, 258, 258, 131, 125].freeze,
      'p' => [512, 18, 18, 18, 18, 18, 15].freeze,
      'q' => [125, 131, 258, 258, 322, 131, 381].freeze,
      'r' => [512, 18, 18, 50, 82, 146, 271].freeze,
      's' => [69, 139, 274, 274, 274, 163, 69].freeze,
      't' => [2, 2, 2, 512, 2, 2, 2].freeze,
      'u' => [128, 129, 257, 257, 257, 129, 128].freeze,
      'v' => [64, 65, 129, 257, 129, 65, 64].freeze,
      'w' => [256, 257, 129, 65, 129, 257, 256].freeze,
      'x' => [388, 69, 41, 17, 41, 69, 388].freeze,
      'y' => [8, 9, 17, 481, 17, 9, 8].freeze,
      'z' => [386, 322, 290, 274, 266, 262, 260].freeze
    }.freeze
    
    puts 'horizontal'
    x = gets.strip.to_i
    puts 'vertical'
    y = gets.strip.to_i
    puts 'centered'
    centered = gets.strip.downcase.chars.first == 'y'
    puts 'character ("all" for character being printed)'
    fill = gets.strip.downcase
    puts 'statement'
    statement = gets.strip.downcase
    
    all = (fill.downcase == 'all')
    lenxs = all ? 1 : fill.length
    start = 1
    start += (63 - 4.5 * y) / lenxs if centered
    
    statement.each_char do |char|
      next puts "\n" * 7 * x if char == ' '
    
      xs = all ? char : fill
      FONT[char].each do |su|
        print ' ' * start
        8.downto(0) do |k|
          if (1 << k) < su
            print xs * y
            su -= (1 << k)
          else
            print ' ' * (y * lenxs)
          end
        end
        puts
      end
    
      (2 * x).times { puts }
    end
    75.times { puts }
    
    
    ================================================
    FILE: 06_Banner/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 06_Banner/vbnet/banner.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.31321.278
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "Banner", "Banner.vbproj", "{091ABE13-3E70-4848-B836-592F725915A3}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{091ABE13-3E70-4848-B836-592F725915A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{091ABE13-3E70-4848-B836-592F725915A3}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{091ABE13-3E70-4848-B836-592F725915A3}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{091ABE13-3E70-4848-B836-592F725915A3}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {04E18CE1-1C55-432F-A15A-DC2FA9DDC0F1}
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 06_Banner/vbnet/banner.vb
    ================================================
    Imports System
    
    Module Banner
        Private Horizontal As Integer
        Private Vertical As Integer
        Private Centered As Boolean
        Private Character As String
        Private Statement As String
    
        ' This provides a bit-ended representation of each symbol
        ' that can be output.  Each symbol Is defined by 7 parts -
        ' where each part Is an integer value that, when converted to
        ' the binary representation, shows which section Is filled in
        ' with values And which are spaces.  i.e., the 'filled in'
        ' parts represent the actual symbol on the paper.
        Private letters As Dictionary(Of Char, Integer()) = New Dictionary(Of Char, Integer()) From
            {
                {" ", {0, 0, 0, 0, 0, 0, 0}},
                {"A", {505, 37, 35, 34, 35, 37, 505}},
                {"B", {512, 274, 274, 274, 274, 274, 239}},
                {"C", {125, 131, 258, 258, 258, 131, 69}},
                {"D", {512, 258, 258, 258, 258, 131, 125}},
                {"E", {512, 274, 274, 274, 274, 258, 258}},
                {"F", {512, 18, 18, 18, 18, 2, 2}},
                {"G", {125, 131, 258, 258, 290, 163, 101}},
                {"H", {512, 17, 17, 17, 17, 17, 512}},
                {"I", {258, 258, 258, 512, 258, 258, 258}},
                {"J", {65, 129, 257, 257, 257, 129, 128}},
                {"K", {512, 17, 17, 41, 69, 131, 258}},
                {"L", {512, 257, 257, 257, 257, 257, 257}},
                {"M", {512, 7, 13, 25, 13, 7, 512}},
                {"N", {512, 7, 9, 17, 33, 193, 512}},
                {"O", {125, 131, 258, 258, 258, 131, 125}},
                {"P", {512, 18, 18, 18, 18, 18, 15}},
                {"Q", {125, 131, 258, 258, 322, 131, 381}},
                {"R", {512, 18, 18, 50, 82, 146, 271}},
                {"S", {69, 139, 274, 274, 274, 163, 69}},
                {"T", {2, 2, 2, 512, 2, 2, 2}},
                {"U", {128, 129, 257, 257, 257, 129, 128}},
                {"V", {64, 65, 129, 257, 129, 65, 64}},
                {"W", {256, 257, 129, 65, 129, 257, 256}},
                {"X", {388, 69, 41, 17, 41, 69, 388}},
                {"Y", {8, 9, 17, 481, 17, 9, 8}},
                {"Z", {386, 322, 290, 274, 266, 262, 260}},
                {"0", {57, 69, 131, 258, 131, 69, 57}},
                {"1", {0, 0, 261, 259, 512, 257, 257}},
                {"2", {261, 387, 322, 290, 274, 267, 261}},
                {"3", {66, 130, 258, 274, 266, 150, 100}},
                {"4", {33, 49, 41, 37, 35, 512, 33}},
                {"5", {160, 274, 274, 274, 274, 274, 226}},
                {"6", {194, 291, 293, 297, 305, 289, 193}},
                {"7", {258, 130, 66, 34, 18, 10, 8}},
                {"8", {69, 171, 274, 274, 274, 171, 69}},
                {"9", {263, 138, 74, 42, 26, 10, 7}},
                {"?", {5, 3, 2, 354, 18, 11, 5}},
                {"*", {69, 41, 17, 512, 17, 41, 69}},
                {"=", {41, 41, 41, 41, 41, 41, 41}},
                {"!", {1, 1, 1, 384, 1, 1, 1}},
                {".", {1, 1, 129, 449, 129, 1, 1}}
            }
    
        ' 
        ' This displays the provided text on the screen And then waits for the user
        ' to enter a integer value greater than 0.
        ' 
        ' Text to display on the screen asking for the input
        ' The integer value entered by the user
        Private Function GetNumber(DisplayText As String) As Integer
            Console.Write(DisplayText)
            Dim TempStr As String = Console.ReadLine()
            Dim TempInt As Integer
    
            Int32.TryParse(TempStr, TempInt)
    
            If (TempInt <= 0) Then
                Throw New ArgumentException($"{DisplayText} must be greater than zero")
            End If
    
            Return TempInt
        End Function
    
        ' 
        ' This displays the provided text on the screen And then waits for the user
        ' to enter a Y Or N.  It cheats by just looking for a 'y' and returning that
        ' as true.  Anything else that the user enters Is returned as false.
        ' 
        ' Text to display on the screen asking for the input
        ' Returns true Or false
        Private Function GetBool(DisplayText As String) As Boolean
            Console.Write(DisplayText)
            Return Console.ReadLine().StartsWith("y", StringComparison.InvariantCultureIgnoreCase)
        End Function
    
        ' 
        ' This displays the provided text on the screen And then waits for the user
        ' to enter an arbitrary string.  That string Is then returned 'as-is'.
        ' 
        ' Text to display on the screen asking for the input
        ' The string entered by the user.
        Private Function GetString(DisplayText As String) As String
            Console.Write(DisplayText)
            Return (Console.ReadLine().ToUpper())
        End Function
    
        ' 
        ' This queries the user for the various inputs needed by the program.
        ' 
        Private Sub GetInput()
            Horizontal = GetNumber("Horizontal ")
            Vertical = GetNumber("Vertical ")
            Centered = GetBool("Centered ")
            Character = GetString("Character (type 'ALL' if you want character being printed) ")
            Statement = GetString("Statement ")
            ' We don't care about what the user enters here.  This is just telling them
            ' to set the page in the printer.
            GetString("Set page ")
        End Sub
    
        ' 
        ' This prints out a single character of the banner - adding
        ' a few blanks lines as a spacer between characters.
        ' 
        Private Sub PrintChar(ch As Char)
            ' In the trivial case (a space character), just print out the spaces
            If ch.Equals(" ") Then
                Console.WriteLine(New String("\n", 7 * Horizontal))
                Return
            End If
    
            ' If a specific character to be printed was provided by the user,
            ' then user that as our ouput character - otherwise take the
            ' current character
            Dim outCh As Char = IIf(Character.Equals("ALL"), ch, Character.Substring(0, 1))
            Dim letter(7) As Integer
            Try
                letters(outCh).CopyTo(letter, 0)
            Catch ex As KeyNotFoundException
                Throw New KeyNotFoundException($"The provided letter {outCh} was not found in the letters list")
            End Try
    
            ' This iterates through each of the parts that make up
            ' each letter.  Each part represents 1 * Horizontal lines
            ' of actual output.
            For idx As Integer = 0 To 7
                ' New int array declarations default to zeros
                ' numSections decides how many 'sections' need to be printed
                ' for a given line of each character
                Dim numSections(7) As Integer
                ' fillInSection decides whether each 'section' of the
                ' character gets filled in with the character Or with blanks
                Dim fillInSection(9) As Integer
    
                ' This uses the value in each part to decide which
                ' sections are empty spaces in the letter Or filled in
                ' spaces.  For each section marked with 1 in fillInSection,
                ' that will correspond to 1 * Vertical characters actually
                ' being output.
                For exp As Integer = 8 To 0 Step -1
                    If (Math.Pow(2, exp) < letter(idx)) Then
                        fillInSection(8 - exp) = 1
                        letter(idx) -= Math.Pow(2, exp)
                        If (letter(idx) = 1) Then
                            ' Once we've exhausted all of the sections
                            ' defined in this part of the letter, then
                            ' we marked that number And break out of this
                            ' for loop.
                            numSections(idx) = 8 - exp
                            Exit For
                        End If
                    End If
                Next exp
    
                ' Now that we know which sections of this part of the letter
                ' are filled in Or spaces, we can actually create the string
                ' to print out.
                Dim lineStr As String = ""
    
                If (Centered) Then
                    lineStr += New String(" ", (63 - 4.5 * Vertical) * 1 / 1 + 1)
                End If
    
                For idx2 As Integer = 0 To numSections(idx)
                    lineStr = lineStr + New String(IIf(fillInSection(idx2) = 0, " ", outCh), Vertical)
                Next idx2
    
                ' Then we print that string out 1 * Horizontal number of times
                For lineIdx As Integer = 1 To Horizontal
                    Console.WriteLine(lineStr)
                Next lineIdx
            Next idx
    
    
            ' Finally, add a little spacer after each character for readability.
            Console.WriteLine(New String(Environment.NewLine, 2 * Horizontal - 1))
        End Sub
    
        ' 
        ' This prints the entire banner based in the parameters
        ' the user provided.
        ' 
        Private Sub PrintBanner()
            ' Iterate through each character in the statement
            For Each ch As Char In Statement
                PrintChar(ch)
            Next ch
    
            ' In the original version, it would print an additional 75 blank
            ' lines in order to feed the printer paper...don't really need this
            ' since we're not actually printing.
            Console.WriteLine(New String(Environment.NewLine, 75))
        End Sub
    
        ' 
        ' Main entry point into the banner class And handles the main loop.
        ' 
        Public Sub Play()
            GetInput()
            PrintBanner()
        End Sub
    End Module
    
    Module Program
        Sub Main(args As String())
            Banner.Play()
        End Sub
    End Module
    
    
    ================================================
    FILE: 06_Banner/vbnet/banner.vbproj
    ================================================
    
    
      
        Exe
        Banner
        netcoreapp3.1
        16.9
      
    
    
    
    
    ================================================
    FILE: 07_Basketball/README.md
    ================================================
    ### Basketball
    
    This program simulates a game of basketball between Dartmouth College and an opponent of your choice. You are the Dartmouth captain and control the type of shot and defense during the course of the game.
    
    There are four types of shots:
    1. Long Jump Shot (30ft)
    2. Short Jump Shot (15ft)
    3. Lay Up
    4. Set Shot
    
    Both teams use the same defense, but you may call it:
    - Enter (6): Press
    - Enter (6.5): Man-to-man
    - Enter (7): Zone
    - Enter (7.5): None
    
    To change defense, type "0" as your next shot.
    
    Note: The game is biased slightly in favor of Dartmouth. The average probability of a Dartmouth shot being good is 62.95% compared to a probability of 61.85% for their opponent. (This makes the sample run slightly remarkable in that Cornell won by a score of 45 to 42 Hooray for the Big Red!)
    
    Charles Bacheller of Dartmouth College was the original author of this game.
    
    ---
    
    As published in Basic Computer Games (1978)
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=12)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=27)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    ##### Original bugs
    
    ###### Initial defense selection
    
    If a number <6 is entered for the starting defense then the original code prompts again until a value >=6 is entered,
    but then skips the opponent selection center jump.
    
    The C# port does not reproduce this behavior. It does prompt for a correct value, but will then go to opponent selection
    followed by the center jump.
    
    ###### Unvalidated defense selection
    
    The original code does not validate the value entered for the defense beyond checking that it is >=6. A large enough
    defense value will guarantee that all shots are good, and the game gets rather predictable.
    
    This bug is preserved in the C# port.
    
    
    ================================================
    FILE: 07_Basketball/basketball.bas
    ================================================
    5 PRINT TAB(31);"BASKETBALL"
    7 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    8 PRINT:PRINT:PRINT
    10 PRINT "THIS IS DARTMOUTH COLLEGE BASKETBALL.  YOU WILL BE DARTMOUTH"
    20 PRINT " CAPTAIN AND PLAYMAKER.  CALL SHOTS AS FOLLOWS:  1. LONG"
    30 PRINT " (30 FT.) JUMP SHOT; 2. SHORT (15 FT.) JUMP SHOT; 3. LAY"
    40 PRINT " UP; 4. SET SHOT."
    60 PRINT "BOTH TEAMS WILL USE THE SAME DEFENSE.  CALL DEFENSE AS"
    70 PRINT "FOLLOWS:  6. PRESS; 6.5 MAN-TO MAN; 7. ZONE; 7.5 NONE."
    72 PRINT "TO CHANGE DEFENSE, JUST TYPE 0 AS YOUR NEXT SHOT."
    76 INPUT "YOUR STARTING DEFENSE WILL BE";D:IF D<6 THEN 2010
    79 PRINT
    80 INPUT "CHOOSE YOUR OPPONENT";O$
    370 PRINT "CENTER JUMP"
    390 IF RND(1)> 3/5 THEN 420
    400 PRINT O$;" CONTROLS THE TAP."
    410 GOTO 3000
    420 PRINT "DARTMOUTH CONTROLS THE TAP."
    425 PRINT
    430 INPUT "YOUR SHOT";Z
    440 P=0
    445 IF Z<>INT(Z) THEN 455
    446 IF Z<0 OR Z>4 THEN 455
    447 GOTO 460
    455 PRINT "INCORRECT ANSWER.  RETYPE IT. ";:GOTO 430
    460 IF RND(1)<.5 THEN 1000
    480 IF T<100 THEN 1000
    490 PRINT
    491 IF S(1)<>S(0) THEN 510
    492 PRINT:PRINT "   ***** END OF SECOND HALF *****":PRINT
    493 PRINT "SCORE AT END OF REGULATION TIME:"
    494 PRINT "        DARTMOUTH:";S(1);"  ";O$;":";S(0)
    495 PRINT
    496 PRINT "BEGIN TWO MINUTE OVERTIME PERIOD"
    499 T=93
    500 GOTO 370
    510 PRINT "   ***** END OF GAME *****"
    515 PRINT "FINAL SCORE: DARTMOUTH:";S(1);"  ";O$;":";S(0)
    520 STOP
    600 PRINT
    610 PRINT "   *** TWO MINUTES LEFT IN THE GAME ***"
    620 PRINT
    630 RETURN
    1000 ON Z GOTO 1040,1040
    1030 GOTO 1300
    1040 T=T+1
    1041 IF T=50 THEN 8000
    1042 IF T=92 THEN 1046
    1043 GOTO 1050
    1046 GOSUB 600
    1050 PRINT "JUMP SHOT"
    1060 IF RND(1)>.341*D/8 THEN 1090
    1070 PRINT "SHOT IS GOOD."
    1075 GOSUB 7000
    1085 GOTO 3000
    1090 IF RND(1)>.682*D/8 THEN 1200
    1100 PRINT "SHOT IS OFF TARGET."
    1105 IF D/6*RND(1)>.45 THEN 1130
    1110 PRINT "DARTMOUTH CONTROLS THE REBOUND."
    1120 GOTO 1145
    1130 PRINT "REBOUND TO ";O$
    1140 GOTO 3000
    1145 IF RND(1)>.4 THEN 1158
    1150 GOTO 1300
    1158 IF D=6 THEN 5100
    1160 PRINT "BALL PASSED BACK TO YOU. ";
    1170 GOTO 430
    1180 IF RND(1)>.9 THEN 1190
    1185 PRINT "PLAYER FOULED, TWO SHOTS."
    1187 GOSUB 4000
    1188 GOTO 3000
    1190 PRINT "BALL STOLEN. ";O$;"'S BALL."
    1195 GOTO 3000
    1200 IF RND(1)>.782*D/8 THEN 1250
    1210 PRINT "SHOT IS BLOCKED.  BALL CONTROLLED BY ";
    1230 IF RND(1)>.5 THEN 1242
    1235 PRINT "DARTMOUTH."
    1240 GOTO 430
    1242 PRINT O$;"."
    1245 GOTO 3000
    1250 IF RND(1)>.843*D/8 THEN 1270
    1255 PRINT "SHOOTER IS FOULED.  TWO SHOTS."
    1260 GOSUB 4000
    1265 GOTO 3000
    1270 PRINT "CHARGING FOUL.  DARTMOUTH LOSES BALL."
    1280 GOTO 3000
    1300 T=T+1
    1301 IF T=50 THEN 8000
    1302 IF T=92 THEN 1304
    1303 GOTO 1305
    1304 GOSUB 600
    1305 IF Z=0 THEN 2010
    1310 IF Z>3 THEN 1700
    1320 PRINT "LAY UP."
    1330 IF 7/D*RND(1)>.4 THEN 1360
    1340 PRINT "SHOT IS GOOD.  TWO POINTS."
    1345 GOSUB 7000
    1355 GOTO 3000
    1360 IF 7/D*RND(1)>.7 THEN 1500
    1370 PRINT "SHOT IS OFF THE RIM."
    1380 IF RND(1)>2/3 THEN 1415
    1390 PRINT O$;" CONTROLS THE REBOUND."
    1400 GOTO 3000
    1415 PRINT "DARTMOUTH CONTROLS THE REBOUND."
    1420 IF RND(1)>.4 THEN 1440
    1430 GOTO 1300
    1440 PRINT "BALL PASSED BACK TO YOU.";
    1450 GOTO 430
    1500 IF 7/D*RND(1)>.875 THEN 1600
    1510 PRINT "SHOOTER FOULED.  TWO SHOTS."
    1520 GOSUB 4000
    1530 GOTO 3000
    1600 IF 7/D*RND(1)>.925 THEN 1630
    1610 PRINT "SHOT BLOCKED. ";O$;"'S BALL."
    1620 GOTO 3000
    1630 PRINT "CHARGING FOUL.  DARTMOUTH LOSES THE BALL."
    1640 GOTO 3000
    1700 PRINT "SET SHOT."
    1710 GOTO 1330
    2010 INPUT "YOUR NEW DEFENSIVE ALLIGNMENT IS";D
    2030 IF D<6 THEN 2010
    2040 GOTO 425
    3000 P=1
    3005 T=T+1
    3008 IF T=50 THEN 8000
    3012 GOTO 3018
    3015 GOSUB 600
    3018 PRINT
    3020 Z1=10/4*RND(1)+1
    3030 IF Z1>2 THEN 3500
    3040 PRINT "JUMP SHOT."
    3050 IF 8/D*RND(1)>.35 THEN 3100
    3060 PRINT "SHOT IS GOOD."
    3080 GOSUB 6000
    3090 GOTO 425
    3100 IF 8/D*RND(1)>.75 THEN 3200
    3105 PRINT "SHOT IS OFF RIM."
    3110 IF D/6*RND(1)>.5 THEN 3150
    3120 PRINT "DARTMOUTH CONTROLS THE REBOUND."
    3130 GOTO 425
    3150 PRINT O$;" CONTROLS THE REBOUND."
    3160 IF D=6 THEN 5000
    3165 IF RND(1)>.5 THEN 3175
    3168 PRINT "PASS BACK TO ";O$;" GUARD."
    3170 GOTO 3000
    3175 GOTO 3500
    3200 IF 8/D*RND(1)>.9 THEN 3310
    3210 PRINT "PLAYER FOULED.  TWO SHOTS."
    3220 GOSUB 4000
    3230 GOTO 425
    3310 PRINT "OFFENSIVE FOUL.  DARTMOUTH'S BALL."
    3320 GOTO 425
    3500 IF Z1>3 THEN 3800
    3510 PRINT "LAY UP."
    3520 IF 7/D*RND(1)>.413 THEN 3600
    3530 PRINT "SHOT IS GOOD."
    3540 GOSUB 6000
    3550 GOTO 425
    3600 PRINT "SHOT IS MISSED."
    3610 GOTO 3110
    3800 PRINT "SET SHOT."
    3810 GOTO 3520
    4000 REM FOUL SHOOTING
    4010 IF RND(1)>.49 THEN 4050
    4020 PRINT "SHOOTER MAKES BOTH SHOTS."
    4030 S(1-P)=S(1-P)+2
    4040 GOSUB 6010
    4041 RETURN
    4050 IF RND(1)>.75 THEN 4100
    4060 PRINT "SHOOTER MAKES ONE SHOT AND MISSES ONE."
    4070 S(1-P)=S(1-P)+1
    4080 GOTO 4040
    4100 PRINT "BOTH SHOTS MISSED."
    4110 GOTO 4040
    5000 IF RND(1)>.75 THEN 5010
    5005 GOTO 3165
    5010 PRINT "BALL STOLEN.  EASY LAY UP FOR DARTMOUTH."
    5015 GOSUB 7000
    5030 GOTO 3000
    5100 IF RND(1)>.6 THEN 5120
    5110 GOTO 1160
    5120 PRINT "PASS STOLEN BY ";O$;" EASY LAYUP."
    5130 GOSUB 6000
    5140 GOTO 425
    6000 S(0)=S(0)+2
    6010 PRINT "SCORE: ";S(1);"TO";S(0)
    6020 RETURN
    7000 S(1)=S(1)+2
    7010 GOSUB 6010
    7020 RETURN
    8000 PRINT:PRINT "   ***** END OF FIRST HALF *****":PRINT
    8010 PRINT "SCORE: DARTMOUTH:";S(1);"  ";O$;":";S(0)
    8015 PRINT
    8016 PRINT
    8020 GOTO 370
    9999 END
    
    
    ================================================
    FILE: 07_Basketball/csharp/Basketball.csproj
    ================================================
    
      
        Exe
        net6.0
        10
        enable
        enable
      
    
      
        
      
    
      
        
      
    
    
    
    
    ================================================
    FILE: 07_Basketball/csharp/Basketball.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Basketball", "Basketball.csproj", "{00D03FB3-B485-480F-B14D-746371BDE08B}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{00D03FB3-B485-480F-B14D-746371BDE08B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{00D03FB3-B485-480F-B14D-746371BDE08B}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{00D03FB3-B485-480F-B14D-746371BDE08B}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{00D03FB3-B485-480F-B14D-746371BDE08B}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 07_Basketball/csharp/Clock.cs
    ================================================
    using Basketball.Resources;
    using Games.Common.IO;
    
    namespace Basketball;
    
    internal class Clock
    {
        private readonly IReadWrite _io;
        private int time;
    
        public Clock(IReadWrite io) => _io = io;
    
        public bool IsHalfTime => time == 50;
        public bool IsFullTime => time >= 100;
        public bool TwoMinutesLeft => time == 92;
    
        public void Increment(Scoreboard scoreboard)
        {
            time += 1;
            if (IsHalfTime) { scoreboard.Display(Resource.Formats.EndOfFirstHalf); }
            if (TwoMinutesLeft) { _io.Write(Resource.Streams.TwoMinutesLeft); }
        }
    
        public void StartOvertime() => time = 93;
    }
    
    ================================================
    FILE: 07_Basketball/csharp/Defense.cs
    ================================================
    namespace Basketball;
    
    internal class Defense
    {
        private float _value;
    
        public Defense(float value) => Set(value);
    
        public void Set(float value) => _value = value;
    
        public static implicit operator float(Defense defense) => defense._value;
    }
    
    
    ================================================
    FILE: 07_Basketball/csharp/Game.cs
    ================================================
    using Basketball.Plays;
    using Basketball.Resources;
    using Games.Common.IO;
    using Games.Common.Randomness;
    
    namespace Basketball;
    
    internal class Game
    {
        private readonly Clock _clock;
        private readonly Scoreboard _scoreboard;
        private readonly TextIO _io;
        private readonly IRandom _random;
    
        private Game(Clock clock, Scoreboard scoreboard, TextIO io, IRandom random)
        {
            _clock = clock;
            _scoreboard = scoreboard;
            _io = io;
            _random = random;
        }
    
        public static Game Create(TextIO io, IRandom random)
        {
            io.Write(Resource.Streams.Introduction);
    
            var defense = new Defense(io.ReadDefense("Your starting defense will be"));
            var clock = new Clock(io);
    
            io.WriteLine();
    
            var scoreboard = new Scoreboard(
                new Team("Dartmouth", new HomeTeamPlay(io, random, clock, defense)),
                new Team(io.ReadString("Choose your opponent"), new VisitingTeamPlay(io, random, clock, defense)),
                io);
    
            return new Game(clock, scoreboard, io, random);
        }
    
        public void Play()
        {
            var ballContest = new BallContest(0.4f, "{0} controls the tap", _io, _random);
    
            while (true)
            {
                _io.WriteLine("Center jump");
                ballContest.Resolve(_scoreboard);
    
                _io.WriteLine();
    
                while (true)
                {
                    var isFullTime = _scoreboard.Offense.ResolvePlay(_scoreboard);
                    if (isFullTime && IsGameOver()) { return; }
                    if (_clock.IsHalfTime) { break; }
                }
            }
        }
    
        private bool IsGameOver()
        {
            _io.WriteLine();
            if (_scoreboard.ScoresAreEqual)
            {
                _scoreboard.Display(Resource.Formats.EndOfSecondHalf);
                _clock.StartOvertime();
                return false;
            }
    
            _scoreboard.Display(Resource.Formats.EndOfGame);
            return true;
        }
    }
    
    
    ================================================
    FILE: 07_Basketball/csharp/IRandomExtensions.cs
    ================================================
    using Games.Common.Randomness;
    
    namespace Basketball;
    
    internal static class IRandomExtensions
    {
        internal static Shot NextShot(this IRandom random) => Shot.Get(random.NextFloat(1, 3.5f));
    }
    
    
    ================================================
    FILE: 07_Basketball/csharp/IReadWriteExtensions.cs
    ================================================
    using Games.Common.IO;
    
    namespace Basketball;
    
    internal static class IReadWriteExtensions
    {
        public static float ReadDefense(this IReadWrite io, string prompt)
        {
            while (true)
            {
                var defense = io.ReadNumber(prompt);
                if (defense >= 6) { return defense; }
            }
        }
    
        private static bool TryReadInteger(this IReadWrite io, string prompt, out int intValue)
        {
            var floatValue = io.ReadNumber(prompt);
            intValue = (int)floatValue;
            return intValue == floatValue;
        }
    
        public static Shot? ReadShot(this IReadWrite io, string prompt)
        {
            while (true)
            {
                if (io.TryReadInteger(prompt, out var value) && Shot.TryGet(value, out var shot))
                {
                    return shot;
                }
                io.Write("Incorrect answer.  Retype it. ");
            }
        }
    }
    
    
    ================================================
    FILE: 07_Basketball/csharp/JumpShot.cs
    ================================================
    namespace Basketball;
    
    public class JumpShot : Shot
    {
        public JumpShot()
            : base("Jump shot")
        {
        }
    }
    
    ================================================
    FILE: 07_Basketball/csharp/Plays/BallContest.cs
    ================================================
    using Games.Common.IO;
    using Games.Common.Randomness;
    
    namespace Basketball.Plays;
    
    internal class BallContest
    {
        private readonly float _probability;
        private readonly string _messageFormat;
        private readonly IReadWrite _io;
        private readonly IRandom _random;
    
        internal BallContest(float probability, string messageFormat, IReadWrite io, IRandom random)
        {
            _io = io;
            _probability = probability;
            _messageFormat = messageFormat;
            _random = random;
        }
    
        internal bool Resolve(Scoreboard scoreboard)
        {
            var winner = _random.NextFloat() <= _probability ? scoreboard.Home : scoreboard.Visitors;
            scoreboard.Offense = winner;
            _io.WriteLine(_messageFormat, winner);
            return false;
        }
    }
    
    
    ================================================
    FILE: 07_Basketball/csharp/Plays/HomeTeamPlay.cs
    ================================================
    using Games.Common.IO;
    using Games.Common.Randomness;
    
    namespace Basketball.Plays;
    
    internal class HomeTeamPlay : Play
    {
        private readonly TextIO _io;
        private readonly IRandom _random;
        private readonly Clock _clock;
        private readonly Defense _defense;
        private readonly BallContest _ballContest;
    
        public HomeTeamPlay(TextIO io, IRandom random, Clock clock, Defense defense)
            : base(io, random, clock)
        {
            _io = io;
            _random = random;
            _clock = clock;
            _defense = defense;
            _ballContest = new BallContest(0.5f, "Shot is blocked.  Ball controlled by {0}.", _io, _random);
        }
    
        internal override bool Resolve(Scoreboard scoreboard)
        {
            var shot = _io.ReadShot("Your shot");
    
            if (_random.NextFloat() >= 0.5f && _clock.IsFullTime) { return true; }
    
            if (shot is null)
            {
                _defense.Set(_io.ReadDefense("Your new defensive alignment is"));
                _io.WriteLine();
                return false;
            }
    
            if (shot is JumpShot jumpShot)
            {
                if (ClockIncrementsToHalfTime(scoreboard)) { return false; }
                if (!Resolve(jumpShot, scoreboard)) { return false; }
            }
    
            do
            {
                if (ClockIncrementsToHalfTime(scoreboard)) { return false; }
            } while (Resolve(shot, scoreboard));
    
            return false;
        }
    
        // The Resolve* methods resolve the probabilistic outcome of the current game state.
        // They return true if the Home team should continue the play and attempt a layup, false otherwise.
        private bool Resolve(JumpShot shot, Scoreboard scoreboard) =>
            Resolve(shot.ToString(), _defense / 8)
                .Do(0.341f, () => scoreboard.AddBasket("Shot is good"))
                .Or(0.682f, () => ResolveShotOffTarget(scoreboard))
                .Or(0.782f, () => _ballContest.Resolve(scoreboard))
                .Or(0.843f, () => ResolveFreeThrows(scoreboard, "Shooter is fouled.  Two shots."))
                .Or(() => scoreboard.Turnover($"Charging foul.  {scoreboard.Home} loses ball."));
    
        private bool Resolve(Shot shot, Scoreboard scoreboard) =>
            Resolve(shot.ToString(), _defense / 7)
                .Do(0.4f, () => scoreboard.AddBasket("Shot is good.  Two points."))
                .Or(0.7f, () => ResolveShotOffTheRim(scoreboard))
                .Or(0.875f, () => ResolveFreeThrows(scoreboard, "Shooter fouled.  Two shots."))
                .Or(0.925f, () => scoreboard.Turnover($"Shot blocked. {scoreboard.Visitors}'s ball."))
                .Or(() => scoreboard.Turnover($"Charging foul.  {scoreboard.Home} loses ball."));
    
        private bool ResolveShotOffTarget(Scoreboard scoreboard) =>
            Resolve("Shot is off target", 6 / _defense)
                .Do(0.45f, () => ResolveHomeRebound(scoreboard, ResolvePossibleSteal))
                .Or(() => scoreboard.Turnover($"Rebound to {scoreboard.Visitors}"));
    
        private bool ResolveHomeRebound(Scoreboard scoreboard, Action endOfPlayAction) =>
            Resolve($"{scoreboard.Home} controls the rebound.")
                .Do(0.4f, () => true)
                .Or(() => endOfPlayAction.Invoke(scoreboard));
        private void ResolvePossibleSteal(Scoreboard scoreboard)
        {
            if (_defense == 6 && _random.NextFloat() > 0.6f)
            {
                scoreboard.Turnover();
                scoreboard.AddBasket($"Pass stolen by {scoreboard.Visitors} easy layup.");
                _io.WriteLine();
            }
            _io.Write("Ball passed back to you. ");
        }
    
        private void ResolveShotOffTheRim(Scoreboard scoreboard) =>
            Resolve("Shot is off the rim.")
                .Do(2 / 3f, () => scoreboard.Turnover($"{scoreboard.Visitors} controls the rebound."))
                .Or(() => ResolveHomeRebound(scoreboard, _ => _io.WriteLine("Ball passed back to you.")));
    }
    
    
    ================================================
    FILE: 07_Basketball/csharp/Plays/Play.cs
    ================================================
    using Games.Common.IO;
    using Games.Common.Randomness;
    
    namespace Basketball.Plays;
    
    internal abstract class Play
    {
        private readonly IReadWrite _io;
        private readonly IRandom _random;
        private readonly Clock _clock;
    
        public Play(IReadWrite io, IRandom random, Clock clock)
        {
            _io = io;
            _random = random;
            _clock = clock;
        }
    
        protected bool ClockIncrementsToHalfTime(Scoreboard scoreboard)
        {
            _clock.Increment(scoreboard);
            return _clock.IsHalfTime;
        }
    
        internal abstract bool Resolve(Scoreboard scoreboard);
    
        protected void ResolveFreeThrows(Scoreboard scoreboard, string message) =>
            Resolve(message)
                .Do(0.49f, () => scoreboard.AddFreeThrows(2, "Shooter makes both shots."))
                .Or(0.75f, () => scoreboard.AddFreeThrows(1, "Shooter makes one shot and misses one."))
                .Or(() => scoreboard.AddFreeThrows(0, "Both shots missed."));
    
        protected Probably Resolve(string message) => Resolve(message, 1f);
    
        protected Probably Resolve(string message, float defenseFactor)
        {
            _io.WriteLine(message);
            return new Probably(defenseFactor, _random);
        }
    }
    
    
    ================================================
    FILE: 07_Basketball/csharp/Plays/VisitingTeamPlay.cs
    ================================================
    using Games.Common.IO;
    using Games.Common.Randomness;
    
    namespace Basketball.Plays;
    
    internal class VisitingTeamPlay : Play
    {
        private readonly TextIO _io;
        private readonly IRandom _random;
        private readonly Defense _defense;
    
        public VisitingTeamPlay(TextIO io, IRandom random, Clock clock, Defense defense)
            : base(io, random, clock)
        {
            _io = io;
            _random = random;
            _defense = defense;
        }
    
        internal override bool Resolve(Scoreboard scoreboard)
        {
            if (ClockIncrementsToHalfTime(scoreboard)) { return false; }
    
            _io.WriteLine();
            var shot = _random.NextShot();
    
            if (shot is JumpShot jumpShot)
            {
                var continuePlay = Resolve(jumpShot, scoreboard);
                _io.WriteLine();
                if (!continuePlay) { return false; }
            }
    
            while (true)
            {
                var continuePlay = Resolve(shot, scoreboard);
                _io.WriteLine();
                if (!continuePlay) { return false; }
            }
        }
    
        // The Resolve* methods resolve the probabilistic outcome of the current game state.
        // They return true if the Visiting team should continue the play and attempt a layup, false otherwise.
        private bool Resolve(JumpShot shot, Scoreboard scoreboard) =>
            Resolve(shot.ToString(), _defense / 8)
                .Do(0.35f, () => scoreboard.AddBasket("Shot is good."))
                .Or(0.75f, () => ResolveBadShot(scoreboard, "Shot is off the rim.", _defense * 6))
                .Or(0.9f, () => ResolveFreeThrows(scoreboard, "Player fouled.  Two shots."))
                .Or(() => _io.WriteLine($"Offensive foul.  {scoreboard.Home}'s ball."));
    
        private bool Resolve(Shot shot, Scoreboard scoreboard) =>
            Resolve(shot.ToString(), _defense / 7)
                .Do(0.413f, () => scoreboard.AddBasket("Shot is good."))
                .Or(() => ResolveBadShot(scoreboard, "Shot is missed.", 6 / _defense));
    
        private bool ResolveBadShot(Scoreboard scoreboard, string message, float defenseFactor) =>
            Resolve(message, defenseFactor)
                .Do(0.5f, () => scoreboard.Turnover($"{scoreboard.Home} controls the rebound."))
                .Or(() => ResolveVisitorsRebound(scoreboard));
    
        private bool ResolveVisitorsRebound(Scoreboard scoreboard)
        {
            _io.Write($"{scoreboard.Visitors} controls the rebound.");
            if (_defense == 6 && _random.NextFloat() <= 0.25f)
            {
                _io.WriteLine();
                scoreboard.Turnover();
                scoreboard.AddBasket($"Ball stolen.  Easy lay up for {scoreboard.Home}.");
                return false;
            }
    
            if (_random.NextFloat() <= 0.5f)
            {
                _io.WriteLine();
                _io.Write($"Pass back to {scoreboard.Visitors} guard.");
                return false;
            }
    
            return true;
        }
    }
    
    
    ================================================
    FILE: 07_Basketball/csharp/Probably.cs
    ================================================
    using Games.Common.Randomness;
    
    namespace Basketball;
    
    /// 
    /// Supports a chain of actions to be performed based on various probabilities. The original game code gets a new
    /// random number for each probability check. Evaluating a set of probabilities against a single random number is
    /// much simpler, but yield a very different outcome distribution. The purpose of this class is to simplify the code
    /// to for the original probabilistic branch decisions.
    /// 
    internal struct Probably
    {
        private readonly float _defenseFactor;
        private readonly IRandom _random;
        private readonly bool? _result;
    
        internal Probably(float defenseFactor, IRandom random, bool? result = null)
        {
            _defenseFactor = defenseFactor;
            _random = random;
            _result = result;
        }
    
        public Probably Do(float probability, Action action) =>
            ShouldResolveAction(probability)
                ? new Probably(_defenseFactor, _random, Resolve(action) ?? false)
                : this;
    
        public Probably Do(float probability, Func action) =>
            ShouldResolveAction(probability)
                ? new Probably(_defenseFactor, _random, Resolve(action) ?? false)
                : this;
    
        public Probably Or(float probability, Action action) => Do(probability, action);
    
        public Probably Or(float probability, Func action) => Do(probability, action);
    
        public bool Or(Action action) => _result ?? Resolve(action) ?? false;
    
        private bool? Resolve(Action action)
        {
            action.Invoke();
            return _result;
        }
    
        private bool? Resolve(Func action) => action.Invoke();
    
        private readonly bool ShouldResolveAction(float probability) =>
            _result is null && _random.NextFloat() <= probability * _defenseFactor;
    }
    
    
    ================================================
    FILE: 07_Basketball/csharp/Program.cs
    ================================================
    using Basketball;
    using Games.Common.IO;
    using Games.Common.Randomness;
    
    var game = Game.Create(new ConsoleIO(), new RandomNumberGenerator());
    
    game.Play();
    
    ================================================
    FILE: 07_Basketball/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 07_Basketball/csharp/Resources/EndOfFirstHalf.txt
    ================================================
    
       ***** End of first half *****
    
    Score: {0}: {1}    {2}: {3}
    
    
    
    ================================================
    FILE: 07_Basketball/csharp/Resources/EndOfGame.txt
    ================================================
       ***** End of game *****
    Final score: {0}: {1}    {2}: {3}
    
    ================================================
    FILE: 07_Basketball/csharp/Resources/EndOfSecondHalf.txt
    ================================================
    
       ***** End of second half *****
    
    Score at end of regulation time:
             {0}: {1}    {2}: {3}
    
    Begin two minute overtime period
    
    ================================================
    FILE: 07_Basketball/csharp/Resources/Introduction.txt
    ================================================
                                   Basketball
                   Creative Computing  Morristown, New Jersey
    
    
    
    This is Dartmouth College basketball.  You will be Dartmouth
     captain and playmaker.  Call shots as follows:  1. Long
     (30 ft.) jump shot; 2. Short (15 ft.) jump shot; 3. Lay
     up; 4. Set shot.
    Both teams will use the same defense.  Call defense as
    follows:  6. Press; 6.5 Man-to-man; 7. Zone; 7.5 None.
    To change defense, just type 0 as your next shot.
    
    ================================================
    FILE: 07_Basketball/csharp/Resources/Resource.cs
    ================================================
    using System.Reflection;
    using System.Runtime.CompilerServices;
    
    namespace Basketball.Resources;
    
    internal static class Resource
    {
        internal static class Streams
        {
            public static Stream Introduction => GetStream();
            public static Stream TwoMinutesLeft => GetStream();
        }
    
        internal static class Formats
        {
            public static string EndOfFirstHalf => GetString();
            public static string EndOfGame => GetString();
            public static string EndOfSecondHalf => GetString();
            public static string Score => GetString();
        }
    
        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($"Basketball.Resources.{name}.txt")
                ?? throw new Exception($"Could not find embedded resource stream '{name}'.");
    }
    
    ================================================
    FILE: 07_Basketball/csharp/Resources/Score.txt
    ================================================
    Score:  {1} to {3}
    
    ================================================
    FILE: 07_Basketball/csharp/Resources/TwoMinutesLeft.txt
    ================================================
    
       *** Two minutes left in the game ***
    
    
    
    ================================================
    FILE: 07_Basketball/csharp/Scoreboard.cs
    ================================================
    using Basketball.Resources;
    using Games.Common.IO;
    
    namespace Basketball;
    
    internal class Scoreboard
    {
        private readonly Dictionary _scores;
        private readonly IReadWrite _io;
    
        public Scoreboard(Team home, Team visitors, IReadWrite io)
        {
            _scores = new() { [home] = 0, [visitors] = 0 };
            Home = home;
            Visitors = visitors;
            Offense = home;  // temporary value till first center jump
            _io = io;
        }
    
        public bool ScoresAreEqual => _scores[Home] == _scores[Visitors];
        public Team Offense { get; set; }
        public Team Home { get; }
        public Team Visitors { get; }
    
        public void AddBasket(string message) => AddScore(2, message);
    
        public void AddFreeThrows(uint count, string message) => AddScore(count, message);
    
        private void AddScore(uint score, string message)
        {
            if (Offense is null) { throw new InvalidOperationException("Offense must be set before adding to score."); }
    
            _io.WriteLine(message);
            _scores[Offense] += score;
            Turnover();
            Display();
        }
    
        public void Turnover(string? message = null)
        {
            if (message is not null) { _io.WriteLine(message); }
    
            Offense = Offense == Home ? Visitors : Home;
        }
    
        public void Display(string? format = null) =>
            _io.WriteLine(format ?? Resource.Formats.Score, Home, _scores[Home], Visitors, _scores[Visitors]);
    }
    
    
    ================================================
    FILE: 07_Basketball/csharp/Shot.cs
    ================================================
    namespace Basketball;
    
    public class Shot
    {
        private readonly string _name;
    
        public Shot(string name)
        {
            _name = name;
        }
    
        public static bool TryGet(int shotNumber, out Shot? shot)
        {
            shot = shotNumber switch
            {
                // Although the game instructions reference two different jump shots,
                // the original game code treats them both the same and just prints "Jump shot"
                0 => null,
                <= 2 => new JumpShot(),
                3 => new Shot("Lay up"),
                4 => new Shot("Set shot"),
                _ => null
            };
            return shotNumber == 0 || shot is not null;
        }
    
        public static Shot Get(float shotNumber) =>
            shotNumber switch
            {
                <= 2 => new JumpShot(),
                > 3 => new Shot("Set shot"),
                > 2 => new Shot("Lay up"),
                _ => throw new Exception("Unexpected value")
            };
    
        public override string ToString() => _name;
    }
    
    
    ================================================
    FILE: 07_Basketball/csharp/Team.cs
    ================================================
    using Basketball.Plays;
    
    namespace Basketball;
    
    internal record Team(string Name, Play PlayResolver)
    {
        public override string ToString() => Name;
    
        public bool ResolvePlay(Scoreboard scoreboard) => PlayResolver.Resolve(scoreboard);
    }
    
    
    ================================================
    FILE: 07_Basketball/java/Basketball.java
    ================================================
    import java.lang.Math;
    import java.util.*;
    import java.util.Scanner;
    
    /* The basketball class is a computer game that allows you to play as
      Dartmouth College's captain and playmaker
      The game uses set probabilites to simulate outcomes of each posession
      You are able to choose your shot types as well as defensive formations */
    
    public class Basketball {
        int time = 0;
        int[] score = {0, 0};
        double defense = -1;
        List defense_choices = Arrays.asList(6.0, 6.5, 7.0, 7.5);
        int shot = -1;
        List shot_choices = Arrays.asList(0, 1, 2, 3, 4);
        double opponent_chance = 0;
        String opponent = null;
    
        public Basketball() {
    
            // Explains the keyboard inputs
            System.out.println("\t\t\t Basketball");
            System.out.println("\t Creative Computing  Morristown, New Jersey\n\n\n");
            System.out.println("This is Dartmouth College basketball. ");
            System.out.println("Υou will be Dartmouth captain and playmaker.");
            System.out.println("Call shots as follows:");
            System.out.println("1. Long (30ft.) Jump Shot; 2. Short (15 ft.) Jump Shot; "
                  + "3. Lay up; 4. Set Shot");
            System.out.println("Both teams will use the same defense. Call Defense as follows:");
            System.out.println("6. Press; 6.5 Man-to-Man; 7. Zone; 7.5 None.");
            System.out.println("To change defense, just type 0 as your next shot.");
            System.out.print("Your starting defense will be? ");
    
            Scanner scanner = new Scanner(System.in); // creates a scanner
    
            // takes input for a defense
            if (scanner.hasNextDouble()) {
                defense = scanner.nextDouble();
            }
            else {
                scanner.next();
            }
    
            // makes sure that input is legal
            while (!defense_choices.contains(defense)) {
                System.out.print("Your new defensive allignment is? ");
                if (scanner.hasNextDouble()) {
                    defense = scanner.nextDouble();
                }
                else {
                    scanner.next();
                    continue;
                }
            }
    
            // takes input for opponent's name
            System.out.print("\nChoose your opponent? ");
    
            opponent = scanner.next();
            start_of_period();
        }
    
        // adds points to the score
        // team can take 0 or 1, for opponent or Dartmouth, respectively
        private void add_points(int team, int points) {
            score[team] += points;
            print_score();
        }
    
    
        private void ball_passed_back() {
            System.out.print("Ball passed back to you. ");
            dartmouth_ball();
        }
    
        // change defense, called when the user enters 0 for their shot
        private void change_defense() {
            defense = -1;
            Scanner scanner = new Scanner(System.in); // creates a scanner
    
            while (!defense_choices.contains(defense)) {
                System.out.println("Your new defensive allignment is? ");
                if (scanner.hasNextDouble()) {
                    defense = (double)(scanner.nextDouble());
                }
                else {
                    continue;
                }
            }
    
            dartmouth_ball();
        }
    
        // simulates two foul shots for a player and adds the points
        private void foul_shots(int team) {
            System.out.println("Shooter fouled.  Two shots.");
    
            if (Math.random() > .49) {
                if (Math.random() > .75) {
                    System.out.println("Both shots missed.");
                }
                else {
                    System.out.println("Shooter makes one shot and misses one.");
                    score[team] += 1;
                }
            }
            else {
                System.out.println("Shooter makes both shots.");
                score[team] += 2;
            }
    
            print_score();
        }
    
        // called when time = 50, starts a new period
        private void halftime() {
            System.out.println("\n   ***** End of first half *****\n");
            print_score();
            start_of_period();
        }
    
        // prints the current score
        private void print_score() {
            System.out.println("Score:  " + score[1] + " to " + score[0] + "\n");
        }
    
        // simulates a center jump for posession at the beginning of a period
        private void start_of_period() {
            System.out.println("Center jump");
            if (Math.random() > .6) {
                System.out.println("Dartmouth controls the tap.\n");
                dartmouth_ball();
            }
            else {
                System.out.println(opponent + " controls the tap.\n");
                opponent_ball();
            }
        }
    
        // called when t = 92
        private void two_minute_warning() {
            System.out.println("   *** Two minutes left in the game ***");
        }
    
        // called when the user enters 1 or 2 for their shot
        private void dartmouth_jump_shot() {
            time ++;
            if (time == 50) {
                halftime();
            }
            else if (time == 92) {
                two_minute_warning();
            }
    
            System.out.println("Jump Shot.");
            // simulates chances of different possible outcomes
            if (Math.random() > .341 * defense / 8) {
                if (Math.random() > .682 * defense / 8) {
                    if (Math.random() > .782 * defense / 8) {
                        if (Math.random() > .843 * defense / 8) {
                            System.out.println("Charging foul. Dartmouth loses ball.\n");
                            opponent_ball();
                        }
                        else {
                            // player is fouled
                            foul_shots(1);
                            opponent_ball();
                        }
                    }
                    else {
                        if (Math.random() > .5) {
                            System.out.println("Shot is blocked. Ball controlled by " +
                                  opponent + ".\n");
                            opponent_ball();
                        }
                        else {
                            System.out.println("Shot is blocked. Ball controlled by Dartmouth.");
                            dartmouth_ball();
                        }
                    }
                }
                else {
                    System.out.println("Shot is off target.");
                    if (defense / 6 * Math.random() > .45) {
                        System.out.println("Rebound to " + opponent + "\n");
                        opponent_ball();
                    }
                    else {
                        System.out.println("Dartmouth controls the rebound.");
                        if (Math.random() > .4) {
                            if (defense == 6 && Math.random() > .6) {
                                System.out.println("Pass stolen by " + opponent
                                      + ", easy lay up");
                                add_points(0, 2);
                                dartmouth_ball();
                            }
                            else {
                                // ball is passed back to you
                                ball_passed_back();
                            }
                        }
                        else {
                            System.out.println("");
                            dartmouth_non_jump_shot();
                        }
                    }
                }
            }
            else {
                System.out.println("Shot is good.");
                add_points(1, 2);
                opponent_ball();
            }
        }
    
        // called when the user enters 0, 3, or 4
        // lay up, set shot, or defense change
        private void dartmouth_non_jump_shot() {
            time ++;
            if (time == 50) {
                halftime();
            }
            else if (time == 92) {
                two_minute_warning();
            }
    
            if (shot == 4) {
                System.out.println("Set shot.");
            }
            else if (shot == 3) {
                System.out.println("Lay up.");
            }
            else if (shot == 0) {
                change_defense();
            }
    
            // simulates different outcomes after a lay up or set shot
            if (7/defense*Math.random() > .4) {
                if (7/defense*Math.random() > .7) {
                    if (7/defense*Math.random() > .875) {
                        if (7/defense*Math.random() > .925) {
                            System.out.println("Charging foul. Dartmouth loses the ball.\n");
                            opponent_ball();
                        }
                        else {
                            System.out.println("Shot blocked. " + opponent + "'s ball.\n");
                            opponent_ball();
                        }
                    }
                    else {
                        foul_shots(1);
                        opponent_ball();
                    }
                }
                else {
                    System.out.println("Shot is off the rim.");
                    if (Math.random() > 2/3) {
                        System.out.println("Dartmouth controls the rebound.");
                        if (Math.random() > .4) {
                            System.out.println("Ball passed back to you.\n");
                            dartmouth_ball();
                        }
                        else {
                            dartmouth_non_jump_shot();
                        }
                    }
                    else {
                        System.out.println(opponent + " controls the rebound.\n");
                        opponent_ball();
                    }
                }
            }
            else {
                System.out.println("Shot is good. Two points.");
                add_points(1, 2);
                opponent_ball();
            }
        }
    
    
        // plays out a Dartmouth posession, starting with your choice of shot
        private void dartmouth_ball() {
            Scanner scanner = new Scanner(System.in); // creates a scanner
            System.out.print("Your shot? ");
            shot = -1;
            if (scanner.hasNextInt()) {
                shot = scanner.nextInt();
            }
            else {
                System.out.println("");
                scanner.next();
            }
    
            while (!shot_choices.contains(shot)) {
                System.out.print("Incorrect answer. Retype it. Your shot?");
                if (scanner.hasNextInt()) {
                    shot = scanner.nextInt();
                }
                else {
                    System.out.println("");
                    scanner.next();
                }
            }
    
            if (time < 100 || Math.random() < .5) {
                if (shot == 1 || shot == 2) {
                    dartmouth_jump_shot();
                }
                else {
                    dartmouth_non_jump_shot();
                }
            }
            else {
                if (score[0] != score[1]) {
                    System.out.println("\n   ***** End Of Game *****");
                    System.out.println("Final Score: Dartmouth: " + score[1] + "  "
                          + opponent + ": " + score[0]);
                    System.exit(0);
                }
                else {
                    System.out.println("\n   ***** End Of Second Half *****");
                    System.out.println("Score at end of regulation time:");
                    System.out.println("     Dartmouth: " + score[1] + " " +
                          opponent + ": " + score[0]);
                    System.out.println("Begin two minute overtime period");
                    time = 93;
                    start_of_period();
                }
            }
        }
    
        // simulates the opponents jumpshot
        private void opponent_jumpshot() {
            System.out.println("Jump Shot.");
            if (8/defense*Math.random() > .35) {
                if (8/defense*Math.random() > .75) {
                    if (8/defense*Math.random() > .9) {
                        System.out.println("Offensive foul. Dartmouth's ball.\n");
                        dartmouth_ball();
                    }
                    else {
                        foul_shots(0);
                        dartmouth_ball();
                    }
                }
                else {
                    System.out.println("Shot is off the rim.");
                    if (defense/6*Math.random() > .5) {
                        System.out.println(opponent + " controls the rebound.");
                        if (defense == 6) {
                            if (Math.random() > .75) {
                                System.out.println("Ball stolen. Easy lay up for Dartmouth.");
                                add_points(1, 2);
                                opponent_ball();
                            }
                            else {
                                if (Math.random() > .5) {
                                    System.out.println("");
                                    opponent_non_jumpshot();
                                }
                                else {
                                    System.out.println("Pass back to " + opponent +
                                          " guard.\n");
                                    opponent_ball();
                                }
                            }
                        }
                        else {
                            if (Math.random() > .5) {
                                opponent_non_jumpshot();
                            }
                            else {
                                System.out.println("Pass back to " + opponent +
                                      " guard.\n");
                                opponent_ball();
                            }
                        }
                    }
                    else {
                        System.out.println("Dartmouth controls the rebound.\n");
                        dartmouth_ball();
                    }
                }
            }
            else {
                System.out.println("Shot is good.");
                add_points(0, 2);
                dartmouth_ball();
            }
        }
    
        // simulates opponents lay up or set shot
        private void opponent_non_jumpshot() {
            if (opponent_chance > 3) {
                System.out.println("Set shot.");
            }
            else {
                System.out.println("Lay up");
            }
            if (7/defense*Math.random() > .413) {
                System.out.println("Shot is missed.");
                if (defense/6*Math.random() > .5) {
                    System.out.println(opponent + " controls the rebound.");
                    if (defense == 6) {
                        if (Math.random() > .75) {
                            System.out.println("Ball stolen. Easy lay up for Dartmouth.");
                            add_points(1, 2);
                            opponent_ball();
                        }
                        else {
                            if (Math.random() > .5) {
                                System.out.println("");
                                opponent_non_jumpshot();
                            }
                            else {
                                System.out.println("Pass back to " + opponent +
                                      " guard.\n");
                                opponent_ball();
                            }
                        }
                    }
                    else {
                        if (Math.random() > .5) {
                            System.out.println("");
                            opponent_non_jumpshot();
                        }
                        else {
                            System.out.println("Pass back to " + opponent + " guard\n");
                            opponent_ball();
                        }
                    }
                }
                else {
                    System.out.println("Dartmouth controls the rebound.\n");
                    dartmouth_ball();
                }
            }
            else {
                System.out.println("Shot is good.");
                add_points(0, 2);
                dartmouth_ball();
            }
        }
    
        // simulates an opponents possesion
        // #randomly picks jump shot or lay up / set shot.
        private void opponent_ball() {
            time ++;
            if (time == 50) {
                halftime();
            }
            opponent_chance = 10/4*Math.random()+1;
            if (opponent_chance > 2) {
                opponent_non_jumpshot();
            }
            else {
                opponent_jumpshot();
            }
        }
    
        public static void main(String[] args) {
            Basketball new_game = new Basketball();
        }
    }
    
    
    ================================================
    FILE: 07_Basketball/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 07_Basketball/javascript/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells)
    
    
    ================================================
    FILE: 07_Basketball/javascript/basketball.html
    ================================================
    
    
    BASKETBALL
    
    
    
    
    
    
    
    
    
    
    ================================================
    FILE: 07_Basketball/javascript/basketball.js
    ================================================
    // BASKETBALL
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    var s = [0, 0];
    var z;
    var d;
    var p;
    var your_turn;
    var game_restart;
    
    function two_minutes()
    {
        print("\n");
        print("   *** TWO MINUTES LEFT IN THE GAME ***\n");
        print("\n");
    }
    
    function show_scores()
    {
        print("SCORE: " + s[1] + " TO " + s[0] + "\n");
    }
    
    function score_computer()
    {
        s[0] = s[0] + 2;
        show_scores();
    }
    
    function score_player()
    {
        s[1] = s[1] + 2;
        show_scores();
    }
    
    function half_time()
    {
        print("\n");
        print("   ***** END OF FIRST HALF *****\n");
        print("SCORE: DARMOUTH: " + s[1] + "  " + os + ": " + s[0] + "\n");
        print("\n");
        print("\n");
    }
    
    function foul()
    {
        if (Math.random() <= 0.49) {
            print("SHOOTER MAKES BOTH SHOTS.\n");
            s[1 - p] = s[1 - p] + 2;
            show_scores();
        } else if (Math.random() <= 0.75) {
            print("SHOOTER MAKES ONE SHOT AND MISSES ONE.\n");
            s[1 - p] = s[1 - p] + 1;
            show_scores();
        } else {
            print("BOTH SHOTS MISSED.\n");
            show_scores();
        }
    }
    
    function player_play()
    {
        if (z == 1 || z == 2) {
            t++;
            if (t == 50) {
                half_time();
                game_restart = 1;
                return;
            }
            if (t == 92)
                two_minutes();
            print("JUMP SHOT\n");
            if (Math.random() <= 0.341 * d / 8) {
                print("SHOT IS GOOD.\n");
                score_player();
                return;
            }
            if (Math.random() <= 0.682 * d / 8) {
                print("SHOT IS OFF TARGET.\n");
                if (d / 6 * Math.random() >= 0.45) {
                    print("REBOUND TO " + os + "\n");
                    return;
                }
                print("DARTMOUTH CONTROLS THE REBOUND.\n");
                if (Math.random() > 0.4) {
                    if (d == 6) {
                        if (Math.random() > 0.6) {
                            print("PASS STOLEN BY " + os + " EASY LAYUP.\n");
                            score_computer();
                            return;
                        }
                    }
                    print("BALL PASSED BACK TO YOU. ");
                    your_turn = 1;
                    return;
                }
            } else if (Math.random() <= 0.782 * d / 8) {
                print("SHOT IS BLOCKED.  BALL CONTROLLED BY ");
                if (Math.random() <= 0.5) {
                    print("DARTMOUTH.\n");
                    your_turn = 1;
                    return;
                }
                print(os + ".\n");
                return;
            } else if (Math.random() <= 0.843 * d / 8) {
                print("SHOOTER IS FOULED.  TWO SHOTS.\n");
                foul();
                return;
                // In original code but lines 1180-1195 aren't used (maybe replicate from computer's play)
                //        } else if (Math.random() <= 0.9 * d / 8) {
                //            print("PLAYER FOULED, TWO SHOTS.\n");
                //            foul();
                //            return;
            } else {
                print("CHARGING FOUL.  DARTMOUTH LOSES BALL.\n");
                return;
            }
        }
        while (1) {
            if (++t == 50) {
                half_time();
                game_restart = 1;
                return;
            }
            if (t == 92)
                two_minutes();
            if (z == 0) {
                your_turn = 2;
                return;
            }
            if (z <= 3)
                print("LAY UP.\n");
            else
                print("SET SHOT.\n");
            if (7 / d * Math.random() <= 0.4) {
                print("SHOT IS GOOD.  TWO POINTS.\n");
                score_player();
                return;
            }
            if (7 / d * Math.random() <= 0.7) {
                print("SHOT IS OFF THE RIM.\n");
                if (Math.random() <= 2.0 / 3.0) {
                    print(os + " CONTROLS THE REBOUND.\n");
                    return;
                }
                print("DARMOUTH CONTROLS THE REBOUND.\n");
                if (Math.random() <= 0.4)
                    continue;
                print("BALL PASSED BACK TO YOU.\n");
                your_turn = 1;
                return;
            }
            if (7 /d * Math.random() <= 0.875) {
                print("SHOOTER FOULED.  TWO SHOTS.\n");
                foul();
                return;
            }
            if (7 /d * Math.random() <= 0.925) {
                print("SHOT BLOCKED. " + os + "'S BALL.\n");
                return;
            }
            print("CHARGING FOUL.  DARTHMOUTH LOSES THE BALL.\n");
            return;
        }
    }
    
    function computer_play()
    {
        rebound = 0;
        while (1) {
            p = 1;
            if (++t == 50) {
                half_time();
                game_restart = 1;
                return;
            }
            print("\n");
            z1 = 10 / 4 * Math.random() + 1;
            if (z1 <= 2) {
                print("JUMP SHOT.\n");
                if (8 / d * Math.random() <= 0.35) {
                    print("SHOT IS GOOD.\n");
                    score_computer();
                    return;
                }
                if (8 / d * Math.random() <= 0.75) {
                    print("SHOT IS OFF RIM.\n");
                    if (d / 6 * Math.random() <= 0.5) {
                        print("DARMOUTH CONTROLS THE REBOUND.\n");
                        return;
                    }
                    print(os + " CONTROLS THE REBOUND.\n");
                    if (d == 6) {
                        if (Math.random() <= 0.75) {
                            print("BALL STOLEN.  EASY LAP UP FOR DARTMOUTH.\n");
                            score_player();
                            continue;
                        }
                        if (Math.random() > 0.6) {
                            print("PASS STOLEN BY " + os + " EASY LAYUP.\n");
                            score_computer();
                            return;
                        }
                        print("BALL PASSED BACK TO YOU. ");
                        return;
                    }
                    if (Math.random() <= 0.5) {
                        print("PASS BACK TO " + os + " GUARD.\n");
                        continue;
                    }
                } else if (8 / d * Math.random() <= 0.90) {
                    print("PLAYER FOULED.  TWO SHOTS.\n");
                    foul();
                    return;
                } else {
                    print("OFFENSIVE FOUL.  DARTMOUTH'S BALL.\n");
                    return;
                }
            }
            while (1) {
                if (z1 > 3) {
                    print("SET SHOT.\n");
                } else {
                    print("LAY UP.\n");
                }
                if (7 / d * Math.random() <= 0.413) {
                    print("SHOT IS GOOD.\n");
                    score_computer();
                    return;
                }
                print("SHOT IS MISSED.\n");
                // Spaguetti jump, better to replicate code
                if (d / 6 * Math.random() <= 0.5) {
                    print("DARMOUTH CONTROLS THE REBOUND.\n");
                    return;
                }
                print(os + " CONTROLS THE REBOUND.\n");
                if (d == 6) {
                    if (Math.random() <= 0.75) {
                        print("BALL STOLEN.  EASY LAP UP FOR DARTMOUTH.\n");
                        score_player();
                        break;
                    }
                    if (Math.random() > 0.6) {
                        print("PASS STOLEN BY " + os + " EASY LAYUP.\n");
                        score_computer();
                        return;
                    }
                    print("BALL PASSED BACK TO YOU. ");
                    return;
                }
                if (Math.random() <= 0.5) {
                    print("PASS BACK TO " + os + " GUARD.\n");
                    break;
                }
            }
        }
    }
    
    // Main program
    async function main()
    {
        print(tab(31) + "BASKETBALL\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        print("THIS IS DARTMOUTH COLLEGE BASKETBALL.  YOU WILL BE DARTMOUTH\n");
        print(" CAPTAIN AND PLAYMAKER.  CALL SHOTS AS FOLLOWS:  1. LONG\n");
        print(" (30 FT.) JUMP SHOT; 2. SHORT (15 FT.) JUMP SHOT; 3. LAY\n");
        print(" UP; 4. SET SHOT.\n");
        print("BOTH TEAMS WILL USE THE SAME DEFENSE.  CALL DEFENSE AS\n");
        print("FOLLOWS:  6. PRESS; 6.5 MAN-TO MAN; 7. ZONE; 7.5 NONE.\n");
        print("TO CHANGE DEFENSE, JUST TYPE 0 AS YOUR NEXT SHOT.\n");
        print("YOUR STARTING DEFENSE WILL BE");
        t = 0;
        p = 0;
        d = parseFloat(await input());
        if (d < 6) {
            your_turn = 2;
        } else {
            print("\n");
            print("CHOOSE YOUR OPPONENT");
            os = await input();
            game_restart = 1;
        }
        while (1) {
            if (game_restart) {
                game_restart = 0;
                print("CENTER JUMP\n");
                if (Math.random() > 3.0 / 5.0) {
                    print("DARMOUTH CONTROLS THE TAP.\n");
                } else {
                    print(os + " CONTROLS THE TAP.\n");
                    computer_play();
                }
            }
            if (your_turn == 2) {
                print("YOUR NEW DEFENSIVE ALLIGNMENT IS");
                d = parseFloat(await input());
            }
            print("\n");
            while (1) {
                print("YOUR SHOT");
                z = parseInt(await input());
                p = 0;
                if (z != Math.floor(z) || z < 0 || z > 4)
                    print("INCORRECT ANSWER.  RETYPE IT. ");
                else
                    break;
            }
            if (Math.random() < 0.5 || t < 100) {
                game_restart = 0;
                your_turn = 0;
                player_play();
                if (game_restart == 0 && your_turn == 0)
                    computer_play();
            } else {
                print("\n");
                if (s[1] == s[0]) {
                    print("\n");
                    print("   ***** END OF SECOND HALF *****\n");
                    print("\n");
                    print("SCORE AT END OF REGULATION TIME:\n");
                    print("        DARTMOUTH: " + s[1] + "  " + os + ": " + s[0] + "\n");
                    print("\n");
                    print("BEGIN TWO MINUTE OVERTIME PERIOD\n");
                    t = 93;
                    print("CENTER JUMP\n");
                    if (Math.random() > 3.0 / 5.0)
                        print("DARMOUTH CONTROLS THE TAP.\n");
                    else
                        print(os + " CONTROLS THE TAP.\n");
                } else {
                    print("   ***** END OF GAME *****\n");
                    print("FINAL SCORE: DARMOUTH: " + s[1] + "  " + os + ": " + s[0] + "\n");
                    break;
                }
            }
        }
    }
    
    main();
    
    
    ================================================
    FILE: 07_Basketball/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 07_Basketball/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 07_Basketball/pascal/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Pascal](https://en.wikipedia.org/wiki/Pascal_(programming_language))
    
    
    ================================================
    FILE: 07_Basketball/perl/README.md
    ================================================
    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.
    
    
    ================================================
    FILE: 07_Basketball/perl/basketball-orig.pl
    ================================================
    #!/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();
    }
    
    
    ================================================
    FILE: 07_Basketball/perl/basketball.pl
    ================================================
    #!/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();
    }
    
    
    ================================================
    FILE: 07_Basketball/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 07_Basketball/python/basketball.py
    ================================================
    """
    The basketball class is a computer game that allows you to play as
    Dartmouth College's captain and playmaker
    The game uses set probabilites to simulate outcomes of each posession
    You are able to choose your shot types as well as defensive formations
    """
    
    import random
    from typing import List, Literal, Optional
    
    
    def print_intro() -> None:
        print("\t\t\t Basketball")
        print("\t Creative Computing  Morristown, New Jersey\n\n\n")
        print("This is Dartmouth College basketball. ")
        print("Υou will be Dartmouth captain and playmaker.")
        print("Call shots as follows:")
        print(
            "1. Long (30ft.) Jump Shot; "
            "2. Short (15 ft.) Jump Shot; "
            "3. Lay up; 4. Set Shot"
        )
        print("Both teams will use the same defense. Call Defense as follows:")
        print("6. Press; 6.5 Man-to-Man; 7. Zone; 7.5 None.")
        print("To change defense, just type 0 as your next shot.")
        print("Your starting defense will be? ", end="")
    
    
    class Basketball:
        def __init__(self) -> None:
            self.time = 0
            self.score = [0, 0]  # first value is opponents score, second is home
            self.defense_choices: List[float] = [6, 6.5, 7, 7.5]
            self.shot: Optional[int] = None
            self.shot_choices: List[Literal[0, 1, 2, 3, 4]] = [0, 1, 2, 3, 4]
            self.z1: Optional[float] = None
    
            print_intro()
    
            self.defense = get_defense_choice(self.defense_choices)
    
            self.opponent = get_opponents_name()
            self.start_of_period()
    
        def add_points(self, team: Literal[0, 1], points: Literal[0, 1, 2]) -> None:
            """
            Add points to the score.
    
            Team can take 0 or 1, for opponent or Dartmouth, respectively
            """
            self.score[team] += points
            self.print_score()
    
        def ball_passed_back(self) -> None:
            print("Ball passed back to you. ", end="")
            self.dartmouth_ball()
    
        def change_defense(self) -> None:
            """change defense, called when the user enters 0 for their shot"""
            defense = None
    
            while defense not in self.defense_choices:
                print("Your new defensive allignment is? ")
                try:
                    defense = float(input())
                except ValueError:
                    continue
            assert isinstance(defense, float)
            self.defense = defense
            self.dartmouth_ball()
    
        def foul_shots(self, team: Literal[0, 1]) -> None:
            """Simulate two foul shots for a player and adds the points."""
            print("Shooter fouled.  Two shots.")
            if random.random() > 0.49:
                if random.random() > 0.75:
                    print("Both shots missed.")
                else:
                    print("Shooter makes one shot and misses one.")
                    self.score[team] += 1
            else:
                print("Shooter makes both shots.")
                self.score[team] += 2
    
            self.print_score()
    
        def halftime(self) -> None:
            """called when t = 50, starts a new period"""
            print("\n   ***** End of first half *****\n")
            self.print_score()
            self.start_of_period()
    
        def print_score(self) -> None:
            """Print the current score"""
            print(f"Score:  {self.score[1]} to {self.score[0]}\n")
    
        def start_of_period(self) -> None:
            """Simulate a center jump for posession at the beginning of a period"""
            print("Center jump")
            if random.random() > 0.6:
                print("Dartmouth controls the tap.\n")
                self.dartmouth_ball()
            else:
                print(self.opponent + " controls the tap.\n")
                self.opponent_ball()
    
        def two_minute_warning(self) -> None:
            """called when t = 92"""
            print("   *** Two minutes left in the game ***")
    
        def dartmouth_jump_shot(self) -> None:
            """called when the user enters 1 or 2 for their shot"""
            self.time += 1
            if self.time == 50:
                self.halftime()
            elif self.time == 92:
                self.two_minute_warning()
            print("Jump Shot.")
            # simulates chances of different possible outcomes
            if random.random() > 0.341 * self.defense / 8:
                if random.random() > 0.682 * self.defense / 8:
                    if random.random() > 0.782 * self.defense / 8:
                        if random.random() > 0.843 * self.defense / 8:
                            print("Charging foul. Dartmouth loses ball.\n")
                        else:
                            # player is fouled
                            self.foul_shots(1)
                        self.opponent_ball()
                    elif 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()
                else:
                    print("Shot is off target.")
                    if self.defense / 6 * random.random() > 0.45:
                        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(f"Pass stolen by {self.opponent}, easy lay up")
                                self.add_points(0, 2)
                                self.dartmouth_ball()
                            else:
                                # ball is passed back to you
                                self.ball_passed_back()
                        else:
                            print()
                            self.dartmouth_non_jump_shot()
            else:
                print("Shot is good.")
                self.add_points(1, 2)
                self.opponent_ball()
    
        def dartmouth_non_jump_shot(self) -> None:
            """
            Lay up, set shot, or defense change
    
            called when the user enters 0, 3, or 4
            """
            self.time += 1
            if self.time == 50:
                self.halftime()
            elif self.time == 92:
                self.two_minute_warning()
    
            if self.shot == 4:
                print("Set shot.")
            elif self.shot == 3:
                print("Lay up.")
            elif self.shot == 0:
                self.change_defense()
    
            # simulates different outcomes after a lay up or set shot
            if 7 / self.defense * random.random() > 0.4:
                if 7 / self.defense * random.random() > 0.7:
                    if 7 / self.defense * random.random() > 0.875:
                        if 7 / self.defense * random.random() > 0.925:
                            print("Charging foul. Dartmouth loses the ball.\n")
                        else:
                            print(f"Shot blocked. {self.opponent}'s ball.\n")
                    else:
                        self.foul_shots(1)
                    self.opponent_ball()
                else:
                    print("Shot is off the rim.")
                    if random.random() > 2 / 3:
                        print("Dartmouth controls the rebound.")
                        if random.random() > 0.4:
                            print("Ball passed back to you.\n")
                            self.dartmouth_ball()
                        else:
                            self.dartmouth_non_jump_shot()
                    else:
                        print(self.opponent + " controls the rebound.\n")
                        self.opponent_ball()
            else:
                print("Shot is good. Two points.")
                self.add_points(1, 2)
                self.opponent_ball()
    
        def dartmouth_ball(self) -> None:
            """plays out a Dartmouth posession, starting with your choice of shot"""
            shot = get_dartmouth_ball_choice(self.shot_choices)
            self.shot = shot
    
            if self.time < 100 or random.random() < 0.5:
                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:
                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"""
            print("Jump Shot.")
            if 8 / self.defense * random.random() > 0.35:
                if 8 / self.defense * random.random() > 0.75:
                    if 8 / self.defense * random.random() > 0.9:
                        print("Offensive foul. Dartmouth's ball.\n")
                    else:
                        self.foul_shots(0)
                    self.dartmouth_ball()
                else:
                    print("Shot is off the rim.")
                    if self.defense / 6 * random.random() > 0.5:
                        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:
                            self.opponent_non_jumpshot()
                    else:
                        print("Dartmouth controls the rebound.\n")
                        self.dartmouth_ball()
            else:
                print("Shot is good.")
                self.add_points(0, 2)
                self.dartmouth_ball()
    
        def opponent_non_jumpshot(self) -> None:
            """Simulate opponents lay up or set shot."""
            if self.z1 > 3:  # type: ignore
                print("Set shot.")
            else:
                print("Lay up")
            if 7 / self.defense * random.random() > 0.413:
                print("Shot is missed.")
                if self.defense / 6 * random.random() > 0.5:
                    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:
                        print(f"Pass back to {self.opponent} guard\n")
                        self.opponent_ball()
                else:
                    print("Dartmouth controls the rebound.\n")
                    self.dartmouth_ball()
            else:
                print("Shot is good.")
                self.add_points(0, 2)
                self.dartmouth_ball()
    
        def opponent_ball(self) -> None:
            """
            Simulate an opponents possesion
    
            Randomly picks jump shot or lay up / set shot.
            """
            self.time += 1
            if self.time == 50:
                self.halftime()
            self.z1 = 10 / 4 * random.random() + 1
            if self.z1 > 2:
                self.opponent_non_jumpshot()
            else:
                self.opponent_jumpshot()
    
    
    def get_defense_choice(defense_choices: List[float]) -> float:
        """Takes input for a defense"""
        try:
            defense = float(input())
        except ValueError:
            defense = None
    
        # if the input wasn't a valid defense, takes input again
        while defense not in defense_choices:
            print("Your new defensive allignment is? ", end="")
            try:
                defense = float(input())
            except ValueError:
                continue
        assert isinstance(defense, float)
        return defense
    
    
    def get_dartmouth_ball_choice(shot_choices: List[Literal[0, 1, 2, 3, 4]]) -> int:
        print("Your shot? ", end="")
        shot = None
        try:
            shot = int(input())
        except ValueError:
            shot = None
    
        while shot not in shot_choices:
            print("Incorrect answer. Retype it. Your shot? ", end="")
            try:
                shot = int(input())
            except Exception:
                continue
        assert isinstance(shot, int)
        return shot
    
    
    def get_opponents_name() -> str:
        """Take input for opponent's name"""
        print("\nChoose your opponent? ", end="")
        return input()
    
    
    if __name__ == "__main__":
        Basketball()
    
    
    ================================================
    FILE: 07_Basketball/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 07_Basketball/ruby/basketball.rb
    ================================================
    class Basketball
    
      def initialize
        @time = 0
        @score = [0, 0]
        @defense_choices = [6, 6.5, 7, 7.5]
        @shot = nil
        @shot_choices = [0, 1, 2, 3, 4]
        @z1 = nil
    
        puts "Υou will be Dartmouth captain and playmaker."
        puts "Call shots as follows:"
        puts "1. Long (30ft.) Jump Shot"
        puts "2. Short (15 ft.) Jump Shot"
        puts "3. Lay up; 4. Set Shot\n\n"
        
        puts "Both teams will use the same defense. Call Defense as follows:"
        puts "6. Press"
        puts "6.5 Man-to-Man"
        puts "7. Zone"
        puts "7.5 None.\n\n"
    
        puts "To change defense, just type 0 as your next shot."
    
        @defense = get_defense @defense_choices
    
    
        puts "\nChoose your opponent? "
        @opponent = gets.chomp!
        start_of_period
      end
    
      def dartmouth_ball
        while true
          puts "Your shot? " 
          @shot = gets.chomp!
          if @shot_choices.include? @shot.to_i
            break
          end
        end
    
        if @time < 100 or Random.rand < 0.5
          if @shot.to_i == 1 or @shot.to_i == 2
            dartmouth_jump_shot
          else
            dartmouth_non_jump_shot
          end
        else
          if @score[0] != @score[1]
            puts "\n   ***** End Of Game *****" 
            puts "Final Score: Dartmouth: #{@score[1].to_s}  #{@opponent}: #{@score[0].to_s}"
          else
            puts "\n   ***** End Of Second Half *****"
            puts "Score at end of regulation time:"
            puts "     Dartmouth: #{@score[1]} #{@opponent}: #{@score[0]}"
    
            puts "Begin two minute overtime period" 
            @time = 93
            start_of_period
          end
        end
      end
    
      def add_points team, points
        @score[team] += points
        print_score
      end
    
      def ball_passed_back
        puts "Ball passed back to you"
      end
    
      def change_defense
        @defense = get_defense @defense_choices, "new"
        dartmouth_ball()
      end
    
      def foul_shots team
        puts "Shooter fouled.  Two shots."
        if Random.rand > 0.49
          if Random.rand > 0.75
            puts "Both shots missed."
          else
            puts "Shooter makes one shot and misses one."
            @score[team] += 1
          end
        else
          puts "Shooter makes both shots."
          @score[team] += 2
        end
      end
    
      def halftime
        puts "\n   ***** End of first half *****\n" 
        print_score
        start_of_period
      end
    
      def print_score
        puts "Score:  #{@score[1]} to #{@score[0]}\n" 
      end
    
      def start_of_period
        puts "Center jump" 
        if Random.rand > 0.6
          puts "Dartmouth controls the tap.\n" 
          dartmouth_ball()
        else
          puts "#{@opponent} controls the tap.\n" 
          opponent_ball
        end
      end
    
      def two_minute_warning
        puts "   *** Two minutes left in the game ***" 
      end
    
      def dartmouth_jump_shot
        @time += 1
        if @time == 50
          halftime
        elsif @time == 92
          two_minute_warning
        end
        puts "Jumpshot"
    
        if Random.rand > 0.341 * @defense.to_i / 8
          if Random.rand > 0.682 * @defense.to_i / 8
            if Random.rand > 0.782 * @defense.to_i / 8
              if Random.rand > 0.843 * @defense.to_i / 8
                puts "Charging foul. Dartmouth loses ball.\n" 
                opponent_ball
              else
                foul_shots 1
                opponent_ball
              end
            else
              if Random.rand > 0.5
                puts "Shot is blocked. Ball controlled by #{@opponent}\n"
                opponent_ball
              else
                puts "Shot is blocked. Ball controlled by Dartmouth."
                dartmouth_ball()
              end
            end
          else
            puts "Shot is off target." 
            if @defense.to_i / 6 * Random.rand > 0.45
              puts "Rebound to " + @opponent + "\n" 
              opponent_ball
            else
              puts "Dartmouth controls the rebound." 
              if Random.rand > 0.4
                if @defense.to_i == 6 and Random.rand > 0.6
                  puts "Pass stolen by #{@opponent}, easy lay up" 
                  add_points(0, 2)
                  dartmouth_ball()
                else
                  ball_passed_back
                end
              else
                puts "\n" 
                dartmouth_non_jump_shot
              end
            end
          end
        else
          puts "Shot is good." 
          add_points(1, 2)
          opponent_ball
        end
      end
    
      def dartmouth_non_jump_shot
        @time += 1
        if @time == 50
          halftime
        elsif @time == 92
          two_minute_warning
        end
    
        if @shot.to_i == 4
          puts "Set shot." 
        elsif @shot.to_i == 3
          puts "Lay up." 
        elsif @shot.to_i == 0
          change_defense
        end
    
        if 7 / @defense.to_i * Random.rand > 0.4
          if 7 / @defense.to_i * Random.rand > 0.7
            if 7 / @defense.to_i * Random.rand > 0.875
              if 7 / @defense.to_i * Random.rand > 0.925
                puts "Charging foul. Dartmouth loses the ball.\n"
                opponent_ball
              else
                puts "Shot blocked. #{@opponent}'s ball.\n" 
                opponent_ball
              end
            else
              foul_shots(1)
              opponent_ball
            end
          else
            puts "Shot is off the rim." 
            if Random.rand > 2 / 3
              puts "Dartmouth controls the rebound."
              if Random.rand > 0.4
                puts "Ball passed back to you.\n"
                dartmouth_ball()
              else
                dartmouth_non_jump_shot
              end
            else
              puts "#{@opponent} controls the rebound.\n"
              opponent_ball
            end
          end
        else
          puts "Shot is good. Two points."
          add_points(1, 2)
          opponent_ball
        end
      end
    
      def opponent_jumpshot
        puts "Jump Shot." 
        if 8 / @defense.to_i * Random.rand > 0.35
          if 8 / @defense.to_i * Random.rand > 0.75
            if 8 / @defense.to_i * Random.rand > 0.9
              puts "Offensive foul. Dartmouth's ball.\n" 
              dartmouth_ball()
            else
              foul_shots(0)
              dartmouth_ball()
            end
          else
            puts "Shot is off the rim." 
            if @defense.to_i / 6 * Random.rand > 0.5
              puts "#{@opponent} controls the rebound." 
              if @defense.to_i == 6
                if Random.rand > 0.75
                  puts "Ball stolen. Easy lay up for Dartmouth." 
                  add_points(1, 2)
                  opponent_ball
                else
                  if Random.rand > 0.5
                    puts ""
                    opponent_non_jumpshot
                  else
                    puts "Pass back to #{@opponent} guard.\n" 
                    opponent_ball
                  end
                end
              else
                if Random.rand > 0.5
                  opponent_non_jumpshot
                else
                  puts "Pass back to #{@opponent} guard.\n" 
                  opponent_ball
                end
              end
            else
              puts "Dartmouth controls the rebound.\n" 
              dartmouth_ball()
            end
          end
        else
          puts "Shot is good." 
          add_points(0, 2)
          dartmouth_ball()
        end
      end
    
      def opponent_non_jumpshot
        if @z1 > 3
            puts "Set shot." 
        else
          puts "Lay up" 
        end
    
        if 7 / @defense.to_i * Random.rand > 0.413
          puts "Shot is missed." 
          if @defense.to_i / 6 * Random.rand > 0.5
            puts "#{@opponent} controls the rebound." 
            if @defense.to_i == 6
                if Random.rand > 0.75
                  puts "Ball stolen. Easy lay up for Dartmouth." 
                  add_points(1, 2)
                  opponent_ball
                else
                  if Random.rand > 0.5
                      puts "" 
                      opponent_non_jumpshot
                  else
                    puts "Pass back to #{@opponent} guard.\n" 
                    opponent_ball
                  end
                end
            else
              if Random.rand > 0.5
                puts "" 
                opponent_non_jumpshot
              else
                puts "Pass back to #{@opponent} guard\n" 
                opponent_ball
              end
            end
          else
            puts "Dartmouth controls the rebound.\n"
            dartmouth_ball()
          end
        else
          puts "Shot is good." 
          add_points(0, 2)
          dartmouth_ball()
        end
      end
    
      def opponent_ball
        @time += 1
        if @time == 50
          halftime
        end
    
        @z1 = 10 / 4 * Random.rand + 1
        
        if @z1 > 2
          opponent_non_jumpshot
        else
          opponent_jumpshot
        end
      end
    
      def get_defense defense_choices, type = "starting"
        while true
          puts "Your #{type} defense will be? [6 - Press] [6.5 - Man-to-Man] [7 - Zone] [7.5 None]"
          defense = gets.chomp!
          if defense_choices.include? defense.to_i
            break
          end
        end 
    
        return defense.to_i
      end
    end
    
    b = Basketball.new
    
    
    ================================================
    FILE: 07_Basketball/vbnet/Basketball.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Basketball", "Basketball.vbproj", "{09C533F2-4874-4BA4-9F80-BBE9E8E17456}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{09C533F2-4874-4BA4-9F80-BBE9E8E17456}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{09C533F2-4874-4BA4-9F80-BBE9E8E17456}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{09C533F2-4874-4BA4-9F80-BBE9E8E17456}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{09C533F2-4874-4BA4-9F80-BBE9E8E17456}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 07_Basketball/vbnet/Basketball.vbproj
    ================================================
    
      
        Exe
        Basketball
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 07_Basketball/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 08_Batnum/README.md
    ================================================
    ### Batnum
    
    The game starts with an imaginary pile of objects, coins for example. You and your opponent (the computer) alternately remove objects from the pile. You specify in advance the minimum and maximum number of objects that can be taken on each turn. You also specify in advance how winning is defined:
    1. To take the last object
    2. To avoid taking the last object
    
    You may also determine whether you or the computer go first.
    
    The strategy of this game is based on modulo arithmetic. If the maximum number of objects a player may remove in a turn is M, then to gain a winning position a player at the end of his turn must leave a stack of 1 modulo (M+1) coins. If you don’t understand this, play the game 23 Matches first, then BATNUM, and have fun!
    
    BATNUM is a generalized version of a great number of manual remove-the-object games. The original computer version was written by one of the two originators of the BASIC language, John Kemeny of Dartmouth College.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=14)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=29)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### 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)
    
    
    
    ================================================
    FILE: 08_Batnum/batnum.bas
    ================================================
    10 PRINT TAB(33);"BATNUM"
    20 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    30 PRINT:PRINT:PRINT
    110 PRINT "THIS PROGRAM IS A 'BATTLE OF NUMBERS' GAME, WHERE THE"
    120 PRINT "COMPUTER IS YOUR OPPONENT."
    130 PRINT
    140 PRINT "THE GAME STARTS WITH AN ASSUMED PILE OF OBJECTS. YOU"
    150 PRINT "AND YOUR OPPONENT ALTERNATELY REMOVE OBJECTS FROM THE PILE."
    160 PRINT "WINNING IS DEFINED IN ADVANCE AS TAKING THE LAST OBJECT OR"
    170 PRINT "NOT. YOU CAN ALSO SPECIFY SOME OTHER BEGINNING CONDITIONS."
    180 PRINT "DON'T USE ZERO, HOWEVER, IN PLAYING THE GAME."
    190 PRINT "ENTER A NEGATIVE NUMBER FOR NEW PILE SIZE TO STOP PLAYING."
    200 PRINT
    210 GOTO 330
    220 FOR I=1 TO 10
    230 PRINT
    240 NEXT I
    330 INPUT "ENTER PILE SIZE";N
    350 IF N>=1 THEN 370
    360 GOTO 330
    370 IF N<>INT(N) THEN 220
    380 IF N<1 THEN 220
    390 INPUT "ENTER WIN OPTION - 1 TO TAKE LAST, 2 TO AVOID LAST: ";M
    410 IF M=1 THEN 430
    420 IF M<>2 THEN 390
    430 INPUT "ENTER MIN AND MAX ";A,B
    450 IF A>B THEN 430
    460 IF A<1 THEN 430
    470 IF A<>INT(A) THEN 430
    480 IF B<>INT(B) THEN 430
    490 INPUT "ENTER START OPTION - 1 COMPUTER FIRST, 2 YOU FIRST ";S
    500 PRINT:PRINT
    510 IF S=1 THEN 530
    520 IF S<>2 THEN 490
    530 C=A+B
    540 IF S=2 THEN 570
    550 GOSUB 600
    560 IF W=1 THEN 220
    570 GOSUB 810
    580 IF W=1 THEN 220
    590 GOTO 550
    600 Q=N
    610 IF M=1 THEN 630
    620 Q=Q-1
    630 IF M=1 THEN 680
    640 IF N>A THEN 720
    650 W=1
    660 PRINT "COMPUTER TAKES";N;"AND LOSES."
    670 RETURN
    680 IF N>B THEN 720
    690 W=1
    700 PRINT "COMPUTER TAKES";N;"AND WINS."
    710 RETURN
    720 P=Q-C*INT(Q/C)
    730 IF P>=A THEN 750
    740 P=A
    750 IF P<=B THEN 770
    760 P=B
    770 N=N-P
    780 PRINT "COMPUTER TAKES";P;"AND LEAVES";N
    790 W=0
    800 RETURN
    810 PRINT:PRINT "YOUR MOVE ";
    820 INPUT P
    830 IF P<>0 THEN 870
    840 PRINT "I TOLD YOU NOT TO USE ZERO! COMPUTER WINS BY FORFEIT."
    850 W=1
    860 RETURN
    870 IF P<>INT(P) THEN 920
    880 IF P>=A THEN 910
    890 IF P=N THEN 960
    900 GOTO 920
    910 IF P<=B THEN 940
    920 PRINT "ILLEGAL MOVE, REENTER IT ";
    930 GOTO 820
    940 N=N-P
    950 IF N<>0 THEN 1030
    960 IF M=1 THEN 1000
    970 PRINT "TOUGH LUCK, YOU LOSE."
    980 W=1
    990 RETURN
    1000 PRINT "CONGRATULATIONS, YOU WIN."
    1010 W=1
    1020 RETURN
    1030 IF N>=0 THEN 1060
    1040 N=N+P
    1050 GOTO 920
    1060 W=0
    1070 RETURN
    1080 END
    
    
    ================================================
    FILE: 08_Batnum/csharp/Batnum.csproj
    ================================================
    
    
      
        Exe
        net5.0
        en-US
      
    
      
        
          True
          True
          Resources.resx
        
      
    
      
        
          ResXFileCodeGenerator
          Resources.Designer.cs
        
      
    
    
    
    
    ================================================
    FILE: 08_Batnum/csharp/Batnum.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.31019.35
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Batnum", "Batnum.csproj", "{64F32165-9D67-42B1-B04C-953CC756A170}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{64F32165-9D67-42B1-B04C-953CC756A170}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{64F32165-9D67-42B1-B04C-953CC756A170}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{64F32165-9D67-42B1-B04C-953CC756A170}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{64F32165-9D67-42B1-B04C-953CC756A170}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {73E40CC2-0E4E-48CF-8BDD-D6B6E995C14F}
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 08_Batnum/csharp/BatnumGame.cs
    ================================================
    using Batnum.Properties;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace Batnum
    {
        public enum WinOptions
        {
            /// 
            /// Last person to play wins
            /// 
            WinWithTakeLast = 1,
            /// 
            /// Last person to play loses
            /// 
            WinWithAvoidLast = 2
        }
    
        public enum Players
        {
            Computer = 1,
            Human = 2
        }
    
        public class BatnumGame
        {
            public BatnumGame(int pileSize, WinOptions winCriteria, int minTake, int maxtake, Players firstPlayer, FuncaskPlayerCallback)
            {
                this.pileSize = pileSize;
                this.winCriteria = winCriteria;
                this.minTake = minTake;
                this.maxTake = maxtake;
                this.currentPlayer = firstPlayer;
                this.askPlayerCallback = askPlayerCallback;
            }
    
            private int pileSize;
            private WinOptions winCriteria;
            private int minTake;
            private int maxTake;
            private Players currentPlayer;
            private Func askPlayerCallback;
    
            /// 
            /// Returns true if the game is running
            /// 
            public bool IsRunning => pileSize > 0;
    
            /// 
            /// Takes the next turn
            /// 
            /// A message to be displayed to the player
            public string TakeTurn()
            {
                //Edge condition - can occur when minTake is more > 1
                if (pileSize < minTake)
                {
                    pileSize = 0;
                    return string.Format(Resources.END_DRAW, minTake);
                }
                return currentPlayer == Players.Computer ? ComputerTurn() : PlayerTurn();
            }
    
            private string PlayerTurn()
            {
                int draw = askPlayerCallback(Resources.INPUT_TURN);
                if (draw == 0)
                {
                    pileSize = 0;
                    return Resources.INPUT_ZERO;
                }
                if (draw < minTake || draw > maxTake || draw > pileSize)
                {
                    return Resources.INPUT_ILLEGAL;
                }
                pileSize = pileSize - draw;
                if (pileSize == 0)
                {
                    return winCriteria == WinOptions.WinWithTakeLast ? Resources.END_PLAYERWIN : Resources.END_PLAYERLOSE;
                }
                currentPlayer = Players.Computer;
                return "";
            }
    
            private string ComputerTurn()
            {
                //first calculate the move to play
                int sumTake = minTake + maxTake;
                int draw = pileSize - sumTake * (int)(pileSize / (float)sumTake);
                draw = Math.Clamp(draw, minTake, maxTake);
    
                //detect win/lose conditions
                switch (winCriteria)
                {
                    case WinOptions.WinWithAvoidLast when (pileSize == minTake): //lose condition
                        pileSize = 0;
                        return string.Format(Resources.END_COMPLOSE, minTake);
                    case WinOptions.WinWithAvoidLast when (pileSize <= maxTake): //avoid automatic loss on next turn
                        draw = Math.Clamp(draw, minTake, pileSize - 1);
                        break;
                    case WinOptions.WinWithTakeLast when pileSize <= maxTake: // win condition
                        draw = Math.Min(pileSize, maxTake);
                        pileSize = 0;
                        return string.Format(Resources.END_COMPWIN, draw);
                }
                pileSize -= draw;
                currentPlayer = Players.Human;
                return string.Format(Resources.COMPTURN, draw, pileSize);
            }
        }
    }
    
    
    ================================================
    FILE: 08_Batnum/csharp/ConsoleUtilities.cs
    ================================================
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace Batnum
    {
        public static class ConsoleUtilities
        {
            /// 
            /// Ask the user a question and expects a comma separated pair of numbers representing a number range in response
            /// the range provided must have a maximum which is greater than the minimum
            /// 
            /// The question to ask
            /// The minimum value expected
            /// The maximum value expected
            /// A pair of numbers representing the minimum and maximum of the range
            public static (int min, int max) AskNumberRangeQuestion(string question, Func Validate)
            {
                while (true)
                {
                    Console.Write(question);
                    Console.Write(" ");
                    string[] rawInput = Console.ReadLine().Split(',');
                    if (rawInput.Length == 2)
                    {
                        if (int.TryParse(rawInput[0], out int min) && int.TryParse(rawInput[1], out int max))
                        {
                            if (Validate(min, max))
                            {
                                return (min, max);
                            }
                        }
                    }
                    Console.WriteLine();
                }
            }
    
            /// 
            /// Ask the user a question and expects a number in response
            /// 
            /// The question to ask
            /// A minimum value expected
            /// A maximum value expected
            /// The number the user entered
            public static int AskNumberQuestion(string question, Func Validate)
            {
                while (true)
                {
                    Console.Write(question);
                    Console.Write(" ");
                    string rawInput = Console.ReadLine();
                    if (int.TryParse(rawInput, out int number))
                    {
                        if (Validate(number))
                        {
                            return number;
                        }
                    }
                    Console.WriteLine();
                }
            }
    
            /// 
            /// Align content to center of console.
            /// 
            /// Content to center
            /// Center aligned text
            public static string CenterText(string content)
            {
                int windowWidth = Console.WindowWidth;
                return String.Format("{0," + ((windowWidth / 2) + (content.Length / 2)) + "}", content);
            }
    
            /// 
            ///     Writes the specified data, followed by the current line terminator, to the standard output stream, while wrapping lines that would otherwise break words.
            ///     source: https://stackoverflow.com/questions/20534318/make-console-writeline-wrap-words-instead-of-letters
            /// 
            /// The value to write.
            /// The value that indicates the column width of tab characters.
            public static void WriteLineWordWrap(string paragraph, int tabSize = 4)
            {
                string[] lines = paragraph
                    .Replace("\t", new String(' ', tabSize))
                    .Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
    
                for (int i = 0; i < lines.Length; i++)
                {
                    string process = lines[i];
                    List wrapped = new List();
    
                    while (process.Length > Console.WindowWidth)
                    {
                        int wrapAt = process.LastIndexOf(' ', Math.Min(Console.WindowWidth - 1, process.Length));
                        if (wrapAt <= 0) break;
    
                        wrapped.Add(process.Substring(0, wrapAt));
                        process = process.Remove(0, wrapAt + 1);
                    }
    
                    foreach (string wrap in wrapped)
                    {
                        Console.WriteLine(wrap);
                    }
    
                    Console.WriteLine(process);
                }
            }
        }
    }
    
    
    ================================================
    FILE: 08_Batnum/csharp/Program.cs
    ================================================
    using Batnum;
    using Batnum.Properties;
    using System;
    
    Console.WriteLine(ConsoleUtilities.CenterText(Resources.GAME_NAME));
    Console.WriteLine(ConsoleUtilities.CenterText(Resources.INTRO_HEADER));
    Console.WriteLine();
    Console.WriteLine();
    Console.WriteLine();
    ConsoleUtilities.WriteLineWordWrap(Resources.INTRO_PART1);
    Console.WriteLine();
    ConsoleUtilities.WriteLineWordWrap(Resources.INTRO_PART2);
    
    while (true)
    {
        Console.WriteLine();
        int pileSize = ConsoleUtilities.AskNumberQuestion(Resources.START_QUESTION_PILESIZE, (n) => n > 1);
        WinOptions winOption = (WinOptions)ConsoleUtilities.AskNumberQuestion(Resources.START_QUESTION_WINOPTION, (n) => Enum.IsDefined(typeof(WinOptions), n));
        (int minTake, int maxTake) = ConsoleUtilities.AskNumberRangeQuestion(Resources.START_QUESTION_DRAWMINMAX, (min,max) => min >= 1 && max < pileSize && max > min);
        Players currentPlayer = (Players)ConsoleUtilities.AskNumberQuestion(Resources.START_QUESTION_WHOSTARTS, (n) => Enum.IsDefined(typeof(Players), n));
    
        BatnumGame game = new BatnumGame(pileSize, winOption, minTake, maxTake, currentPlayer, (question) => ConsoleUtilities.AskNumberQuestion(question, (c) => true));
        while(game.IsRunning)
        {
            string message = game.TakeTurn();
            Console.WriteLine(message);
        }
    
    }
    
    
    ================================================
    FILE: 08_Batnum/csharp/Properties/Resources.Designer.cs
    ================================================
    //------------------------------------------------------------------------------
    // 
    //     This code was generated by a tool.
    //     Runtime Version:4.0.30319.42000
    //
    //     Changes to this file may cause incorrect behavior and will be lost if
    //     the code is regenerated.
    // 
    //------------------------------------------------------------------------------
    
    namespace Batnum.Properties {
        using System;
    
    
        /// 
        ///   A strongly-typed resource class, for looking up localized strings, etc.
        /// 
        // This class was auto-generated by the StronglyTypedResourceBuilder
        // class via a tool like ResGen or Visual Studio.
        // To add or remove a member, edit your .ResX file then rerun ResGen
        // with the /str option, or rebuild your VS project.
        [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
        [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
        [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
        internal class Resources {
    
            private static global::System.Resources.ResourceManager resourceMan;
    
            private static global::System.Globalization.CultureInfo resourceCulture;
    
            [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
            internal Resources() {
            }
    
            /// 
            ///   Returns the cached ResourceManager instance used by this class.
            /// 
            [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
            internal static global::System.Resources.ResourceManager ResourceManager {
                get {
                    if (object.ReferenceEquals(resourceMan, null)) {
                        global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Batnum.Properties.Resources", typeof(Resources).Assembly);
                        resourceMan = temp;
                    }
                    return resourceMan;
                }
            }
    
            /// 
            ///   Overrides the current thread's CurrentUICulture property for all
            ///   resource lookups using this strongly typed resource class.
            /// 
            [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
            internal static global::System.Globalization.CultureInfo Culture {
                get {
                    return resourceCulture;
                }
                set {
                    resourceCulture = value;
                }
            }
    
            /// 
            ///   Looks up a localized string similar to COMPUTER TAKES {0} AND LEAVES {1}.
            /// 
            internal static string COMPTURN {
                get {
                    return ResourceManager.GetString("COMPTURN", resourceCulture);
                }
            }
    
            /// 
            ///   Looks up a localized string similar to COMPUTER TAKES {0} AND LOSES.
            /// 
            internal static string END_COMPLOSE {
                get {
                    return ResourceManager.GetString("END_COMPLOSE", resourceCulture);
                }
            }
    
            /// 
            ///   Looks up a localized string similar to COMPUTER TAKES {0} AND WINS.
            /// 
            internal static string END_COMPWIN {
                get {
                    return ResourceManager.GetString("END_COMPWIN", resourceCulture);
                }
            }
    
            /// 
            ///   Looks up a localized string similar to ITS A DRAW, THERE ARE ONLY {0} PIECES LEFT.
            /// 
            internal static string END_DRAW {
                get {
                    return ResourceManager.GetString("END_DRAW", resourceCulture);
                }
            }
    
            /// 
            ///   Looks up a localized string similar to TOUGH LUCK, YOU LOSE..
            /// 
            internal static string END_PLAYERLOSE {
                get {
                    return ResourceManager.GetString("END_PLAYERLOSE", resourceCulture);
                }
            }
    
            /// 
            ///   Looks up a localized string similar to CONGRATULATIONS, YOU WIN..
            /// 
            internal static string END_PLAYERWIN {
                get {
                    return ResourceManager.GetString("END_PLAYERWIN", resourceCulture);
                }
            }
    
            /// 
            ///   Looks up a localized string similar to BATNUM.
            /// 
            internal static string GAME_NAME {
                get {
                    return ResourceManager.GetString("GAME_NAME", resourceCulture);
                }
            }
    
            /// 
            ///   Looks up a localized string similar to ILLEGAL MOVE, RENETER IT.
            /// 
            internal static string INPUT_ILLEGAL {
                get {
                    return ResourceManager.GetString("INPUT_ILLEGAL", resourceCulture);
                }
            }
    
            /// 
            ///   Looks up a localized string similar to YOUR MOVE ?.
            /// 
            internal static string INPUT_TURN {
                get {
                    return ResourceManager.GetString("INPUT_TURN", resourceCulture);
                }
            }
    
            /// 
            ///   Looks up a localized string similar to I TOLD YOU NOT TO USE ZERO! COMPUTER WINS BY FORFEIT..
            /// 
            internal static string INPUT_ZERO {
                get {
                    return ResourceManager.GetString("INPUT_ZERO", resourceCulture);
                }
            }
    
            /// 
            ///   Looks up a localized string similar to CREATIVE COMPUTING MORRISTOWN, NEW JERSEY.
            /// 
            internal static string INTRO_HEADER {
                get {
                    return ResourceManager.GetString("INTRO_HEADER", resourceCulture);
                }
            }
    
            /// 
            ///   Looks up a localized string similar to THIS PROGRAM IS A 'BATTLE' OF NUMBERS GAME, WHERE THE COMPUTER IS YOUR OPPONENT.
            /// 
            internal static string INTRO_PART1 {
                get {
                    return ResourceManager.GetString("INTRO_PART1", resourceCulture);
                }
            }
    
            /// 
            ///   Looks up a localized string similar to THE GAME STARTS WITH AN ASSUMED PILE OF OBJECTS. YOU AND YOUR OPPONENT ALTERNATELY REMOVE OBJECTS FROM THE PILE. WINNNING IS DEFINED IN ADVANCE AS TAKING THE LAST OBJECT OR NOT. YOU CAN ALSO SPECIFY SOME OTHER BEGINING CONDITIONS. DON'T USER ZERO, HOWWEVER, IN PLAYING THE GAME..
            /// 
            internal static string INTRO_PART2 {
                get {
                    return ResourceManager.GetString("INTRO_PART2", resourceCulture);
                }
            }
    
            /// 
            ///   Looks up a localized string similar to ENTER MIN AND MAX ?.
            /// 
            internal static string START_QUESTION_DRAWMINMAX {
                get {
                    return ResourceManager.GetString("START_QUESTION_DRAWMINMAX", resourceCulture);
                }
            }
    
            /// 
            ///   Looks up a localized string similar to ENTER PILE SIZE ?.
            /// 
            internal static string START_QUESTION_PILESIZE {
                get {
                    return ResourceManager.GetString("START_QUESTION_PILESIZE", resourceCulture);
                }
            }
    
            /// 
            ///   Looks up a localized string similar to ENTER START OPTION - 1 COMPUTER FIRST, 2 YOU FIRST ?.
            /// 
            internal static string START_QUESTION_WHOSTARTS {
                get {
                    return ResourceManager.GetString("START_QUESTION_WHOSTARTS", resourceCulture);
                }
            }
    
            /// 
            ///   Looks up a localized string similar to ENTER WIN OPTION - 1 TO TAKE LAST, 2 TO AVOID LAST: ?.
            /// 
            internal static string START_QUESTION_WINOPTION {
                get {
                    return ResourceManager.GetString("START_QUESTION_WINOPTION", resourceCulture);
                }
            }
        }
    }
    
    
    ================================================
    FILE: 08_Batnum/csharp/Properties/Resources.en.resx
    ================================================
    
    
      
      
        
        
          
            
              
                
                  
                    
                  
                  
                  
                  
                  
                
              
              
                
                  
                  
                
              
              
                
                  
                    
                    
                  
                  
                  
                  
                  
                
              
              
                
                  
                    
                  
                  
                
              
            
          
        
      
      
        text/microsoft-resx
      
      
        2.0
      
      
        System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
      
      
        System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
      
      
        COMPUTER TAKES {0} AND LEAVES {1}
      
      
        COMPUTER TAKES {0} AND LOSES
      
      
        COMPUTER TAKES {0} AND WINS
      
      
        ITS A DRAW, THERE ARE ONLY {0} PIECES LEFT
      
      
        TOUGH LUCK, YOU LOSE.
      
      
        CONGRATULATIONS, YOU WIN.
      
      
        BATNUM
      
      
        ILLEGAL MOVE, RENETER IT
      
      
        YOUR MOVE ?
      
      
        I TOLD YOU NOT TO USE ZERO! COMPUTER WINS BY FORFEIT.
      
      
        CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
      
      
        THIS PROGRAM IS A 'BATTLE' OF NUMBERS GAME, WHERE THE COMPUTER IS YOUR OPPONENT
      
      
        THE GAME STARTS WITH AN ASSUMED PILE OF OBJECTS. YOU AND YOUR OPPONENT ALTERNATELY REMOVE OBJECTS FROM THE PILE. WINNNING IS DEFINED IN ADVANCE AS TAKING THE LAST OBJECT OR NOT. YOU CAN ALSO SPECIFY SOME OTHER BEGINING CONDITIONS. DON'T USER ZERO, HOWWEVER, IN PLAYING THE GAME.
      
      
        ENTER MIN AND MAX ?
      
      
        ENTER PILE SIZE ?
      
      
        ENTER START OPTION - 1 COMPUTER FIRST, 2 YOU FIRST ?
      
      
        ENTER WIN OPTION - 1 TO TAKE LAST, 2 TO AVOID LAST: ?
      
    
    
    
    ================================================
    FILE: 08_Batnum/csharp/Properties/Resources.fr.resx
    ================================================
    
    
      
      
        
        
          
            
              
                
                  
                    
                  
                  
                  
                  
                  
                
              
              
                
                  
                  
                
              
              
                
                  
                    
                    
                  
                  
                  
                  
                  
                
              
              
                
                  
                    
                  
                  
                
              
            
          
        
      
      
        text/microsoft-resx
      
      
        2.0
      
      
        System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
      
      
        System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
      
      
        L'ORDINATEUR PREND {0} ET LAISSE {1}
      
      
        L'ORDINATEUR PREND {0} ET PERD
      
      
        L'ORDINATEUR PREND {0} ET GAGNE
      
      
        MATCH NUL, IL RESTE SEULEMENT {0} PIECES
      
      
        PAS DE CHANCE. TU AS PERDU.
      
      
        BRAVO! TU AS GAGNE
      
      
        BATNUM
      
      
        CE COUP EST INTERDIT. RE-ESSAIE
      
      
        TON TOUR ?
      
      
        JE TE DIS DE NE PAS UTILISER LE ZERO! L'ORDINATEUR GAGNE PAR DEFAUT.
      
      
        CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
      
      
        CE PROGRAMME EST UN JEU 'BATAILLE' DE NOMBRES, OU L'ORDINATEUR EST TON ADVERSAIRE
      
      
        LE JEU COMMENCE AVEC UNE NOMBRE D'OBJETS DEFINI. TOI ET TON ADVERSAIRE ENLEVEZ EN ALTERNANCE UN NOMBRE D'OBJETS. LES CONDITIONS DE VICTOIRE SON DÉFINIES À L'AVANCE COMME PRENDRE OU NE PAS PRENDRE LE DERNIER OBJET. VOUS POUVEZ ÉGALEMENT PRÉCISER D'AUTRES CONDITIONS DES LE DÉBUT. TOUTEFOIS, N'UTILISEZ PAS LE CHIFFRE ZERO.
      
      
        ENTRE LE MINIMUM ET MAXIMUM D'OBJETS A RETIRER POUR CHAQUE TOUR ?
      
      
        ENTRE LE NOMBRE D'OBJETS ?
      
      
        ENTRE QUI COMMENCE - 1 L'ORDINATEUR, 2 TOI ?
      
      
        ENTRE LA CONDITION DE VICTOIRE - 1 PRENDRE LA DERNIERE PIECE, 2 EVITER LA DERNIERE PIECE ?
      
    
    
    
    ================================================
    FILE: 08_Batnum/csharp/Properties/Resources.resx
    ================================================
    
    
      
      
        
        
          
            
              
                
                  
                    
                  
                  
                  
                  
                  
                
              
              
                
                  
                  
                
              
              
                
                  
                    
                    
                  
                  
                  
                  
                  
                
              
              
                
                  
                    
                  
                  
                
              
            
          
        
      
      
        text/microsoft-resx
      
      
        2.0
      
      
        System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
      
      
        System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
      
      
        COMPUTER TAKES {0} AND LEAVES {1}
      
      
        COMPUTER TAKES {0} AND LOSES
      
      
        COMPUTER TAKES {0} AND WINS
      
      
        ITS A DRAW, THERE ARE ONLY {0} PIECES LEFT
      
      
        TOUGH LUCK, YOU LOSE.
      
      
        CONGRATULATIONS, YOU WIN.
      
      
        BATNUM
      
      
        ILLEGAL MOVE, RENETER IT
      
      
        YOUR MOVE ?
      
      
        I TOLD YOU NOT TO USE ZERO! COMPUTER WINS BY FORFEIT.
      
      
        CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
      
      
        THIS PROGRAM IS A 'BATTLE' OF NUMBERS GAME, WHERE THE COMPUTER IS YOUR OPPONENT
      
      
        THE GAME STARTS WITH AN ASSUMED PILE OF OBJECTS. YOU AND YOUR OPPONENT ALTERNATELY REMOVE OBJECTS FROM THE PILE. WINNNING IS DEFINED IN ADVANCE AS TAKING THE LAST OBJECT OR NOT. YOU CAN ALSO SPECIFY SOME OTHER BEGINING CONDITIONS. DON'T USER ZERO, HOWWEVER, IN PLAYING THE GAME.
      
      
        ENTER MIN AND MAX ?
      
      
        ENTER PILE SIZE ?
      
      
        ENTER START OPTION - 1 COMPUTER FIRST, 2 YOU FIRST ?
      
      
        ENTER WIN OPTION - 1 TO TAKE LAST, 2 TO AVOID LAST: ?
      
    
    
    
    ================================================
    FILE: 08_Batnum/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    This conversion uses C#9 and is built for .net 5.0
    
    Functional changes from Original
    - handle edge condition for end game where the minimum draw amount is greater than the number of items remaining in the pile
    - Takes into account the width of the console
    - Mulilingual Support (English/French currently)
    
    
    ================================================
    FILE: 08_Batnum/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 08_Batnum/java/src/BatNum.java
    ================================================
    import java.util.Arrays;
    import java.util.Scanner;
    
    /**
     * Game of BatNum
     * 

    * Based on the Basic game of BatNum here * https://github.com/coding-horror/basic-computer-games/blob/main/08%20Batnum/batnum.bas *

    * Note: The idea was to create a version of the 1970's Basic game in Java, without introducing * new features - no additional text, error checking, etc has been added. */ public class BatNum { private enum GAME_STATE { STARTING, START_GAME, CHOOSE_PILE_SIZE, SELECT_WIN_OPTION, CHOOSE_MIN_AND_MAX, SELECT_WHO_STARTS_FIRST, PLAYERS_TURN, COMPUTERS_TURN, ANNOUNCE_WINNER, GAME_OVER } // Used for keyboard input private final Scanner kbScanner; // Current game state private GAME_STATE gameState; private int pileSize; // How to win the game options enum WIN_OPTION { TAKE_LAST, AVOID_LAST } // Tracking the winner enum WINNER { COMPUTER, PLAYER } private WINNER winner; private WIN_OPTION winOption; private int minSelection; private int maxSelection; // Used by computer for optimal move private int rangeOfRemovals; public BatNum() { gameState = GAME_STATE.STARTING; // Initialise kb scanner kbScanner = new Scanner(System.in); } /** * Main game loop */ public void play() { do { switch (gameState) { // Show an introduction and optional instructions the first time the game is played. case STARTING: intro(); gameState = GAME_STATE.START_GAME; break; // Start new game case START_GAME: gameState = GAME_STATE.CHOOSE_PILE_SIZE; break; case CHOOSE_PILE_SIZE: System.out.println(); System.out.println(); pileSize = displayTextAndGetNumber("ENTER PILE SIZE "); if (pileSize >= 1) { gameState = GAME_STATE.SELECT_WIN_OPTION; } break; case SELECT_WIN_OPTION: int winChoice = displayTextAndGetNumber("ENTER WIN OPTION - 1 TO TAKE LAST, 2 TO AVOID LAST: "); if (winChoice == 1) { winOption = WIN_OPTION.TAKE_LAST; gameState = GAME_STATE.CHOOSE_MIN_AND_MAX; } else if (winChoice == 2) { winOption = WIN_OPTION.AVOID_LAST; gameState = GAME_STATE.CHOOSE_MIN_AND_MAX; } break; case CHOOSE_MIN_AND_MAX: String range = displayTextAndGetInput("ENTER MIN AND MAX "); minSelection = getDelimitedValue(range, 0); maxSelection = getDelimitedValue(range, 1); if (maxSelection > minSelection && minSelection >= 1) { gameState = GAME_STATE.SELECT_WHO_STARTS_FIRST; } // Used by computer in its turn rangeOfRemovals = minSelection + maxSelection; break; case SELECT_WHO_STARTS_FIRST: int playFirstChoice = displayTextAndGetNumber("ENTER START OPTION - 1 COMPUTER FIRST, 2 YOU FIRST "); if (playFirstChoice == 1) { gameState = GAME_STATE.COMPUTERS_TURN; } else if (playFirstChoice == 2) { gameState = GAME_STATE.PLAYERS_TURN; } break; case PLAYERS_TURN: int playersMove = displayTextAndGetNumber("YOUR MOVE "); if (playersMove == 0) { System.out.println("I TOLD YOU NOT TO USE ZERO! COMPUTER WINS BY FORFEIT."); winner = WINNER.COMPUTER; gameState = GAME_STATE.ANNOUNCE_WINNER; break; } if (playersMove == pileSize && winOption == WIN_OPTION.AVOID_LAST) { winner = WINNER.COMPUTER; gameState = GAME_STATE.ANNOUNCE_WINNER; break; } // Check if players move is with the min and max possible if (playersMove >= minSelection && playersMove <= maxSelection) { // Valid so reduce pileSize by amount player entered pileSize -= playersMove; // Did this move result in there being no more objects on pile? if (pileSize == 0) { // Was the game setup so the winner was whoever took the last object if (winOption == WIN_OPTION.TAKE_LAST) { // Player won winner = WINNER.PLAYER; } else { // Computer one winner = WINNER.COMPUTER; } gameState = GAME_STATE.ANNOUNCE_WINNER; } else { // There are still items left. gameState = GAME_STATE.COMPUTERS_TURN; } } else { // Invalid move System.out.println("ILLEGAL MOVE, REENTER IT "); } break; case COMPUTERS_TURN: int pileSizeLeft = pileSize; if (winOption == WIN_OPTION.TAKE_LAST) { if (pileSize > maxSelection) { int objectsToRemove = calculateComputersTurn(pileSizeLeft); pileSize -= objectsToRemove; System.out.println("COMPUTER TAKES " + objectsToRemove + " AND LEAVES " + pileSize); gameState = GAME_STATE.PLAYERS_TURN; } else { System.out.println("COMPUTER TAKES " + pileSize + " AND WINS."); winner = WINNER.COMPUTER; gameState = GAME_STATE.ANNOUNCE_WINNER; } } else { pileSizeLeft--; if (pileSize > minSelection) { int objectsToRemove = calculateComputersTurn(pileSizeLeft); pileSize -= objectsToRemove; System.out.println("COMPUTER TAKES " + objectsToRemove + " AND LEAVES " + pileSize); gameState = GAME_STATE.PLAYERS_TURN; } else { System.out.println("COMPUTER TAKES " + pileSize + " AND LOSES."); winner = WINNER.PLAYER; gameState = GAME_STATE.ANNOUNCE_WINNER; } } break; case ANNOUNCE_WINNER: switch (winner) { case PLAYER: System.out.println("CONGRATULATIONS, YOU WIN."); break; case COMPUTER: System.out.println("TOUGH LUCK, YOU LOSE."); break; } gameState = GAME_STATE.START_GAME; break; } } while (gameState != GAME_STATE.GAME_OVER); } /** * Figure out the computers turn - i.e. how many objects to remove * * @param pileSizeLeft current size * @return the number of objects to remove. */ private int calculateComputersTurn(int pileSizeLeft) { int computersNumberToRemove = pileSizeLeft - rangeOfRemovals * (pileSizeLeft / rangeOfRemovals); if (computersNumberToRemove < minSelection) { computersNumberToRemove = minSelection; } if (computersNumberToRemove > maxSelection) { computersNumberToRemove = maxSelection; } return computersNumberToRemove; } private void intro() { System.out.println(simulateTabs(33) + "BATNUM"); System.out.println(simulateTabs(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println(); System.out.println("THIS PROGRAM IS A 'BATTLE OF NUMBERS' GAME, WHERE THE"); System.out.println("COMPUTER IS YOUR OPPONENT."); System.out.println(); System.out.println("THE GAME STARTS WITH AN ASSUMED PILE OF OBJECTS. YOU"); System.out.println("AND YOUR OPPONENT ALTERNATELY REMOVE OBJECTS FROM THE PILE."); System.out.println("WINNING IS DEFINED IN ADVANCE AS TAKING THE LAST OBJECT OR"); System.out.println("NOT. YOU CAN ALSO SPECIFY SOME OTHER BEGINNING CONDITIONS."); System.out.println("DON'T USE ZERO, HOWEVER, IN PLAYING THE GAME."); System.out.println("ENTER A NEGATIVE NUMBER FOR NEW PILE SIZE TO STOP PLAYING."); } /* * Print a message on the screen, then accept input from Keyboard. * Converts input to Integer * * @param text message to be displayed on screen. * @return what was typed by the player. */ private int displayTextAndGetNumber(String text) { return Integer.parseInt(displayTextAndGetInput(text)); } /* * Print a message on the screen, then accept input from Keyboard. * * @param text message to be displayed on screen. * @return what was typed by the player. */ private String displayTextAndGetInput(String text) { System.out.print(text); return kbScanner.next(); } /** * Simulate the old basic tab(xx) command which indented text by xx spaces. * * @param spaces number of spaces required * @return String with number of spaces */ private String simulateTabs(int spaces) { char[] spacesTemp = new char[spaces]; Arrays.fill(spacesTemp, ' '); return new String(spacesTemp); } /** * Accepts a string delimited by comma's and returns the nth delimited * value (starting at count 0). * * @param text - text with values separated by comma's * @param pos - which position to return a value for * @return the int representation of the value */ private int getDelimitedValue(String text, int pos) { String[] tokens = text.split(","); return Integer.parseInt(tokens[pos]); } } ================================================ FILE: 08_Batnum/java/src/BatNumGame.java ================================================ public class BatNumGame { public static void main(String[] args) { BatNum batNum = new BatNum(); batNum.play(); } } ================================================ FILE: 08_Batnum/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 08_Batnum/javascript/batnum.html ================================================ BATNUM

    
    
    
    
    
    
    ================================================
    FILE: 08_Batnum/javascript/batnum.js
    ================================================
    // BATNUM
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    // Main program
    async function main()
    {
        print(tab(33) + "BATNUM\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        print("THIS PROGRAM IS A 'BATTLE OF NUMBERS' GAME, WHERE THE\n");
        print("COMPUTER IS YOUR OPPONENT.\n");
        print("\n");
        print("THE GAME STARTS WITH AN ASSUMED PILE OF OBJECTS. YOU\n");
        print("AND YOUR OPPONENT ALTERNATELY REMOVE OBJECTS FROM THE PILE.\n");
        print("WINNING IS DEFINED IN ADVANCE AS TAKING THE LAST OBJECT OR\n");
        print("NOT. YOU CAN ALSO SPECIFY SOME OTHER BEGINNING CONDITIONS.\n");
        print("DON'T USE ZERO, HOWEVER, IN PLAYING THE GAME.\n");
        print("ENTER A NEGATIVE NUMBER FOR NEW PILE SIZE TO STOP PLAYING.\n");
        print("\n");
        first_time = 1;
        while (1) {
            while (1) {
                if (first_time == 1) {
                    first_time = 0;
                } else {
                    for (i = 1; i <= 10; i++)
                        print("\n");
                }
                print("ENTER PILE SIZE");
                n = parseInt(await input());
                if (n >= 1)
                    break;
            }
            while (1) {
                print("ENTER WIN OPTION - 1 TO TAKE LAST, 2 TO AVOID LAST: ");
                m = parseInt(await input());
                if (m == 1 || m == 2)
                    break;
            }
            while (1) {
                print("ENTER MIN AND MAX ");
                str = await input();
                a = parseInt(str);
                b = parseInt(str.substr(str.indexOf(",") + 1));
                if (a <= b && a >= 1)
                    break;
            }
            while (1) {
                print("ENTER START OPTION - 1 COMPUTER FIRST, 2 YOU FIRST ");
                s = parseInt(await input());
                print("\n");
                print("\n");
                if (s == 1 || s == 2)
                    break;
            }
            w = 0;
            c = a + b;
            while (1) {
                if (s == 1) {
                    // Computer's turn
                    q = n;
                    if (m != 1)
                        q--;
                    if (m != 1 && n <= a) {
                        w = 1;
                        print("COMPUTER TAKES " + n + " AND LOSES.\n");
                    } else if (m == 1 && n <= b) {
                        w = 1;
                        print("COMPUTER TAKES " + n + " AND WINS.\n");
                    } else {
                        p = q - c * Math.floor(q / c);
                        if (p < a)
                            p = a;
                        if (p > b)
                            p = b;
                        n -= p;
                        print("COMPUTER TAKES " + p + " AND LEAVES " + n + "\n");
                        w = 0;
                    }
                    s = 2;
                }
                if (w)
                    break;
                if (s == 2) {
                    while (1) {
                        print("\n");
                        print("YOUR MOVE ");
                        p = parseInt(await input());
                        if (p == 0) {
                            print("I TOLD YOU NOT TO USE ZERO! COMPUTER WINS BY FORFEIT.\n");
                            w = 1;
                            break;
                        } else if (p >= a && p <= b && n - p >= 0) {
                            break;
                        }
                    }
                    if (p != 0) {
                        n -= p;
                        if (n == 0) {
                            if (m != 1) {
                                print("TOUGH LUCK, YOU LOSE.\n");
                            } else {
                                print("CONGRATULATIONS, YOU WIN.\n");
                            }
                            w = 1;
                        } else {
                            w = 0;
                        }
                    }
                    s = 1;
                }
                if (w)
                    break;
            }
        }
    }
    
    main();
    
    
    ================================================
    FILE: 08_Batnum/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 08_Batnum/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 08_Batnum/pascal/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Pascal](https://en.wikipedia.org/wiki/Pascal_(programming_language))
    
    
    ================================================
    FILE: 08_Batnum/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/) by [Austin White](https://github.com/austinwhite)
    
    
    ================================================
    FILE: 08_Batnum/perl/batnum.pl
    ================================================
    use strict;
    use warnings;
    
    my %WIN_OPTIONS = (
        "UNDEFINED" => 0,
        "TAKE_LAST" => 1,
        "AVOID_LAST" => 2,
    );
    
    my %START_OPTIONS = (
        "UNDEFINED" => 0,
        "COMPUTER_FIRST" => 1,
        "PLAYER_FIRST" => 2,
    );
    
    
    sub run {
        # input:        no input perameters
        #
        # output:       nothing returned
        #
        # description:  This is the primary game loop. Once a game is concluded
        #               another will begin right away until the exeecution
        #               is terminated.
    
        my $pile_size = undef;
        my $min_select = undef;
        my $max_select = undef;
        my $win_option = undef;
        my $start_option = undef;
    
        while (1) {
            write_intro();
    
            ($pile_size, $min_select, $max_select, $win_option, $start_option)
                = &get_user_input();
    
            if ($pile_size < 0) {
                last;
            }
    
            play($pile_size, $min_select, $max_select, $win_option, $start_option);
    
            printf "\n";
        }
    }
    
    
    sub write_intro {
        # input:        no input perameters
        #
        # output:       nothing returned
        #
        # description:  This subroutine prints the intro and rules.
    
        printf "%33s", "BATNUM\n";
        printf "%15s", "CREATIVE COMPUTING  MORRISSTOWN, NEW JERSEY\n";
        printf "%s", "\n";
        printf "%s", "\n";
        printf "%s", "\n";
        printf "%s", "THIS PROGRAM IS A 'BATTLE OF NUMBERS' GAME, WHERE THE\n";
        printf "%s", "COMPUTER IS YOUR OPPONENT.\n";
        printf "%s", "\n";
        printf "%s", "THE GAME STARTS WITH AN ASSUMED PILE OF OBJECTS. YOU\n";
        printf "%s", "AND YOUR OPPONENT ALTERNATELY REMOVE OBJECTS FROM THE PILE.\n";
        printf "%s", "WINNING IS DEFINED IN ADVANCE AS TAKING THE LAST OBJECT OR\n";
        printf "%s", "NOT. YOU CAN ALSO SPECIFY SOME OTHER BEGINNING CONDITIONS.\n";
        printf "%s", "DON'T USE ZERO, HOWEVER, IN PLAYING THE GAME.\n";
        printf "%s", "ENTER A NEGATIVE NUMBER FOR NEW PILE SIZE TO STOP PLAYING.\n";
        printf "%s", "\n";
    }
    
    
    sub get_user_input {
        # input:        no input perameters
        #
        # output:       (int) pile_size
        #               (int) min_select
        #               (int) max_select
        #               (int) win_option
        #               (int) start_option
        #
        # description:  This subroutine gets the necessary perametes from the player.
        #
        #               pile_size (int > 0)
        #               min_select (int > 0) max_select (int > 0)
        #                   -> min/max, space delimated
        #               win_option (int 1|2)
        #               start_option (int 1|2)
    
        my $pile_size = 0;
        my $min_select = 0;
        my $max_select = 0;
        my $win_option = $WIN_OPTIONS{ "UNDEFINED" };
        my $start_option = $START_OPTIONS{ "UNDEFINED" };
    
        while ($pile_size < 1) {
            printf "%s", "ENTER PILE SIZE: ";
            $pile_size = ;
            if ($pile_size < 0) {
                return ($pile_size);
            }
        }
    
        while ($min_select < 1 || $max_select < 1 || $min_select > $max_select) {
            printf "%s", "ENTER MIN AND MAX: ";
            my $raw_input = ;
            ($min_select, $max_select) = split(' ', $raw_input);
        }
    
        while ($win_option eq $WIN_OPTIONS{ "UNDEFINED" }) {
            printf "%s", "ENTER WIN OPTION - 1 TO TAKE LAST, 2 TO AVOID LAST: ";
            $win_option = ;
        }
    
        while ($start_option eq $START_OPTIONS{ "UNDEFINED" }) {
            printf "%s", "ENTER START OPTION - 1 COMPUTER FIRST, 2 YOU FIRST: ";
            $start_option = ;
        }
    
        return ($pile_size, $min_select, $max_select, $win_option, $start_option);
    }
    
    
    sub play {
        # input:        (int) pile_size
        #               (int) min_select
        #               (int) max_select
        #               (int) win_option
        #               (int) start_option
        #
        # output:       nothing returned
        #
        # description:  This is where the game logic lives. The player and computer
        #               both take turns until the current game is over.
    
        my $pile_size = shift;
        my $min_select = shift;
        my $max_select = shift;
        my $win_option = shift;
        my $start_option = shift;
    
        my $game_over = 0;
        my $players_turn = $start_option eq $START_OPTIONS{ "PLAYER_FIRST" } ? 1 : 0;
    
        while (!$game_over) {
            if ($players_turn) {
                ($game_over, $pile_size) = players_move(
                    $pile_size, $min_select, $max_select, $win_option);
    
                $players_turn = 0;
    
                if ($game_over) {
                    return;
                }
            } else {
                ($game_over, $pile_size) = computers_move(
                    $pile_size, $min_select, $max_select, $win_option);
    
                $players_turn = 1;
            }
        }
    
        return;
    }
    
    
    sub players_move {
        # input:        (int) pile_size
        #               (int) min_select
        #               (int) max_select
        #               (int) win_option
        #
        # output:       (boolean) game is over
        #               (int) new pile_size
        #
        # description:  This subroutine handles the players move.
    
        my $pile_size = shift;
        my $min_select = shift;
        my $max_select = shift;
        my $win_option = shift;
    
        my $finished = 0;
    
        while (!$finished) {
            my $remove_amount = undef;
    
            printf "%s", "YOUR MOVE: ";
            $remove_amount = <>;
    
            if ($remove_amount eq 0) {
                printf "%s", "I TOLD YOU NOT TO USE ZERO!  COMPUTER WINS BY FORFEIT.";
                return (1, $pile_size);
            } elsif ($remove_amount > $max_select || $remove_amount < $min_select) {
                printf "%s", "ILLEGAL MOVE, TRY AGAIN.\n";
                next;
            } else {
                $pile_size -= $remove_amount;
                $finished = 1;
            }
    
            if ($pile_size <= 0) {
                if ($win_option eq $WIN_OPTIONS{ "AVOID_LAST" }) {
                    printf "%s", "TOUGH LUCK, YOU LOSE.\n";
                } else {
                    printf "%s", "CONGRATULATIONS, YOU WIN.\n";
                }
                return (1, $pile_size);
            }
        }
    
        return (0, $pile_size);
    }
    
    
    sub computers_move {
        # input:        (int) pile_size
        #               (int) min_select
        #               (int) max_select
        #               (int) win_option
        #
        # output:       (boolean) game is over
        #               (int) new pile_size
        #
        # description:  This subroutine handles the computers move.
    
        my $pile_size = shift;
        my $min_select = shift;
        my $max_select = shift;
        my $win_option = shift;
    
        if ($win_option eq $WIN_OPTIONS{ "TAKE_LAST" } && $pile_size <= $max_select) {
            printf "COMPUTER TAKES %d AND WINS.\n", $pile_size;
            return (1, $pile_size);
        }
    
        if ($win_option eq $WIN_OPTIONS{ "AVOID_LAST" } && $pile_size <= $min_select) {
            printf "COMPUTER TAKES %d AND LOSES.\n", $pile_size;
            return (1, $pile_size);
        }
    
        my $remove_amount = get_computer_remove_amount($min_select, $max_select);
    
        $pile_size -= $remove_amount;
    
        if ($pile_size <= 0) {
            printf "COMPUTER TAKES %d AND WINS.\n", $remove_amount;
            return (1, $pile_size);
        } else {
            printf "COMPUTER TAKES %d AND LEAVES %d.\n", $remove_amount, $pile_size;
        }
    
        return (0, $pile_size);
    }
    
    
    sub get_computer_remove_amount {
        # input:        (int) min_select
        #               (int) max_select
        #
        # output:       (int) random number (x) where,
        #               min_select <= x <= max_select
        #
        # description:  This subroutine generates the amount of items the computer
        #               will remove.
    
        my $min_select = shift;
        my $max_select = shift;
    
        return (int(rand($max_select - $min_select)) + $min_select);
    }
    
    
    # start the game
    run();
    
    
    ================================================
    FILE: 08_Batnum/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 08_Batnum/python/batnum.py
    ================================================
    from enum import IntEnum
    from typing import Any, Tuple
    
    
    class WinOptions(IntEnum):
        Undefined = 0
        TakeLast = 1
        AvoidLast = 2
    
        @classmethod
        def _missing_(cls, value: Any) -> "WinOptions":
            try:
                int_value = int(value)
            except Exception:
                return WinOptions.Undefined
            if int_value == 1:
                return WinOptions.TakeLast
            elif int_value == 2:
                return WinOptions.AvoidLast
            else:
                return WinOptions.Undefined
    
    
    class StartOptions(IntEnum):
        Undefined = 0
        ComputerFirst = 1
        PlayerFirst = 2
    
        @classmethod
        def _missing_(cls, value: Any) -> "StartOptions":
            try:
                int_value = int(value)
            except Exception:
                return StartOptions.Undefined
            if int_value == 1:
                return StartOptions.ComputerFirst
            elif int_value == 2:
                return StartOptions.PlayerFirst
            else:
                return StartOptions.Undefined
    
    
    def print_intro() -> None:
        """Print out the introduction and rules for the game."""
        print("BATNUM".rjust(33, " "))
        print("CREATIVE COMPUTING  MORRISSTOWN, NEW JERSEY".rjust(15, " "))
        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()
        return
    
    
    def get_params() -> Tuple[int, int, int, StartOptions, WinOptions]:
        """This requests the necessary parameters to play the game.
    
        Returns a set with the 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 - 1 if the computer is first
                          or 2 if the player is first
            win_option - 1 if the goal is to take the last object
                        or 2 if the goal is to not take the last object
        """
        pile_size = get_pile_size()
        if pile_size < 0:
            return (-1, 0, 0, StartOptions.Undefined, WinOptions.Undefined)
        win_option = get_win_option()
        min_select, max_select = get_min_max()
        start_option = get_start_option()
        return (pile_size, min_select, max_select, start_option, win_option)
    
    
    def get_pile_size() -> int:
        # A negative number will stop the game.
        pile_size = 0
        while pile_size == 0:
            try:
                pile_size = int(input("ENTER PILE SIZE "))
            except ValueError:
                pile_size = 0
        return pile_size
    
    
    def get_win_option() -> WinOptions:
        win_option: WinOptions = WinOptions.Undefined
        while win_option == WinOptions.Undefined:
            win_option = WinOptions(input("ENTER WIN OPTION - 1 TO TAKE LAST, 2 TO AVOID LAST: "))  # type: ignore
        return win_option
    
    
    def get_min_max() -> Tuple[int, int]:
        min_select = 0
        max_select = 0
        while min_select < 1 or max_select < 1 or min_select > max_select:
            (min_select, max_select) = (
                int(x) for x in input("ENTER MIN AND MAX ").split(" ")
            )
        return min_select, max_select
    
    
    def get_start_option() -> StartOptions:
        start_option: StartOptions = StartOptions.Undefined
        while start_option == StartOptions.Undefined:
            start_option = StartOptions(input("ENTER START OPTION - 1 COMPUTER FIRST, 2 YOU FIRST "))  # type: ignore
        return start_option
    
    
    def player_move(
        pile_size: int, min_select: int, max_select: int, win_option: WinOptions
    ) -> Tuple[bool, int]:
        """This handles the player's turn - asking the player how many objects
        to take and doing some basic validation around that input.  Then it
        checks for any win conditions.
    
        Returns a boolean indicating whether the game is over and the new pile_size."""
        player_done = False
        while not player_done:
            player_move = int(input("YOUR MOVE "))
            if player_move == 0:
                print("I TOLD YOU NOT TO USE ZERO!  COMPUTER WINS BY FORFEIT.")
                return (True, pile_size)
            if player_move > max_select or player_move < min_select:
                print("ILLEGAL MOVE, REENTER IT")
                continue
            pile_size = pile_size - player_move
            player_done = True
            if pile_size <= 0:
                if win_option == WinOptions.AvoidLast:
                    print("TOUGH LUCK, YOU LOSE.")
                else:
                    print("CONGRATULATIONS, YOU WIN.")
                return (True, pile_size)
        return (False, pile_size)
    
    
    def computer_pick(
        pile_size: int, min_select: int, max_select: int, win_option: WinOptions
    ) -> int:
        """This handles the logic to determine how many objects the computer
        will select on its turn.
        """
        q = pile_size - 1 if win_option == WinOptions.AvoidLast else pile_size
        c = min_select + max_select
        computer_pick = q - (c * int(q / c))
        computer_pick = max(computer_pick, min_select)
        return min(computer_pick, max_select)
    
    
    def computer_move(
        pile_size: int, min_select: int, max_select: int, win_option: WinOptions
    ) -> Tuple[bool, int]:
        """This handles the computer's turn - first checking for the various
        win/lose conditions and then calculating how many objects
        the computer will take.
    
        Returns a boolean indicating whether the game is over and the new pile_size."""
        # First, check for win conditions on this move
        # In this case, we win by taking the last object and
        # the remaining pile is less than max select
        # so the computer can grab them all and win
        if win_option == WinOptions.TakeLast and pile_size <= max_select:
            print(f"COMPUTER TAKES {pile_size} AND WINS.")
            return (True, pile_size)
        # In this case, we lose by taking the last object and
        # the remaining pile is less than minsize and the computer
        # has to take all of them.
        if win_option == WinOptions.AvoidLast and pile_size <= min_select:
            print(f"COMPUTER TAKES {min_select} AND LOSES.")
            return (True, pile_size)
    
        # Otherwise, we determine how many the computer selects
        curr_sel = computer_pick(pile_size, min_select, max_select, win_option)
        pile_size -= curr_sel
        print(f"COMPUTER TAKES {curr_sel} AND LEAVES {pile_size}")
        return (False, pile_size)
    
    
    def play_game(
        pile_size: int,
        min_select: int,
        max_select: int,
        start_option: StartOptions,
        win_option: WinOptions,
    ) -> None:
        """This is the main game loop - repeating each turn until one
        of the win/lose conditions is met.
        """
        game_over = False
        # players_turn is a boolean keeping track of whether it's the
        # player's or computer's turn
        players_turn = start_option == StartOptions.PlayerFirst
    
        while not game_over:
            if players_turn:
                (game_over, pile_size) = player_move(
                    pile_size, min_select, max_select, win_option
                )
                players_turn = False
                if game_over:
                    return
            if not players_turn:
                (game_over, pile_size) = computer_move(
                    pile_size, min_select, max_select, win_option
                )
                players_turn = True
    
    
    def main() -> None:
        while True:
            print_intro()
            (pile_size, min_select, max_select, start_option, win_option) = get_params()
    
            if pile_size < 0:
                return
    
            # Just keep playing the game until the user kills it with ctrl-C
            play_game(pile_size, min_select, max_select, start_option, win_option)
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 08_Batnum/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 08_Batnum/ruby/batnum.rb
    ================================================
    #!/usr/bin/env ruby
    
    def instructions
        puts <<~EOF
        THIS PROGRAM IS A 'BATTLE OF NUMBERS' GAME, WHERE THE
        COMPUTER IS YOUR OPPONENT.
    
        THE GAME STARTS WITH AN ASSUMED PILE OF OBJECTS. YOU
        AND YOUR OPPONENT ALTERNATELY REMOVE OBJECTS FROM THE PILE.
        WINNING IS DEFINED IN ADVANCE AS TAKING THE LAST OBJECT OR
        NOT. YOU CAN ALSO SPECIFY SOME OTHER BEGINNING CONDITIONS.
        DON'T USE ZERO, HOWEVER, IN PLAYING THE GAME.
        ENTER A NEGATIVE NUMBER FOR NEW PILE SIZE TO STOP PLAYING.
        EOF
    end
    
    def ask_for_pile_size
        loop do
            puts "ENTER PILE SIZE:"
            pile_size = gets.to_i
            break pile_size if pile_size != 0
        end
    end
    
    GOAL_TAKE_LAST = 1
    GOAL_AVOID_LAST = 2
    def ask_for_goal
        loop do
            puts "ENTER WIN OPTION - #{GOAL_TAKE_LAST} TO TAKE LAST, #{GOAL_AVOID_LAST} TO AVOID LAST:"
            response = gets.to_i
            break response if [GOAL_TAKE_LAST, GOAL_AVOID_LAST].member? response
        end
    end
    
    def ask_for_min_max_take
        loop do
            puts "ENTER MIN AND MAX, SEPARATED BY A COMMA:"
            response = gets.split(',').map {|piece| piece.to_i}
            next if response.length != 2
            min_take, max_take = response
            break min_take, max_take if 0 < min_take && min_take <= max_take
        end
    end
    
    COMPUTER_MOVE = 1
    PLAYER_MOVE = 2
    def ask_who_makes_first_move
        loop do
            puts "ENTER START OPTION - #{COMPUTER_MOVE} COMPUTER FIRST, #{PLAYER_MOVE} YOU FIRST:"
            response = gets.to_i
            break response if [COMPUTER_MOVE, PLAYER_MOVE].member? response
        end
    end
    
    def ask_for_player_take(min_take:, max_take:)
        loop do
            puts "YOUR MOVE:"
            response = gets.to_i
            break response if response == 0 || response == response.clamp(min_take, max_take)
            puts "ILLEGAL MOVE, REENTER IT."
        end
    end
    
    def battle(pile:, goal:, min_take:, max_take:, first_move:)
        next_move = first_move
        loop do
            if next_move == COMPUTER_MOVE
                next_move = PLAYER_MOVE
                take = (
                    if goal == GOAL_TAKE_LAST
                        pile % (min_take + max_take)
                    else
                        (pile - 1) % (min_take + max_take)
                    end
                ).clamp(min_take, max_take)
                pile = pile - take
                if pile > 0
                    puts "COMPUTER TAKES #{take} AND LEAVES #{pile}"
                else
                    if goal == GOAL_TAKE_LAST
                        puts "COMPUTER TAKES #{take} AND WINS."
                        break
                    else
                        puts "COMPUTER TAKES #{take} AND LOSES."
                        break
                    end
                end
            else
                next_move = COMPUTER_MOVE
                take = ask_for_player_take(min_take: min_take, max_take: max_take)
                if take == 0
                    puts "I TOLD YOU NOT TO USE ZERO! COMPUTER WINS BY FORFEIT."
                    break
                end
                pile = pile - take
                if pile > 0
                    puts "PLAYER TAKES #{take} AND LEAVES #{pile}"
                else
                    if goal == GOAL_TAKE_LAST
                        puts "CONGRATULATIONS, YOU WIN."
                        break
                    else
                        puts "TOUGH LUCK, YOU LOSE."
                        break
                    end
                end
            end
        end
    end
    
    
    def main
        instructions
        loop do
            pile = ask_for_pile_size
            break if pile < 0
            goal = ask_for_goal
            min_take, max_take = ask_for_min_max_take
            first_move = ask_who_makes_first_move
            battle(
                pile: pile,
                goal: goal,
                min_take: min_take,
                max_take: max_take,
                first_move: first_move,
            )
        end
    end
    
    main
    
    
    ================================================
    FILE: 08_Batnum/rust/Cargo.toml
    ================================================
    [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]
    
    
    ================================================
    FILE: 08_Batnum/rust/src/main.rs
    ================================================
    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);
        }
    }
    
    
    ================================================
    FILE: 08_Batnum/vbnet/Program.vb
    ================================================
    Imports System
    
    Module BatNum
        Enum WinOptions
            Undefined = 0
            TakeLast = 1
            AvoidLast = 2
        End Enum
    
        Enum StartOptions
            Undefined = 0
            ComputerFirst = 1
            PlayerFirst = 2
        End Enum
    
        Dim pileSize As Integer = 0
        Dim minSelect As Integer = 0
        Dim maxSelect As Integer = 0
        Dim startOption As StartOptions = StartOptions.Undefined
        Dim winOption As WinOptions = WinOptions.Undefined
    
        ' 
        ' Prints the intro and rules of the game.
        ' 
        Private Sub PrintIntro()
            Console.WriteLine("BATNUM".PadLeft(33, " "))
            Console.WriteLine("CREATIVE COMPUTING  MORRISSTOWN, NEW JERSEY".PadLeft(15, " "))
            Console.WriteLine()
            Console.WriteLine()
            Console.WriteLine()
            Console.WriteLine("THIS PROGRAM IS A 'BATTLE OF NUMBERS' GAME, WHERE THE")
            Console.WriteLine("COMPUTER IS YOUR OPPONENT.")
            Console.WriteLine()
            Console.WriteLine("THE GAME STARTS WITH AN ASSUMED PILE OF OBJECTS. YOU")
            Console.WriteLine("AND YOUR OPPONENT ALTERNATELY REMOVE OBJECTS FROM THE PILE.")
            Console.WriteLine("WINNING IS DEFINED IN ADVANCE AS TAKING THE LAST OBJECT OR")
            Console.WriteLine("NOT. YOU CAN ALSO SPECIFY SOME OTHER BEGINNING CONDITIONS.")
            Console.WriteLine("DON'T USE ZERO, HOWEVER, IN PLAYING THE GAME.")
            Console.WriteLine("ENTER A NEGATIVE NUMBER FOR NEW PILE SIZE TO STOP PLAYING.")
            Console.WriteLine()
        End Sub
    
        ' 
        ' Asks the user for the various parameters necessary
        ' to play the game.
        ' 
        Private Sub GetParams()
            ' Reset the game parameters
            pileSize = 0
            minSelect = 0
            maxSelect = 0
            startOption = StartOptions.Undefined
            winOption = WinOptions.Undefined
    
            While pileSize < 1
                Console.Write("ENTER PILE SIZE ")
                pileSize = Convert.ToInt32(Console.ReadLine())
            End While
            While winOption = WinOptions.Undefined
                Console.Write("ENTER WIN OPTION - 1 TO TAKE LAST, 2 TO AVOID LAST: ")
                winOption = Convert.ToInt32(Console.ReadLine())
            End While
            While minSelect < 1 Or maxSelect < 1 Or minSelect > maxSelect
                Console.Write("ENTER MIN AND MAX ")
                Dim vals = Console.ReadLine().ToString().Split(" ").[Select](Function(n) Integer.Parse(n)).ToList()
                If vals.Count() <> 2 Then
                    Continue While
                End If
                minSelect = vals(0)
                maxSelect = vals(1)
            End While
            While startOption = StartOptions.Undefined
                Console.Write("ENTER START OPTION - 1 COMPUTER FIRST, 2 YOU FIRST ")
                startOption = Convert.ToInt32(Console.ReadLine())
            End While
        End Sub
    
        '
        'This handles the player's turn - asking the player how many objects
        'to take And doing some basic validation around that input.  Then it
        'checks for any win conditions.
        '
        'Returns a Boolean indicating whether the game Is over And the New pileSize.'
        Private Function PlayerMove() As Boolean
            Dim playerDone As Boolean = False
    
            While Not playerDone
                Console.WriteLine("YOUR MOVE ")
                Dim playerNum As Integer = Convert.ToInt32(Console.ReadLine())
                If playerNum = 0 Then
                    Console.WriteLine("I TOLD YOU NOT TO USE ZERO!  COMPUTER WINS BY FORFEIT.")
                    Return True
                End If
                If playerNum > maxSelect Or playerNum < minSelect Then
                    Console.WriteLine("ILLEGAL MOVE, REENTER IT")
                    Continue While
                End If
    
                pileSize = pileSize - playerNum
                playerDone = True
                If pileSize <= 0 Then
                    If winOption = WinOptions.AvoidLast Then
                        Console.WriteLine("TOUGH LUCK, YOU LOSE.")
                    Else
                        Console.WriteLine("CONGRATULATIONS, YOU WIN.")
                    End If
                    Return True
                End If
            End While
    
            Return False
        End Function
    
        '
        'This handles the logic to determine how many objects the computer
        'will select on its turn.
        '
        Private Function ComputerPick() As Integer
            Dim q As Integer = IIf(winOption = WinOptions.AvoidLast, pileSize - 1, pileSize)
            Dim c As Integer = minSelect + maxSelect
            Dim computerNum As Integer = q - (c * Int(q / c))
            If computerNum < minSelect Then
                computerNum = minSelect
            End If
            If computerNum > maxSelect Then
                ComputerPick = maxSelect
            End If
    
            Return computerNum
        End Function
    
        '
        'This handles the computer's turn - first checking for the various
        'win/lose conditions And then calculating how many objects
        'the computer will take.
        '
        'Returns a boolean indicating whether the game is over.'
        Private Function ComputerMove() As Boolean
            ' First, check For win conditions On this move
            ' In this Case, we win by taking the last Object And
            ' the remaining pile Is less than max Select
            ' so the computer can grab them all And win
            If winOption = WinOptions.TakeLast And pileSize <= maxSelect Then
                Console.WriteLine($"COMPUTER TAKES {pileSize} AND WINS.")
                Return True
            End If
            ' In this Case, we lose by taking the last Object And
            ' the remaining pile Is less than minsize And the computer
            ' has To take all Of them.
            If winOption = WinOptions.AvoidLast And pileSize <= minSelect Then
                Console.WriteLine($"COMPUTER TAKES {minSelect} AND LOSES.")
                Return True
            End If
    
            ' Otherwise, we determine how many the computer selects
            Dim currSel As Integer = ComputerPick()
            pileSize = pileSize - currSel
            Console.WriteLine($"COMPUTER TAKES {currSel} AND LEAVES {pileSize}")
            Return False
        End Function
    
        '
        'This is the main game loop - repeating each turn until one
        'of the win/lose conditions Is met.
        '
        Private Sub PlayGame()
            Dim gameOver As Boolean = False
            ' playersTurn Is a Boolean keeping track Of whether it's the
            ' player's or computer's turn
            Dim playersTurn As Boolean = (startOption = StartOptions.PlayerFirst)
    
            While Not gameOver
                If playersTurn Then
                    gameOver = PlayerMove()
                    playersTurn = False
                    If gameOver Then Return
                End If
    
                If Not playersTurn Then
                    gameOver = ComputerMove()
                    playersTurn = True
                End If
            End While
        End Sub
    
        Public Sub Play()
            While True
                PrintIntro()
                GetParams()
                PlayGame()
            End While
        End Sub
    End Module
    
    Module Program
        Sub Main(args As String())
            BatNum.Play()
        End Sub
    End Module
    
    
    ================================================
    FILE: 08_Batnum/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 08_Batnum/vbnet/batnum.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 17
    VisualStudioVersion = 17.0.32014.148
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "Batnum", "Batnum.vbproj", "{D577E429-F84D-4E84-86E7-E6526CFD5FD9}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{D577E429-F84D-4E84-86E7-E6526CFD5FD9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{D577E429-F84D-4E84-86E7-E6526CFD5FD9}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{D577E429-F84D-4E84-86E7-E6526CFD5FD9}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{D577E429-F84D-4E84-86E7-E6526CFD5FD9}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {426DF7FE-66E7-4319-9AD8-7A2DD3964A2F}
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 08_Batnum/vbnet/batnum.vbproj
    ================================================
    
    
      
        Exe
        Batnum
        netcoreapp3.1
        16.9
      
    
    
    
    
    ================================================
    FILE: 09_Battle/README.md
    ================================================
    ### Battle
    
    BATTLE is based on the popular game Battleship which is primarily played to familiarize people with the location and designation of points on a coordinate plane.
    
    BATTLE first randomly sets up the bad guy’s fleet disposition on a 6 by 6 matrix or grid. The fleet consists of six ships:
    - Two destroyers (ships number 1 and 2) which are two units long
    - Two cruisers (ships number 3 and 4) which are three units long
    - Two aircraft carriers (ships number 5 and 6) which are four units long
    
    The program then prints out this fleet disposition in a coded or disguised format (see the sample computer print-out). You then proceed to sink the various ships by typing in the coordinates (two digits. each from 1 to 6, separated by a comma) of the place where you want to drop a bomb, if you’ll excuse the expression. The computer gives the appropriate response (splash, hit, etc.) which you should record on a 6 by 6 matrix. You are thus building a representation of the actual fleet disposition which you will hopefully use to decode the coded fleet disposition printed out by the computer. Each time a ship is sunk, the computer prints out which ships have been sunk so far and also gives you a “SPLASH/HIT RATIO.”
    
    The first thing you should learn is how to locate and designate positions on the matrix, and specifically the difference between “3,4” and “4,3.” Our method corresponds to the location of points on the coordinate plane rather than the location of numbers in a standard algebraic matrix: the first number gives the column counting from left to right and the second number gives the row counting from bottom to top.
    
    The second thing you should learn about is the splash/hit ratio. “What is a ratio?” A good reply is “It’s a fraction or quotient.” Specifically, the spash/hit ratio is the number of splashes divided by the number of hits. If you had 9 splashes and 15 hits, the ratio would be 9/15 or 3/5, both of which are correct. The computer would give this splash/hit ratio as .6.
    
    The main objective and primary education benefit of BATTLE comes from attempting to decode the 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.
    
    ---
    
    As published in Basic Computer Games (1978)
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=15)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=30)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    - 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.)
    
    ================================================
    FILE: 09_Battle/battle.bas
    ================================================
    5 PRINT TAB(33);"BATTLE"
    7 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    10 REM -- BATTLE WRITTEN BY RAY WESTERGARD  10/70
    20 REM COPYRIGHT 1971 BY THE REGENTS OF THE UNIV. OF CALIF.
    30 REM PRODUCED AT THE LAWRENCE HALL OF SCIENCE, BERKELEY
    40 DIM F(6,6),H(6,6),A(4),B(4),C(6),L(3)
    50 FOR X=1 TO 6
    51 FOR Y=1 TO 6
    52 F(X,Y)=0
    53 NEXT Y
    54 NEXT X
    60 FOR I=1 TO 3
    70 N=4-I
    80 FOR J=1 TO 2
    90 A=INT(6*RND(1)+1)
    100 B=INT(6*RND(1)+1)
    110 D=INT(4*RND(1)+1)
    120 IF F(A,B)>0 THEN 90
    130 M=0
    140 ON D GOTO 150,340,550,740
    150 B(1)=B
    160 B(2)=7:B(3)=7
    170 FOR K=1 TO N
    180 IF M>1 THEN 240
    190 IF B(K)=6 THEN 230
    200 IF F(A,B(K)+1)>0 THEN 230
    210 B(K+1)=B(K)+1
    220 GOTO 280
    230 M=2
    240 IF B(1)0 THEN 90
    270 B(K+1)=Z-1
    280 NEXT K
    290 F(A,B)=9-2*I-J
    300 FOR K=1 TO N
    310 F(A,B(K+1))=F(A,B)
    320 NEXT K
    330 GOTO 990
    340 A(1)=A
    350 B(1)=B
    360 A(2)=0:A(3)=0:B(2)=0:B(3)=0
    370 FOR K=1 TO N
    380 IF M>1 THEN 460
    390 IF A(K)=1 OR B(K)=1 THEN 450
    400 IF F(A(K)-1,B(K)-1)>0 THEN 450
    410 IF F(A(K)-1,B(K))>0 AND F(A(K)-1,B(K))=F(A(K),B(K)-1) THEN 450
    420 A(K+1)=A(K)-1
    430 B(K+1)=B(K)-1
    440 GOTO 530
    450 M=2
    460 IF A(1)>A(2) AND A(1)>A(3) THEN Z1=A(1)
    462 IF A(2)>A(1) AND A(2)>A(3) THEN Z1=A(2)
    464 IF A(3)>A(1) AND A(3)>A(2) THEN Z1=A(3)
    470 IF B(1)>B(2) AND B(1)>B(3) THEN Z2=B(1)
    474 IF B(2)>B(1) AND B(2)>B(3) THEN Z2=B(2)
    476 IF B(3)>B(1) AND B(3)>B(2) THEN Z2=B(3)
    480 IF Z1=6 OR Z2=6 THEN 90
    490 IF F(Z1+1,Z2+1)>0 THEN 90
    500 IF F(Z1,Z2+1)>0 AND F(Z1,Z2+1)=F(Z1+1,Z2) THEN 90
    510 A(K+1)=Z1+1
    520 B(K+1)=Z2+1
    530 NEXT K
    540 GOTO 950
    550 A(1)=A
    560 A(2)=7:A(3)=7
    570 FOR K=1 TO N
    580 IF M>1 THEN 640
    590 IF A(K)=6 THEN 630
    600 IF F(A(K)+1,B)>0 THEN 630
    610 A(K+1)=A(K)+1
    620 GOTO 680
    630 M=2
    640 IF A(1)0 THEN 90
    670 A(K+1)=Z-1
    680 NEXT K
    690 F(A,B)=9-2*I-J
    700 FOR K=1 TO N
    710 F(A(K+1),B)=F(A,B)
    720 NEXT K
    730 GOTO 990
    740 A(1)=A
    750 B(1)=B
    760 A(2)=7:A(3)=7
    770 B(2)=0:B(3)=0
    780 FOR K=1 TO N
    790 IF M>1 THEN 870
    800 IF A(K)=6 OR B(K)=1 THEN 860
    810 IF F(A(K)+1,B(K)-1)>0 THEN 860
    820 IF F(A(K)+1,B(K))>0 AND F(A(K)+1,B(K))=F(A(K),B(K)-1) THEN 860
    830 A(K+1)=A(K)+1
    840 B(K+1)=B(K)-1
    850 GOTO 940
    860 M=2
    870 IF A(1)B(2) AND B(1)>B(3) THEN Z2=B(1)
    882 IF B(2)>B(1) AND B(2)>B(3) THEN Z2=B(2)
    884 IF B(3)>B(1) AND B(3)>B(2) THEN Z2=B(3)
    890 IF Z1=1 OR Z2=6 THEN 90
    900 IF F(Z1-1,Z2+1)>0 THEN 90
    910 IF F(Z1,Z2+1)>0 AND F(Z1,Z2+1)=F(Z1-1,Z2) THEN 90
    920 A(K+1)=Z1-1
    930 B(K+1)=Z2+1
    940 NEXT K
    950 F(A,B)=9-2*I-J
    960 FOR K=1 TO N
    970 F(A(K+1),B(K+1))=F(A,B)
    980 NEXT K
    990 NEXT J
    1000 NEXT I
    1010 PRINT
    1020 PRINT "THE FOLLOWING CODE OF THE BAD GUYS' FLEET DISPOSITION"
    1030 PRINT "HAS BEEN CAPTURED BUT NOT DECODED:"
    1040 PRINT
    1050 FOR I=1 TO 6
    1051 FOR J=1 TO 6
    1052 H(I,J)=F(J,I)
    1053 NEXT J
    1054 NEXT I
    1060 FOR I=1 TO 6
    1061 FOR J=1 TO 6
    1062 PRINT H(I,J);
    1063 NEXT J
    1064 PRINT
    1065 NEXT I
    1070 PRINT
    1080 PRINT "DE-CODE IT AND USE IT IF YOU CAN"
    1090 PRINT "BUT KEEP THE DE-CODING METHOD A SECRET."
    1100 PRINT
    1110 FOR I=1 TO 6
    1111 FOR J=1 TO 6
    1112 H(I,J)=0
    1113 NEXT J
    1114 NEXT I
    1120 FOR I=1 TO 3
    1121 L(I)=0
    1122 NEXT I
    1130 C(1)=2:C(2)=2
    1140 C(3)=1:C(4)=1
    1150 C(5)=0:C(6)=0
    1160 S=0:H=0
    1170 PRINT "START GAME"
    1180 INPUT X,Y
    1190 IF X<1 OR X>6 OR INT(X)<>ABS(X) THEN 1210
    1200 IF Y>0 AND Y<7 AND INT(Y)=ABS(Y) THEN 1230
    1210 PRINT "INVALID INPUT.  TRY AGAIN."
    1220 GOTO 1180
    1230 R=7-Y
    1240 C=X
    1250 IF F(R,C)>0 THEN 1290
    1260 S=S+1
    1270 PRINT "SPLASH!  TRY AGAIN."
    1280 GOTO 1180
    1290 IF C(F(R,C))<4 THEN 1340
    1300 PRINT "THERE USED TO BE A SHIP AT THAT POINT, BUT YOU SUNK IT."
    1310 PRINT "SPLASH!  TRY AGAIN."
    1320 S=S+1
    1330 GOTO 1180
    1340 IF H(R,C)>0 THEN 1420
    1350 H=H+1
    1360 H(R,C)=F(R,C)
    1370 PRINT "A DIRECT HIT ON SHIP NUMBER";F(R,C)
    1380 C(F(R,C))=C(F(R,C))+1
    1390 IF C(F(R,C))>=4 THEN 1470
    1400 PRINT "TRY AGAIN."
    1410 GOTO 1180
    1420 PRINT "YOU ALREADY PUT A HOLE IN SHIP NUMBER";F(R,C);
    1430 PRINT "AT THAT POINT."
    1440 PRINT "SPLASH!  TRY AGAIN."
    1450 S=S+1
    1460 GOTO 1180
    1470 L((INT(F(R,C)-1)/2)+1)=L((INT(F(R,C)-1)/2)+1)+1
    1480 PRINT "AND YOU SUNK IT.  HURRAH FOR THE GOOD GUYS."
    1490 PRINT "SO FAR, THE BAD GUYS HAVE LOST"
    1500 PRINT L(1);"DESTROYER(S),";L(2);"CRUISER(S), AND";
    1510 PRINT L(3);"AIRCRAFT CARRIER(S)."
    1520 PRINT "YOUR CURRENT SPLASH/HIT RATIO IS";S/H
    1530 IF (L(1)+L(2)+L(3))<6 THEN 1180
    1540 PRINT
    1550 PRINT "YOU HAVE TOTALLY WIPED OUT THE BAD GUYS' FLEET"
    1560 PRINT "WITH A FINAL SPLASH/HIT RATIO OF";S/H
    1570 IF S/H>0 THEN 1590
    1580 PRINT "CONGRATULATIONS -- A DIRECT HIT EVERY TIME."
    1590 PRINT
    1600 PRINT "****************************"
    1610 PRINT
    1620 GOTO 50
    1630 END
    
    
    ================================================
    FILE: 09_Battle/csharp/Battle.csproj
    ================================================
    
    
      
        Exe
        netcoreapp3.1
      
    
    
    
    
    ================================================
    FILE: 09_Battle/csharp/Battle.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.31005.135
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Battle", "Battle.csproj", "{154E5E43-053B-4FB1-88D0-ED7166464257}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{154E5E43-053B-4FB1-88D0-ED7166464257}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{154E5E43-053B-4FB1-88D0-ED7166464257}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{154E5E43-053B-4FB1-88D0-ED7166464257}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{154E5E43-053B-4FB1-88D0-ED7166464257}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {5CC3AC8C-6CEC-4FD6-B5A0-B2376B37C42F}
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 09_Battle/csharp/Game.cs
    ================================================
    using System;
    using System.Linq;
    
    namespace Battle
    {
        public class Game
        {
            private int[,] field = new int[7, 7];
    
            private Random random = new Random();
    
            public void Run()
            {
                DisplayIntro();
    
                while (true)
                {
                    field = new int[7, 7];
    
                    foreach (var shipType in new []{ 1, 2, 3})
                    {
                        foreach (var ship in new int[] { 1, 2 })
                        {
                            while (!SetShip(shipType, ship)) { }
                        }
                    }
    
                    UserInteraction();
                }
            }
    
            private bool SetShip(int shipType, int shipNum)
            {
                var shipSize = 4 - shipType;
                int direction;
                int[] A = new int[5];
                int[] B = new int[5];
                int row, col;
    
                do
                {
                    row = Rnd(6) + 1;
                    col = Rnd(6) + 1;
                    direction = Rnd(4) + 1;
                } while (field[row, col] > 0);
    
                var M = 0;
    
                switch (direction)
                {
                    case 1:
                        B[1] = col;
                        B[2] = 7;
                        B[3] = 7;
    
                        for (var K = 1; K <= shipSize; K++)
                        {
                            if (!(M > 1 || B[K] == 6 || field[row, B[K] + 1] > 0))
                            {
                                B[K + 1] = B[K] + 1;
                                continue;
                            }
    
                            M = 2;
                            var Z = 1;
    
                            if (B[1] < B[2] && B[1] < B[3]) Z = B[1];
                            if (B[2] < B[1] && B[2] < B[3]) Z = B[2];
                            if (B[3] < B[1] && B[3] < B[2]) Z = B[3];
    
                            if (Z == 1 || field[row, Z - 1] > 0) return false;
    
                            B[K + 1] = Z - 1;
                        }
    
                        field[row, col] = 9 - 2 * shipType - shipNum;
    
                        for (var K = 1; K <= shipSize; K++)
                        {
                            field[row, B[K + 1]] = field[row, col];
                        }
                        break;
    
                    case 2:
                        A[1] = row;
                        B[1] = col;
                        A[2] = 0;
                        A[3] = 0;
                        B[2] = 0;
                        B[3] = 0;
    
                        for (var K = 1; K <= shipSize; K++)
                        {
                            if (!(M > 1
                                || A[K] == 1 || B[K] == 1
                                || field[A[K] - 1, B[K] - 1] > 0
                                || (field[A[K] - 1, B[K]] > 0 && field[A[K] - 1, B[K]] == field[A[K], B[K] - 1])))
                            {
                                A[K + 1] = A[K] - 1;
                                B[K + 1] = B[K] - 1;
                                continue;
                            }
    
                            M = 2;
                            var Z1 = 1;
                            var Z2 = 1;
    
                            if (A[1] > A[2] && A[1] > A[3]) Z1 = A[1];
                            if (A[2] > A[1] && A[2] > A[3]) Z1 = A[2];
                            if (A[3] > A[1] && A[3] > A[2]) Z1 = A[3];
                            if (B[1] > B[2] && B[1] > B[3]) Z2 = B[1];
                            if (B[2] > B[1] && B[2] > B[3]) Z2 = B[2];
                            if (B[3] > B[1] && B[3] > B[2]) Z2 = B[3];
    
                            if (Z1 == 6 || Z2 == 6
                                || field[Z1 + 1, Z2 + 1] > 0
                                || (field[Z1, Z2 + 1] > 0 && field[Z1, Z2 + 1] == field[Z1 + 1, Z2])) return false;
    
                            A[K + 1] = Z1 + 1;
                            B[K + 1] = Z2 + 1;
                        }
    
                        field[row, col] = 9 - 2 * shipType - shipNum;
    
                        for (var K = 1; K <= shipSize; K++)
                        {
                            field[A[K + 1], B[K + 1]] = field[row, col];
                        }
                        break;
    
                    case 3:
                        A[1] = row;
                        A[2] = 7;
                        A[3] = 7;
    
                        for (var K = 1; K <= shipSize; K++)
                        {
                            if (!(M > 1 || A[K] == 6
                                || field[A[K] + 1, col] > 0))
                            {
                                A[K + 1] = A[K] + 1;
                                continue;
                            }
    
                            M = 2;
                            var Z = 1;
    
                            if (A[1] < A[2] && A[1] < A[3]) Z = A[1];
                            if (A[2] < A[1] && A[2] < A[3]) Z = A[2];
                            if (A[3] < A[1] && A[3] < A[2]) Z = A[3];
    
                            if (Z == 1 || field[Z - 1, col] > 0) return false;
    
                            A[K + 1] = Z - 1;
                        }
    
                        field[row, col] = 9 - 2 * shipType - shipNum;
    
                        for (var K = 1; K <= shipSize; K++)
                        {
                            field[A[K + 1], col] = field[row, col];
                        }
                        break;
    
                    case 4:
                    default:
                        A[1] = row;
                        B[1] = col;
                        A[2] = 7;
                        A[3] = 7;
                        B[2] = 0;
                        B[3] = 0;
    
                        for (var K = 1; K <= shipSize; K++)
                        {
                            if (!(M > 1 || A[K] == 6 || B[K] == 1
                                || field[A[K] + 1, B[K] - 1] > 0
                                || (field[A[K] + 1, B[K]] > 0 && field[A[K] + 1, B[K]] == field[A[K], B[K] - 1])))
                            {
                                A[K + 1] = A[K] + 1;
                                B[K + 1] = B[K] - 1;
                                continue;
                            }
    
                            M = 2;
                            var Z1 = 1;
                            var Z2 = 1;
    
                            if (A[1] < A[2] && A[1] < A[3]) Z1 = A[1];
                            if (A[2] < A[1] && A[2] < A[3]) Z1 = A[2];
                            if (A[3] < A[1] && A[3] < A[2]) Z1 = A[3];
                            if (B[1] > B[2] && B[1] > B[3]) Z2 = B[1];
                            if (B[2] > B[1] && B[2] > B[3]) Z2 = B[2];
                            if (B[3] > B[1] && B[3] > B[2]) Z2 = B[3];
    
                            if (Z1 == 1 || Z2 == 6
                                || field[Z1 - 1, Z2 + 1] > 0
                                || (field[Z1, Z2 + 1] > 0 && field[Z1, Z2 + 1] == field[Z1 - 1, Z2])) return false;
    
                            A[K + 1] = Z1 - 1;
                            B[K + 1] = Z2 + 1;
                        }
    
                        field[row, col] = 9 - 2 * shipType - shipNum;
    
                        for (var K = 1; K <= shipSize; K++)
                        {
                            field[A[K + 1], B[K + 1]] = field[row, col];
                        }
    
                        break;
                }
    
                return true;
            }
    
            public void DisplayIntro()
            {
                Console.ForegroundColor = ConsoleColor.Green;
                Print(Tab(33) + "BATTLE");
                Print(Tab(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
            }
    
            public void UserInteraction()
            {
                Print();
                Print("THE FOLLOWING CODE OF THE BAD GUYS' FLEET DISPOSITION");
                Print("HAS BEEN CAPTURED BUT NOT DECODED:");
                Print();
    
                for (var row = 1; row <= 6; row++)
                {
                    for (var col = 1; col <= 6; col++)
                    {
                        Write(field[col, row].ToString());
                    }
    
                    Print();
                }
    
                Print();
                Print("DE-CODE IT AND USE IT IF YOU CAN");
                Print("BUT KEEP THE DE-CODING METHOD A SECRET.");
                Print();
    
                var hit = new int[7, 7];
                var lost = new int[4];
                var shipHits = new[] { 0, 2, 2, 1, 1, 0, 0 };
                var splashes = 0;
                var hits = 0;
    
                Print("START GAME");
    
                do
                {
                    var input = Console.ReadLine().Split(',').Select(x => int.TryParse(x, out var num) ? num : 0).ToArray();
    
                    if (!IsValid(input))
                    {
                        Print("INVALID INPUT.  TRY AGAIN.");
                        continue;
                    }
    
                    var col = input[0];
                    var row = 7 - input[1];
                    var shipNum = field[row, col];
    
                    if (shipNum == 0)
                    {
                        splashes = splashes + 1;
                        Print("SPLASH!  TRY AGAIN.");
                        continue;
                    }
    
                    if (shipHits[shipNum] > 3)
                    {
                        Print("THERE USED TO BE A SHIP AT THAT POINT, BUT YOU SUNK IT.");
                        Print("SPLASH!  TRY AGAIN.");
                        splashes = splashes + 1;
                        continue;
                    }
    
                    if (hit[row, col] > 0)
                    {
                        Print($"YOU ALREADY PUT A HOLE IN SHIP NUMBER {shipNum} AT THAT POINT.");
                        Print("SPLASH!  TRY AGAIN.");
                        splashes = splashes + 1;
                        continue;
                    }
    
                    hits = hits + 1;
                    hit[row, col] = shipNum;
    
                    Print($"A DIRECT HIT ON SHIP NUMBER {shipNum}");
                    shipHits[shipNum] = shipHits[shipNum] + 1;
    
                    if (shipHits[shipNum] < 4)
                    {
                        Print("TRY AGAIN.");
                        continue;
                    }
    
                    var shipType = (shipNum - 1) / 2 + 1;
                    lost[shipType] = lost[shipType] + 1;
    
                    Print("AND YOU SUNK IT.  HURRAH FOR THE GOOD GUYS.");
                    Print("SO FAR, THE BAD GUYS HAVE LOST");
                    Write($"{lost[1]} DESTROYER(S), {lost[2]} CRUISER(S), AND ");
                    Print($"{lost[3]} AIRCRAFT CARRIER(S).");
                    Print($"YOUR CURRENT SPLASH/HIT RATIO IS {splashes / hits}");
    
                    if ((lost[1] + lost[2] + lost[3]) < 6) continue;
    
                    Print();
                    Print("YOU HAVE TOTALLY WIPED OUT THE BAD GUYS' FLEET");
                    Print($"WITH A FINAL SPLASH/HIT RATIO OF {splashes / hits}");
    
                    if ((splashes / hits) == 0)
                    {
                        Print("CONGRATULATIONS -- A DIRECT HIT EVERY TIME.");
                    }
    
                    Print();
                    Print("****************************");
                    Print();
    
                    return;
    
                } while (true);
            }
    
            public bool IsValid(int[] input) => input.Length == 2 && input.All(Valid);
    
            public bool Valid(int value) => value > 0 && value < 7;
    
            public void Print(string str = "") => Console.WriteLine(str);
    
            public void Write(string value) => Console.Write(value);
    
            public string Tab(int pos) => new String(' ', pos);
    
            public int Rnd(int seed) => random.Next(seed);
        }
    }
    
    
    ================================================
    FILE: 09_Battle/csharp/Program.cs
    ================================================
    using System;
    
    namespace Battle
    {
        class Program
        {
            static void Main(string[] args)
            {
                new Game().Run();
            }
        }
    }
    
    
    ================================================
    FILE: 09_Battle/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 09_Battle/java/.gitignore
    ================================================
    *~
    
    
    ================================================
    FILE: 09_Battle/java/Battle.java
    ================================================
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.Random;
    import java.util.function.Predicate;
    import java.text.NumberFormat;
    
    
    /* This class holds the game state and the game logic */
    public class Battle {
    
        /* parameters of the game */
        private int seaSize;
        private int[] sizes;
        private int[] counts;
    
        /* The game setup - the ships and the sea */
        private ArrayList ships;
        private Sea sea;
    
        /* game state counts */
        private int[] losses;    // how many of each type of ship have been sunk
        private int hits;        // how many hits the player has made
        private int misses;      // how many misses the player has made
    
        // Names of ships of each size. The game as written has ships of size 3, 4 and 5 but
        // can easily be modified. It makes no sense to have a ship of size zero though.
        private static String NAMES_BY_SIZE[] = {
            "error",
            "size1",
            "destroyer",
            "cruiser",
            "aircraft carrier",
            "size5" };
    
        // Entrypoint
        public static void main(String args[]) {
            Battle game = new Battle(6,                        // Sea is 6 x 6 tiles
                                     new int[] { 2, 3, 4 },    // Ships are of sizes 2, 3, and 4
                                     new int[] { 2, 2, 2 });   // there are two ships of each size
            game.play();
        }
    
        public Battle(int scale, int[] shipSizes, int[] shipCounts) {
            seaSize = scale;
            sizes = shipSizes;
            counts = shipCounts;
    
            // validate parameters
            if (seaSize < 4) throw new RuntimeException("Sea Size " + seaSize + " invalid, must be at least 4");
    
            for (int sz : sizes) {
                if ((sz < 1) || (sz > seaSize))
                    throw new RuntimeException("Ship has invalid size " + sz);
            }
    
            if (counts.length != sizes.length) {
                throw new RuntimeException("Ship counts must match");
            }
    
            // Initialize game state
            sea = new Sea(seaSize);          // holds what ship if any occupies each tile
            ships = new ArrayList();   // positions and states of all the ships
            losses = new int[counts.length]; // how many ships of each type have been sunk
    
            // Build up the list of all the ships
            int shipNumber = 1;
            for (int type = 0; type < counts.length; ++type) {
                for (int i = 0; i < counts[i]; ++i) {
                    ships.add(new Ship(shipNumber++, sizes[type]));
                }
            }
    
            // When we put the ships in the sea, we put the biggest ones in first, or they might
            // not fit
            ArrayList largestFirst = new ArrayList<>(ships);
            Collections.sort(largestFirst, Comparator.comparingInt((Ship ship) -> ship.size()).reversed());
    
            // place each ship into the sea
            for (Ship ship : largestFirst) {
                ship.placeRandom(sea);
            }
        }
    
        public void play() {
            System.out.println("The following code of the bad guys' fleet disposition\nhas been captured but not decoded:\n");
            System.out.println(sea.encodedDump());
            System.out.println("De-code it and use it if you can\nbut keep the de-coding method a secret.\n");
    
            int lost = 0;
            System.out.println("Start game");
            Input input = new Input(seaSize);
            try {
                while (lost < ships.size()) {          // the game continues while some ships remain unsunk
                    if (! input.readCoordinates()) {   // ... unless there is no more input from the user
                        return;
                    }
    
                    // The computer thinks of the sea as a grid of rows, from top to bottom.
                    // However, the user will use X and Y coordinates, with Y going bottom to top
                    int row = seaSize - input.y();
                    int col = input.x() - 1;
    
                    if (sea.isEmpty(col, row)) {
                        ++misses;
                        System.out.println("Splash!  Try again.");
                    } else {
                        Ship ship = ships.get(sea.get(col, row) - 1);
                        if (ship.isSunk()) {
                            ++misses;
                            System.out.println("There used to be a ship at that point, but you sunk it.");
                            System.out.println("Splash!  Try again.");
                        } else if (ship.wasHit(col, row)) {
                            ++misses;
                            System.out.println("You already put a hole in ship number " + ship.id());
                            System.out.println("Splash!  Try again.");
                        } else {
                            ship.hit(col, row);
                            ++hits;
                            System.out.println("A direct hit on ship number " + ship.id());
    
                            // If a ship was hit, we need to know whether it was sunk.
                            // If so, tell the player and update our counts
                            if (ship.isSunk()) {
                                ++lost;
                                System.out.println("And you sunk it.  Hurrah for the good guys.");
                                System.out.print("So far, the bad guys have lost ");
                                ArrayList typeDescription = new ArrayList<>();
                                for (int i = 0 ; i < sizes.length; ++i) {
                                    if (sizes[i] == ship.size()) {
                                        ++losses[i];
                                    }
                                    StringBuilder sb = new StringBuilder();
                                    sb.append(losses[i]);
                                    sb.append(" ");
                                    sb.append(NAMES_BY_SIZE[sizes[i]]);
                                    if (losses[i] != 1)
                                        sb.append("s");
                                    typeDescription.add(sb.toString());
                                }
                                System.out.println(String.join(", ", typeDescription));
                                double ratioNum = ((double)misses)/hits;
                                String ratio = NumberFormat.getInstance().format(ratioNum);
                                System.out.println("Your current splash/hit ratio is " + ratio);
    
                                if (lost == ships.size()) {
                                    System.out.println("You have totally wiped out the bad guys' fleet");
                                    System.out.println("With a final splash/hit ratio of " + ratio);
    
                                    if (misses == 0) {
                                        System.out.println("Congratulations - A direct hit every time.");
                                    }
    
                                    System.out.println("\n****************************\n");
                                }
                            }
                        }
                    }
                }
            }
            catch (IOException e) {
                // This should not happen running from console, but java requires us to check for it
                System.err.println("System error.\n" + e);
            }
        }
    }
    
    
    ================================================
    FILE: 09_Battle/java/Input.java
    ================================================
    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.io.IOException;
    import java.text.NumberFormat;
    
    // This class handles reading input from the player
    // Each input is an x and y coordinate
    // e.g. 5,3
    public class Input {
        private BufferedReader reader;
        private NumberFormat parser;
        private int scale;             // size of the sea, needed to validate input
        private boolean isQuit;        // whether the input has ended
        private int[] coords;          // the last coordinates read
    
        public Input(int seaSize) {
            scale = seaSize;
            reader = new BufferedReader(new InputStreamReader(System.in));
            parser = NumberFormat.getIntegerInstance();
        }
    
        public boolean readCoordinates() throws IOException {
            while (true) {
                // Write a prompt
                System.out.print("\nTarget x,y\n> ");
                String inputLine = reader.readLine();
                if (inputLine == null) {
                    // If the input stream is ended, there is no way to continue the game
                    System.out.println("\nGame quit\n");
                    isQuit = true;
                    return false;
                }
    
                // split the input into two fields
                String[] fields = inputLine.split(",");
                if (fields.length != 2) {
                    // has to be exactly two
                    System.out.println("Need two coordinates separated by ','");
                    continue;
                }
    
                coords = new int[2];
                boolean error = false;
                // each field should contain an integer from 1 to the size of the sea
                try {
                    for (int c = 0 ; c < 2; ++c ) {
                        int val = Integer.parseInt(fields[c].strip());
                        if ((val < 1) || (val > scale)) {
                            System.out.println("Coordinates must be from 1 to " + scale);
                            error = true;
                        } else {
                            coords[c] = val;
                        }
                    }
                }
                catch (NumberFormatException ne) {
                    // this happens if the field is not a valid number
                    System.out.println("Coordinates must be numbers");
                    error = true;
                }
                if (!error) return true;
            }
        }
    
        public int x() { return coords[0]; }
        public int y() { return coords[1]; }
    }
    
    
    ================================================
    FILE: 09_Battle/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 09_Battle/java/Sea.java
    ================================================
    // Track the content of the sea
    class Sea {
        // the sea is a square grid of tiles. It is a one-dimensional array, and this
        // class maps x and y coordinates to an array index
        // Each tile is either empty (value of tiles at index is 0)
        // or contains a ship (value of tiles at index is the ship number)
        private int tiles[];
    
        private int size;
    
        public Sea(int make_size) {
            size = make_size;
            tiles = new int[size*size];
        }
    
        public int size() { return size; }
    
        // This writes out a representation of the sea, but in a funny order
        // The idea is to give the player the job of working it out
        public String encodedDump() {
            StringBuilder out = new StringBuilder();
            for (int x = 0; x < size; ++x) {
                for (int y = 0; y < size; ++y)
                    out.append(Integer.toString(get(x, y)));
                out.append('\n');
            }
            return out.toString();
        }
    
        /* return true if x,y is in the sea and empty
         * return false if x,y is occupied or is out of range
         * Doing this in one method makes placing ships much easier
         */
        public boolean isEmpty(int x, int y) {
            if ((x<0)||(x>=size)||(y<0)||(y>=size)) return false;
            return (get(x,y) == 0);
        }
    
        /* return the ship number, or zero if no ship.
         * Unlike isEmpty(x,y), these other methods require that the
         * coordinates passed be valid
         */
        public int get(int x, int y) {
            return tiles[index(x,y)];
        }
    
        public void set(int x, int y, int value) {
            tiles[index(x, y)] = value;
        }
    
        // map the coordinates to the array index
        private int index(int x, int y) {
            if ((x < 0) || (x >= size))
                throw new ArrayIndexOutOfBoundsException("Program error: x cannot be " + x);
            if ((y < 0) || (y >= size))
                throw new ArrayIndexOutOfBoundsException("Program error: y cannot be " + y);
    
            return y*size + x;
        }
    }
    
    
    ================================================
    FILE: 09_Battle/java/Ship.java
    ================================================
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.Random;
    import java.util.function.Predicate;
    
    /** A single ship, with its position and where it has been hit */
    class Ship {
        // These are the four directions that ships can be in
        public static final int ORIENT_E=0;   // goes East from starting position
        public static final int ORIENT_SE=1;  // goes SouthEast from starting position
        public static final int ORIENT_S=2;   // goes South from starting position
        public static final int ORIENT_SW=3;  // goes SouthWest from starting position
    
        private int id;                   // ship number
        private int size;                 // how many tiles it occupies
        private boolean placed;           // whether this ship is in the sea yet
        private boolean sunk;             // whether this ship has been sunk
        private ArrayList hits;  // which tiles of the ship have been hit
    
        private int startX;               // starting position coordinates
        private int startY;
        private int orientX;              // x and y deltas from each tile occupied to the next
        private int orientY;
    
        public Ship(int i, int sz) {
            id = i; size = sz;
            sunk = false; placed = false;
            hits = new ArrayList<>(Collections.nCopies(size, false));
        }
    
        /** @returns the ship number */
        public int id() { return id; }
        /** @returns the ship size */
        public int size() { return size; }
    
        /* record the ship as having been hit at the given coordinates */
        public void hit(int x, int y) {
            // need to work out how many tiles from the ship's starting position the hit is at
            // that can be worked out from the difference between the starting X coord and this one
            // unless the ship runs N-S, in which case use the Y coord instead
            int offset;
            if (orientX != 0) {
                offset = (x - startX) / orientX;
            } else {
                offset = (y - startY) / orientY;
            }
            hits.set(offset, true);
    
            // if every tile of the ship has been hit, the ship is sunk
            sunk = hits.stream().allMatch(Predicate.isEqual(true));
        }
    
        public boolean isSunk() { return sunk; }
    
        // whether the ship has already been hit at the given coordinates
        public boolean wasHit(int x, int y) {
            int offset;
            if (orientX != 0) {
                offset = (x - startX) / orientX;
            } else {
                offset = (y - startY) / orientY;
            }
            return hits.get(offset);
        };
    
        // Place the ship in the sea.
        // choose a random starting position, and a random direction
        // if that doesn't fit, keep picking different positions and directions
        public void placeRandom(Sea s) {
            Random random = new Random();
            for (int tries = 0 ; tries < 1000 ; ++tries) {
                int x = random.nextInt(s.size());
                int y = random.nextInt(s.size());
                int orient = random.nextInt(4);
    
                if (place(s, x, y, orient)) return;
            }
    
            throw new RuntimeException("Could not place any more ships");
        }
    
        // Attempt to fit the ship into the sea, starting from a given position and
        // in a given direction
        // This is by far the most complicated part of the program.
        // It will start at the position provided, and attempt to occupy tiles in the
        // requested direction. If it does not fit, either because of the edge of the
        // sea, or because of ships already in place, it will try to extend the ship
        // in the opposite direction instead. If that is not possible, it fails.
        public boolean place(Sea s, int x, int y, int orient) {
            if (placed) {
                throw new RuntimeException("Program error - placed ship " + id + " twice");
            }
            switch(orient) {
            case ORIENT_E:                 // east is increasing X coordinate
                orientX = 1; orientY = 0;
                break;
            case ORIENT_SE:                // southeast is increasing X and Y
                orientX = 1; orientY = 1;
                break;
            case ORIENT_S:                 // south is increasing Y
                orientX = 0; orientY = 1;
                break;
            case ORIENT_SW:                // southwest is increasing Y but decreasing X
                orientX = -1; orientY = 1;
                break;
            default:
                throw new RuntimeException("Invalid orientation " + orient);
            }
    
            if (!s.isEmpty(x, y)) return false; // starting position is occupied - placing fails
    
            startX = x; startY = y;
            int tilesPlaced = 1;
            int nextX = startX;
            int nextY = startY;
            while (tilesPlaced < size) {
                if (extendShip(s, nextX, nextY, nextX + orientX, nextY + orientY)) {
                    // It is clear to extend the ship forwards
                    tilesPlaced += 1;
                    nextX = nextX + orientX;
                    nextY = nextY + orientY;
                } else {
                    int backX = startX - orientX;
                    int backY = startY - orientY;
    
                    if (extendShip(s, startX, startY, backX, backY)) {
                        // We can move the ship backwards, so it can be one tile longer
                        tilesPlaced +=1;
                        startX = backX;
                        startY = backY;
                    } else {
                        // Could not make it longer or move it backwards
                        return false;
                    }
                }
            }
    
            // Mark in the sea which tiles this ship occupies
            for (int i = 0; i < size; ++i) {
                int sx = startX + i * orientX;
                int sy = startY + i * orientY;
                s.set(sx, sy, id);
            }
    
            placed = true;
            return true;
        }
    
        // Check whether a ship which already occupies the "from" coordinates,
        // can also occupy the "to" coordinates.
        // They must be within the sea area, empty, and not cause the ship to cross
        // over another ship
        private boolean extendShip(Sea s, int fromX, int fromY, int toX, int toY) {
            if (!s.isEmpty(toX, toY)) return false;                  // no space
            if ((fromX == toX)||(fromY == toY)) return true;         // horizontal or vertical
    
            // we can extend the ship without colliding, but we are going diagonally
            // and it should not be possible for two ships to cross each other on
            // opposite diagonals.
    
            // check the two tiles that would cross us here - if either is empty, we are OK
            // if they both contain different ships, we are OK
            // but if they both contain the same ship, we are crossing!
            int corner1 = s.get(fromX, toY);
            int corner2 = s.get(toX, fromY);
            if ((corner1 == 0) || (corner1 != corner2)) return true;
            return false;
        }
    }
    
    
    ================================================
    FILE: 09_Battle/javascript/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells)
    
    
    ================================================
    FILE: 09_Battle/javascript/battle.html
    ================================================
    
    
    BATTLE
    
    
    
    
    
    
    
    
    
    
    ================================================
    FILE: 09_Battle/javascript/battle.js
    ================================================
    // BATTLE
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    var fa = [];
    var ha = [];
    var aa = [];
    var ba = [];
    var ca = [];
    var la = [];
    
    // Main program
    async function main()
    {
        print(tab(33) + "BATTLE\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        // -- 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
        while (1) {
            for (x = 1; x <= 6; x++) {
                fa[x] = [];
                ha[x] = [];
                for (y = 1; y <= 6; y++) {
                    fa[x][y] = 0;
                    ha[x][y] = 0;
                }
            }
            for (i = 1; i <= 3; i++) {
                n = 4 - i;
                for (j = 1; j <= 2; j++) {
                    while (1) {
                        a = Math.floor(6 * Math.random() + 1);
                        b = Math.floor(6 * Math.random() + 1);
                        d = Math.floor(4 * Math.random() + 1);
                        if (fa[a][b] > 0)
                            continue;
                        m = 0;
                        switch (d) {
                            case 1:
                                ba[1] = b;
                                ba[2] = 7;
                                ba[3] = 7;
                                for (k = 1; k <= n; k++) {
                                    if (m <= 1 && ba[k] != 6 && fa[a][ba[k] + 1] <= 0) {
                                        ba[k + 1] = ba[k] + 1;
                                    } else {
                                        m = 2;
                                        if (ba[1] < ba[2] && ba[1] < ba[3])
                                            z = ba[1];
                                        if (ba[2] < ba[1] && ba[2] < ba[3])
                                            z = ba[2];
                                        if (ba[3] < ba[1] && ba[3] < ba[2])
                                            z = ba[3];
                                        if (z == 1)
                                            break;
                                        if (fa[a][z - 1] > 0)
                                            break;
                                        ba[k + 1] = z - 1;
                                    }
                                }
                                if (k <= n)
                                    continue;
                                fa[a][b] = 9 - 2 * i - j;
                                for (k = 1; k <= n; k++)
                                    fa[a][ba[k + 1]] = fa[a][b];
                                break;
                            case 2:
                                aa[1] = a;
                                ba[1] = b;
                                aa[2] = 0;
                                aa[3] = 0;
                                ba[2] = 0;
                                ba[3] = 0;
                                for (k = 1; k <= n; k++) {
                                    if (m <= 1 && aa[k] != 1 && ba[k] != 1 && fa[aa[k] - 1][ba[k] - 1] <= 0 && (fa[aa[k] - 1][ba[k]] <= 0 || fa[aa[k] - 1][ba[k]] != fa[aa[k]][ba[k] - 1])) {
                                        aa[k + 1] = aa[k] - 1;
                                        ba[k + 1] = ba[k] - 1;
                                    } else {
                                        m = 2;
                                        if (aa[1] > aa[2] && aa[1] > aa[3])
                                            z1 = aa[1];
                                        if (aa[2] > aa[1] && aa[2] > aa[3])
                                            z1 = aa[2];
                                        if (aa[3] > aa[1] && aa[3] > aa[2])
                                            z1 = aa[3];
                                        if (ba[1] > ba[2] && ba[1] > ba[3])
                                            z2 = ba[1];
                                        if (ba[2] > ba[1] && ba[2] > ba[3])
                                            z2 = ba[2];
                                        if (ba[3] > ba[1] && ba[3] > ba[2])
                                            z2 = ba[3];
                                        if (z1 == 6 || z2 == 6)
                                            break;
                                        if (fa[z1 + 1][z2 + 1] > 0)
                                            break;
                                        if (fa[z1][z2 + 1] > 0 && fa[z1][z2 + 1] == fa[z1 + 1][z2])
                                            break;
                                        aa[k + 1] = z1 + 1;
                                        ba[k + 1] = z2 + 1;
                                    }
                                }
                                if (k <= n)
                                    continue;
                                fa[a][b] = 9 - 2 * i - j;
                                for (k = 1; k <= n; k++)
                                    fa[aa[k + 1]][ba[k + 1]] = fa[a][b];
                                break;
                            case 3:
                                aa[1] = a;
                                aa[2] = 7;
                                aa[3] = 7;
                                for (k = 1; k <= n; k++) {
                                    if (m <= 1 && aa[k] != 6 && fa[aa[k] + 1][b] <= 0) {
                                        aa[k + 1] = aa[k] + 1;
                                    } else {
                                        m = 2;
                                        if (aa[1] < aa[2] && aa[1] < aa[3])
                                            z = aa[1];
                                        if (aa[2] < aa[1] && aa[2] < aa[3])
                                            z = aa[2];
                                        if (aa[3] < aa[1] && aa[3] < aa[2])
                                            z = aa[3];
                                        if (z == 1)
                                            break;
                                        if (fa[z - 1][b] > 0)
                                            break;
                                        aa[k + 1] = z - 1;
                                    }
                                }
                                if (k <= n)
                                    continue;
                                fa[a][b] = 9 - 2 * i - j;
                                for (k = 1; k <= n; k++)
                                    fa[aa[k + 1]][b] = fa[a][b];
                                break;
                            case 4:
                                aa[1] = a;
                                ba[1] = b;
                                aa[2] = 7;
                                aa[3] = 7;
                                ba[2] = 0;
                                ba[3] = 0;
                                for (k = 1; k <= n; k++) {
                                    if (m <= 1 && aa[k] != 6 && ba[k] != 1 && fa[aa[k] + 1][ba[k] - 1] <= 0 && (fa[aa[k] + 1][ba[k]] <= 0 || fa[aa[k] + 1][ba[k]] != fa[aa[k]][ba[k] - 1])) {
                                        aa[k + 1] = aa[k] + 1;
                                        ba[k + 1] = ba[k] - 1;
                                    } else {
                                        m = 2;
                                        if (aa[1] < aa[2] && aa[1] < aa[3])
                                            z1 = aa[1];
                                        if (aa[2] < aa[1] && aa[2] < aa[3])
                                            z1 = aa[2];
                                        if (aa[3] < aa[1] && aa[3] < aa[2])
                                            z1 = aa[3];
                                        if (ba[1] > ba[2] && ba[1] > ba[3])
                                            z2 = ba[1];
                                        if (ba[2] > ba[1] && ba[2] > ba[3])
                                            z2 = ba[2];
                                        if (ba[3] > ba[1] && ba[3] > ba[2])
                                            z2 = ba[3];
                                        if (z1 == 1 || z2 == 6)
                                            break;
                                        if (fa[z1 - 1][z2 + 1] > 0)
                                            break;
                                        if (fa[z1][z2 + 1] > 0 && fa[z1][z2 + 1] == fa[z1 - 1][z2])
                                            break;
                                        aa[k + 1] = z1 - 1;
                                        ba[k + 1] = z2 + 1;
                                    }
                                }
                                if (k <= n)
                                    continue;
                                fa[a][b] = 9 - 2 * i - j;
                                for (k = 1; k <= n; k++)
                                    fa[aa[k + 1]][ba[k + 1]] = fa[a][b];
                                break;
                        }
                        break;
                    }
                }
            }
            print("\n");
            print("THE FOLLOWING CODE OF THE BAD GUYS' FLEET DISPOSITION\n");
            print("HAS BEEN CAPTURED BUT NOT DECODED:\n");
            print("\n");
            for (i = 1; i <= 6; i++) {
                for (j = 1; j <= 6; j++) {
                    ha[i][j] = fa[j][i];
                }
            }
            for (i = 1; i <= 6; i++) {
                str = "";
                for (j = 1; j <= 6; j++) {
                    str += " " + ha[i][j] + " ";
                }
                print(str + "\n");
            }
            print("\n");
            print("DE-CODE IT AND USE IT IF YOU CAN\n");
            print("BUT KEEP THE DE-CODING METHOD A SECRET.\n");
            print("\n");
            for (i = 1; i <= 6; i++) {
                for (j = 1; j <= 6; j++) {
                    ha[i][j] = 0;
                }
            }
            for (i = 1; i <= 3; i++)
                la[i] = 0;
            ca[1] = 2;
            ca[2] = 2;
            ca[3] = 1;
            ca[4] = 1;
            ca[5] = 0;
            ca[6] = 0;
            s = 0;
            h = 0;
            print("START GAME\n");
            while (1) {
                str = await input();
                // Check if user types anything other than a number
                if (isNaN(str)) {
                    print("INVALID INPUT. TRY ENTERING A NUMBER INSTEAD.\n");
                    continue;
                }
                x = parseInt(str);
                y = parseInt(str.substr(str.indexOf(",") + 1));
                if (x < 1 || x > 6 || y < 1 || y > 6) {
                    print("INVALID INPUT.  TRY AGAIN.\n");
                    continue;
                }
                r = 7 - y;
                c = x;
                if (fa[r][c] <= 0) {
                    s++;
                    print("SPLASH!  TRY AGAIN.\n");
                    continue;
                }
                if (ca[fa[r][c]] >= 4) {
                    print("THERE USED TO BE A SHIP AT THAT POINT, BUT YOU SUNK IT.\n");
                    print("SPLASH!  TRY AGAIN.\n");
                    s++;
                    continue;
                }
                if (ha[r][c] > 0) {
                    print("YOU ALREADY PUT A HOLE IN SHIP NUMBER " + fa[r][c] + " AT THAT POINT.\n");
                    print("SPLASH!  TRY AGAIN.\n");
                    s++;
                    continue;
                }
                h++;
                ha[r][c] = fa[r][c];
                print("A DIRECT HIT ON SHIP NUMBER " + fa[r][c] + "\n");
                ca[fa[r][c]]++;
                if (ca[fa[r][c]] < 4) {
                    print("TRY AGAIN.\n");
                    continue;
                }
                la[Math.floor((fa[r][c] - 1) / 2) + 1]++;
                print("AND YOU SUNK IT.  HURRAH FOR THE GOOD GUYS.\n");
                print("SO FAR, THE BAD GUYS HAVE LOST\n");
                print(" " + la[1] + " DESTROYER(S), " + la[2] + " CRUISER(S), AND");
                print(" " + la[3] + " AIRCRAFT CARRIER(S).\n");
                print("YOUR CURRENT SPLASH/HIT RATIO IS " + s / h + "\n");
                if (la[1] + la[2] + la[3] < 6)
                    continue;
                print("\n");
                print("YOU HAVE TOTALLY WIPED OUT THE BAD GUYS' FLEET\n");
                print("WITH A FINAL SPLASH/HIT RATIO OF " + s / h + "\n");
                if (s / h <= 0) {
                    print("CONGRATULATIONS -- A DIRECT HIT EVERY TIME.\n");
                }
                print("\n");
                print("****************************\n");
                print("\n");
                break;
            }
        }
    }
    
    main();
    
    
    ================================================
    FILE: 09_Battle/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 09_Battle/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 09_Battle/pascal/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Pascal](https://en.wikipedia.org/wiki/Pascal_(programming_language))
    
    
    ================================================
    FILE: 09_Battle/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 09_Battle/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 09_Battle/python/battle.py
    ================================================
    #!/usr/bin/env python3
    from random import randrange
    from typing import List, Tuple
    
    PointType = Tuple[int, int]
    VectorType = PointType
    SeaType = Tuple[List[int], ...]
    
    SEA_WIDTH = 6
    DESTROYER_LENGTH = 2
    CRUISER_LENGTH = 3
    AIRCRAFT_CARRIER_LENGTH = 4
    
    
    def random_vector() -> Tuple[int, int]:
        while True:
            vector = (randrange(-1, 2), randrange(-1, 2))
    
            if vector == (0, 0):
                # We can't have a zero vector, so try again
                continue
    
            return vector
    
    
    def add_vector(point: PointType, vector: VectorType) -> PointType:
        return (point[0] + vector[0], point[1] + vector[1])
    
    
    def place_ship(sea: SeaType, size: int, code: int) -> None:
        while True:
            start = (randrange(1, SEA_WIDTH + 1), randrange(1, SEA_WIDTH + 1))
            vector = random_vector()
    
            # Get potential ship points
            point = start
            points = []
    
            for _ in range(size):
                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
            ):
                # ship out of bounds or crosses other ship, trying again
                continue
    
            # We found a valid spot, so actually place it now
            for point in points:
                set_value_at(code, point, sea)
    
            break
    
    
    def print_encoded_sea(sea: SeaType) -> None:
        for x in range(len(sea)):
            print(" ".join([str(sea[y][x]) for y in range(len(sea) - 1, -1, -1)]))
    
    
    def is_within_sea(point: PointType, sea: SeaType) -> bool:
        return (1 <= point[0] <= len(sea)) and (1 <= point[1] <= len(sea))
    
    
    def has_ship(sea: SeaType, code: int) -> bool:
        return any(code in row for row in sea)
    
    
    def count_sunk(sea: SeaType, *codes: int) -> int:
        return sum(not has_ship(sea, code) for code in codes)
    
    
    def value_at(point: PointType, sea: SeaType) -> int:
        return sea[point[1] - 1][point[0] - 1]
    
    
    def set_value_at(value: int, point: PointType, sea: SeaType) -> None:
        sea[point[1] - 1][point[0] - 1] = value
    
    
    def get_next_target(sea: SeaType) -> PointType:
        while True:
            try:
                guess = input("? ")
                point_str_list = guess.split(",")
    
                if len(point_str_list) != 2:
                    raise ValueError()
    
                point = (int(point_str_list[0]), int(point_str_list[1]))
    
                if not is_within_sea(point, sea):
                    raise ValueError()
    
                return point
            except ValueError:
                print(
                    f"INVALID. SPECIFY TWO NUMBERS FROM 1 TO {len(sea)}, SEPARATED BY A COMMA."
                )
    
    
    def setup_ships(sea: SeaType) -> None:
        place_ship(sea, DESTROYER_LENGTH, 1)
        place_ship(sea, DESTROYER_LENGTH, 2)
        place_ship(sea, CRUISER_LENGTH, 3)
        place_ship(sea, CRUISER_LENGTH, 4)
        place_ship(sea, AIRCRAFT_CARRIER_LENGTH, 5)
        place_ship(sea, AIRCRAFT_CARRIER_LENGTH, 6)
    
    
    def main() -> None:
        sea = tuple([0 for _ in range(SEA_WIDTH)] for _ in range(SEA_WIDTH))
        setup_ships(sea)
        print(
            """
                    BATTLE
    CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY
    
    THE FOLLOWING CODE OF THE BAD GUYS' FLEET DISPOSITION
    HAS BEEN CAPTURED BUT NOT DECODED:
    
    """
        )
        print_encoded_sea(sea)
        print(
            """
    
    DE-CODE IT AND USE IT IF YOU CAN
    BUT KEEP THE DE-CODING METHOD A SECRET.
    
    START GAME"""
        )
        splashes = 0
        hits = 0
    
        while True:
            target = get_next_target(sea)
            target_value = value_at(target, sea)
    
            if target_value < 0:
                print(
                    f"YOU ALREADY PUT A HOLE IN SHIP NUMBER {abs(target_value)} AT THAT POINT."
                )
    
            if target_value <= 0:
                print("SPLASH! TRY AGAIN.")
                splashes += 1
                continue
    
            print(f"A DIRECT HIT ON SHIP NUMBER {target_value}")
            hits += 1
            set_value_at(-target_value, target, sea)
    
            if not has_ship(sea, target_value):
                print("AND YOU SUNK IT. HURRAH FOR THE GOOD GUYS.")
                print("SO FAR, THE BAD GUYS HAVE LOST")
                print(
                    f"{count_sunk(sea, 1, 2)} DESTROYER(S),",
                    f"{count_sunk(sea, 3, 4)} CRUISER(S),",
                    f"AND {count_sunk(sea, 5, 6)} AIRCRAFT CARRIER(S).",
                )
    
            if any(has_ship(sea, code) for code in range(1, 7)):
                print(f"YOUR CURRENT SPLASH/HIT RATIO IS {splashes}/{hits}")
                continue
    
            print(
                "YOU HAVE TOTALLY WIPED OUT THE BAD GUYS' FLEET "
                f"WITH A FINAL SPLASH/HIT RATIO OF {splashes}/{hits}"
            )
    
            if not splashes:
                print("CONGRATULATIONS -- A DIRECT HIT EVERY TIME.")
    
            print("\n****************************")
            break
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 09_Battle/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 09_Battle/rust/Cargo.toml
    ================================================
    [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"
    
    
    ================================================
    FILE: 09_Battle/rust/src/main.rs
    ================================================
    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;
        }
    }
    
    
    ================================================
    FILE: 09_Battle/vbnet/Battle.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Battle", "Battle.vbproj", "{D8475464-CB9B-448F-89A7-5BA15193C495}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{D8475464-CB9B-448F-89A7-5BA15193C495}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{D8475464-CB9B-448F-89A7-5BA15193C495}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{D8475464-CB9B-448F-89A7-5BA15193C495}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{D8475464-CB9B-448F-89A7-5BA15193C495}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 09_Battle/vbnet/Battle.vbproj
    ================================================
    
      
        Exe
        Battle
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 09_Battle/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 10_Blackjack/README.md
    ================================================
    ### Blackjack
    
    This is a simulation of the card game of Blackjack or 21, Las Vegas style. This rather comprehensive version allows for up to seven players. On each hand a player may get another card (a hit), stand, split a hand in the event two identical cards were received or double down. Also, the dealer will ask for an insurance bet if he has an exposed ace.
    
    Cards are automatically reshuffled as the 51st card is reached. For greater realism, you may wish to change this to the 41st card. Actually, fanatical purists will want to modify the program so it uses three decks of cards instead of just one.
    
    This program originally surfaced at Digital Equipment Corp.; the author is unknown.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=18)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=33)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    
    #### Porting Notes
    
    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
    
    
    ================================================
    FILE: 10_Blackjack/blackjack.bas
    ================================================
    2 PRINT TAB(31);"BLACK JACK"
    4 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    6 PRINT:PRINT:PRINT
    10 DEF FNA(Q)=Q+11*(Q>=22)
    20 DIM P(15,12),Q(15),C(52),D(52),T(8),S(7),B(15)
    30 DIM R(15)
    40 REM--P(I,J) IS THE JTH CARD IN HAND I, Q(I) IS TOTAL OF HAND I
    50 REM--C IS THE DECK BEING DEALT FROM, D IS THE DISCARD PILE,
    60 REM--T(I) IS THE TOTAL FOR PLAYER I, S(I) IS THE TOTAL THIS HAND FOR
    70 REM--PLAYER I, B(I) IS TH BET FOR HAND I
    80 REM--R(I) IS THE LENGTH OF P(I,*)
    90 GOTO 1500
    100 REM--SUBROUTINE TO GET A CARD.  RESULT IS PUT IN X.
    110 IF C<51 THEN 230
    120 PRINT "RESHUFFLING"
    130 FOR D=D TO 1 STEP -1
    140 C=C-1
    150 C(C)=D(D)
    160 NEXT D
    170 FOR C1=52 TO C STEP -1
    180 C2=INT(RND(1)*(C1-C+1))+C
    190 C3=C(C2)
    200 C(C2)=C(C1)
    210 C(C1)=C3
    220 NEXT C1
    230 X=C(C)
    240 C=C+1
    250 RETURN
    300 REM--SUBROUTINE TO EVALUATE HAND I.  TOTAL IS PUT INTO
    310 REM--Q(I). TOTALS HAVE THE FOLLOWING MEANING:
    320 REM--  2-10...HARD 2-10
    330 REM-- 11-21...SOFT 11-21
    340 REM-- 22-32...HARD 11-21
    350 REM--  33+....BUSTED
    360 Q=0
    370 FOR Q2=1 TO R(I)
    380 X=P(I,Q2)
    390 GOSUB 500
    400 NEXT Q2
    410 Q(I)=Q
    420 RETURN
    500 REM--SUBROUTINE TO ADD CARD X TO TOTAL Q.
    510 X1=X: IF X1>10 THEN X1=10:  REM  SAME AS X1=10 MIN X
    520 Q1=Q+X1
    530 IF Q>=11 THEN 590
    540 IF X>1 THEN 570
    550 Q=Q+11
    560 RETURN
    570 Q=Q1-11*(Q1>=11)
    580 RETURN
    590 Q=Q1-(Q<=21 AND Q1>21)
    600 IF Q<33 THEN 620
    610 Q=-1
    620 RETURN
    700 REM--CARD PRINTING SUBROUTINE
    710 REM  D$ DEFINED ELSEWHERE
    720 PRINT MID$(D$,3*X-2,3);
    730 PRINT "  ";
    740 RETURN
    750 REM--ALTERNATIVE PRINTING ROUTINE
    760 PRINT " ";MID$(D$,3*X-1,2);
    770 PRINT "   ";
    780 RETURN
    800 REM--SUBROUTINE TO PLAY OUT A HAND.
    810 REM--NO SPLITTING OR BLACKJACKS ALLOWED
    820 H1=5
    830 GOSUB 1410
    840 H1=3
    850 ON H GOTO 950,930
    860 GOSUB 100
    870 B(I)=B(I)*2
    880 PRINT "RECEIVED A";
    890 GOSUB 700
    900 GOSUB 1100
    910 IF Q>0 THEN GOSUB 1300
    920 RETURN
    930 GOSUB 1320
    940 RETURN
    950 GOSUB 100
    960 PRINT "RECEIVED A";
    970 GOSUB 700
    980 GOSUB 1100
    990 IF Q<0 THEN 940
    1000 PRINT "HIT";
    1010 GOTO 830
    1100 REM--SUBROUTINE TO ADD A CARD TO ROW I
    1110 R(I)=R(I)+1
    1120 P(I,R(I))=X
    1130 Q=Q(I)
    1140 GOSUB 500
    1150 Q(I)=Q
    1160 IF Q>=0 THEN 1190
    1170 PRINT "...BUSTED"
    1180 GOSUB 1200
    1190 RETURN
    1200 REM--SUBROUTINE TO DISCARD ROW I
    1210 IF R(I)<>0 THEN 1230
    1220 RETURN
    1230 D=D+1
    1240 D(D)=P(I,R(I))
    1250 R(I)=R(I)-1
    1260 GOTO 1210
    1300 REM--PRINTS TOTAL OF HAND I
    1310 PRINT
    1320 AA=Q(I): GOSUB 3400
    1325 PRINT "TOTAL IS";AA
    1330 RETURN
    1400 REM--SUBROUTINE TO READ REPLY
    1410 REM  I$ DEFINED ELSEWHERE
    1420 INPUT H$: H$=LEFT$(H$,1)
    1430 FOR H=1 TO H1 STEP 2
    1440 IF H$=MID$(I$,H,1) THEN 1480
    1450 NEXT H
    1460 PRINT "TYPE ";MID$(I$,1,H1-1);" OR ";MID$(I$,H1,2);" PLEASE";
    1470 GOTO 1420
    1480 H=(H+1)/2
    1490 RETURN
    1500 REM--PROGRAM STARTS HERE
    1510 REM--INITIALIZE
    1520 D$="N A  2  3  4  5  6  7N 8  9 10  J  Q  K"
    1530 I$="H,S,D,/,"
    1540 FOR I=1 TO 13
    1550 FOR J=4*I-3 TO 4*I
    1560 D(J)=I
    1570 NEXT J
    1580 NEXT I
    1590 D=52
    1600 C=53
    1610 PRINT "DO YOU WANT INSTRUCTIONS";
    1620 INPUT H$
    1630 IF LEFT$(H$,1)="N" OR LEFT$(H$,1)="n" THEN 1760
    1640 PRINT "THIS IS THE GAME OF 21. AS MANY AS 7 PLAYERS MAY PLAY THE"
    1650 PRINT "GAME. ON EACH DEAL, BETS WILL BE ASKED FOR, AND THE"
    1660 PRINT "PLAYERS' BETS SHOULD BE TYPED IN. THE CARDS WILL THEN BE"
    1670 PRINT "DEALT, AND EACH PLAYER IN TURN PLAYS HIS HAND. THE"
    1680 PRINT "FIRST RESPONSE SHOULD BE EITHER 'D', INDICATING THAT THE"
    1690 PRINT "PLAYER IS DOUBLING DOWN, 'S', INDICATING THAT HE IS"
    1700 PRINT "STANDING, 'H', INDICATING HE WANTS ANOTHER CARD, OR '/',"
    1710 PRINT "INDICATING THAT HE WANTS TO SPLIT HIS CARDS. AFTER THE"
    1720 PRINT "INITIAL RESPONSE, ALL FURTHER RESPONSES SHOULD BE 'S' OR"
    1730 PRINT "'H', UNLESS THE CARDS WERE SPLIT, IN WHICH CASE DOUBLING"
    1740 PRINT "DOWN IS AGAIN PERMITTED. IN ORDER TO COLLECT FOR"
    1750 PRINT "BLACKJACK, THE INITIAL RESPONSE SHOULD BE 'S'."
    1760 PRINT "NUMBER OF PLAYERS";
    1770 INPUT N
    1775 PRINT
    1780 IF N<1 OR N>7 OR N>INT(N) THEN 1760
    1790 FOR I=1 TO 8: T(I)=0: NEXT I
    1800 D1=N+1
    1810 IF 2*D1+C>=52 THEN GOSUB 120
    1820 IF C=2 THEN C=C-1
    1830 FOR I=1 TO N: Z(I)=0: NEXT I
    1840 FOR I=1 TO 15: B(I)=0: NEXT I
    1850 FOR I=1 TO 15: Q(I)=0: NEXT I
    1860 FOR I=1 TO 7: S(I)=0: NEXT I
    1870 FOR I=1 TO 15: R(I)=0: NEXT I
    1880 PRINT "BETS:"
    1890 FOR I=1 TO N: PRINT "#";I;: INPUT Z(I): NEXT I
    1900 FOR I=1 TO N
    1910 IF Z(I)<=0 OR Z(I)>500 THEN 1880
    1920 B(I)=Z(I)
    1930 NEXT I
    1940 PRINT "PLAYER";
    1950 FOR I=1 TO N
    1960 PRINT I;"   ";
    1970 NEXT I
    1980 PRINT "DEALER"
    1990 FOR J=1 TO 2
    2000 PRINT TAB(5);
    2010 FOR I=1 TO D1
    2020 GOSUB 100
    2030 P(I,J)=X
    2040 IF J=1 OR I<=N THEN GOSUB 750
    2050 NEXT I
    2060 PRINT
    2070 NEXT J
    2080 FOR I=1 TO D1
    2090 R(I)=2
    2100 NEXT I
    2110 REM--TEST FOR INSURANCE
    2120 IF P(D1,1)>1 THEN 2240
    2130 PRINT "ANY INSURANCE";
    2140 INPUT H$
    2150 IF LEFT$(H$,1)<>"Y" THEN 2240
    2160 PRINT "INSURANCE BETS"
    2170 FOR I=1 TO N: PRINT "#";I;: INPUT Z(I): NEXT I
    2180 FOR I=1 TO N
    2190 IF Z(I)<0 OR Z(I)>B(I)/2 THEN 2160
    2200 NEXT I
    2210 FOR I=1 TO N
    2220 S(I)=Z(I)*(3*(-(P(D1,2)>=10))-1)
    2230 NEXT I
    2240 REM--TEST FOR DEALER BLACKJACK
    2250 L1=1: L2=1
    2252 IF P(D1,1)=1 AND P(D1,2)>9 THEN L1=0: L2=0
    2253 IF P(D1,2)=1 AND P(D1,1)>9 THEN L1=0: L2=0
    2254 IF L1<>0 OR L2<>0 THEN 2320
    2260 PRINT:PRINT "DEALER HAS A";MID$(D$,3*P(D1,2)-2,3);" IN THE HOLE ";
    2270 PRINT "FOR BLACKJACK"
    2280 FOR I=1 TO D1
    2290 GOSUB 300
    2300 NEXT I
    2310 GOTO 3140
    2320 REM--NO DEALER BLACKJACK
    2330 IF P(D1,1)>1 AND P(D1,1)<10 THEN 2350
    2340 PRINT:PRINT "NO DEALER BLACKJACK."
    2350 REM--NOW PLAY THE HANDS
    2360 FOR I=1 TO N
    2370 PRINT "PLAYER";I;
    2380 H1=7
    2390 GOSUB 1410
    2400 ON H GOTO 2550,2410,2510,2600
    2410 REM--PLAYER WANTS TO STAND
    2420 GOSUB 300
    2430 IF Q(I)<>21 THEN 2490
    2440 PRINT "BLACKJACK"
    2450 S(I)=S(I)+1.5*B(I)
    2460 B(I)=0
    2470 GOSUB 1200
    2480 GOTO 2900
    2490 GOSUB 1320
    2500 GOTO 2900
    2510 REM--PLAYER WANTS TO DOUBLE DOWN
    2520 GOSUB 300
    2530 GOSUB 860
    2540 GOTO 2900
    2550 REM--PLAYER WANTS TO BE HIT
    2560 GOSUB 300
    2570 H1=3
    2580 GOSUB 950
    2590 GOTO 2900
    2600 REM--PLAYER WANTS TO SPLIT
    2610 L1=P(I,1): IF P(I,1)>10 THEN L1=10
    2612 L2=P(I,2): IF P(I,2)>10 THEN L2=10
    2614 IF L1=L2 THEN 2640
    2620 PRINT "SPLITTING NOT ALLOWED."
    2630 GOTO 2370
    2640 REM--PLAY OUT SPLIT
    2650 I1=I+D1
    2660 R(I1)=2
    2670 P(I1,1)=P(I,2)
    2680 B(I+D1)=B(I)
    2690 GOSUB 100
    2700 PRINT "FIRST HAND RECEIVES A";
    2710 GOSUB 700
    2720 P(I,2)=X
    2730 GOSUB 300
    2740 PRINT
    2750 GOSUB 100
    2760 PRINT "SECOND HAND RECEIVES A";
    2770 I=I1
    2780 GOSUB 700
    2790 P(I,2)=X
    2800 GOSUB 300
    2810 PRINT
    2820 I=I1-D1
    2830 IF P(I,1)=1 THEN 2900
    2840 REM--NOW PLAY THE TWO HANDS
    2850 PRINT "HAND";1-(I>D1);
    2860 GOSUB 800
    2870 I=I+D1
    2880 IF I=I1 THEN 2850
    2890 I=I1-D1
    2900 NEXT I
    2910 GOSUB 300
    2920 REM--TEST FOR PLAYING DEALER'S HAND
    2930 FOR I=1 TO N
    2940 IF R(I)>0 OR R(I+D1)>0 THEN 3010
    2950 NEXT I
    2960 PRINT "DEALER HAD A";
    2970 X=P(D1,2)
    2980 GOSUB 700
    2990 PRINT " CONCEALED."
    3000 GOTO 3140
    3010 PRINT "DEALER HAS A";MID$(D$,3*P(D1,2)-2,3);" CONCEALED ";
    3020 I=D1
    3030 AA=Q(I): GOSUB 3400
    3035 PRINT "FOR A TOTAL OF";AA
    3040 IF AA>16 THEN 3130
    3050 PRINT "DRAWS";
    3060 GOSUB 100
    3070 GOSUB 750
    3080 GOSUB 1100
    3090 AA=Q: GOSUB 3400
    3095 IF Q>0 AND AA<17 THEN 3060
    3100 Q(I)=Q-(Q<0)/2
    3110 IF Q<0 THEN 3140
    3120 AA=Q: GOSUB 3400
    3125 PRINT "---TOTAL IS";AA
    3130 PRINT
    3140 REM--TALLY THE RESULT
    3150 REM
    3160 Z$="LOSES PUSHES WINS "
    3165 PRINT
    3170 FOR I=1 TO N
    3180 AA=Q(I): GOSUB 3400
    3182 AB=Q(I+D1): GOSUB 3410
    3184 AC=Q(D1): GOSUB 3420
    3186 S(I)=S(I)+B(I)*SGN(AA-AC)+B(I+D1)*SGN(AB-AC)
    3188 B(I+D1)=0
    3200 PRINT "PLAYER";I;
    3210 PRINT MID$(Z$,SGN(S(I))*6+7,6);" ";
    3220 IF S(I)<>0 THEN 3250
    3230 PRINT "      ";
    3240 GOTO 3260
    3250 PRINT ABS(S(I));
    3260 T(I)=T(I)+S(I)
    3270 PRINT "TOTAL=";T(I)
    3280 GOSUB 1200
    3290 T(D1)=T(D1)-S(I)
    3300 I=I+D1
    3310 GOSUB 1200
    3320 I=I-D1
    3330 NEXT I
    3340 PRINT "DEALER'S TOTAL=";T(D1)
    3345 PRINT
    3350 GOSUB 1200
    3360 GOTO 1810
    3400 AA=AA+11*(AA>=22): RETURN
    3410 AB=AB+11*(AB>=22): RETURN
    3420 AC=AC+11*(AC>=22): RETURN
    
    
    ================================================
    FILE: 10_Blackjack/csharp/Blackjack.csproj
    ================================================
    
    
      
        Exe
        Blackjack
        net5.0
      
    
    
    
    
    ================================================
    FILE: 10_Blackjack/csharp/Blackjack.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Blackjack", "Blackjack.csproj", "{83253F48-9CCD-475C-A990-8703F1A2E31C}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{83253F48-9CCD-475C-A990-8703F1A2E31C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{83253F48-9CCD-475C-A990-8703F1A2E31C}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{83253F48-9CCD-475C-A990-8703F1A2E31C}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{83253F48-9CCD-475C-A990-8703F1A2E31C}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 10_Blackjack/csharp/Card.cs
    ================================================
    namespace Blackjack
    {
        public class Card
        {
            private static readonly string[] _names = new[] {"A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"};
    
            public Card(int index)
            {
                Index = index;
            }
    
            public int Index { get; private set; }
    
            public string Name => _names[Index];
    
            public string IndefiniteArticle => (Index == 0 || Index == 7) ? "an" : "a";
    
            public bool IsAce => Index == 0;
    
            public int Value
            {
                get
                {
                    if (IsAce)
                        return 11;
                    if (Index > 8)
                        return 10;
                    return Index + 1;
                }
            }
        }
    }
    
    
    ================================================
    FILE: 10_Blackjack/csharp/Deck.cs
    ================================================
    using System;
    using System.Collections.Generic;
    
    namespace Blackjack
    {
        public class Deck
        {
            private static readonly Random _random = new Random();
    
            private readonly List _cards = new List(52);
            private readonly List _discards = new List(52);
    
            public Deck()
            {
                for (var index = 0; index < 12; index++)
                {
                    for (var suit = 0; suit < 4; suit++)
                    {
                        _discards.Add(new Card(index));
                    }
                }
                Reshuffle();
            }
    
            private void Reshuffle()
            {
                Console.WriteLine("Reshuffling");
    
                _cards.AddRange(_discards);
                _discards.Clear();
    
                for (var index1 = _cards.Count - 1; index1 > 0; index1--)
                {
                    var index2 = _random.Next(0, index1);
                    var swapCard = _cards[index1];
                    _cards[index1] = _cards[index2];
                    _cards[index2] = swapCard;
                }
            }
    
            public Card DrawCard()
            {
                if (_cards.Count < 2)
                    Reshuffle();
    
                var card = _cards[_cards.Count - 1];
                _cards.RemoveAt(_cards.Count - 1);
                return card;
            }
    
            public void Discard(IEnumerable cards)
            {
                _discards.AddRange(cards);
            }
        }
    }
    
    
    ================================================
    FILE: 10_Blackjack/csharp/Game.cs
    ================================================
    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace Blackjack
    {
        public class Game
        {
            private readonly Deck _deck = new Deck();
            private readonly int _numberOfPlayers;
            private readonly Player[] _players;
            private readonly Hand _dealerHand;
    
            public Game(int numberOfPlayers)
            {
                _numberOfPlayers = numberOfPlayers;
                _players = new Player[_numberOfPlayers];
                for (var playerIndex = 0; playerIndex < _numberOfPlayers; playerIndex++)
                    _players[playerIndex] = new Player(playerIndex);
                _dealerHand = new Hand();
            }
    
            public void PlayGame()
            {
                while (true)
                {
                    PlayRound();
                    TallyResults();
                    ResetRoundState();
                    Console.WriteLine();
                }
            }
    
            public void PlayRound()
            {
                GetPlayerBets();
    
                DealHands();
    
                // Test for insurance
                var dealerIsShowingAce = _dealerHand.Cards[0].IsAce;
                if (dealerIsShowingAce && Prompt.ForYesNo("Any insurance?"))
                {
                    Console.WriteLine("Insurance bets");
                    var insuranceBets = new int[_numberOfPlayers];
                    foreach (var player in _players)
                        insuranceBets[player.Index] = Prompt.ForInteger($"# {player.Index + 1} ?", 0, player.RoundBet / 2);
    
                    var insuranceEffectMultiplier = _dealerHand.IsBlackjack ? 2 : -1;
                    foreach (var player in _players)
                        player.RoundWinnings += insuranceBets[player.Index] * insuranceEffectMultiplier;
                }
    
                // Test for dealer blackjack
                var concealedCard = _dealerHand.Cards[0];
                if (_dealerHand.IsBlackjack)
                {
                    Console.WriteLine();
                    Console.WriteLine("Dealer has {0} {1} in the hole for blackjack.", concealedCard.IndefiniteArticle, concealedCard.Name);
                    return;
                }
                else if (dealerIsShowingAce)
                {
                    Console.WriteLine();
                    Console.WriteLine("No dealer blackjack.");
                }
    
                foreach (var player in _players)
                    PlayHand(player);
    
                // Dealer hand
                var allPlayersBusted = _players.All(p => p.Hand.IsBusted && (!p.SecondHand.Exists || p.SecondHand.IsBusted));
                if (allPlayersBusted)
                    Console.WriteLine("Dealer had {0} {1} concealed.", concealedCard.IndefiniteArticle, concealedCard.Name);
                else
                {
                    Console.WriteLine("Dealer has {0} {1} concealed for a total of {2}", concealedCard.IndefiniteArticle, concealedCard.Name, _dealerHand.Total);
                    if (_dealerHand.Total < 17)
                    {
                        Console.Write("Draws");
                        while (_dealerHand.Total < 17)
                        {
                            var card = _dealerHand.AddCard(_deck.DrawCard());
                            Console.Write("  {0}", card.Name);
                        }
                        if (_dealerHand.IsBusted)
                            Console.WriteLine("  ...Busted");
                        else
                            Console.WriteLine("  ---Total is {0}", _dealerHand.Total);
                    }
                }
            }
    
            private void GetPlayerBets()
            {
                Console.WriteLine("Bets:");
                foreach (var player in _players)
                    player.RoundBet = Prompt.ForInteger($"# {player.Name} ?", 1, 500);
            }
    
            private void DealHands()
            {
                Console.Write("Player ");
                foreach (var player in _players)
                    Console.Write("{0}     ", player.Name);
                Console.WriteLine("Dealer");
    
                for (var cardIndex = 0; cardIndex < 2; cardIndex++)
                {
                    Console.Write("      ");
                    foreach (var player in _players)
                        Console.Write("  {0,-4}", player.Hand.AddCard(_deck.DrawCard()).Name);
                    var dealerCard = _dealerHand.AddCard(_deck.DrawCard());
                    Console.Write("  {0,-4}", (cardIndex == 0) ? "XX" : dealerCard.Name);
    
                    Console.WriteLine();
                }
            }
    
            private void PlayHand(Player player)
            {
                var hand = player.Hand;
    
                Console.Write("Player {0} ", player.Name);
    
                var playerCanSplit = hand.Cards[0].Value == hand.Cards[1].Value;
                var command = Prompt.ForCommandCharacter("?", playerCanSplit ? "HSD/" : "HSD");
                switch (command)
                {
                    case "D":
                        player.RoundBet *= 2;
                        goto case "H";
    
                    case "H":
                        while (TakeHit(hand) && PromptForAnotherHit())
                        { }
                        if (!hand.IsBusted)
                            Console.WriteLine("Total is {0}", hand.Total);
                        break;
    
                    case "S":
                        if (hand.IsBlackjack)
                        {
                            Console.WriteLine("Blackjack!");
                            player.RoundWinnings = (int)(1.5 * player.RoundBet + 0.5);
                            player.RoundBet = 0;
                        }
                        else
                            Console.WriteLine("Total is {0}", hand.Total);
                        break;
    
                    case "/":
                        hand.SplitHand(player.SecondHand);
                        var card = hand.AddCard(_deck.DrawCard());
                        Console.WriteLine("First hand receives {0} {1}", card.IndefiniteArticle, card.Name);
                        card = player.SecondHand.AddCard(_deck.DrawCard());
                        Console.WriteLine("Second hand receives {0} {1}", card.IndefiniteArticle, card.Name);
    
                        for (int handNumber = 1; handNumber <= 2; handNumber++)
                        {
                            hand = (handNumber == 1) ? player.Hand : player.SecondHand;
    
                            Console.Write("Hand {0}", handNumber);
                            while (PromptForAnotherHit() && TakeHit(hand))
                            { }
                            if (!hand.IsBusted)
                                Console.WriteLine("Total is {0}", hand.Total);
                        }
                        break;
                }
            }
    
            private bool TakeHit(Hand hand)
            {
                var card = hand.AddCard(_deck.DrawCard());
                Console.Write("Received {0,-6}", $"{card.IndefiniteArticle} {card.Name}");
                if (hand.IsBusted)
                {
                    Console.WriteLine("...Busted");
                    return false;
                }
                return true;
            }
    
            private bool PromptForAnotherHit()
            {
                return String.Equals(Prompt.ForCommandCharacter(" Hit?", "HS"), "H");
            }
    
            private void TallyResults()
            {
                Console.WriteLine();
                foreach (var player in _players)
                {
                    player.RoundWinnings += CalculateWinnings(player, player.Hand);
                    if (player.SecondHand.Exists)
                        player.RoundWinnings += CalculateWinnings(player, player.SecondHand);
                    player.TotalWinnings += player.RoundWinnings;
    
                    Console.WriteLine("Player {0} {1,-6} {2,3}   Total= {3,5}",
                            player.Name,
                            (player.RoundWinnings > 0) ? "wins" : (player.RoundWinnings) < 0 ? "loses" : "pushes",
                            (player.RoundWinnings != 0) ? Math.Abs(player.RoundWinnings).ToString() : "",
                            player.TotalWinnings);
                }
                Console.WriteLine("Dealer's total= {0}", -_players.Sum(p => p.TotalWinnings));
            }
    
            private int CalculateWinnings(Player player, Hand hand)
            {
                if (hand.IsBusted)
                    return -player.RoundBet;
                if (hand.Total == _dealerHand.Total)
                    return 0;
                if (_dealerHand.IsBusted || hand.Total > _dealerHand.Total)
                    return player.RoundBet;
                return -player.RoundBet;
            }
    
            private void ResetRoundState()
            {
                foreach (var player in _players)
                {
                    player.RoundWinnings = 0;
                    player.RoundBet = 0;
                    player.Hand.Discard(_deck);
                    player.SecondHand.Discard(_deck);
                }
                _dealerHand.Discard(_deck);
            }
        }
    }
    
    
    ================================================
    FILE: 10_Blackjack/csharp/Hand.cs
    ================================================
    using System;
    using System.Collections.Generic;
    
    namespace Blackjack
    {
        public class Hand
        {
            private readonly List _cards = new List(12);
            private int _cachedTotal = 0;
    
            public Card AddCard(Card card)
            {
                _cards.Add(card);
                _cachedTotal = 0;
                return card;
            }
    
            public void Discard(Deck deck)
            {
                deck.Discard(_cards);
                _cards.Clear();
                _cachedTotal = 0;
            }
    
            public void SplitHand(Hand secondHand)
            {
                if (Count != 2 || secondHand.Count != 0)
                    throw new InvalidOperationException();
                secondHand.AddCard(_cards[1]);
                _cards.RemoveAt(1);
                _cachedTotal = 0;
            }
    
            public IReadOnlyList Cards => _cards;
    
            public int Count => _cards.Count;
    
            public bool Exists => _cards.Count > 0;
    
            public int Total
            {
                get
                {
                    if (_cachedTotal == 0)
                    {
                        var aceCount = 0;
                        foreach (var card in _cards)
                        {
                            _cachedTotal += card.Value;
                            if (card.IsAce)
                                aceCount++;
                        }
                        while (_cachedTotal > 21 && aceCount > 0)
                        {
                            _cachedTotal -= 10;
                            aceCount--;
                        }
                    }
                    return _cachedTotal;
                }
            }
    
            public bool IsBlackjack => Total == 21 && Count == 2;
    
            public bool IsBusted => Total > 21;
        }
    }
    
    
    ================================================
    FILE: 10_Blackjack/csharp/Player.cs
    ================================================
    namespace Blackjack
    {
        public class Player
        {
            public Player(int index)
            {
                Index = index;
                Name = (index + 1).ToString();
                Hand = new Hand();
                SecondHand = new Hand();
            }
    
            public int Index { get; private set; }
    
            public string Name { get; private set; }
    
            public Hand Hand { get; private set; }
    
            public Hand SecondHand { get; private set;}
    
            public int RoundBet { get; set; }
    
            public int RoundWinnings { get; set; }
    
            public int TotalWinnings { get; set; }
        }
    }
    
    
    ================================================
    FILE: 10_Blackjack/csharp/Program.cs
    ================================================
    using System;
    
    namespace Blackjack
    {
        static class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine("{0}BLACK JACK", new string(' ', 31));
                Console.WriteLine("{0}CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY", new string(' ', 15));
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine();
    
                OfferInstructions();
    
                var numberOfPlayers = Prompt.ForInteger("Number of players?", 1, 6);
                var game = new Game(numberOfPlayers);
                game.PlayGame();
            }
    
            private static void OfferInstructions()
            {
                if (!Prompt.ForYesNo("Do you want instructions?"))
                    return;
    
                Console.WriteLine("This is the game of 21. As many as 7 players may play the");
                Console.WriteLine("game. On each deal, bets will be asked for, and the");
                Console.WriteLine("players' bets should be typed in. The cards will then be");
                Console.WriteLine("dealt, and each player in turn plays his hand. The");
                Console.WriteLine("first response should be either 'D', indicating that the");
                Console.WriteLine("player is doubling down, 'S', indicating that he is");
                Console.WriteLine("standing, 'H', indicating he wants another card, or '/',");
                Console.WriteLine("indicating that he wants to split his cards. After the");
                Console.WriteLine("initial response, all further responses should be 's' or");
                Console.WriteLine("'H', unless the cards were split, in which case doubling");
                Console.WriteLine("down is again permitted. In order to collect for");
                Console.WriteLine("Blackjack, the initial response should be 'S'.");
            }
        }
    }
    
    
    ================================================
    FILE: 10_Blackjack/csharp/Prompt.cs
    ================================================
    using System;
    
    namespace Blackjack
    {
        public static class Prompt
        {
            public static bool ForYesNo(string prompt)
            {
                while(true)
                {
                    Console.Write("{0} ", prompt);
                    var input = Console.ReadLine();
                    if (input.StartsWith("y", StringComparison.InvariantCultureIgnoreCase))
                        return true;
                    if (input.StartsWith("n", StringComparison.InvariantCultureIgnoreCase))
                        return false;
                    WriteNotUnderstood();
                }
            }
    
            public static int ForInteger(string prompt, int minimum = 1, int maximum = int.MaxValue)
            {
                while (true)
                {
                    Console.Write("{0} ", prompt);
                    if (!int.TryParse(Console.ReadLine(), out var number))
                        WriteNotUnderstood();
                    else if (number < minimum || number > maximum)
                        Console.WriteLine("Sorry, I need a number between {0} and {1}.", minimum, maximum);
                    else
                        return number;
                }
            }
    
            public static string ForCommandCharacter(string prompt, string allowedCharacters)
            {
                while (true)
                {
                    Console.Write("{0} ", prompt);
                    var input = Console.ReadLine();
                    if (input.Length > 0)
                    {
                        var character = input.Substring(0, 1);
                        var characterIndex = allowedCharacters.IndexOf(character, StringComparison.InvariantCultureIgnoreCase);
                        if (characterIndex != -1)
                            return allowedCharacters.Substring(characterIndex, 1);
                    }
    
                    Console.WriteLine("Type one of {0} please", String.Join(", ", allowedCharacters.ToCharArray()));
                }
            }
    
            private static void WriteNotUnderstood()
            {
                Console.WriteLine("Sorry, I didn't understand.");
            }
        }
    }
    
    
    ================================================
    FILE: 10_Blackjack/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 10_Blackjack/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 10_Blackjack/java/src/Blackjack.java
    ================================================
    import java.io.InputStreamReader;
    import java.io.OutputStreamWriter;
    import java.io.Reader;
    import java.io.Writer;
    import java.util.Collections;
    
    /**
     * Plays a game of blackjack on the terminal. Looking at the code, the reader
     * might conclude that this implementation is "over engineered." We use many
     * techniques and patterns developed for much larger code bases to create more
     * maintainable code, which may not be as relevant for a simple game of
     * Blackjack. To wit, the rules and requirements are not likely to ever change
     * so there is not so much value making the code flexible.
     * 
     * Nevertheless, this is meant to be an example that the reader can learn good
     * Java coding techniques from. Furthermore, many of the "over-engineering"
     * tactics are as much about testability as they are about maintainability.
     * Imagine trying to manually test infrequent scenarios like Blackjack,
     * insurance, or splitting without any ability to automate a specific scenario
     * and the value of unit testing becomes immediately apparent.
     * 
     * Another "unnecessary" aspect of this codebase is good Javadoc. Again, this is
     * meant to be educational, but another often overlooked benefit is that most
     * IDEs will display Javadoc in "autocomplete" suggestions. This is remarkably
     * helpful when using a class as a quick reminder of what you coded earlier.
     * This is true even if no one ever publishes or reads the HTML output of the
     * javadoc.
     * 
     */
    public class Blackjack {
    	public static void main(String[] args) {
    		// Intuitively it might seem like the main program logic should be right
    		// here in 'main' and that we should just use System.in and System.out
    		// directly whenever we need them.  However, notice that System.out and
    		// System.in are just an OutputStream and InputStream respectively. By
    		// allowing alternate streams to be specified to Game at runtime, we can
    		// write non-interactive tests of the code. See UserIoTest as an
    		// example.
    		// Likewise, by allowing an alternative "shuffle" algorithm, test code
    		// can provide a deterministic card ordering.
    		try (Reader in = new InputStreamReader(System.in)) {
    			Writer out = new OutputStreamWriter(System.out);
    			UserIo userIo = new UserIo(in, out);
    			Deck deck = new Deck(cards -> {
    				userIo.println("RESHUFFLING");
    			    Collections.shuffle(cards);
    			    return cards;
    			});
    			Game game = new Game(deck, userIo);
    			game.run();
    		} catch (Exception e) {
    			// This allows us to elegantly handle CTRL+D / CTRL+Z by throwing an exception.
    			System.out.println(e.getMessage());
    			System.exit(1);
    		}
    	}
    }
    
    
    ================================================
    FILE: 10_Blackjack/java/src/Card.java
    ================================================
    /**
     * This is an example of an "record" class in Java. That's just a fancy way
     * of saying the properties (value and suit) can't change after the object has
     * been created (it has no 'setter' methods and the properties are implicitly 'final'). 
     *
     * Immutability often makes it easier to reason about code logic and avoid
     * certain classes of bugs.
     *
     * Since it would never make sense for a card to change in the middle of a game,
     * this is a good candidate for immutability.
     */
    record Card(int value, Suit suit) {
    
    	public enum Suit {
    		HEARTS, DIAMONDS, SPADES, CLUBS;
    	}
    
    	public Card {
            if(value < 1 || value > 13) {
                throw new IllegalArgumentException("Invalid card value " + value);
            }
            if(suit == null) {
                throw new IllegalArgumentException("Card suit must be non-null");
            }
    	}
    
        public String toString() {
            StringBuilder result = new StringBuilder(2); 
            if(value == 1) {
                result.append("A");
            } else if(value < 11) {
                result.append(value);
            } else if(value == 11) {
                result.append('J');
            } else if(value == 12) {
                result.append('Q');
            } else if(value == 13) {
                result.append('K');
            }
            // Uncomment to include the suit in output. Useful for debugging, but
            // doesn't match the original BASIC behavior.
            // result.append(suit.name().charAt(0));
            return result.toString();
        }
    
        /**
         * Returns the value of {@link #toString()} preceded by either "AN " or "A " depending on which is gramatically correct.
         * 
         * @return "AN [x]" when [x] is "an" ace or "an" 8, and "A [X]" otherwise.
         */
        public String toProseString() {
    		if(value == 1 || value == 8) {
                return "AN " + toString();
            } else {
                return "A " + toString();
            }
        }
    
    }
    
    ================================================
    FILE: 10_Blackjack/java/src/Deck.java
    ================================================
    import java.util.Collections;
    import java.util.LinkedList;
    import java.util.List;
    import java.util.function.Function;
    
    public class Deck {
    
        private LinkedList cards;
        private Function, LinkedList> shuffleAlgorithm;
        
        /**
         * Initialize the game deck with the given number of standard decks.
         * e.g. if you want to play with 2 decks, then {@code new Decks(2)} will
         * initialize 'cards' with 2 copies of a standard 52 card deck.
         * 
         * @param shuffleAlgorithm A function that takes the initial sorted card
         * list and returns a shuffled list ready to deal.
         * 
         */
        public Deck(Function, LinkedList> shuffleAlgorithm) {
            this.shuffleAlgorithm = shuffleAlgorithm;
        }
    
        /**
         * Deals one card from the deck, removing it from this object's state. If
         * the deck is empty, it will be reshuffled before dealing a new card.
         * 
         * @return The card that was dealt.
         */
        public Card deal() {
            if(cards == null || cards.isEmpty()) {
                reshuffle();
            }
            return cards.pollFirst();
        }
    
        /**
         * Shuffle the cards in this deck using the shuffleAlgorithm.
         */
        public void reshuffle() {
            LinkedList newCards = new LinkedList<>();
            for(Card.Suit suit : Card.Suit.values()) {
                for(int value = 1; value < 14; value++) {
                    newCards.add(new Card(value, suit));
                }
            }
            this.cards = this.shuffleAlgorithm.apply(newCards);
        }
    
        /**
         * Get the number of cards in this deck.
         * @return The number of cards in this deck. For example, 52 for a single deck.
         */
        public int size() {
            return cards.size();
        }
    
        /**
         * Returns the cards in this deck.
         * @return An immutable view of the cards in this deck.
         */
        public List getCards() {
            // The returned list is immutable because we don't want other code messing with the deck.
            return Collections.unmodifiableList(cards);
        }
    }
    
    
    ================================================
    FILE: 10_Blackjack/java/src/Game.java
    ================================================
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.List;
    import java.text.DecimalFormat;
    
    /**
     * This is the primary class that runs the game itself.
     */
    public class Game {
        
        private Deck deck;
        private UserIo userIo;
    
        public Game(Deck deck, UserIo userIo) {
            this.deck = deck;
            this.userIo = userIo;
        }
    
    	/**
    	 * Run the game, running rounds until ended with CTRL+D/CTRL+Z or CTRL+C
    	 */
        public void run() {
    		userIo.println("BLACK JACK", 31);
    		userIo.println("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n\n\n", 15);
    		if(userIo.promptBoolean("DO YOU WANT INSTRUCTIONS")){
    			userIo.println("THIS IS THE GAME OF 21. AS MANY AS 7 PLAYERS MAY PLAY THE");
    			userIo.println("GAME. ON EACH DEAL, BETS WILL BE ASKED FOR, AND THE");
    			userIo.println("PLAYERS' BETS SHOULD BE TYPED IN. THE CARDS WILL THEN BE");
    			userIo.println("DEALT, AND EACH PLAYER IN TURN PLAYS HIS HAND. THE");
    			userIo.println("FIRST RESPONSE SHOULD BE EITHER 'D', INDICATING THAT THE");
    			userIo.println("PLAYER IS DOUBLING DOWN, 'S', INDICATING THAT HE IS");
    			userIo.println("STANDING, 'H', INDICATING HE WANTS ANOTHER CARD, OR '/',");
    			userIo.println("INDICATING THAT HE WANTS TO SPLIT HIS CARDS. AFTER THE");
    			userIo.println("INITIAL RESPONSE, ALL FURTHER RESPONSES SHOULD BE 'S' OR");
    			userIo.println("'H', UNLESS THE CARDS WERE SPLIT, IN WHICH CASE DOUBLING");
    			userIo.println("DOWN IS AGAIN PERMITTED. IN ORDER TO COLLECT FOR");
    			userIo.println("BLACKJACK, THE INITIAL RESPONSE SHOULD BE 'S'.");
    		}
    
    		int nPlayers = 0;
    		while(nPlayers < 1 || nPlayers > 7) {
    			nPlayers = userIo.promptInt("NUMBER OF PLAYERS");
    		}
    
    		deck.reshuffle();
    
    		Player dealer = new Player(0); //Dealer is Player 0
    		
    		List players = new ArrayList<>();
    		for(int i = 0; i < nPlayers; i++) {
    			players.add(new Player(i + 1));
    		}
    
    		while(true) {
    			while(!betsAreValid(players)){
    				userIo.println("BETS:");
    				for(int i = 0; i < nPlayers; i++) {
    					double bet = userIo.promptDouble("#" + (i + 1)); // 1st player is "Player 1" not "Player 0"
    					players.get(i).setCurrentBet(bet);
    				}
    			}
    
    			// It doesn't *really* matter whether we deal two cards at once to each player
    			// or one card to each and then a second card to each, but this technically
    			// mimics the way a deal works in real life.
    			for(int i = 0; i < 2; i++){
    				for(Player player : players){
    					player.dealCard(deck.deal());
    				}
    				dealer.dealCard(deck.deal());
    			}
    
    			printInitialDeal(players, dealer);
    
    			if(dealer.getHand().get(0).value() == 1) {
    				collectInsurance(players);
    			}
    
    			if(ScoringUtils.scoreHand(dealer.getHand()) == 21) {
    				userIo.println("DEALER HAS " + dealer.getHand().get(1).toProseString() + " IN THE HOLE");
    				userIo.println("FOR BLACKJACK");
    			} else {
    				Card dealerFirstCard = dealer.getHand().get(0);
    				if(dealerFirstCard.value() == 1 || dealerFirstCard.value() > 9) {
    					userIo.println("");
    					userIo.println("NO DEALER BLACKJACK.");
    				} // else dealer blackjack is imposible
    				for(Player player : players){
    					play(player);
    				}
    
    				if(shouldPlayDealer(players)){
    					playDealer(dealer);
    				} else {
    					userIo.println("DEALER HAD " + dealer.getHand().get(1).toProseString() + " CONCEALED.");
    				}
    			}
    
    			evaluateRound(players, dealer);
    		} 
        }
    
    	protected void collectInsurance(Iterable players) {
    		boolean isInsurance = userIo.promptBoolean("ANY INSURANCE");
    		if(isInsurance) {
    			userIo.println("INSURANCE BETS");
    			for(Player player : players) {
    				while(true) {
    					double insuranceBet = userIo.promptDouble("# " + player.getPlayerNumber() + " ");
    					// 0 indicates no insurance for that player.
    					if(insuranceBet >= 0 && insuranceBet <= (player.getCurrentBet() / 2)) {
    						player.setInsuranceBet(insuranceBet);
    						break;
    					}
    				}
    			}
    		}
    	}
    
    	/**
    	 * Print the cards for each player and the up card for the dealer.
    	 * Prints the initial deal in the following format:
    	 *		
    	 *	PLAYER 1     2    DEALER
         *         7    10     4   
         *         2     A   
    	 */
    	private void printInitialDeal(List players, Player dealer) {
    	
            StringBuilder output = new StringBuilder(); 
    		output.append("PLAYERS ");
    		for (Player player : players) {
    			output.append(player.getPlayerNumber() + "\t");
    		}
    		output.append("DEALER\n");
    		//Loop through two rows of cards		
            for (int j = 0; j < 2; j++) {
    			output.append("\t");
    			for (Player player : players) {
    				output.append(player.getHand().get(j).toString()).append("\t");
    			}
    			if(j == 0 ){
    				output.append(dealer.getHand().get(j).toString());
    			}
    			output.append("\n");
    		}
    		userIo.print(output.toString());
    	}
    
    	/**
    	 * Plays the players turn. Prompts the user to hit (H), stay (S), or if
    	 * appropriate, split (/) or double down (D), and then performs those
    	 * actions. On a hit, prints "RECEIVED A  [x]  HIT? "
    	 * 
    	 * @param player
    	 */
    	protected void play(Player player) {
    		play(player, 1);
    	}
    
    	private void play(Player player, int handNumber) {
    		String action;
    		if(player.isSplit()){
    			action = userIo.prompt("HAND " + handNumber);
    		} else {
    			action = userIo.prompt("PLAYER " + player.getPlayerNumber() + " ");
    		}
    		while(true){
    			if(action.equalsIgnoreCase("H")){ // HIT
    				Card c = deck.deal();
    				player.dealCard(c, handNumber);
    				if(ScoringUtils.scoreHand(player.getHand(handNumber)) > 21){
    					userIo.println("RECEIVED " + c.toProseString() + "  ...BUSTED");
    					break;
    				}
    				action = userIo.prompt("RECEIVED " + c.toProseString() + " HIT");
    			} else if(action.equalsIgnoreCase("S")){ // STAY
    				break;
    			} else if(action.equalsIgnoreCase("D") && player.canDoubleDown(handNumber)) { // DOUBLE DOWN
    				Card c = deck.deal();
    				player.doubleDown(c, handNumber);
    				if(ScoringUtils.scoreHand(player.getHand(handNumber)) > 21){
    					userIo.println("RECEIVED " + c.toProseString() + "  ...BUSTED");
    					break;
    				}
    				userIo.println("RECEIVED " + c.toProseString());
    				break;
    			} else if(action.equalsIgnoreCase("/")) { // SPLIT
    				if(player.isSplit()) {
    					// The original basic code printed different output
    					// if a player tries to split twice vs if they try to split
    					// a non-pair hand.
    					action = userIo.prompt("TYPE H, S OR D, PLEASE");
    				} else if(player.canSplit()) {
    					player.split();
    					Card card = deck.deal();
    					player.dealCard(card, 1);
    					userIo.println("FIRST HAND RECEIVES " + card.toProseString());
    					card = deck.deal();
    					player.dealCard(card, 2);
    					userIo.println("SECOND HAND RECEIVES " + card.toProseString());					
    					if(player.getHand().get(0).value() > 1){ //Can't play after splitting aces
    						play(player, 1);
    						play(player, 2);
    					}
    					return; // Don't fall out of the while loop and print another total
    				} else {
    					userIo.println("SPLITTING NOT ALLOWED");
    					action = userIo.prompt("PLAYER " + player.getPlayerNumber() + " ");
    				}
    			} else {
    				if(player.getHand(handNumber).size() == 2) {
    					action = userIo.prompt("TYPE H,S,D, OR /, PLEASE");
    				} else {
    					action = userIo.prompt("TYPE H, OR S, PLEASE");
    				}
    			}
    		}
    		int total = ScoringUtils.scoreHand(player.getHand(handNumber));
    		if(total == 21) {
    			userIo.println("BLACKJACK");
    		} else {
    			userIo.println("TOTAL IS " + total);
    		}
    	}
    
    	/**
    	 * Check the Dealer's hand should be played out. If every player has either busted or won with natural Blackjack,
    	 * the Dealer doesn't need to play.
    	 * 
    	 * @param players
    	 * @return boolean whether the dealer should play
    	 */
    	protected boolean shouldPlayDealer(List players){
    		for(Player player : players){
    			int score = ScoringUtils.scoreHand(player.getHand());
    			if(score < 21 || (score == 21 && player.getHand().size() > 2)){
    				return true;
    			}
    			if(player.isSplit()){				
    				int splitScore = ScoringUtils.scoreHand(player.getHand(2));
    				if(splitScore < 21 || (splitScore == 21 && player.getHand(2).size() > 2)){
    					return true;
    				}
    			}
    		}
    		return false;
    	}	
    
    	/**
    	 * Play the dealer's hand. The dealer draws until they have >=17 or busts. Prints each draw as in the following example:
    	 * 
    	 * DEALER HAS A  5 CONCEALED FOR A TOTAL OF 11 
    	 * DRAWS 10   ---TOTAL IS 21
    	 *  
    	 * @param dealerHand
    	 */
    	protected void playDealer(Player dealer) {
    		int score = ScoringUtils.scoreHand(dealer.getHand());
    		userIo.println("DEALER HAS " + dealer.getHand().get(1).toProseString() + " CONCEALED FOR A TOTAL OF " + score);
    
    		if(score < 17){
    			userIo.print("DRAWS");
    		}
    		while(score < 17) {
    			Card dealtCard = deck.deal();
    			dealer.dealCard(dealtCard);
    			score = ScoringUtils.scoreHand(dealer.getHand());
    			userIo.print("  " + String.format("%-4s", dealtCard.toString()));
    		}
    		
    		if(score > 21) {
    			userIo.println("...BUSTED\n");
    		} else {
    			userIo.println("---TOTAL IS " + score + "\n");
    		}
    	}
    
    	/**
    	 * Evaluates the result of the round, prints the results, and updates player/dealer totals.
    	 * 
    	 *	PLAYER 1 LOSES   100 TOTAL=-100 
    	 *	PLAYER 2  WINS   150 TOTAL= 150
    	 *	DEALER'S TOTAL= 200
    	  *
    	 * @param players
    	 * @param dealerHand
    	 */
    	protected void evaluateRound(List players, Player dealer) {
    		DecimalFormat formatter = new DecimalFormat("0.#"); //Removes trailing zeros
    		for(Player player : players){
    			int result = ScoringUtils.compareHands(player.getHand(), dealer.getHand());
    			double totalBet = 0;
    			if(result > 0) {
    				totalBet += player.getCurrentBet();
    			} else if(result < 0){
    				totalBet -= player.getCurrentBet();
    			}
    			if(player.isSplit()) {
    				int splitResult = ScoringUtils.compareHands(player.getHand(2), dealer.getHand());
    				if(splitResult > 0){
    					totalBet += player.getSplitBet();
    				} else if(splitResult < 0){
    					totalBet -= player.getSplitBet();
    				} 
    			}
    			if(player.getInsuranceBet() != 0){
    				int dealerResult = ScoringUtils.scoreHand(dealer.getHand());
    				if(dealerResult == 21 && dealer.getHand().size() == 2){
    					totalBet += (player.getInsuranceBet() * 2);
    				} else {
    					totalBet -= player.getInsuranceBet();
    				}
    			}
    			
    			userIo.print("PLAYER " + player.getPlayerNumber());
    			if(totalBet < 0) {
    				userIo.print(" LOSES " + String.format("%6s", formatter.format(Math.abs(totalBet)))); 
    			} else if(totalBet > 0) {
    				userIo.print("  WINS " + String.format("%6s", formatter.format(totalBet))); 
    			} else {
    				userIo.print(" PUSHES      ");
    			}
    			player.recordRound(totalBet);
    			dealer.recordRound(totalBet * (-1));
    			userIo.println(" TOTAL= " + formatter.format(player.getTotal()));
    			player.resetHand();
    		}
    		userIo.println("DEALER'S TOTAL= " + formatter.format(dealer.getTotal()) + "\n");
    		dealer.resetHand();
    	}
    
    	/**
    	 * Validates that all bets are between 0 (exclusive) and 500 (inclusive). Fractional bets are valid.
    	 * 
    	 * @param players The players with their current bet set.
    	 * @return true if all bets are valid, false otherwise.
    	 */
    	public boolean betsAreValid(Collection players) {
    		return players.stream()
    			.map(Player::getCurrentBet)
    			.allMatch(bet -> bet > 0 && bet <= 500);
    	}
    }
    
    
    ================================================
    FILE: 10_Blackjack/java/src/Player.java
    ================================================
    import java.util.Collections;
    import java.util.LinkedList;
    import java.util.List;
    
    /**
     * Represents a player and data related to them (number, bets, cards).
     */
    public class Player {
    
        private int playerNumber;     // e.g. playerNumber = 1 means "this is Player 1"
        private double currentBet;
        private double insuranceBet; // 0 when the player has not made an insurance bet (either it does not apply or they chose not to)
        private double splitBet; // 0 whenever the hand is not split
        private double total;
        private LinkedList hand;
        private LinkedList splitHand; // null whenever the hand is not split
    
        /**
        * Represents a player in the game with cards, bets, total and a playerNumber. 
        */
        public Player(int playerNumber) {
            this.playerNumber = playerNumber;
            currentBet = 0;
            insuranceBet = 0;
            splitBet = 0;
            total = 0;
            hand = new LinkedList<>();
            splitHand = null;
        }
    
        public int getPlayerNumber() {
            return this.playerNumber;
        }
        
        public double getCurrentBet() {
            return this.currentBet;
        }
    
        public void setCurrentBet(double currentBet) {
            this.currentBet = currentBet;
        }
    
        public double getSplitBet() {
            return splitBet;
        }
    
        public double getInsuranceBet() {
            return insuranceBet;
        }
    
        public void setInsuranceBet(double insuranceBet) {
            this.insuranceBet = insuranceBet;
        }
    
        /**
        * RecordRound adds input paramater 'totalBet' to 'total' and then 
        * sets 'currentBet', 'splitBet', and 'insuranceBet' to zero
        */
        public void recordRound(double totalBet) {
            this.total = this.total + totalBet;
            this.currentBet = 0;
            this.splitBet = 0;
            this.insuranceBet = 0;
        }
    
        /**
         * Returns the total of all bets won/lost.
         * @return Total value
         */
        public double getTotal() {
            return this.total;
        }
    
        /**
         * Add the given card to the players main hand.
         * 
         * @param card The card to add.
         */
        public void dealCard(Card card) {
            dealCard(card, 1);
        }
        
        /**
         * Adds the given card to the players hand or split hand depending on the handNumber.
         * 
         * @param card The card to add
         * @param handNumber 1 for the "first" hand and 2 for the "second" hand in a split hand scenario.
         */
        public void dealCard(Card card, int handNumber) {
            if(handNumber == 1) {
                hand.add(card);
            } else if (handNumber == 2) {
                splitHand.add(card);
            } else {
                throw new IllegalArgumentException("Invalid hand number " + handNumber);
            }
        }
    
        /**
         * Determines whether the player is eligible to split.
         * @return True if the player has not already split, and their hand is a pair. False otherwise.
         */
        public boolean canSplit() {
            if(isSplit()) {
                // Can't split twice
                return false;
            } else {
                boolean isPair = this.hand.get(0).value() == this.hand.get(1).value();
                return isPair;
            }
        }
    
        /**
         * Determines whether the player has already split their hand.
         * @return false if splitHand is null, true otherwise.
         */
        public boolean isSplit() {
            return this.splitHand != null;
        }
    
        /**
         * Removes first card from hand to add it to new split hand
         */
        public void split() {
            this.splitBet = this.currentBet;
            this.splitHand = new LinkedList<>();
            splitHand.add(hand.pop());
        }
    
        /**
         * Determines whether the player can double down.
         * 
         * @param handNumber
         * @return
         */
        public boolean canDoubleDown(int handNumber) {
            if(handNumber == 1){
                return this.hand.size() == 2;
            } else if(handNumber == 2){
                return this.splitHand.size() == 2;
            } else {
                throw new IllegalArgumentException("Invalid hand number " + handNumber);
            }
        }
    
        /**
         * Doubles down on the given hand. Specifically, this method doubles the bet for the given hand and deals the given card.
         * 
         * @param card The card to deal
         * @param handNumber The hand to deal to and double the bet for
         */
        public void doubleDown(Card card, int handNumber) {
            if(handNumber == 1){
                this.currentBet = this.currentBet * 2;
            } else if(handNumber == 2){
                this.splitBet = this.splitBet * 2;
            } else {
                throw new IllegalArgumentException("Invalid hand number " + handNumber);
            }
            this.dealCard(card, handNumber);
        }
    
        /**
         * Resets the hand to an empty list and the splitHand to null.
         */
        public void resetHand() {
            this.hand = new LinkedList<>();
            this.splitHand = null;
        }
    
        public List getHand() {
            return getHand(1);
        }
    
        /**
         * Returns the given hand
         * @param handNumber 1 for the "first" of a split hand (or the main hand when there is no split) or 2 for the "second" hand of a split hand.
         * @return The hand specified by handNumber
         */
        public List getHand(int handNumber) {
            if(handNumber == 1){
                return Collections.unmodifiableList(this.hand);
            } else if(handNumber == 2){
                return Collections.unmodifiableList(this.splitHand);
            } else {
                throw new IllegalArgumentException("Invalid hand number " + handNumber);
            }
        }
    
    }
    
    ================================================
    FILE: 10_Blackjack/java/src/ScoringUtils.java
    ================================================
    import java.util.List;
    
    public final class ScoringUtils {
    
    	/**
    	 * Calculates the value of a hand. When the hand contains aces, it will
    	 * count one of them as 11 if that does not result in a bust.
    	 * 
    	 * @param hand the hand to evaluate
    	 * @return The numeric value of a hand. A value over 21 indicates a bust.
    	 */
    	public static final int scoreHand(List hand) {
    		int nAces = (int) hand.stream().filter(c -> c.value() == 1).count();
    		int value = hand.stream()
    				.mapToInt(Card::value)
    				.filter(v -> v != 1) // start without aces
    				.map(v -> v > 10 ? 10 : v) // all face cards are worth 10. The 'expr ? a : b' syntax is called the
    											// 'ternary operator'
    				.sum();
    		value += nAces; // start by treating all aces as 1
    		if (nAces > 0 && value <= 11) {
    			value += 10; // We can use one of the aces to an 11
    			// You can never use more than one ace as 11, since that would be 22 and a bust.
    		}
    		return value;
    	}
    
    	/**
    	 * Compares two hands accounting for natural blackjacks and busting using the
    	 * java.lang.Comparable convention of returning positive or negative integers
    	 * 
    	 * @param handA hand to compare
    	 * @param handB other hand to compare
    	 * @return a negative integer, zero, or a positive integer as handA is less
    	 *         than, equal to, or greater than handB.
    	 */
    	public static final int compareHands(List handA, List handB) {
    		int scoreA = scoreHand(handA);
    		int scoreB = scoreHand(handB);
    		if (scoreA == 21 && scoreB == 21) {
    			if (handA.size() == 2 && handB.size() != 2) {
    				return 1; // Hand A wins with a natural blackjack
    			} else if (handA.size() != 2 && handB.size() == 2) {
    				return -1; // Hand B wins with a natural blackjack
    			} else {
    				return 0; // Tie
    			}
    		} else if (scoreA > 21 || scoreB > 21) {
    			if (scoreA > 21 && scoreB > 21) {
    				return 0; // Tie, both bust
    			} else if (scoreB > 21) {
    				return 1; // A wins, B busted
    			} else {
    				return -1; // B wins, A busted
    			}
    		} else {
    			return Integer.compare(scoreA, scoreB);
    		}
    	}
    
    }
    
    
    ================================================
    FILE: 10_Blackjack/java/src/UserIo.java
    ================================================
    import java.io.BufferedReader;
    import java.io.EOFException;
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.io.Reader;
    import java.io.UncheckedIOException;
    import java.io.Writer;
    import java.util.stream.IntStream;
    
    /**
     * This class is responsible for printing output to the screen and reading input
     * from the user. It must be initialized with a reader to get input data from
     * and a writer to send output to. Typically these will wrap System.in and
     * System.out respectively, but can be a StringReader and StringWriter when
     * running in test code.
     */
    public class UserIo {
    
        private BufferedReader in;
        private PrintWriter out;
    
    	/**
    	 * Initializes the UserIo with the given reader/writer. The reader will be
    	 * wrapped in a BufferedReader and so should not be a BufferedReader
    	 * already (to avoid double buffering).
    	 * 
    	 * @param in Typically an InputStreamReader wrapping System.in or a StringReader
    	 * @param out Typically an OuputStreamWriter wrapping System.out or a StringWriter
    	 */
        public UserIo(Reader in, Writer out) {
            this.in = new BufferedReader(in);
            this.out = new PrintWriter(out, true);
        }
    
    	/**
    	 * Print the line of text to output including a trailing linebreak.
    	 * 
    	 * @param text the text to print
    	 */
    	public void println(String text) {
    		out.println(text);
    	}
    
    	/**
    	 * Print the given text left padded with spaces.
    	 * 
    	 * @param text The text to print
    	 * @param leftPad The number of spaces to pad with.
    	 */
    	public void println(String text, int leftPad) {
    		IntStream.range(0, leftPad).forEach((i) -> out.print(' '));
    		out.println(text);
    	}
    
    	/**
    	 * Print the given text without a trailing linebreak.
    	 * 
    	 * @param text The text to print.
    	 */
    	public void print(String text) {
    		out.print(text);
    		out.flush();
    	}
    
    	/**
    	 * Reads a line of text from input.
    	 * 
    	 * @return The line entered into input.
    	 * @throws UncheckedIOException if the line is null (CTRL+D or CTRL+Z was pressed)
    	 */
    	private String readLine() {
    		try {
    			String line = in.readLine();
    			if(line == null) {
    				throw new UncheckedIOException("!END OF INPUT", new EOFException());
    			}
    			return line;
    		} catch (IOException e) {
    			throw new UncheckedIOException(e);
    		}
    	}
    
    	/**
    	 * Prompt the user via input.
    	 * 
    	 * @param prompt The text to display as a prompt. A question mark and space will be added to the end, so if prompt = "EXAMPLE" then the user will see "EXAMPLE? ".
    	 * @return The line read from input.
    	 */
    	public String prompt(String prompt) {
    		print(prompt + "? ");
    		return readLine();
    	}
    
    	/**
    	 * Prompts the user for a "Yes" or "No" answer.
    	 * @param prompt The prompt to display to the user on STDOUT.
    	 * @return false if the user enters a value beginning with "N" or "n"; true otherwise.
    	 */
    	public boolean promptBoolean(String prompt) {
    		print(prompt + "? ");
    
    		String input = readLine();
    
    		if(input.toLowerCase().startsWith("n")) {
    			return false;
    		} else {
    			return true;
    		}
    	}
    
    	/**
    	 * Prompts the user for an integer.  As in Vintage Basic, "the optional
    	 * prompt string is followed by a question mark and a space." and if the
    	 * input is non-numeric, "an error will be generated and the user will be
    	 * re-prompted.""
    	 *
    	 * @param prompt The prompt to display to the user.
    	 * @return the number given by the user.
    	 */
    	public int promptInt(String prompt) {
    		print(prompt + "? ");
    
    		while(true) {
    			String input = readLine();
    			try {
    				return Integer.parseInt(input);
    			} catch(NumberFormatException e) {
    				// Input was not numeric.
    				println("!NUMBER EXPECTED - RETRY INPUT LINE");
    				print("? ");
    				continue;
    			}
    		}
    	}
    
    	/**
    	 * Prompts the user for a double.  As in Vintage Basic, "the optional
    	 * prompt string is followed by a question mark and a space." and if the
    	 * input is non-numeric, "an error will be generated and the user will be
    	 * re-prompted.""
    	 *
    	 * @param prompt The prompt to display to the user.
    	 * @return the number given by the user.
    	 */
    	public double promptDouble(String prompt) {
    		print(prompt + "? ");
    
    		while(true) {
    			String input = readLine();
    			try {
    				return Double.parseDouble(input);
    			} catch(NumberFormatException e) {
    				// Input was not numeric.
    				println("!NUMBER EXPECTED - RETRY INPUT LINE");
    				print("? ");
    				continue;
    			}
    		}
    	}
    }
    
    
    ================================================
    FILE: 10_Blackjack/java/test/DeckTest.java
    ================================================
    import static org.junit.jupiter.api.Assertions.assertEquals;
    import static org.junit.jupiter.api.Assertions.assertAll;
    import org.junit.jupiter.api.Test;
    
    public class DeckTest {
    
        @Test
        void testInit() {
            // When
            Deck deck = new Deck((cards) -> cards);
            deck.reshuffle();
    
            // Then
            long nCards = deck.size();
            long nSuits = deck.getCards().stream()
                    .map(card -> card.suit())
                    .distinct()
                    .count();
            long nValues = deck.getCards().stream()
                    .map(card -> card.value())
                    .distinct()
                    .count();
    
            assertAll("deck",
                () -> assertEquals(52, nCards, "Expected 52 cards in a deck, but got " + nCards),
                () -> assertEquals(4, nSuits, "Expected 4 suits, but got " + nSuits),
                () -> assertEquals(13, nValues, "Expected 13 values, but got " + nValues)
            );
            
        }
    
    }
    
    
    ================================================
    FILE: 10_Blackjack/java/test/GameTest.java
    ================================================
    import org.junit.jupiter.api.Test;
    
    import org.junit.jupiter.api.AfterEach;
    import org.junit.jupiter.api.BeforeEach;
    import org.junit.jupiter.api.DisplayName;
    
    import static org.junit.jupiter.api.Assertions.assertAll;
    import static org.junit.jupiter.api.Assertions.assertEquals;
    import static org.junit.jupiter.api.Assertions.assertFalse;
    import static org.junit.jupiter.api.Assertions.assertThrows;
    import static org.junit.jupiter.api.Assertions.assertTrue;
    
    import java.io.EOFException;
    import java.io.StringReader;
    import java.io.StringWriter;
    import java.io.UncheckedIOException;
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.LinkedList;
    import java.util.List;
    
    public class GameTest {
    
        private StringReader in;
        private StringWriter out;
        private Game game;
    
        private StringBuilder playerActions;
        private LinkedList cards;
    
        @BeforeEach
        public void resetIo() {
            in = null;
            out = null;
            game = null;
            playerActions = new StringBuilder();
            cards = new LinkedList<>();
        }
    
        private void playerGets(int value, Card.Suit suit) {
            cards.add(new Card(value, suit));
        }
    
        private void playerSays(String action) {
            playerActions.append(action).append(System.lineSeparator());
        }
    
        private void initGame() {
            System.out.printf("Running game with input: %s\tand cards: %s\n",playerActions.toString(), cards);
            in = new StringReader(playerActions.toString());
            out = new StringWriter();
            UserIo userIo = new UserIo(in, out);
            Deck deck = new Deck((c) -> cards);
            game = new Game(deck, userIo);
        }
    
        @AfterEach
        private void printOutput() {
            System.out.println(out.toString());
        }
    
        @Test
        public void shouldQuitOnCtrlD() {
            // Given
            playerSays("\u2404"); // U+2404 is "End of Transmission" sent by CTRL+D (or CTRL+Z on Windows)
            initGame();
    
            // When
            Exception e = assertThrows(UncheckedIOException.class, game::run);
    
            // Then
            assertTrue(e.getCause() instanceof EOFException);
            assertEquals("!END OF INPUT", e.getMessage());
        }
    
        @Test
        @DisplayName("collectInsurance() should not prompt on N")
        public void collectInsuranceNo(){
            // Given
            List players = Collections.singletonList(new Player(1));
            playerSays("N");
            initGame();
    
            // When
            game.collectInsurance(players);
    
            // Then
            assertAll(
                () -> assertTrue(out.toString().contains("ANY INSURANCE")),
                () -> assertFalse(out.toString().contains("INSURANCE BETS"))
            );
        }
    
        @Test
        @DisplayName("collectInsurance() should collect on Y")
        public void collectInsuranceYes(){
            // Given
            List players = Collections.singletonList(new Player(1));
            players.get(0).setCurrentBet(100);
            playerSays("Y");
            playerSays("50");
            initGame();
    
            // When
            game.collectInsurance(players);
    
            // Then
            assertAll(
                () -> assertTrue(out.toString().contains("ANY INSURANCE")),
                () -> assertTrue(out.toString().contains("INSURANCE BETS")),
                () -> assertEquals(50, players.get(0).getInsuranceBet())
            );
        }
    
        @Test
        @DisplayName("collectInsurance() should not allow more than 50% of current bet")
        public void collectInsuranceYesTooMuch(){
            // Given
            List players = Collections.singletonList(new Player(1));
            players.get(0).setCurrentBet(100);
            playerSays("Y");
            playerSays("51");
            playerSays("50");
            initGame();
    
            // When
            game.collectInsurance(players);
    
            // Then
            assertAll(
                () -> assertEquals(50, players.get(0).getInsuranceBet()),
                () -> assertTrue(out.toString().contains("# 1 ? # 1 ?"))
            );
        }
    
        @Test
        @DisplayName("collectInsurance() should not allow negative bets")
        public void collectInsuranceYesNegative(){
            // Given
            List players = Collections.singletonList(new Player(1));
            players.get(0).setCurrentBet(100);
            playerSays("Y");
            playerSays("-1");
            playerSays("1");
            initGame();
    
            // When
            game.collectInsurance(players);
    
            // Then
            assertAll(
                () -> assertEquals(1, players.get(0).getInsuranceBet()),
                () -> assertTrue(out.toString().contains("# 1 ? # 1 ?"))
            );
        }
    
        @Test
        @DisplayName("collectInsurance() should prompt all players")
        public void collectInsuranceYesTwoPlayers(){
            // Given
            List players = Arrays.asList(
                new Player(1),
                new Player(2)
            );
            players.get(0).setCurrentBet(100);
            players.get(1).setCurrentBet(100);
    
            playerSays("Y");
            playerSays("50");
            playerSays("25");
            initGame();
    
            // When
            game.collectInsurance(players);
    
            // Then
            assertAll(
                () -> assertEquals(50, players.get(0).getInsuranceBet()),
                () -> assertEquals(25, players.get(1).getInsuranceBet()),
                () -> assertTrue(out.toString().contains("# 1 ? # 2 ?"))
            );
        }
    
        @Test
        @DisplayName("play() should end on STAY")
        public void playEndOnStay(){
            // Given
            Player player = new Player(1);
            player.dealCard(new Card(3, Card.Suit.CLUBS));
            player.dealCard(new Card(2, Card.Suit.SPADES));
            playerSays("S"); // "I also like to live dangerously."
            initGame();
    
            // When
            game.play(player);
    
            // Then
            assertTrue(out.toString().startsWith("PLAYER 1 ? TOTAL IS 5"));
        }
    
        @Test
        @DisplayName("play() should allow HIT until BUST")
        public void playHitUntilBust() {
            // Given
            Player player = new Player(1);
            player.dealCard(new Card(10, Card.Suit.HEARTS));
            player.dealCard(new Card(10, Card.Suit.SPADES));
    
            playerSays("H");
            playerGets(1, Card.Suit.SPADES); // 20
            playerSays("H");
            playerGets(1, Card.Suit.HEARTS); // 21
            playerSays("H");
            playerGets(1, Card.Suit.CLUBS); // 22 - D'oh!
            initGame();
    
            // When
            game.play(player);
    
            // Then
            assertTrue(out.toString().contains("BUSTED"));
        }
    
        @Test
        @DisplayName("Should allow double down on initial turn")
        public void playDoubleDown(){
            // Given
            Player player = new Player(1);
            player.setCurrentBet(100);
            player.dealCard(new Card(10, Card.Suit.HEARTS));
            player.dealCard(new Card(4, Card.Suit.SPADES));
    
            playerSays("D");
            playerGets(7, Card.Suit.SPADES);
            initGame();
    
            // When
            game.play(player);
    
            // Then
            assertTrue(player.getCurrentBet() == 200);
            assertTrue(player.getHand().size() == 3);
        }
    
        @Test
        @DisplayName("Should NOT allow double down after initial deal")
        public void playDoubleDownLate(){
            // Given
            Player player = new Player(1);
            player.setCurrentBet(100);
            player.dealCard(new Card(10, Card.Suit.HEARTS));
            player.dealCard(new Card(2, Card.Suit.SPADES));
    
            playerSays("H");
            playerGets(7, Card.Suit.SPADES);
            playerSays("D");
            playerSays("S");
            initGame();
    
            // When
            game.play(player);
    
            // Then
            assertTrue(out.toString().contains("TYPE H, OR S, PLEASE"));
        }
    
        @Test
        @DisplayName("play() should end on STAY after split")
        public void playSplitEndOnStay(){
            // Given
            Player player = new Player(1);
            player.dealCard(new Card(1, Card.Suit.CLUBS));
            player.dealCard(new Card(1, Card.Suit.SPADES));
    
            playerSays("/");
            playerGets(2, Card.Suit.SPADES); // First hand
            playerSays("S");
            playerGets(2, Card.Suit.SPADES); // Second hand
            playerSays("S");
            initGame();
    
            // When
            game.play(player);
    
            // Then
            assertTrue(out.toString().contains("FIRST HAND RECEIVES"));
            assertTrue(out.toString().contains("SECOND HAND RECEIVES"));
        }
    
        @Test
        @DisplayName("play() should allow HIT until BUST after split")
        public void playSplitHitUntilBust() {
            // Given
            Player player = new Player(1);
            player.dealCard(new Card(10, Card.Suit.HEARTS));
            player.dealCard(new Card(10, Card.Suit.SPADES));
    
            playerSays("/");
            playerGets(12, Card.Suit.SPADES); // First hand has 20
            playerSays("H");
            playerGets(12, Card.Suit.HEARTS); // First hand busted
            playerGets(10, Card.Suit.HEARTS); // Second hand gets a 10
            playerSays("S");
            initGame();
    
            // When
            game.play(player);
    
            // Then
            assertTrue(out.toString().contains("BUSTED"));
        }
    
        @Test
        @DisplayName("play() should allow HIT on split hand until BUST")
        public void playSplitHitUntilBustHand2() {
            // Given
            Player player = new Player(1);
            player.dealCard(new Card(10, Card.Suit.HEARTS));
            player.dealCard(new Card(10, Card.Suit.SPADES));
    
            playerSays("/");
            playerGets(1, Card.Suit.CLUBS); // First hand is 21
            playerSays("S");
            playerGets(12, Card.Suit.SPADES); // Second hand is 20
            playerSays("H");
            playerGets(12, Card.Suit.HEARTS); // Busted
            playerSays("H");
            initGame();
    
            // When
            game.play(player);
    
            // Then
            assertTrue(out.toString().contains("BUSTED"));
        }
    
        @Test
        @DisplayName("play() should allow double down on split hands")
        public void playSplitDoubleDown(){
            // Given
            Player player = new Player(1);
            player.setCurrentBet(100);
            player.dealCard(new Card(9, Card.Suit.HEARTS));
            player.dealCard(new Card(9, Card.Suit.SPADES));
    
            playerSays("/");
            playerGets(5, Card.Suit.DIAMONDS); // First hand is 14
            playerSays("D");
            playerGets(6, Card.Suit.HEARTS); // First hand is 20
            playerGets(7, Card.Suit.CLUBS); // Second hand is 16
            playerSays("D");
            playerGets(4, Card.Suit.CLUBS); // Second hand is 20
            initGame();
    
            // When
            game.play(player);
    
            // Then
            assertAll(
                () -> assertEquals(200, player.getCurrentBet(), "Current bet should be doubled"),
                () -> assertEquals(200, player.getSplitBet(), "Split bet should be doubled"),
                () -> assertEquals(3, player.getHand(1).size(), "First hand should have exactly three cards"),
                () -> assertEquals(3, player.getHand(2).size(), "Second hand should have exactly three cards")
            );
        }
    
        @Test
        @DisplayName("play() should NOT allow re-splitting first split hand")
        public void playSplitTwice(){
            // Given
            Player player = new Player(1);
            player.setCurrentBet(100);
            player.dealCard(new Card(2, Card.Suit.HEARTS));
            player.dealCard(new Card(2, Card.Suit.SPADES));
    
            playerSays("/");
            playerGets(13, Card.Suit.CLUBS); // First hand
            playerSays("/"); // Not allowed
            playerSays("S");
            playerGets(13, Card.Suit.SPADES); // Second hand
            playerSays("S");
            initGame();
    
            // When
            game.play(player);
    
            // Then
            assertTrue(out.toString().contains("TYPE H, S OR D, PLEASE"));
        }
    
        @Test
        @DisplayName("play() should NOT allow re-splitting second split hand")
        public void playSplitTwiceHand2(){
            // Given
            Player player = new Player(1);
            player.setCurrentBet(100);
            player.dealCard(new Card(10, Card.Suit.HEARTS));
            player.dealCard(new Card(10, Card.Suit.SPADES));
    
            playerSays("/");
            playerGets(13, Card.Suit.CLUBS); // First hand
            playerSays("S");
            playerGets(13, Card.Suit.SPADES); // Second hand
            playerSays("/"); // Not allowed
            playerSays("S");
            initGame();
    
            // When
            game.play(player);
    
            // Then
            assertTrue(out.toString().contains("TYPE H, S OR D, PLEASE"));
        }
    
        @Test
        @DisplayName("evaluateRound() should total both hands when split")
        public void evaluateRoundWithSplitHands(){
            // Given
            Player dealer = new Player(0); //Dealer
            dealer.dealCard(new Card(1, Card.Suit.HEARTS));
            dealer.dealCard(new Card(1, Card.Suit.SPADES));
    
            Player player = new Player(1);
            player.recordRound(200);//Set starting total
            player.setCurrentBet(50);
            player.dealCard(new Card(1, Card.Suit.HEARTS));
            player.dealCard(new Card(1, Card.Suit.SPADES));
            
            playerSays("/");
            playerGets(13, Card.Suit.CLUBS); // First hand
            playerSays("S");
            playerGets(13, Card.Suit.SPADES); // Second hand
            playerSays("S");
            initGame();
    
            // When
            game.play(player);
            game.evaluateRound(Arrays.asList(player), dealer);
    
            // Then
            assertAll(
                () -> assertTrue(out.toString().contains("PLAYER 1  WINS    100 TOTAL= 300")),
                () -> assertTrue(out.toString().contains("DEALER'S TOTAL= -100"))
            );
        }
    
        @Test
        @DisplayName("evaluateRound() should total add twice insurance bet")
        public void evaluateRoundWithInsurance(){
            // Given
            Player dealer = new Player(0); //Dealer
            dealer.dealCard(new Card(10, Card.Suit.HEARTS));
            dealer.dealCard(new Card(1, Card.Suit.SPADES));
    
            Player player = new Player(1);
            player.setCurrentBet(50);
            player.setInsuranceBet(10);
            player.dealCard(new Card(2, Card.Suit.HEARTS));
            player.dealCard(new Card(1, Card.Suit.SPADES));
            initGame();
    
            // When
            game.evaluateRound(Arrays.asList(player), dealer);
    
            // Then
            // Loses current bet (50) and wins 2*10 for total -30
            assertAll(
                () -> assertTrue(out.toString().contains("PLAYER 1 LOSES     30 TOTAL= -30")),
                () -> assertTrue(out.toString().contains("DEALER'S TOTAL= 30"))
            );
        }
    
        @Test
        @DisplayName("evaluateRound() should push with no total change")
        public void evaluateRoundWithPush(){
            // Given
            Player dealer = new Player(0);
            dealer.dealCard(new Card(10, Card.Suit.HEARTS));
            dealer.dealCard(new Card(8, Card.Suit.SPADES)); 
    
            Player player = new Player(1);
            player.setCurrentBet(10);
            player.dealCard(new Card(9, Card.Suit.HEARTS));
            player.dealCard(new Card(9, Card.Suit.SPADES));
            initGame();
    
            // When (Dealer and Player both have 19)
            game.evaluateRound(Arrays.asList(player), dealer);
    
            // Then        
            assertAll(
                () -> assertTrue(out.toString().contains("PLAYER 1 PUSHES       TOTAL= 0")),
                () -> assertTrue(out.toString().contains("DEALER'S TOTAL= 0"))
            );
        }
    
        @Test
        @DisplayName("shouldPlayDealer() return false when players bust")
        public void shouldPlayDealerBust(){
            // Given
            Player player = new Player(1);
            player.dealCard(new Card(10, Card.Suit.SPADES));
            player.dealCard(new Card(10, Card.Suit.SPADES));
            player.split();
            player.dealCard(new Card(5, Card.Suit.SPADES));
            player.dealCard(new Card(8, Card.Suit.SPADES));//First hand Busted
    
            player.dealCard(new Card(5, Card.Suit.SPADES),2);
            player.dealCard(new Card(8, Card.Suit.SPADES),2);//Second hand Busted
    
            Player playerTwo = new Player(2);
            playerTwo.dealCard(new Card(7, Card.Suit.HEARTS));
            playerTwo.dealCard(new Card(8, Card.Suit.HEARTS));
            playerTwo.dealCard(new Card(9, Card.Suit.HEARTS));
            initGame();
    
            // When 
            boolean result = game.shouldPlayDealer(Arrays.asList(player,playerTwo));
    
            // Then        
            assertFalse(result);
        }
    
        @Test
        @DisplayName("shouldPlayDealer() return false when players bust")
        public void ShouldPlayer(){
            // Given
            Player player = new Player(1);
            player.dealCard(new Card(10, Card.Suit.SPADES));
            player.dealCard(new Card(10, Card.Suit.SPADES));
            player.split();
            player.dealCard(new Card(5, Card.Suit.SPADES));
            player.dealCard(new Card(8, Card.Suit.SPADES));//First hand Busted
    
            player.dealCard(new Card(5, Card.Suit.SPADES),2);
            player.dealCard(new Card(8, Card.Suit.SPADES),2);//Second hand Busted
    
            Player playerTwo = new Player(2);
            playerTwo.dealCard(new Card(7, Card.Suit.HEARTS));
            playerTwo.dealCard(new Card(8, Card.Suit.HEARTS));
            playerTwo.dealCard(new Card(9, Card.Suit.HEARTS));
            initGame();
    
            // When 
            boolean result = game.shouldPlayDealer(Arrays.asList(player,playerTwo));
    
            // Then        
            assertFalse(result);
        }
    
        @Test
        @DisplayName("shouldPlayDealer() return true when player has non-natural blackjack")
        public void shouldPlayDealerNonNaturalBlackjack(){
            // Given
            Player player = new Player(1);
            player.dealCard(new Card(5, Card.Suit.SPADES));
            player.dealCard(new Card(6, Card.Suit.DIAMONDS));
            player.dealCard(new Card(10, Card.Suit.SPADES));
    
            initGame();
    
            // When 
            boolean result = game.shouldPlayDealer(Arrays.asList(player));
    
            // Then        
            assertTrue(result);
        }
    
        @Test
        @DisplayName("shouldPlayDealer() return true when player doesn't have blackjack")
        public void shouldPlayDealerNonBlackjack(){
            // Given
            Player player = new Player(1);
            player.dealCard(new Card(10, Card.Suit.SPADES));
            player.dealCard(new Card(6, Card.Suit.DIAMONDS));
            initGame();
    
            // When 
            boolean result = game.shouldPlayDealer(Arrays.asList(player));
    
            // Then        
            assertTrue(result);
        }
    
    
        @Test
        @DisplayName("playDealer() should DRAW on less than 17 intial deal")
        public void playDealerLessThanSeventeen(){
            // Given
            Player dealer = new Player(0);
            dealer.dealCard(new Card(10, Card.Suit.SPADES));
            dealer.dealCard(new Card(6, Card.Suit.DIAMONDS));
            playerGets(11, Card.Suit.DIAMONDS);
            initGame();
    
            // When 
           game.playDealer(dealer);
    
            // Then        
            assertTrue(out.toString().contains("DRAWS"));
            assertTrue(out.toString().contains("BUSTED"));
        }
    
        @Test
        @DisplayName("playDealer() should stay on more than 17 intial deal")
        public void playDealerMoreThanSeventeen(){
            // Given
            Player dealer = new Player(0);
            dealer.dealCard(new Card(10, Card.Suit.SPADES));
            dealer.dealCard(new Card(8, Card.Suit.DIAMONDS));
            initGame();
    
            // When 
           game.playDealer(dealer);
    
            // Then        
            assertFalse(out.toString().contains("DRAWS"));
            assertFalse(out.toString().contains("BUSTED"));
            assertTrue(out.toString().contains("---TOTAL IS"));
        }
    
    }
    
    
    ================================================
    FILE: 10_Blackjack/java/test/ScoringUtilsTest.java
    ================================================
    import org.junit.jupiter.api.Test;
    
    import org.junit.jupiter.api.DisplayName;
    
    import static org.junit.jupiter.api.Assertions.assertEquals;
    
    import java.util.LinkedList;
    
    public class ScoringUtilsTest {
    
        @Test
        @DisplayName("scoreHand should score aces as 1 when using 11 would bust")
        public void scoreHandHardAce() {
            // Given
            LinkedList hand = new LinkedList<>();
            hand.add(new Card(10, Card.Suit.SPADES));
            hand.add(new Card(9, Card.Suit.SPADES));
            hand.add(new Card(1, Card.Suit.SPADES));
    
            // When
            int result = ScoringUtils.scoreHand(hand);
    
            // Then
            assertEquals(20, result);
        }
    
        @Test
        @DisplayName("scoreHand should score 3 aces as 13")
        public void scoreHandMultipleAces() {
            // Given
            LinkedList hand = new LinkedList<>();
            hand.add(new Card(1, Card.Suit.SPADES));
            hand.add(new Card(1, Card.Suit.CLUBS));
            hand.add(new Card(1, Card.Suit.HEARTS));
    
            // When
            int result = ScoringUtils.scoreHand(hand);
    
            // Then
            assertEquals(13, result);
        }
    
        @Test
        @DisplayName("compareHands should return 1 meaning A beat B, 20 to 12")
        public void compareHandsAWins() {
            LinkedList handA = new LinkedList<>();
            handA.add(new Card(10, Card.Suit.SPADES));
            handA.add(new Card(10, Card.Suit.CLUBS));
    
            LinkedList handB = new LinkedList<>();
            handB.add(new Card(1, Card.Suit.SPADES));
            handB.add(new Card(1, Card.Suit.CLUBS));
    
            int result = ScoringUtils.compareHands(handA,handB);
    
            assertEquals(1, result);
        }
    
        @Test
        @DisplayName("compareHands should return -1 meaning B beat A, 18 to 4")
        public void compareHandsBwins() {
            LinkedList handA = new LinkedList<>();
            handA.add(new Card(2, Card.Suit.SPADES));
            handA.add(new Card(2, Card.Suit.CLUBS));
    
            LinkedList handB = new LinkedList<>();
            handB.add(new Card(5, Card.Suit.SPADES));
            handB.add(new Card(6, Card.Suit.HEARTS));
            handB.add(new Card(7, Card.Suit.CLUBS));
    
            int result = ScoringUtils.compareHands(handA,handB);
    
            assertEquals(-1, result);
        }
    
        @Test
        @DisplayName("compareHands should return 1 meaning A beat B, natural Blackjack to Blackjack")
        public void compareHandsAWinsWithNaturalBlackJack() {
            //Hand A wins with natural BlackJack, B with Blackjack
            LinkedList handA = new LinkedList<>();
            handA.add(new Card(10, Card.Suit.SPADES));
            handA.add(new Card(1, Card.Suit.CLUBS));
    
            LinkedList handB = new LinkedList<>();
            handB.add(new Card(6, Card.Suit.SPADES));
            handB.add(new Card(7, Card.Suit.HEARTS));
            handB.add(new Card(8, Card.Suit.CLUBS));
    
            int result = ScoringUtils.compareHands(handA,handB);
    
            assertEquals(1, result);
        }
    
        @Test
        @DisplayName("compareHands should return -1 meaning B beat A, natural Blackjack to Blackjack")
        public void compareHandsBWinsWithNaturalBlackJack() {
            LinkedList handA = new LinkedList<>();
            handA.add(new Card(6, Card.Suit.SPADES));
            handA.add(new Card(7, Card.Suit.HEARTS));
            handA.add(new Card(8, Card.Suit.CLUBS));
            
            LinkedList handB = new LinkedList<>();
            handB.add(new Card(10, Card.Suit.SPADES));
            handB.add(new Card(1, Card.Suit.CLUBS));
    
            int result = ScoringUtils.compareHands(handA,handB);
    
            assertEquals(-1, result);
        }
    
        @Test
        @DisplayName("compareHands should return 0, hand A and B tied with a Blackjack")
        public void compareHandsTieBothBlackJack() {
            LinkedList handA = new LinkedList<>();
            handA.add(new Card(11, Card.Suit.SPADES));
            handA.add(new Card(10, Card.Suit.CLUBS));
            
            LinkedList handB = new LinkedList<>();
            handB.add(new Card(10, Card.Suit.SPADES));
            handB.add(new Card(11, Card.Suit.CLUBS));
    
            int result = ScoringUtils.compareHands(handA,handB);
    
            assertEquals(0, result);
        }
    
        @Test
        @DisplayName("compareHands should return 0, hand A and B tie without a Blackjack")
        public void compareHandsTieNoBlackJack() {
            LinkedList handA = new LinkedList<>();
            handA.add(new Card(10, Card.Suit.DIAMONDS));
            handA.add(new Card(10, Card.Suit.HEARTS));
            
            LinkedList handB = new LinkedList<>();
            handB.add(new Card(10, Card.Suit.SPADES));
            handB.add(new Card(10, Card.Suit.CLUBS));
    
            int result = ScoringUtils.compareHands(handA,handB);
    
            assertEquals(0, result);
        }
    
        @Test
        @DisplayName("compareHands should return 0, hand A and B tie when both bust")
        public void compareHandsTieBust() {
            LinkedList handA = new LinkedList<>();
            handA.add(new Card(10, Card.Suit.DIAMONDS));
            handA.add(new Card(10, Card.Suit.HEARTS));
            handA.add(new Card(3, Card.Suit.HEARTS));
            
            LinkedList handB = new LinkedList<>();
            handB.add(new Card(10, Card.Suit.SPADES));
            handB.add(new Card(11, Card.Suit.SPADES));
            handB.add(new Card(4, Card.Suit.SPADES));
    
            int result = ScoringUtils.compareHands(handA,handB);
    
            assertEquals(0, result);
        }
        @Test
        @DisplayName("compareHands should return -1, meaning B beat A, A busted")
        public void compareHandsABusted() {
            LinkedList handA = new LinkedList<>();
            handA.add(new Card(10, Card.Suit.DIAMONDS));
            handA.add(new Card(10, Card.Suit.HEARTS));
            handA.add(new Card(3, Card.Suit.HEARTS));
            
            LinkedList handB = new LinkedList<>();
            handB.add(new Card(10, Card.Suit.SPADES));
            handB.add(new Card(10, Card.Suit.SPADES));
    
            int result = ScoringUtils.compareHands(handA,handB);
    
            assertEquals(-1, result);
        }
    
        @Test
        @DisplayName("compareHands should return 1, meaning A beat B, B busted")
        public void compareHandsBBusted() {
            LinkedList handA = new LinkedList<>();
            handA.add(new Card(10, Card.Suit.DIAMONDS));
            handA.add(new Card(3, Card.Suit.HEARTS));
            
            LinkedList handB = new LinkedList<>();
            handB.add(new Card(10, Card.Suit.SPADES));
            handB.add(new Card(10, Card.Suit.SPADES));
            handB.add(new Card(5, Card.Suit.SPADES));
    
            int result = ScoringUtils.compareHands(handA,handB);
    
            assertEquals(1, result);
        }
    }
    
    
    ================================================
    FILE: 10_Blackjack/java/test/UserIoTest.java
    ================================================
    import static org.junit.jupiter.api.Assertions.assertEquals;
    import static org.junit.jupiter.api.Assertions.assertFalse;
    import static org.junit.jupiter.api.Assertions.assertTrue;
    
    import java.io.Reader;
    import java.io.StringReader;
    import java.io.StringWriter;
    
    import org.junit.jupiter.api.DisplayName;
    import org.junit.jupiter.api.Test;
    import org.junit.jupiter.params.ParameterizedTest;
    import org.junit.jupiter.params.provider.CsvSource;
    import org.junit.jupiter.params.provider.ValueSource;
    
    public class UserIoTest {
    
        @ParameterizedTest(name = "''{0}'' is accepted as ''no''")
        @ValueSource(strings = {"N", "n", "No", "NO", "no"})
        public void testPromptBooleanAcceptsNo(String response) {
            // Given
            Reader in = new StringReader(response + "\n");
            StringWriter out = new StringWriter();
            UserIo userIo = new UserIo(in, out);
    
            // When
            boolean result = userIo.promptBoolean("TEST");
    
            // Then
            assertEquals("TEST? ", out.toString());
            assertFalse(result);
        }
    
        @ParameterizedTest(name = "''{0}'' is accepted as ''yes''")
        @ValueSource(strings = {"Y", "y", "Yes", "YES", "yes", "", "foobar"})
        public void testPromptBooleanAcceptsYes(String response) {
            // Given
            Reader in = new StringReader(response + "\n");
            StringWriter out = new StringWriter();
            UserIo userIo = new UserIo(in, out);
    
            // When
            boolean result = userIo.promptBoolean("TEST");
    
            // Then
            assertEquals("TEST? ", out.toString());
            assertTrue(result);
        }
    
        @ParameterizedTest(name = "''{0}'' is accepted as number")
        @CsvSource({
            "1,1",
            "0,0",
            "-1,-1",
        })
        public void testPromptIntAcceptsNumbers(String response, int expected) {
            // Given
            Reader in = new StringReader(response + "\n");
            StringWriter out = new StringWriter();
            UserIo userIo = new UserIo(in, out);
    
            // When
            int result = userIo.promptInt("TEST");
    
            // Then
            assertEquals("TEST? ", out.toString());
            assertEquals(expected, result);
        }
    
        @Test
        @DisplayName("promptInt should print an error and reprompt if given a non-numeric response")
        public void testPromptIntRepromptsOnNonNumeric() {
            // Given
            Reader in = new StringReader("foo" + System.lineSeparator() +"1"); // word, then number
            StringWriter out = new StringWriter();
            UserIo userIo = new UserIo(in, out);
    
            // When
            int result = userIo.promptInt("TEST");
    
            // Then
            assertEquals("TEST? !NUMBER EXPECTED - RETRY INPUT LINE" + System.lineSeparator() +"? ", out.toString());
            assertEquals(1, result);
        }
    }
    
    
    ================================================
    FILE: 10_Blackjack/javascript/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells)
    
    
    ================================================
    FILE: 10_Blackjack/javascript/blackjack.html
    ================================================
    
    
    BLACKJACK
    
    
    
    
    
    
    
    
    
    
    ================================================
    FILE: 10_Blackjack/javascript/blackjack.js
    ================================================
    // BLACKJACK
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    var da = [];
    
    var pa = [];
    var qa = [];
    var ca = [];
    var ta = [];
    var sa = [];
    var ba = [];
    var za = [];
    var ra = [];
    
    var ds = "N A  2  3  4  5  6  7N 8  9 10  J  Q  K";
    var is = "H,S,D,/,"
    
    var q;
    var aa;
    var ab;
    var ac;
    var h;
    var h1;
    
    function af(q) {
        return q >= 22 ? q - 11 : q;
    }
    
    function reshuffle()
    {
        print("RESHUFFLING\n");
        for (; d >= 1; d--)
            ca[--c] = da[d];
        for (c1 = 52; c1 >= c; c1--) {
            c2 = Math.floor(Math.random() * (c1 - c + 1)) + c;
            c3 = ca[c2];
            ca[c2] = ca[c1];
            ca[c1] = c3;
        }
    }
    
    // Subroutine to get a card.
    function get_card()
    {
        if (c >= 51)
            reshuffle();
        return ca[c++];
    }
    
    // Card printing subroutine
    function card_print(x)
    {
        print(ds.substr(3 * x - 3, 3) + "  ");
    }
    
    // Alternate card printing subroutine
    function alt_card_print(x)
    {
        print(" " + ds.substr(3 * x - 2, 2) + "   ");
    }
    
    // Subroutine to add card 'which' to total 'q'
    function add_card(which)
    {
        x1 = which;
        if (x1 > 10)
            x1 = 10;
        q1 = q + x1;
        if (q < 11) {
            if (which <= 1) {
                q += 11;
                return;
            }
            if (q1 >= 11)
                q = q1 + 11;
            else
                q = q1;
            return;
        }
        if (q <= 21 && q1 > 21)
            q = q1 + 1;
        else
            q = q1;
        if (q >= 33)
            q = -1;
    }
    
    // Subroutine to evaluate hand 'which'. Total is put into
    // qa[which]. Totals have the following meaning:
    //  2-10...hard 2-10
    // 11-21...soft 11-21
    // 22-32...hard 11-21
    //  33+....busted
    function evaluate_hand(which)
    {
        q = 0;
        for (q2 = 1; q2 <= ra[which]; q2++) {
            add_card(pa[i][q2]);
        }
        qa[which] = q;
    }
    
    // Subroutine to add a card to row i
    function add_card_to_row(i, x) {
        ra[i]++;
        pa[i][ra[i]] = x;
        q = qa[i];
        add_card(x);
        qa[i] = q;
        if (q < 0) {
            print("...BUSTED\n");
            discard_row(i);
        }
    }
    
    // Subroutine to discard row i
    function discard_row(i) {
        while (ra[i]) {
            d++;
            da[d] = pa[i][ra[i]];
            ra[i]--;
        }
    }
    
    // Prints total of hand i
    function print_total(i) {
        print("\n");
        aa = qa[i];
        total_aa();
        print("TOTAL IS " + aa + "\n");
    }
    
    function total_aa()
    {
        if (aa >= 22)
            aa -= 11;
    }
    
    function total_ab()
    {
        if (ab >= 22)
            ab -= 11;
    }
    
    function total_ac()
    {
        if (ac >= 22)
            ac -= 11;
    }
    
    function process_input(str)
    {
        str = str.substr(0, 1);
        for (h = 1; h <= h1; h += 2) {
            if (str == is.substr(h - 1, 1))
                break;
        }
        if (h <= h1) {
            h = (h + 1) / 2;
            return 0;
        }
        print("TYPE " + is.substr(0, h1 - 1) + " OR " + is.substr(h1 - 1, 2) + " PLEASE");
        return 1;
    }
    
    // Main program
    async function main()
    {
        print(tab(31) + "BLACK JACK\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        // --pa[i][j] IS THE JTH CARD IN HAND I, qa[i] IS TOTAL OF HAND I
        // --C IS THE DECK BEING DEALT FROM, D IS THE DISCARD PILE,
        // --ta[i] IS THE TOTAL FOR PLAYER I, sa[i] IS THE TOTAL THIS HAND FOR
        // --PLAYER I, ba[i] IS TH BET FOR HAND I
        // --ra[i] IS THE LENGTH OF pa[I,*]
    
        // --Program starts here
        // --Initialize
        for (i = 1; i <= 15; i++)
            pa[i] = [];
        for (i = 1; i <= 13; i++)
            for (j = 4 * i - 3; j <= 4 * i; j++)
                da[j] = i;
        d = 52;
        c = 53;
        print("DO YOU WANT INSTRUCTIONS");
        str = await input();
        if (str.toUpperCase().substr(0, 1) != "N") {
            print("THIS IS THE GAME OF 21. AS MANY AS 7 PLAYERS MAY PLAY THE\n");
            print("GAME. ON EACH DEAL, BETS WILL BE ASKED FOR, AND THE\n");
            print("PLAYERS' BETS SHOULD BE TYPED IN. THE CARDS WILL THEN BE\n");
            print("DEALT, AND EACH PLAYER IN TURN PLAYS HIS HAND. THE\n");
            print("FIRST RESPONSE SHOULD BE EITHER 'D', INDICATING THAT THE\n");
            print("PLAYER IS DOUBLING DOWN, 'S', INDICATING THAT HE IS\n");
            print("STANDING, 'H', INDICATING HE WANTS ANOTHER CARD, OR '/',\n");
            print("INDICATING THAT HE WANTS TO SPLIT HIS CARDS. AFTER THE\n");
            print("INITIAL RESPONSE, ALL FURTHER RESPONSES SHOULD BE 'S' OR\n");
            print("'H', UNLESS THE CARDS WERE SPLIT, IN WHICH CASE DOUBLING\n");
            print("DOWN IS AGAIN PERMITTED. IN ORDER TO COLLECT FOR\n");
            print("BLACKJACK, THE INITIAL RESPONSE SHOULD BE 'S'.\n");
        }
        while (1) {
            print("NUMBER OF PLAYERS");
            n = parseInt(await input());
            print("\n");
            if (n < 1 || n > 7)
                continue;
            else
                break;
        }
        for (i = 1; i <= 8; i++)
            ta[i] = 0;
        d1 = n + 1;
        while (1) {
            if (2 * d1 + c >= 52) {
                reshuffle();
            }
            if (c == 2)
                c--;
            for (i = 1; i <= n; i++)
                za[i] = 0;
            for (i = 1; i <= 15; i++)
                ba[i] = 0;
            for (i = 1; i <= 15; i++)
                qa[i] = 0;
            for (i = 1; i <= 7; i++)
                sa[i] = 0;
            for (i = 1; i <= 15; i++)
                ra[i] = 0;
            print("BETS:\n");
            for (i = 1; i <= n; i++) {
                do {
                    print("#" + i + " ");
                    za[i] = parseFloat(await input());
                } while (za[i] <= 0 || za[i] > 500) ;
            }
            for (i = 1; i <= n; i++)
                ba[i] = za[i];
            print("PLAYER");
            for (i = 1; i <= n; i++) {
                print(" " + i + "    ");
            }
            print("DEALER\n");
            for (j = 1; j <= 2; j++) {
                print(tab(5));
                for (i = 1; i <= d1; i++) {
                    pa[i][j] = get_card();
                    if (j == 1 || i <= n)
                        alt_card_print(pa[i][j]);
                }
                print("\n");
            }
            for (i = 1; i <= d1; i++)
                ra[i] = 2;
            // --Test for insurance
            if (pa[d1][1] <= 1) {
                print("ANY INSURANCE");
                str = await input();
                if (str.substr(0, 1) == "Y") {
                    print("INSURANCE BETS\n");
                    for (i = 1; i <= n; i++) {
                        do {
                            print("#" + i + " ");
                            za[i] = parseFloat(await input());
                        } while (za[i] < 0 || za[i] > ba[i] / 2) ;
                    }
                    for (i = 1; i <= n; i++)
                        sa[i] = za[i] * ((pa[d1][2] >= 10 ? 3 : 0) - 1);
                }
            }
            // --Test for dealer blackjack
            l1 = 1;
            l2 = 1;
            if (pa[d1][1] == 1 && pa[d1][2] > 9) {
                l1 = 0;
                l2 = 0;
            }
            if (pa[d1][2] == 1 && pa[d1][1] > 9) {
                l1 = 0;
                l2 = 0;
            }
            if (l1 == 0 && l2 == 0) {
                print("\n");
                print("DEALER HAS A" + ds.substr(3 * pa[d1][2] - 3, 3) + " IN THE HOLE FOR BLACKJACK\n");
                for (i = 1; i <= d1; i++)
                    evaluate_hand(i);
            } else {
                // --No dealer blackjack
                if (pa[d1][1] <= 1 || pa[d1][1] >= 10) {
                    print("\n");
                    print("NO DEALER BLACKJACK.\n");
                }
                // --Now play the hands
                for (i = 1; i <= n; i++) {
                    print("PLAYER " + i + " ");
                    h1 = 7;
                    do {
                        str = await input();
                    } while (process_input(str)) ;
                    if (h == 1) {   // Player wants to be hit
                        evaluate_hand(i);
                        h1 = 3;
                        x = get_card();
                        print("RECEIVED A");
                        card_print(x);
                        add_card_to_row(i, x);
                        if (q > 0)
                            print_total(i);
                    } else if (h == 2) {    // Player wants to stand
                        evaluate_hand(i);
                        if (qa[i] == 21) {
                            print("BLACKJACK\n");
                            sa[i] = sa[i] + 1.5 * ba[i];
                            ba[i] = 0;
                            discard_row(i);
                        } else {
                            print_total(i);
                        }
                    } else if (h == 3) {    // Player wants to double down
                        evaluate_hand(i);
                        h1 = 3;
                        h = 1;
                        while (1) {
                            if (h == 1) {   // Hit
                                x = get_card();
                                print("RECEIVED A");
                                card_print(x);
                                add_card_to_row(i, x);
                                if (q < 0)
                                    break;
                                print("HIT");
                            } else if (h == 2) {    // Stand
                                print_total(i);
                                break;
                            }
                            do {
                                str = await input();
                            } while (process_input(str)) ;
                            h1 = 3;
                        }
                    } else if (h == 4) {    // Player wants to split
                        l1 = pa[i][1];
                        if (l1 > 10)
                            l1 = 10;
                        l2 = pa[i][2];
                        if (l2 > 10)
                            l2 = 10;
                        if (l1 != l2) {
                            print("SPLITTING NOT ALLOWED.\n");
                            i--;
                            continue;
                        }
                        // --Play out split
                        i1 = i + d1;
                        ra[i1] = 2;
                        pa[i1][1] = pa[i1][2];
                        ba[i + d1] = ba[i];
                        x = get_card();
                        print("FIRST HAND RECEIVES A");
                        card_print(x);
                        pa[i][2] = x;
                        evaluate_hand(i);
                        print("\n");
                        x = get_card();
                        print("SECOND HAND RECEIVES A");
                        i = i1;
                        card_print(x);
                        pa[i][2] = x;
                        evaluate_hand(i);
                        print("\n");
                        i = i1 - d1;
                        if (pa[i][1] != 1) {
                            // --Now play the two hands
                            do {
    
                                print("HAND " + (i > d1 ? 2 : 1) + " ");
                                h1 = 5;
                                while (1) {
                                    do {
                                        str = await input();
                                    } while (process_input(str)) ;
                                    h1 = 3;
                                    if (h == 1) {   // Hit
                                        x = get_card();
                                        print("RECEIVED A");
                                        card_print(x);
                                        add_card_to_row(i, x);
                                        if (q < 0)
                                            break;
                                        print("HIT");
                                    } else if (h == 2) {    // Stand
                                        print_total(i);
                                        break;
                                    } else {    // Double
                                        x = get_card();
                                        ba[i] *= 2;
                                        print("RECEIVED A");
                                        card_print(x);
                                        add_card_to_row(i, x);
                                        if (q > 0)
                                            print_total(i);
                                        break;
                                    }
                                }
                                i += d1;
                            } while (i == i1) ;
                            i = i1 - d1;
                        }
                    }
                }
                // --Test for playing dealer's hand
                evaluate_hand(i);
                for (i = 1; i <= n; i++) {
                    if (ra[i] > 0 || ra[i + d1] > 0)
                        break;
                }
                if (i > n) {
                    print("DEALER HAD A");
                    x = pa[d1][2];
                    card_print(x);
                    print(" CONCEALED.\n");
                } else {
                    print("DEALER HAS A" + ds.substr(3 * pa[d1][2] - 3, 3) + " CONCEALED ");
                    i = d1;
                    aa = qa[i];
                    total_aa();
                    print("FOR A TOTAL OF " + aa + "\n");
                    if (aa <= 16) {
                        print("DRAWS");
                        do {
    
                            x = get_card();
                            alt_card_print(x);
                            add_card_to_row(i, x);
                            aa = q;
                            total_aa();
                        } while (q > 0 && aa < 17) ;
                        if (q < 0) {
                            qa[i] = q + 0.5;
                        } else {
                            qa[i] = q;
                        }
                        if (q >= 0) {
                            aa = q;
                            total_aa();
                            print("---TOTAL IS " + aa + "\n");
                        }
                    }
                    print("\n");
                }
            }
            // --TALLY THE RESULT
            str = "LOSES PUSHES WINS "
            print("\n");
            for (i = 1; i <= n; i++) {
                aa = qa[i]
                total_aa();
                ab = qa[i + d1];
                total_ab();
                ac = qa[d1];
                total_ac();
                signaaac = aa - ac;
                if (signaaac) {
                    if (signaaac < 0)
                        signaaac = -1;
                    else
                        signaaac = 1;
                }
                signabac = ab - ac;
                if (signabac) {
                    if (signabac < 0)
                        signabac = -1;
                    else
                        signabac = 1;
                }
                sa[i] = sa[i] + ba[i] * signaaac + ba[i + d1] * signabac;
                ba[i + d1] = 0;
                print("PLAYER " + i + " ");
                signsai = sa[i];
                if (signsai) {
                    if (signsai < 0)
                        signsai = -1;
                    else
                        signsai = 1;
                }
                print(str.substr(signsai * 6 + 6, 6) + " ");
                if (sa[i] == 0)
                    print("      ");
                else
                    print(" " + Math.abs(sa[i]) + " ");
                ta[i] = ta[i] + sa[i];
                print("TOTAL= " + ta[i] + "\n");
                discard_row(i);
                ta[d1] = ta[d1] - sa[i];
                i += d1;
                discard_row(i);
                i -= d1;
            }
            print("DEALER'S TOTAL= " + ta[d1] + "\n");
            print("\n");
            discard_row(i);
        }
    }
    
    main();
    
    
    ================================================
    FILE: 10_Blackjack/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 10_Blackjack/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 10_Blackjack/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 10_Blackjack/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 10_Blackjack/python/blackjack.py
    ================================================
    """
    Blackjack
    
    Ported by Martin Thoma in 2022,
    using the rust implementation of AnthonyMichaelTDM
    """
    
    import enum
    import random
    from dataclasses import dataclass
    from typing import List, NamedTuple
    
    
    class PlayerType(enum.Enum):
        Player = "Player"
        Dealer = "Dealer"
    
    
    class Play(enum.Enum):
        Stand = enum.auto()
        Hit = enum.auto()
        DoubleDown = enum.auto()
        Split = enum.auto()
    
    
    class Card(NamedTuple):
        name: str
    
        @property
        def value(self) -> int:
            """
            returns the value associated with a card with the passed name
            return 0 if the passed card name doesn't exist
            """
            return {
                "ACE": 11,
                "2": 2,
                "3": 3,
                "4": 4,
                "5": 5,
                "6": 6,
                "7": 7,
                "8": 8,
                "9": 9,
                "10": 10,
                "JACK": 10,
                "QUEEN": 10,
                "KING": 10,
            }.get(self.name, 0)
    
    
    class Hand(NamedTuple):
        cards: List[Card]
    
        def add_card(self, card: Card) -> None:
            """add a passed card to this hand"""
            self.cards.append(card)
    
        def get_total(self) -> int:
            """returns the total points of the cards in this hand"""
            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):
                total -= 10
    
            return total
    
        def discard_hand(self, deck: "Decks") -> None:
            """adds the cards in hand into the discard pile"""
            _len = len(self.cards)
            for _i in range(_len):
                if len(self.cards) == 0:
                    raise ValueError("hand empty")
                deck.discard_pile.append(self.cards.pop())
    
    
    class Decks(NamedTuple):
        deck: List[Card]
        discard_pile: List[Card]
    
        @classmethod
        def new(cls) -> "Decks":
            """creates a new full and shuffled deck, and an empty discard pile"""
            # returns a number of full decks of 52 cards, shuffles them
            deck = Decks(deck=[], discard_pile=[])
            number_of_decks = 3
    
            # fill deck
            for _n in range(number_of_decks):
                # fill deck with number_of_decks decks worth of cards
                for card_name in CARD_NAMES:
                    # add 4 of each card, totaling one deck with 4 of each card
                    for _ in range(4):
                        deck.deck.append(Card(name=card_name))
    
            deck.shuffle()
            return deck
    
        def shuffle(self) -> None:
            """shuffles the deck"""
            random.shuffle(self.deck)
    
        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:
                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
    class Player:
        hand: Hand
        balance: int
        bet: int
        wins: int
        player_type: PlayerType
        index: int
    
        @classmethod
        def new(cls, player_type: PlayerType, index: int) -> "Player":
            """creates a new player of the given type"""
            return Player(
                hand=Hand(cards=[]),
                balance=STARTING_BALANCE,
                bet=0,
                wins=0,
                player_type=player_type,
                index=index,
            )
    
        def get_name(self) -> str:
            return f"{self.player_type}{self.index}"
    
        def get_bet(self) -> None:
            """gets a bet from the player"""
            if PlayerType.Player == self.player_type:
                if self.balance < 1:
                    print(f"{self.get_name()} is out of money :(")
                    self.bet = 0
                self.bet = get_number_from_user_input(
                    f"{self.get_name()}\tWhat is your bet", 1, self.balance
                )
    
        def hand_as_string(self, hide_dealer: bool) -> str:
            """
            returns a string of the players hand
    
            if player is a dealer, returns the first card in the hand followed
            by *'s for every other card
            if player is a player, returns every card and the total
            """
            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
            raise Exception("This is unreachable")
    
        def get_play(self) -> Play:
            """get the players 'play'"""
            # do different things depending on what type of player this is:
            # 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:
                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:
                    # if there are more than 2 cards in the hand,
                    # at least one turn has happened, so splitting and
                    # doubling down are not allowed
                    valid_results = ["s", "h"]
                else:
                    valid_results = ["s", "h", "d", "/"]
                play = get_char_from_user_input("\tWhat is your play?", valid_results)
                if play == "s":
                    return Play.Stand
                elif play == "h":
                    return Play.Hit
                elif play == "d":
                    return Play.DoubleDown
                elif play == "/":
                    return Play.Split
                else:
                    raise ValueError(f"got invalid character {play}")
            raise Exception("This is unreachable")
    
    
    @dataclass
    class Game:
        players: List[Player]  # last item in this is the dealer
        decks: Decks
        games_played: int
    
        @classmethod
        def new(cls, num_players: int) -> "Game":
            players: List[Player] = [Player.new(PlayerType.Dealer, 0)]
    
            # create human player(s) (at least one)
            players.append(Player.new(PlayerType.Player, 1))
            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()
    
            return Game(players=players, decks=Decks.new(), games_played=0)
    
        def _print_stats(self) -> None:
            """prints the score of every player"""
            print(f"{self.stats_as_string()}")
    
        def stats_as_string(self) -> str:
            """returns a string of the wins, balance, and bets of every player"""
            s = ""
            for p in self.players:
                # format the presentation of player stats
                if p.player_type == PlayerType.Dealer:
                    s += f"{p.get_name()} Wins:\t{p.wins}\n"
                elif p.player_type == PlayerType.Player:
                    s += f"{p.get_name()} "
                    s += f"Wins:\t{p.wins}\t\t"
                    s += f"Balance:\t{p.balance}\t\tBet\t{p.bet}\n"
            return f"Scores:\n{s}"
    
        def play_game(self) -> None:
            """plays a round of blackjack"""
            game = self.games_played
            player_hands_message: str = ""
    
            # deal two cards to each player
            for _i in range(2):
                for player in self.players:
                    player.hand.add_card(self.decks.draw_card())
    
            # get everyones bets
            for player in self.players:
                player.get_bet()
            scores = self.stats_as_string()
    
            # play game for each player
            for player in self.players:
                # turn loop, ends when player finishes their turn
                while True:
                    clear()
                    print_welcome_screen()
                    print(f"\n\t\t\tGame {game}")
                    print(scores)
                    print(player_hands_message)
                    print(f"{player.get_name()} Hand:\t{player.hand_as_string(True)}")
    
                    if PlayerType.Player == player.player_type and player.bet == 0:
                        break
    
                    # play through turn
                    # check their hand value for a blackjack(21) or bust
                    score = player.hand.get_total()
                    if score >= 21:
                        if score == 21:
                            print("\tBlackjack! (21 points)")
                        else:
                            print(f"\tBust      ({score} points)")
                        break
    
                    # get player move
                    play = player.get_play()
                    # process play
                    if play == Play.Stand:
                        print(f"\t{play}")
                        break
                    elif play == Play.Hit:
                        print(f"\t{play}")
                        player.hand.add_card(self.decks.draw_card())
                    elif play == Play.DoubleDown:
                        print(f"\t{play}")
    
                        # double their balance if there's enough money,
                        # othewise go all-in
                        if player.bet * 2 < player.balance:
                            player.bet *= 2
                        else:
                            player.bet = player.balance
                        player.hand.add_card(self.decks.draw_card())
                # add player to score cache thing
                player_hands_message += (
                    f"{player.get_name()} Hand:\t{player.hand_as_string(True)}\n"
                )
    
            # determine winner
            top_score = 0
    
            # player with the highest points
            num_winners = 1
    
            non_burst_players = [
                player for player in self.players if player.hand.get_total() <= 21
            ]
            for player in non_burst_players:
                score = player.hand.get_total()
                if score > top_score:
                    top_score = score
                    num_winners = 1
                elif score == top_score:
                    num_winners += 1
    
            # show winner(s)
            top_score_players = [
                player
                for player in non_burst_players
                if player.hand.get_total() == top_score
            ]
            for x in top_score_players:
                print(f"{x.get_name()} ")
                x.wins += 1
                # increment their wins
            if num_winners > 1:
                print(f"all tie with {top_score}\n\n\n")
            else:
                print(
                    f"wins with {top_score}!\n\n\n",
                )
    
            # handle bets
            # remove money from losers
            losers = [
                player for player in self.players if player.hand.get_total() != top_score
            ]
            for loser in losers:
                loser.balance -= loser.bet
            # add money to winner
            winners = [
                player for player in self.players if player.hand.get_total() == top_score
            ]
            for winner in winners:
                winner.balance += winner.bet
    
            # discard hands
            for player in self.players:
                player.hand.discard_hand(self.decks)
    
            # increment games_played
            self.games_played += 1
    
    
    CARD_NAMES: List[str] = [
        "ACE",
        "2",
        "3",
        "4",
        "5",
        "6",
        "7",
        "8",
        "9",
        "10",
        "JACK",
        "QUEEN",
        "KING",
    ]
    STARTING_BALANCE: int = 100
    
    
    def main() -> None:
        game: Game
    
        print_welcome_screen()
    
        # create game
        game = Game.new(
            get_number_from_user_input("How many players should there be", 1, 7)
        )
    
        # game loop, play game until user wants to stop
        char = "y"
        while char == "y":
            game.play_game()
            char = get_char_from_user_input("Play Again?", ["y", "n"])
    
    
    def print_welcome_screen() -> None:
        print(
            """
                                BLACK JACK
                  CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY
        \n\n"""
        )
    
    
    def print_instructions() -> None:
        print(
            """
        THIS IS THE GAME OF 21. AS MANY AS 7 PLAYERS MAY PLAY THE
        GAME. ON EACH DEAL, BETS WILL BE ASKED FOR, AND THE
        PLAYERS' BETS SHOULD BE TYPED IN. THE CARDS WILL THEN BE
        DEALT, AND EACH PLAYER IN TURN PLAYS HIS HAND. THE
        FIRST RESPONSE SHOULD BE EITHER 'D', INDICATING THAT THE
        PLAYER IS DOUBLING DOWN, 'S', INDICATING THAT HE IS
        STANDING, 'H', INDICATING HE WANTS ANOTHER CARD, OR '/',
        INDICATING THAT HE WANTS TO SPLIT HIS CARDS. AFTER THE
        INITIAL RESPONSE, ALL FURTHER RESPONSES SHOULD BE 'S' OR
        'H', UNLESS THE CARDS WERE SPLIT, IN WHICH CASE DOUBLING
        DOWN IS AGAIN PERMITTED. IN ORDER TO COLLECT FOR
        BLACKJACK, THE INITIAL RESPONSE SHOULD BE 'S'.
        NUMBER OF PLAYERS
    
        NOTE:'/' (splitting) is not currently implemented, and does nothing
    
        PRESS ENTER TO CONTINUE
        """
        )
        input()
    
    
    def get_number_from_user_input(prompt: str, min_value: int, max_value: int) -> int:
        """gets a int integer from user input"""
        # input loop
        user_input = None
        while user_input is None or user_input < min_value or user_input > max_value:
            raw_input = input(f"{prompt} ({min_value}-{max_value})? ")
    
            try:
                user_input = int(raw_input)
                if user_input < min_value or user_input > max_value:
                    print("Invalid input, please try again")
            except ValueError:
                print("Invalid input, please try again")
        return user_input
    
    
    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(f"{prompt} {valid_results}? ").lower()
            if user_input not in valid_results:
                print("Invalid input, please try again")
        assert user_input is not None
        return user_input
    
    
    def clear() -> None:
        """clear std out"""
        print("\x1b[2J\x1b[0;0H")
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 10_Blackjack/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 10_Blackjack/ruby/blackjack.rb
    ================================================
    require_relative "./game.rb"
    
    def intro
        puts "Welcome to Blackjack"
    end
    
    def ask_for_players_count
        puts "How many of you want to join the table?"
        return gets.to_i
    end
    
    begin
        intro
        players_count = ask_for_players_count
        Game.new(players_count).start
    rescue SystemExit, Interrupt
        exit
    rescue => exception
       p exception
    end
    
    
    ================================================
    FILE: 10_Blackjack/ruby/game.rb
    ================================================
    require_relative "./model/hand.rb"
    require_relative "./model/player.rb"
    require_relative "./model/card_kind.rb"
    require_relative "./model/pack.rb"
    
    class Game
    
      ALLOWED_HAND_ACTIONS = {
        "hit" => ["H", "S"],
        "split" => ["H", "S", "D"],
        "normal" => ["H", "S", "/", "D"]
      }
    
      def initialize(players_count)
        @pack = Model::Pack.new
        @dealer_balance = 0
        @dealer_hand = nil
        @players = 1.upto(players_count).map { |id| Model::Player.new(id) }
      end
    
      def start
        loop do
          collect_bets_and_deal
          play_players
          check_for_insurance_bets
          play_dealer
          settle
        end
      end
    
      private
    
      def collect_bets_and_deal
        puts "BETS"
    
        @players.each_entry do |player|
          print "# #{player.id} ? "
          bet = gets.to_i
          player.deal_initial_hand Model::Hand.new(bet, [@pack.draw, @pack.draw])
        end
    
        @dealer_hand = Model::Hand.new(0, [@pack.draw, @pack.draw])
        print_players_and_dealer_hands
      end
    
      def play_players
        @players.each_entry do |player|
          play_hand player, player.hand
        end
      end
    
      def check_for_insurance_bets
        return if @dealer_hand.cards[0].label != "A"
    
        print "ANY INSURANCE? "
        return if gets.strip != "Y"
    
        @players.each_entry do |player|
          print "PLAYER #{player.id} INSURANCE BET? "
          player.bet_insurance(gets.to_i)
        end
      end
    
      def play_dealer
        puts "DEALER HAS A \t#{@dealer_hand.cards[1].label} CONCEALED FOR A TOTAL OF #{@dealer_hand.total}"
    
        while @dealer_hand.total(is_dealer: true) < 17
          card = @pack.draw
          @dealer_hand.hit card
    
          puts "DRAWS #{card.label} \t---TOTAL = #{@dealer_hand.total}"
        end
    
        if !@dealer_hand.is_busted?
          @dealer_hand.stand
        end
      end
    
      def settle
        @players.each_entry do |player|
          player_balance_update = player.update_balance @dealer_hand
          @dealer_balance -= player_balance_update
    
          puts "PLAYER #{player.id} #{player_balance_update < 0 ? "LOSES" : "WINS"} \t#{player_balance_update} \tTOTAL=#{player.balance}"
        end
    
        puts "DEALER'S TOTAL = #{@dealer_balance}"
      end
    
    
      def print_players_and_dealer_hands
        puts "PLAYER\t#{@players.map(&:id).join("\t")}\tDEALER"
        puts "      \t#{@players.map {|p| p.hand.cards[0].label}.join("\t")}\t#{@dealer_hand.cards[0].label}"
        puts "      \t#{@players.map {|p| p.hand.cards[1].label}.join("\t")}"
      end
    
      def play_hand player, hand
        allowed_actions = ALLOWED_HAND_ACTIONS[(hand.is_split_hand || !hand.can_split?) ? "split" : "normal"]
        name = "PLAYER #{player.id}"
        if hand.is_split_hand
          name += " - HAND #{hand === player.hand ? 1 : 2}"
        end
    
        did_hit = false
    
        while hand.is_playing?
          print "#{name}? "
    
          action = gets.strip
    
          if !allowed_actions.include?(action)
            puts "Possible actions: #{allowed_actions.join(", ")}"
            next
          end
    
          if action === "/"
            player.split
    
            play_hand player, player.hand
            play_hand player, player.split_hand
    
            return
          end
    
          if action === "S"
            hand.stand
          end
    
          if action === "D"
            card = @pack.draw
            hand.double_down card
    
            puts "RECEIVED #{card.label}"
          end
    
          if action === "H"
            did_hit = true
            allowed_actions = ALLOWED_HAND_ACTIONS["hit"]
            card = @pack.draw
            hand.hit card
    
            puts "RECEIVED #{card.label}"
          end
        end
    
        puts "TOTAL IS #{hand.total}"
    
        if hand.is_busted?
          puts "... BUSTED"
        end
      end
    end
    
    
    ================================================
    FILE: 10_Blackjack/ruby/model/card_kind.rb
    ================================================
    module Model
    class CardKind
      def initialize(label, value)
        @label = label
        @value = value
      end
      
      private_class_method :new
    
      TWO = self.new("2", 2)
      THREE = self.new("3", 3)
      FOUR = self.new("4", 4)
      FIVE = self.new("5", 5)
      SIX = self.new("6", 6)
      SEVEN = self.new("7", 7)
      EIGHT = self.new("8", 8)
      NINE = self.new("9", 9)
      TEN = self.new("10", 10)
      JACK = self.new("J", 10)
      QUEEN = self.new("Q", 10)
      KING = self.new("K", 10)
      ACE = self.new("A", 11)
    
      KINDS_SET = [
        TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN,
        JACK, QUEEN, KING, ACE
      ]
    
      def same_value?(other_card)
        value == other_card.value
      end
    
      def +(other)
        throw "other doesn't respond to +" unless other.responds_to? :+
    
        other.+(@value)
      end 
    
      attr_reader :label, :value
    end
    end
    
    
    ================================================
    FILE: 10_Blackjack/ruby/model/hand.rb
    ================================================
    require_relative "./card_kind.rb"
    
    module Model
    class Hand
      HAND_STATE_PLAYING = :hand_playing
      HAND_STATE_BUSTED = :hand_busted
      HAND_STATE_STAND = :hand_stand
      HAND_STATE_DOUBLED_DOWN = :hand_doubled_down
    
      def initialize(bet, cards, is_split_hand: false)
        @state = HAND_STATE_PLAYING
        @bet = bet
        @cards = cards
        @total = nil
        @is_split_hand = is_split_hand
      end
    
      attr_reader :bet, :cards, :is_split_hand
    
      def is_playing?
        @state == HAND_STATE_PLAYING
      end
    
      def is_busted?
        @state == HAND_STATE_BUSTED
      end
    
      def is_standing?
        @state == HAND_STATE_STAND
      end
    
      def is_blackjack?
        total == 21 && @cards.length == 2
      end
    
      def total(is_dealer: false)
        return @total unless @total.nil?
        
        @total = @cards.reduce(0) {|sum, card| sum + card.value}
    
        if @total > 21
          aces_count = @cards.count {|c| c == CardKind::ACE}
          while ((!is_dealer && @total > 21) || (is_dealer && @total < 16)) && aces_count > 0 do
            @total -= 10
            aces_count -= 1
          end
        end
    
        @total
      end
    
      ## Hand actions
    
      def can_split?
        not @is_split_hand and @cards.length == 2 && @cards[0].same_value?(cards[1])
      end
    
      def split
        throw "can't split" unless can_split?
        [
          Hand.new(@bet, @cards[0...1], is_split_hand: true),
          Hand.new(@bet, @cards[1..1], is_split_hand: true)
        ]
      end
    
      def hit(card)
        throw "can't hit" unless is_playing?
    
        @cards.push(card)
        @total = nil
    
        check_busted
      end
    
      def double_down(card)
        throw "can't double down" unless is_playing?
    
        @bet *= 2
        hit card
    
        @state = HAND_STATE_DOUBLED_DOWN
      end
    
      def stand
        throw "can't stand" unless is_playing?
    
        @state = HAND_STATE_STAND
      end
    
    
      private
    
      def check_busted
        @state = HAND_STATE_BUSTED if total > 21
      end
    end
    end
    
    
    ================================================
    FILE: 10_Blackjack/ruby/model/pack.rb
    ================================================
    require_relative "./card_kind.rb"
    
    module Model
    class Pack
      def initialize
        @cards = []
        reshuffle
      end
    
      def reshuffle_if_necessary
        return if @cards.count > 2
        reshuffle
      end
    
      def draw
        reshuffle_if_necessary
        @cards.pop
      end
    
      private
    
      def reshuffle
        puts "RESHUFFLING"
        @cards = 4.times.map {|_| CardKind::KINDS_SET}.flatten
        @cards.shuffle!
      end
    end
    end
    
    
    ================================================
    FILE: 10_Blackjack/ruby/model/player.rb
    ================================================
    require_relative "./hand.rb"
    
    module Model
    class Player
      def initialize(id)
        @id = id
        @balance = 0
        @original_bet = 0
        @insurance = 0
    
        @hand = nil
        @split_hand = nil
      end
    
      attr_reader :id, :balance, :hand, :split_hand, :insurance
    
      ## Begining of hand dealing actions
      def deal_initial_hand(hand)
        @hand = hand
        @split_hand = nil
        @max_insurance = @hand.bet / 2
        @insurance = 0
      end
    
      def has_split_hand?
        !@split_hand.nil?
      end
    
      def can_split?
        not has_split_hand? and @hand.can_split?
      end
    
      def split
        throw "can't split" unless can_split?
    
        @hand, @split_hand = @hand.split
      end
    
      def bet_insurance(bet)
        if bet < 0
          bet = 0
          puts "NEGATIVE BET -- using 0 insurance bet"
        end
    
        if bet > @max_insurance
          bet = @max_insurance
          puts "TOO HIGH -- using max insurance bet of #{bet}"
        end
    
        @insurance = bet
      end
    
      ## End of hand dealing actions
    
      def update_balance(dealer_hand)
        balance_update = 0
    
        balance_update += get_balance_update(@hand, dealer_hand)
        if has_split_hand? then
          balance_update += get_balance_update(@split_hand, dealer_hand)
        end
    
        if dealer_hand.is_blackjack?
          balance_update += 2 * @insurance
        else
          balance_update -= @insurance
        end
    
        @balance += balance_update
    
        balance_update
      end
    
    
      private
    
      def get_balance_update(hand, dealer_hand)
        if hand.is_busted?
          return -hand.bet
        elsif dealer_hand.is_busted?
          return hand.bet
        elsif dealer_hand.total == hand.total
          return 0
        else
          return (dealer_hand.total < hand.total ? 1 : -1) * hand.bet
        end
      end
    end
    end
    
    
    ================================================
    FILE: 10_Blackjack/rust/Cargo.toml
    ================================================
    [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"
    
    
    ================================================
    FILE: 10_Blackjack/rust/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Rust](https://www.rust-lang.org/) by Anthony Rubick [AnthonyMichaelTDM](https://github.com/AnthonyMichaelTDM)
    
    
    ================================================
    FILE: 10_Blackjack/rust/src/main.rs
    ================================================
    use rand::{prelude::{thread_rng, SliceRandom}};
    use std::{io, io::{stdout, Write}};
    
    /**
     * todo list:
     *
     * allow splitting
     */
    
    
    //DATA
    //enums
    enum PlayerType {
        Player,
        Dealer,
    }
    impl ToString for PlayerType {
        fn to_string(&self) -> String {
            match self {
                &PlayerType::Dealer => return String::from("Dealer"),
                &PlayerType::Player => return String::from("Player"),
            }
        }
    }
    enum Play {
        Stand,
        Hit,
        DoubleDown,
        Split,
    }
    impl ToString for Play {
        fn to_string(&self) -> String {
            match self {
                &Play::Hit => return String::from("Hit"),
                &Play::Stand => return String::from("Stand"),
                &Play::DoubleDown => return String::from("Double Down"),
                &Play::Split => return String::from("Split")
            }
        }
    }
    //structs
    struct CARD<'a> {
        name: &'a str,
        value: u8,
    }
    impl<'a> CARD<'a> {
        /**
         * creates a new card from the passed card name
         */
        fn new(card_name: &str) -> CARD {
            return CARD { name: card_name, value: CARD::determine_value_from_name(card_name) };
        }
    
        /**
         * returns the value associated with a card with the passed name
         * return 0 if the passed card name doesn't exist
         */
        fn determine_value_from_name(card_name: &str) -> u8 {
            //DATA
            let value:u8;
    
            match card_name.to_ascii_uppercase().as_str() {
                "ACE" => value = 11,
                "2" => value = 2,
                "3" => value = 3,
                "4" => value = 4,
                "5" => value = 5,
                "6" => value = 6,
                "7" => value = 7,
                "8" => value = 8,
                "9" => value = 9,
                "10" => value = 10,
                "JACK" => value = 10,
                "QUEEN" => value = 10,
                "KING" => value = 10,
                _ => value = 0,
            }
    
            return value;
        }
    }
    struct HAND<'a> {
        cards: Vec>,
    }
    impl<'a> HAND<'a> {
        /**
         * returns a new empty hand
         */
        fn new() -> HAND<'a> {
            return HAND { cards: Vec::new()};
        }
    
        /**
         * add a passed card to this hand
         */
        fn add_card(&mut self, card: CARD<'a>) {
            self.cards.push(card);
        }
    
        /**
         * returns the total points of the cards in this hand
         */
        fn get_total(&self) -> usize {
            let mut total:usize = 0;
            for card in &self.cards {
                total += card.value as usize;
            }
    
            //if there is an ACE, and the hand would otherwise bust, treat the ace like it's worth 1
            if total > 21 && self.cards.iter().any(|c| -> bool {*c.name == *"ACE"}) {
                total -= 10;
            }
    
            return total;
        }
    
        /**
         * adds the cards in hand into the discard pile
         */
        fn discard_hand(&mut self, deck: &mut DECKS<'a>) {
            let len = self.cards.len();
            for _i in 0..len {
                deck.discard_pile.push(self.cards.pop().expect("hand empty"));
            }
        }
    }
    struct DECKS<'a> {
        deck: Vec>, //cards in the deck
        discard_pile: Vec> //decks discard pile
    }
    impl<'a> DECKS<'a> {
        /**
         * creates a new full and shuffled deck, and an empty discard pile
         */
        fn new() -> DECKS<'a> {
            //returns a number of full decks of 52 cards, shuffles them
            //DATA
            let mut deck = DECKS{deck: Vec::new(), discard_pile: Vec::new()};
            let number_of_decks = 3;
    
            //fill deck
            for _n in 0..number_of_decks { //fill deck with number_of_decks decks worth of cards
                for card_name in CARD_NAMES { //add 4 of each card, totaling one deck with 4 of each card
                    deck.deck.push( CARD::new(card_name) );
                    deck.deck.push( CARD::new(card_name) );
                    deck.deck.push( CARD::new(card_name) );
                    deck.deck.push( CARD::new(card_name) );
                }
            }
    
            //shuffle deck
            deck.shuffle();
    
            //return deck
            return deck;
        }
    
        /**
         * shuffles the deck
         */
        fn shuffle(&mut self) {
            self.deck.shuffle(&mut thread_rng());
        }
    
        /**
         * draw card from deck, and return it
         * if deck is empty, shuffles discard pile into it and tries again
         */
        fn draw_card(&mut self) -> CARD<'a> {
            match self.deck.pop() {
                Some(card) => return card,
                None => {
                    let len = self.discard_pile.len();
    
                    if len > 0 {//deck is empty, shuffle discard pile into deck and try again
                        println!("deck is empty, shuffling");
                        for _i in 0..len {
                            self.deck.push( self.discard_pile.pop().expect("discard pile empty") )
                        }
                        self.shuffle();
                        return self.draw_card();
                    } else { //discard pile and deck are empty, should never happen
                        panic!("discard pile empty");
                    }
                }
            }
        }
    }
    
    struct PLAYER<'a> {
        hand: HAND<'a>,
        balance: usize,
        bet: usize,
        wins: usize,
        player_type: PlayerType,
        index: usize,
    
    }
    impl<'a> PLAYER<'a> {
        /**
         * creates a new player of the given type
         */
        fn new(player_type: PlayerType, index: usize) -> PLAYER<'a> {
            return PLAYER { hand: HAND::new(), balance: STARTING_BALANCE, bet: 0, wins: 0, player_type: player_type, index: index};
        }
    
        fn get_name(&self) -> String {
            format!("{}{}", self.player_type.to_string(),self.index)
        }
    
        /**
         * gets a bet from the player
         */
        fn get_bet(&mut self) {
            if let PlayerType::Player = self.player_type {
                if self.balance < 1 {
                    println!("{} is out of money :(", self.get_name());
                    self.bet = 0;
                }
                self.bet = get_number_from_user_input(format!("{}\tBet?",self.get_name()).as_str(), 1, self.balance);
            }
        }
    
        /**
         * returns a string of the players hand
         *
         * if player is a dealer, returns the first card in the hand followed by *'s for every other card
         * if player is a player, returns every card and the total
         */
        fn hand_as_string(&self, hide_dealer:bool) -> String {
            if !hide_dealer {
                return format!(
                    "{}\n\ttotal points = {}", //message
                    { //cards in hand
                        let mut s:String = String::new();
                        for cards_in_hand in self.hand.cards.iter().rev() {
                            s += format!("{}\t", cards_in_hand.name).as_str();
                        }
                        s
                    },
                    self.hand.get_total() //total points in hand
                );
            }
            else {
                match &self.player_type {
                    &PlayerType::Dealer =>  { //if this is a dealer
                        return format!(
                            "{}*",//message
                            { //*'s for other cards
                                let mut s:String = String::new();
                                let mut cards_in_hand = self.hand.cards.iter();
                                cards_in_hand.next();//consume first card drawn
                                for c in cards_in_hand.rev() {
                                    s += format!("{}\t", c.name).as_str();
                                }
                                s
                            }
                        );
                    },
                    &PlayerType::Player => { //if this is a player
                        return format!(
                            "{}\n\ttotal points = {}", //message
                            { //cards in hand
                                let mut s:String = String::new();
                                for cards_in_hand in self.hand.cards.iter().rev() {
                                    s += format!("{}\t", cards_in_hand.name).as_str();
                                }
                                s
                            },
                            self.hand.get_total() //total points in hand
                        );
                    }
                }
            }
        }
    
        /**
         * get the players 'play'
         */
        fn get_play(&self) -> Play {
            /*
             do different things depending on what type of player this is:
             if it's a dealer, use an algorithm to determine the play
             if it's a player, ask user for input
             */
            match &self.player_type {
                &PlayerType::Dealer => {
                    if self.hand.get_total() > 16 { // if total value of hand is greater than 16, stand
                        return Play::Stand;
                    } else { //otherwise hit
                        return Play::Hit;
                    }
                },
                &PlayerType::Player => {
                    let valid_results:Vec;
                    if self.hand.cards.len() > 2 {//if there are more than 2 cards in the hand, at least one turn has happened, so splitting and doubling down are not allowed
                        valid_results = vec!['s','S','h','H'];
                    } else {
                        valid_results = vec!['s','S','h','H','d','D','/'];
                    }
                    let play = get_char_from_user_input("\tWhat is your play?", &valid_results);
                    match play {
                        's' | 'S' => return Play::Stand,
                        'h' | 'H' => return Play::Hit,
                        'd' | 'D' => return Play::DoubleDown,
                        '/'       => return Play::Split,
                        _ => panic!("get_char_from_user_input() returned invalid character"),
                    }
                },
            }
        }
    }
    
    struct GAME<'a> {
        players: Vec>, //last item in this is the dealer
        decks: DECKS<'a>,
        games_played:usize,
    }
    impl<'a> GAME<'a> {
        /**
         * creates a new game
         */
        fn new(num_players:usize) -> GAME<'a> {
            //DATA
            let mut players: Vec = Vec::new();
    
            //add dealer
            players.push(PLAYER::new(PlayerType::Dealer,0));
            //create human player(s) (at least one)
            players.push(PLAYER::new(PlayerType::Player,1));
            for i in 2..=num_players { //one less than num_players players
                players.push(PLAYER::new(PlayerType::Player,i));
            }
    
            //ask if they want instructions
            if let 'y'|'Y' = get_char_from_user_input("Do you want instructions? (y/n)", &vec!['y','Y','n','N']) {
                instructions();
            }
            println!();
    
            //return a game
            return GAME { players: players, decks: DECKS::new(), games_played: 0}
        }
    
        /**
         * prints the score of every player
         */
        fn _print_stats(&self) {
            println!("{}", self.stats_as_string());
        }
    
        /**
         * returns a string of the wins, balance, and bets of every player
         */
        fn stats_as_string(&self) -> String {
            format!("Scores:\n{}",{
                let mut s = String::new();
                self.players.iter().for_each(|p| {
                    //format the presentation of player stats
                    match p.player_type {
                        PlayerType::Player => s+= format!("{} Wins:\t{}\t\tBalance:\t{}\t\tBet\t{}\n",p.get_name(),p.wins,p.balance,p.bet).as_str(),
                        PlayerType::Dealer => s+= format!("{} Wins:\t{}\n",p.get_name(),p.wins).as_str()
                    }
                });
                s
            })
        }
    
        /**
         * plays a round of blackjack
         */
        fn play_game(&mut self) {
            //DATA
            let scores;
            let game = self.games_played; //save it here so we don't have borrowing issues
            let mut player_hands_message: String = String::new();//cache it here so we don't have borrowing issues
    
            //deal cards to each player
            for _i in 0..2 { // do this twice
                //draw card for each player
                self.players.iter_mut().for_each(|player| {player.hand.add_card( self.decks.draw_card() );});
            }
    
            //get everyones bets
            self.players.iter_mut().for_each(|player| player.get_bet());
            scores = self.stats_as_string(); //save it here so we don't have borrowing issues later
    
            //play game for each player
            for player in self.players.iter_mut() {
                //turn loop, ends when player finishes their turn
                loop{
                    //clear screen
                    clear();
                    //print welcome
                    welcome();
                    //print game state
                    println!("\n\t\t\tGame {}", game);
                    //print scores
                    println!("{}",scores);
                    //print hands of all players
                    print!("{}", player_hands_message);
                    println!("{} Hand:\t{}", player.get_name(), player.hand_as_string(true));
    
                    if let PlayerType::Player = player.player_type { //player isn't the dealer
                        if player.bet == 0 {//player is out of money
                            break;//exit turn loop
                        }
                    }
    
                    //play through turn
                    //check their hand value for a blackjack(21) or bust
                    let score = player.hand.get_total();
                    if score >= 21 {
                        if score == 21 { // == 21
                            println!("\tBlackjack! (21 points)");
                        } else { // > 21
                            println!("\tBust      ({} points)", score);
                        }
                        break; //end turn
                    }
    
                    //get player move
                    let play = player.get_play();
                    //process play
                    match play {
                        Play::Stand => {
                            println!("\t{}", play.to_string());
                            break; //end turn
                        },
                        Play::Hit => {
                            println!("\t{}", play.to_string());
                            //give them a card
                            player.hand.add_card( self.decks.draw_card() );
                        },
                        Play::DoubleDown => {
                            println!("\t{}", play.to_string());
    
                            //double their balance if there's enough money, othewise go all-in
                            if player.bet * 2 < player.balance {
                                player.bet *= 2;
                            }
                            else {
                                player.bet = player.balance;
                            }
                            //give them a card
                            player.hand.add_card( self.decks.draw_card() );
                        },
                        Play::Split => {
    
                        },
                    }
                }
    
                //add player to score cache thing
                player_hands_message += format!("{} Hand:\t{}\n", player.get_name(), player.hand_as_string(true)).as_str();
            }
    
            //determine winner
            let mut top_score = 0; //player with the highest points
            let mut num_winners = 1;
            for player in self.players.iter_mut().enumerate().filter( |x| -> bool {x.1.hand.get_total()<=21}) { //players_who_didnt_bust
                let score = player.1.hand.get_total();
                if score > top_score {
                    top_score = score;
                    num_winners = 1;
                } else if score == top_score {
                    num_winners += 1;
                }
            }
    
            //print winner(s)
            self.players.iter_mut().filter(|x|->bool{x.hand.get_total()==top_score}).for_each(|x| {//for each player with the top score
                print!("{} ", x.get_name());//print name
                x.wins += 1;//increment their wins
            });
            if num_winners > 1 {println!("all tie with {}\n\n\n", top_score);}
            else {println!("wins with {}!\n\n\n",top_score);}
    
            //handle bets
            //remove money from losers
            self.players.iter_mut().filter(|p| p.hand.get_total()!=top_score).for_each( |p| p.balance -= p.bet); //for every player who didn't get the winning score, remove their bet from their balance
            //add money to winner
            self.players.iter_mut().filter(|p| p.hand.get_total()==top_score).for_each(|p| p.balance += p.bet); //for each player who got the winning score, add their bet to their balance
    
            //discard hands
            self.players.iter_mut().for_each(|player| {player.hand.discard_hand(&mut self.decks);});
    
            //increment games_played
            self.games_played += 1;
        }
    
    
    
    }
    
    const CARD_NAMES: [&str;13] = ["ACE","2","3","4","5","6","7","8","9","10","JACK","QUEEN","KING"];
    const STARTING_BALANCE: usize = 100;
    
    fn main() {
        //DATA
        let mut game: GAME;
    
        //print welcome message
        welcome();
    
        //create game
        game = GAME::new( get_number_from_user_input("How many players should there be (at least 1)?", 1, 7) );
    
        //game loop, play game until user wants to stop
        loop {
            //play round
            game.play_game();
    
            //ask if they want to play again
            match get_char_from_user_input("Play Again? (y/n)", &vec!['y','Y','n','N']) {
                'y' | 'Y' => continue,
                'n' | 'N' => break,
                _ => break,
            }
        }
    }
    
    /**
     * prints the welcome screen
     */
    fn welcome() {
        //welcome message
        print!("
                                BLACK JACK
                  CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY
        \n\n");
    }
    
    /**
     * prints the instructions
     */
    fn instructions() {
        println!("
        THIS IS THE GAME OF 21. AS MANY AS 7 PLAYERS MAY PLAY THE
        GAME. ON EACH DEAL, BETS WILL BE ASKED FOR, AND THE
        PLAYERS' BETS SHOULD BE TYPED IN. THE CARDS WILL THEN BE
        DEALT, AND EACH PLAYER IN TURN PLAYS HIS HAND. THE
        FIRST RESPONSE SHOULD BE EITHER 'D', INDICATING THAT THE
        PLAYER IS DOUBLING DOWN, 'S', INDICATING THAT HE IS
        STANDING, 'H', INDICATING HE WANTS ANOTHER CARD, OR '/',
        INDICATING THAT HE WANTS TO SPLIT HIS CARDS. AFTER THE
        INITIAL RESPONSE, ALL FURTHER RESPONSES SHOULD BE 'S' OR
        'H', UNLESS THE CARDS WERE SPLIT, IN WHICH CASE DOUBLING
        DOWN IS AGAIN PERMITTED. IN ORDER TO COLLECT FOR
        BLACKJACK, THE INITIAL RESPONSE SHOULD BE 'S'.
        NUMBER OF PLAYERS
    
        NOTE:'/' (splitting) is not currently implemented, and does nothing
    
        PRESS ENTER TO CONTINUE
        ");
        io::stdin().read_line(&mut String::new()).expect("Failed to read line");
    }
    
    /**
     * gets a usize integer from user input
     */
    fn get_number_from_user_input(prompt: &str, min:usize, max:usize) -> usize {
        //input loop
        return loop {
            let mut raw_input = String::new(); // temporary variable for user input that can be parsed later
    
            //print prompt
            println!("{}", prompt);
            stdout().flush().expect("Failed to flush to stdout.");
            //read user input from standard input, and store it to raw_input
            //raw_input.clear(); //clear input
            io::stdin().read_line(&mut raw_input).expect( "CANNOT READ INPUT!");
    
            //from input, try to read a number
            match raw_input.trim().parse::() {
                Ok(i) => {
                    if i < min || i > max { //input out of desired range
                        println!("INPUT OUT OF VALID RANGE.  TRY AGAIN.  {}-{}",min,max);
                        continue; // run the loop again
                    }
                    else {
                        break i;// this escapes the loop, returning i
                    }
                },
                Err(e) => {
                    println!("INVALID INPUT.  TRY AGAIN.  {}", e.to_string().to_uppercase());
                    continue; // run the loop again
                }
            };
        };
    }
    
    /**
     * gets a character from user input
     * returns the first character they type
     */
    fn get_char_from_user_input(prompt: &str, valid_results: &Vec) -> char {
        //input loop
        return loop {
            let mut raw_input = String::new(); // temporary variable for user input that can be parsed later
    
            //print prompt
            println!("{}", prompt);
            stdout().flush().expect("Failed to flush to stdout.");
            //read user input from standard input, and store it to raw_input
            //raw_input.clear(); //clear input
            io::stdin().read_line(&mut raw_input).expect( "CANNOT READ INPUT!");
    
            //from input, try to read a valid character
            match raw_input.trim().chars().nth(0) {
                Some(i) => {
                    if !valid_results.contains(&i) { //input out of desired range
                        println!("INPUT IS NOT VALID CHARACTER.  TRY AGAIN.");
                        continue; // run the loop again
                    }
                    else {
                        break i;// this escapes the loop, returning i
                    }
                },
                None => {
                    println!("INVALID INPUT.  TRY AGAIN.");
                    continue; // run the loop again
                }
            };
        };
    }
    
    /**
     * clear std out
     */
    fn clear() {
        println!("\x1b[2J\x1b[0;0H");
    }
    
    
    ================================================
    FILE: 10_Blackjack/vbnet/Blackjack.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Blackjack", "Blackjack.vbproj", "{B112CA5F-142B-46E9-92CB-5E3A84816AAE}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{B112CA5F-142B-46E9-92CB-5E3A84816AAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{B112CA5F-142B-46E9-92CB-5E3A84816AAE}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{B112CA5F-142B-46E9-92CB-5E3A84816AAE}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{B112CA5F-142B-46E9-92CB-5E3A84816AAE}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 10_Blackjack/vbnet/Blackjack.vbproj
    ================================================
    
      
        Exe
        Blackjack
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 10_Blackjack/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 11_Bombardment/README.md
    ================================================
    ### Bombardment
    
    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 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.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=22)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=37)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### 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)
    
    
    ================================================
    FILE: 11_Bombardment/bombardment.bas
    ================================================
    10 PRINT TAB(33);"BOMBARDMENT"
    20 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    30 PRINT:PRINT:PRINT
    100 PRINT "YOU ARE ON A BATTLEFIELD WITH 4 PLATOONS AND YOU"
    110 PRINT "HAVE 25 OUTPOSTS AVAILABLE WHERE THEY MAY BE PLACED."
    120 PRINT "YOU CAN ONLY PLACE ONE PLATOON AT ANY ONE OUTPOST."
    130 PRINT "THE COMPUTER DOES THE SAME WITH ITS FOUR PLATOONS."
    135 PRINT
    140 PRINT "THE OBJECT OF THE GAME IS TO FIRE MISSLES AT THE"
    150 PRINT "OUTPOSTS OF THE COMPUTER.  IT WILL DO THE SAME TO YOU."
    160 PRINT "THE ONE WHO DESTROYS ALL FOUR OF THE ENEMY'S PLATOONS"
    170 PRINT "FIRST IS THE WINNER."
    180 PRINT
    190 PRINT "GOOD LUCK... AND TELL US WHERE YOU WANT THE BODIES SENT!"
    200 PRINT
    210 PRINT "TEAR OFF MATRIX AND USE IT TO CHECK OFF THE NUMBERS."
    220 FOR R=1 TO 5: PRINT: NEXT R
    260 DIM M(100)
    270 FOR R=1 TO 5
    280 I=(R-1)*5+1
    290 PRINT I,I+1,I+2,I+3,I+4
    300 NEXT R
    350 FOR R=1 TO 10: PRINT: NEXT R
    380 C=INT(RND(1)*25)+1
    390 D=INT(RND(1)*25)+1
    400 E=INT(RND(1)*25)+1
    410 F=INT(RND(1)*25)+1
    420 IF C=D THEN 390
    430 IF C=E THEN 400
    440 IF C=F THEN 410
    450 IF D=E THEN 400
    460 IF D=F THEN 410
    470 IF E=F THEN 410
    480 PRINT "WHAT ARE YOUR FOUR POSITIONS";
    490 INPUT G,H,K,L
    495 PRINT
    500 PRINT "WHERE DO YOU WISH TO FIRE YOUR MISSLE";
    510 INPUT Y
    520 IF Y=C THEN 710
    530 IF Y=D THEN 710
    540 IF Y=E THEN 710
    550 IF Y=F THEN 710
    560 GOTO 630
    570 M=INT(RND(1)*25)+1
    575 GOTO 1160
    580 IF X=G THEN 920
    590 IF X=H THEN 920
    600 IF X=L THEN 920
    610 IF X=K THEN 920
    620 GOTO 670
    630 PRINT "HA, HA YOU MISSED. MY TURN NOW:"
    640 PRINT: PRINT: GOTO 570
    670 PRINT "I MISSED YOU, YOU DIRTY RAT. I PICKED";M". YOUR TURN:"
    680 PRINT: PRINT: GOTO 500
    710 Q=Q+1
    720 IF Q=4 THEN 890
    730 PRINT "YOU GOT ONE OF MY OUTPOSTS!"
    740 IF Q=1 THEN 770
    750 IF Q=2 THEN 810
    760 IF Q=3 THEN 850
    770 PRINT "ONE DOWN, THREE TO GO."
    780 PRINT: PRINT: GOTO 570
    810 PRINT "TWO DOWN, TWO TO GO."
    820 PRINT: PRINT: GOTO 570
    850 PRINT "THREE DOWN, ONE TO GO."
    860 PRINT: PRINT: GOTO 570
    890 PRINT "YOU GOT ME, I'M GOING FAST. BUT I'LL GET YOU WHEN"
    900 PRINT "MY TRANSISTO&S RECUP%RA*E!"
    910 GOTO 1235
    920 Z=Z+1
    930 IF Z=4 THEN 1110
    940 PRINT "I GOT YOU. IT WON'T BE LONG NOW. POST";X;"WAS HIT."
    950 IF Z=1 THEN 990
    960 IF Z=2 THEN 1030
    970 IF Z=3 THEN 1070
    990 PRINT "YOU HAVE ONLY THREE OUTPOSTS LEFT."
    1000 PRINT: PRINT: GOTO 500
    1030 PRINT "YOU HAVE ONLY TWO OUTPOSTS LEFT."
    1040 PRINT: PRINT: GOTO 500
    1070 PRINT "YOU HAVE ONLY ONE OUTPOST LEFT."
    1080 PRINT: PRINT: GOTO 500
    1110 PRINT "YOU'RE DEAD. YOUR LAST OUTPOST WAS AT";X;". HA, HA, HA."
    1120 PRINT "BETTER LUCK NEXT TIME."
    1150 GOTO 1235
    1160 P=P+1
    1170 N=P-1
    1180 FOR T=1 TO N
    1190 IF M=M(T) THEN 570
    1200 NEXT T
    1210 X=M
    1220 M(P)=M
    1230 GOTO 580
    1235 END
    
    
    ================================================
    FILE: 11_Bombardment/csharp/Bombardment.cs
    ================================================
    using System;
    using System.Collections.Generic;
    
    namespace Bombardment
    {
        // 
        // Game of Bombardment
        // Based on the Basic game of Bombardment here
        // https://github.com/coding-horror/basic-computer-games/blob/main/11%20Bombardment/bombardment.bas
        // Note:  The idea was to create a version of the 1970's Basic game in C#, without introducing
        // new features - no additional text, error checking, etc has been added.
        // 
        internal class Bombardment
        {
            private static int MAX_GRID_SIZE = 25;
            private static int MAX_PLATOONS = 4;
            private static Random random = new Random();
            private List computerPositions = new List();
            private List playerPositions = new List();
            private List computerGuesses = new List();
    
            private void PrintStartingMessage()
            {
                Console.WriteLine("{0}BOMBARDMENT", new string(' ', 33));
                Console.WriteLine("{0}CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY", new string(' ', 15));
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine();
    
                Console.WriteLine("YOU ARE ON A BATTLEFIELD WITH 4 PLATOONS AND YOU");
                Console.WriteLine("HAVE 25 OUTPOSTS AVAILABLE WHERE THEY MAY BE PLACED.");
                Console.WriteLine("YOU CAN ONLY PLACE ONE PLATOON AT ANY ONE OUTPOST.");
                Console.WriteLine("THE COMPUTER DOES THE SAME WITH ITS FOUR PLATOONS.");
                Console.WriteLine();
                Console.WriteLine("THE OBJECT OF THE GAME IS TO FIRE MISSLES AT THE");
                Console.WriteLine("OUTPOSTS OF THE COMPUTER.  IT WILL DO THE SAME TO YOU.");
                Console.WriteLine("THE ONE WHO DESTROYS ALL FOUR OF THE ENEMY'S PLATOONS");
                Console.WriteLine("FIRST IS THE WINNER.");
                Console.WriteLine();
                Console.WriteLine("GOOD LUCK... AND TELL US WHERE YOU WANT THE BODIES SENT!");
                Console.WriteLine();
                Console.WriteLine("TEAR OFF MATRIX AND USE IT TO CHECK OFF THE NUMBERS.");
    
                // As an alternative to repeating the call to WriteLine(),
                // we can print the new line character five times.
                Console.Write(new string('\n', 5));
    
                // Print a sample board (presumably the game was originally designed to be
                // physically printed on paper while played).
                for (var i = 1; i <= 25; i += 5)
                {
                    // The token replacement can be padded by using the format {tokenPosition, padding}
                    // Negative values for the padding cause the output to be left-aligned.
                    Console.WriteLine("{0,-3}{1,-3}{2,-3}{3,-3}{4,-3}", i, i + 1, i + 2, i + 3, i + 4);
                }
    
                Console.WriteLine("\n");
            }
    
            // Generate 5 random positions for the computer's platoons.
            private void PlaceComputerPlatoons()
            {
                do
                {
                    var nextPosition = random.Next(1, MAX_GRID_SIZE);
                    if (!computerPositions.Contains(nextPosition))
                    {
                        computerPositions.Add(nextPosition);
                    }
    
                } while (computerPositions.Count < MAX_PLATOONS);
            }
    
            private void StoreHumanPositions()
            {
                Console.WriteLine("WHAT ARE YOUR FOUR POSITIONS");
    
                // The original game assumed that the input would be five comma-separated values, all on one line.
                // For example: 12,22,1,4,17
                var input = Console.ReadLine();
                var playerPositionsAsStrings = input.Split(",");
                foreach (var playerPosition in playerPositionsAsStrings) {
                    playerPositions.Add(int.Parse(playerPosition));
                }
            }
    
            private void HumanTurn()
            {
                Console.WriteLine("WHERE DO YOU WISH TO FIRE YOUR MISSLE");
                var input = Console.ReadLine();
                var humanGuess = int.Parse(input);
    
                if(computerPositions.Contains(humanGuess))
                {
                    Console.WriteLine("YOU GOT ONE OF MY OUTPOSTS!");
                    computerPositions.Remove(humanGuess);
    
                    switch(computerPositions.Count)
                    {
                        case 3:
                            Console.WriteLine("ONE DOWN, THREE TO GO.");
                            break;
                        case 2:
                            Console.WriteLine("TWO DOWN, TWO TO GO.");
                            break;
                        case 1:
                            Console.WriteLine("THREE DOWN, ONE TO GO.");
                            break;
                        case 0:
                            Console.WriteLine("YOU GOT ME, I'M GOING FAST.");
                            Console.WriteLine("BUT I'LL GET YOU WHEN MY TRANSISTO&S RECUP%RA*E!");
                            break;
                    }
                }
                else
                {
                    Console.WriteLine("HA, HA YOU MISSED. MY TURN NOW:");
                }
            }
    
            private int GenerateComputerGuess()
            {
                int computerGuess;
                do
                {
                    computerGuess = random.Next(1, 25);
                }
                while(computerGuesses.Contains(computerGuess));
                computerGuesses.Add(computerGuess);
    
                return computerGuess;
            }
    
            private void ComputerTurn()
            {
                var computerGuess = GenerateComputerGuess();
    
                if (playerPositions.Contains(computerGuess))
                {
                    Console.WriteLine("I GOT YOU. IT WON'T BE LONG NOW. POST {0} WAS HIT.", computerGuess);
                    playerPositions.Remove(computerGuess);
    
                    switch(playerPositions.Count)
                    {
                        case 3:
                            Console.WriteLine("YOU HAVE ONLY THREE OUTPOSTS LEFT.");
                            break;
                        case 2:
                            Console.WriteLine("YOU HAVE ONLY TWO OUTPOSTS LEFT.");
                            break;
                        case 1:
                            Console.WriteLine("YOU HAVE ONLY ONE OUTPOST LEFT.");
                            break;
                        case 0:
                            Console.WriteLine("YOU'RE DEAD. YOUR LAST OUTPOST WAS AT {0}. HA, HA, HA.", computerGuess);
                            Console.WriteLine("BETTER LUCK NEXT TIME.");
                            break;
                    }
                }
                else
                {
                    Console.WriteLine("I MISSED YOU, YOU DIRTY RAT. I PICKED {0}. YOUR TURN:", computerGuess);
                }
            }
    
            public void Play()
            {
                PrintStartingMessage();
                PlaceComputerPlatoons();
                StoreHumanPositions();
    
                while (playerPositions.Count > 0 && computerPositions.Count > 0)
                {
                    HumanTurn();
    
                    if (computerPositions.Count > 0)
                    {
                        ComputerTurn();
                    }
                }
            }
        }
    }
    
    
    ================================================
    FILE: 11_Bombardment/csharp/Bombardment.csproj
    ================================================
    
    
      
        Exe
        netcoreapp2.1
      
    
    
    
    
    ================================================
    FILE: 11_Bombardment/csharp/Bombardment.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bombardment", "Bombardment.csproj", "{1DCFD283-9300-405B-A2B4-231F30265730}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{1DCFD283-9300-405B-A2B4-231F30265730}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{1DCFD283-9300-405B-A2B4-231F30265730}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{1DCFD283-9300-405B-A2B4-231F30265730}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{1DCFD283-9300-405B-A2B4-231F30265730}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 11_Bombardment/csharp/Program.cs
    ================================================
    using System;
    
    namespace Bombardment
    {
        class Program
        {
            static void Main(string[] args)
            {
                var bombardment = new Bombardment();
                bombardment.Play();
            }
        }
    }
    
    
    ================================================
    FILE: 11_Bombardment/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 11_Bombardment/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 11_Bombardment/java/src/Bombardment.java
    ================================================
    import java.util.HashSet;
    import java.util.Scanner;
    
    /**
     * Game of Bombardment
     * 

    * Based on the Basic game of Bombardment here * https://github.com/coding-horror/basic-computer-games/blob/main/11%20Bombardment/bombardment.bas *

    * Note: The idea was to create a version of the 1970's Basic game in Java, without introducing * new features - no additional text, error checking, etc has been added. */ public class Bombardment { public static final int MAX_GRID_SIZE = 25; public static final int PLATOONS = 4; private enum GAME_STATE { STARTING, DRAW_BATTLEFIELD, GET_PLAYER_CHOICES, PLAYERS_TURN, COMPUTER_TURN, PLAYER_WON, PLAYER_LOST, GAME_OVER } private GAME_STATE gameState; public static final String[] PLAYER_HIT_MESSAGES = {"ONE DOWN, THREE TO GO.", "TWO DOWN, TWO TO GO.", "THREE DOWN, ONE TO GO."}; public static final String[] COMPUTER_HIT_MESSAGES = {"YOU HAVE ONLY THREE OUTPOSTS LEFT.", "YOU HAVE ONLY TWO OUTPOSTS LEFT.", "YOU HAVE ONLY ONE OUTPOST LEFT."}; private HashSet computersPlatoons; private HashSet playersPlatoons; private HashSet computersGuesses; // Used for keyboard input private final Scanner kbScanner; public Bombardment() { gameState = GAME_STATE.STARTING; // Initialise kb scanner kbScanner = new Scanner(System.in); } /** * Main game loop */ public void play() { do { switch (gameState) { // Show an introduction the first time the game is played. case STARTING: init(); intro(); gameState = GAME_STATE.DRAW_BATTLEFIELD; break; // Enter the players name case DRAW_BATTLEFIELD: drawBattlefield(); gameState = GAME_STATE.GET_PLAYER_CHOICES; break; // Get the players 4 locations for their platoons case GET_PLAYER_CHOICES: String playerChoices = displayTextAndGetInput("WHAT ARE YOUR FOUR POSITIONS? "); // Store the 4 player choices that were entered separated with commas for (int i = 0; i < PLATOONS; i++) { playersPlatoons.add(getDelimitedValue(playerChoices, i)); } gameState = GAME_STATE.PLAYERS_TURN; break; // Players turn to pick a location case PLAYERS_TURN: int firePosition = getDelimitedValue( displayTextAndGetInput("WHERE DO YOU WISH TO FIRE YOUR MISSILE? "), 0); if (didPlayerHitComputerPlatoon(firePosition)) { // Player hit a player platoon int hits = updatePlayerHits(firePosition); // How many hits has the player made? if (hits != PLATOONS) { showPlayerProgress(hits); gameState = GAME_STATE.COMPUTER_TURN; } else { // Player has obtained 4 hits, they win gameState = GAME_STATE.PLAYER_WON; } } else { // Player missed System.out.println("HA, HA YOU MISSED. MY TURN NOW:"); System.out.println(); gameState = GAME_STATE.COMPUTER_TURN; } break; // Computers time to guess a location case COMPUTER_TURN: // Computer takes a guess of a location int computerFirePosition = uniqueComputerGuess(); if (didComputerHitPlayerPlatoon(computerFirePosition)) { // Computer hit a player platoon int hits = updateComputerHits(computerFirePosition); // How many hits has the computer made? if (hits != PLATOONS) { showComputerProgress(hits, computerFirePosition); gameState = GAME_STATE.PLAYERS_TURN; } else { // Computer has obtained 4 hits, they win System.out.println("YOU'RE DEAD. YOUR LAST OUTPOST WAS AT " + computerFirePosition + ". HA, HA, HA."); gameState = GAME_STATE.PLAYER_LOST; } } else { // Computer missed System.out.println("I MISSED YOU, YOU DIRTY RAT. I PICKED " + computerFirePosition + ". YOUR TURN:"); System.out.println(); gameState = GAME_STATE.PLAYERS_TURN; } break; // The player won case PLAYER_WON: System.out.println("YOU GOT ME, I'M GOING FAST. BUT I'LL GET YOU WHEN"); System.out.println("MY TRANSISTO&S RECUP%RA*E!"); gameState = GAME_STATE.GAME_OVER; break; case PLAYER_LOST: System.out.println("BETTER LUCK NEXT TIME."); gameState = GAME_STATE.GAME_OVER; break; // GAME_OVER State does not specifically have a case } } while (gameState != GAME_STATE.GAME_OVER); } /** * Calculate computer guess. Make that the computer does not guess the same * location twice * * @return location of the computers guess that has not been guessed previously */ private int uniqueComputerGuess() { boolean validGuess = false; int computerGuess; do { computerGuess = randomNumber(); if (!computersGuesses.contains(computerGuess)) { validGuess = true; } } while (!validGuess); computersGuesses.add(computerGuess); return computerGuess; } /** * Create four unique platoons locations for the computer * We are using a hashset which guarantees uniqueness so * all we need to do is keep trying to add a random number * until all four are in the hashset * * @return 4 locations of computers platoons */ private HashSet computersChosenPlatoons() { // Initialise contents HashSet tempPlatoons = new HashSet<>(); boolean allPlatoonsAdded = false; do { tempPlatoons.add(randomNumber()); // All four created? if (tempPlatoons.size() == PLATOONS) { // Exit when we have created four allPlatoonsAdded = true; } } while (!allPlatoonsAdded); return tempPlatoons; } /** * Shows a different message for each number of hits * * @param hits total number of hits by player on computer */ private void showPlayerProgress(int hits) { System.out.println("YOU GOT ONE OF MY OUTPOSTS!"); showProgress(hits, PLAYER_HIT_MESSAGES); } /** * Shows a different message for each number of hits * * @param hits total number of hits by computer on player */ private void showComputerProgress(int hits, int lastGuess) { System.out.println("I GOT YOU. IT WON'T BE LONG NOW. POST " + lastGuess + " WAS HIT."); showProgress(hits, COMPUTER_HIT_MESSAGES); } /** * Prints a message from the passed array based on the value of hits * * @param hits - number of hits the player or computer has made * @param messages - an array of string with messages */ private void showProgress(int hits, String[] messages) { System.out.println(messages[hits - 1]); } /** * Update a player hit - adds a hit the player made on the computers platoon. * * @param fireLocation - computer location that got hit * @return number of hits the player has inflicted on the computer in total */ private int updatePlayerHits(int fireLocation) { // N.B. only removes if present, so its redundant to check if it exists first computersPlatoons.remove(fireLocation); // return number of hits in total return PLATOONS - computersPlatoons.size(); } /** * Update a computer hit - adds a hit the computer made on the players platoon. * * @param fireLocation - player location that got hit * @return number of hits the player has inflicted on the computer in total */ private int updateComputerHits(int fireLocation) { // N.B. only removes if present, so its redundant to check if it exists first playersPlatoons.remove(fireLocation); // return number of hits in total return PLATOONS - playersPlatoons.size(); } /** * Determine if the player hit one of the computers platoons * * @param fireLocation the players choice of location to fire on * @return true if a computer platoon was at that position */ private boolean didPlayerHitComputerPlatoon(int fireLocation) { return computersPlatoons.contains(fireLocation); } /** * Determine if the computer hit one of the players platoons * * @param fireLocation the computers choice of location to fire on * @return true if a players platoon was at that position */ private boolean didComputerHitPlayerPlatoon(int fireLocation) { return playersPlatoons.contains(fireLocation); } /** * Draw the battlefield grid */ private void drawBattlefield() { for (int i = 1; i < MAX_GRID_SIZE + 1; i += 5) { System.out.printf("%-2s %-2s %-2s %-2s %-2s %n", i, i + 1, i + 2, i + 3, i + 4); } } /** * Basic information about the game */ private void intro() { System.out.println("BOMBARDMENT"); System.out.println("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println(); System.out.println("YOU ARE ON A BATTLEFIELD WITH 4 PLATOONS AND YOU"); System.out.println("HAVE 25 OUTPOSTS AVAILABLE WHERE THEY MAY BE PLACED."); System.out.println("YOU CAN ONLY PLACE ONE PLATOON AT ANY ONE OUTPOST."); System.out.println("THE COMPUTER DOES THE SAME WITH ITS FOUR PLATOONS."); System.out.println(); System.out.println("THE OBJECT OF THE GAME IS TO FIRE MISSILES AT THE"); System.out.println("OUTPOSTS OF THE COMPUTER. IT WILL DO THE SAME TO YOU."); System.out.println("THE ONE WHO DESTROYS ALL FOUR OF THE ENEMY'S PLATOONS"); System.out.println("FIRST IS THE WINNER."); System.out.println(); System.out.println("GOOD LUCK... AND TELL US WHERE YOU WANT THE BODIES SENT!"); System.out.println(); System.out.println("TEAR OFF MATRIX AND USE IT TO CHECK OFF THE NUMBERS."); System.out.println(); System.out.println(); } private void init() { // Create four locations for the computers platoons. computersPlatoons = computersChosenPlatoons(); // Players platoons. playersPlatoons = new HashSet<>(); computersGuesses = new HashSet<>(); } /** * Accepts a string delimited by comma's and returns the nth delimited * value (starting at count 0). * * @param text - text with values separated by comma's * @param pos - which position to return a value for * @return the int representation of the value */ private int getDelimitedValue(String text, int pos) { String[] tokens = text.split(","); return Integer.parseInt(tokens[pos]); } /* * Print a message on the screen, then accept input from Keyboard. * * @param text message to be displayed on screen. * @return what was typed by the player. */ private String displayTextAndGetInput(String text) { System.out.print(text); return kbScanner.next(); } /** * Generate random number * * @return random number */ private int randomNumber() { return (int) (Math.random() * (MAX_GRID_SIZE) + 1); } } ================================================ FILE: 11_Bombardment/java/src/BombardmentGame.java ================================================ public class BombardmentGame { public static void main(String[] args) { Bombardment bombardment = new Bombardment(); bombardment.play(); } } ================================================ FILE: 11_Bombardment/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 11_Bombardment/javascript/bombardment.html ================================================ BOMBARDMENT

    
    
    
    
    
    
    ================================================
    FILE: 11_Bombardment/javascript/bombardment.js
    ================================================
    // BOMBARDMENT
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    // Main program
    async function main()
    {
        print(tab(33) + "BOMBARDMENT\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        print("YOU ARE ON A BATTLEFIELD WITH 4 PLATOONS AND YOU\n");
        print("HAVE 25 OUTPOSTS AVAILABLE WHERE THEY MAY BE PLACED.\n");
        print("YOU CAN ONLY PLACE ONE PLATOON AT ANY ONE OUTPOST.\n");
        print("THE COMPUTER DOES THE SAME WITH ITS FOUR PLATOONS.\n");
        print("\n");
        print("THE OBJECT OF THE GAME IS TO FIRE MISSILES AT THE\n");
        print("OUTPOSTS OF THE COMPUTER.  IT WILL DO THE SAME TO YOU.\n");
        print("THE ONE WHO DESTROYS ALL FOUR OF THE ENEMY'S PLATOONS\n");
        print("FIRST IS THE WINNER.\n");
        print("\n");
        print("GOOD LUCK... AND TELL US WHERE YOU WANT THE BODIES SENT!\n");
        print("\n");
        // "TEAR OFF" because it supposed this to be printed on a teletype
        print("TEAR OFF MATRIX AND USE IT TO CHECK OFF THE NUMBERS.\n");
        for (r = 1; r <= 5; r++)
            print("\n");
        ma = [];
        for (r = 1; r <= 100; r++)
            ma[r] = 0;
        p = 0;
        q = 0;
        z = 0;
        for (r = 1; r <= 5; r++) {
            i = (r - 1) * 5 + 1;
            print(i + "\t" + (i + 1) + "\t" + (i + 2) + "\t" + (i + 3) + "\t" + (i + 4) + "\n");
        }
        for (r = 1; r <= 10; r++)
            print("\n");
        c = Math.floor(Math.random() * 25) + 1;
        do {
            d = Math.floor(Math.random() * 25) + 1;
            e = Math.floor(Math.random() * 25) + 1;
            f = Math.floor(Math.random() * 25) + 1;
        } while (c == d || c == e || c == f || d == e || d == f || e == f) ;
        print("WHAT ARE YOUR FOUR POSITIONS");
        str = await input();
        g = parseInt(str);
        str = str.substr(str.indexOf(",") + 1);
        h = parseInt(str);
        str = str.substr(str.indexOf(",") + 1);
        k = parseInt(str);
        str = str.substr(str.indexOf(",") + 1);
        l = parseInt(str);
        print("\n");
        // Another "bug" your outpost can be in the same position as a computer outpost
        // Let us suppose both live in a different matrix.
        while (1) {
            // The original game didn't limited the input to 1-25
            do {
                print("WHERE DO YOU WISH TO FIRE YOUR MISSLE");
                y = parseInt(await input());
            } while (y < 0 || y > 25) ;
            if (y == c || y == d || y == e || y == f) {
    
                // The original game has a bug. You can shoot the same outpost
                // several times. This solves it.
                if (y == c)
                    c = 0;
                if (y == d)
                    d = 0;
                if (y == e)
                    e = 0;
                if (y == f)
                    f = 0;
                q++;
                if (q == 1) {
                    print("ONE DOWN. THREE TO GO.\n");
                } else if (q == 2) {
                    print("TWO DOWN. TWO TO GO.\n");
                } else if (q == 3) {
                    print("THREE DOWN. ONE TO GO.\n");
                } else {
                    print("YOU GOT ME, I'M GOING FAST. BUT I'LL GET YOU WHEN\n");
                    print("MY TRANSISTO&S RECUP%RA*E!\n");
                    break;
                }
            } else {
                print("HA, HA YOU MISSED. MY TURN NOW:\n");
            }
            print("\n");
            print("\n");
            do {
                m = Math.floor(Math.random() * 25 + 1);
                p++;
                n = p - 1;
                for (t = 1; t <= n; t++) {
                    if (m == ma[t])
                        break;
                }
            } while (t <= n) ;
            x = m;
            ma[p] = m;
            if (x == g || x == h || x == l || x == k) {
                z++;
                if (z < 4)
                    print("I GOT YOU. IT WON'T BE LONG NOW. POST " + x + " WAS HIT.\n");
                if (z == 1) {
                    print("YOU HAVE ONLY THREE OUTPOSTS LEFT.\n");
                } else if (z == 2) {
                    print("YOU HAVE ONLY TWO OUTPOSTS LEFT.\n");
                } else if (z == 3) {
                    print("YOU HAVE ONLY ONE OUTPOST LEFT.\n");
                } else {
                    print("YOU'RE DEAD. YOUR LAST OUTPOST WAS AT " + x + ". HA, HA, HA.\n");
                    print("BETTER LUCK NEXT TIME.\n");
                }
            } else {
                print("I MISSED YOU, YOU DIRTY RAT. I PICKED " + m + ". YOUR TURN:\n");
            }
            print("\n");
            print("\n");
        }
    }
    
    main();
    
    
    ================================================
    FILE: 11_Bombardment/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 11_Bombardment/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 11_Bombardment/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 11_Bombardment/perl/bombardment.pl
    ================================================
    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    
    #GLOBAL
    my %player_bases;
    my %computer_bases;
    my %player_choices;
    my %computer_choices;
    
    &main;
    
    sub main {
    	&print_intro;
    	&display_field;
    	&populate_computer_bases;
    	&populate_player_bases;
    	&game_play;
    }
    
    sub game_play {
    	until (keys %computer_bases == 0 || keys %player_bases == 0) {
    		&player_turn;
    		if (keys %computer_bases == 0) {
    			exit;
    		}
    		&computer_turn;
    	}
    	exit;
    }
    
    sub computer_turn {
    	# There is logic in here to ensure that the computer doesn't try to pick a target it has already picked
    	my $valid_choice = 0;
    	until ($valid_choice == 1) {
    		my $target = int(rand(25)+1);
    		if (exists $computer_choices{$target}) {
    			$valid_choice = 0;
    		}
    		else {
    			$valid_choice = 1;
    			$computer_choices{$target}=1;
    			if (exists $player_bases{$target}) {
    				delete($player_bases{$target});
    				my $size = keys %player_bases;
    				if ($size > 0) {
    					print "I GOT YOU.  IT WON'T BE LONG NOW. POST $target WAS HIT.\n";
    					if ($size == 3) { print "YOU HAVE ONLY THREE OUTPOSTS LEFT.\n"};
    					if ($size == 2) { print "YOU HAVE ONLY TWO OUTPOSTS LEFT.\n"};
    					if ($size == 1) { print "YOU HAVE ONLY ONE OUTPOSTS LEFT.\n"};
    				}
    				else {
    					print "YOU'RE DEAD. YOUR LAST OUTPOST WAS AT $target. HA, HA, HA.\nBETTER LUCK NEXT TIME\n";
    				}
    
    			}
    			else {
    				print "I MISSED YOU, YOU DIRTY RAT. I PICKED $target. YOUR TURN:\n";
    			}
    		}
    	}
    }
    
    sub player_turn {
    	print "WHERE DO YOU WISH TO FIRE YOUR MISSILE\n";
    	chomp(my $target=);
    	if (exists $computer_bases{$target}) {
    		print "YOU GOT ONE OF MY OUTPOSTS!\n";
    		delete($computer_bases{$target});
    		my $size = keys %computer_bases;
    		if ($size == 3) { print "ONE DOWN, THREE TO GO.\n"};
    		if ($size == 2) { print "TWO DOWN, TWO TO GO.\n"};
    		if ($size == 1) { print "THREE DOWN, ONE TO GO.\n"};
    		if ($size == 0) { print "YOU GOT ME, I'M GOING FAST. BUT I'LL GET YOU WHEN\nMY TRANSISTO&S RECUP%RA*E!\n"};
    	}
    	else {
    		print "HA, HA YOU MISSED. MY TURN NOW:\n";
    	}
    }
    
    sub populate_player_bases {
    	print "WHAT ARE YOUR FOUR POSITIONS\n";
    	my $positions=;
    	chomp($positions);
    	my @positions = split/ /,$positions;
    	foreach my $base (@positions) {
    		$player_bases{$base}=0;
    	}
    }
    
    sub display_field {
    	for my $num (1..25) {
    		if (length($num) < 2) {
    			$num = " $num";
    		}
    		print "$num ";
    		if ($num % 5 == 0) {
    			print "\n";
    		}
    	}
    }
    
    sub populate_computer_bases {
    	my $size = 0;
    	until ($size == 4) {
    		my $base = int(rand(25)+1);
    		$computer_bases{$base}=0;
    		$size = keys %computer_bases;
    	}
    }
    
    sub print_intro {
        print " " x 33, "BOMBARDMENT\n";
        print " " x 15, " CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n";
        print "\n\n";
        print "YOU ARE ON A BATTLEFIELD WITH 4 PLATOONS AND YOU\n";
        print "HAVE 25 OUTPOSTS AVAILABLE WHERE THEY MAY BE PLACED.\n";
        print "YOU CAN ONLY PLACE ONE PLATOON AT ANY ONE OUTPOST.\n";
        print "THE COMPUTER DOES THE SAME WITH ITS FOUR PLATOONS.\n\n";
        print "THE OBJECT OF THE GAME IS TO FIRE MISSLES AT THE\n";
        print "OUTPOSTS OF THE COMPUTER.  IT WILL DO THE SAME TO YOU.\n";
        print "THE ONE WHO DESTROYS ALL FOUR OF THE ENEMY'S PLATOONS\n";
        print "FIRST IS THE WINNER.\n\n";
        print "GOOD LUCK... AND TELL US WHERE YOU WANT THE BODIES SENT!\n\n";
        print "TEAR OFF MATRIX AND USE IT TO CHECK OFF THE NUMBERS.\n";
        print "\n\n\n\n";
    }
    
    
    ================================================
    FILE: 11_Bombardment/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 11_Bombardment/python/bombardment.py
    ================================================
    #!/usr/bin/env python3
    import random
    from functools import partial
    from typing import Callable, List, Set
    
    
    def print_intro() -> None:
        print(" " * 33 + "BOMBARDMENT")
        print(" " * 15 + " CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY")
        print("\n\n")
        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 MISSLES 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()
        print("TEAR OFF MATRIX AND USE IT TO CHECK OFF THE NUMBERS.")
        print("\n" * 4)
    
    
    def display_field() -> None:
        for row in range(5):
            initial = row * 5 + 1
            print("\t".join([str(initial + column) for column in range(5)]))
        print("\n" * 9)
    
    
    def positions_list() -> List[int]:
        return list(range(1, 26, 1))
    
    
    def generate_enemy_positions() -> Set[int]:
        """Randomly choose 4 'positions' out of a range of 1 to 25"""
        positions = positions_list()
        random.shuffle(positions)
        return set(positions[:4])
    
    
    def is_valid_position(pos: int) -> bool:
        return pos in positions_list()
    
    
    def prompt_for_player_positions() -> Set[int]:
        while True:
            raw_positions = input("WHAT ARE YOUR FOUR POSITIONS? ")
            positions = {int(pos) for pos in raw_positions.split()}
            # Verify user inputs (for example, if the player gives a
            # a position for 26, the enemy can never hit it)
            if len(positions) != 4:
                print("PLEASE ENTER 4 UNIQUE POSITIONS\n")
                continue
            elif any(not is_valid_position(pos) for pos in positions):
                print("ALL POSITIONS MUST RANGE (1-25)\n")
                continue
            else:
                return positions
    
    
    def prompt_player_for_target() -> int:
    
        while True:
            target = int(input("WHERE DO YOU WISH TO FIRE YOUR MISSLE? "))
            if not is_valid_position(target):
                print("POSITIONS MUST RANGE (1-25)\n")
                continue
    
            return target
    
    
    def attack(
        target: int,
        positions: Set[int],
        hit_message: str,
        miss_message: str,
        progress_messages: str,
    ) -> bool:
        """Performs attack procedure returning True if we are to continue."""
    
        if target in positions:
            print(hit_message.format(target))
            positions.remove(target)
            print(progress_messages[len(positions)].format(target))
        else:
            print(miss_message.format(target))
    
        return len(positions) > 0
    
    
    def init_enemy() -> Callable[[], int]:
        """
        Return a closure analogous to prompt_player_for_target.
    
        Will choose from a unique sequence of positions to avoid picking the
        same position twice.
        """
    
        position_sequence = positions_list()
        random.shuffle(position_sequence)
        position = iter(position_sequence)
    
        def choose() -> int:
            return next(position)
    
        return choose
    
    
    # Messages correspond to outposts remaining (3, 2, 1, 0)
    PLAYER_PROGRESS_MESSAGES = (
        "YOU GOT ME, I'M GOING FAST. BUT I'LL GET YOU WHEN\nMY TRANSISTO&S RECUP%RA*E!",
        "THREE DOWN, ONE TO GO.\n\n",
        "TWO DOWN, TWO TO GO.\n\n",
        "ONE DOWN, THREE TO GO.\n\n",
    )
    
    
    ENEMY_PROGRESS_MESSAGES = (
        "YOU'RE DEAD. YOUR LAST OUTPOST WAS AT {}. HA, HA, HA.\nBETTER LUCK NEXT TIME.",
        "YOU HAVE ONLY ONE OUTPOST LEFT.\n\n",
        "YOU HAVE ONLY TWO OUTPOSTS LEFT.\n\n",
        "YOU HAVE ONLY THREE OUTPOSTS LEFT.\n\n",
    )
    
    
    def main() -> None:
        print_intro()
        display_field()
    
        enemy_positions = generate_enemy_positions()
        player_positions = prompt_for_player_positions()
    
        # Build partial functions only requiring the target as input
        player_attacks = partial(
            attack,
            positions=enemy_positions,
            hit_message="YOU GOT ONE OF MY OUTPOSTS!",
            miss_message="HA, HA YOU MISSED. MY TURN NOW:\n\n",
            progress_messages=PLAYER_PROGRESS_MESSAGES,
        )
    
        enemy_attacks = partial(
            attack,
            positions=player_positions,
            hit_message="I GOT YOU. IT WON'T BE LONG NOW. POST {} WAS HIT.",
            miss_message="I MISSED YOU, YOU DIRTY RAT. I PICKED {}. YOUR TURN:\n\n",
            progress_messages=ENEMY_PROGRESS_MESSAGES,
        )
    
        enemy_position_choice = init_enemy()
    
        # Play as long as both player_attacks and enemy_attacks allow to continue
        while player_attacks(prompt_player_for_target()) and enemy_attacks(
            enemy_position_choice()
        ):
            pass
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 11_Bombardment/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 11_Bombardment/ruby/bombardment.rb
    ================================================
    require "set"
    
    class Battlefield
      TOTAL_OUTPOSTS = 25
      TOTAL_PLATOONS = 4
    
      class << self
        def with_random_platoon_placements
          new((1..TOTAL_OUTPOSTS).to_a.sample(4))
        end
    
        def valid_platoon_placement?(i)
          i.between?(1, TOTAL_OUTPOSTS)
        end
      end
    
      def initialize(platoon_placements)
        @platoon_placements = Set.new(platoon_placements)
      end
    
      # Fires a missile at the specified outpost. Returns true if it hits a
      # platoon, false otherwise.
      def fire_missile_at(outpost)
        !@platoon_placements.delete?(outpost).nil?
      end
    
      def remaining_platoons_count
        @platoon_placements.size
      end
    end
    
    class Game
      def initialize(player_battlefield:, computer_battlefield:)
        @player_battlefield = player_battlefield
        @computer_battlefield = computer_battlefield
      end
    
      def play
        player_choice = get_integer_answer_to("WHERE DO YOU WISH TO FIRE")
    
        if @computer_battlefield.fire_missile_at(player_choice)
          print_player_success_message
          return if @computer_battlefield.remaining_platoons_count == 0
        else
          puts "HA, HA YOU MISSED. MY TURN NOW:"
        end
        puts
    
        computer_choice = rand(1..Battlefield::TOTAL_OUTPOSTS)
    
        if @player_battlefield.fire_missile_at(computer_choice)
          print_computer_success_message(computer_choice)
        else
          puts "I MISSED YOU, YOU DIRTY RAT. I PICKED #{computer_choice}. YOUR TURN:"
        end
        puts
      end
    
      def finished?
        @player_battlefield.remaining_platoons_count == 0 ||
          @computer_battlefield.remaining_platoons_count == 0
      end
    
      private
    
      def get_integer_answer_to(question)
        puts question
        $stdin.gets.strip.to_i
      end
    
      def print_computer_success_message(computer_choice)
        if @player_battlefield.remaining_platoons_count == 0
          puts "YOU'RE DEAD. YOUR LAST OUTPOST WAS AT #{computer_choice}. HA, HA, HA."
          puts "BETTER LUCK NEXT TIME."
          return
        end
    
        puts "I GOT YOU. IT WON'T BE LONG NOW. POST #{computer_choice} WAS HIT."
    
        case @player_battlefield.remaining_platoons_count
        when 1
          puts "YOU HAVE ONLY ONE OUTPOST LEFT"
        when 2
          puts "YOU HAVE ONLY TWO OUTPOSTS LEFT"
        when 3
          puts "YOU HAVE ONLY THREE OUTPOSTS LEFT"
        end
      end
    
      def print_player_success_message
        if @computer_battlefield.remaining_platoons_count == 0
          puts "YOU GOT ME, I'M GOING FAST. BUT I'LL GET YOU WHEN"
          puts "MY TRANSISTO&S RECUP%RA*E!"
          return
        end
    
        puts "YOU GOT ONE OF MY OUTPOSTS!"
    
        case @computer_battlefield.remaining_platoons_count
        when 1
          puts "THREE DOWN, ONE TO GO."
        when 2
          puts "TWO DOWN, TWO TO GO."
        when 3
          puts "ONE DOWN, THREE TO GO."
        end
      end
    end
    
    INTRODUCTION = <<~TEXT
                                       BOMBARDMENT
                     CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY
    
    
    
      YOU ARE ON A BATTLEFIELD WITH 4 PLATOONS AND YOU
      HAVE 25 OUTPOSTS AVAILABLE WHERE THEY MAY BE PLACED.
      YOU CAN ONLY PLACE ONE PLATOON AT ANY ONE OUTPOST.
      THE COMPUTER DOES THE SAME WITH ITS FOUR PLATOONS.
    
      THE OBJECT OF THE GAME IS TO FIRE MISSLES AT THE
      OUTPOSTS OF THE COMPUTER.  IT WILL DO THE SAME TO YOU.
      THE ONE WHO DESTROYS ALL FOUR OF THE ENEMY'S PLATOONS
      FIRST IS THE WINNER.
    
      GOOD LUCK... AND TELL US WHERE YOU WANT THE BODIES SENT!
    
      TEAR OFF MATRIX AND USE IT TO CHECK OFF THE NUMBERS.
    TEXT
    
    def print_matrix
      (1..25).each do |i|
        print i
        print i % 5 == 0 ? "\n" : "\t"
      end
    end
    
    def ask_for_platoon_placements
      puts "WAHT ARE YOUR FOUR POSITIONS?"
      platoon_placements = $stdin.gets.strip.split(",").map(&:to_i)
    
      if platoon_placements.size == 4 &&
          platoon_placements.all? { |i| Battlefield.valid_platoon_placement?(i) }
        platoon_placements
      else
        puts "POSITIONS SHOULD BE BETWEEN 1 TO 25, DELIMITED BY COMMA."
        ask_for_platoon_placements
      end
    end
    
    def start_game
      puts INTRODUCTION
      puts
      print_matrix
      puts
      platoon_placements = ask_for_platoon_placements
      puts
    
      game = Game.new(
        player_battlefield: Battlefield.new(platoon_placements),
        computer_battlefield: Battlefield.with_random_platoon_placements
      )
    
      game.play until game.finished?
    end
    
    start_game
    
    
    ================================================
    FILE: 11_Bombardment/rust/Cargo.toml
    ================================================
    [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"
    morristown = "0.1.4"
    
    ================================================
    FILE: 11_Bombardment/rust/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Rust](https://www.rust-lang.org/) by [ugurkupeli](https://github.com/ugurkupeli)
    
    
    ================================================
    FILE: 11_Bombardment/rust/src/main.rs
    ================================================
    use morristown::PromptMultiOption;
    use rand::{prelude::SliceRandom, thread_rng};
    use std::time::Duration;
    
    fn main() {
        morristown::print_intro("BOMBARDMENT");
    
        println!(
            r#"
    
    YOU ARE ON A BATTLEFIELD WITH 4 PLATOONS AND YOU
    HAVE 25 OUTPOSTS AVAILABLE WHERE THEY MAY BE PLACED.
    YOU CAN ONLY PLACE ONE PLATOON AT ANY ONE OUTPOST.
    THE COMPUTER DOES THE SAME WITH ITS FOUR PLATOONS.
    
    THE OBJECT OF THE GAME IS TO FIRE MISSLES AT THE
    OUTPOSTS OF THE COMPUTER.  IT WILL DO THE SAME TO YOU.
    THE ONE WHO DESTROYS ALL FOUR OF THE ENEMY'S PLATOONS
    FIRST IS THE WINNER.
    
    GOOD LUCK... AND TELL US WHERE YOU WANT THE BODIES SENT!
    
    TEAR OFF MATRIX AND USE IT TO CHECK OFF THE NUMBERS.
    
    
    "#
        );
    
        let mut all_positions = Vec::with_capacity(25);
    
        for i in 1u8..=25 {
            all_positions.push(i);
    
            print!("{i}\t");
            if (i % 5) == 0 {
                println!();
            }
        }
    
        let mut player_positions = morristown::prompt_multi_number::(
            "WHAT ARE YOUR FOUR POSITIONS?\n",
            ",",
            Some(PromptMultiOption::UnitAmount(4)),
            Some(1..=25),
        );
    
        let mut rng = thread_rng();
    
        let ai_positions = rand::seq::index::sample(&mut rng, 24, 4).into_vec();
        let mut ai_positions: Vec = ai_positions.iter().map(|i| (i + 1) as u8).collect();
    
        loop {
            if !player_turn(&mut ai_positions) {
                break;
            } else if !ai_turn(&mut all_positions, &mut player_positions) {
                break;
            }
        }
    }
    
    fn player_turn(ai_positions: &mut Vec) -> bool {
        let player_missile =
            morristown::prompt_number_range::("WHERE DO YOU WISH TO FIRE YOUR MISSILE?", 1..=25);
    
        if let Some(index) = ai_positions.iter().position(|p| *p == player_missile) {
            ai_positions.remove(index);
    
            let remaining = ai_positions.len();
    
            if remaining > 0 {
                let down = 4 - remaining;
    
                println!(
                    "YOU GOT ONE OF MY OUTPOSTS.\n{} DOWN, {} TO GO\n",
                    get_text_from_number(down),
                    get_text_from_number(remaining)
                );
            } else {
                println!(
                    "YOU GOT ME, I'M GOING FAST. BUT I'LL GET YOU WHEN MY TRANSISTO&S RECUP%RA*E!\n\n"
                );
                return false;
            }
        } else {
            println!("HA, HA YOU MISSED. MY TURN NOW\n");
        }
        true
    }
    
    fn ai_turn(all_positions: &mut Vec, player_positions: &mut Vec) -> bool {
        std::thread::sleep(Duration::from_secs(1));
    
        let ai_missile = *all_positions
            .choose(&mut rand::thread_rng())
            .expect("AI RAN OUT OF OPTIONS!");
    
        let index = all_positions
            .iter()
            .position(|p| p == &ai_missile)
            .expect("AI CHOOSE AN INVALID POSITION!");
    
        all_positions.remove(index);
    
        if let Some(index) = player_positions.iter().position(|p| p == &ai_missile) {
            player_positions.remove(index);
    
            let remaining = player_positions.len();
    
            if remaining > 0 {
                println!("I GOT YOU. IT WON'T BE LONG NOW. POST {ai_missile} WAS HIT.");
                println!(
                    "YOU HAVE ONLY {} OUTPOST LEFT.\n",
                    get_text_from_number(remaining)
                );
            } else {
                println!("YOU'RE DEAD. YOUR LAST OUTPOST WAS AT {ai_missile} . HA, HA, HA.");
                println!("BETTER LUCK NEXT TIME.");
                return false;
            }
        } else {
            println!("I MISSED YOU, YOU DIRTY RAT. I PICKED {ai_missile} . YOUR TURN.\n")
        }
    
        true
    }
    
    fn get_text_from_number(n: usize) -> &'static str {
        match n {
            1 => "ONE",
            2 => "TWO",
            3 => "THREE",
            _ => panic!("INVALID INDEX!"),
        }
    }
    
    
    ================================================
    FILE: 11_Bombardment/vbnet/Bombardment.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Bombardment", "Bombardment.vbproj", "{7C967BC0-101C-413F-92A8-3A8A7D9846FE}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{7C967BC0-101C-413F-92A8-3A8A7D9846FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{7C967BC0-101C-413F-92A8-3A8A7D9846FE}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{7C967BC0-101C-413F-92A8-3A8A7D9846FE}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{7C967BC0-101C-413F-92A8-3A8A7D9846FE}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 11_Bombardment/vbnet/Bombardment.vbproj
    ================================================
    
      
        Exe
        Bombardment
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 11_Bombardment/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 12_Bombs_Away/README.md
    ================================================
    ### Bombs Away
    
    In this program, you fly a World War II bomber for one of the four protagonists of the war. You then pick your target or the type of plane you are flying. Depending on your flying experience and the quality of enemy defenders, you then may accomplish your mission, get shot down, or make it back through enemy fire. In any case, you get a chance to fly again.
    
    David Ahl modified the original program which was created by David Sherman while a student at Curtis Jr. High School, Sudbury, Massachusetts.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=24)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=39)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### 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)
    
    
    ================================================
    FILE: 12_Bombs_Away/bombsaway.bas
    ================================================
    8 PRINT "YOU ARE A PILOT IN A WORLD WAR II BOMBER."
    10 INPUT "WHAT SIDE -- ITALY(1), ALLIES(2), JAPAN(3), GERMANY(4)";A
    20 IF A>0 AND A<5 THEN 25
    22 PRINT "TRY AGAIN..." : GOTO 10
    25 ON A GOTO 30, 110, 200, 220
    30 INPUT "YOUR TARGET -- ALBANIA(1), GREECE(2), NORTH AFRICA(3)";B
    40 IF B>0 AND B<4 THEN 45
    42 PRINT "TRY AGAIN..." : GOTO 30
    45 PRINT : ON B GOTO 50, 80,90
    50 PRINT "SHOULD BE EASY -- YOU'RE FLYING A NAZI-MADE PLANE."
    60 GOTO 280
    80 PRINT "BE CAREFUL!!!" : GOTO 280
    90 PRINT "YOU'RE GOING FOR THE OIL, EH?" : GOTO 280
    110 INPUT "AIRCRAFT -- LIBERATOR(1), B-29(2), B-17(3), LANCASTER(4)";G
    120 IF G>0 AND G<5 THEN 125
    122 PRINT "TRY AGAIN..." : GOTO 110
    125 PRINT : ON G GOTO 130, 150, 170, 190
    130 PRINT "YOU'VE GOT 2 TONS OF BOMBS FLYING FOR PLOESTI." : GOTO 280
    150 PRINT "YOU'RE DUMPING THE A-BOMB ON HIROSHIMA." : GOTO 280
    170 PRINT "YOU'RE CHASING THE BISMARK IN THE NORTH SEA." : GOTO 280
    190 PRINT "YOU'RE BUSTING A GERMAN HEAVY WATER PLANT IN THE RUHR."
    195 GOTO 280
    200 PRINT "YOU'RE FLYING A KAMIKAZE MISSION OVER THE USS LEXINGTON."
    205 INPUT "YOUR FIRST KAMIKAZE MISSION(Y OR N)";F$
    207 IF F$="N" THEN S=0 : GOTO 358
    210 PRINT : IF RND(1)>.65 THEN 325
    215 GOTO 380
    220 PRINT "A NAZI, EH?  OH WELL.  ARE YOU GOING FOR RUSSIA(1),"
    230 INPUT "ENGLAND(2), OR FRANCE(3)";M : IF M>0 AND M<4 THEN 235
    232 PRINT "TRY AGAIN..." : GOTO 220
    235 PRINT : ON M GOTO 250, 260, 270
    250 PRINT "YOU'RE NEARING STALINGRAD." : GOTO 280
    260 PRINT "NEARING LONDON.  BE CAREFUL, THEY'VE GOT RADAR." : GOTO 280
    270 PRINT "NEARING VERSAILLES.  DUCK SOUP.  THEY'RE NEARLY DEFENSELESS."
    280 PRINT
    285 INPUT "HOW MANY MISSIONS HAVE YOU FLOWN";D
    290 IF D<160 THEN 300
    292 PRINT "MISSIONS, NOT MILES..."
    295 PRINT "150 MISSIONS IS HIGH EVEN FOR OLD-TIMERS."
    297 PRINT "NOW THEN, "; : GOTO 285
    300 PRINT:IF D<100 THEN 310
    305 PRINT "THAT'S PUSHING THE ODDS!" : GOTO 320
    310 IF D<25 THEN PRINT "FRESH OUT OF TRAINING, EH?"
    320 PRINT : IF D<160*RND(1) THEN 330
    325 PRINT "DIRECT HIT!!!! "INT(100*RND(1))"KILLED."
    327 PRINT "MISSION SUCCESSFUL." : GOTO 390
    330 PRINT "MISSED TARGET BY"INT(2+30*RND(1))"MILES!"
    335 PRINT "NOW YOU'RE REALLY IN FOR IT !!" : PRINT
    340 INPUT "DOES THE ENEMY HAVE GUNS(1), MISSILES(2), OR BOTH(3)";R
    345 IF R>0 AND R<4 THEN 350
    347 PRINT "TRY AGAIN..." : GOTO 340
    350 PRINT : T=0 : IF R=2 THEN 360
    355 INPUT "WHAT'S THE PERCENT HIT RATE OF ENEMY GUNNERS (10 TO 50)";S
    357 IF S<10 THEN PRINT "YOU LIE, BUT YOU'LL PAY...": GOTO 380
    358 PRINT
    360 PRINT : IF R>1 THEN T=35
    365 IF S+T>100*RND(1) THEN 380
    370 PRINT "YOU MADE IT THROUGH TREMENDOUS FLAK!!" : GOTO 390
    380 PRINT "* * * * BOOM * * * *"
    384 PRINT "YOU HAVE BEEN SHOT DOWN....."
    386 PRINT "DEARLY BELOVED, WE ARE GATHERED HERE TODAY TO PAY OUR"
    387 PRINT "LAST TRIBUTE..."
    390 PRINT:PRINT:PRINT:INPUT "ANOTHER MISSION (Y OR N)";U$
    395 IF U$="Y" THEN 8
    400 PRINT "CHICKEN !!!" : PRINT : END
    
    
    ================================================
    FILE: 12_Bombs_Away/csharp/BombsAway.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 17
    VisualStudioVersion = 17.0.32014.148
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BombsAwayConsole", "BombsAwayConsole\BombsAwayConsole.csproj", "{D80015FA-423C-4A16-AA2B-16669245AD59}"
    EndProject
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BombsAwayGame", "BombsAwayGame\BombsAwayGame.csproj", "{F57AEC18-FEE9-4F08-9F20-DFC56EFE6BFC}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{D80015FA-423C-4A16-AA2B-16669245AD59}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{D80015FA-423C-4A16-AA2B-16669245AD59}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{D80015FA-423C-4A16-AA2B-16669245AD59}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{D80015FA-423C-4A16-AA2B-16669245AD59}.Release|Any CPU.Build.0 = Release|Any CPU
    		{F57AEC18-FEE9-4F08-9F20-DFC56EFE6BFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{F57AEC18-FEE9-4F08-9F20-DFC56EFE6BFC}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{F57AEC18-FEE9-4F08-9F20-DFC56EFE6BFC}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{F57AEC18-FEE9-4F08-9F20-DFC56EFE6BFC}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {39B2ECFB-037D-4335-BBD2-64892E953DD4}
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 12_Bombs_Away/csharp/BombsAwayConsole/BombsAwayConsole.csproj
    ================================================
    
    
      
        Exe
        net6.0
        enable
        enable
      
    
      
        
      
    
    
    
    
    ================================================
    FILE: 12_Bombs_Away/csharp/BombsAwayConsole/ConsoleUserInterface.cs
    ================================================
    namespace BombsAwayConsole;
    
    /// 
    /// Implements  by writing to and reading from .
    /// 
    internal class ConsoleUserInterface : BombsAwayGame.IUserInterface
    {
        /// 
        /// Write message to console.
        /// 
        /// Message to display.
        public void Output(string message)
        {
            Console.WriteLine(message);
        }
    
        /// 
        /// Write choices with affixed indexes, allowing the user to choose by index.
        /// 
        /// Message to display.
        /// Choices to display.
        /// Choice that user picked.
        public int Choose(string message, IList choices)
        {
            IEnumerable choicesWithIndexes = choices.Select((choice, index) => $"{choice}({index + 1})");
            string choiceText = string.Join(", ", choicesWithIndexes);
            Output($"{message} -- {choiceText}");
    
            ISet allowedKeys = ConsoleKeysFromList(choices);
            ConsoleKey? choice;
            do
            {
                choice = ReadChoice(allowedKeys);
                if (choice is null)
                {
                    Output("TRY AGAIN...");
                }
            }
            while (choice is null);
    
            return ListIndexFromConsoleKey(choice.Value);
        }
    
        /// 
        /// Convert the given list to its  equivalents. This generates keys that map
        /// the first element to , the second element to ,
        /// and so on, up to the last element of the list.
        /// 
        /// List whose elements will be converted to  equivalents.
        ///  equivalents from .
        private ISet ConsoleKeysFromList(IList list)
        {
            IEnumerable indexes = Enumerable.Range((int)ConsoleKey.D1, list.Count);
            return new HashSet(indexes.Cast());
        }
    
        /// 
        /// Convert the given console key to its list index equivalent. This assumes the key was generated from
        /// 
        /// 
        /// Key to convert to its list index equivalent.
        /// List index equivalent of key.
        private int ListIndexFromConsoleKey(ConsoleKey key)
        {
            return key - ConsoleKey.D1;
        }
    
        /// 
        /// Read a key from the console and return it if it is in the given allowed keys.
        /// 
        /// Allowed keys.
        /// Key read from , if it is in ; null otherwise./>
        private ConsoleKey? ReadChoice(ISet allowedKeys)
        {
            ConsoleKeyInfo keyInfo = ReadKey();
            return allowedKeys.Contains(keyInfo.Key) ? keyInfo.Key : null;
        }
    
        /// 
        /// Read key from .
        /// 
        /// Key read from .
        private ConsoleKeyInfo ReadKey()
        {
            ConsoleKeyInfo result = Console.ReadKey(intercept: false);
            // Write a blank line to the console so the displayed key is on its own line.
            Console.WriteLine();
            return result;
        }
    
        /// 
        /// Allow user to choose 'Y' or 'N' from .
        /// 
        /// Message to display.
        /// True if user chose 'Y', false if user chose 'N'.
        public bool ChooseYesOrNo(string message)
        {
            Output(message);
            ConsoleKey? choice;
            do
            {
                choice = ReadChoice(new HashSet(new[] { ConsoleKey.Y, ConsoleKey.N }));
                if (choice is null)
                {
                    Output("ENTER Y OR N");
                }
            }
            while (choice is null);
    
            return choice.Value == ConsoleKey.Y;
        }
    
        /// 
        /// Get integer by reading a line from .
        /// 
        /// Integer read from .
        public int InputInteger()
        {
            bool resultIsValid;
            int result;
            do
            {
                string? integerText = Console.ReadLine();
                resultIsValid = int.TryParse(integerText, out result);
                if (!resultIsValid)
                {
                    Output("PLEASE ENTER A NUMBER");
                }
            }
            while (!resultIsValid);
    
            return result;
        }
    }
    
    
    ================================================
    FILE: 12_Bombs_Away/csharp/BombsAwayConsole/Program.cs
    ================================================
    using BombsAwayConsole;
    using BombsAwayGame;
    
    /// Create and play s using a .
    PlayGameWhileUserWantsTo(new ConsoleUserInterface());
    
    void PlayGameWhileUserWantsTo(ConsoleUserInterface ui)
    {
        do
        {
            new Game(ui).Play();
        }
        while (UserWantsToPlayAgain(ui));
    }
    
    bool UserWantsToPlayAgain(IUserInterface ui)
    {
        bool result = ui.ChooseYesOrNo("ANOTHER MISSION (Y OR N)?");
        if (!result)
        {
            Console.WriteLine("CHICKEN !!!");
        }
    
        return result;
    }
    
    
    ================================================
    FILE: 12_Bombs_Away/csharp/BombsAwayGame/AlliesSide.cs
    ================================================
    namespace BombsAwayGame;
    
    /// 
    /// Allies protagonist. Can fly missions in a Liberator, B-29, B-17, or Lancaster.
    /// 
    internal class AlliesSide : MissionSide
    {
        public AlliesSide(IUserInterface ui)
            : base(ui)
        {
        }
    
        protected override string ChooseMissionMessage => "AIRCRAFT";
    
        protected override IList AllMissions => new Mission[]
        {
            new("LIBERATOR", "YOU'VE GOT 2 TONS OF BOMBS FLYING FOR PLOESTI."),
            new("B-29", "YOU'RE DUMPING THE A-BOMB ON HIROSHIMA."),
            new("B-17", "YOU'RE CHASING THE BISMARK IN THE NORTH SEA."),
            new("LANCASTER", "YOU'RE BUSTING A GERMAN HEAVY WATER PLANT IN THE RUHR.")
        };
    }
    
    
    ================================================
    FILE: 12_Bombs_Away/csharp/BombsAwayGame/BombsAwayGame.csproj
    ================================================
    
    
      
        net6.0
        enable
        enable
      
    
    
    
    
    ================================================
    FILE: 12_Bombs_Away/csharp/BombsAwayGame/EnemyArtillery.cs
    ================================================
    namespace BombsAwayGame;
    
    /// 
    /// Represents enemy artillery.
    /// 
    /// Name of artillery type.
    /// Accuracy of artillery. This is the `T` variable in the original BASIC.
    internal record class EnemyArtillery(string Name, int Accuracy);
    
    
    ================================================
    FILE: 12_Bombs_Away/csharp/BombsAwayGame/Game.cs
    ================================================
    namespace BombsAwayGame;
    
    /// 
    /// Plays the Bombs Away game using a supplied .
    /// 
    public class Game
    {
        private readonly IUserInterface _ui;
    
        /// 
        /// Create game instance using the given UI.
        /// 
        /// UI to use for game.
        public Game(IUserInterface ui)
        {
            _ui = ui;
        }
    
        /// 
        /// Play game. Choose a side and play the side's logic.
        /// 
        public void Play()
        {
            _ui.Output("YOU ARE A PILOT IN A WORLD WAR II BOMBER.");
            Side side = ChooseSide();
            side.Play();
        }
    
        /// 
        /// Represents a .
        /// 
        /// Name of side.
        /// Create instance of side that this descriptor represents.
        private record class SideDescriptor(string Name, Func CreateSide);
    
        /// 
        /// Choose side and return a new instance of that side.
        /// 
        /// New instance of side that was chosen.
        private Side ChooseSide()
        {
            SideDescriptor[] sides = AllSideDescriptors;
            string[] sideNames = sides.Select(a => a.Name).ToArray();
            int index = _ui.Choose("WHAT SIDE", sideNames);
            return sides[index].CreateSide();
        }
    
        /// 
        /// All side descriptors.
        /// 
        private SideDescriptor[] AllSideDescriptors => new SideDescriptor[]
        {
            new("ITALY", () => new ItalySide(_ui)),
            new("ALLIES", () => new AlliesSide(_ui)),
            new("JAPAN", () => new JapanSide(_ui)),
            new("GERMANY", () => new GermanySide(_ui)),
        };
    }
    
    
    ================================================
    FILE: 12_Bombs_Away/csharp/BombsAwayGame/GermanySide.cs
    ================================================
    namespace BombsAwayGame;
    
    /// 
    /// Germany protagonist. Can fly missions to Russia, England, and France.
    /// 
    internal class GermanySide : MissionSide
    {
        public GermanySide(IUserInterface ui)
            : base(ui)
        {
        }
    
        protected override string ChooseMissionMessage => "A NAZI, EH?  OH WELL.  ARE YOU GOING FOR";
    
        protected override IList AllMissions => new Mission[]
        {
            new("RUSSIA", "YOU'RE NEARING STALINGRAD."),
            new("ENGLAND", "NEARING LONDON.  BE CAREFUL, THEY'VE GOT RADAR."),
            new("FRANCE", "NEARING VERSAILLES.  DUCK SOUP.  THEY'RE NEARLY DEFENSELESS.")
        };
    }
    
    
    ================================================
    FILE: 12_Bombs_Away/csharp/BombsAwayGame/IUserInterface.cs
    ================================================
    namespace BombsAwayGame;
    
    /// 
    /// Represents an interface for supplying data to the game.
    /// 
    /// 
    /// Abstracting the UI allows us to concentrate its concerns in one part of our code and to change UI behavior
    /// without creating any risk of changing the game logic. It also allows us to supply an automated UI for tests.
    /// 
    public interface IUserInterface
    {
        /// 
        /// Display the given message.
        /// 
        /// Message to display.
        void Output(string message);
    
        /// 
        /// Choose an item from the given choices.
        /// 
        /// Message to display.
        /// Choices to choose from.
        /// Index of choice in  that user chose.
        int Choose(string message, IList choices);
    
        /// 
        /// Allow user to choose Yes or No.
        /// 
        /// Message to display.
        /// True if user chose Yes, false if user chose No.
        bool ChooseYesOrNo(string message);
    
        /// 
        /// Get integer from user.
        /// 
        /// Integer supplied by user.
        int InputInteger();
    }
    
    
    ================================================
    FILE: 12_Bombs_Away/csharp/BombsAwayGame/ItalySide.cs
    ================================================
    namespace BombsAwayGame;
    
    /// 
    /// Italy protagonist. Can fly missions to Albania, Greece, and North Africa.
    /// 
    internal class ItalySide : MissionSide
    {
        public ItalySide(IUserInterface ui)
            : base(ui)
        {
        }
    
        protected override string ChooseMissionMessage => "YOUR TARGET";
    
        protected override IList AllMissions => new Mission[]
        {
            new("ALBANIA", "SHOULD BE EASY -- YOU'RE FLYING A NAZI-MADE PLANE."),
            new("GREECE", "BE CAREFUL!!!"),
            new("NORTH AFRICA", "YOU'RE GOING FOR THE OIL, EH?")
        };
    }
    
    
    ================================================
    FILE: 12_Bombs_Away/csharp/BombsAwayGame/JapanSide.cs
    ================================================
    namespace BombsAwayGame;
    
    /// 
    /// Japan protagonist. Flies a kamikaze mission, which has a different logic from s.
    /// 
    internal class JapanSide : Side
    {
        public JapanSide(IUserInterface ui)
            : base(ui)
        {
        }
    
        /// 
        /// Perform a kamikaze mission. If first kamikaze mission, it will succeed 65% of the time. If it's not
        /// first kamikaze mission, perform an enemy counterattack.
        /// 
        public override void Play()
        {
            UI.Output("YOU'RE FLYING A KAMIKAZE MISSION OVER THE USS LEXINGTON.");
    
            bool isFirstMission = UI.ChooseYesOrNo("YOUR FIRST KAMIKAZE MISSION(Y OR N)?");
            if (!isFirstMission)
            {
                // LINE 207 of original BASIC: hitRatePercent is initialized to 0,
                // but R, the type of artillery, is not initialized at all. Setting
                // R = 1, which is to say EnemyArtillery = Guns, gives the same result.
                EnemyCounterattack(Guns, hitRatePercent: 0);
            }
            else if (RandomFrac() > 0.65)
            {
                MissionSucceeded();
            }
            else
            {
                MissionFailed();
            }
        }
    }
    
    
    ================================================
    FILE: 12_Bombs_Away/csharp/BombsAwayGame/Mission.cs
    ================================================
    namespace BombsAwayGame;
    
    /// 
    /// Represents a mission that can be flown by a .
    /// 
    /// Name of mission.
    /// Description of mission.
    internal record class Mission(string Name, string Description);
    
    
    ================================================
    FILE: 12_Bombs_Away/csharp/BombsAwayGame/MissionSide.cs
    ================================================
    namespace BombsAwayGame;
    
    /// 
    /// Represents a protagonist that chooses a standard (non-kamikaze) mission.
    /// 
    internal abstract class MissionSide : Side
    {
        /// 
        /// Create instance using the given UI.
        /// 
        /// UI to use.
        public MissionSide(IUserInterface ui)
            : base(ui)
        {
        }
    
        /// 
        /// Reasonable upper bound for missions flown previously.
        /// 
        private const int MaxMissionCount = 160;
    
        /// 
        /// Choose a mission and attempt it. If attempt fails, perform an enemy counterattack.
        /// 
        public override void Play()
        {
            Mission mission = ChooseMission();
            UI.Output(mission.Description);
    
            int missionCount = MissionCountFromUI();
            CommentOnMissionCount(missionCount);
    
            AttemptMission(missionCount);
        }
    
        /// 
        /// Choose a mission.
        /// 
        /// Mission chosen.
        private Mission ChooseMission()
        {
            IList missions = AllMissions;
            string[] missionNames = missions.Select(a => a.Name).ToArray();
            int index = UI.Choose(ChooseMissionMessage, missionNames);
            return missions[index];
        }
    
        /// 
        /// Message to display when choosing a mission.
        /// 
        protected abstract string ChooseMissionMessage { get; }
    
        /// 
        /// All aviailable missions to choose from.
        /// 
        protected abstract IList AllMissions { get; }
    
        /// 
        /// Get mission count from UI. If mission count exceeds a reasonable maximum, ask UI again.
        /// 
        /// Mission count from UI.
        private int MissionCountFromUI()
        {
            const string HowManyMissions = "HOW MANY MISSIONS HAVE YOU FLOWN?";
            string inputMessage = HowManyMissions;
    
            bool resultIsValid;
            int result;
            do
            {
                UI.Output(inputMessage);
                result = UI.InputInteger();
                if (result < 0)
                {
                    UI.Output($"NUMBER OF MISSIONS CAN'T BE NEGATIVE.");
                    resultIsValid = false;
                }
                else if (result > MaxMissionCount)
                {
                    resultIsValid = false;
                    UI.Output($"MISSIONS, NOT MILES...{MaxMissionCount} MISSIONS IS HIGH EVEN FOR OLD-TIMERS.");
                    inputMessage = "NOW THEN, " + HowManyMissions;
                }
                else
                {
                    resultIsValid = true;
                }
            }
            while (!resultIsValid);
    
            return result;
        }
    
        /// 
        /// Display a message about the given mission count, if it is unusually high or low.
        /// 
        /// Mission count to comment on.
        private void CommentOnMissionCount(int missionCount)
        {
            if (missionCount >= 100)
            {
                UI.Output("THAT'S PUSHING THE ODDS!");
            }
            else if (missionCount < 25)
            {
                UI.Output("FRESH OUT OF TRAINING, EH?");
            }
        }
    
        /// 
        /// Attempt mission.
        /// 
        /// Number of missions previously flown. Higher mission counts will yield a higher probability of success.
        private void AttemptMission(int missionCount)
        {
            if (missionCount < RandomInteger(0, MaxMissionCount))
            {
                MissedTarget();
            }
            else
            {
                MissionSucceeded();
            }
        }
    
        /// 
        /// Display message indicating that target was missed. Choose enemy artillery and perform a counterattack.
        /// 
        private void MissedTarget()
        {
            UI.Output("MISSED TARGET BY " + (2 + RandomInteger(0, 30)) + " MILES!");
            UI.Output("NOW YOU'RE REALLY IN FOR IT !!");
    
            // Choose enemy and counterattack.
            EnemyArtillery enemyArtillery = ChooseEnemyArtillery();
    
            if (enemyArtillery == Missiles)
            {
                EnemyCounterattack(enemyArtillery, hitRatePercent: 0);
            }
            else
            {
                int hitRatePercent = EnemyHitRatePercentFromUI();
                if (hitRatePercent < MinEnemyHitRatePercent)
                {
                    UI.Output("YOU LIE, BUT YOU'LL PAY...");
                    MissionFailed();
                }
                else
                {
                    EnemyCounterattack(enemyArtillery, hitRatePercent);
                }
            }
        }
    
        /// 
        /// Choose enemy artillery from UI.
        /// 
        /// Artillery chosen.
        private EnemyArtillery ChooseEnemyArtillery()
        {
            EnemyArtillery[] artilleries = new EnemyArtillery[] { Guns, Missiles, Both };
            string[] artilleryNames = artilleries.Select(a => a.Name).ToArray();
            int index = UI.Choose("DOES THE ENEMY HAVE", artilleryNames);
            return artilleries[index];
        }
    
        /// 
        /// Minimum allowed hit rate percent.
        /// 
        private const int MinEnemyHitRatePercent = 10;
    
        /// 
        /// Maximum allowed hit rate percent.
        /// 
        private const int MaxEnemyHitRatePercent = 50;
    
        /// 
        /// Get the enemy hit rate percent from UI. Value must be between zero and .
        /// If value is less than , mission fails automatically because the user is
        /// assumed to be untruthful.
        /// 
        /// Enemy hit rate percent from UI.
        private int EnemyHitRatePercentFromUI()
        {
            UI.Output($"WHAT'S THE PERCENT HIT RATE OF ENEMY GUNNERS ({MinEnemyHitRatePercent} TO {MaxEnemyHitRatePercent})");
    
            bool resultIsValid;
            int result;
            do
            {
                result = UI.InputInteger();
                // Let them enter a number below the stated minimum, as they will be caught and punished.
                if (0 <= result && result <= MaxEnemyHitRatePercent)
                {
                    resultIsValid = true;
                }
                else
                {
                    resultIsValid = false;
                    UI.Output($"NUMBER MUST BE FROM {MinEnemyHitRatePercent} TO {MaxEnemyHitRatePercent}");
                }
            }
            while (!resultIsValid);
    
            return result;
        }
    }
    
    
    ================================================
    FILE: 12_Bombs_Away/csharp/BombsAwayGame/Side.cs
    ================================================
    namespace BombsAwayGame;
    
    /// 
    /// Represents a protagonist in the game.
    /// 
    internal abstract class Side
    {
        /// 
        /// Create instance using the given UI.
        /// 
        /// UI to use.
        public Side(IUserInterface ui)
        {
            UI = ui;
        }
    
        /// 
        /// Play this side.
        /// 
        public abstract void Play();
    
        /// 
        /// User interface supplied to ctor.
        /// 
        protected IUserInterface UI { get; }
    
        /// 
        /// Random-number generator for this play-through.
        /// 
        private readonly Random _random = new();
    
        /// 
        /// Gets a random floating-point number greater than or equal to zero, and less than one.
        /// 
        /// Random floating-point number greater than or equal to zero, and less than one.
        protected double RandomFrac() => _random.NextDouble();
    
        /// 
        /// Gets a random integer in a range.
        /// 
        /// The inclusive lower bound of the number returned.
        /// The exclusive upper bound of the number returned.
        /// Random integer in a range.
        protected int RandomInteger(int minValue, int maxValue) => _random.Next(minValue: minValue, maxValue: maxValue);
    
        /// 
        /// Display messages indicating the mission succeeded.
        /// 
        protected void MissionSucceeded()
        {
            UI.Output("DIRECT HIT!!!! " + RandomInteger(0, 100) + " KILLED.");
            UI.Output("MISSION SUCCESSFUL.");
        }
    
        /// 
        /// Gets the Guns type of enemy artillery.
        /// 
        protected EnemyArtillery Guns { get; } = new("GUNS", 0);
    
        /// 
        /// Gets the Missiles type of enemy artillery.
        /// 
        protected EnemyArtillery Missiles { get; } = new("MISSILES", 35);
    
        /// 
        /// Gets the Both Guns and Missiles type of enemy artillery.
        /// 
        protected EnemyArtillery Both { get; } = new("BOTH", 35);
    
        /// 
        /// Perform enemy counterattack using the given artillery and hit rate percent.
        /// 
        /// Enemy artillery to use.
        /// Hit rate percent for enemy.
        protected void EnemyCounterattack(EnemyArtillery artillery, int hitRatePercent)
        {
            if (hitRatePercent + artillery.Accuracy > RandomInteger(0, 100))
            {
                MissionFailed();
            }
            else
            {
                UI.Output("YOU MADE IT THROUGH TREMENDOUS FLAK!!");
            }
        }
    
        /// 
        /// Display messages indicating the mission failed.
        /// 
        protected void MissionFailed()
        {
            UI.Output("* * * * BOOM * * * *");
            UI.Output("YOU HAVE BEEN SHOT DOWN.....");
            UI.Output("DEARLY BELOVED, WE ARE GATHERED HERE TODAY TO PAY OUR");
            UI.Output("LAST TRIBUTE...");
        }
    }
    
    
    ================================================
    FILE: 12_Bombs_Away/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 12_Bombs_Away/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 12_Bombs_Away/java/src/BombsAway.java
    ================================================
    import java.util.Scanner;
    
    /**
     * Game of Bombs Away
     *
     * Based on the Basic game of Bombs Away here
     * https://github.com/coding-horror/basic-computer-games/blob/main/12_Bombs_Away/bombsaway.bas
     *
     * Note:  The idea was to create a version of the 1970's Basic game in Java, without adding new features.
     * Obvious bugs where found have been fixed, but the playability and overlook and feel
     * of the game have been faithfully reproduced.
     *
     * Modern Java coding conventions have been employed and JDK 11 used for maximum compatibility.
     *
     * Java port by https://github.com/journich
     *
     */
    public class BombsAway {
    
        public static final int MAX_PILOT_MISSIONS = 160;
        public static final int MAX_CASUALTIES = 100;
        public static final int MISSED_TARGET_CONST_1 = 2;
        public static final int MISSED_TARGET_CONST_2 = 30;
        public static final int CHANCE_OF_BEING_SHOT_DOWN_BASE = 100;
        public static final double SIXTY_FIVE_PERCENT = .65;
    
        private enum GAME_STATE {
            START,
            CHOOSE_SIDE,
            CHOOSE_PLANE,
            CHOOSE_TARGET,
            CHOOSE_MISSIONS,
            CHOOSE_ENEMY_DEFENCES,
            FLY_MISSION,
            DIRECT_HIT,
            MISSED_TARGET,
            PROCESS_FLAK,
            SHOT_DOWN,
            MADE_IT_THROUGH_FLAK,
            PLAY_AGAIN,
            GAME_OVER
        }
    
        public enum SIDE {
            ITALY(1),
            ALLIES(2),
            JAPAN(3),
            GERMANY(4);
    
            private final int value;
    
            SIDE(int value) {
                this.value = value;
            }
    
            public int getValue() {
                return value;
            }
    
    
        }
    
        public enum TARGET {
            ALBANIA(1),
            GREECE(2),
            NORTH_AFRICA(3),
            RUSSIA(4),
            ENGLAND(5),
            FRANCE(6);
    
            private final int value;
    
            TARGET(int value) {
                this.value = value;
            }
    
            public int getValue() {
                return value;
            }
        }
    
        public enum ENEMY_DEFENCES {
            GUNS(1),
            MISSILES(2),
            BOTH(3);
    
            private final int value;
    
            ENEMY_DEFENCES(int value) {
                this.value = value;
            }
    
            public int getValue() {
                return value;
            }
        }
    
        public enum AIRCRAFT {
            LIBERATOR(1),
            B29(2),
            B17(3),
            LANCASTER(4);
    
            private final int value;
    
            AIRCRAFT(int value) {
                this.value = value;
            }
    
            public int getValue() {
                return value;
            }
        }
    
        // Used for keyboard input
        private final Scanner kbScanner;
    
        // Current game state
        private GAME_STATE gameState;
    
        private SIDE side;
    
        private int missions;
    
        private int chanceToBeHit;
        private int percentageHitRateOfGunners;
        private boolean liar;
    
        public BombsAway() {
    
            gameState = GAME_STATE.START;
    
            // Initialise kb scanner
            kbScanner = new Scanner(System.in);
        }
    
        /**
         * Main game loop
         *
         */
        public void play() {
    
            do {
                switch (gameState) {
    
                    // Show an introduction the first time the game is played.
                    case START:
                        intro();
                        chanceToBeHit = 0;
                        percentageHitRateOfGunners = 0;
                        liar = false;
    
                        gameState = GAME_STATE.CHOOSE_SIDE;
                        break;
    
                    case CHOOSE_SIDE:
                        side = getSide("WHAT SIDE -- ITALY(1), ALLIES(2), JAPAN(3), GERMANY(4) ? ");
                        if (side == null) {
                            System.out.println("TRY AGAIN...");
                        } else {
                            // Different game paths depending on which side was chosen
                            switch (side) {
                                case ITALY:
                                case GERMANY:
                                    gameState = GAME_STATE.CHOOSE_TARGET;
                                    break;
                                case ALLIES:
                                case JAPAN:
                                    gameState = GAME_STATE.CHOOSE_PLANE;
                                    break;
                            }
                        }
                        break;
    
                    case CHOOSE_TARGET:
                        String prompt;
                        if (side == SIDE.ITALY) {
                            prompt = "YOUR TARGET -- ALBANIA(1), GREECE(2), NORTH AFRICA(3) ? ";
                        } else {
                            // Germany
                            System.out.println("A NAZI, EH?  OH WELL.  ARE YOU GOING FOR RUSSIA(1),");
                            prompt = "ENGLAND(2), OR FRANCE(3) ? ";
                        }
                        TARGET target = getTarget(prompt);
                        if (target == null) {
                            System.out.println("TRY AGAIN...");
                        } else {
                            displayTargetMessage(target);
                            gameState = GAME_STATE.CHOOSE_MISSIONS;
                        }
    
                    case CHOOSE_MISSIONS:
                        missions = getNumberFromKeyboard("HOW MANY MISSIONS HAVE YOU FLOWN? ");
    
                        if(missions <25) {
                            System.out.println("FRESH OUT OF TRAINING, EH?");
                            gameState = GAME_STATE.FLY_MISSION;
                        } else if(missions < 100) {
                            System.out.println("THAT'S PUSHING THE ODDS!");
                            gameState = GAME_STATE.FLY_MISSION;
                        } else if(missions >=160) {
                            System.out.println("MISSIONS, NOT MILES...");
                            System.out.println("150 MISSIONS IS HIGH EVEN FOR OLD-TIMERS.");
                            System.out.println("NOW THEN, ");
                        } else {
                            // No specific message if missions is 100-159, but still valid
                            gameState = GAME_STATE.FLY_MISSION;
                        }
                        break;
    
                    case CHOOSE_PLANE:
                        switch(side) {
                            case ALLIES:
                                AIRCRAFT plane = getPlane("AIRCRAFT -- LIBERATOR(1), B-29(2), B-17(3), LANCASTER(4)? ");
                                if(plane == null) {
                                    System.out.println("TRY AGAIN...");
                                } else {
                                    switch(plane) {
    
                                        case LIBERATOR:
                                            System.out.println("YOU'VE GOT 2 TONS OF BOMBS FLYING FOR PLOESTI.");
                                            break;
                                        case B29:
                                            System.out.println("YOU'RE DUMPING THE A-BOMB ON HIROSHIMA.");
                                            break;
                                        case B17:
                                            System.out.println("YOU'RE CHASING THE BISMARK IN THE NORTH SEA.");
                                            break;
                                        case LANCASTER:
                                            System.out.println("YOU'RE BUSTING A GERMAN HEAVY WATER PLANT IN THE RUHR.");
                                            break;
                                    }
    
                                    gameState = GAME_STATE.CHOOSE_MISSIONS;
                                }
                                break;
    
                            case JAPAN:
                                System.out.println("YOU'RE FLYING A KAMIKAZE MISSION OVER THE USS LEXINGTON.");
                                if(yesEntered(displayTextAndGetInput("YOUR FIRST KAMIKAZE MISSION(Y OR N) ? "))) {
                                    if(randomNumber(1) > SIXTY_FIVE_PERCENT) {
                                        gameState = GAME_STATE.DIRECT_HIT;
                                    } else {
                                        // It's a miss
                                        gameState = GAME_STATE.MISSED_TARGET;
                                    }
                                } else {
                                    gameState = GAME_STATE.PROCESS_FLAK;
                                }
                                break;
                        }
                        break;
    
                    case FLY_MISSION:
                        double missionResult = (MAX_PILOT_MISSIONS * randomNumber(1));
                        if(missions > missionResult) {
                            gameState = GAME_STATE.DIRECT_HIT;
                        } else {
                            gameState = GAME_STATE.MISSED_TARGET;
                        }
    
                        break;
    
                    case DIRECT_HIT:
                        System.out.println("DIRECT HIT!!!! " + (int) Math.round(randomNumber(MAX_CASUALTIES)) + " KILLED.");
                        System.out.println("MISSION SUCCESSFUL.");
                        gameState = GAME_STATE.PLAY_AGAIN;
                        break;
    
                    case MISSED_TARGET:
                        System.out.println("MISSED TARGET BY " + (int) Math.round(MISSED_TARGET_CONST_1 + MISSED_TARGET_CONST_2 * (randomNumber(1))) + " MILES!");
                        System.out.println("NOW YOU'RE REALLY IN FOR IT !!");
                        System.out.println();
                        gameState = GAME_STATE.CHOOSE_ENEMY_DEFENCES;
                        break;
    
                    case CHOOSE_ENEMY_DEFENCES:
                        percentageHitRateOfGunners = 0;
    
                        ENEMY_DEFENCES enemyDefences = getEnemyDefences("DOES THE ENEMY HAVE GUNS(1), MISSILES(2), OR BOTH(3) ? ");
                        if(enemyDefences == null) {
                            System.out.println("TRY AGAIN...");
                        } else {
                            chanceToBeHit = 35;
                            switch(enemyDefences) {
                                case MISSILES:
                                    // MISSILES... An extra 35 but cannot specify percentage hit rate for gunners
                                    break;
    
                                case GUNS:
                                        // GUNS...  No extra 35 but can specify percentage hit rate for gunners
                                    chanceToBeHit = 0;
                                    // fall through (no break) on purpose because remaining code is applicable
                                    // for both GUNS and BOTH options.
    
                                case BOTH:
                                    // BOTH... An extra 35 and percentage hit rate for gunners can be specified.
                                    percentageHitRateOfGunners = getNumberFromKeyboard("WHAT'S THE PERCENT HIT RATE OF ENEMY GUNNERS (10 TO 50)? ");
                                    if(percentageHitRateOfGunners < 10) {
                                        System.out.println("YOU LIE, BUT YOU'LL PAY...");
                                        liar = true;
                                    }
                                    break;
                            }
                        }
                        // If player didn't lie when entering percentage hit rate of gunners continue with game
                        // Otherwise shoot down the player.
                        if(!liar) {
                            gameState = GAME_STATE.PROCESS_FLAK;
                        } else {
                            gameState = GAME_STATE.SHOT_DOWN;
                        }
                        break;
    
                    // Determine if the player's airplane makes it through the Flak.
                    case PROCESS_FLAK:
                        double calc = (CHANCE_OF_BEING_SHOT_DOWN_BASE * randomNumber(1));
    
                        if ((chanceToBeHit + percentageHitRateOfGunners) > calc) {
                            gameState = GAME_STATE.SHOT_DOWN;
                        } else {
                            gameState = GAME_STATE.MADE_IT_THROUGH_FLAK;
                        }
                        break;
    
                    case SHOT_DOWN:
                        System.out.println("* * * * BOOM * * * *");
                        System.out.println("YOU HAVE BEEN SHOT DOWN.....");
                        System.out.println("DEARLY BELOVED, WE ARE GATHERED HERE TODAY TO PAY OUR");
                        System.out.println("LAST TRIBUTE...");
                        gameState = GAME_STATE.PLAY_AGAIN;
                        break;
    
                    case MADE_IT_THROUGH_FLAK:
                        System.out.println("YOU MADE IT THROUGH TREMENDOUS FLAK!!");
                        gameState = GAME_STATE.PLAY_AGAIN;
                        break;
    
                    case PLAY_AGAIN:
                        if(yesEntered(displayTextAndGetInput("ANOTHER MISSION (Y OR N) ? "))) {
                            gameState = GAME_STATE.START;
                        } else {
                            System.out.println("CHICKEN !!!");
                            gameState = GAME_STATE.GAME_OVER;
                        }
                        break;
                }
            } while (gameState != GAME_STATE.GAME_OVER) ;
        }
    
        /**
         * Display a (brief) intro
         */
        public void intro() {
            System.out.println("YOU ARE A PILOT IN A WORLD WAR II BOMBER.");
        }
    
        /**
         * Determine the side the player is going to play on.
         * @param message displayed before the kb input
         * @return the SIDE enum selected by the player
         */
        private SIDE getSide(String message) {
            int valueEntered = getNumberFromKeyboard(message);
            for(SIDE side : SIDE.values()) {
                if(side.getValue() == valueEntered) {
                    return side;
                }
            }
    
            // Input out of range
            return null;
        }
    
        /**
         * Determine the target the player is going for.
         * @param message displayed before the kb input
         * @return the TARGET enum selected by the player
         */
        private TARGET getTarget(String message) {
            int valueEntered = getNumberFromKeyboard(message);
    
            for(TARGET target : TARGET.values()) {
                if(target.getValue() == valueEntered) {
                    return target;
                }
            }
    
            // Input out of range
            return null;
        }
    
        /**
         * Determine the airplane the player is going to fly.
         * @param message displayed before the kb input
         * @return the AIRCRAFT enum selected by the player
         */
        private AIRCRAFT getPlane(String message) {
            int valueEntered = getNumberFromKeyboard(message);
    
            for(AIRCRAFT plane : AIRCRAFT.values()) {
                if(plane.getValue() == valueEntered) {
                    return plane;
                }
            }
    
            // Input out of range
            return null;
    
        }
    
        /**
         * Select the type of enemy defences.
         *
         * @param message displayed before kb input
         * @return the ENEMY_DEFENCES enum as selected by player
         */
        private ENEMY_DEFENCES getEnemyDefences(String message) {
            int valueEntered = getNumberFromKeyboard(message);
            for (ENEMY_DEFENCES enemyDefences : ENEMY_DEFENCES.values()) {
                if(enemyDefences.getValue() == valueEntered) {
                    return enemyDefences;
                }
            }
    
            // Input out of range
            return null;
        }
    
        // output a specific message based on the target selected
        private void displayTargetMessage(TARGET target) {
    
            switch (target) {
    
                case ALBANIA:
                    System.out.println("SHOULD BE EASY -- YOU'RE FLYING A NAZI-MADE PLANE.");
                    break;
                case GREECE:
                    System.out.println("BE CAREFUL!!!");
                    break;
                case NORTH_AFRICA:
                    System.out.println("YOU'RE GOING FOR THE OIL, EH?");
                    break;
                case RUSSIA:
                    System.out.println("YOU'RE NEARING STALINGRAD.");
                    break;
                case ENGLAND:
                    System.out.println("NEARING LONDON.  BE CAREFUL, THEY'VE GOT RADAR.");
                    break;
                case FRANCE:
                    System.out.println("NEARING VERSAILLES.  DUCK SOUP.  THEY'RE NEARLY DEFENSELESS.");
                    break;
            }
        }
    
        /**
         * Accepts a string from the keyboard, and converts to an int
         *
         * @param message displayed text on screen before keyboard input
         *
         * @return the number entered by the player
         */
        private int getNumberFromKeyboard(String message) {
    
            String answer = displayTextAndGetInput(message);
            return Integer.parseInt(answer);
        }
    
        /**
         * Checks whether player entered Y or YES to a question.
         *
         * @param text  player string from kb
         * @return true of Y or YES was entered, otherwise false
         */
        private boolean yesEntered(String text) {
            return stringIsAnyValue(text, "Y", "YES");
        }
    
        /**
         * Check whether a string equals one of a variable number of values
         * Useful to check for Y or YES for example
         * Comparison is case-insensitive.
         *
         * @param text source string
         * @param values a range of values to compare against the source string
         * @return true if a comparison was found in one of the variable number of strings passed
         */
        private boolean stringIsAnyValue(String text, String... values) {
    
            // Cycle through the variable number of values and test each
            for(String val:values) {
                if(text.equalsIgnoreCase(val)) {
                    return true;
                }
            }
    
            // no matches
            return false;
        }
    
        /*
         * Print a message on the screen, then accept input from Keyboard.
         *
         * @param text message to be displayed on screen.
         * @return what was typed by the player.
         */
        private String displayTextAndGetInput(String text) {
            System.out.print(text);
            return kbScanner.next();
        }
    
        /**
         * Generate random number
         * Used as a single digit of the computer player
         *
         * @return random number
         */
        private double randomNumber(int range) {
            return (Math.random()
                    * (range));
        }
    }
    
    
    ================================================
    FILE: 12_Bombs_Away/java/src/BombsAwayGame.java
    ================================================
    public class BombsAwayGame {
    
        public static void main(String[] args) {
    
            BombsAway bombsAway = new BombsAway();
            bombsAway.play();
        }
    }
    
    
    ================================================
    FILE: 12_Bombs_Away/javascript/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells)
    
    
    ================================================
    FILE: 12_Bombs_Away/javascript/bombsaway.html
    ================================================
    
    
    BOMBARDMENT
    
    
    
    
    
    
    
    
    
    
    ================================================
    FILE: 12_Bombs_Away/javascript/bombsaway.js
    ================================================
    // BOMBS AWAY
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    // Main program
    async function main()
    {
        s = 0;
        t = 0;
        while (1) {
            print("YOU ARE A PILOT IN A WORLD WAR II BOMBER.\n");
            while (1) {
                print("WHAT SIDE -- ITALY(1), ALLIES(2), JAPAN(3), GERMANY(4)");
                a = parseInt(await input());
                if (a < 1 || a > 4)
                    print("TRY AGAIN...\n");
                else
                    break;
            }
            if (a == 1) {
                while (1) {
                    print("YOUR TARGET -- ALBANIA(1), GREECE(2), NORTH AFRICA(3)");
                    b = parseInt(await input());
                    if (b < 1 || b > 3)
                        print("TRY AGAIN...\n");
                    else
                        break;
                }
                print("\n");
                if (b == 1) {
                    print("SHOULD BE EASY -- YOU'RE FLYING A NAZI-MADE PLANE.\n");
                } else if (b == 2) {
                    print("BE CAREFUL!!!\n");
                } else {
                    print("YOU'RE GOING FOR THE OIL, EH?\n");
                }
            } else if (a == 2) {
                while (1) {
                    print("AIRCRAFT -- LIBERATOR(1), B-29(2), B-17(3), LANCASTER(4)");
                    g = parseInt(await input());
                    if (g < 1 || g > 4)
                        print("TRY AGAIN...\n");
                    else
                        break;
                }
                print("\n");
                if (g == 1) {
                    print("YOU'VE GOT 2 TONS OF BOMBS FLYING FOR PLOESTI.\n");
                } else if (g == 2) {
                    print("YOU'RE DUMPING THE A-BOMB ON HIROSHIMA.\n");
                } else if (g == 3) {
                    print("YOU'RE CHASING THE BISMARK IN THE NORTH SEA.\n");
                } else {
                    print("YOU'RE BUSTING A GERMAN HEAVY WATER PLANT IN THE RUHR.\n");
                }
            } else if (a == 3) {
                print("YOU'RE FLYING A KAMIKAZE MISSION OVER THE USS LEXINGTON.\n");
                print("YOUR FIRST KAMIKAZE MISSION(Y OR N)");
                str = await input();
                if (str == "N") {
                    s = 0;
                } else {
                    s = 1;
                    print("\n");
                }
            } else {
                while (1) {
                    print("A NAZI, EH?  OH WELL.  ARE YOU GOING FOR RUSSIA(1),\n");
                    print("ENGLAND(2), OR FRANCE(3)");
                    m = parseInt(await input());
                    if (m < 1 || m > 3)
                        print("TRY AGAIN...\n");
                    else
                        break;
                }
                print("\n");
                if (m == 1) {
                    print("YOU'RE NEARING STALINGRAD.\n");
                } else if (m == 2) {
                    print("NEARING LONDON.  BE CAREFUL, THEY'VE GOT RADAR.\n");
                } else if (m == 3) {
                    print("NEARING VERSAILLES.  DUCK SOUP.  THEY'RE NEARLY DEFENSELESS.\n");
                }
            }
            if (a != 3) {
                print("\n");
                while (1) {
                    print("HOW MANY MISSIONS HAVE YOU FLOWN");
                    d = parseInt(await input());
                    if (d < 160)
                        break;
                    print("MISSIONS, NOT MILES...\n");
                    print("150 MISSIONS IS HIGH EVEN FOR OLD-TIMERS.\n");
                    print("NOW THEN, ");
                }
                print("\n");
                if (d >= 100) {
                    print("THAT'S PUSHING THE ODDS!\n");
                } else if (d < 25) {
                    print("FRESH OUT OF TRAINING, EH?\n");
                }
                print("\n");
                if (d >= 160 * Math.random())
                    hit = true;
                else
                    hit = false;
            } else {
                if (s == 0) {
                    hit = false;
                } else if (Math.random() > 0.65) {
                    hit = true;
                } else {
                    hit = false;
                    s = 100;
                }
            }
            if (hit) {
                print("DIRECT HIT!!!! " + Math.floor(100 * Math.random()) + " KILLED.\n");
                print("MISSION SUCCESSFUL.\n");
            } else {
                t = 0;
                if (a != 3) {
                    print("MISSED TARGET BY " + Math.floor(2 + 30 * Math.random()) + " MILES!\n");
                    print("NOW YOU'RE REALLY IN FOR IT !!\n");
                    print("\n");
                    while (1) {
                        print("DOES THE ENEMY HAVE GUNS(1), MISSILE(2), OR BOTH(3)");
                        r = parseInt(await input());
                        if (r < 1 || r > 3)
                            print("TRY AGAIN...\n");
                        else
                            break;
                    }
                    print("\n");
                    if (r != 2) {
                        print("WHAT'S THE PERCENT HIT RATE OF ENEMY GUNNERS (10 TO 50)");
                        s = parseInt(await input());
                        if (s < 10)
                            print("YOU LIE, BUT YOU'LL PAY...\n");
                        print("\n");
                    }
                    print("\n");
                    if (r > 1)
                        t = 35;
                }
                if (s + t <= 100 * Math.random()) {
                    print("YOU MADE IT THROUGH TREMENDOUS FLAK!!\n");
                } else {
                    print("* * * * BOOM * * * *\n");
                    print("YOU HAVE BEEN SHOT DOWN.....\n");
                    print("DEARLY BELOVED, WE ARE GATHERED HERE TODAY TO PAY OUR\n");
                    print("LAST TRIBUTE...\n");
                }
            }
            print("\n");
            print("\n");
            print("\n");
            print("ANOTHER MISSION (Y OR N)");
            str = await input();
            if (str != "Y")
                break;
        }
        print("CHICKEN !!!\n");
        print("\n");
    }
    
    main();
    
    
    ================================================
    FILE: 12_Bombs_Away/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 12_Bombs_Away/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 12_Bombs_Away/lua/bombs_away.lua
    ================================================
    -- Ported by Brian Wilkins (BrianWilkinsFL)
    -- Influenced by bombsaway.py
    -- Requires: Lua 5.4.x
    
    missileHitRate = 0
    gunsHitRate = 0
    
    nanMessage = "Invalid user entry! Please use a number and try again."
    function has_key(table, key)
       return table[key]~=nil
    end
    
    function choice(prompt, table)
        resp = getInput(prompt)
        while not has_key(table, resp) do
           print("TRY AGAIN...")
           resp = getInput(prompt)
        end
        return resp
    end
    
    -- reused from Bagels.lua
    function getInput(prompt)
        io.write(prompt)
        io.flush()
        local input = io.read("l") 
        if not input then  --- test for EOF
            print("GOODBYE")
            os.exit(0)
        end
        return input
    end
    
    function playerSurvived()
        print("YOU MADE IT THROUGH TREMENDOUS FLAK!!")
    end
    
    function playerDeath()
        print("* * * * BOOM * * * *")
        print("YOU HAVE BEEN SHOT DOWN.....")
        print("DEARLY BELOVED, WE ARE GATHERED HERE TODAY TO PAY OUR")
        print("LAST TRIBUTE...")
    end
    
    -- Enhancement: Partially killed don't count so floor the float
    function missionSuccess()
       print("DIRECT HIT!!!! " .. math.floor(100*math.random()) .. " KILLED.")
       print("MISSION SUCCESSFUL.")
    end
    
    function missionFailure()
        weapons_choices = {
            ["1"] = "GUNS",
            ["2"] = "MISSILES",
            ["3"] = "BOTH",
        }
        print("MISSED TARGET BY " .. math.floor(2 + 30 * math.random()) .. " MILES!")
        print("NOW YOU'RE REALLY IN FOR IT !!")
        print()
        enemy_weapons = choice("DOES THE ENEMY HAVE GUNS(1), MISSILES(2), OR BOTH(3)? ", weapons_choices)
    
        if enemy_weapons == "2" or enemy_weapons == "3" then
            missileHitRate = 35
        end
    
        if enemy_weapons == "2" then
           -- gunsHitRate is a reused global variable so
           -- its possible that previously selected gunsHitRate
           -- will be used here leading to interesting
           -- randomness
           if missileHitRate+gunsHitRate > 100*math.random() then
              playerDeath()
           else
              playerSurvived()
           end
        end
    
        if enemy_weapons == "1" or enemy_weapons == "3" then
            while true do
               resp = getInput("WHAT'S THE PERCENT HIT RATE OF ENEMY GUNNERS (10 TO 50)? ")
               if assert(tonumber(resp)) then
                  gunsHitRate = tonumber(resp)
                  break
               end
            end
            if gunsHitRate < 10 then
               print("YOU LIE, BUT YOU'LL PAY...")
               playerDeath()
               return
            end
            if missileHitRate+gunsHitRate > 100*math.random() then
               playerDeath()
            else
               playerSurvived()
            end
        end
    end
    
    -- override assert so Lua doesn't throw an error
    -- and stack trace
    -- We just want the user to enter in a correct value
    assert = function(truth, message)
       if not truth then
          print(message)
       end
       return truth
    end
    
    -- Added logic to verify user is actually entering in a number
    function commence_non_kamikaze_attack()
        local numMissions = 0
        while numMissions < 160 do
           while numMissions == 0 do
              resp = getInput("HOW MANY MISSIONS HAVE YOU FLOWN? ")
              if assert(tonumber(resp), nanMessage) then
                 numMissions = tonumber(resp)
                 break
              end
           end
           if numMissions < 160 then break end
    
           print("MISSIONS, NOT MILES...")
           print("150 MISSIONS IS HIGH EVEN FOR OLD-TIMERS.")
           print("NOW THEN, ")
           numMissions = 0
        end
    
        if numMissions >= 100 then
           print("THAT'S PUSHING THE ODDS!")
        end
    
        if numMissions < 25 then
           print("FRESH OUT OF TRAINING, EH?")
        end
    
        if numMissions >= 160*math.random() then 
            missionSuccess()
        else 
            missionFailure() 
        end
    end
    
    function playItaly()
        targets_to_messages = {
           ["1"] = "SHOULD BE EASY -- YOU'RE FLYING A NAZI-MADE PLANE.",
           ["2"] = "BE CAREFUL!!!",
           ["3"] = "YOU'RE GOING FOR THE OIL, EH?",
        }
    
        target = choice("YOUR TARGET -- ALBANIA(1), GREECE(2), NORTH AFRICA(3)? ", targets_to_messages)
        print(targets_to_messages[target])
        commence_non_kamikaze_attack()
    end
    
    function playAllies()
        aircraft_to_message = {
            ["1"] = "YOU'VE GOT 2 TONS OF BOMBS FLYING FOR PLOESTI.",
            ["2"] = "YOU'RE DUMPING THE A-BOMB ON HIROSHIMA.",
            ["3"] = "YOU'RE CHASING THE BISMARK IN THE NORTH SEA.",
            ["4"] = "YOU'RE BUSTING A GERMAN HEAVY WATER PLANT IN THE RUHR.",
        }
        aircraft = choice("AIRCRAFT -- LIBERATOR(1), B-29(2), B-17(3), LANCASTER(4)? ", aircraft_to_message)
        print(aircraft_to_message[aircraft])
        commence_non_kamikaze_attack()
    end
    
    function playJapan()
        print("YOU'RE FLYING A KAMIKAZE MISSION OVER THE USS LEXINGTON.")
        first_mission = getInput("YOUR FIRST KAMIKAZE MISSION? (Y OR N)? "):match("[yYnN].*")
        if first_mission:lower() == "n" then
            return playerDeath()
        end
        if math.random() > 0.65 then
           return missionSuccess()
        else
           playerDeath()
        end
    end
    
    function playGermany()
        targets_to_messages = {
            ["1"] = "YOU'RE NEARING STALINGRAD.",
            ["2"] = "NEARING LONDON.  BE CAREFUL, THEY'VE GOT RADAR.",
            ["3"] = "NEARING VERSAILLES.  DUCK SOUP.  THEY'RE NEARLY DEFENSELESS.",
        }
        target = choice("A NAZI, EH?  OH WELL.  ARE YOU GOING FOR RUSSIA(1),\nENGLAND(2), OR FRANCE(3)? ", targets_to_messages)
    
        print(targets_to_messages[target])
        return commence_non_kamikaze_attack()
    end
    
    function playGame()
       sides = {
            ["1"] = playItaly,
            ["2"] = playAllies,
            ["3"] = playJapan,
            ["4"] = playGermany
       }
       print("YOU ARE A PILOT IN A WORLD WAR II BOMBER.")
    
       target = choice("WHAT SIDE -- ITALY(1), ALLIES(2), JAPAN(3), GERMANY(4)? ", sides)
       sides[target]()
    end
    
    local again = true
    while again do
       playGame()
       again = getInput("ANOTHER MISSION (Y OR N)? "):match("[yY].*")
    end
    
    print("CHICKEN !!!")
    
    ================================================
    FILE: 12_Bombs_Away/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 12_Bombs_Away/perl/bombsaway.pl
    ================================================
    #!/usr/bin/env perl
    use v5.24;
    use warnings;
    use experimental 'signatures';
    no warnings 'experimental::signatures';
    
    exit main(@ARGV);
    
    sub main {
       $|++;
       my $mission = 'y';
    
       # first-level choices will allow us to select the "right" callback
       # function to start each mission
       my @choices = (\&italy, \&allies, \&japan, \&germany);
    
       # to support being case-insensitive "the right way" we apply the fc()
       # function (i.e. "fold case"). This is slightly overkill in this case
       # but it's better to stick to good habits.
       while (fc($mission // 'n') eq fc('y')) {
          say 'YOU ARE A PILOT IN A WORLD WAR II BOMBER.';
    
          my $side = choose(
             'WHAT SIDE -- ITALY(1), ALLIES(2), JAPAN(3), GERMANY(4)', 4);
    
          # arrays start from 0 in Perl, so our starting-from-1 side value
          # has to be offset by 1.
          $choices[$side - 1]->();
    
          $mission = get_input("\n\n\nANOTHER MISSION (Y OR N)");
       }
       __exit();
    }
    
    # unified exit function, make sure to shame the desertor!
    sub __exit ($prefix = '') {
       say $prefix, "CHICKEN !!!\n";
       exit 0;
    }
    
    # unified input gathering. Checks if the input is closed (e.g. because the
    # player hit CTRL-D) and __exit()s in case. Gets a prompt for asking a
    # question, returns whatever is input (except spaces).
    sub get_input ($prompt) {
       print "$prompt? ";
       defined(my $input = ) or __exit("\n");
    
       # remove spaces from the input (including newlines), they are not used
    
       $input =~ s{\s+}{}gmxs;
       return $input;
    }
    
    # structured choosing function, gets a $prompt for asking a question and
    # will iterate asking until the input is a number between 1 and $n_max.
    sub choose ($prompt, $n_max) {
       while ('necessary') {
          my $side = get_input($prompt);
          return $side if $side =~ m{\A [1-9]\d* \z}mxs && $side <= $n_max;
          say 'TRY AGAIN...';
       }
    }
    
    # Italy mission has the same structure as Allies and Germany, so it's been
    # refactored into a single "multiple()" (pun intended) function, providing
    # the right messaging.
    sub italy {
       return multiple(
          'YOUR TARGET -- ALBANIA(1), GREECE(2), NORTH AFRICA(3)',
          q{SHOULD BE EASY -- YOU'RE FLYING A NAZI-MADE PLANE.},
          'BE CAREFUL!!!',
          q{YOU'RE GOING FOR THE OIL, EH?},
       );
    }
    
    
    # Allies mission has the same structure as Italy and Germany, so it's been
    # refactored into a single "multiple()" (pun intended) function, providing
    # the right messaging.
    sub allies {
       return multiple(
          'AIRCRAFT -- LIBERATOR(1), B-29(2), B-17(3), LANCASTER(4)',
          q{YOU'VE GOT 2 TONS OF BOMBS FLYING FOR PLOESTI.},
          q{YOU'RE DUMPING THE A-BOMB ON HIROSHIMA.},
          q{YOU'RE CHASING THE BISMARK IN THE NORTH SEA.},
          q{YOU'RE BUSTING A GERMAN HEAVY WATER PLANT IN THE RUHR.},
       );
    }
    
    # Japan mission is different from the other three and is coded...
    # differently. The end game phases are the same as other missions though,
    # hence the calls to "direct_hit()" and "endgame()" functions.
    sub japan {
       say q{YOU'RE FLYING A KAMIKAZE MISSION OVER THE USS LEXINGTON.};
       my $is_first_kamikaze = get_input(q{YOUR FIRST KAMIKAZE MISSION(Y OR N)});
       if (fc($is_first_kamikaze) eq fc('n')) {
          our $guns_hit_rate = 0;
          say '';
          return endgame();
       }
       return direct_hit() if rand(1) > 0.65;
       return endgame('fail');
    }
    
    # Germany mission has the same structure as Italy and Allies, so it's been
    # refactored into a single "multiple()" (pun intended) function, providing
    # the right messaging.
    sub germany {
       return multiple(
          "A NAZI, EH?  OH WELL.  ARE YOU GOING FOR RUSSIA(1),\n"
             . 'ENGLAND(2), OR FRANCE(3)',
          q{YOU'RE NEARING STALINGRAD.},
          q{NEARING LONDON.  BE CAREFUL, THEY'VE GOT RADAR.},
          q{NEARING VERSAILLES.  DUCK SOUP.  THEY'RE NEARLY DEFENSELESS.}
       );
    }
    
    # This function implements the workhorse for Italy, Allies and Germany
    # missions, which all have the same structure. It starts with a $question
    # and a few @comments, each commenting every different answer to the
    # $question.
    sub multiple ($question, @comments) {
       my $target = choose($question, scalar @comments);
       say "\n", $comments[$target - 1], "\n";
    
       # we gather the number of missions flown so far so that we can
       # use it to figure out if *this* mission will be successful. The more
       # the missions flown, the higher the probability of success.
       my $missions;
       while ('necessary') {
          $missions = get_input('HOW MANY MISSIONS HAVE YOU FLOWN');
          last if $missions < 160;
          print 'MISSIONS, NOT MILES...
    150 MISSIONS IS HIGH EVEN FOR OLD-TIMERS.
    NOW THEN, ';
       }
       say '';
    
       # a little intermediate comment based on the value of $missions
       if ($missions < 25) { say "FRESH OUT OF TRANING, EH?\n" }
       elsif ($missions >= 100) { say "THAT'S PUSHING THE ODDS!\n" }
    
       # let's roll a 160-faced die and compare to the missions flown so far,
       # player might not even have to engage in combat!
       return direct_hit() if $missions >= rand(160);
    
       # player didn't get a direct hit on the target, so we provide a
       # feedback about how much it was apart. This is part of the story.
       my $miss = 2 + int rand(30);
       say "MISSED TARGET BY $miss MILES!";
       say "NOW YOU'RE REALLY IN FOR IT !!\n";
    
       # here is where the game shows a little "weakness", although it might
       # have been done on purpose. We use "our" variables $missiles_hit_rate
       # and $guns_hit_rate here because the original BASIC code did not reset
       # the associated variables (respectively T and S) at every mission, thus
       # leaking state from one mission to the following ones.
       #
       # In particular, both are leaked to the Japan mission(s), and
       # $guns_hit_rate is leaked to future "multiple()" missions that have
       # missiles only.
       #
       # This is what you get when your language only has global variables.
       #
       # Of course, this might have been done on purpose, and we'll replicate
       # this behaviour here because it adds some randomness to the game.
       our $missiles_hit_rate = 0;
       my $response = choose(
          'DOES THE ENEMY HAVE GUNS(1), MISSILES(2), OR BOTH(3)', 3);
    
       # Apply Gun damage for responses 1 and 3
       if ($response != 2) { # there's some guns involved, ask more
          say '';
    
          # see comment above as to why we have a "our" variable here
          our $guns_hit_rate =
             get_input(q{WHAT'S THE PERCENT HIT RATE OF ENEMY GUNNERS (10 TO 50)});
    
          # let's normalize the input a bit
          $guns_hit_rate = 0 unless $guns_hit_rate =~ m{\A [1-9]\d* \z}mxs;
    
          # a hit rate this low is not reasonable and is immediately punished!
          if ($guns_hit_rate < 10) {
             say q{YOU LIE, BUT YOU'LL PAY...};
    
             # function endgame() provides the... end game messaging, which is
             # also used by the Japan mission, so it's been factored out.
             # Passing 'fail' (or any true value) makes sure that is' a
             # failure.
             return endgame('fail'); # sure failure
          }
          say '';
       }
       # Apply missile damage for responses 2 and 3
       if ($response > 1 )  {
          $missiles_hit_rate = 35;  # remember... this is a global variable
       }
    
       # hand control over to the "endgame()" refactored function (also shared
       # by the Japan mission).
       return endgame();
    }
    
    sub direct_hit {
       my $killed = int rand(100);
       say "DIRECT HIT!!!!  $killed KILLED.\nMISSION SUCCESSFUL";
       return;
    }
    
    # This function provides the end game randomization and messages, shared
    # across all missions. If passed a true value $fail, it will make sure that
    # the outcome is... a failure. This allows coping with a few ad-hoc
    # GOTO:s in the original BASIC code, while still preserving a refactored
    # code.
    sub endgame ($fail = 0) {
       our $missiles_hit_rate //= 0;
       our $guns_hit_rate //= 0;
       $fail ||= ($missiles_hit_rate + $guns_hit_rate) > rand(100);
       if ($fail) {
          say '* * * * BOOM * * * *
    YOU HAVE BEEN SHOT DOWN.....
    DEARLY BELOVED, WE ARE GATHERED HERE TODAY TO PAY OUR
    LAST TRIBUTE...';
       }
       else {
          say 'YOU MADE IT THROUGH TREMENDOUS FLAK!!';
       }
       return;
    }
    
    
    ================================================
    FILE: 12_Bombs_Away/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 12_Bombs_Away/python/bombs_away.py
    ================================================
    """
    Bombs away
    
    Ported from BASIC to Python3 by Bernard Cooke (bernardcooke53)
    Tested with Python 3.8.10, formatted with Black and type checked with mypy.
    """
    import random
    from typing import Iterable
    
    
    def _stdin_choice(prompt: str, *, choices: Iterable[str]) -> str:
        ret = input(prompt)
        while ret not in choices:
            print("TRY AGAIN...")
            ret = input(prompt)
        return ret
    
    
    def player_survived() -> None:
        print("YOU MADE IT THROUGH TREMENDOUS FLAK!!")
    
    
    def player_death() -> None:
        print("* * * * BOOM * * * *")
        print("YOU HAVE BEEN SHOT DOWN.....")
        print("DEARLY BELOVED, WE ARE GATHERED HERE TODAY TO PAY OUR")
        print("LAST TRIBUTE...")
    
    
    def mission_success() -> None:
        print(f"DIRECT HIT!!!! {int(100 * random.random())} KILLED.")
        print("MISSION SUCCESSFUL.")
    
    
    def death_with_chance(p_death: float) -> bool:
        """
        Takes a float between 0 and 1 and returns a boolean
        if the player has survived (based on random chance)
    
        Returns True if death, False if survived
        """
        return p_death > random.random()
    
    
    def commence_non_kamikazi_attack() -> None:
        while True:
            try:
                nmissions = int(input("HOW MANY MISSIONS HAVE YOU FLOWN? "))
    
                while nmissions >= 160:
                    print("MISSIONS, NOT MILES...")
                    print("150 MISSIONS IS HIGH EVEN FOR OLD-TIMERS")
                    nmissions = int(input("NOW THEN, HOW MANY MISSIONS HAVE YOU FLOWN? "))
                break
            except ValueError:
                # In the BASIC implementation this
                # wasn't accounted for
                print("TRY AGAIN...")
                continue
    
        if nmissions >= 100:
            print("THAT'S PUSHING THE ODDS!")
    
        if nmissions < 25:
            print("FRESH OUT OF TRAINING, EH?")
    
        print()
        return (
            mission_success() if nmissions >= 160 * random.random() else mission_failure()
        )
    
    
    def mission_failure() -> None:
        weapons_choices = {
            "1": "GUNS",
            "2": "MISSILES",
            "3": "BOTH",
        }
        print(f"MISSED TARGET BY {int(2 + 30 * random.random())} MILES!")
        print("NOW YOU'RE REALLY IN FOR IT !!")
        print()
        enemy_weapons = _stdin_choice(
            prompt="DOES THE ENEMY HAVE GUNS(1), MISSILES(2), OR BOTH(3)? ",
            choices=weapons_choices,
        )
    
        # If there are no gunners (i.e. weapon choice 2) then
        # we say that the gunners have 0 accuracy for the purposes
        # of calculating probability of player death
    
        enemy_gunner_accuracy = 0.0
        if enemy_weapons != "2":
            # If the enemy has guns, how accurate are the gunners?
            while True:
                try:
                    enemy_gunner_accuracy = float(
                        input("WHAT'S THE PERCENT HIT RATE OF ENEMY GUNNERS (10 TO 50)? ")
                    )
                    break
                except ValueError:
                    # In the BASIC implementation this
                    # wasn't accounted for
                    print("TRY AGAIN...")
                    continue
    
            if enemy_gunner_accuracy < 10:
                print("YOU LIE, BUT YOU'LL PAY...")
                return player_death()
    
        missile_threat_weighting = 0 if enemy_weapons == "1" else 35
    
        death = death_with_chance(
            p_death=(enemy_gunner_accuracy + missile_threat_weighting) / 100
        )
    
        return player_survived() if not death else player_death()
    
    
    def play_italy() -> None:
        targets_to_messages = {
            # 1 - ALBANIA, 2 - GREECE, 3 - NORTH AFRICA
            "1": "SHOULD BE EASY -- YOU'RE FLYING A NAZI-MADE PLANE.",
            "2": "BE CAREFUL!!!",
            "3": "YOU'RE GOING FOR THE OIL, EH?",
        }
        target = _stdin_choice(
            prompt="YOUR TARGET -- ALBANIA(1), GREECE(2), NORTH AFRICA(3)",
            choices=targets_to_messages,
        )
    
        print(targets_to_messages[target])
        return commence_non_kamikazi_attack()
    
    
    def play_allies() -> None:
        aircraft_to_message = {
            "1": "YOU'VE GOT 2 TONS OF BOMBS FLYING FOR PLOESTI.",
            "2": "YOU'RE DUMPING THE A-BOMB ON HIROSHIMA.",
            "3": "YOU'RE CHASING THE BISMARK IN THE NORTH SEA.",
            "4": "YOU'RE BUSTING A GERMAN HEAVY WATER PLANT IN THE RUHR.",
        }
        aircraft = _stdin_choice(
            prompt="AIRCRAFT -- LIBERATOR(1), B-29(2), B-17(3), LANCASTER(4): ",
            choices=aircraft_to_message,
        )
    
        print(aircraft_to_message[aircraft])
        return commence_non_kamikazi_attack()
    
    
    def play_japan() -> None:
        print("YOU'RE FLYING A KAMIKAZE MISSION OVER THE USS LEXINGTON.")
        first_mission = input("YOUR FIRST KAMIKAZE MISSION? (Y OR N): ")
        if first_mission.lower() == "n":
            return player_death()
        return mission_success() if random.random() > 0.65 else player_death()
    
    
    def play_germany() -> None:
        targets_to_messages = {
            # 1 - RUSSIA, 2 - ENGLAND, 3 - FRANCE
            "1": "YOU'RE NEARING STALINGRAD.",
            "2": "NEARING LONDON.  BE CAREFUL, THEY'VE GOT RADAR.",
            "3": "NEARING VERSAILLES.  DUCK SOUP.  THEY'RE NEARLY DEFENSELESS.",
        }
        target = _stdin_choice(
            prompt="A NAZI, EH?  OH WELL.  ARE YOU GOING FOR RUSSIA(1),\nENGLAND(2), OR FRANCE(3)? ",
            choices=targets_to_messages,
        )
    
        print(targets_to_messages[target])
    
        return commence_non_kamikazi_attack()
    
    
    def play_game() -> None:
        print("YOU ARE A PILOT IN A WORLD WAR II BOMBER.")
        sides = {"1": play_italy, "2": play_allies, "3": play_japan, "4": play_germany}
        side = _stdin_choice(
            prompt="WHAT SIDE -- ITALY(1), ALLIES(2), JAPAN(3), GERMANY(4): ", choices=sides
        )
        return sides[side]()
    
    
    if __name__ == "__main__":
        again = True
        while again:
            play_game()
            again = input("ANOTHER MISSION? (Y OR N): ").upper() == "Y"
    
    
    ================================================
    FILE: 12_Bombs_Away/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 12_Bombs_Away/rust/Cargo.toml
    ================================================
    [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"
    
    
    ================================================
    FILE: 12_Bombs_Away/rust/src/main.rs
    ================================================
    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;
            }
        }
    }
    
    
    ================================================
    FILE: 12_Bombs_Away/vbnet/BombsAway.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "BombsAway", "BombsAway.vbproj", "{7FB28848-EC1C-4594-B823-BA6DB06B34A8}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{7FB28848-EC1C-4594-B823-BA6DB06B34A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{7FB28848-EC1C-4594-B823-BA6DB06B34A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{7FB28848-EC1C-4594-B823-BA6DB06B34A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{7FB28848-EC1C-4594-B823-BA6DB06B34A8}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 12_Bombs_Away/vbnet/BombsAway.vbproj
    ================================================
    
      
        Exe
        BombsAway
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 12_Bombs_Away/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 13_Bounce/README.md
    ================================================
    ### Bounce
    
    This program plots a bouncing ball. Most computer plots run along the paper in the terminal (top to bottom); however, this plot is drawn horizontally on the paper (left to right).
    
    You may specify the initial velocity of the ball and the coefficient of elasticity of the ball (a superball is about 0.85 — other balls are much less). You also specify the time increment to be used in “strobing” the flight of the ball. In other words, it is as though the ball is thrown up in a darkened room and you flash a light at fixed time intervals and photograph the progress of the ball.
    
    The program was originally written by Val Skalabrin while he was at DEC.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=25)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=40)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 13_Bounce/bounce.bas
    ================================================
    10 PRINT TAB(33);"BOUNCE"
    20 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    30 PRINT:PRINT:PRINT
    90 DIM T(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
    135 INPUT "TIME INCREMENT (SEC)";S2
    140 PRINT
    150 INPUT "VELOCITY (FPS)";V
    160 PRINT
    170 INPUT "COEFFICIENT";C
    180 PRINT
    182 PRINT "FEET"
    184 PRINT
    186 S1=INT(70/(V/(16*S2)))
    190 FOR I=1 TO S1
    200 T(I)=V*C^(I-1)/16
    210 NEXT I
    220 FOR H=INT(-16*(V/32)^2+V^2/32+.5) TO 0 STEP -.5
    221 IF INT(H)<>H THEN 225
    222 PRINT H;
    225 L=0
    230 FOR I=1 TO S1
    240 FOR T=0 TO T(I) STEP S2
    245 L=L+S2
    250 IF ABS(H-(.5*(-32)*T^2+V*C^(I-1)*T))>.25 THEN 270
    260 PRINT TAB(L/S2);"0";
    270 NEXT T
    275 T=T(I+1)/2
    276 IF -16*T^2+V*C^(I-1)*T
    /// Represents the bounce of the ball, calculating duration, height and position in time.
    /// 
    /// 
    /// All calculations are derived from the equation for projectile motion: s = vt + 0.5at^2
    /// 
    internal class Bounce
    {
        private const float _acceleration = -32; // feet/s^2
    
        private readonly float _velocity;
    
        internal Bounce(float velocity)
        {
            _velocity = velocity;
        }
    
        public float Duration => -2 * _velocity / _acceleration;
    
        public float MaxHeight =>
            (float)Math.Round(-_velocity * _velocity / 2 / _acceleration, MidpointRounding.AwayFromZero);
    
        public float Plot(Graph graph, float startTime)
        {
            var time = 0f;
            for (; time <= Duration; time += graph.TimeIncrement)
            {
                var height = _velocity * time + _acceleration * time * time / 2;
                graph.Plot(startTime + time, height);
            }
    
            return startTime + time;
        }
    
        public Bounce Next(float elasticity) => new Bounce(_velocity * elasticity);
    }
    
    
    ================================================
    FILE: 13_Bounce/csharp/Bounce.csproj
    ================================================
    
      
        Exe
        net6.0
        10
        enable
        enable
      
    
      
        
      
    
      
        
      
    
    
    
    ================================================
    FILE: 13_Bounce/csharp/Bounce.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bounce", "Bounce.csproj", "{4A967985-8CB0-49D2-B322-B2668491CA6E}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{4A967985-8CB0-49D2-B322-B2668491CA6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{4A967985-8CB0-49D2-B322-B2668491CA6E}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{4A967985-8CB0-49D2-B322-B2668491CA6E}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{4A967985-8CB0-49D2-B322-B2668491CA6E}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 13_Bounce/csharp/Game.cs
    ================================================
    using static Bounce.Resources.Resource;
    
    namespace Bounce;
    
    internal class Game
    {
        private readonly IReadWrite _io;
    
        public Game(IReadWrite io)
        {
            _io = io;
        }
    
        public void Play(Func playAgain)
        {
            _io.Write(Streams.Title);
            _io.Write(Streams.Instructions);
    
            while (playAgain.Invoke())
            {
                var timeIncrement = _io.ReadParameter("Time increment (sec)");
                var velocity = _io.ReadParameter("Velocity (fps)");
                var elasticity = _io.ReadParameter("Coefficient");
    
                var bounce = new Bounce(velocity);
                var bounceCount = (int)(Graph.Row.Width * timeIncrement / bounce.Duration);
                var graph = new Graph(bounce.MaxHeight, timeIncrement);
    
                var time = 0f;
                for (var i = 0; i < bounceCount; i++, bounce = bounce.Next(elasticity))
                {
                    time = bounce.Plot(graph, time);
                }
    
                _io.WriteLine(graph);
            }
        }
    }
    
    
    ================================================
    FILE: 13_Bounce/csharp/Graph.cs
    ================================================
    using System.Text;
    
    namespace Bounce;
    
    /// 
    /// Provides support for plotting a graph of height vs time, and rendering it to a string.
    /// 
    internal class Graph
    {
        private readonly Dictionary _rows;
    
        public Graph(float maxHeight, float timeIncrement)
        {
            // 1 row == 1/2 foot + 1 row for zero
            var rowCount = 2 * (int)Math.Round(maxHeight, MidpointRounding.AwayFromZero) + 1;
            _rows = Enumerable.Range(0, rowCount)
                .ToDictionary(x => x, x => new Row(x % 2 == 0 ? $" {x / 2} " : ""));
            TimeIncrement = timeIncrement;
        }
    
        public float TimeIncrement { get; }
        public float MaxTimePlotted { get; private set; }
    
        public void Plot(float time, float height)
        {
            var rowIndex = (int)Math.Round(height * 2, MidpointRounding.AwayFromZero);
            var colIndex = (int)(time / TimeIncrement) + 1;
            if (_rows.TryGetValue(rowIndex, out var row))
            {
                row[colIndex] = '0';
            }
            MaxTimePlotted = Math.Max(time, MaxTimePlotted);
        }
    
        public override string ToString()
        {
            var sb = new StringBuilder().AppendLine("Feet").AppendLine();
            foreach (var (_, row) in _rows.OrderByDescending(x => x.Key))
            {
                sb.Append(row).AppendLine();
            }
            sb.Append(new Axis(MaxTimePlotted, TimeIncrement));
    
            return sb.ToString();
        }
    
        internal class Row
        {
            public const int Width = 70;
    
            private readonly char[] _chars = new char[Width + 2];
            private int nextColumn = 0;
    
            public Row(string label)
            {
                Array.Fill(_chars, ' ');
                Array.Copy(label.ToCharArray(), _chars, label.Length);
                nextColumn = label.Length;
            }
    
            public char this[int column]
            {
                set
                {
                    if (column >= _chars.Length) { return; }
                    if (column < nextColumn) { column = nextColumn; }
                    _chars[column] = value;
                    nextColumn = column + 1;
                }
            }
    
            public override string ToString() => new string(_chars);
        }
    
        internal class Axis
        {
            private readonly int _maxTimeMark;
            private readonly float _timeIncrement;
            private readonly Labels _labels;
    
            internal Axis(float maxTimePlotted, float timeIncrement)
            {
                _maxTimeMark = (int)Math.Ceiling(maxTimePlotted);
                _timeIncrement = timeIncrement;
    
                _labels = new Labels();
                for (var i = 1; i <= _maxTimeMark; i++)
                {
                    _labels.Add((int)(i / _timeIncrement), $" {i} ");
                }
            }
    
            public override string ToString()
                => new StringBuilder()
                    .Append(' ').Append('.', (int)(_maxTimeMark / _timeIncrement) + 1).AppendLine()
                    .Append(_labels).AppendLine()
                    .Append(' ', (int)(_maxTimeMark / _timeIncrement / 2 - 2)).AppendLine("Seconds")
                    .ToString();
        }
    
        internal class Labels : Row
        {
            public Labels()
                : base(" 0")
            {
            }
    
            public void Add(int column, string label)
            {
                for (var i = 0; i < label.Length; i++)
                {
                    this[column + i] = label[i];
                }
            }
        }
    }
    
    
    ================================================
    FILE: 13_Bounce/csharp/IReadWriteExtensions.cs
    ================================================
    namespace Bounce;
    
    internal static class IReadWriteExtensions
    {
        internal static float ReadParameter(this IReadWrite io, string parameter)
        {
            var value = io.ReadNumber(parameter);
            io.WriteLine();
            return value;
        }
    }
    
    ================================================
    FILE: 13_Bounce/csharp/Program.cs
    ================================================
    global using Games.Common.IO;
    global using Games.Common.Numbers;
    
    using Bounce;
    
    new Game(new ConsoleIO()).Play(() => true);
    
    ================================================
    FILE: 13_Bounce/csharp/README.md
    ================================================
    # Bounce
    
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    ## Conversion notes
    
    ### Mode of Operation
    
    This conversion performs the same function as the original, and provides the same experience, but does it in a different
    way.
    
    The original BASIC code builds the graph as it writes to the screen, scanning each line for points that need to be
    plotted.
    
    This conversion steps through time, calculating the position of the ball at each instant, building the graph in memory.
    It then writes the graph to the output in one go.
    
    ### Failure Modes
    
    The original BASIC code performs no validation of the input parameters. Some combinations of parameters produce no
    output, others crash the program.
    
    In the spirit of the original this conversion also performs no validation of the parameters, but it does not attempt to
    replicate the original's failure modes. It fails quite happily in its own way.
    
    
    ================================================
    FILE: 13_Bounce/csharp/Resources/Instructions.txt
    ================================================
    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).
    
    
    
    ================================================
    FILE: 13_Bounce/csharp/Resources/Resource.cs
    ================================================
    using System.Reflection;
    using System.Runtime.CompilerServices;
    
    namespace Bounce.Resources;
    
    internal static class Resource
    {
        internal static class Streams
        {
            public static Stream Instructions => GetStream();
            public static Stream Title => GetStream();
        }
    
        private static Stream GetStream([CallerMemberName] string? name = null)
            => Assembly.GetExecutingAssembly().GetManifestResourceStream($"Bounce.Resources.{name}.txt")
                ?? throw new ArgumentException($"Resource stream {name} does not exist", nameof(name));
    }
    
    ================================================
    FILE: 13_Bounce/csharp/Resources/Title.txt
    ================================================
                                     Bounce
                   Creative Computing  Morristown, New Jersey
    
    
    
    
    
    ================================================
    FILE: 13_Bounce/java/Bounce.java
    ================================================
    import java.util.Scanner;
    import java.lang.Math;
    
    /**
     * Game of Bounce
     * 

    * Based on the BASIC game of Bounce here * https://github.com/coding-horror/basic-computer-games/blob/main/13%20Bounce/bounce.bas *

    * Note: The idea was to create a version of the 1970's BASIC game in Java, without introducing * new features - no additional text, error checking, etc has been added. * * Converted from BASIC to Java by Darren Cardenas. */ public class Bounce { private final Scanner scan; // For user input public Bounce() { scan = new Scanner(System.in); } // End of constructor Bounce public void play() { showIntro(); startGame(); } // End of method play private void showIntro() { System.out.println(" ".repeat(32) + "BOUNCE"); System.out.println(" ".repeat(14) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println("\n\n"); } // End of method showIntro private void startGame() { double coefficient = 0; double height = 0; double timeIncrement = 0; double timeIndex = 0; double timeTotal = 0; double velocity = 0; double[] timeData = new double[21]; int heightInt = 0; int index = 0; int maxData = 0; String lineContent = ""; System.out.println("THIS SIMULATION LETS YOU SPECIFY THE INITIAL VELOCITY"); System.out.println("OF A BALL THROWN STRAIGHT UP, AND THE COEFFICIENT OF"); System.out.println("ELASTICITY OF THE BALL. PLEASE USE A DECIMAL FRACTION"); System.out.println("COEFFICIENCY (LESS THAN 1)."); System.out.println(""); System.out.println("YOU ALSO SPECIFY THE TIME INCREMENT TO BE USED IN"); System.out.println("'STROBING' THE BALL'S FLIGHT (TRY .1 INITIALLY)."); System.out.println(""); // Begin outer while loop while (true) { System.out.print("TIME INCREMENT (SEC)? "); timeIncrement = Double.parseDouble(scan.nextLine()); System.out.println(""); System.out.print("VELOCITY (FPS)? "); velocity = Double.parseDouble(scan.nextLine()); System.out.println(""); System.out.print("COEFFICIENT? "); coefficient = Double.parseDouble(scan.nextLine()); System.out.println(""); System.out.println("FEET"); System.out.println(""); maxData = (int)(70 / (velocity / (16 * timeIncrement))); for (index = 1; index <= maxData; index++) { timeData[index] = velocity * Math.pow(coefficient, index - 1) / 16; } // Begin loop through all rows of y-axis data for (heightInt = (int)(-16 * Math.pow(velocity / 32, 2) + Math.pow(velocity, 2) / 32 + 0.5) * 10; heightInt >= 0; heightInt -= 5) { height = heightInt / 10.0; lineContent = ""; if ((int)(Math.floor(height)) == height) { lineContent += " " + (int)(height) + " "; } timeTotal = 0; for (index = 1; index <= maxData; index++) { for (timeIndex = 0; timeIndex <= timeData[index]; timeIndex += timeIncrement) { timeTotal += timeIncrement; if (Math.abs(height - (0.5 * (-32) * Math.pow(timeIndex, 2) + velocity * Math.pow(coefficient, index - 1) * timeIndex)) <= 0.25) { while (lineContent.length() < (timeTotal / timeIncrement) - 1) { lineContent += " "; } lineContent += "0"; } } timeIndex = timeData[index + 1] / 2; if (-16 * Math.pow(timeIndex, 2) + velocity * Math.pow(coefficient, index - 1) * timeIndex < height) { break; } } System.out.println(lineContent); } // End loop through all rows of y-axis data lineContent = ""; // Show the x-axis for (index = 1; index <= (int)(timeTotal + 1) / timeIncrement + 1; index++) { lineContent += "."; } System.out.println(lineContent); lineContent = " 0"; for (index = 1; index <= (int)(timeTotal + 0.9995); index++) { while (lineContent.length() < (int)(index / timeIncrement)) { lineContent += " "; } lineContent += index; } System.out.println(lineContent); System.out.println(" ".repeat((int)((timeTotal + 1) / (2 * timeIncrement) - 3)) + "SECONDS"); } // End outer while loop } // End of method startGame public static void main(String[] args) { Bounce game = new Bounce(); game.play(); } // End of method main } // End of class Bounce ================================================ FILE: 13_Bounce/java/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Oracle Java](https://openjdk.java.net/) ================================================ FILE: 13_Bounce/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 13_Bounce/javascript/bounce.html ================================================ BOUNCE

    
    
    
    
    
    
    ================================================
    FILE: 13_Bounce/javascript/bounce.js
    ================================================
    // BOUNCE
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    // Main program
    async function main()
    {
        print(tab(33) + "BOUNCE\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        ta = [];
        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");
        print("\n");
        print("YOU ALSO SPECIFY THE TIME INCREMENT TO BE USED IN\n");
        print("'STROBING' THE BALL'S FLIGHT (TRY .1 INITIALLY).\n");
        print("\n");
        while (1) {
            print("TIME INCREMENT (SEC)");
            s2 = parseFloat(await input());
            print("\n");
            print("VELOCITY (FPS)");
            v = parseFloat(await input());
            print("\n");
            print("COEFFICIENT");
            c = parseFloat(await input());
            print("\n");
            print("FEET\n");
            print("\n");
            s1 = Math.floor(70 / (v / (16 * s2)));
            for (i = 1; i <= s1; i++)
                ta[i] = v * Math.pow(c, i - 1) / 16;
            for (h = Math.floor(-16 * Math.pow(v / 32, 2) + Math.pow(v, 2) / 32 + 0.5); h >= 0; h -= 0.5) {
                str = "";
                if (Math.floor(h) == h)
                    str += " " + h + " ";
                l = 0;
                for (i = 1; i <= s1; i++) {
                    for (t = 0; t <= ta[i]; t += s2) {
                        l += s2;
                        if (Math.abs(h - (0.5 * (-32) * Math.pow(t, 2) + v * Math.pow(c, i - 1) * t)) <= 0.25) {
                            while (str.length < l / s2)
                                str += " ";
                            str += "0";
                        }
                    }
                    t = ta[i + 1] / 2;
                    if (-16 * Math.pow(t, 2) + v * Math.pow(c, i - 1) * t < h)
                        break;
                }
                print(str + "\n");
            }
            str = " ";
            for (i = 1; i < Math.floor(l + 1) / s2 + 1; i++)
                str += ".";
            print(str + "\n");
            str = " 0";
            for (i = 1; i < Math.floor(l + 0.9995); i++) {
                while (str.length < Math.floor(i / s2))
                    str += " ";
                str += i;
            }
            print(str + "\n");
            print(tab(Math.floor(l + 1) / (2 * s2) - 2) + "SECONDS\n");
        }
    }
    
    main();
    
    
    ================================================
    FILE: 13_Bounce/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 13_Bounce/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 13_Bounce/perl/README.md
    ================================================
    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.
    
    
    ================================================
    FILE: 13_Bounce/perl/bounce.pl
    ================================================
    #!/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";
    }
    
    
    ================================================
    FILE: 13_Bounce/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 13_Bounce/python/bounce.py
    ================================================
    """
    BOUNCE
    
    A physics simulation
    
    Ported by Dave LeCompte
    """
    
    from typing import Tuple, List
    
    PAGE_WIDTH = 64
    
    
    def print_centered(msg: str) -> None:
        spaces = " " * ((PAGE_WIDTH - len(msg)) // 2)
        print(spaces + msg)
    
    
    def print_header(title: str) -> None:
        print_centered(title)
        print_centered("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY")
        print()
        print()
        print()
    
    
    def print_instructions() -> None:
        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()
    
    
    def get_initial_conditions() -> Tuple[float, float, float]:
        delta_t = float(input("TIME INCREMENT (SEC)? "))
        print()
        v0 = float(input("VELOCITY (FPS)? "))
        print()
        coeff_rest = float(input("COEFFICIENT? "))
        print()
    
        return delta_t, v0, coeff_rest
    
    
    def print_at_tab(line: str, tab: int, s: str) -> str:
        line += (" " * (tab - len(line))) + s
        return line
    
    
    def run_simulation(delta_t: float, v0: float, coeff_rest: float) -> None:
        bounce_time: List[float] = [0] * 20  # time of each bounce
    
        print("FEET")
        print()
    
        sim_dur = int(70 / (v0 / (16 * delta_t)))
        for i in range(1, sim_dur + 1):
            bounce_time[i] = v0 * coeff_rest ** (i - 1) / 16
    
        # Draw the trajectory of the bouncing ball, one slice of height at a time
        h: float = int(-16 * (v0 / 32) ** 2 + v0**2 / 32 + 0.5)
        while h >= 0:
            line = ""
            if int(h) == h:
                line += str(int(h))
            total_time: float = 0
            for i in range(1, sim_dur + 1):
                tm: float = 0
                while tm <= bounce_time[i]:
                    total_time += delta_t
                    if (
                        abs(h - (0.5 * (-32) * tm**2 + v0 * coeff_rest ** (i - 1) * tm))
                        <= 0.25
                    ):
                        line = print_at_tab(line, int(total_time / delta_t), "0")
                    tm += delta_t
                tm = bounce_time[i + 1] / 2
    
                if -16 * tm**2 + v0 * coeff_rest ** (i - 1) * tm < h:
                    break
            print(line)
            h -= 0.5
    
        print("." * (int((total_time + 1) / delta_t) + 1))
        print
        line = " 0"
        for i in range(1, int(total_time + 0.9995) + 1):
            line = print_at_tab(line, int(i / delta_t), str(i))
        print(line)
        print()
        print(print_at_tab("", int((total_time + 1) / (2 * delta_t) - 2), "SECONDS"))
        print()
    
    
    def main() -> None:
        print_header("BOUNCE")
        print_instructions()
    
        while True:
            delta_t, v0, coeff_rest = get_initial_conditions()
    
            run_simulation(delta_t, v0, coeff_rest)
            break
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 13_Bounce/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 13_Bounce/ruby/bounce.rb
    ================================================
    ## Global constants
    
    # Gravity accelaration (F/S^2) ~= 32
    G = 32
    
    # Used to indent the plotting of ball positions
    # so that the height digits don't affect
    # where we start plotting ball positions
    BALL_PLOT_INDENT = "\t"
    
    # The deviation between current plotted height and the actual
    # height of the ball that we will accept to plot the ball in
    # that plotted height
    BALL_PLOT_DEVIATION = 0.25
    
    # The step we will take as we move down vertically while
    # plotting ball positions
    BALL_PLOT_HEIGHT_STEP = 0.5
    
    
    ## Helper functions
    
    # Calculates the bounce speed (up) of the ball for a given
    # bounce number and coefficient
    def calc_velocity_for_bounce(v0, bounce, coefficient)
        v = v0 * coefficient**bounce
    end
    
    # Check https://physics.stackexchange.com/a/333436 for nice explanation
    def calc_bounce_total_time(v0, bounce, coefficient)
        v = calc_velocity_for_bounce(v0, bounce, coefficient)
        t = 2 * v / G
    end
    
    # Check https://physics.stackexchange.com/a/333436 for nice explanation
    def calc_ball_height(v0, bounce, coefficient, t)
        v = calc_velocity_for_bounce(v0, bounce, coefficient)
        h = v * t - 0.5 * G * t**2
    end
    
    def heighest_position_in_next_bounce(time_in_bounce, v0, i, c)
        time_in_next_bounce = time_in_bounce[i+1]
        return -1 if time_in_next_bounce.nil?
        return calc_ball_height(v0, i, c, time_in_next_bounce / 2) unless time_in_next_bounce.nil?
    end
    
    def intro
        puts <<~INSTRUCTIONS
                        BOUNCE
        CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY
    
    
        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).
        INSTRUCTIONS
    end
    
    
    ## Plottin functions
    
    def plot_header
        puts
        puts "FEET"
    end
    
    def plot_bouncing_ball(strobbing_time, v0, c)
        ## Initializing helper values
    
        # How many bounces we want to plot
        # original BASIC version is 70 / (V / (16 * S2))
        # 70 is assumed to be an arbitrary number higher than 2G and 16 is 1/2G
        bounces_to_plot = (G**2 / (v0 / strobbing_time)).to_i
    
        # Holds the total time the ball spends in the air in every bounce
        time_in_bounce = bounces_to_plot.times.map { |i| calc_bounce_total_time v0, i, c }
    
        plot_width = 0
    
        # Calculate the highest position for the ball after the very first bounce
        plotted_height = (calc_ball_height(v0, 0, c, v0/G) + 0.5).to_i
    
        ## Plotting bouncing ball
        while plotted_height >= 0 do
            # We will print only whole-number heights
            print plotted_height.to_i if plotted_height.to_i === plotted_height
    
            print BALL_PLOT_INDENT
    
            bounces_to_plot.times { |i|
                (0..time_in_bounce[i]).step(strobbing_time) { |t|
                    ball_pos = calc_ball_height v0, i, c, t
    
                    # If the ball is within the acceptable deviation
                    # from the current height, we will plot it
                    if (plotted_height - ball_pos).abs <= BALL_PLOT_DEVIATION then
                        print "0"
                    else
                        print " "
                    end
    
                    # Increment the plot width when we are plotting height = 0
                    # which will definitely be the longest since it never gets
                    # skipped by line 98
                    plot_width += 1 if plotted_height == 0
                }
    
                if heighest_position_in_next_bounce(time_in_bounce, v0, i, c) < plotted_height then
                    # If we got no more ball positions at or above current height in the next bounce,
                    # we can skip the rest of the bounces and move down to the next height to plot
                    puts
                    break
                end
            }
    
            plotted_height -= BALL_PLOT_HEIGHT_STEP
        end
    
        # Return plot_width to be used by the plot_footer
        plot_width
    end
    
    def plot_footer (plot_width, strobbing_time)
        # Dotted separator line
        puts
        print BALL_PLOT_INDENT
        (plot_width).times { |_| print "." }
        puts
    
        # Time values line
        print BALL_PLOT_INDENT
        points_in_sec = (1 / strobbing_time).to_i
        plot_width.times { |i|
            if i % points_in_sec == 0 then
                print (i / points_in_sec).to_i
            else
                print " "
            end
        }
        puts
    
        # Time unit line
        print BALL_PLOT_INDENT
        (plot_width / 2 - 4).to_i.times { |_| print " " }
        puts "SECONDS"
        puts
    end
    
    def game_loop
        # Read strobing, velocity and coefficient parameters from user input
        puts "TIME INCREMENT (SEC)"
        strobbing_time = gets.to_f
    
        puts "VELOCITY (FPS)"
        v0 = gets.to_f
    
        puts "COEFFICIENT"
        c = gets.to_f
    
        # Plotting
        plot_header
    
        plot_width = plot_bouncing_ball strobbing_time, v0, c
    
        plot_footer plot_width, strobbing_time
    end
    
    
    ## Entry point
    
    begin
        intro
        loop do
            game_loop
        end
    rescue SystemExit, Interrupt
        exit
    rescue => exception
       p exception
    end
    
    
    ================================================
    FILE: 13_Bounce/rust/Cargo.toml
    ================================================
    [package]
    name = "rust"
    version = "0.1.0"
    edition = "2021"
    
    [dependencies]
    
    
    ================================================
    FILE: 13_Bounce/rust/src/main.rs
    ================================================
    /** 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
      
        Exe
        Bounce
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 13_Bounce/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 14_Bowling/README.md
    ================================================
    ### Bowling
    
    This is a simulated bowling game for up to four players. You play 10 frames. To roll the ball, you simply type “ROLL.” After each roll, the computer will show you a diagram of the remaining pins (“0” means the pin is down, “+” means it is still standing), and it will give you a roll analysis:
    - GUTTER
    - STRIKE
    - SPARE
    - ERROR (on second ball if pins still standing)
    
    Bowling was written by Paul Peraino while a student at Woodrow Wilson High School, San Francisco, California.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=26)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=41)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Known Bugs
    
    - In the original code, scores is not kept accurately in multiplayer games.  It stores scores in F*P, where F is the frame and P is the player.  So, for example, frame 8 player 1 (index 16) clobbers the score from frame 4 player 2 (also index 16).
    
    - Even when scores are kept accurately, they don't match normal bowling rules.  In this game, the score for each ball is just the total number of pins down after that ball, and the third row of scores is a status indicator (3 for strike, 2 for spare, 1 for anything else).
    
    - The program crashes with a "NEXT without FOR" error if you elect to play again after the first game.
    
    #### Porting Notes
    
    - The funny control characters in the "STRIKE!" string literal are there to make the terminal beep.
    
    
    ================================================
    FILE: 14_Bowling/bowling.bas
    ================================================
    10 PRINT TAB(34);"BOWL"
    20 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    30 PRINT:PRINT:PRINT
    270 DIM C(15),A(100,6)
    360 PRINT "WELCOME TO THE ALLEY"
    450 PRINT "BRING YOUR FRIENDS"
    540 PRINT "OKAY LET'S FIRST GET ACQUAINTED"
    630 PRINT ""
    720 PRINT "THE INSTRUCTIONS (Y/N)"
    810 INPUT Z$
    900 IF Z$="Y" THEN 990
    960 IF Z$="N" THEN 1530
    990 PRINT "THE GAME OF BOWLING TAKES MIND AND SKILL.DURING THE GAME"
    1080 PRINT "THE COMPUTER WILL KEEP SCORE.YOU MAY COMPETE WITH"
    1170 PRINT "OTHER PLAYERS[UP TO FOUR].YOU WILL BE PLAYING TEN FRAMES"
    1260 PRINT "ON THE PIN DIAGRAM 'O' MEANS THE PIN IS DOWN...'+' MEANS THE"
    1350 PRINT "PIN IS STANDING.AFTER THE GAME THE COMPUTER WILL SHOW YOUR"
    1440 PRINT "SCORES ."
    1530 PRINT "FIRST OF ALL...HOW MANY ARE PLAYING";
    1620 INPUT R
    1710 PRINT
    1800 PRINT "VERY GOOD..."
    1890 FOR I=1 TO 100: FOR J=1 TO 6: A(I,J)=0: NEXT J: NEXT I
    1980 F=1
    2070 FOR P=1 TO R
    2160 M=0
    2250 B=1
    2340 M=0: Q=0
    2430 FOR I=1 TO 15: C(I)=0: NEXT I
    2520 REM ARK BALL GENERATOR USING MOD '15' SYSTEM
    2610 PRINT "TYPE ROLL TO GET THE BALL GOING."
    2700 INPUT N$
    2790 K=0: D=0
    2880 FOR I=1 TO 20
    2970 X=INT(RND(1)*100)
    3060 FOR J=1 TO 10
    3150 IF X<15*J THEN 3330
    3240 NEXT J
    3330 C(15*J-X)=1
    3420 NEXT I
    3510 REM ARK PIN DIAGRAM
    3600 PRINT "PLAYER:"P;"FRAME:";F"BALL:"B
    3690 FOR I=0 TO 3
    3780 PRINT
    3870 FOR J=1 TO 4-I
    3960 K=K+1
    4050 IF C(K)=1 THEN 4320
    4140 PRINT TAB(I);"+ ";
    4230 GOTO 4410
    4320 PRINT TAB(I);"O ";
    4410 NEXT J
    4500 NEXT I
    4590 PRINT ""
    4680 REM ARK ROLL ANALYSIS
    4770 FOR I=1 TO 10
    4860 D=D+C(I)
    4950 NEXT I
    5040 IF D-M <> 0 THEN 5220
    5130 PRINT "GUTTER!!"
    5220 IF B<>1 OR D<>10 THEN 5490
    5310 PRINT "STRIKE!!!!!"
    5400 Q=3
    5490 IF B<>2 OR D<>10 THEN 5760
    5580 PRINT "SPARE!!!!"
    5670 Q=2
    5760 IF B<>2 OR D>=10 THEN 6030
    5850 PRINT "ERROR!!!"
    5940 Q=1
    6030 IF B<>1 OR D>=10 THEN 6210
    6120 PRINT "ROLL YOUR 2ND BALL"
    6210 REM ARK STORAGE OF THE SCORES
    6300 PRINT
    6390 A(F*P,B)=D
    6480 IF B=2 THEN 7020
    6570 B=2
    6660 M=D
    6750 IF Q=3 THEN 6210
    6840 A(F*P,B)=D-M
    6930 IF Q=0 THEN 2520
    7020 A(F*P,3)=Q
    7110 NEXT P
    7200 F=F+1
    7290 IF F<11 THEN 2070
    7295 PRINT "FRAMES"
    7380 FOR I=1 TO 10
    7470 PRINT I;
    7560 NEXT I
    7650 PRINT
    7740 FOR P=1 TO R
    7830 FOR I=1 TO 3
    7920 FOR J=1 TO 10
    8010 PRINT A(J*P,I);
    8100 NEXT J
    8105 PRINT
    8190 NEXT I
    8280 PRINT
    8370 NEXT P
    8460 PRINT "DO YOU WANT ANOTHER GAME"
    8550 INPUT A$
    8640 IF LEFT$(A$,1)="Y" THEN 2610
    8730 END
    
    
    ================================================
    FILE: 14_Bowling/csharp/Bowling.cs
    ================================================
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace Bowling
    {
        public class Bowling
        {
            private readonly Pins pins = new();
    
            private int players;
    
            public void Play()
            {
                ShowBanner();
                MaybeShowInstructions();
                Setup();
                GameLoop();
            }
    
            private static void ShowBanner()
            {
                Utility.PrintString(34, "BOWL");
                Utility.PrintString(15, "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
                Utility.PrintString();
                Utility.PrintString();
                Utility.PrintString();
                Utility.PrintString("WELCOME TO THE ALLEY");
                Utility.PrintString("BRING YOUR FRIENDS");
                Utility.PrintString("OKAY LET'S FIRST GET ACQUAINTED");
                Utility.PrintString();
            }
            private static void MaybeShowInstructions()
            {
                Utility.PrintString("THE INSTRUCTIONS (Y/N)");
                if (Utility.InputString() == "N") return;
                Utility.PrintString("THE GAME OF BOWLING TAKES MIND AND SKILL.DURING THE GAME");
                Utility.PrintString("THE COMPUTER WILL KEEP SCORE.YOU MAY COMPETE WITH");
                Utility.PrintString("OTHER PLAYERS[UP TO FOUR].YOU WILL BE PLAYING TEN FRAMES");
                Utility.PrintString("ON THE PIN DIAGRAM 'O' MEANS THE PIN IS DOWN...'+' MEANS THE");
                Utility.PrintString("PIN IS STANDING.AFTER THE GAME THE COMPUTER WILL SHOW YOUR");
                Utility.PrintString("SCORES .");
            }
            private void Setup()
            {
                Utility.PrintString("FIRST OF ALL...HOW MANY ARE PLAYING", false);
                var input = Utility.InputInt();
                players = input < 1 ? 1 : input;
                Utility.PrintString();
                Utility.PrintString("VERY GOOD...");
            }
            private void GameLoop()
            {
                GameResults[] gameResults = InitGameResults();
                var done = false;
                while (!done)
                {
                    ResetGameResults(gameResults);
                    for (int frame = 0; frame < GameResults.FramesPerGame; ++frame)
                    {
                        for (int player = 0; player < players; ++player)
                        {
                            pins.Reset();
                            int pinsDownThisFrame = pins.GetPinsDown();
    
                            int ball = 1;
                            while (ball == 1 || ball == 2) // One or two rolls
                            {
                                Utility.PrintString("TYPE ROLL TO GET THE BALL GOING.");
                                _ = Utility.InputString();
    
                                int pinsDownAfterRoll = pins.Roll();
                                ShowPins(player, frame, ball);
    
                                if (pinsDownAfterRoll == pinsDownThisFrame)
                                {
                                    Utility.PrintString("GUTTER!!");
                                }
    
                                if (ball == 1)
                                {
                                    // Store current pin count
                                    gameResults[player].Results[frame].PinsBall1 = pinsDownAfterRoll;
    
                                    // Special handling for strike
                                    if (pinsDownAfterRoll == Pins.TotalPinCount)
                                    {
                                        Utility.PrintString("STRIKE!!!!!\a\a\a\a");
                                        // No second roll
                                        ball = 0;
                                        gameResults[player].Results[frame].PinsBall2 = pinsDownAfterRoll;
                                        gameResults[player].Results[frame].Score = FrameResult.Points.Strike;
                                    }
                                    else
                                    {
                                        ball = 2; // Roll again
                                        Utility.PrintString("ROLL YOUR SECOND BALL");
                                    }
                                }
                                else if (ball == 2)
                                {
                                    // Store current pin count
                                    gameResults[player].Results[frame].PinsBall2 = pinsDownAfterRoll;
                                    ball = 0;
    
                                    // Determine the score for the frame
                                    if (pinsDownAfterRoll == Pins.TotalPinCount)
                                    {
                                        Utility.PrintString("SPARE!!!!");
                                        gameResults[player].Results[frame].Score = FrameResult.Points.Spare;
                                    }
                                    else
                                    {
                                        Utility.PrintString("ERROR!!!");
                                        gameResults[player].Results[frame].Score = FrameResult.Points.Error;
                                    }
                                }
                                Utility.PrintString();
                            }
                        }
                    }
                    ShowGameResults(gameResults);
                    Utility.PrintString("DO YOU WANT ANOTHER GAME");
                    var a = Utility.InputString();
                    done = a.Length == 0 || a[0] != 'Y';
                }
            }
    
            private GameResults[] InitGameResults()
            {
                var gameResults = new GameResults[players];
                for (int i = 0; i < gameResults.Length; i++)
                {
                    gameResults[i] = new GameResults();
                }
                return gameResults;
            }
    
            private void ShowPins(int player, int frame, int ball)
            {
                Utility.PrintString($"FRAME: {frame + 1} PLAYER: {player + 1} BALL: {ball}");
                var breakPins = new bool[] { true, false, false, false, true, false, false, true, false, true };
                var indent = 0;
                for (int pin = 0; pin < Pins.TotalPinCount; ++pin)
                {
                    if (breakPins[pin])
                    {
                        Utility.PrintString(); // End row
                        Utility.PrintString(indent++, false); // Indent next row
                    }
                    var s = pins[pin] == Pins.State.Down ? "+ " : "o ";
                    Utility.PrintString(s, false);
                }
                Utility.PrintString();
                Utility.PrintString();
            }
            private void ResetGameResults(GameResults[] gameResults)
            {
                foreach (var gameResult in gameResults)
                {
                    foreach (var frameResult in gameResult.Results)
                    {
                        frameResult.Reset();
                    }
                }
            }
            private void ShowGameResults(GameResults[] gameResults)
            {
                Utility.PrintString("FRAMES");
                for (int i = 0; i < GameResults.FramesPerGame; ++i)
                {
                    Utility.PrintString(Utility.PadInt(i, 3), false);
                }
                Utility.PrintString();
                foreach (var gameResult in gameResults)
                {
                    foreach (var frameResult in gameResult.Results)
                    {
                        Utility.PrintString(Utility.PadInt(frameResult.PinsBall1, 3), false);
                    }
                    Utility.PrintString();
                    foreach (var frameResult in gameResult.Results)
                    {
                        Utility.PrintString(Utility.PadInt(frameResult.PinsBall2, 3), false);
                    }
                    Utility.PrintString();
                    foreach (var frameResult in gameResult.Results)
                    {
                        Utility.PrintString(Utility.PadInt((int)frameResult.Score, 3), false);
                    }
                    Utility.PrintString();
                    Utility.PrintString();
                }
            }
        }
    }
    
    
    ================================================
    FILE: 14_Bowling/csharp/Bowling.csproj
    ================================================
    
      
        Exe
        net6.0
        10
        enable
        enable
      
    
    
    
    ================================================
    FILE: 14_Bowling/csharp/Bowling.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bowling", "Bowling.csproj", "{9951637A-8D70-42A4-8CB7-315FA414F960}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{9951637A-8D70-42A4-8CB7-315FA414F960}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{9951637A-8D70-42A4-8CB7-315FA414F960}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{9951637A-8D70-42A4-8CB7-315FA414F960}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{9951637A-8D70-42A4-8CB7-315FA414F960}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 14_Bowling/csharp/FrameResult.cs
    ================================================
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace Bowling
    {
        public class FrameResult
        {
            public enum Points { None, Error, Spare, Strike };
    
            public int PinsBall1 { get; set; }
            public int PinsBall2 { get; set; }
            public Points Score { get; set; }
    
            public void Reset()
            {
                PinsBall1 = PinsBall2 = 0;
                Score = Points.None;
            }
        }
    }
    
    
    ================================================
    FILE: 14_Bowling/csharp/GameResults.cs
    ================================================
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace Bowling
    {
        public class GameResults
        {
            public static readonly int FramesPerGame = 10;
            public FrameResult[] Results { get; set; }
    
            public GameResults()
            {
                Results = new FrameResult[FramesPerGame];
                for (int i = 0; i < FramesPerGame; ++i)
                {
                    Results[i] = new FrameResult();
                }
            }
        }
    }
    
    
    ================================================
    FILE: 14_Bowling/csharp/Pins.cs
    ================================================
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace Bowling
    {
        public class Pins
        {
            public enum State { Up, Down };
            public static readonly int TotalPinCount = 10;
            private readonly Random random = new();
    
            private State[] PinSet { get; set; }
    
            public Pins()
            {
                PinSet = new State[TotalPinCount];
            }
            public State this[int i]
            {
                get { return PinSet[i]; }
                set { PinSet[i] = value; }
            }
            public int Roll()
            {
                // REM ARK BALL GENERATOR USING MOD '15' SYSTEM
                for (int i = 0; i < 20; ++i)
                {
                    var x = random.Next(100) + 1;
                    int j;
                    for (j = 1; j <= 10; ++j)
                    {
                        if (x < 15 * j)
                            break;
                    }
                    var pindex = 15 * j - x;
                    if (pindex > 0 && pindex <= TotalPinCount)
                        PinSet[--pindex] = State.Down;
                }
                return GetPinsDown();
            }
            public void Reset()
            {
                for (int i = 0; i < PinSet.Length; ++i)
                {
                    PinSet[i] = State.Up;
                }
            }
            public int GetPinsDown()
            {
                return PinSet.Count(p => p == State.Down);
            }
        }
    }
    
    
    ================================================
    FILE: 14_Bowling/csharp/Program.cs
    ================================================
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace Bowling
    {
        public static class Program
        {
            public static void Main()
            {
                new Bowling().Play();
            }
        }
    }
    
    
    ================================================
    FILE: 14_Bowling/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 14_Bowling/csharp/Utility.cs
    ================================================
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace Bowling
    {
        internal static class Utility
        {
            public static string PadInt(int value, int width)
            {
                return value.ToString().PadLeft(width);
            }
            public static int InputInt()
            {
                while (true)
                {
                    if (int.TryParse(InputString(), out int i))
                        return i;
                    else
                        PrintString("!NUMBER EXPECTED - RETRY INPUT LINE");
                }
            }
            public static string InputString()
            {
                PrintString("? ", false);
                var input = Console.ReadLine();
                return input == null ? string.Empty : input.ToUpper();
            }
            public static void PrintInt(int value, bool newLine = false)
            {
                PrintString($"{value} ", newLine);
            }
            public static void PrintString(bool newLine = true)
            {
                PrintString(0, string.Empty);
            }
            public static void PrintString(int tab, bool newLine = true)
            {
                PrintString(tab, string.Empty, newLine);
            }
            public static void PrintString(string value, bool newLine = true)
            {
                PrintString(0, value, newLine);
            }
            public static void PrintString(int tab, string value, bool newLine = true)
            {
                Console.Write(new String(' ', tab));
                Console.Write(value);
                if (newLine) Console.WriteLine();
            }
        }
    }
    
    
    ================================================
    FILE: 14_Bowling/java/Bowling.java
    ================================================
    import java.util.Scanner;
    import java.lang.Math;
    
    /**
     * Game of Bowling
     * 

    * Based on the BASIC game of Bowling here * https://github.com/coding-horror/basic-computer-games/blob/main/14%20Bowling/bowling.bas *

    * Note: The idea was to create a version of the 1970's BASIC game in Java, without introducing * new features - no additional text, error checking, etc has been added. * * Converted from BASIC to Java by Darren Cardenas. */ public class Bowling { private final Scanner scan; // For user input public Bowling() { scan = new Scanner(System.in); } // End of constructor Bowling public void play() { showIntro(); startGame(); } // End of method play private static void showIntro() { System.out.println(" ".repeat(33) + "BOWL"); System.out.println(" ".repeat(14) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println("\n\n"); } // End of method showIntro private void startGame() { int ball = 0; int bell = 0; int frame = 0; int ii = 0; // Loop iterator int jj = 0; // Loop iterator int kk = 0; // Loop iterator int numPlayers = 0; int pinsDownBefore = 0; int pinsDownNow = 0; int player = 0; int randVal = 0; int result = 0; int[] pins = new int[16]; int[][] scores = new int[101][7]; String userResponse = ""; System.out.println("WELCOME TO THE ALLEY"); System.out.println("BRING YOUR FRIENDS"); System.out.println("OKAY LET'S FIRST GET ACQUAINTED"); System.out.println(""); System.out.println("THE INSTRUCTIONS (Y/N)"); System.out.print("? "); userResponse = scan.nextLine(); if (userResponse.toUpperCase().equals("Y")) { printRules(); } System.out.print("FIRST OF ALL...HOW MANY ARE PLAYING? "); numPlayers = Integer.parseInt(scan.nextLine()); System.out.println(""); System.out.println("VERY GOOD..."); // Begin outer while loop while (true) { for (ii = 1; ii <= 100; ii++) { for (jj = 1; jj <= 6; jj++) { scores[ii][jj] = 0; } } frame = 1; // Begin frame while loop while (frame < 11) { // Begin loop through all players for (player = 1; player <= numPlayers; player++) { pinsDownBefore = 0; ball = 1; result = 0; for (ii = 1; ii <= 15; ii++) { pins[ii] = 0; } while (true) { // Ball generator using mod '15' system System.out.println("TYPE ROLL TO GET THE BALL GOING."); System.out.print("? "); scan.nextLine(); kk = 0; pinsDownNow = 0; for (ii = 1; ii <= 20; ii++) { randVal = (int)(Math.random() * 100) + 1; for (jj = 1; jj <= 10; jj++) { if (randVal < 15 * jj) { break; } } pins[15 * jj - randVal] = 1; } // Pin diagram System.out.println("PLAYER: " + player + " FRAME: " + frame + " BALL: " + ball); for (ii = 0; ii <= 3; ii++) { System.out.println(""); System.out.print(" ".repeat(ii)); for (jj = 1; jj <= 4 - ii; jj++) { kk++; if (pins[kk] == 1) { System.out.print("O "); } else { System.out.print("+ "); } } } System.out.println(""); // Roll analysis for (ii = 1; ii <= 10; ii++) { pinsDownNow += pins[ii]; } if (pinsDownNow - pinsDownBefore == 0) { System.out.println("GUTTER!!"); } if (ball == 1 && pinsDownNow == 10) { System.out.println("STRIKE!!!!!"); // Ring bell for (bell = 1; bell <= 4; bell++) { System.out.print("\007"); try { Thread.sleep(500); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } result = 3; } if (ball == 2 && pinsDownNow == 10) { System.out.println("SPARE!!!!"); result = 2; } if (ball == 2 && pinsDownNow < 10) { System.out.println("ERROR!!!"); result = 1; } if (ball == 1 && pinsDownNow < 10) { System.out.println("ROLL YOUR 2ND BALL"); } // Storage of the scores System.out.println(""); scores[frame * player][ball] = pinsDownNow; if (ball != 2) { ball = 2; pinsDownBefore = pinsDownNow; if (result != 3) { scores[frame * player][ball] = pinsDownNow - pinsDownBefore; if (result == 0) { continue; } } else { scores[frame * player][ball] = pinsDownNow; } } break; } scores[frame * player][3] = result; } // End loop through all players frame++; } // End frame while loop System.out.println("FRAMES"); System.out.print(" "); for (ii = 1; ii <= 10; ii++) { System.out.print(ii + " "); } System.out.println(""); for (player = 1; player <= numPlayers; player++) { for (ii = 1; ii <= 3; ii++) { System.out.print(" "); for (jj = 1; jj <= 10; jj++) { System.out.print (scores[jj * player][ii] + " "); } System.out.println(""); } System.out.println(""); } System.out.println("DO YOU WANT ANOTHER GAME"); System.out.print("? "); userResponse = scan.nextLine(); if (!String.valueOf(userResponse.toUpperCase().charAt(0)).equals("Y")) { break; } } // End outer while loop } // End of method startGame public static void printRules() { System.out.println("THE GAME OF BOWLING TAKES MIND AND SKILL.DURING THE GAME"); System.out.println("THE COMPUTER WILL KEEP SCORE.YOU MAY COMPETE WITH"); System.out.println("OTHER PLAYERS[UP TO FOUR].YOU WILL BE PLAYING TEN FRAMES"); System.out.println("ON THE PIN DIAGRAM 'O' MEANS THE PIN IS DOWN...'+' MEANS THE"); System.out.println("PIN IS STANDING.AFTER THE GAME THE COMPUTER WILL SHOW YOUR"); System.out.println("SCORES ."); } // End of method printRules public static void main(String[] args) { Bowling game = new Bowling(); game.play(); } // End of method main } // End of class Bowling ================================================ FILE: 14_Bowling/java/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Oracle Java](https://openjdk.java.net/) ================================================ FILE: 14_Bowling/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 14_Bowling/javascript/bowling.html ================================================ BOWLING

    
    
    
    
    
    
    ================================================
    FILE: 14_Bowling/javascript/bowling.js
    ================================================
    // BOWLING
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    // Main program
    async function main()
    {
        print(tab(34) + "BOWL\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        c = [];
        a = [];
        for (i = 0; i <= 15; i++)
            c[i] = 0;
        print("WELCOME TO THE ALLEY\n");
        print("BRING YOUR FRIENDS\n");
        print("OKAY LET'S FIRST GET ACQUAINTED\n");
        print("\n");
        print("THE INSTRUCTIONS (Y/N)\n");
        str = await input();
        if (str.substr(0, 1) == "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\n");
            print("SCORES .\n");
        }
        print("FIRST OF ALL...HOW MANY ARE PLAYING");
        r = parseInt(await input());
        while (1) {
            print("\n");
            print("VERY GOOD...\n");
            for (i = 1; i <= 100; i++) {
                a[i] = [];
                for (j = 1; j <= 6; j++)
                    a[i][j] = 0;
            }
            f = 1;
            do {
                for (p = 1; p <= r; p++) {
                    // m = 0; // Repeated in original
                    b = 1;
                    m = 0;
                    q = 0;
                    for (i = 1; i <= 15; i++)
                        c[i] = 0;
                    while (1) {
                        // Ball generator using mod '15' system
                        print("TYPE ROLL TO GET THE BALL GOING.\n");
                        ns = await input();
                        k = 0;
                        d = 0;
                        for (i = 1; i <= 20; i++) {
                            x = Math.floor(Math.random() * 100);
                            for (j = 1; j <= 10; j++)
                                if (x < 15 * j)
                                    break;
                            c[15 * j - x] = 1;
                        }
                        // Pin diagram
                        print("PLAYER: " + p + " FRAME: " + f + " BALL: " + b + "\n");
                        print("\n");
                        for (i = 0; i <= 3; i++) {
                            str = "";
                            for (j = 1; j <= 4 - i; j++) {
                                k++;
                                while (str.length < i)
                                    str += " ";
                                if (c[k] == 1)
                                    str += "O ";
                                else
                                    str += "+ ";
                            }
                            print(str + "\n");
                        }
                        // Roll analysis
                        for (i = 1; i <= 10; i++)
                            d += c[i];
                        if (d - m == 0)
                            print("GUTTER!!\n");
                        if (b == 1 && d == 10) {
                            print("STRIKE!!!!!\n");
                            q = 3;
                        }
                        if (b == 2 && d == 10) {
                            print("SPARE!!!!\n");
                            q = 2;
                        }
                        if (b == 2 && d < 10) {
                            print("ERROR!!!\n");
                            q = 1;
                        }
                        if (b == 1 && d < 10) {
                            print("ROLL YOUR 2ND BALL\n");
                        }
                        // Storage of the scores
                        print("\n");
                        a[f * p][b] = d;
                        if (b != 2) {
                            b = 2;
                            m = d;
                            if (q == 3) {
                                a[f * p][b] = d;
                            } else {
                                a[f * p][b] = d - m;
                                if (q == 0) // ROLL
                                    continue;
                            }
                        }
                        break;
                    }
                    a[f * p][3] = q;
                }
            } while (++f < 11) ;
            print("FRAMES\n");
            for (i = 1; i <= 10; i++)
                print(" " + i + " ");
            print("\n");
            for (p = 1; p <= r; p++) {
                for (i = 1; i <= 3; i++) {
                    for (j = 1; j <= 10; j++) {
                        print(" " + a[j * p][i] + " ");
                    }
                    print("\n");
                }
                print("\n");
            }
            print("DO YOU WANT ANOTHER GAME");
            str = await input();
            if (str.substr(0, 1) != "Y")
                break;
            // Bug in original game, jumps to 2610, without restarting P variable
        }
    }
    
    main();
    
    
    ================================================
    FILE: 14_Bowling/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 14_Bowling/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 14_Bowling/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    ###Bowling program in Perl
    
    Run normally, this is a fairly faithful translation of the Basic game.
    The only real differences are a few trivial fix-ups on the prints to make it
    look better, and the player/frame/ball line was put before the "get the ball
    going" line to make it more obvious who's turn it is.
    
    However, if you run it with "-a" on the command line, it will go into
    "advanced" mode, which means that "." is used to show pin down and "!" for
    pin up, current running scores are shown at the end of each frame, and the
    scoring also looks more normal at the end. This is all done because I think it
    looks better and I wanted to see a score. Having a flag says you can play
    whichever version of the game you like.
    
    Note, the original code doesn't do the 10th frame correctly, in that it will
    never do more than 2 balls, so the best score you can get is a 290.
    This is true in both modes. That being said, it will always give you a mediocre
    game; I don't think I've ever seen a score over 140.
    
    
    ================================================
    FILE: 14_Bowling/perl/bowling.pl
    ================================================
    #!/usr/bin/perl
    
    # Bowling program in Perl
    #   Run normally, this is a fairly faithful translation of the Basic game.
    #   The only real differences are a few trivial fix-ups on the prints to make it
    #   look better, and the player/frame/ball line was put before the "get the ball
    #   going" line to make it more obvious who's turn it is.
    #
    #   However, if you run it with "-a" on the command line, it will go into
    #   'advanced' mode, which means that "." is used to show pin down and "!" for
    #   pin up, current running scores are shown at the end of each frame, and the
    #   scoring also looks more normal at the end. This is all done because I think it
    #   looks better and I wanted to see a score. Having a flag says you can play
    #   whichever version of the game you like.
    #
    #   Note, the original code doesn't do the 10th frame correctly, in that it will
    #   never do more than 2 balls, so the best score you can get is a 290.
    #   This is true in both modes. That being said, it will always give you a mediocre game.
    # Translated by Kevin Brannen (kbrannen)
    
    use strict;
    use warnings;
    
    print "\n";
    print " " x 34, "BASKETBALL\n";
    print " " x 15, "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n\n\n";
    
    # globals
    my @C;              # pin position matraix?
    my @Scores;         # scores: [player num][frame][ball] # ball3 is end-result, ball4 is frame score (advanced mode)
    my $Answer;         # get answers
    my $Num_players;    # number of players
    my $Advanced = 0;   # flag, 1 to use advanced code, 0 for original code
    my $char_down = '0'; # char to show pin is down
    my $char_up = '+';   # char to show pin is standing
    
    if ($ARGV[0] && $ARGV[0] eq "-a")
    {
        shift;
        $Advanced = 1;
        $char_down = '.';
        $char_up = '!';
    }
    
    print "WELCOME TO THE ALLEY\n";
    print "BRING YOUR FRIENDS\n";
    print "OKAY LET'S FIRST GET ACQUAINTED\n\n";
    print "SEE THE INSTRUCTIONS (Y/N): ";
    chomp($Answer = uc(<>));
    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;
    }
    
    
    ================================================
    FILE: 14_Bowling/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 14_Bowling/python/bowling.py
    ================================================
    import random
    from typing import List
    
    
    def simulate_roll(pins: List[int]) -> None:
        for _ in range(20):
            x = random.randint(0, 14)
            if x < len(pins):
                pins[x] = 1
    
    
    def calculate_score(rolls: List[int]) -> int:
        score = 0
        frame = 1
        b = 1
        for index, pins in enumerate(rolls):
            score += pins
            if b == 1:
                if pins == 10:  # strike
                    score += sum(rolls[index + 1 : index + 3])
                    frame += 1
                else:
                    b = 2
            else:
                if sum(rolls[index - 1 : index + 1]) == 10:  # spare
                    score += rolls[index + 1]
                b = 1
                frame += 1
            if frame > 10:
                break
    
        return score
    
    
    class Player:
        def __init__(self, name: str) -> None:
            self.name = name
            self.rolls: List[int] = []
    
        def play_frame(self, frame: int) -> None:
            extra = 0
            prev_score = 0
            pins = [0] * 10  # reset the pins
            for ball in range(2):
                simulate_roll(pins)
                score = sum(pins)
                self.show(pins)
                pin_count = score - prev_score
                self.rolls.append(pin_count)  # log the number of pins toppled this roll
                print(f"{pin_count} for {self.name}")
                if score - prev_score == 0:
                    print("GUTTER!!!")
                if ball == 0:
                    if score == 10:
                        print("STRIKE!!!")
                        extra = 2
                        break  # cannot roll more than once in a frame
                    else:
                        print(f"next roll {self.name}")
                elif score == 10:
                    print("SPARE!")
                    extra = 1
    
                prev_score = score  # remember previous pins to distinguish ...
            if frame == 9 and extra > 0:
                print(f"Extra rolls for {self.name}")
                pins = [0] * 10  # reset the pins
                score = 0
                for _ball in range(extra):
                    if score == 10:
                        pins = [0] * 10
                    simulate_roll(pins)
                    score = sum(pins)
                    self.rolls.append(score)
    
        def __str__(self) -> str:
            return f"{self.name}: {self.rolls}, total:{calculate_score(self.rolls)}"
    
        def show(self, pins: List[int]) -> None:
            pins_iter = iter(pins)
            print()
            for row in range(4):
                print(" " * row, end="")
                for _ in range(4 - row):
                    p = next(pins_iter)
                    print("O " if p else "+ ", end="")
                print()
    
    
    def centre_text(text: str, width: int) -> str:
        t = len(text)
        return (" " * ((width - t) // 2)) + text
    
    
    def main() -> None:
        print(centre_text("Bowl", 80))
        print(centre_text("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY", 80))
        print()
        print("WELCOME TO THE ALLEY.")
        print("BRING YOUR FRIENDS.")
        print("OKAY LET'S FIRST GET ACQUAINTED.")
    
        while True:
            print()
            if input("THE INSTRUCTIONS (Y/N)? ") in "yY":
                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.")
    
            total_players = int(input("FIRST OF ALL...HOW MANY ARE PLAYING? "))
            print()
            print("VERY GOOD...")
            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)
    
            for player in player_names:
                print(player)
    
            if input("DO YOU WANT ANOTHER GAME? ") not in "yY":
                break
    
    
    if __name__ == "__main__":
        main()
    
    
    ############################################################################################
    #
    # This is a fairly straight conversion to python with some exceptions.
    # I have kept most of the upper case text that the program prints.
    # I have added the feature of giving names to players.
    # I have added a Player class to store player data in.
    # This last change works around the problems in the original storing data in a matrix.
    # The original had bugs in calculating indexes which meant that the program
    # would overwrite data in the matrix, so the results printed out contained errors.
    # The last change is to do with the strict rules which allow extra rolls if the player
    # scores a spare or strike in the last frame.
    # This program allows these extra rolls and also calculates the proper score.
    #
    ############################################################################################
    
    
    ================================================
    FILE: 14_Bowling/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 14_Bowling/vbnet/Bowling.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Bowling", "Bowling.vbproj", "{DBEB424A-1538-4F14-BA57-BA4E326EF4D8}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{DBEB424A-1538-4F14-BA57-BA4E326EF4D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{DBEB424A-1538-4F14-BA57-BA4E326EF4D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{DBEB424A-1538-4F14-BA57-BA4E326EF4D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{DBEB424A-1538-4F14-BA57-BA4E326EF4D8}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 14_Bowling/vbnet/Bowling.vbproj
    ================================================
    
      
        Exe
        Bowling
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 14_Bowling/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 15_Boxing/README.md
    ================================================
    ### Boxing
    
    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 of three rounds wins.
    
    Jesse Lynch of St. Paul, Minnesota created this program.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=28)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=43)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### 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)
    
    
    ================================================
    FILE: 15_Boxing/boxing.bas
    ================================================
    1 PRINT TAB(33);"BOXING"
    2 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    3 PRINT:PRINT:PRINT
    4 PRINT "BOXING OLYMPIC STYLE (3 ROUNDS -- 2 OUT OF 3 WINS)"
    5 J=0
    6 L=0
    8 PRINT
    10 PRINT "WHAT IS YOUR OPPONENT'S NAME";
    20 INPUT J$
    30 PRINT "INPUT YOUR MAN'S NAME";
    40 INPUT L$
    50 PRINT "DIFFERENT PUNCHES ARE: (1) FULL SWING; (2) HOOK; (3) UPPERCUT; (4) JAB."
    60 PRINT "WHAT IS YOUR MANS BEST";
    64 INPUT B
    70 PRINT "WHAT IS HIS VULNERABILITY";
    80 INPUT D
    90 B1=INT(4*RND(1)+1)
    100 D1=INT(4*RND(1)+1)
    110 IF B1=D1 THEN 90
    120 PRINT J$;"'S ADVANTAGE IS";B1;"AND VULNERABILITY IS SECRET.":PRINT
    130 FOR R=1 TO 3
    140 IF J>= 2 THEN 1040
    150 IF L>=2 THEN 1060
    160 X=0
    170 Y=0
    180 PRINT "ROUND";R;"BEGINS..."
    185 FOR R1= 1 TO 7
    190 I=INT(10*RND(1)+1)
    200 IF I>5 THEN 600
    210 PRINT L$;"'S PUNCH";
    220 INPUT P
    221 IF P=B THEN 225
    222 GOTO 230
    225 X=X+2
    230 IF P=1 THEN 340
    240 IF P=2 THEN 450
    250 IF P=3 THEN 520
    270 PRINT L$;" JABS AT ";J$"'S HEAD ";
    271 IF D1=4 THEN 290
    275 C=INT(8*RND(1)+1)
    280 IF C<4 THEN 310
    290 X=X+3
    300 GOTO 950
    310 PRINT "IT'S BLOCKED."
    330 GOTO 950
    340 PRINT L$ " SWINGS AND ";
    341 IF D1=4 THEN 410
    345 X3=INT(30*RND(1)+1)
    350 IF X3<10 THEN 410
    360 PRINT "HE MISSES ";
    370 PRINT
    375 IF X=1 THEN 950
    380 PRINT
    390 PRINT
    400 GOTO 300
    410 PRINT "HE CONNECTS!"
    420 IF X>35 THEN 980
    425 X=X+15
    440 GOTO 300
    450 PRINT L$;" GIVES THE HOOK... ";
    455 IF D1=2 THEN 480
    460 H1=INT(2*RND(1)+1)
    470 IF H1=1 THEN 500
    475 PRINT "CONNECTS..."
    480 X=X+7
    490 GOTO 300
    500 PRINT "BUT IT'S BLOCKED!!!!!!!!!!!!!"
    510 GOTO 300
    520 PRINT L$ " TRIES AN UPPERCUT ";
    530 IF D1=3 THEN 570
    540 D5=INT(100*RND(1)+1)
    550 IF D5<51 THEN 570
    560 PRINT "AND IT'S BLOCKED (LUCKY BLOCK!)"
    565 GOTO 300
    570 PRINT "AND HE CONNECTS!"
    580 X=X+4
    590 GOTO 300
    600 J7=INT(4*RND(1)+1)
    601 IF J7 =B1 THEN 605
    602 GOTO 610
    605 Y=Y+2
    610 IF J7=1 THEN 720
    620 IF J7=2 THEN 810
    630 IF J7 =3 THEN 860
    640 PRINT J$;" JABS AND ";
    645 IF D=4 THEN 700
    650 Z4=INT(7*RND(1)+1)
    655 IF Z4>4 THEN 690
    660 PRINT "IT'S BLOCKED!"
    670 GOTO 300
    690 PRINT " BLOOD SPILLS !!!"
    700 Y=Y+5
    710 GOTO 300
    720 PRINT J$" TAKES A FULL SWING AND";
    730 IF D=1 THEN 770
    740 R6=INT(60*RND(1)+1)
    745 IF R6 <30 THEN 770
    750 PRINT " IT'S BLOCKED!"
    760 GOTO 300
    770 PRINT " POW!!!!! HE HITS HIM RIGHT IN THE FACE!"
    780 IF Y>35 THEN 1010
    790 Y=Y+15
    800 GOTO 300
    810 PRINT J$;" GETS ";L$;" IN THE JAW (OUCH!)"
    820 Y=Y+7
    830 PRINT "....AND AGAIN!"
    835 Y=Y+5
    840 IF Y>35 THEN 1010
    850 PRINT
    860 PRINT L$;" IS ATTACKED BY AN UPPERCUT (OH,OH)..."
    865 IF D=3 THEN 890
    870 Q4=INT(200*RND(1)+1)
    880 IF Q4>75 THEN 920
    890 PRINT "AND ";J$;" CONNECTS..."
    900 Y=Y+8
    910 GOTO 300
    920 PRINT " BLOCKS AND HITS ";J$;" WITH A HOOK."
    930 X=X+5
    940 GOTO 300
    950 NEXT R1
    951 IF X>Y THEN 955
    952 PRINT:PRINT J$" WINS ROUND" R
    953 J=J+1
    954 GOTO 960
    955 PRINT:PRINT L$" WINS ROUND"R
    956 L=L+1
    960 NEXT R
    961 IF J>= 2 THEN 1040
    962 IF L>=2 THEN 1060
    980 PRINT J$ " IS KNOCKED COLD AND " L$" IS THE WINNER AND CHAMP!";
    1000 GOTO 1080
    1010 PRINT L$ " IS KNOCKED COLD AND " J$" IS THE WINNER AND CHAMP!";
    1030 GOTO 1000
    1040 PRINT J$ " WINS (NICE GOING," J$;")."
    1050 GOTO 1000
    1060 PRINT L$ " AMAZINGLY WINS!!"
    1070 GOTO 1000
    1080 PRINT
    1085 PRINT
    1090 PRINT "AND NOW GOODBYE FROM THE OLYMPIC ARENA."
    1100 PRINT
    1110 END
    
    
    ================================================
    FILE: 15_Boxing/csharp/AttackStrategy.cs
    ================================================
    namespace Boxing;
    
    public abstract class AttackStrategy
    {
        protected const int KnockoutDamageThreshold = 35;
        protected readonly Boxer Other;
        protected readonly Stack Work;
        private readonly Action _notifyGameEnded;
    
        public AttackStrategy(Boxer other, Stack work, Action notifyGameEnded)
        {
            Other = other;
            Work = work;
            _notifyGameEnded = notifyGameEnded;
        }
    
        public void Attack()
        {
            var punch = GetPunch();
            if (punch.IsBestPunch)
            {
                Other.DamageTaken += 2;
            }
    
            Work.Push(punch.Punch switch
            {
                Punch.FullSwing => FullSwing,
                Punch.Hook => Hook,
                Punch.Uppercut => Uppercut,
                _ => Jab
            });
        }
    
        protected abstract AttackPunch GetPunch();
        protected abstract void FullSwing();
        protected abstract void Hook();
        protected abstract void Uppercut();
        protected abstract void Jab();
    
        protected void RegisterKnockout(string knockoutMessage)
        {
            Work.Clear();
            _notifyGameEnded();
            Console.WriteLine(knockoutMessage);
        }
    
        protected record AttackPunch(Punch Punch, bool IsBestPunch);
    }
    
    
    ================================================
    FILE: 15_Boxing/csharp/Boxer.cs
    ================================================
    namespace Boxing;
    
    public class Boxer
    {
        private int _wins;
    
        private string Name { get; set; } = string.Empty;
    
        public Punch BestPunch { get; set; }
    
        public Punch Vulnerability { get; set; }
    
        public void SetName(string prompt)
        {
            Console.WriteLine(prompt);
            string? name;
            do
            {
                name = Console.ReadLine();
            } while (string.IsNullOrWhiteSpace(name));
            Name = name;
        }
    
        public int DamageTaken { get; set; }
    
        public void ResetForNewRound() => DamageTaken = 0;
    
        public void RecordWin() => _wins += 1;
    
        public bool IsWinner => _wins >= 2;
    
        public override string ToString() => Name;
    }
    
    public class Opponent : Boxer
    {
        public void SetRandomPunches()
        {
            do
            {
                BestPunch = (Punch) GameUtils.Roll(4); // B1
                Vulnerability = (Punch) GameUtils.Roll(4); // D1
            } while (BestPunch == Vulnerability);
        }
    }
    
    
    ================================================
    FILE: 15_Boxing/csharp/Boxing.csproj
    ================================================
    
    
      
        Exe
        net6.0
        enable
        enable
      
    
    
    
    
    ================================================
    FILE: 15_Boxing/csharp/Boxing.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Boxing", "Boxing.csproj", "{52A7BDE5-3085-4F58-AC57-2BA4E65212D8}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{52A7BDE5-3085-4F58-AC57-2BA4E65212D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{52A7BDE5-3085-4F58-AC57-2BA4E65212D8}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{52A7BDE5-3085-4F58-AC57-2BA4E65212D8}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{52A7BDE5-3085-4F58-AC57-2BA4E65212D8}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 15_Boxing/csharp/OpponentAttackStrategy.cs
    ================================================
    using static Boxing.GameUtils;
    using static System.Console;
    
    namespace Boxing;
    
    public class OpponentAttackStrategy : AttackStrategy
    {
        private readonly Opponent _opponent;
    
        public OpponentAttackStrategy(Opponent opponent, Boxer player,  Action notifyGameEnded, Stack work) : base(player, work, notifyGameEnded)
        {
            _opponent = opponent;
        }
    
        protected override AttackPunch GetPunch()
        {
            var punch = (Punch)Roll(4);
            return new AttackPunch(punch, punch == _opponent.BestPunch);
        }
    
        protected override void FullSwing() // 720
        {
            Write($"{_opponent}  TAKES A FULL SWING AND");
            if (Other.Vulnerability == Punch.FullSwing)
            {
                ScoreFullSwing();
            }
            else
            {
                if (RollSatisfies(60, x => x < 30))
                {
                    WriteLine(" IT'S BLOCKED!");
                }
                else
                {
                    ScoreFullSwing();
                }
            }
    
            void ScoreFullSwing()
            {
                WriteLine(" POW!!!!! HE HITS HIM RIGHT IN THE FACE!");
                if (Other.DamageTaken > KnockoutDamageThreshold)
                {
                    Work.Push(RegisterOtherKnockedOut);
                }
                Other.DamageTaken += 15;
            }
        }
    
        protected override void Hook() // 810
        {
            Write($"{_opponent} GETS {Other} IN THE JAW (OUCH!)");
            Other.DamageTaken += 7;
            WriteLine("....AND AGAIN!");
            Other.DamageTaken += 5;
            if (Other.DamageTaken > KnockoutDamageThreshold)
            {
                Work.Push(RegisterOtherKnockedOut);
            }
        }
    
        protected override void Uppercut() // 860
        {
            Write($"{Other} IS ATTACKED BY AN UPPERCUT (OH,OH)...");
            if (Other.Vulnerability == Punch.Uppercut)
            {
                ScoreUppercut();
            }
            else
            {
                if (RollSatisfies(200, x => x > 75))
                {
                    WriteLine($" BLOCKS AND HITS {_opponent} WITH A HOOK.");
                    _opponent.DamageTaken += 5;
                }
                else
                {
                    ScoreUppercut();
                }
            }
    
            void ScoreUppercut()
            {
                WriteLine($"AND {_opponent} CONNECTS...");
                Other.DamageTaken += 8;
            }
        }
    
        protected override void Jab() // 640
        {
            Write($"{_opponent}  JABS AND ");
            if (Other.Vulnerability == Punch.Jab)
            {
                ScoreJab();
            }
            else
            {
                if (RollSatisfies(7, x => x > 4))
                {
                    WriteLine("BLOOD SPILLS !!!");
                    ScoreJab();
                }
                else
                {
                    WriteLine("IT'S BLOCKED!");
                }
            }
    
            void ScoreJab() => Other.DamageTaken += 5;
        }
    
        private void RegisterOtherKnockedOut()
            => RegisterKnockout($"{Other} IS KNOCKED COLD AND {_opponent} IS THE WINNER AND CHAMP!");
    }
    
    
    ================================================
    FILE: 15_Boxing/csharp/PlayerAttackStrategy.cs
    ================================================
    using static Boxing.GameUtils;
    using static System.Console;
    namespace Boxing;
    
    public class PlayerAttackStrategy : AttackStrategy
    {
        private readonly Boxer _player;
    
        public PlayerAttackStrategy(Boxer player, Opponent opponent, Action notifyGameEnded, Stack work)
            : base(opponent, work, notifyGameEnded) => _player = player;
    
        protected override AttackPunch GetPunch()
        {
            var punch = GameUtils.GetPunch($"{_player}'S PUNCH");
            return new AttackPunch(punch, punch == _player.BestPunch);
        }
    
        protected override void FullSwing() // 340
        {
            Write($"{_player} SWINGS AND ");
            if (Other.Vulnerability == Punch.FullSwing)
            {
                ScoreFullSwing();
            }
            else
            {
                if (RollSatisfies(30, x => x < 10))
                {
                    ScoreFullSwing();
                }
                else
                {
                    WriteLine("HE MISSES");
                }
            }
    
            void ScoreFullSwing()
            {
                WriteLine("HE CONNECTS!");
                if (Other.DamageTaken > KnockoutDamageThreshold)
                {
                    Work.Push(() => RegisterKnockout($"{Other} IS KNOCKED COLD AND {_player} IS THE WINNER AND CHAMP!"));
                }
                Other.DamageTaken += 15;
            }
        }
    
        protected override void Uppercut() // 520
        {
            Write($"{_player} TRIES AN UPPERCUT ");
            if (Other.Vulnerability == Punch.Uppercut)
            {
                ScoreUpperCut();
            }
            else
            {
                if (RollSatisfies(100, x => x < 51))
                {
                    ScoreUpperCut();
                }
                else
                {
                    WriteLine("AND IT'S BLOCKED (LUCKY BLOCK!)");
                }
            }
    
            void ScoreUpperCut()
            {
                WriteLine("AND HE CONNECTS!");
                Other.DamageTaken += 4;
            }
        }
    
        protected override void Hook() // 450
        {
            Write($"{_player} GIVES THE HOOK... ");
            if (Other.Vulnerability == Punch.Hook)
            {
                ScoreHookOnOpponent();
            }
            else
            {
                if (RollSatisfies(2, x => x == 1))
                {
                    WriteLine("BUT IT'S BLOCKED!!!!!!!!!!!!!");
                }
                else
                {
                    ScoreHookOnOpponent();
                }
            }
    
            void ScoreHookOnOpponent()
            {
                WriteLine("CONNECTS...");
                Other.DamageTaken += 7;
            }
        }
    
        protected override void Jab()
        {
            WriteLine($"{_player} JABS AT {Other}'S HEAD");
            if (Other.Vulnerability == Punch.Jab)
            {
                ScoreJabOnOpponent();
            }
            else
            {
                if (RollSatisfies(8, x => x < 4))
                {
                    WriteLine("IT'S BLOCKED.");
                }
                else
                {
                    ScoreJabOnOpponent();
                }
            }
    
            void ScoreJabOnOpponent() => Other.DamageTaken += 3;
        }
    }
    
    
    ================================================
    FILE: 15_Boxing/csharp/Program.cs
    ================================================
    using Boxing;
    using static Boxing.GameUtils;
    using static System.Console;
    
    WriteLine(new string('\t', 33) + "BOXING");
    WriteLine(new string('\t', 15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
    WriteLine("{0}{0}{0}BOXING OLYMPIC STYLE (3 ROUNDS -- 2 OUT OF 3 WINS){0}", Environment.NewLine);
    
    var opponent = new Opponent();
    opponent.SetName("WHAT IS YOUR OPPONENT'S NAME"); // J$
    var player = new Boxer();
    player.SetName("INPUT YOUR MAN'S NAME"); // L$
    
    PrintPunchDescription();
    player.BestPunch = GetPunch("WHAT IS YOUR MANS BEST"); // B
    player.Vulnerability = GetPunch("WHAT IS HIS VULNERABILITY"); // D
    opponent.SetRandomPunches();
    WriteLine($"{opponent}'S ADVANTAGE IS {opponent.BestPunch.ToFriendlyString()} AND VULNERABILITY IS SECRET.");
    
    
    for (var i = 1; i <= 3; i ++) // R
    {
        var round = new Round(player, opponent, i);
        round.Start();
        round.CheckOpponentWin();
        round.CheckPlayerWin();
        if (round.GameEnded) break;
    }
    WriteLine("{0}{0}AND NOW GOODBYE FROM THE OLYMPIC ARENA.{0}", Environment.NewLine);
    
    
    ================================================
    FILE: 15_Boxing/csharp/Punch.cs
    ================================================
    namespace Boxing;
    
    public enum Punch
    {
        FullSwing = 1,
        Hook = 2,
        Uppercut = 3,
        Jab = 4
    }
    
    
    ================================================
    FILE: 15_Boxing/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 15_Boxing/csharp/Round.cs
    ================================================
    namespace Boxing;
    
    class Round
    {
    
        private readonly Boxer _player;
        private readonly Boxer _opponent;
        private readonly int _round;
        private Stack _work = new();
        private readonly PlayerAttackStrategy _playerAttackStrategy;
        private readonly OpponentAttackStrategy _opponentAttackStrategy;
    
        public bool GameEnded { get; private set; }
    
        public Round(Boxer player, Opponent opponent, int round)
        {
            _player = player;
            _opponent = opponent;
            _round = round;
            _work.Push(ResetPlayers);
            _work.Push(CheckOpponentWin);
            _work.Push(CheckPlayerWin);
    
            void NotifyGameEnded() => GameEnded = true;
            _playerAttackStrategy = new PlayerAttackStrategy(player, opponent, NotifyGameEnded, _work);
            _opponentAttackStrategy = new OpponentAttackStrategy(opponent, player, NotifyGameEnded, _work);
        }
    
        public void Start()
        {
            while (_work.Count > 0)
            {
                var action = _work.Pop();
                // This delay does not exist in the VB code but it makes a bit easier to follow the game.
                // I assume the computers at the time were slow enough
                // so that they did not need this delay...
                Thread.Sleep(300);
                action();
            }
        }
    
        public void CheckOpponentWin()
        {
            if (_opponent.IsWinner)
            {
                Console.WriteLine($"{_opponent} WINS (NICE GOING, {_opponent}).");
                GameEnded = true;
            }
        }
    
        public void CheckPlayerWin()
        {
            if (_player.IsWinner)
            {
                Console.WriteLine($"{_player}  AMAZINGLY WINS!!");
                GameEnded = true;
            }
        }
    
        private void ResetPlayers()
        {
            _player.ResetForNewRound();
            _opponent.ResetForNewRound();
            _work.Push(RoundBegins);
        }
    
        private void RoundBegins()
        {
            Console.WriteLine();
            Console.WriteLine($"ROUND {_round} BEGINS...");
            _work.Push(CheckRoundWinner);
            for (var i = 0; i < 7; i++)
            {
                _work.Push(DecideWhoAttacks);
            }
        }
    
        private void CheckRoundWinner()
        {
            if (_opponent.DamageTaken > _player.DamageTaken)
            {
                Console.WriteLine($"{_player} WINS ROUND {_round}");
                _player.RecordWin();
            }
            else
            {
                Console.WriteLine($"{_opponent} WINS ROUND {_round}");
                _opponent.RecordWin();
            }
        }
    
        private void DecideWhoAttacks()
        {
            _work.Push( GameUtils.RollSatisfies(10, x => x > 5) ? _opponentAttackStrategy.Attack : _playerAttackStrategy.Attack );
        }
    }
    
    
    ================================================
    FILE: 15_Boxing/csharp/Utils.cs
    ================================================
    namespace Boxing;
    public static class GameUtils
    {
        private static readonly Random Rnd = new((int) DateTime.UtcNow.Ticks);
        public static void PrintPunchDescription() =>
            Console.WriteLine($"DIFFERENT PUNCHES ARE: {PunchDesc(Punch.FullSwing)}; {PunchDesc(Punch.Hook)}; {PunchDesc(Punch.Uppercut)}; {PunchDesc(Punch.Jab)}.");
    
        private static string PunchDesc(Punch punch) => $"({(int)punch}) {punch.ToFriendlyString()}";
    
        public static Punch GetPunch(string prompt)
        {
            Console.WriteLine(prompt);
            Punch result;
            while (!Enum.TryParse(Console.ReadLine(), out result) || !Enum.IsDefined(typeof(Punch), result))
            {
                PrintPunchDescription();
            }
            return result;
        }
    
        public static Func Roll { get;  } =  upperLimit => (int) (upperLimit * Rnd.NextSingle()) + 1;
    
        public static bool RollSatisfies(int upperLimit, Predicate predicate) => predicate(Roll(upperLimit));
    
        public static string ToFriendlyString(this Punch punch)
            => punch switch
            {
                Punch.FullSwing => "FULL SWING",
                Punch.Hook => "HOOK",
                Punch.Uppercut => "UPPERCUT",
                Punch.Jab => "JAB",
                _ => throw new ArgumentOutOfRangeException(nameof(punch), punch, null)
            };
    
    }
    
    
    ================================================
    FILE: 15_Boxing/java/Basic.java
    ================================================
    import java.util.Scanner;
    
    /**
     * It provide some kind of BASIC language behaviour simulations.
     */
    final class Basic {
    
        public static int randomOf(int base) {
            return (int)Math.round(Math.floor(base* Math.random() + 1));
        }
    
        /**
         * The Console "simulate" the message error when input does not match with the expected type.
         * Specifically for this game if you enter an String when and int was expected.
         */
        public static class Console {
            private final Scanner input = new Scanner(System.in);
    
            public String readLine() {
                return input.nextLine();
            }
    
            public int readInt() {
                int ret = -1;
                boolean failedInput = true;
                do {
                    boolean b = input.hasNextInt();
                    if (b) {
                        ret = input.nextInt();
                        failedInput = false;
                    } else {
                        input.next(); // discard read
                        System.out.print("!NUMBER EXPECTED - RETRY INPUT LINE\n? ");
                    }
    
                } while (failedInput);
    
                return ret;
            }
    
            public void print(String message, Object... args) {
                System.out.printf(message, args);
            }
        }
    }
    
    
    ================================================
    FILE: 15_Boxing/java/Boxing.java
    ================================================
    /**
     * Boxing
     *
     * 

    * Based on the Basic game of BatNum here * https://github.com/coding-horror/basic-computer-games/tree/main/15%20Boxing *

    */ public class Boxing { private static final Basic.Console console = new Basic.Console(); private GameSession session; public void play() { showIntro(); loadPlayers(); console.print("%s'S ADVANTAGE IS %d AND VULNERABILITY IS SECRET.\n", session.getOpponent().getName(), session.getOpponent().getBestPunch().getCode()); for (int roundNro = 1; roundNro <= 3; roundNro++) { if (session.isOver()) break; session.resetPoints(); console.print("\nROUND %d BEGINS...%n", roundNro); for (int majorPunches = 1; majorPunches <= 7; majorPunches++) { long i = Basic.randomOf(10); if (i > 5) { boolean stopPunches = opponentPunch(); if (stopPunches ) break; } else { playerPunch(); } } showRoundWinner(roundNro); } showWinner(); } private boolean opponentPunch() { final Punch punch = Punch.random(); if (punch == session.getOpponent().getBestPunch()) session.addOpponentPoints(2); if (punch == Punch.FULL_SWING) { console.print("%s TAKES A FULL SWING AND", session.getOpponent().getName()); long r6 = Basic.randomOf(60); if (session.getPlayer().hitVulnerability(Punch.FULL_SWING) || r6 < 30) { console.print(" POW!!!!! HE HITS HIM RIGHT IN THE FACE!\n"); if (session.getPoints(session.getOpponent()) > 35) { session.setKnocked(); return true; } session.addOpponentPoints(15); } else { console.print(" IT'S BLOCKED!\n"); } } if (punch == Punch.HOOK || punch == Punch.UPPERCUT) { if (punch == Punch.HOOK) { console.print("%s GETS %s IN THE JAW (OUCH!)\n", session.getOpponent().getName(), session.getPlayer().getName()); session.addOpponentPoints(7); console.print("....AND AGAIN!\n"); session.addOpponentPoints(5); if (session.getPoints(session.getOpponent()) > 35) { session.setKnocked(); return true; } console.print("\n"); } console.print("%s IS ATTACKED BY AN UPPERCUT (OH,OH)...\n", session.getPlayer().getName()); long q4 = Basic.randomOf(200); if (session.getPlayer().hitVulnerability(Punch.UPPERCUT) || q4 <= 75) { console.print("AND %s CONNECTS...\n", session.getOpponent().getName()); session.addOpponentPoints(8); } else { console.print(" BLOCKS AND HITS %s WITH A HOOK.\n", session.getOpponent().getName()); session.addPlayerPoints(5); } } else { console.print("%s JABS AND ", session.getOpponent().getName()); long z4 = Basic.randomOf(7); if (session.getPlayer().hitVulnerability(Punch.JAB)) session.addOpponentPoints(5); else if (z4 > 4) { console.print(" BLOOD SPILLS !!!\n"); session.addOpponentPoints(5); } else { console.print("IT'S BLOCKED!\n"); } } return true; } private void playerPunch() { console.print("%s'S PUNCH? ", session.getPlayer().getName()); final Punch punch = Punch.fromCode(console.readInt()); if (punch == session.getPlayer().getBestPunch()) session.addPlayerPoints(2); switch (punch) { case FULL_SWING -> { console.print("%s SWINGS AND ", session.getPlayer().getName()); if (session.getOpponent().getBestPunch() == Punch.JAB) { console.print("HE CONNECTS!\n"); if (session.getPoints(session.getPlayer()) <= 35) session.addPlayerPoints(15); } else { long x3 = Basic.randomOf(30); if (x3 < 10) { console.print("HE CONNECTS!\n"); if (session.getPoints(session.getPlayer()) <= 35) session.addPlayerPoints(15); } else { console.print("HE MISSES \n"); if (session.getPoints(session.getPlayer()) != 1) console.print("\n\n"); } } } case HOOK -> { console.print("\n%s GIVES THE HOOK... ", session.getPlayer().getName()); long h1 = Basic.randomOf(2); if (session.getOpponent().getBestPunch() == Punch.HOOK) { session.addPlayerPoints(7); } else if (h1 == 1) { console.print("BUT IT'S BLOCKED!!!!!!!!!!!!!\n"); } else { console.print("CONNECTS...\n"); session.addPlayerPoints(7); } } case UPPERCUT -> { console.print("\n%s TRIES AN UPPERCUT ", session.getPlayer().getName()); long d5 = Basic.randomOf(100); if (session.getOpponent().getBestPunch() == Punch.UPPERCUT || d5 < 51) { console.print("AND HE CONNECTS!\n"); session.addPlayerPoints(4); } else { console.print("AND IT'S BLOCKED (LUCKY BLOCK!)\n"); } } default -> { console.print("%s JABS AT %s'S HEAD \n", session.getPlayer().getName(), session.getOpponent().getName()); if (session.getOpponent().getBestPunch() == Punch.JAB) { session.addPlayerPoints(3); } else { long c = Basic.randomOf(8); if (c < 4) { console.print("IT'S BLOCKED.\n"); } else { session.addPlayerPoints(3); } } } } } private void showRoundWinner(int roundNro) { if (session.isRoundWinner(session.getPlayer())) { console.print("\n %s WINS ROUND %d\n", session.getPlayer().getName(), roundNro); session.addRoundWind(session.getPlayer()); } else { console.print("\n %s WINS ROUND %d\n", session.getOpponent().getName(), roundNro); session.addRoundWind(session.getOpponent()); } } private void showWinner() { if (session.isGameWinner(session.getOpponent())) { console.print("%s WINS (NICE GOING, " + session.getOpponent().getName() + ").", session.getOpponent().getName()); } else if (session.isGameWinner(session.getPlayer())) { console.print("%s AMAZINGLY WINS!!", session.getPlayer().getName()); } else if (session.isPlayerKnocked()) { console.print("%s IS KNOCKED COLD AND %s IS THE WINNER AND CHAMP!", session.getPlayer().getName(), session.getOpponent().getName()); } else { console.print("%s IS KNOCKED COLD AND %s IS THE WINNER AND CHAMP!", session.getOpponent().getName(), session.getPlayer().getName()); } console.print("\n\nAND NOW GOODBYE FROM THE OLYMPIC ARENA.\n"); } private void loadPlayers() { console.print("WHAT IS YOUR OPPONENT'S NAME? "); final String opponentName = console.readLine(); console.print("INPUT YOUR MAN'S NAME? "); final String playerName = console.readLine(); console.print("DIFFERENT PUNCHES ARE: (1) FULL SWING; (2) HOOK; (3) UPPERCUT; (4) JAB.\n"); console.print("WHAT IS YOUR MANS BEST? "); final int b = console.readInt(); console.print("WHAT IS HIS VULNERABILITY? "); final int d = console.readInt(); final Player player = new Player(playerName, Punch.fromCode(b), Punch.fromCode(d)); final Player opponent = new Player(opponentName); session = new GameSession(player, opponent); } private void showIntro () { console.print(" BOXING\n"); console.print(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n"); console.print("BOXING OLYMPIC STYLE (3 ROUNDS -- 2 OUT OF 3 WINS)\n\n"); } } ================================================ FILE: 15_Boxing/java/BoxingGame.java ================================================ public class BoxingGame { public static void main(String[] args) { new Boxing().play(); } } ================================================ FILE: 15_Boxing/java/GameSession.java ================================================ /** * Game Session * The session store the state of the game */ public class GameSession { private final Player player; private final Player opponent; private int opponentRoundWins = 0; private int playerRoundWins = 0; int playerPoints = 0; int opponentPoints = 0; boolean knocked = false; GameSession(Player player, Player opponent) { this.player = player; this.opponent = opponent; } public Player getPlayer() { return player;} public Player getOpponent() { return opponent;} public void setKnocked() { knocked = true; } public void resetPoints() { playerPoints = 0; opponentPoints = 0; } public void addPlayerPoints(int ptos) { playerPoints+=ptos;} public void addOpponentPoints(int ptos) { opponentPoints+=ptos;} public int getPoints(Player player) { if(player.isPlayer()) return playerPoints; else return opponentPoints; } public void addRoundWind(Player player) { if(player.isPlayer()) playerRoundWins++; else opponentRoundWins++; } public boolean isOver() { return (opponentRoundWins >= 2 || playerRoundWins >= 2); } public boolean isRoundWinner(Player player) { if (player.isPlayer()) return playerPoints > opponentPoints; else return opponentPoints > playerPoints; } public boolean isGameWinner(Player player) { if (player.isPlayer()) return playerRoundWins > 2; else return opponentRoundWins > 2; } public boolean isPlayerKnocked() { return knocked; } } ================================================ FILE: 15_Boxing/java/Player.java ================================================ /** * The Player class model the user and compuer player */ public class Player { private final String name; private final Punch bestPunch; private final Punch vulnerability; private boolean isPlayer = false; public Player(String name, Punch bestPunch, Punch vulnerability) { this.name = name; this.bestPunch = bestPunch; this.vulnerability = vulnerability; this.isPlayer = true; } /** * Player with random Best Punch and Vulnerability */ public Player(String name) { this.name = name; int b1; int d1; do { b1 = Basic.randomOf(4); d1 = Basic.randomOf(4); } while (b1 == d1); this.bestPunch = Punch.fromCode(b1); this.vulnerability = Punch.fromCode(d1); } public boolean isPlayer() { return isPlayer; } public String getName() { return name; } public Punch getBestPunch() { return bestPunch; } public boolean hitVulnerability(Punch punch) { return vulnerability == punch; } } ================================================ FILE: 15_Boxing/java/Punch.java ================================================ import java.util.Arrays; /** * Types of Punches */ public enum Punch { FULL_SWING(1), HOOK(2), UPPERCUT(3), JAB(4); private final int code; Punch(int code) { this.code = code; } int getCode() { return code;} public static Punch fromCode(int code) { return Arrays.stream(Punch.values()).filter(p->p.code == code).findAny().orElse(null); } public static Punch random() { return Punch.fromCode(Basic.randomOf(4)); } } ================================================ FILE: 15_Boxing/java/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Oracle Java](https://openjdk.java.net/) ================================================ FILE: 15_Boxing/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 15_Boxing/javascript/boxing.html ================================================ BOXING

    
    
    
    
    
    
    ================================================
    FILE: 15_Boxing/javascript/boxing.js
    ================================================
    // BOWLING
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    // Main program
    async function main()
    {
        print(tab(33) + "BOXING\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        print("BOXING OLYMPIC STYLE (3 ROUNDS -- 2 OUT OF 3 WINS)\n");
        j = 0;
        l = 0;
        print("\n");
        print("WHAT IS YOUR OPPONENT'S NAME");
        js = await input();
        print("INPUT YOUR MAN'S NAME");
        ls = await input();
        print("DIFFERENT PUNCHES ARE: (1) FULL SWING; (2) HOOK; (3) UPPERCUT; (4) JAB.\n");
        print("WHAT IS YOUR MANS BEST");
        b = parseInt(await input());
        print("WHAT IS HIS VULNERABILITY");
        d = parseInt(await input());
        do {
            b1 = Math.floor(4 * Math.random() + 1);
            d1 = Math.floor(4 * Math.random() + 1);
        } while (b1 == d1) ;
        print(js + "'S ADVANTAGE IS " + b1 + " AND VULNERABILITY IS SECRET.\n");
        print("\n");
        knocked = 0;
        for (r = 1; r <= 3; r++) {
            if (j >= 2)
                break;
            if (l >= 2)
                break;
            x = 0;
            y = 0;
            print("ROUND " + r + " BEGIN...\n");
            for (r1 = 1; r1 <= 7; r1++) {
                i = Math.floor(10 * Math.random() + 1);
                if (i <= 5) {
                    print(ls + "'S PUNCH");
                    p = parseInt(await input());
                    if (p == b)
                        x += 2;
                    if (p == 1) {
                        print(ls + " SWINGS AND ");
                        x3 = Math.floor(30 * Math.random() + 1);
                        if (d1 == 4 || x3 < 10) {
                            print("HE CONNECTS!\n");
                            if (x > 35) {
                                r = 3;
                                break;
                            }
                            x += 15;
                        } else {
                            print("HE MISSES \n");
                            if (x != 1)
                                print("\n\n");
                        }
                    } else if (p == 2) {
                        print(ls + " GIVES THE HOOK... ");
                        h1 = Math.floor(2 * Math.random() + 1);
                        if (d1 == 2) {
                            x += 7;
                        } else if (h1 != 1) {
                            print("CONNECTS...\n");
                            x += 7;
                        } else {
                            print("BUT IT'S BLOCKED!!!!!!!!!!!!!\n");
                        }
                    } else if (p == 3) {
                        print(ls + " TRIES AN UPPERCUT ");
                        d5 = Math.floor(100 * Math.random() + 1);
                        if (d1 == 3 || d5 < 51) {
                            print("AND HE CONNECTS!\n");
                            x += 4;
                        } else {
                            print("AND IT'S BLOCKED (LUCKY BLOCK!)\n");
                        }
                    } else {
                        print(ls + " JABS AT " + js + "'S HEAD ");
                        c = Math.floor(8 * Math.random() + 1);
                        if (d1 == 4 || c >= 4) {
                            x += 3;
                        } else {
                            print("IT'S BLOCKED.\n");
                        }
                    }
                } else {
                    j7 = Math.random(4 * Math.random() + 1);
                    if (j7 == b1)
                        y += 2;
                    if (j7 == 1) {
                        print(js + " TAKES A FULL SWING AND");
                        r6 = Math.floor(60 * Math.random() + 1);
                        if (d == 1 || r6 < 30) {
                            print(" POW!!!!! HE HITS HIM RIGHT IN THE FACE!\n");
                            if (y > 35) {
                                knocked = 1;
                                r = 3;
                                break;
                            }
                            y += 15;
                        } else {
                            print(" IT'S BLOCKED!\n");
                        }
                    } else if (j7 == 2 || j7 == 3) {
                        if (j7 == 2) {
                            print(js + " GETS " + ls + " IN THE JAW (OUCH!)\n");
                            y += 7;
                            print("....AND AGAIN!\n");
                            y += 5;
                            if (y > 35) {
                                knocked = 1;
                                r = 3;
                                break;
                            }
                            print("\n");
                            // From original, it goes over from handling 2 to handling 3
                        }
                        print(ls + " IS ATTACKED BY AN UPPERCUT (OH,OH)...\n");
                        q4 = Math.floor(200 * Math.random() + 1);
                        if (d == 3 || q4 <= 75) {
                            print("AND " + js + " CONNECTS...\n");
                            y += 8;
                        } else {
                            print(" BLOCKS AND HITS " + js + " WITH A HOOK.\n");
                            x += 5;
                        }
                    } else {
                        print(js + " JABS AND ");
                        z4 = Math.floor(7 * Math.random() + 1);
                        if (d == 4)
                            y += 5;
                        else if (z4 > 4) {
                            print(" BLOOD SPILLS !!!\n");
                            y += 5;
                        } else {
                            print("IT'S BLOCKED!\n");
                        }
                    }
                }
            }
            if (x > y) {
                print("\n");
                print(ls + " WINS ROUND " + r + "\n");
                l++;
            } else {
                print("\n");
                print(js + " WINS ROUND " + r + "\n");
                j++;
            }
        }
        if (j >= 2) {
            print(js + " WINS (NICE GOING, " + js + ").\n");
        } else if (l >= 2) {
            print(ls + " AMAZINGLY WINS!!\n");
        } else if (knocked) {
            print(ls + " IS KNOCKED COLD AND " + js + " IS THE WINNER AND CHAMP!\n");
        } else {
            print(js + " IS KNOCKED COLD AND " + ls + " IS THE WINNER AND CHAMP!\n");
        }
        print("\n");
        print("\n");
        print("AND NOW GOODBYE FROM THE OLYMPIC ARENA.\n");
        print("\n");
    }
    
    main();
    
    
    ================================================
    FILE: 15_Boxing/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 15_Boxing/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 15_Boxing/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 15_Boxing/perl/boxing.pl
    ================================================
    #!/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";
        }
    }
    
    
    ================================================
    FILE: 15_Boxing/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 15_Boxing/python/boxing.py
    ================================================
    #!/usr/bin/env python3
    import json
    import random
    from dataclasses import dataclass
    from pathlib import Path
    from typing import Dict, Literal, NamedTuple, Tuple, cast
    
    
    class PunchProfile(NamedTuple):
        choices: int
        threshold: int
        hit_damage: int
        block_damage: int
    
        pre_msg: str
        hit_msg: str
        blocked_msg: str
    
        knockout_possible: bool = False
    
        def is_hit(self) -> bool:
            return random.randint(1, self.choices) <= self.threshold
    
    
    @dataclass
    class Player:
        name: str
        best: int  # this hit guarantees 2 damage on opponent
        weakness: int  # you're always hit when your opponent uses this punch
        is_computer: bool
    
        # for each of the 4 punch types, we have a probability of hitting
        punch_profiles: Dict[Literal[1, 2, 3, 4], PunchProfile]
    
        damage: int = 0
        score: int = 0
        knockedout: bool = False
    
        def get_punch_choice(self) -> Literal[1, 2, 3, 4]:
            if self.is_computer:
                return random.randint(1, 4)  # 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
    
    QUESTION_PROMPT = "? "
    KNOCKED_COLD = "{loser} IS KNOCKED COLD AND {winner} IS THE WINNER AND CHAMP"
    
    
    def get_vulnerability() -> int:
        print("WHAT IS HIS VULNERABILITY", end=QUESTION_PROMPT)
        return int(input())
    
    
    def get_opponent_stats() -> Tuple[int, int]:
        opponent_best = 0
        opponent_weakness = 0
        while opponent_best == opponent_weakness:
            opponent_best = random.randint(1, 4)
            opponent_weakness = random.randint(1, 4)
        return opponent_best, opponent_weakness
    
    
    def read_punch_profiles(filepath: Path) -> Dict[Literal[1, 2, 3, 4], PunchProfile]:
        with open(filepath) as f:
            punch_profile_dict = json.load(f)
        return {
            cast(Literal[1, 2, 3, 4], int(key)): PunchProfile(**value)
            for key, value in punch_profile_dict.items()
        }
    
    
    def main() -> None:
        print("BOXING")
        print("CREATIVE COMPUTING   MORRISTOWN, NEW JERSEY")
        print("\n\n")
        print("BOXING OLYMPIC STYLE (3 ROUNDS -- 2 OUT OF 3 WINS)")
    
        print("WHAT IS YOUR OPPONENT'S NAME", end=QUESTION_PROMPT)
        opponent_name = input()
        print("WHAT IS YOUR MAN'S NAME", end=QUESTION_PROMPT)
        player_name = input()
    
        print("DIFFERENT PUNCHES ARE 1 FULL SWING 2 HOOK 3 UPPERCUT 4 JAB")
        print("WHAT IS YOUR MAN'S BEST", end=QUESTION_PROMPT)
        player_best = int(input())  # noqa: TODO - this likely is a bug!
        player_weakness = get_vulnerability()
        player = Player(
            name=player_name,
            best=player_best,
            weakness=player_weakness,
            is_computer=False,
            punch_profiles=read_punch_profiles(
                Path(__file__).parent / "player-profile.json"
            ),
        )
    
        opponent_best, opponent_weakness = get_opponent_stats()
        opponent = Player(
            name=opponent_name,
            best=opponent_best,
            weakness=opponent_weakness,
            is_computer=True,
            punch_profiles=read_punch_profiles(
                Path(__file__).parent / "opponent-profile.json"
            ),
        )
    
        print(
            f"{opponent.name}'S ADVANTAGE is {opponent.weakness} AND VULNERABILITY IS SECRET."
        )
    
        for round_number in (1, 2, 3):
            play_round(round_number, player, opponent)
    
        if player.knockedout:
            print(KNOCKED_COLD.format(loser=player.name, winner=opponent.name))
        elif opponent.knockedout:
            print(KNOCKED_COLD.format(loser=opponent.name, winner=player.name))
        elif opponent.score > player.score:
            print(f"{opponent.name} WINS (NICE GOING), {player.name}")
        else:
            print(f"{player.name} AMAZINGLY WINS")
    
        print("\n\nAND NOW GOODBYE FROM THE OLYMPIC ARENA.")
    
    
    def is_opponents_turn() -> bool:
        return random.randint(1, 10) > 5
    
    
    def play_round(round_number: int, player: Player, opponent: Player) -> None:
        print(f"ROUND {round_number} BEGINS...\n")
        if opponent.score >= 2 or player.score >= 2:
            return
    
        for _action in range(7):
            if is_opponents_turn():
                punch = opponent.get_punch_choice()
                active = opponent
                passive = player
            else:
                punch = player.get_punch_choice()
                active = player
                passive = opponent
    
            # Load the hit characteristics of the current player's punch
            punch_profile = active.punch_profiles[punch]
    
            if punch == active.best:
                passive.damage += 2
    
            print(punch_profile.pre_msg.format(active=active, passive=passive), end=" ")
            if passive.weakness == punch or punch_profile.is_hit():
                print(punch_profile.hit_msg.format(active=active, passive=passive))
                if punch_profile.knockout_possible and passive.damage > KNOCKOUT_THRESHOLD:
                    passive.knockedout = True
                    break
                passive.damage += punch_profile.hit_damage
            else:
                print(punch_profile.blocked_msg.format(active=active, passive=passive))
                active.damage += punch_profile.block_damage
    
        if player.knockedout or opponent.knockedout:
            return
        elif player.damage > opponent.damage:
            print(f"{opponent.name} WINS ROUND {round_number}")
            opponent.score += 1
        else:
            print(f"{player.name} WINS ROUND {round_number}")
            player.score += 1
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 15_Boxing/python/opponent-profile.json
    ================================================
    {
        "1": {
            "choices": 60,
            "threshold": 29,
            "hit_damage": 15,
            "block_damage": 0,
            "knockout_possible": true,
            "pre_msg": "{active.name} TAKES A FULL SWING",
            "hit_msg": "AND POW!!!! HE HITS HIM RIGHT IN THE FACE!",
            "blocked_msg": "BUT IT'S BLOCKED!"
        },
        "2": {
            "choices": 1,
            "threshold": 1,
            "hit_damage": 12,
            "block_damage": 0,
            "knockout_possible": true,
            "pre_msg": "{active.name} GETS {passive.name} IN THE JAW (OUCH!)....AND AGAIN",
            "hit_msg": "CONNECTS...",
            "blocked_msg": "BUT IT'S BLOCKED!!!!!!!!!!!!!"
        },
        "3": {
            "choices": 200,
            "threshold": 125,
            "hit_damage": 8,
            "block_damage": 5,
            "pre_msg": "{passive.name} IS ATTACKED BY AN UPPERCUT (OH,OH)",
            "hit_msg": "AND {active.name} CONNECTS...",
            "blocked_msg": "{passive.name} BLOCKS AND HITS {active.name} WITH A HOOK"
        },
        "4": {
            "choices": 7,
            "threshold": 3,
            "hit_damage": 3,
            "block_damage": 0,
            "pre_msg": "{active.name} JABS AND",
            "hit_msg": "BLOOD SPILLS !!!",
            "blocked_msg": "AND IT'S BLOCKED (LUCKY BLOCK!)"
        }
    }
    
    
    ================================================
    FILE: 15_Boxing/python/player-profile.json
    ================================================
    {
        "1": {
            "choices": 30,
            "threshold": 10,
            "hit_damage": 15,
            "block_damage": 0,
            "pre_msg": "{active.name} SWINGS AND",
            "hit_msg": "HE CONNECTS!",
            "blocked_msg": "HE MISSES"
        },
        "2": {
            "choices": 2,
            "threshold": 1,
            "hit_damage": 7,
            "block_damage": 0,
            "pre_msg": "{active.name} GIVES THE HOOK...",
            "hit_msg": "CONNECTS...",
            "blocked_msg": "BUT IT'S BLOCKED!!!!!!!!!!!!!"
        },
        "3": {
            "choices": 100,
            "threshold": 50,
            "hit_damage": 4,
            "block_damage": 0,
            "pre_msg": "{player_name} TRIES AN UPPERCUT",
            "hit_msg": "AND HE CONNECTS!",
            "blocked_msg": "AND IT'S BLOCKED (LUCKY BLOCK!)"
        },
        "4": {
            "choices": 8,
            "threshold": 3,
            "hit_damage": 3,
            "block_damage": 0,
            "pre_msg": "{active.name} JABS AT {passive.name}'S HEAD",
            "hit_msg": "AND HE CONNECTS!",
            "blocked_msg": "AND IT'S BLOCKED (LUCKY BLOCK!)"
        }
    }
    
    
    ================================================
    FILE: 15_Boxing/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 15_Boxing/vbnet/Boxing.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Boxing", "Boxing.vbproj", "{8BCEDE01-59A7-4AA3-AE09-8B7FD69A5867}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{8BCEDE01-59A7-4AA3-AE09-8B7FD69A5867}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{8BCEDE01-59A7-4AA3-AE09-8B7FD69A5867}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{8BCEDE01-59A7-4AA3-AE09-8B7FD69A5867}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{8BCEDE01-59A7-4AA3-AE09-8B7FD69A5867}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 15_Boxing/vbnet/Boxing.vbproj
    ================================================
    
      
        Exe
        Boxing
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 15_Boxing/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 16_Bug/README.md
    ================================================
    ### Bug
    
    The object of this game is to finish your drawing of a bug before the computer finishes.
    
    You and the computer roll a die alternately with each number standing for a part of the bug. You must add the parts in the right order; in other words, you cannot have a neck until you have a body, you cannot have a head until you have a neck, and so on. After each new part has been added, you have the option of seeing pictures of the two bugs.
    
    If you elect to see all the pictures, this program has the ability of consuming well over six feet of terminal paper per run. We can only suggest recycling the paper by using the other side.
    
    Brian Leibowitz wrote this program while in the 7th grade at Harrison Jr-Se High School in Harrison, New York.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=30)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=45)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 16_Bug/bug.bas
    ================================================
    10 PRINT TAB(34);"BUG"
    20 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    30 PRINT:PRINT:PRINT
    40 REM
    50 A=0: B=0: H=0: L=0: N=0: P=0: Q=0: R=0: S=0: T=0: U=0: V=0: Y=0
    60 PRINT "THE GAME BUG"
    70 PRINT "I HOPE YOU ENJOY THIS GAME."
    80 PRINT
    90 PRINT "DO YOU WANT INSTRUCTIONS";
    100 INPUT Z$
    110 IF Z$="NO" THEN 300
    120 PRINT "THE OBJECT OF BUG IS TO FINISH YOUR BUG BEFORE I FINISH"
    130 PRINT "MINE. EACH NUMBER STANDS FOR A PART OF THE BUG BODY."
    140 PRINT "I WILL ROLL THE DIE FOR YOU, TELL YOU WHAT I ROLLED FOR YOU"
    150 PRINT "WHAT THE NUMBER STANDS FOR, AND IF YOU CAN GET THE PART."
    160 PRINT "IF YOU CAN GET THE PART I WILL GIVE IT TO YOU."
    170 PRINT "THE SAME WILL HAPPEN ON MY TURN."
    180 PRINT "IF THERE IS A CHANGE IN EITHER BUG I WILL GIVE YOU THE"
    190 PRINT "OPTION OF SEEING THE PICTURES OF THE BUGS."
    200 PRINT "THE NUMBERS STAND FOR PARTS AS FOLLOWS:"
    210 PRINT "NUMBER","PART","NUMBER OF PART NEEDED"
    220 PRINT "1","BODY","1"
    230 PRINT "2","NECK","1"
    240 PRINT "3","HEAD","1"
    250 PRINT "4","FEELERS","2"
    260 PRINT "5","TAIL","1"
    270 PRINT "6","LEGS","6"
    280 PRINT
    290 PRINT
    300 IF Y>0 THEN 2480
    310 Z=INT(6*RND(1)+1)
    320 C=1
    330 PRINT "YOU ROLLED A";Z
    340 ON Z GOTO 350,430,540,650,760,870
    350 PRINT "1=BODY"
    360 IF B=1 THEN 410
    370 PRINT "YOU NOW HAVE A BODY."
    380 B=1
    390 C=0
    400 GOTO 970
    410 PRINT "YOU DO NOT NEED A BODY."
    420 GOTO 970
    430 PRINT "2=NECK"
    440 IF N=1 THEN 500
    450 IF B=0 THEN 520
    460 PRINT "YOU NOW HAVE A NECK."
    470 N=1
    480 C=0
    490 GOTO 970
    500 PRINT "YOU DO NOT NEED A NECK."
    510 GOTO 970
    520 PRINT "YOU DO NOT HAVE A BODY."
    530 GOTO 970
    540 PRINT "3=HEAD"
    550 IF N=0 THEN 610
    560 IF H=1 THEN 630
    570 PRINT "YOU NEEDED A HEAD."
    580 H=1
    590 C=0
    600 GOTO 970
    610 PRINT "YOU DO NOT HAVE A NECK."
    620 GOTO 970
    630 PRINT "YOU HAVE A HEAD."
    640 GOTO 970
    650 PRINT "4=FEELERS"
    660 IF H=0 THEN 740
    670 IF A=2 THEN 720
    680 PRINT "I NOW GIVE YOU A FEELER."
    690 A=A+1
    700 C=0
    710 GOTO 970
    720 PRINT "YOU HAVE TWO FEELERS ALREADY."
    730 GOTO 970
    740 PRINT "YOU DO NOT HAVE A HEAD."
    750 GOTO 970
    760 PRINT "5=TAIL"
    770 IF B=0 THEN 830
    780 IF T=1 THEN 850
    790 PRINT "I NOW GIVE YOU A TAIL."
    800 T=T+1
    810 C=0
    820 GOTO 970
    830 PRINT "YOU DO NOT HAVE A BODY."
    840 GOTO 970
    850 PRINT "YOU ALREADY HAVE A TAIL."
    860 GOTO 970
    870 PRINT "6=LEG"
    880 IF L=6 THEN 940
    890 IF B=0 THEN 960
    900 L=L+1
    910 C=0
    920 PRINT "YOU NOW HAVE";L;"LEGS."
    930 GOTO 970
    940 PRINT "YOU HAVE 6 FEET ALREADY."
    950 GOTO 970
    960 PRINT "YOU DO NOT HAVE A BODY."
    970 X=INT(6*RND(1)+1)
    971 PRINT
    975 FOR DELAY=1 TO 2000:NEXT DELAY
    980 PRINT "I ROLLED A";X
    990 ON X GOTO 1000,1080,1190,1300,1410,1520
    1000 PRINT "1=BODY"
    1010 IF P=1 THEN 1060
    1020 PRINT "I NOW HAVE A BODY."
    1030 C=0
    1040 P=1
    1050 GOTO 1630
    1060 PRINT "I DO NOT NEED A BODY."
    1070 GOTO 1630
    1080 PRINT "2=NECK"
    1090 IF Q=1 THEN 1150
    1100 IF P=0 THEN 1170
    1110 PRINT "I NOW HAVE A NECK."
    1120 Q=1
    1130 C=0
    1140 GOTO 1630
    1150 PRINT "I DO NOT NEED A NECK."
    1160 GOTO 1630
    1170 PRINT "I DO NOT HAVE A BODY."
    1180 GOTO 1630
    1190 PRINT "3=HEAD"
    1200 IF Q=0 THEN 1260
    1210 IF R=1 THEN 1280
    1220 PRINT "I NEEDED A HEAD."
    1230 R=1
    1240 C=0
    1250 GOTO 1630
    1260 PRINT "I DO NOT HAVE A NECK."
    1270 GOTO 1630
    1280 PRINT "I DO NOT NEED A HEAD."
    1290 GOTO 1630
    1300 PRINT "4=FEELERS"
    1310 IF R=0 THEN 1390
    1320 IF S=2 THEN 1370
    1330 PRINT "I GET A FEELER."
    1340 S=S+1
    1350 C=0
    1360 GOTO 1630
    1370 PRINT "I HAVE 2 FEELERS ALREADY."
    1380 GOTO 1630
    1390 PRINT "I DO NOT HAVE A HEAD."
    1400 GOTO 1630
    1410 PRINT "5=TAIL"
    1420 IF P=0 THEN 1480
    1430 IF U=1 THEN 1500
    1440 PRINT "I NOW HAVE A TAIL."
    1450 U=1
    1460 C=0
    1470 GOTO 1630
    1480 PRINT "I DO NOT HAVE A BODY."
    1490 GOTO 1630
    1500 PRINT "I DO NOT NEED A TAIL."
    1510 GOTO 1630
    1520 PRINT "6=LEGS"
    1530 IF V=6 THEN 1590
    1540 IF P=0 THEN 1610
    1550 V=V+1
    1560 C=0
    1570 PRINT "I NOW HAVE";V;"LEGS."
    1580 GOTO 1630
    1590 PRINT,"I HAVE 6 FEET."
    1600 GOTO 1630
    1610 PRINT "I DO NOT HAVE A BODY."
    1620 GOTO 1630
    1630 IF A=2 AND T=1 AND L=6 THEN 1650
    1640 GOTO 1670
    1650 PRINT "YOUR BUG IS FINISHED."
    1660 Y=Y+1
    1670 IF S=2 AND P=1 AND V=6 THEN 1690
    1680 GOTO 1710
    1690 PRINT "MY BUG IS FINISHED."
    1700 Y=Y+2
    1710 IF C=1 THEN 300
    1720 PRINT "DO YOU WANT THE PICTURES";
    1730 INPUT Z$
    1740 IF Z$="NO" THEN 300
    1750 PRINT "*****YOUR BUG*****"
    1760 PRINT
    1770 PRINT
    1780 IF A=0 THEN 1860
    1790 FOR Z=1 TO 4
    1800 FOR X=1 TO A
    1810 PRINT TAB(10);
    1820 PRINT "A ";
    1830 NEXT X
    1840 PRINT
    1850 NEXT Z
    1860 IF H=0 THEN 1880
    1870 GOSUB 2470
    1880 IF N=0 THEN 1920
    1890 FOR Z=1 TO 2
    1900 PRINT "          N N"
    1910 NEXT Z
    1920 IF B=0 THEN 2000
    1930 PRINT "     BBBBBBBBBBBB"
    1940 FOR Z=1 TO 2
    1950 PRINT "     B          B"
    1960 NEXT Z
    1970 IF T<>1 THEN 1990
    1980 PRINT "TTTTTB          B"
    1990 PRINT "     BBBBBBBBBBBB"
    2000 IF L=0 THEN 2080
    2010 FOR Z=1 TO 2
    2020 PRINT TAB(5);
    2030 FOR X=1 TO L
    2040 PRINT " L";
    2050 NEXT X
    2060 PRINT
    2070 NEXT Z
    2080 FOR Z=1 TO 4
    2090 PRINT
    2100 NEXT Z
    2110 PRINT "*****MY BUG*****"
    2120 PRINT
    2130 PRINT
    2140 PRINT
    2150 IF S=0 THEN 2230
    2160 FOR Z=1 TO 4
    2170 PRINT TAB(10);
    2180 FOR X=1 TO S
    2190 PRINT "F ";
    2200 NEXT X
    2210 PRINT
    2220 NEXT Z
    2230 IF R<>1 THEN 2250
    2240 GOSUB 2470
    2250 IF Q=0 THEN 2280
    2260 PRINT "          N N"
    2270 PRINT "          N N"
    2280 IF P=0 THEN 2360
    2290 PRINT "     BBBBBBBBBBBB"
    2300 FOR Z=1 TO 2
    2310 PRINT "     B          B"
    2320 NEXT Z
    2330 IF U<>1 THEN 2350
    2340 PRINT "TTTTTB          B"
    2350 PRINT "     BBBBBBBBBBBB"
    2360 IF V=0 THEN 2450
    2370 FOR Z=1 TO 2
    2380 PRINT TAB(5);
    2390 FOR X=1 TO V
    2400 PRINT " L";
    2410 NEXT X
    2420 PRINT
    2430 NEXT Z
    2450 IF Y<>0 THEN 2540
    2460 GOTO 300
    2470 PRINT "        HHHHHHH"
    2480 PRINT "        H     H"
    2490 PRINT "        H O O H"
    2500 PRINT "        H     H"
    2510 PRINT "        H  V  H"
    2520 PRINT "        HHHHHHH"
    2530 RETURN
    2540 PRINT "I HOPE YOU ENJOYED THE GAME, PLAY IT AGAIN SOON!!"
    2550 END
    
    
    ================================================
    FILE: 16_Bug/csharp/Bug.cs
    ================================================
    using System.Text;
    using BugGame.Parts;
    using BugGame.Resources;
    
    namespace BugGame;
    
    internal class Bug
    {
        private readonly Body _body = new();
    
        public bool IsComplete => _body.IsComplete;
    
        public bool TryAdd(IPart part, out Message message) => _body.TryAdd(part, out message);
    
        public string ToString(string pronoun, char feelerCharacter)
        {
            var builder = new StringBuilder($"*****{pronoun} Bug*****").AppendLine().AppendLine().AppendLine();
            _body.AppendTo(builder, feelerCharacter);
            return builder.ToString();
        }
    }
    
    ================================================
    FILE: 16_Bug/csharp/Bug.csproj
    ================================================
    
      
        Exe
        net6.0
        10
        enable
        enable
      
    
      
        
      
    
      
        
      
    
    
    
    
    ================================================
    FILE: 16_Bug/csharp/Bug.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bug", "Bug.csproj", "{C1929CC1-C366-43E4-9476-AE107231A302}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{C1929CC1-C366-43E4-9476-AE107231A302}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{C1929CC1-C366-43E4-9476-AE107231A302}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{C1929CC1-C366-43E4-9476-AE107231A302}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{C1929CC1-C366-43E4-9476-AE107231A302}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 16_Bug/csharp/Game.cs
    ================================================
    using BugGame.Parts;
    using BugGame.Resources;
    using Games.Common.IO;
    using Games.Common.Randomness;
    using static System.StringComparison;
    namespace BugGame;
    
    internal class Game
    {
        private readonly IReadWrite _io;
        private readonly IRandom _random;
    
        public Game(IReadWrite io, IRandom random)
        {
            _io = io;
            _random = random;
        }
    
        public void Play()
        {
            _io.Write(Resource.Streams.Introduction);
            if (!_io.ReadString("Do you want instructions").Equals("no", InvariantCultureIgnoreCase))
            {
                _io.Write(Resource.Streams.Instructions);
            }
    
            BuildBugs();
    
            _io.Write(Resource.Streams.PlayAgain);
        }
    
        private void BuildBugs()
        {
            var yourBug = new Bug();
            var myBug = new Bug();
    
            while (true)
            {
                var partAdded = TryBuild(yourBug, m => m.You);
                Thread.Sleep(500);
                _io.WriteLine();
                partAdded |= TryBuild(myBug, m => m.I);
    
                if (partAdded)
                {
                    if (yourBug.IsComplete) { _io.WriteLine("Your bug is finished."); }
                    if (myBug.IsComplete) { _io.WriteLine("My bug is finished."); }
    
                    if (!_io.ReadString("Do you want the picture").Equals("no", InvariantCultureIgnoreCase))
                    {
                        _io.Write(yourBug.ToString("Your", 'A'));
                        _io.WriteLine();
                        _io.WriteLine();
                        _io.WriteLine();
                        _io.WriteLine();
                        _io.Write(myBug.ToString("My", 'F'));
                    }
                }
    
                if (yourBug.IsComplete || myBug.IsComplete) { break; }
            }
        }
    
        private bool TryBuild(Bug bug, Func messageTransform)
        {
            var roll = _random.Next(6) + 1;
            _io.WriteLine(messageTransform(Message.Rolled.ForValue(roll)));
    
            IPart part = roll switch
            {
                1 => new Body(),
                2 => new Neck(),
                3 => new Head(),
                4 => new Feeler(),
                5 => new Tail(),
                6 => new Leg(),
                _ => throw new Exception("Unexpected roll value")
            };
            _io.WriteLine($"{roll}={part.GetType().Name}");
    
            var partAdded = bug.TryAdd(part, out var message);
            _io.WriteLine(messageTransform.Invoke(message));
    
            return partAdded;
        }
    }
    
    ================================================
    FILE: 16_Bug/csharp/Parts/Body.cs
    ================================================
    using System.Text;
    using BugGame.Resources;
    
    namespace BugGame.Parts;
    
    internal class Body : ParentPart
    {
        private readonly Neck _neck = new();
        private readonly Tail _tail = new();
        private readonly Legs _legs = new();
    
        public Body()
            : base(Message.BodyAdded, Message.BodyNotNeeded)
        {
        }
    
        public override bool IsComplete => _neck.IsComplete && _tail.IsComplete && _legs.IsComplete;
    
        protected override bool TryAddCore(IPart part, out Message message)
            => part switch
            {
                Neck => _neck.TryAdd(out message),
                Head or Feeler => _neck.TryAdd(part, out message),
                Tail => _tail.TryAdd(out message),
                Leg => _legs.TryAddOne(out message),
                _ => throw new NotSupportedException($"Can't add a {part.Name} to a {Name}.")
            };
    
        public void AppendTo(StringBuilder builder, char feelerCharacter)
        {
            if (IsPresent)
            {
                _neck.AppendTo(builder, feelerCharacter);
                builder
                    .AppendLine("     BBBBBBBBBBBB")
                    .AppendLine("     B          B")
                    .AppendLine("     B          B");
                _tail.AppendTo(builder);
                builder
                    .AppendLine("     BBBBBBBBBBBB");
                _legs.AppendTo(builder);
            }
        }
    }
    
    
    ================================================
    FILE: 16_Bug/csharp/Parts/Feeler.cs
    ================================================
    namespace BugGame.Parts;
    
    internal class Feeler : IPart
    {
        public string Name => nameof(Feeler);
    }
    
    
    ================================================
    FILE: 16_Bug/csharp/Parts/Feelers.cs
    ================================================
    using System.Text;
    using BugGame.Resources;
    
    namespace BugGame.Parts;
    
    internal class Feelers : PartCollection
    {
        public Feelers()
            : base(2, Message.FeelerAdded, Message.FeelersFull)
        {
        }
    
        public void AppendTo(StringBuilder builder, char character) => AppendTo(builder, 10, 4, character);
    }
    
    
    ================================================
    FILE: 16_Bug/csharp/Parts/Head.cs
    ================================================
    using System.Text;
    using BugGame.Resources;
    
    namespace BugGame.Parts;
    
    internal class Head : ParentPart
    {
        private Feelers _feelers = new();
    
        public Head()
            : base(Message.HeadAdded, Message.HeadNotNeeded)
        {
        }
    
        public override bool IsComplete => _feelers.IsComplete;
    
        protected override bool TryAddCore(IPart part, out Message message)
            => part switch
            {
                Feeler => _feelers.TryAddOne(out message),
                _ => throw new NotSupportedException($"Can't add a {part.Name} to a {Name}.")
            };
    
        public void AppendTo(StringBuilder builder, char feelerCharacter)
        {
            if (IsPresent)
            {
                _feelers.AppendTo(builder, feelerCharacter);
                builder
                    .AppendLine("        HHHHHHH")
                    .AppendLine("        H     H")
                    .AppendLine("        H O O H")
                    .AppendLine("        H     H")
                    .AppendLine("        H  V  H")
                    .AppendLine("        HHHHHHH");
            }
        }
    }
    
    
    ================================================
    FILE: 16_Bug/csharp/Parts/IPart.cs
    ================================================
    namespace BugGame.Parts;
    
    internal interface IPart
    {
        string Name { get; }
    }
    
    
    ================================================
    FILE: 16_Bug/csharp/Parts/Leg.cs
    ================================================
    namespace BugGame.Parts;
    
    internal class Leg : IPart
    {
        public string Name => nameof(Leg);
    }
    
    
    ================================================
    FILE: 16_Bug/csharp/Parts/Legs.cs
    ================================================
    using System.Text;
    using BugGame.Resources;
    
    namespace BugGame.Parts;
    
    internal class Legs : PartCollection
    {
        public Legs()
            : base(6, Message.LegAdded, Message.LegsFull)
        {
        }
    
        public void AppendTo(StringBuilder builder) => AppendTo(builder, 6, 2, 'L');
    }
    
    
    ================================================
    FILE: 16_Bug/csharp/Parts/Neck.cs
    ================================================
    using System.Text;
    using BugGame.Resources;
    
    namespace BugGame.Parts;
    
    internal class Neck : ParentPart
    {
        private Head _head = new();
    
        public Neck()
            : base(Message.NeckAdded, Message.NeckNotNeeded)
        {
        }
    
        public override bool IsComplete => _head.IsComplete;
    
        protected override bool TryAddCore(IPart part, out Message message)
            => part switch
            {
                Head => _head.TryAdd(out message),
                Feeler => _head.TryAdd(part, out message),
                _ => throw new NotSupportedException($"Can't add a {part.Name} to a {Name}.")
            };
    
        public void AppendTo(StringBuilder builder, char feelerCharacter)
        {
            if (IsPresent)
            {
                _head.AppendTo(builder, feelerCharacter);
                builder.AppendLine("          N N").AppendLine("          N N");
            }
        }
    }
    
    
    ================================================
    FILE: 16_Bug/csharp/Parts/ParentPart.cs
    ================================================
    using BugGame.Resources;
    
    namespace BugGame.Parts;
    
    internal abstract class ParentPart : Part
    {
        public ParentPart(Message addedMessage, Message duplicateMessage)
            : base(addedMessage, duplicateMessage)
        {
        }
    
        public bool TryAdd(IPart part, out Message message)
            => (part.GetType() == GetType(), IsPresent) switch
            {
                (true, _) => TryAdd(out message),
                (false, false) => ReportDoNotHave(out message),
                _ => TryAddCore(part, out message)
            };
    
        protected abstract bool TryAddCore(IPart part, out Message message);
    
        private bool ReportDoNotHave(out Message message)
        {
            message = Message.DoNotHaveA(this);
            return false;
        }
    }
    
    
    ================================================
    FILE: 16_Bug/csharp/Parts/Part.cs
    ================================================
    using BugGame.Resources;
    
    namespace BugGame.Parts;
    
    internal class Part : IPart
    {
        private readonly Message _addedMessage;
        private readonly Message _duplicateMessage;
    
        public Part(Message addedMessage, Message duplicateMessage)
        {
            _addedMessage = addedMessage;
            _duplicateMessage = duplicateMessage;
        }
    
        public virtual bool IsComplete => IsPresent;
    
        protected bool IsPresent { get; private set; }
    
        public string Name => GetType().Name;
    
        public bool TryAdd(out Message message)
        {
            if (IsPresent)
            {
                message = _duplicateMessage;
                return false;
            }
    
            message = _addedMessage;
            IsPresent = true;
            return true;
        }
    }
    
    
    ================================================
    FILE: 16_Bug/csharp/Parts/PartCollection.cs
    ================================================
    using System.Text;
    using BugGame.Resources;
    
    namespace BugGame.Parts;
    
    internal class PartCollection
    {
        private readonly int _maxCount;
        private readonly Message _addedMessage;
        private readonly Message _fullMessage;
        private int _count;
    
        public PartCollection(int maxCount, Message addedMessage, Message fullMessage)
        {
            _maxCount = maxCount;
            _addedMessage = addedMessage;
            _fullMessage = fullMessage;
        }
    
        public bool IsComplete => _count == _maxCount;
    
        public bool TryAddOne(out Message message)
        {
            if (_count < _maxCount)
            {
                _count++;
                message = _addedMessage.ForValue(_count);
                return true;
            }
    
            message = _fullMessage;
            return false;
        }
    
        protected void AppendTo(StringBuilder builder, int offset, int length, char character)
        {
            if (_count == 0) { return; }
    
            for (var i = 0; i < length; i++)
            {
                builder.Append(' ', offset);
    
                for (var j = 0; j < _count; j++)
                {
                    builder.Append(character).Append(' ');
                }
                builder.AppendLine();
            }
        }
    }
    
    
    ================================================
    FILE: 16_Bug/csharp/Parts/Tail.cs
    ================================================
    using System.Text;
    using BugGame.Resources;
    
    namespace BugGame.Parts;
    
    internal class Tail : Part
    {
        public Tail()
            : base(Message.TailAdded, Message.TailNotNeeded)
        {
        }
    
        public void AppendTo(StringBuilder builder)
        {
            if (IsPresent)
            {
                builder.AppendLine("TTTTTB          B");
            }
        }
    }
    
    ================================================
    FILE: 16_Bug/csharp/Program.cs
    ================================================
    using BugGame;
    using Games.Common.IO;
    using Games.Common.Randomness;
    
    new Game(new ConsoleIO(), new RandomNumberGenerator()).Play();
    
    
    ================================================
    FILE: 16_Bug/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 16_Bug/csharp/Resources/Instructions.txt
    ================================================
    The object of Bug is to finish your bug before I finish
    mine. Each number stands for a part of the bug body.
    I will roll the die for you, tell you what I rolled for you
    what the number stands for, and if you can get the part.
    If you can get the part I will give it to you.
    The same will happen on my turn.
    If there is a change in either bug I will give you the
    option of seeing the pictures of the bugs.
    The numbers stand for parts as follows:
    Number        Part          Number of part needed
     1            Body           1
     2            Neck           1
     3            Head           1
     4            Feelers        2
     5            Tail           1
     6            Legs           6
    
    
    
    
    ================================================
    FILE: 16_Bug/csharp/Resources/Introduction.txt
    ================================================
                                    Bug
                   Creative Computing  Morristown, New Jersey
    
    
    
    The Game Bug
    I hope you enjoy this game.
    
    
    
    ================================================
    FILE: 16_Bug/csharp/Resources/Message.cs
    ================================================
    using BugGame.Parts;
    
    namespace BugGame.Resources;
    
    internal class Message
    {
        public static Message Rolled = new("rolled a {0}");
    
        public static Message BodyAdded = new("now have a body.");
        public static Message BodyNotNeeded = new("do not need a body.");
    
        public static Message NeckAdded = new("now have a neck.");
        public static Message NeckNotNeeded = new("do not need a neck.");
    
        public static Message HeadAdded = new("needed a head.");
        public static Message HeadNotNeeded = new("I do not need a head.", "You have a head.");
    
        public static Message TailAdded = new("I now have a tail.", "I now give you a tail.");
        public static Message TailNotNeeded = new("I do not need a tail.", "You already have a tail.");
    
        public static Message FeelerAdded = new("I get a feeler.", "I now give you a feeler");
        public static Message FeelersFull = new("I have 2 feelers already.", "You have two feelers already");
    
        public static Message LegAdded = new("now have {0} legs");
        public static Message LegsFull = new("I have 6 feet.", "You have 6 feet already");
    
        public static Message Complete = new("bug is finished.");
    
        private Message(string common)
            : this("I " + common, "You " + common)
        {
        }
    
        private Message(string i, string you)
        {
            I = i;
            You = you;
        }
    
        public string I { get; }
        public string You { get; }
    
        public static Message DoNotHaveA(Part part) => new($"do not have a {part.Name}");
    
        public Message ForValue(int quantity) => new(string.Format(I, quantity), string.Format(You, quantity));
    }
    
    ================================================
    FILE: 16_Bug/csharp/Resources/PlayAgain.txt
    ================================================
    I hope you enjoyed the game, play it again soon!!
    
    
    ================================================
    FILE: 16_Bug/csharp/Resources/Resource.cs
    ================================================
    using System.Reflection;
    using System.Runtime.CompilerServices;
    
    namespace BugGame.Resources;
    
    internal static class Resource
    {
        internal static class Streams
        {
            public static Stream Introduction => GetStream();
            public static Stream Instructions => GetStream();
            public static Stream PlayAgain => GetStream();
        }
    
        private static Stream GetStream([CallerMemberName] string? name = null) =>
            Assembly.GetExecutingAssembly()
                .GetManifestResourceStream($"Bug.Resources.{name}.txt")
                ?? throw new Exception($"Could not find embedded resource stream '{name}'.");
    }
    
    ================================================
    FILE: 16_Bug/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 16_Bug/java/src/Bug.java
    ================================================
    import java.util.ArrayList;
    import java.util.Scanner;
    
    /**
     * Game of Bug
     * 

    * Based on the Basic game of Bug here * https://github.com/coding-horror/basic-computer-games/blob/main/16%20Bug/bug.bas *

    * Note: The idea was to create a version of the 1970's Basic game in Java, without introducing * new features - no additional text, error checking, etc has been added. */ public class Bug { // Dice roll public static final int SIX = 6; private enum GAME_STATE { START, PLAYER_TURN, COMPUTER_TURN, CHECK_FOR_WINNER, GAME_OVER } // Used for keyboard input private final Scanner kbScanner; // Current game state private GAME_STATE gameState; private final Insect playersBug; private final Insect computersBug; // Used to show the result of dice roll. private final String[] ROLLS = new String[]{"BODY", "NECK", "HEAD", "FEELERS", "TAIL", "LEGS"}; public Bug() { playersBug = new PlayerBug(); computersBug = new ComputerBug(); gameState = GAME_STATE.START; // Initialise kb scanner kbScanner = new Scanner(System.in); } /** * Main game loop */ public void play() { do { switch (gameState) { // Show an introduction the first time the game is played. // And optionally instructions. case START: intro(); if (!noEntered(displayTextAndGetInput("DO YOU WANT INSTRUCTIONS? "))) { instructions(); } gameState = GAME_STATE.PLAYER_TURN; break; case PLAYER_TURN: int playersRoll = randomNumber(); System.out.println("YOU ROLLED A " + playersRoll + "=" + ROLLS[playersRoll - 1]); switch (playersRoll) { case 1: System.out.println(playersBug.addBody()); break; case 2: System.out.println(playersBug.addNeck()); break; case 3: System.out.println(playersBug.addHead()); break; case 4: System.out.println(playersBug.addFeelers()); break; case 5: System.out.println(playersBug.addTail()); break; case 6: System.out.println(playersBug.addLeg()); break; } gameState = GAME_STATE.COMPUTER_TURN; break; case COMPUTER_TURN: int computersRoll = randomNumber(); System.out.println("I ROLLED A " + computersRoll + "=" + ROLLS[computersRoll - 1]); switch (computersRoll) { case 1: System.out.println(computersBug.addBody()); break; case 2: System.out.println(computersBug.addNeck()); break; case 3: System.out.println(computersBug.addHead()); break; case 4: System.out.println(computersBug.addFeelers()); break; case 5: System.out.println(computersBug.addTail()); break; case 6: System.out.println(computersBug.addLeg()); break; } gameState = GAME_STATE.CHECK_FOR_WINNER; break; case CHECK_FOR_WINNER: boolean gameOver = false; if (playersBug.complete()) { System.out.println("YOUR BUG IS FINISHED."); gameOver = true; } else if (computersBug.complete()) { System.out.println("MY BUG IS FINISHED."); gameOver = true; } if (noEntered(displayTextAndGetInput("DO YOU WANT THE PICTURES? "))) { gameState = GAME_STATE.PLAYER_TURN; } else { System.out.println("*****YOUR BUG*****"); System.out.println(); draw(playersBug); System.out.println(); System.out.println("*****MY BUG*****"); System.out.println(); draw(computersBug); gameState = GAME_STATE.PLAYER_TURN; } if (gameOver) { System.out.println("I HOPE YOU ENJOYED THE GAME, PLAY IT AGAIN SOON!!"); gameState = GAME_STATE.GAME_OVER; } } } while (gameState != GAME_STATE.GAME_OVER); } /** * Draw the bug (player or computer) based on what has * already been added to it. * * @param bug The bug to be drawn. */ private void draw(Insect bug) { ArrayList insectOutput = bug.draw(); for (String s : insectOutput) { System.out.println(s); } } /** * Display an intro */ private void intro() { System.out.println("BUG"); System.out.println("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println(); System.out.println("THE GAME BUG"); System.out.println("I HOPE YOU ENJOY THIS GAME."); } private void instructions() { System.out.println("THE OBJECT OF BUG IS TO FINISH YOUR BUG BEFORE I FINISH"); System.out.println("MINE. EACH NUMBER STANDS FOR A PART OF THE BUG BODY."); System.out.println("I WILL ROLL THE DIE FOR YOU, TELL YOU WHAT I ROLLED FOR YOU"); System.out.println("WHAT THE NUMBER STANDS FOR, AND IF YOU CAN GET THE PART."); System.out.println("IF YOU CAN GET THE PART I WILL GIVE IT TO YOU."); System.out.println("THE SAME WILL HAPPEN ON MY TURN."); System.out.println("IF THERE IS A CHANGE IN EITHER BUG I WILL GIVE YOU THE"); System.out.println("OPTION OF SEEING THE PICTURES OF THE BUGS."); System.out.println("THE NUMBERS STAND FOR PARTS AS FOLLOWS:"); System.out.println("NUMBER\tPART\tNUMBER OF PART NEEDED"); System.out.println("1\tBODY\t1"); System.out.println("2\tNECK\t1"); System.out.println("3\tHEAD\t1"); System.out.println("4\tFEELERS\t2"); System.out.println("5\tTAIL\t1"); System.out.println("6\tLEGS\t6"); System.out.println(); } /** * Checks whether player entered N or NO to a question. * * @param text player string from kb * @return true if N or NO was entered, otherwise false */ private boolean noEntered(String text) { return stringIsAnyValue(text, "N", "NO"); } /** * Check whether a string equals one of a variable number of values * Useful to check for Y or YES for example * Comparison is case insensitive. * * @param text source string * @param values a range of values to compare against the source string * @return true if a comparison was found in one of the variable number of strings passed */ private boolean stringIsAnyValue(String text, String... values) { // Cycle through the variable number of values and test each for (String val : values) { if (text.equalsIgnoreCase(val)) { return true; } } // no matches return false; } /* * Print a message on the screen, then accept input from Keyboard. * * @param text message to be displayed on screen. * @return what was typed by the player. */ private String displayTextAndGetInput(String text) { System.out.print(text); return kbScanner.next(); } /** * Generate random number * * @return random number */ private int randomNumber() { return (int) (Math.random() * (SIX) + 1); } } ================================================ FILE: 16_Bug/java/src/BugGame.java ================================================ public class BugGame { public static void main(String[] args) { Bug bug = new Bug(); bug.play(); } } ================================================ FILE: 16_Bug/java/src/ComputerBug.java ================================================ public class ComputerBug extends Insect { // Create messages specific to the computer player. public ComputerBug() { // Call superclass constructor for initialization. super(); addMessages(new String[]{"I GET A FEELER.", "I HAVE " + MAX_FEELERS + " FEELERS ALREADY.", "I DO NOT HAVE A HEAD."}, PARTS.FEELERS); addMessages(new String[]{"I NEEDED A HEAD.", "I DO NOT NEED A HEAD.", "I DO NOT HAVE A NECK."}, PARTS.HEAD); addMessages(new String[]{"I NOW HAVE A NECK.", "I DO NOT NEED A NECK.", "I DO NOT HAVE A BODY."}, PARTS.NECK); addMessages(new String[]{"I NOW HAVE A BODY.", "I DO NOT NEED A BODY."}, PARTS.BODY); addMessages(new String[]{"I NOW HAVE A TAIL.", "I DO NOT NEED A TAIL.", "I DO NOT HAVE A BODY."}, PARTS.TAIL); addMessages(new String[]{"I NOW HAVE ^^^" + " LEG", "I HAVE " + MAX_LEGS + " FEET.", "I DO NOT HAVE A BODY."}, PARTS.LEGS); } } ================================================ FILE: 16_Bug/java/src/Insect.java ================================================ import java.util.ArrayList; import java.util.Arrays; /** * This tracks the insect (bug) and has methods to * add body parts, create an array of output so it * can be drawn and to determine if a bug is complete. * N.B. This is a super class for ComputerBug and PlayerBug */ public class Insect { public static final int MAX_FEELERS = 2; public static final int MAX_LEGS = 6; public static final int ADDED = 0; public static final int NOT_ADDED = 1; public static final int MISSING = 2; // Various parts of the bug public enum PARTS { FEELERS, HEAD, NECK, BODY, TAIL, LEGS } // Tracks what parts of the bug have been added private boolean body; private boolean neck; private boolean head; private int feelers; private boolean tail; private int legs; // Messages about for various body parts // These are set in the subclass ComputerBug or PlayerBug private String[] bodyMessages; private String[] neckMessages; private String[] headMessages; private String[] feelerMessages; private String[] tailMessages; private String[] legMessages; public Insect() { init(); } /** * Add a body to the bug if there is not one already added. * * @return return an appropriate message about the status of the operation. */ public String addBody() { boolean currentState = false; if (!body) { body = true; currentState = true; } return addBodyMessage(currentState); } /** * Create output based on adding the body or it being already added previously * * @return contains the output message */ private String addBodyMessage(boolean wasAdded) { // Return the appropriate message depending on whether the // body was added or not. if (wasAdded) { return bodyMessages[ADDED]; } else { return bodyMessages[NOT_ADDED]; } } /** * Add a neck if a) a body has previously been added and * b) a neck has not previously been added. * * @return text containing the status of the operation */ public String addNeck() { int status = NOT_ADDED; // Default is not added if (!body) { // No body, cannot add a neck status = MISSING; } else if (!neck) { neck = true; status = ADDED; } return neckMessages[status]; } /** * Add a head to the bug if a) there already exists a neck and * b) a head has not previously been added * * @return text outlining the success of the operation */ public String addHead() { int status = NOT_ADDED; // Default is not added if (!neck) { // No neck, cannot add a head status = MISSING; } else if (!head) { head = true; status = ADDED; } return headMessages[status]; } /** * Add a feeler to the head if a) there has been a head added to * the bug previously, and b) there are not already 2 (MAX_FEELERS) * feelers previously added to the bug. * * @return text outlining the status of the operation */ public String addFeelers() { int status = NOT_ADDED; // Default is not added if (!head) { // No head, cannot add a feeler status = MISSING; } else if (feelers < MAX_FEELERS) { feelers++; status = ADDED; } return feelerMessages[status]; } /** * Add a tail to the bug if a) there is already a body previously added * to the bug and b) there is not already a tail added. * * @return text outlining the status of the operation. */ public String addTail() { int status = NOT_ADDED; // Default is not added if (!body) { // No body, cannot add a tail status = MISSING; } else if (!tail) { tail = true; status = ADDED; } return tailMessages[status]; } /** * Add a leg to the bug if a) there is already a body previously added * b) there are less than 6 (MAX_LEGS) previously added. * * @return text outlining status of the operation. */ public String addLeg() { int status = NOT_ADDED; // Default is not added if (!body) { // No body, cannot add a leg status = MISSING; } else if (legs < MAX_LEGS) { legs++; status = ADDED; } String message = ""; // Create a string showing the result of the operation switch(status) { case ADDED: // Replace # with number of legs message = legMessages[status].replace("^^^", String.valueOf(legs)); // Add text S. if >1 leg, or just . if one leg. if (legs > 1) { message += "S."; } else { message += "."; } break; case NOT_ADDED: // Deliberate fall through to next case as its the // same code to be executed case MISSING: message = legMessages[status]; break; } return message; } /** * Initialise */ public void init() { body = false; neck = false; head = false; feelers = 0; tail = false; legs = 0; } /** * Add unique messages depending on type of player * A subclass of this class calls this method * e.g. See ComputerBug or PlayerBug classes * * @param messages an array of messages * @param bodyPart the bodypart the messages relate to. */ public void addMessages(String[] messages, PARTS bodyPart) { switch (bodyPart) { case FEELERS: feelerMessages = messages; break; case HEAD: headMessages = messages; break; case NECK: neckMessages = messages; break; case BODY: bodyMessages = messages; break; case TAIL: tailMessages = messages; break; case LEGS: legMessages = messages; break; } } /** * Returns a string array containing * the "bug" that can be output to console * * @return the bug ready to draw */ public ArrayList draw() { ArrayList bug = new ArrayList<>(); StringBuilder lineOutput; // Feelers if (feelers > 0) { for (int i = 0; i < 4; i++) { lineOutput = new StringBuilder(addSpaces(10)); for (int j = 0; j < feelers; j++) { lineOutput.append("A "); } bug.add(lineOutput.toString()); } } if (head) { lineOutput = new StringBuilder(addSpaces(8) + "HHHHHHH"); bug.add(lineOutput.toString()); lineOutput = new StringBuilder(addSpaces(8) + "H" + addSpaces(5) + "H"); bug.add(lineOutput.toString()); lineOutput = new StringBuilder(addSpaces(8) + "H O O H"); bug.add(lineOutput.toString()); lineOutput = new StringBuilder(addSpaces(8) + "H" + addSpaces(5) + "H"); bug.add(lineOutput.toString()); lineOutput = new StringBuilder(addSpaces(8) + "H" + addSpaces(2) + "V" + addSpaces(2) + "H"); bug.add(lineOutput.toString()); lineOutput = new StringBuilder(addSpaces(8) + "HHHHHHH"); bug.add(lineOutput.toString()); } if (neck) { for (int i = 0; i < 2; i++) { lineOutput = new StringBuilder(addSpaces(10) + "N N"); bug.add(lineOutput.toString()); } } if (body) { lineOutput = new StringBuilder(addSpaces(5) + "BBBBBBBBBBBB"); bug.add(lineOutput.toString()); for (int i = 0; i < 2; i++) { lineOutput = new StringBuilder(addSpaces(5) + "B" + addSpaces(10) + "B"); bug.add(lineOutput.toString()); } if (tail) { lineOutput = new StringBuilder("TTTTTB" + addSpaces(10) + "B"); bug.add(lineOutput.toString()); } lineOutput = new StringBuilder(addSpaces(5) + "BBBBBBBBBBBB"); bug.add(lineOutput.toString()); } if (legs > 0) { for (int i = 0; i < 2; i++) { lineOutput = new StringBuilder(addSpaces(5)); for (int j = 0; j < legs; j++) { lineOutput.append(" L"); } bug.add(lineOutput.toString()); } } return bug; } /** * Check if the bug is complete i.e. it has * 2 (MAX_FEELERS) feelers, a head, a neck, a body * a tail and 6 (MAX_FEET) feet. * * @return true if complete. */ public boolean complete() { return (feelers == MAX_FEELERS) && head && neck && body && tail && (legs == MAX_LEGS); } /** * Simulate tabs be creating a string of X spaces. * * @param number contains number of spaces needed. * @return a String containing the spaces */ private String addSpaces(int number) { char[] spaces = new char[number]; Arrays.fill(spaces, ' '); return new String(spaces); } } ================================================ FILE: 16_Bug/java/src/PlayerBug.java ================================================ public class PlayerBug extends Insect { // Create messages specific to the player. public PlayerBug() { // Call superclass constructor for initialization. super(); addMessages(new String[]{"I NOW GIVE YOU A FEELER.", "YOU HAVE " + MAX_FEELERS + " FEELERS ALREADY.", "YOU DO NOT HAVE A HEAD."}, PARTS.FEELERS); addMessages(new String[]{"YOU NEEDED A HEAD.", "YOU HAVE A HEAD.", "YOU DO NOT HAVE A NECK."}, PARTS.HEAD); addMessages(new String[]{"YOU NOW HAVE A NECK.", "YOU DO NOT NEED A NECK.", "YOU DO NOT HAVE A BODY."}, PARTS.NECK); addMessages(new String[]{"YOU NOW HAVE A BODY.", "YOU DO NOT NEED A BODY."}, PARTS.BODY); addMessages(new String[]{"I NOW GIVE YOU A TAIL.", "YOU ALREADY HAVE A TAIL.", "YOU DO NOT HAVE A BODY."}, PARTS.TAIL); addMessages(new String[]{"YOU NOW HAVE ^^^ LEG", "YOU HAVE " + MAX_LEGS + " FEET ALREADY.", "YOU DO NOT HAVE A BODY."}, PARTS.LEGS); } } ================================================ FILE: 16_Bug/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 16_Bug/javascript/bug.html ================================================ BUG

    
    
    
    
    
    
    ================================================
    FILE: 16_Bug/javascript/bug.js
    ================================================
    // BUG
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        return new Promise(function (resolve) {
                           const input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_element.addEventListener("keydown",
                               function (event) {
                                          if (event.keyCode === 13) {
                                              const input_str = input_element.value;
                                              document.getElementById("output").removeChild(input_element);
                                              print(input_str);
                                              print("\n");
                                              resolve(input_str);
                                          }
                                      });
                           });
    }
    
    function tab(space)
    {
        let str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    function waitNSeconds(n) {
        return new Promise(resolve => setTimeout(resolve, n*1000));
    }
    
    function scrollToBottom() {
        window.scrollTo(0, document.body.scrollHeight);
    }
    
    function draw_head()
    {
        print("        HHHHHHH\n");
        print("        H     H\n");
        print("        H O O H\n");
        print("        H     H\n");
        print("        H  V  H\n");
        print("        HHHHHHH\n");
    }
    
    function drawFeelers(feelerCount, character) {
        for (let z = 1; z <= 4; z++) {
            print(tab(10));
            for (let x = 1; x <= feelerCount; x++) {
                print(character + " ");
            }
            print("\n");
        }
    }
    
    function drawNeck() {
        for (let z = 1; z <= 2; z++)
            print("          N N\n");
    }
    
    function drawBody(computerTailCount) {
        print("     BBBBBBBBBBBB\n");
        for (let z = 1; z <= 2; z++)
            print("     B          B\n");
        if (computerTailCount === 1)
            print("TTTTTB          B\n");
        print("     BBBBBBBBBBBB\n");
    }
    
    function drawFeet(computerFeetCount) {
        for (let z = 1; z <= 2; z++) {
            print(tab(5));
            for (let x = 1; x <= computerFeetCount; x++)
                print(" L");
            print("\n");
        }
    }
    
    function drawBug(playerFeelerCount, playerHeadCount, playerNeckCount, playerBodyCount, playerTailCount, playerFeetCount, feelerCharacter) {
        if (playerFeelerCount !== 0) {
            drawFeelers(playerFeelerCount, feelerCharacter);
        }
        if (playerHeadCount !== 0)
            draw_head();
        if (playerNeckCount !== 0) {
            drawNeck();
        }
        if (playerBodyCount !== 0) {
            drawBody(playerTailCount)
        }
        if (playerFeetCount !== 0) {
            drawFeet(playerFeetCount);
        }
        for (let z = 1; z <= 4; z++)
            print("\n");
    }
    
    // Main program
    async function main()
    {
        print(tab(34) + "BUG\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        let playerFeelerCount = 0;
        let playerHeadCount = 0;
        let playerNeckCount = 0;
        let playerBodyCount = 0;
        let playerFeetCount = 0;
        let playerTailCount = 0;
    
        let computerFeelerCount = 0;
        let computerHeadCount = 0;
        let computerNeckCount = 0;
        let computerBodyCount = 0;
        let computerTailCount = 0;
        let computerFeetCount = 0;
    
        print("THE GAME BUG\n");
        print("I HOPE YOU ENJOY THIS GAME.\n");
        print("\n");
        print("DO YOU WANT INSTRUCTIONS");
        const instructionsRequired = await input();
        if (instructionsRequired.toUpperCase() !== "NO") {
            print("THE OBJECT OF BUG IS TO FINISH YOUR BUG BEFORE I FINISH\n");
            print("MINE. EACH NUMBER STANDS FOR A PART OF THE BUG BODY.\n");
            print("I WILL ROLL THE DIE FOR YOU, TELL YOU WHAT I ROLLED FOR YOU\n");
            print("WHAT THE NUMBER STANDS FOR, AND IF YOU CAN GET THE PART.\n");
            print("IF YOU CAN GET THE PART I WILL GIVE IT TO YOU.\n");
            print("THE SAME WILL HAPPEN ON MY TURN.\n");
            print("IF THERE IS A CHANGE IN EITHER BUG I WILL GIVE YOU THE\n");
            print("OPTION OF SEEING THE PICTURES OF THE BUGS.\n");
            print("THE NUMBERS STAND FOR PARTS AS FOLLOWS:\n");
            print("NUMBER\tPART\tNUMBER OF PART NEEDED\n");
            print("1\tBODY\t1\n");
            print("2\tNECK\t1\n");
            print("3\tHEAD\t1\n");
            print("4\tFEELERS\t2\n");
            print("5\tTAIL\t1\n");
            print("6\tLEGS\t6\n");
            print("\n");
            print("\n");
        }
    
        let gameInProgress = true;
        while (gameInProgress) {
            let dieRoll = Math.floor(6 * Math.random() + 1);
            let partFound = false;
            print("YOU ROLLED A " + dieRoll + "\n");
            switch (dieRoll) {
                case 1:
                    print("1=BODY\n");
                    if (playerBodyCount === 0) {
                        print("YOU NOW HAVE A BODY.\n");
                        playerBodyCount = 1;
                        partFound = true;
                    } else {
                        print("YOU DO NOT NEED A BODY.\n");
                    }
                    break;
                case 2:
                    print("2=NECK\n");
                    if (playerNeckCount === 0) {
                        if (playerBodyCount === 0) {
                            print("YOU DO NOT HAVE A BODY.\n");
                        } else {
                            print("YOU NOW HAVE A NECK.\n");
                            playerNeckCount = 1;
                            partFound = true;
                        }
                    } else {
                        print("YOU DO NOT NEED A NECK.\n");
                    }
                    break;
                case 3:
                    print("3=HEAD\n");
                    if (playerNeckCount === 0) {
                        print("YOU DO NOT HAVE A NECK.\n");
                    } else if (playerHeadCount === 0) {
                        print("YOU NEEDED A HEAD.\n");
                        playerHeadCount = 1;
                        partFound = true;
                    } else {
                        print("YOU HAVE A HEAD.\n");
                    }
                    break;
                case 4:
                    print("4=FEELERS\n");
                    if (playerHeadCount === 0) {
                        print("YOU DO NOT HAVE A HEAD.\n");
                    } else if (playerFeelerCount === 2) {
                        print("YOU HAVE TWO FEELERS ALREADY.\n");
                    } else {
                        print("I NOW GIVE YOU A FEELER.\n");
                        playerFeelerCount ++;
                        partFound = true;
                    }
                    break;
                case 5:
                    print("5=TAIL\n");
                    if (playerBodyCount === 0) {
                        print("YOU DO NOT HAVE A BODY.\n");
                    } else if (playerTailCount === 1) {
                        print("YOU ALREADY HAVE A TAIL.\n");
                    } else {
                        print("I NOW GIVE YOU A TAIL.\n");
                        playerTailCount++;
                        partFound = true;
                    }
                    break;
                case 6:
                    print("6=LEG\n");
                    if (playerFeetCount === 6) {
                        print("YOU HAVE 6 FEET ALREADY.\n");
                    } else if (playerBodyCount === 0) {
                        print("YOU DO NOT HAVE A BODY.\n");
                    } else {
                        playerFeetCount++;
                        partFound = true;
                        print("YOU NOW HAVE " + playerFeetCount + " LEGS.\n");
                    }
                    break;
            }
            dieRoll = Math.floor(6 * Math.random() + 1) ;
            print("\n");
            scrollToBottom();
            await waitNSeconds(1);
    
            print("I ROLLED A " + dieRoll + "\n");
            switch (dieRoll) {
                case 1:
                    print("1=BODY\n");
                    if (computerBodyCount === 1) {
                        print("I DO NOT NEED A BODY.\n");
                    } else {
                        print("I NOW HAVE A BODY.\n");
                        partFound = true;
                        computerBodyCount = 1;
                    }
                    break;
                case 2:
                    print("2=NECK\n");
                    if (computerNeckCount === 1) {
                        print("I DO NOT NEED A NECK.\n");
                    } else if (computerBodyCount === 0) {
                        print("I DO NOT HAVE A BODY.\n");
                    } else {
                        print("I NOW HAVE A NECK.\n");
                        computerNeckCount = 1;
                        partFound = true;
                    }
                    break;
                case 3:
                    print("3=HEAD\n");
                    if (computerNeckCount === 0) {
                        print("I DO NOT HAVE A NECK.\n");
                    } else if (computerHeadCount === 1) {
                        print("I DO NOT NEED A HEAD.\n");
                    } else {
                        print("I NEEDED A HEAD.\n");
                        computerHeadCount = 1;
                        partFound = true;
                    }
                    break;
                case 4:
                    print("4=FEELERS\n");
                    if (computerHeadCount === 0) {
                        print("I DO NOT HAVE A HEAD.\n");
                    } else if (computerFeelerCount === 2) {
                        print("I HAVE 2 FEELERS ALREADY.\n");
                    } else {
                        print("I GET A FEELER.\n");
                        computerFeelerCount++;
                        partFound = true;
                    }
                    break;
                case 5:
                    print("5=TAIL\n");
                    if (computerBodyCount === 0) {
                        print("I DO NOT HAVE A BODY.\n");
                    } else if (computerTailCount === 1) {
                        print("I DO NOT NEED A TAIL.\n");
                    } else {
                        print("I NOW HAVE A TAIL.\n");
                        computerTailCount = 1;
                        partFound = true;
                    }
                    break;
                case 6:
                    print("6=LEGS\n");
                    if (computerFeetCount === 6) {
                        print("I HAVE 6 FEET.\n");
                    } else if (computerBodyCount === 0) {
                        print("I DO NOT HAVE A BODY.\n");
                    } else {
                        computerFeetCount++;
                        partFound = true;
                        print("I NOW HAVE " + computerFeetCount + " LEGS.\n");
                    }
                    break;
            }
            if (playerFeelerCount === 2 && playerTailCount === 1 && playerFeetCount === 6) {
                print("YOUR BUG IS FINISHED.\n");
                gameInProgress = false;
            }
            if (computerFeelerCount === 2 && computerBodyCount === 1 && computerFeetCount === 6) {
                print("MY BUG IS FINISHED.\n");
                gameInProgress = false;
            }
            if (!partFound)
                continue;
            print("DO YOU WANT THE PICTURES");
            const showPictures = await input();
            if (showPictures.toUpperCase() === "NO")
                continue;
            print("*****YOUR BUG*****\n");
            print("\n");
            print("\n");
            drawBug(playerFeelerCount, playerHeadCount, playerNeckCount, playerBodyCount, playerTailCount, playerFeetCount, "A");
            print("*****MY BUG*****\n");
            print("\n");
            print("\n");
            drawBug(computerFeelerCount, computerHeadCount, computerNeckCount, computerBodyCount, computerTailCount, computerFeetCount, "F");
            for (let z = 1; z <= 4; z++)
                print("\n");
        }
        print("I HOPE YOU ENJOYED THE GAME, PLAY IT AGAIN SOON!!\n");
        scrollToBottom();
    }
    
    main();
    
    
    ================================================
    FILE: 16_Bug/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 16_Bug/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 16_Bug/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 16_Bug/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 16_Bug/python/bug.py
    ================================================
    import random
    import time
    from dataclasses import dataclass
    from typing import Literal
    
    
    @dataclass
    class State:
        is_player: bool
        body: int = 0
        neck: int = 0
        head: int = 0
        feelers: int = 0
        tail: int = 0
        legs: int = 0
    
        def is_finished(self) -> bool:
            return (
                self.feelers == 2
                and self.tail == 1
                and self.legs == 6
                and self.head == 1
                and self.neck == 1
            )
    
        def display(self) -> None:
            if self.feelers != 0:
                print_feelers(self.feelers, is_player=self.is_player)
            if self.head != 0:
                print_head()
            if self.neck != 0:
                print_neck()
            if self.body != 0:
                print_body(True) if self.tail == 1 else print_body(False)
            if self.legs != 0:
                print_legs(self.legs)
    
    
    def print_n_newlines(n: int) -> None:
        for _ in range(n):
            print()
    
    
    def print_feelers(n_feelers: int, is_player: bool = True) -> None:
        for _ in range(4):
            print(" " * 10, end="")
            for _ in range(n_feelers):
                print("A " if is_player else "F ", end="")
            print()
    
    
    def print_head() -> None:
        print("        HHHHHHH")
        print("        H     H")
        print("        H O O H")
        print("        H     H")
        print("        H  V  H")
        print("        HHHHHHH")
    
    
    def print_neck() -> None:
        print("          N N")
        print("          N N")
    
    
    def print_body(has_tail: bool = False) -> None:
        print("     BBBBBBBBBBBB")
        print("     B          B")
        print("     B          B")
        print("TTTTTB          B") if has_tail else ""
        print("     BBBBBBBBBBBB")
    
    
    def print_legs(n_legs: int) -> None:
        for _ in range(2):
            print(" " * 5, end="")
            for _ in range(n_legs):
                print(" L", end="")
            print()
    
    
    def handle_roll(diceroll: Literal[1, 2, 3, 4, 5, 6], state: State) -> bool:
        who = "YOU" if state.is_player else "I"
        changed = False
    
        print(f"{who} ROLLED A", diceroll)
        if diceroll == 1:
            print("1=BODY")
            if state.body:
                print(f"{who} DO NOT NEED A BODY.")
            else:
                print(f"{who} NOW HAVE A BODY.")
                state.body = 1
                changed = True
        elif diceroll == 2:
            print("2=NECK")
            if state.neck:
                print(f"{who} DO NOT NEED A NECK.")
            elif state.body == 0:
                print(f"{who} DO NOT HAVE A BODY.")
            else:
                print(f"{who} NOW HAVE A NECK.")
                state.neck = 1
                changed = True
        elif diceroll == 3:
            print("3=HEAD")
            if state.neck == 0:
                print(f"{who} DO NOT HAVE A NECK.")
            elif state.head:
                print(f"{who} HAVE A HEAD.")
            else:
                print(f"{who} NEEDED A HEAD.")
                state.head = 1
                changed = True
        elif diceroll == 4:
            print("4=FEELERS")
            if state.head == 0:
                print(f"{who} DO NOT HAVE A HEAD.")
            elif state.feelers == 2:
                print(f"{who} HAVE TWO FEELERS ALREADY.")
            else:
                if state.is_player:
                    print("I NOW GIVE YOU A FEELER.")
                else:
                    print(f"{who} GET A FEELER.")
                state.feelers += 1
                changed = True
        elif diceroll == 5:
            print("5=TAIL")
            if state.body == 0:
                print(f"{who} DO NOT HAVE A BODY.")
            elif state.tail:
                print(f"{who} ALREADY HAVE A TAIL.")
            else:
                if state.is_player:
                    print("I NOW GIVE YOU A TAIL.")
                else:
                    print(f"{who} NOW HAVE A TAIL.")
                state.tail = 1
                changed = True
        elif diceroll == 6:
            print("6=LEG")
            if state.legs == 6:
                print(f"{who} HAVE 6 FEET ALREADY.")
            elif state.body == 0:
                print(f"{who} DO NOT HAVE A BODY.")
            else:
                state.legs += 1
                changed = True
                print(f"{who} NOW HAVE {state.legs} LEGS")
        return changed
    
    
    def main() -> None:
        print(" " * 34 + "BUG")
        print(" " * 15 + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY")
        print_n_newlines(3)
    
        print("THE GAME BUG")
        print("I HOPE YOU ENJOY THIS GAME.")
        print()
        want_instructions = input("DO YOU WANT INSTRUCTIONS? ")
        if want_instructions != "NO":
            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("THE NUMBERS STAND FOR PARTS AS FOLLOWS:")
            table = [
                ["NUMBER", "PART", "NUMBER OF PART NEEDED"],
                ["1", "BODY", "1"],
                ["2", "NECK", "1"],
                ["3", "HEAD", "1"],
                ["4", "FEELERS", "2"],
                ["5", "TAIL", "1"],
                ["6", "LEGS", "6"],
            ]
            for row in table:
                print(f"{row[0]:<16}{row[1]:<16}{row[2]:<20}")
            print_n_newlines(2)
    
        player = State(is_player=True)
        opponent = State(is_player=False)
        bugs_finished = 0
    
        while bugs_finished <= 0:
            diceroll = random.randint(1, 6)
            print()
            changed = handle_roll(diceroll, player)  # type: ignore
    
            diceroll = random.randint(1, 6)
            print()
            time.sleep(2)
    
            changed_op = handle_roll(diceroll, opponent)  # type: ignore
    
            changed = changed or changed_op
    
            if player.is_finished():
                print("YOUR BUG IS FINISHED.")
                bugs_finished += 1
            if opponent.is_finished():
                print("MY BUG IS FINISHED.")
                bugs_finished += 1
            if not changed:
                continue
            want_pictures = input("DO YOU WANT THE PICTURES? ")
            if want_pictures != "NO":
                print("*****YOUR BUG*****")
                print_n_newlines(2)
                player.display()
                print_n_newlines(4)
                print("*****MY BUG*****")
                print_n_newlines(3)
                opponent.display()
    
                if bugs_finished != 0:
                    break
    
        print("I HOPE YOU ENJOYED THE GAME, PLAY IT AGAIN SOON!!")
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 16_Bug/ruby/Bug.rb
    ================================================
    class BugGame
      YES = ["Y", "y", "Yes", "YES"]
      NO  = ["N", "n", "No", "NO"]
    
      YOU = { body: 0, neck: 0, head: 0, antennaes: 0, legs: 0, arms: 0, name: "YOU" }
      I   = { body: 0, neck: 0, head: 0, antennaes: 0, legs: 0, arms: 0, name: "I" }
      def initialize
        puts "The Game Bug"
        puts "I HOPE YOU ENJOY THIS GAME."
        instructions
      end
    
      def run
        loop do
          # YOU FIRST
          play YOU
          if is_completed? YOU
            puts "\n\n\n\nYOU WON"
            break
          end
          puts "\n"
    
          # I SECOND
          play I
          if is_completed? I
            puts "\n\n\n\nI WON"
            break
          end
    
          loop do
            puts "Do you want the pictures? [Y,y,Yes,YES] [N,n,No,NO]"
            answer = gets.chomp!
            if YES.include?(answer) || NO.include?(answer)
              if YES.include?(answer)
                puts "--- YOUR BUG ---"
                print_bug YOU
                puts "\n\n--- MY BUG ---"
                print_bug I
              end
              break
            end
          end
        end
      end
    
      private
        def play player
          number = Random.rand(1..6)
          case number
          when 1
            if player[:body].eql? 0
              player[:body] = 1
              puts "#{player[:name]} have acquired a body"
            else
              puts "#{player[:name]} already have a body"
            end
          when 2
            one_part player, :neck, :body
          when 3
            one_part player, :head, :neck
          when 4
            two_parts player, :antennaes, :head
          when 5
            two_parts player, :legs, :body
          when 6
            two_parts player, :arms, :body
          end
        end
    
        def one_part player, part, part_needed
          if player[part].eql? 0
            if player[part_needed].eql? 0
              puts "#{player[:name]} need to have a #{part_needed.to_s} first"
            else
              player[part] = 1
              puts "#{player[:name]} have acquired a #{part.to_s}"
            end
          else
            puts "#{player[:name]} already have a #{part.to_s}"
          end
        end
    
        def two_parts player, part, part_needed
          if player[part].eql? 0
            if player[part_needed].eql? 0
              puts "#{player[:name]} need to have a #{part_needed.to_s} first"
            else
              player[part] = 1
              puts "#{player[:name]} have acquired first #{part.to_s.chop}"
            end
          else
            if player[part].eql? 2
              puts "#{player[:name]} already have 2 #{part.to_s}"
            else
              player[part] = 2
              puts "#{player[:name]} have acquired second #{part.to_s.chop}"
            end
          end
        end
    
        def is_completed? player
          player[:body].eql?(1) && player[:neck].eql?(1) && player[:head].eql?(1) && player[:antennaes].eql?(2) && player[:legs].eql?(2) && player[:arms].eql?(2)
        end
    
        def print_bug player
          antennae_and_leg player, :antennaes
          head player
          neck player
          body player
          antennae_and_leg player, :legs
        end
    
        def instructions
          loop do
            puts "Do you want an instruction? [Y,y,Yes,YES] [N,n,No,NO]"
            answer = gets.chomp!
    
            if YES.include?(answer) || NO.include?(answer)
              if YES.include?(answer)
                puts "THE OBJECT OF BUG IS TO FINISH YOUR BUG BEFORE I FINISH"
                puts "MINE. EACH NUMBER STANDS FOR A PART OF THE BUG BODY."
                puts "I WILL ROLL THE DIE FOR YOU, TELL YOU WHAT I ROLLED FOR YOU"
                puts "WHAT THE NUMBER STANDS FOR, AND IF YOU CAN GET THE PART."
                puts "IF YOU CAN GET THE PART I WILL GIVE IT TO YOU."
                puts "THE SAME WILL HAPPEN ON MY TURN."
                puts "IF THERE IS A CHANGE IN EITHER BUG I WILL GIVE YOU THE"
                puts "OPTION OF SEEING THE PICTURES OF THE BUGS."
                puts "THE NUMBERS STAND FOR PARTS AS FOLLOWS:\n\n\n"
                puts "Number    Part          Required Part #"
                puts "1         Body          1"
                puts "2         Neck          1"
                puts "3         Head          2"
                puts "4      (2)antennaes     3"
                puts "5      (2)arms          1"
                puts "6      (2)Legs          1"
              end
              break
            end
          end
        end
    
        def antennae_and_leg player, part
          if !player[part].eql? 0
            for i in (1...5) do
              if player[part].eql? 1
                puts "     N"
              else
                puts "     N    N"
              end
            end
          end
        end
    
        def head player
          if !player[:head].eql? 0
            puts "     NNNNNNN"
            puts "     N     N"
            puts "     N O O N"
            puts "     N     N"
            puts "     N  V  N"
            puts "     NNNNNNN"
          end
        end
    
        def neck player
          if !player[:neck].eql? 0
            puts "     N  N"
          end
        end
    
        def body player
          if !player[:body].eql? 0
            puts "     NNNNNNNNN"
            for i in (1...5) do
              if i.eql? 2
                if player[:arms].eql? 1
                  puts "NNNNNN       N"
                elsif player[:arms].eql? 2
                  
                  puts "NNNNNN       NNNNNN"
                end
              else
                puts "     N       N"
              end
            end
            puts "     NNNNNNNNN"
          end
        end
    end
    
    
    if __FILE__ == $0
      bug = BugGame.new
      puts "\n\nNOW WE START THE GAME\n\n"
      bug.run
    end
    
    ================================================
    FILE: 16_Bug/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 16_Bug/vbnet/Bug.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Bug", "Bug.vbproj", "{4F4EA8E5-55A8-4191-BE57-B28638C33609}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{4F4EA8E5-55A8-4191-BE57-B28638C33609}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{4F4EA8E5-55A8-4191-BE57-B28638C33609}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{4F4EA8E5-55A8-4191-BE57-B28638C33609}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{4F4EA8E5-55A8-4191-BE57-B28638C33609}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 16_Bug/vbnet/Bug.vbproj
    ================================================
    
      
        Exe
        Bug
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 16_Bug/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 17_Bullfight/README.md
    ================================================
    ### Bullfight
    
    In this simulated bullfight, you are the matador — i.e., the one with the principle role and the one who must kill the bull or be killed (or run from the ring).
    
    On each pass of the bull, you may try:
    - 0: Veronica (dangerous inside move of the cape)
    - 1: Less dangerous outside move of the cape
    - 2: Ordinary swirl of the cape
    
    Or you may try to kill the bull:
    - 4: Over the horns
    - 5: In the chest
    
    The crowd will determine what award you deserve, posthumously if necessary. The braver you are, the better the reward you receive. It’s nice to stay alive too. The better the job the picadores and toreadores do, the better your chances.
    
    David Sweet of Dartmouth wrote the original version of this program. It was then modified by students at Lexington High School and finally by Steve North of Creative Computing.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=32)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=47)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    - 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.
    
    - Lines 1800-1820 (part of the pre-fight subroutine) can never be reached.
    
    
    ================================================
    FILE: 17_Bullfight/bullfight.bas
    ================================================
    10 PRINT TAB(34);"BULL"
    20 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    30 DEF FNA(K)=INT(RND(1)*2+1)
    200 PRINT:PRINT:PRINT
    202 L=1
    205 PRINT "DO YOU WANT INSTRUCTIONS";
    206 INPUT Z$
    207 IF Z$="NO" THEN 400
    210 PRINT "HELLO, ALL YOU BLOODLOVERS AND AFICIONADOS."
    220 PRINT "HERE IS YOUR BIG CHANCE TO KILL A BULL."
    230 PRINT
    240 PRINT "ON EACH PASS OF THE BULL, YOU MAY TRY"
    250 PRINT "0 - VERONICA (DANGEROUS INSIDE MOVE OF THE CAPE)"
    260 PRINT "1 - LESS DANGEROUS OUTSIDE MOVE OF THE CAPE"
    270 PRINT "2 - ORDINARY SWIRL OF THE CAPE."
    280 PRINT
    290 PRINT "INSTEAD OF THE ABOVE, YOU MAY TRY TO KILL THE BULL"
    300 PRINT "ON ANY TURN: 4 (OVER THE HORNS), 5 (IN THE CHEST)."
    310 PRINT "BUT IF I WERE YOU,"
    320 PRINT "I WOULDN'T TRY IT BEFORE THE SEVENTH PASS."
    330 PRINT
    340 PRINT "THE CROWD WILL DETERMINE WHAT AWARD YOU DESERVE"
    350 PRINT "(POSTHUMOUSLY IF NECESSARY)."
    360 PRINT "THE BRAVER YOU ARE, THE BETTER THE AWARD YOU RECEIVE."
    370 PRINT
    380 PRINT "THE BETTER THE JOB THE PICADORES AND TOREADORES DO,"
    390 PRINT "THE BETTER YOUR CHANCES ARE."
    400 PRINT
    410 PRINT
    420 D(5)=1
    430 D(4)=1
    450 DIM L$(5)
    455 A=INT(RND(1)*5+1)
    460 FOR I=1 TO 5
    463 READ L$(I)
    467 NEXT I
    470 DATA "SUPERB","GOOD","FAIR","POOR","AWFUL"
    490 PRINT "YOU HAVE DRAWN A ";L$(A);" BULL."
    500 IF A>4 THEN 530
    510 IF A<2 THEN 550
    520 GOTO 570
    530 PRINT "YOU'RE LUCKY."
    540 GOTO 570
    550 PRINT "GOOD LUCK.  YOU'LL NEED IT."
    560 PRINT
    570 PRINT
    590 A$="PICADO"
    595 B$="RES"
    600 GOSUB 1610
    610 D(1)=C
    630 A$="TOREAD"
    635 B$="ORES"
    640 GOSUB 1610
    650 D(2)=C
    660 PRINT
    670 PRINT
    680 IF Z=1 THEN 1310
    690 D(3)=D(3)+1
    700 PRINT "PASS NUMBER";D(3)
    710 IF D(3)<3 THEN 760
    720 PRINT "HERE COMES THE BULL.  TRY FOR A KILL";
    730 GOSUB 1930
    735 IF Z1=1 THEN 1130
    740 PRINT "CAPE MOVE";
    750 GOTO 800
    760 PRINT "THE BULL IS CHARGING AT YOU!  YOU ARE THE MATADOR--"
    770 PRINT "DO YOU WANT TO KILL THE BULL";
    780 GOSUB 1930
    785 IF Z1=1 THEN 1130
    790 PRINT "WHAT MOVE DO YOU MAKE WITH THE CAPE";
    800 INPUT E
    810 IF E<>INT(ABS(E)) THEN 830
    820 IF E<3 THEN 850
    830 PRINT "DON'T PANIC, YOU IDIOT!  PUT DOWN A CORRECT NUMBER"
    840 GOTO 800
    850 REM
    860 IF E=0 THEN 920
    870 IF E=1 THEN 900
    880 M=.5
    890 GOTO 930
    900 M=2
    910 GOTO 930
    920 M=3
    930 L=L+M
    940 F=(6-A+M/10)*RND(1)/((D(1)+D(2)+D(3)/10)*5)
    950 IF F<.51 THEN 660
    960 PRINT "THE BULL HAS GORED YOU!"
    970 ON FNA(0) GOTO 980,1010
    980 PRINT "YOU ARE DEAD."
    990 D(4)=1.5
    1000 GOTO 1310
    1010 PRINT "YOU ARE STILL ALIVE.":PRINT
    1020 PRINT "DO YOU RUN FROM THE RING";
    1030 GOSUB 1930
    1035 IF Z1=2 THEN 1070
    1040 PRINT "COWARD"
    1050 D(4)=0
    1060 GOTO 1310
    1070 PRINT "YOU ARE BRAVE.  STUPID, BUT BRAVE."
    1080 ON FNA(0) GOTO 1090,1110
    1090 D(4)=2
    1100 GOTO 660
    1110 PRINT "YOU ARE GORED AGAIN!"
    1120 GOTO 970
    1130 REM
    1140 Z=1
    1150 PRINT:PRINT "IT IS THE MOMENT OF TRUTH.":PRINT
    1155 PRINT "HOW DO YOU TRY TO KILL THE BULL";
    1160 INPUT H
    1170 IF H=4 THEN 1230
    1180 IF H=5 THEN 1230
    1190 PRINT "YOU PANICKED.  THE BULL GORED YOU."
    1220 GOTO 970
    1230 K=(6-A)*10*RND(1)/((D(1)+D(2))*5*D(3))
    1240 IF H=4 THEN 1290
    1250 IF K>.2 THEN 960
    1260 PRINT "YOU KILLED THE BULL!"
    1270 D(5)=2
    1280 GOTO 1320
    1290 IF K>.8 THEN 960
    1300 GOTO 1260
    1310 PRINT
    1320 PRINT
    1330 PRINT
    1340 IF D(4)<>0 THEN 1390
    1350 PRINT "THE CROWD BOOS FOR TEN MINUTES.  IF YOU EVER DARE TO SHOW"
    1360 PRINT "YOUR FACE IN A RING AGAIN, THEY SWEAR THEY WILL KILL YOU--"
    1370 PRINT "UNLESS THE BULL DOES FIRST."
    1380 GOTO 1580
    1390 DEF FNC(Q)=FND(Q)*RND(1)
    1395 DEF FND(Q)=(4.5+L/6-(D(1)+D(2))*2.5+4*D(4)+2*D(5)-D(3)^2/120-A)
    1400 IF D(4)<>2 THEN 1430
    1410 PRINT "THE CROWD CHEERS WILDLY!"
    1420 GOTO 1450
    1430 IF D(5)<>2 THEN 1450
    1440 PRINT "THE CROWD CHEERS!":PRINT
    1450 PRINT "THE CROWD AWARDS YOU"
    1460 IF FNC(Q)<2.4 THEN 1570
    1470 IF FNC(Q)<4.9 THEN 1550
    1480 IF FNC(Q)<7.4 THEN 1520
    1500 PRINT "OLE!  YOU ARE 'MUY HOMBRE'!! OLE!  OLE!"
    1510 GOTO 1580
    1520 PRINT "BOTH EARS OF THE BULL!"
    1530 PRINT "OLE!"
    1540 GOTO 1580
    1550 PRINT "ONE EAR OF THE BULL."
    1560 GOTO 1580
    1570 PRINT "NOTHING AT ALL."
    1580 PRINT
    1590 PRINT "ADIOS":PRINT:PRINT:PRINT
    1600 GOTO 2030
    1610 B=3/A*RND(1)
    1620 IF B<.37 THEN 1740
    1630 IF B<.5 THEN 1720
    1640 IF B<.63 THEN 1700
    1650 IF B<.87 THEN 1680
    1660 C=.1
    1670 GOTO 1750
    1680 C=.2
    1690 GOTO 1750
    1700 C=.3
    1710 GOTO 1750
    1720 C=.4
    1730 GOTO 1750
    1740 C=.5
    1750 T=INT(10*C+.2)
    1760 PRINT "THE ";A$;B$;" DID A ";L$(T);" JOB."
    1770 IF 4>T THEN 1900
    1780 IF 5=T THEN 1870
    1790 ON FNA(K) GOTO 1830,1850
    1800 IF A$="TOREAD" THEN 1820
    1810 PRINT "ONE OF THE HORSES OF THE ";A$;B$;" WAS KILLED."
    1820 ON FNA(K) GOTO 1830,1850
    1830 PRINT "ONE OF THE ";A$;B$;" WAS KILLED."
    1840 GOTO 1900
    1850 PRINT "NO ";A$;B$;" WERE KILLED."
    1860 GOTO 1900
    1870 IF A$="TOREAD" THEN 1890
    1880 PRINT FNA(K);"OF THE HORSES OF THE ";A$;B$;" KILLED."
    1890 PRINT FNA(K);"OF THE ";A$;B$;" KILLED."
    1900 PRINT
    1910 RETURN
    1920 REM
    1930 INPUT A$
    1940 IF A$="YES" THEN 1990
    1950 IF A$="NO" THEN 2010
    1970 PRINT "INCORRECT ANSWER - - PLEASE TYPE 'YES' OR 'NO'."
    1980 GOTO 1930
    1990 Z1=1
    2000 GOTO 2020
    2010 Z1=2
    2020 RETURN
    2030 END
    
    
    ================================================
    FILE: 17_Bullfight/csharp/Action.cs
    ================================================
    namespace Game
    {
        /// 
        /// Enumerates the different actions that the player can take on each round
        /// of the fight.
        /// 
        public enum Action
        {
            /// 
            /// Dodge the bull.
            /// 
            Dodge,
    
            /// 
            /// Kill the bull.
            /// 
            Kill,
    
            /// 
            /// Freeze in place and don't do anything.
            /// 
            Panic
        }
    }
    
    
    ================================================
    FILE: 17_Bullfight/csharp/ActionResult.cs
    ================================================
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace Game
    {
        /// 
        /// Enumerates the different possible outcomes of the player's action.
        /// 
        public enum ActionResult
        {
            /// 
            /// The fight continues.
            /// 
            FightContinues,
    
            /// 
            /// The player fled from the ring.
            /// 
            PlayerFlees,
    
            /// 
            /// The bull has gored the player.
            /// 
            BullGoresPlayer,
    
            /// 
            /// The bull killed the player.
            /// 
            BullKillsPlayer,
    
            /// 
            /// The player killed the bull.
            /// 
            PlayerKillsBull,
    
            /// 
            /// The player attempted to kill the bull and both survived.
            /// 
            Draw
        }
    }
    
    
    ================================================
    FILE: 17_Bullfight/csharp/BullFight.cs
    ================================================
    using System;
    using System.Collections.Generic;
    
    namespace Game
    {
        /// 
        /// Provides a method for simulating a bull fight.
        /// 
        public static class BullFight
        {
            /// 
            /// Begins a new fight.
            /// 
            /// 
            /// Object used to communicate with the player.
            /// 
            /// 
            /// The sequence of events that take place during the fight.
            /// 
            /// 
            /// After receiving each event, the caller must invoke the appropriate
            /// mediator method to inform this coroutine what to do next.  Failure
            /// to do so will result in an exception.
            /// 
            public static IEnumerable Begin(Mediator mediator)
            {
                var random = new Random();
                var result = ActionResult.FightContinues;
    
                var bullQuality          = GetBullQuality();
                var toreadorePerformance = GetHelpQuality(bullQuality);
                var picadorePerformance  = GetHelpQuality(bullQuality);
    
                var bullStrength    = 6 - (int)bullQuality;
                var assistanceLevel = (12 - (int)toreadorePerformance - (int)picadorePerformance) * 0.1;
                var bravery         = 1.0;
                var style           = 1.0;
                var passNumber      = 0;
    
                yield return new Events.MatchStarted(
                    bullQuality,
                    toreadorePerformance,
                    picadorePerformance,
                    GetHumanCasualties(toreadorePerformance),
                    GetHumanCasualties(picadorePerformance),
                    GetHorseCasualties(picadorePerformance));
    
                while (result == ActionResult.FightContinues)
                {
                    yield return new Events.BullCharging(++passNumber);
    
                    var (action, riskLevel) = mediator.GetInput<(Action, RiskLevel)>();
                    result = action switch
                    {
                        Action.Dodge => TryDodge(riskLevel),
                        Action.Kill  => TryKill(riskLevel),
                        _            => Panic()
                    };
    
                    var first = true;
                    while (result == ActionResult.BullGoresPlayer)
                    {
                        yield return new Events.PlayerGored(action == Action.Panic, first);
                        first = false;
    
                        result = TrySurvive();
                        if (result == ActionResult.FightContinues)
                        {
                            yield return new Events.PlayerSurvived();
    
                            var runFromRing = mediator.GetInput();
                            if (runFromRing)
                                result = Flee();
                            else
                                result = IgnoreInjury(action);
                        }
                    }
                }
    
                yield return new Events.MatchCompleted(
                    result,
                    bravery == 2,
                    GetReward());
    
                Quality GetBullQuality() =>
                    (Quality)random.Next(1, 6);
    
                Quality GetHelpQuality(Quality bullQuality) =>
                    ((3.0 / (int)bullQuality) * random.NextDouble()) switch
                    {
                        < 0.37 => Quality.Superb,
                        < 0.50 => Quality.Good,
                        < 0.63 => Quality.Fair,
                        < 0.87 => Quality.Poor,
                        _      => Quality.Awful
                    };
    
                int GetHumanCasualties(Quality performance) =>
                    performance switch
                    {
                        Quality.Poor  => random.Next(0, 2),
                        Quality.Awful => random.Next(1, 3),
                        _             => 0
                    };
    
                int GetHorseCasualties(Quality performance) =>
                    performance switch
                    {
                        // NOTE: The code for displaying a single horse casuality
                        //  following a poor picadore peformance was unreachable
                        //  in the original BASIC version.  I've assumed this was
                        //  a bug.
                        Quality.Poor  => 1,
                        Quality.Awful => random.Next(1, 3),
                        _             => 0
                    };
    
                ActionResult TryDodge(RiskLevel riskLevel)
                {
                    var difficultyModifier = riskLevel switch
                    {
                        RiskLevel.High   => 3.0,
                        RiskLevel.Medium => 2.0,
                        _                => 0.5
                    };
    
                    var outcome = (bullStrength + (difficultyModifier / 10)) * random.NextDouble() /
                        ((assistanceLevel + (passNumber / 10.0)) * 5);
    
                    if (outcome < 0.51)
                    {
                        style += difficultyModifier;
                        return ActionResult.FightContinues;
                    }
                    else
                        return ActionResult.BullGoresPlayer;
                }
    
                ActionResult TryKill(RiskLevel riskLevel)
                {
                    var luck = bullStrength * 10 * random.NextDouble() / (assistanceLevel * 5 * passNumber);
    
                    return ((riskLevel == RiskLevel.High && luck > 0.2) || luck > 0.8) ?
                        ActionResult.BullGoresPlayer : ActionResult.PlayerKillsBull;
                }
    
                ActionResult Panic() =>
                    ActionResult.BullGoresPlayer;
    
                ActionResult TrySurvive()
                {
                    if (random.Next(2) == 0)
                    {
                        bravery = 1.5;
                        return ActionResult.BullKillsPlayer;
                    }
                    else
                        return ActionResult.FightContinues;
                }
    
                ActionResult Flee()
                {
                    bravery = 0.0;
                    return ActionResult.PlayerFlees;
                }
    
                ActionResult IgnoreInjury(Action action)
                {
                    if (random.Next(2) == 0)
                    {
                        bravery = 2.0;
                        return action == Action.Dodge ? ActionResult.FightContinues : ActionResult.Draw;
                    }
                    else
                        return ActionResult.BullGoresPlayer;
                }
    
                Reward GetReward()
                {
                    var score = CalculateScore();
    
                    if (score * random.NextDouble() < 2.4)
                        return Reward.Nothing;
                    else
                    if (score * random.NextDouble() < 4.9)
                        return Reward.OneEar;
                    else
                    if (score * random.NextDouble() < 7.4)
                        return Reward.TwoEars;
                    else
                        return Reward.CarriedFromRing;
                }
    
                double CalculateScore()
                {
                    var score = 4.5;
    
                    // Style
                    score += style / 6;
    
                    // Assisstance
                    score -= assistanceLevel * 2.5;
    
                    // Courage
                    score += 4 * bravery;
    
                    // Kill bonus
                    score += (result == ActionResult.PlayerKillsBull) ? 4 : 2;
    
                    // Match length
                    score -= Math.Pow(passNumber, 2) / 120;
    
                    // Difficulty
                    score -= (int)bullQuality;
    
                    return score;
                }
            }
        }
    }
    
    
    ================================================
    FILE: 17_Bullfight/csharp/Bullfight.csproj
    ================================================
    
      
        Exe
        net5.0
        enable
      
    
    
    
    ================================================
    FILE: 17_Bullfight/csharp/Bullfight.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.31321.278
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bullfight", "Bullfight.csproj", "{502A672C-F7D7-4A85-973A-B5EA8761008A}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{502A672C-F7D7-4A85-973A-B5EA8761008A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{502A672C-F7D7-4A85-973A-B5EA8761008A}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{502A672C-F7D7-4A85-973A-B5EA8761008A}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{502A672C-F7D7-4A85-973A-B5EA8761008A}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {C5BFC749-C7D8-4981-A7D4-1D401901A890}
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 17_Bullfight/csharp/Controller.cs
    ================================================
    using System;
    
    namespace Game
    {
        /// 
        /// Contains functions for getting input from the user.
        /// 
        public static class Controller
        {
            /// 
            /// Handles the initial interaction with the player.
            /// 
            public static void StartGame()
            {
                View.ShowBanner();
                View.PromptShowInstructions();
    
                var input = Console.ReadLine();
                if (input is null)
                    Environment.Exit(0);
    
                if (input.ToUpperInvariant() != "NO")
                    View.ShowInstructions();
    
                View.ShowSeparator();
            }
    
            /// 
            /// Gets the player's action for the current round.
            /// 
            /// 
            /// The current pass number.
            /// 
            public static (Action action, RiskLevel riskLevel) GetPlayerIntention(int passNumber)
            {
                if (passNumber < 3)
                    View.PromptKillBull();
                else
                    View.PromptKillBullBrief();
    
                var attemptToKill = GetYesOrNo();
    
                if (attemptToKill)
                {
                    View.PromptKillMethod();
    
                    var input = Console.ReadLine();
                    if (input is null)
                        Environment.Exit(0);
    
                    return input switch
                    {
                        "4" => (Action.Kill,  RiskLevel.High),
                        "5" => (Action.Kill,  RiskLevel.Low),
                        _   => (Action.Panic, default(RiskLevel))
                    };
                }
                else
                {
                    if (passNumber < 2)
                        View.PromptCapeMove();
                    else
                        View.PromptCapeMoveBrief();
    
                    var action = Action.Panic;
                    var riskLevel = default(RiskLevel);
    
                    while (action == Action.Panic)
                    {
                        var input = Console.ReadLine();
                        if (input is null)
                            Environment.Exit(0);
    
                        (action, riskLevel) = input switch
                        {
                            "0" => (Action.Dodge, RiskLevel.High),
                            "1" => (Action.Dodge, RiskLevel.Medium),
                            "2" => (Action.Dodge, RiskLevel.Low),
                            _   => (Action.Panic, default(RiskLevel))
                        };
    
                        if (action == Action.Panic)
                            View.PromptDontPanic();
                    }
    
                    return (action, riskLevel);
                }
            }
    
            /// 
            /// Gets the player's intention to flee (or not).
            /// 
            /// 
            /// True if the player flees; otherwise, false.
            /// 
            public static bool GetPlayerRunsFromRing()
            {
                View.PromptRunFromRing();
    
                var playerFlees = GetYesOrNo();
                if (!playerFlees)
                    View.ShowPlayerFoolhardy();
    
                return playerFlees;
            }
    
            /// 
            /// Gets a yes or no response from the player.
            /// 
            /// 
            /// True if the user answered yes; otherwise, false.
            /// 
            public static bool GetYesOrNo()
            {
                while (true)
                {
                    var input = Console.ReadLine();
                    if (input is null)
                        Environment.Exit(0);
    
                    switch (input.ToUpperInvariant())
                    {
                        case "YES":
                            return true;
                        case "NO":
                            return false;
                        default:
                            Console.WriteLine("INCORRECT ANSWER - - PLEASE TYPE 'YES' OR 'NO'.");
                            break;
                    }
                }
            }
        }
    }
    
    
    ================================================
    FILE: 17_Bullfight/csharp/Events/BullCharging.cs
    ================================================
    namespace Game.Events
    {
        /// 
        /// Indicates that the bull is charing the player.
        /// 
        public sealed record BullCharging(int PassNumber) : Event;
    }
    
    
    ================================================
    FILE: 17_Bullfight/csharp/Events/Event.cs
    ================================================
    namespace Game.Events
    {
        /// 
        /// Common base class for all events in the game.
        /// 
        public abstract record Event();
    }
    
    
    ================================================
    FILE: 17_Bullfight/csharp/Events/MatchCompleted.cs
    ================================================
    namespace Game.Events
    {
        /// 
        /// Indicates that the fight has completed.
        /// 
        public sealed record MatchCompleted(ActionResult Result, bool ExtremeBravery, Reward Reward) : Event;
    }
    
    
    ================================================
    FILE: 17_Bullfight/csharp/Events/MatchStarted.cs
    ================================================
    namespace Game.Events
    {
        /// 
        /// Indicates that a new match has started.
        /// 
        public sealed record MatchStarted(
            Quality BullQuality,
            Quality ToreadorePerformance,
            Quality PicadorePerformance,
            int ToreadoresKilled,
            int PicadoresKilled,
            int HorsesKilled) : Event;
    }
    
    
    ================================================
    FILE: 17_Bullfight/csharp/Events/PlayerGored.cs
    ================================================
    namespace Game.Events
    {
        /// 
        /// Indicates that the player has been gored by the bull.
        /// 
        public sealed record PlayerGored(bool Panicked, bool FirstGoring) : Event;
    }
    
    
    ================================================
    FILE: 17_Bullfight/csharp/Events/PlayerSurvived.cs
    ================================================
    namespace Game.Events
    {
        /// 
        /// Indicates that the player has survived being gored by the bull.
        /// 
        public sealed record PlayerSurvived() : Event;
    }
    
    
    ================================================
    FILE: 17_Bullfight/csharp/Mediator.cs
    ================================================
    using System.Diagnostics;
    
    namespace Game
    {
        /// 
        /// Facilitates sending messages between the two game loops.
        /// 
        /// 
        /// This class serves as a little piece of glue in between the main program
        /// loop and the bull fight coroutine.  When the main program calls one of
        /// its methods, the mediator creates the appropriate input data that the
        /// bull fight coroutine later retrieves with .
        /// 
        public class Mediator
        {
            private object? m_input;
    
            public void Dodge(RiskLevel riskLevel) =>
                m_input = (Action.Dodge, riskLevel);
    
            public void Kill(RiskLevel riskLevel) =>
                m_input = (Action.Kill, riskLevel);
    
            public void Panic() =>
                m_input = (Action.Panic, default(RiskLevel));
    
            public void RunFromRing() =>
                m_input = true;
    
            public void ContinueFighting() =>
                m_input = false;
    
            /// 
            /// Gets the next input from the user.
            /// 
            /// 
            /// The type of input to receive.
            /// 
            public T GetInput()
            {
                Debug.Assert(m_input is not null, "No input received");
                Debug.Assert(m_input.GetType() == typeof(T), "Invalid input received");
                var result = (T)m_input;
                m_input = null;
                return result;
            }
        }
    }
    
    
    ================================================
    FILE: 17_Bullfight/csharp/Program.cs
    ================================================
    namespace Game
    {
        class Program
        {
            static void Main()
            {
                Controller.StartGame();
    
                var mediator = new Mediator();
                foreach (var evt in BullFight.Begin(mediator))
                {
                    switch (evt)
                    {
                        case Events.MatchStarted matchStarted:
                            View.ShowStartingConditions(matchStarted);
                            break;
    
                        case Events.BullCharging bullCharging:
                            View.ShowStartOfPass(bullCharging.PassNumber);
                            var (action, riskLevel) = Controller.GetPlayerIntention(bullCharging.PassNumber);
                            switch (action)
                            {
                                case Action.Dodge:
                                    mediator.Dodge(riskLevel);
                                    break;
                                case Action.Kill:
                                    mediator.Kill(riskLevel);
                                    break;
                                case Action.Panic:
                                    mediator.Panic();
                                    break;
                            }
                            break;
    
                        case Events.PlayerGored playerGored:
                            View.ShowPlayerGored(playerGored.Panicked, playerGored.FirstGoring);
                            break;
    
                        case Events.PlayerSurvived:
                            View.ShowPlayerSurvives();
                            if (Controller.GetPlayerRunsFromRing())
                                mediator.RunFromRing();
                            else
                                mediator.ContinueFighting();
                            break;
    
                        case Events.MatchCompleted matchCompleted:
                            View.ShowFinalResult(matchCompleted.Result, matchCompleted.ExtremeBravery, matchCompleted.Reward);
                            break;
                    }
                }
            }
        }
    }
    
    
    ================================================
    FILE: 17_Bullfight/csharp/Quality.cs
    ================================================
    namespace Game
    {
        /// 
        /// Enumerates the different levels of quality in the game.
        /// 
        /// 
        /// Quality applies both to the bull and to the help received from the
        /// toreadores and picadores.  Note that the ordinal values are significant
        /// (these are used in various calculations).
        /// 
        public enum Quality
        {
            Superb  = 1,
            Good    = 2,
            Fair    = 3,
            Poor    = 4,
            Awful   = 5
        }
    }
    
    
    ================================================
    FILE: 17_Bullfight/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 17_Bullfight/csharp/Reward.cs
    ================================================
    namespace Game
    {
        /// 
        /// Enumerates the different things the player can be awarded.
        /// 
        public enum Reward
        {
            Nothing,
            OneEar,
            TwoEars,
            CarriedFromRing
        }
    }
    
    
    ================================================
    FILE: 17_Bullfight/csharp/RiskLevel.cs
    ================================================
    namespace Game
    {
        /// 
        /// Enumerates the different levels of risk for manoeuvres in the game.
        /// 
        public enum RiskLevel
        {
            Low,
            Medium,
            High
        }
    }
    
    
    ================================================
    FILE: 17_Bullfight/csharp/View.cs
    ================================================
    using System;
    
    namespace Game
    {
        /// 
        /// Contains functions for displaying information to the user.
        /// 
        public static class View
        {
            private static readonly string[] QualityString = { "SUPERB", "GOOD", "FAIR", "POOR", "AWFUL" };
    
            public static void ShowBanner()
            {
                Console.WriteLine("                                  BULL");
                Console.WriteLine("               CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine();
            }
    
            public static void ShowInstructions()
            {
                Console.WriteLine("HELLO, ALL YOU BLOODLOVERS AND AFICIONADOS.");
                Console.WriteLine("HERE IS YOUR BIG CHANCE TO KILL A BULL.");
                Console.WriteLine();
                Console.WriteLine("ON EACH PASS OF THE BULL, YOU MAY TRY");
                Console.WriteLine("0 - VERONICA (DANGEROUS INSIDE MOVE OF THE CAPE)");
                Console.WriteLine("1 - LESS DANGEROUS OUTSIDE MOVE OF THE CAPE");
                Console.WriteLine("2 - ORDINARY SWIRL OF THE CAPE.");
                Console.WriteLine();
                Console.WriteLine("INSTEAD OF THE ABOVE, YOU MAY TRY TO KILL THE BULL");
                Console.WriteLine("ON ANY TURN: 4 (OVER THE HORNS), 5 (IN THE CHEST).");
                Console.WriteLine("BUT IF I WERE YOU,");
                Console.WriteLine("I WOULDN'T TRY IT BEFORE THE SEVENTH PASS.");
                Console.WriteLine();
                Console.WriteLine("THE CROWD WILL DETERMINE WHAT AWARD YOU DESERVE");
                Console.WriteLine("(POSTHUMOUSLY IF NECESSARY).");
                Console.WriteLine("THE BRAVER YOU ARE, THE BETTER THE AWARD YOU RECEIVE.");
                Console.WriteLine();
                Console.WriteLine("THE BETTER THE JOB THE PICADORES AND TOREADORES DO,");
                Console.WriteLine("THE BETTER YOUR CHANCES ARE.");
            }
    
            public static void ShowSeparator()
            {
                Console.WriteLine();
                Console.WriteLine();
            }
    
            public static void ShowStartingConditions(Events.MatchStarted matchStarted)
            {
                ShowBullQuality();
                ShowHelpQuality("TOREADORES", matchStarted.ToreadorePerformance, matchStarted.ToreadoresKilled, 0);
                ShowHelpQuality("PICADORES", matchStarted.PicadorePerformance, matchStarted.PicadoresKilled, matchStarted.HorsesKilled);
    
                void ShowBullQuality()
                {
                    Console.WriteLine($"YOU HAVE DRAWN A {QualityString[(int)matchStarted.BullQuality - 1]} BULL.");
    
                    if (matchStarted.BullQuality > Quality.Poor)
                    {
                        Console.WriteLine("YOU'RE LUCKY");
                    }
                    else
                    if (matchStarted.BullQuality < Quality.Good)
                    {
                        Console.WriteLine("GOOD LUCK.  YOU'LL NEED IT.");
                        Console.WriteLine();
                    }
    
                    Console.WriteLine();
                }
    
                static void ShowHelpQuality(string helperName, Quality helpQuality, int helpersKilled, int horsesKilled)
                {
                    Console.WriteLine($"THE {helperName} DID A {QualityString[(int)helpQuality - 1]} JOB.");
    
                    // NOTE: The code below makes some *strong* assumptions about
                    //  how the casualty numbers were generated.  It is written
                    //  this way to preserve the behaviour of the original BASIC
                    //  version, but it would make more sense ignore the helpQuality
                    //  parameter and just use the provided numbers to decide what
                    //  to display.
                    switch (helpQuality)
                    {
                        case Quality.Poor:
                            if (horsesKilled > 0)
                                Console.WriteLine($"ONE OF THE HORSES OF THE {helperName} WAS KILLED.");
    
                            if (helpersKilled > 0)
                                Console.WriteLine($"ONE OF THE {helperName} WAS KILLED.");
                            else
                                Console.WriteLine($"NO {helperName} WERE KILLED.");
                            break;
    
                        case Quality.Awful:
                            if (horsesKilled > 0)
                                Console.WriteLine($" {horsesKilled} OF THE HORSES OF THE {helperName} KILLED.");
    
                            Console.WriteLine($" {helpersKilled} OF THE {helperName} KILLED.");
                            break;
                    }
                }
            }
    
            public static void ShowStartOfPass(int passNumber)
            {
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine($"PASS NUMBER {passNumber}");
            }
    
            public static void ShowPlayerGored(bool playerPanicked, bool firstGoring)
            {
                Console.WriteLine((playerPanicked, firstGoring) switch
                {
                    (true,  true) => "YOU PANICKED.  THE BULL GORED YOU.",
                    (false, true) => "THE BULL HAS GORED YOU!",
                    (_, false)    => "YOU ARE GORED AGAIN!"
                });
            }
    
            public static void ShowPlayerSurvives()
            {
                Console.WriteLine("YOU ARE STILL ALIVE.");
                Console.WriteLine();
            }
    
            public static void ShowPlayerFoolhardy()
            {
                Console.WriteLine("YOU ARE BRAVE.  STUPID, BUT BRAVE.");
            }
    
            public static void ShowFinalResult(ActionResult result, bool extremeBravery, Reward reward)
            {
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine();
    
                switch (result)
                {
                    case ActionResult.PlayerFlees:
                        Console.WriteLine("COWARD");
                        break;
                    case ActionResult.BullKillsPlayer:
                        Console.WriteLine("YOU ARE DEAD.");
                        break;
                    case ActionResult.PlayerKillsBull:
                        Console.WriteLine("YOU KILLED THE BULL!");
                        break;
                }
    
                if (result == ActionResult.PlayerFlees)
                {
                    Console.WriteLine("THE CROWD BOOS FOR TEN MINUTES.  IF YOU EVER DARE TO SHOW");
                    Console.WriteLine("YOUR FACE IN A RING AGAIN, THEY SWEAR THEY WILL KILL YOU--");
                    Console.WriteLine("UNLESS THE BULL DOES FIRST.");
                }
                else
                {
                    if (extremeBravery)
                        Console.WriteLine("THE CROWD CHEERS WILDLY!");
                    else
                    if (result == ActionResult.PlayerKillsBull)
                    {
                        Console.WriteLine("THE CROWD CHEERS!");
                        Console.WriteLine();
                    }
    
                    Console.WriteLine("THE CROWD AWARDS YOU");
                    switch (reward)
                    {
                        case Reward.Nothing:
                            Console.WriteLine("NOTHING AT ALL.");
                            break;
                        case Reward.OneEar:
                            Console.WriteLine("ONE EAR OF THE BULL.");
                            break;
                        case Reward.TwoEars:
                            Console.WriteLine("BOTH EARS OF THE BULL!");
                            Console.WriteLine("OLE!");
                            break;
                        default:
                            Console.WriteLine("OLE!  YOU ARE 'MUY HOMBRE'!! OLE!  OLE!");
                            break;
                    }
                }
    
                Console.WriteLine();
                Console.WriteLine("ADIOS");
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine();
            }
    
            public static void PromptShowInstructions()
            {
                Console.Write("DO YOU WANT INSTRUCTIONS? ");
            }
    
            public static void PromptKillBull()
            {
                Console.WriteLine("THE BULL IS CHARGING AT YOU!  YOU ARE THE MATADOR--");
                Console.Write("DO YOU WANT TO KILL THE BULL? ");
            }
    
            public static void PromptKillBullBrief()
            {
                Console.Write("HERE COMES THE BULL.  TRY FOR A KILL? ");
            }
    
            public static void PromptKillMethod()
            {
                Console.WriteLine();
                Console.WriteLine("IT IS THE MOMENT OF TRUTH.");
                Console.WriteLine();
    
                Console.Write("HOW DO YOU TRY TO KILL THE BULL? ");
            }
    
            public static void PromptCapeMove()
            {
                Console.Write("WHAT MOVE DO YOU MAKE WITH THE CAPE? ");
            }
    
            public static void PromptCapeMoveBrief()
            {
                Console.Write("CAPE MOVE? ");
            }
    
            public static void PromptDontPanic()
            {
                Console.WriteLine("DON'T PANIC, YOU IDIOT!  PUT DOWN A CORRECT NUMBER");
                Console.Write("? ");
            }
    
            public static void PromptRunFromRing()
            {
                Console.Write("DO YOU RUN FROM THE RING? ");
            }
        }
    }
    
    
    ================================================
    FILE: 17_Bullfight/java/Bullfight.java
    ================================================
    import java.util.Random;
    import java.util.Scanner;
    
    /**
     * BULLFIGHT
     * 

    * 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; } } } ================================================ FILE: 17_Bullfight/java/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Oracle Java](https://openjdk.java.net/) ================================================ FILE: 17_Bullfight/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 17_Bullfight/javascript/bullfight.html ================================================ BULLFIGHT

    
    
    
    
    
    
    ================================================
    FILE: 17_Bullfight/javascript/bullfight.js
    ================================================
    // BULLFIGHT
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    var a;
    var b;
    var c;
    var l;
    var t;
    var as;
    var bs;
    var d = [];
    var ls = [, "SUPERB", "GOOD", "FAIR", "POOR", "AWFUL"];
    
    function af(k)
    {
        return Math.floor(Math.random() * 2 + 1);
    }
    
    function cf(q)
    {
        return df(q) * Math.random();
    }
    
    function df(q)
    {
        return (4.5 + l / 6 - (d[1] + d[2]) * 2.5 + 4 * d[4] + 2 * d[5] - Math.pow(d[3], 2) / 120 - a);
    }
    
    function setup_helpers()
    {
        b = 3 / a * Math.random();
        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;
        t = Math.floor(10 * c + 0.2);
        print("THE " + as + bs + " DID A " + ls[t] + " JOB.\n");
        if (4 <= t) {
            if (5 != t) {
                // Lines 1800 and 1810 of original program are unreachable
                switch (af(0)) {
                    case 1:
                        print("ONE OF THE " + as + bs + " WAS KILLED.\n");
                        break;
                    case 2:
                        print("NO " + as + b + " WERE KILLED.\n");
                        break;
                }
            } else {
                if (as != "TOREAD")
                    print(af(0) + " OF THE HORSES OF THE " + as + bs + " KILLED.\n");
                print(af(0) + " OF THE " + as + bs + " KILLED.\n");
            }
        }
        print("\n");
    }
    
    // Main program
    async function main()
    {
        print(tab(34) + "BULL\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        l = 1;
        print("DO YOU WANT INSTRUCTIONS");
        str = await input();
        if (str != "NO") {
            print("HELLO, ALL YOU BLOODLOVERS AND AFICIONADOS.\n");
            print("HERE IS YOUR BIG CHANCE TO KILL A BULL.\n");
            print("\n");
            print("ON EACH PASS OF THE BULL, YOU MAY TRY\n");
            print("0 - VERONICA (DANGEROUS INSIDE MOVE OF THE CAPE)\n");
            print("1 - LESS DANGEROUS OUTSIDE MOVE OF THE CAPE\n");
            print("2 - ORDINARY SWIRL OF THE CAPE.\n");
            print("\n");
            print("INSTEAD OF THE ABOVE, YOU MAY TRY TO KILL THE BULL\n");
            print("ON ANY TURN: 4 (OVER THE HORNS), 5 (IN THE CHEST).\n");
            print("BUT IF I WERE YOU,\n");
            print("I WOULDN'T TRY IT BEFORE THE SEVENTH PASS.\n");
            print("\n");
            print("THE CROWD WILL DETERMINE WHAT AWARD YOU DESERVE\n");
            print("(POSTHUMOUSLY IF NECESSARY).\n");
            print("THE BRAVER YOU ARE, THE BETTER THE AWARD YOU RECEIVE.\n");
            print("\n");
            print("THE BETTER THE JOB THE PICADORES AND TOREADORES DO,\n");
            print("THE BETTER YOUR CHANCES ARE.\n");
        }
        print("\n");
        print("\n");
        d[5] = 1;
        d[4] = 1;
        d[3] = 0;
        a = Math.floor(Math.random() * 5 + 1);
        print("YOU HAVE DRAWN A " + ls[a] + " BULL.\n");
        if (a > 4) {
            print("YOU'RE LUCKY.\n");
        } else if (a < 2) {
            print("GOOD LUCK.  YOU'LL NEED IT.\n");
            print("\n");
        }
        print("\n");
        as = "PICADO";
        bs = "RES";
        setup_helpers();
        d[1] = c;
        as = "TOREAD";
        bs = "ORES";
        setup_helpers();
        d[2] = c;
        print("\n");
        print("\n");
        z = 0;
        while (z == 0) {
            d[3]++;
            print("PASS NUMBER " + d[3] + "\n");
            if (d[3] >= 3) {
                print("HERE COMES THE BULL.  TRY FOR A KILL");
                while (1) {
                    str = await input();
                    if (str != "YES" && str != "NO")
                        print("INCORRECT ANSWER - - PLEASE TYPE 'YES' OR 'NO'.\n");
                    else
                        break;
                }
                z1 = (str == "YES") ? 1 : 2;
                if (z1 != 1) {
                    print("CAPE MOVE");
                }
            } else {
                print("THE BULL IS CHARGING AT YOU!  YOU ARE THE MATADOR--\n");
                print("DO YOU WANT TO KILL THE BULL");
                while (1) {
                    str = await input();
                    if (str != "YES" && str != "NO")
                        print("INCORRECT ANSWER - - PLEASE TYPE 'YES' OR 'NO'.\n");
                    else
                        break;
                }
                z1 = (str == "YES") ? 1 : 2;
                if (z1 != 1) {
                    print("WHAT MOVE DO YOU MAKE WITH THE CAPE");
                }
            }
            gore = 0;
            if (z1 != 1) {
                while (1) {
                    e = parseInt(await input());
                    if (e >= 3) {
                        print("DON'T PANIC, YOU IDIOT!  PUT DOWN A CORRECT NUMBER\n");
                    } else {
                        break;
                    }
                }
                if (e == 0)
                    m = 3;
                else if (e == 1)
                    m = 2;
                else
                    m = 0.5;
                l += m;
                f = (6 - a + m / 10) * Math.random() / ((d[1] + d[2] + d[3] / 10) * 5);
                if (f < 0.51)
                    continue;
                gore = 1;
            } else {
                z = 1;
                print("\n");
                print("IT IS THE MOMENT OF THE TRUTH.\n");
                print("\n");
                print("HOW DO YOU TRY TO KILL THE BULL");
                h = parseInt(await input());
                if (h != 4 && h != 5) {
                    print("YOU PANICKED.  THE BULL GORED YOU.\n");
                    gore = 2;
                } else {
                    k = (6 - a) * 10 * Math.random() / ((d[1] + d[2]) * 5 * d[3]);
                    if (h != 4) {   // Bug in original game, it says J instead of H
                        if (k > 0.2)
                            gore = 1;
                    } else {
                        if (k > 0.8)
                            gore = 1;
                    }
                    if (gore == 0) {
                        print("YOU KILLED THE BULL!\n");
                        d[5] = 2;
                        break;
                    }
                }
            }
            if (gore) {
                if (gore == 1)
                    print("THE BULL HAS GORED YOU!\n");
                kill = false;
                while (1) {
                    if (af(0) == 1) {
                        print("YOU ARE DEAD.\n");
                        d[4] = 1.5;
                        kill = true;
                        break;
                    }
                    print("YOU ARE STILL ALIVE.\n");
                    print("\n");
                    print("DO YOU RUN FROM THE RING");
                    while (1) {
                        str = await input();
                        if (str != "YES" && str != "NO")
                            print("INCORRECT ANSWER - - PLEASE TYPE 'YES' OR 'NO'.\n");
                        else
                            break;
                    }
                    z1 = (str == "YES") ? 1 : 2;
                    if (z1 != 2) {
                        print("COWARD\n");
                        d[4] = 0;
                        kill = true;
                        break;
                    }
                    print("YOU ARE BRAVE.  STUPID, BUT BRAVE.\n");
                    if (af(0) == 1) {
                        d[4] = 2;
                        kill = false;
                        break;
                    }
                    print("YOU ARE GORED AGAIN!\n");
                }
                if (kill)
                    break;
                continue;
            }
        }
        print("\n");
        print("\n");
        print("\n");
        if (d[4] == 0) {
            print("THE CROWD BOOS FOR TEN MINUTES.  IF YOU EVER DARE TO SHOW\n");
            print("YOUR FACE IN A RING AGAIN, THEY SWEAR THEY WILL KILL YOU--\n");
            print("UNLESS THE BULL DOES FIRST.\n");
        } else {
            if (d[4] == 2) {
                print("THE CROWD CHEERS WILDLY!\n");
            } else if (d[5] == 2) {
                print("THE CROWD CHEERS!\n");
                print("\n");
            }
            print("THE CROWD AWARDS YOU\n");
            if (cf(0) < 2.4) {
                print("NOTHING AT ALL.\n");
            } else if (cf(0) < 4.9) {
                print("ONE EAR OF THE BULL.\n");
            } else if (cf(0) < 7.4) {
                print("BOTH EARS OF THE BULL!\n");
                print("OLE!\n");
            } else {
                print("OLE!  YOU ARE 'MUY HOMBRE'!! OLE!  OLE!\n");
            }
            print("\n");
            print("ADIOS\n");
            print("\n");
            print("\n");
            print("\n");
        }
    }
    
    
    main();
    
    
    ================================================
    FILE: 17_Bullfight/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 17_Bullfight/kotlin/src/bullfight/Main.kt
    ================================================
    package bullfight
    
    import bullfight.Yorn.*
    import kotlin.random.Random
    import kotlin.system.exitProcess
    
    private val Boolean.asInteger get() = if (this) 1 else 2
    private val Float.squared: Float
        get() = this * this
    val fna: Boolean get() = RandomNumbers.nextBoolean()
    
    enum class Quality(private val typeName: String) {
        Superb("SUPERB"),
        Good("GOOD"),
        Fair("FAIR"),
        Poor("POOR"),
        Awful("AWFUL");
        override fun toString() = typeName
    
        val level get() = (ordinal + 1).toFloat()
    }
    
    enum class BullDeath(val factor: Float) {
        Alive(1f), Dead(2f);
    }
    
    var l = 1f
    lateinit var bullQuality: Quality
    var momentOfTruth = false
    var picadoresSuccess = 0f
    var toreadoresSuccess = 0f
    var passNumber = 0f
    var honor = 0f
    var bullDeath = BullDeath.Alive
    
    
    interface RandomNumberSource {
        fun nextBoolean(): Boolean
        fun nextInt(from: Int, until: Int): Int
        fun nextFloat(): Float
    }
    
    object RandomNumbers : RandomNumberSource {
        override fun nextBoolean() = Random.nextBoolean()
        override fun nextInt(from: Int, until: Int) = Random.nextInt(from, until)
        override fun nextFloat() = Random.nextFloat()
    }
    
    
    fun main() {
        intro()
        instructions()
        bullDeath = BullDeath.Alive
        honor = 1f
    
        bullQuality = Quality.values()[RandomNumbers.nextInt(1, 6)]
        println("YOU HAVE DRAWN A $bullQuality BULL.")
        when (bullQuality) {
            Quality.Superb -> println("GOOD LUCK.  YOU'LL NEED IT.")
            Quality.Awful -> println("YOU'RE LUCKY.")
            else -> Unit
        }
    
        picadoresSuccess = fight(FirstAct.picadores)
        toreadoresSuccess = fight(FirstAct.toreadores)
        println()
        println()
    
        var gored: Boolean
    
        gameLoop@ do {
            passNumber++
            println("PASS NUMBER ${passNumber.toInt()}")
            gored = if (passNumber >= 3) {
                print("HERE COMES THE BULL.  TRY FOR A KILL")
                killAttempt("CAPE MOVE")
            } else {
                println("THE BULL IS CHARGING AT YOU!  YOU ARE THE MATADOR--")
                print("DO YOU WANT TO KILL THE BULL")
                killAttempt("WHAT MOVE DO YOU MAKE WITH THE CAPE")
            }
    
            if (!gored) {
                val move = restrictedInput(
                    values = Cape.values(),
                    errorMessage = "DON'T PANIC, YOU IDIOT!  PUT DOWN A CORRECT NUMBER"
                )
                val m = when (move) {
                    Cape.Veronica -> 3f
                    Cape.Outside -> 2f
                    Cape.Swirl -> 0.5f
                }
    
                l += m
                val f =
                    (6 - bullQuality.level + m / 10f) * RandomNumbers.nextFloat() / ((picadoresSuccess + toreadoresSuccess + passNumber / 10f) * 5f)
                if (f < 0.51)
                    continue
            }
    
            println("THE BULL HAS GORED YOU!")
            goreLoop@ do {
                when (fna) {
                    false -> {
                        println("YOU ARE DEAD.")
                        honor = 1.5f
                        gameResult()
                    }
    
                    true -> {
                        println("YOU ARE STILL ALIVE.")
                        println()
                        print("DO YOU RUN FROM THE RING")
                        when (Yorn.input()) {
    
                            YES -> {
                                println("COWARD")
                                honor = 0f
                            }
    
                            NO -> {
                                println("YOU ARE BRAVE.  STUPID, BUT BRAVE.")
                                when (fna) {
                                    true -> {
                                        honor = 2f
                                        continue@gameLoop
                                    }
    
                                    false -> {
                                        println("YOU ARE GORED AGAIN!")
                                        continue@goreLoop
                                    }
                                }
                            }
                        }
    
                    }
                }
            } while (true)
    
        } while (true)
    
    }
    
    fun fnd() = 4.5 +
            l / 6 -
            (picadoresSuccess + toreadoresSuccess) * 2.5 +
            4 * honor +
            2 * bullDeath.factor -
            passNumber.squared / 120f -
            bullQuality.level
    
    fun fnc() = fnd() * RandomNumbers.nextFloat()
    
    private fun killAttempt(capeMessage: String): Boolean {
        when (Yorn.input()) {
    
            YES ->
                when (momentOfTruth()) {
                    KillResult.Success -> gameResult()
                    KillResult.Fail -> return true
                }
    
            NO ->
                print(capeMessage)
    
        }
        return false
    }
    
    private fun gameResult() {
        println()
        println()
        if (honor == 0f) {
            println(
                """
                                THE CROWD BOOS FOR TEN MINUTES.  IF YOU EVER DARE TO SHOW
                                YOUR FACE IN A RING AGAIN, THEY SWEAR THEY WILL KILL YOU--
                                UNLESS THE BULL DOES FIRST.
                                """.trimIndent()
            )
        } else {
            if (honor == 2f)
                println("THE CROWD CHEERS WILDLY!")
            else
                if (bullDeath == BullDeath.Dead) {
                    println("THE CROWD CHEERS!")
                    println()
                }
            println("THE CROWD AWARDS YOU")
            if (fnc() < 2.4)
                println("NOTHING AT ALL.")
            else if (fnc() < 4.9)
                println("ONE EAR OF THE BULL.")
            else
                if (fnc() < 7.4)
                    println("BOTH EARS OF THE BULL!")
                else
                    println("OLE!  YOU ARE 'MUY HOMBRE'!! OLE!  OLE!")
            println()
        }
        println()
        println("ADIOS")
        println()
        println()
        println()
        exitProcess(0)
    }
    
    enum class KillResult { Success, Fail }
    
    fun momentOfTruth(): KillResult {
        momentOfTruth = true
        print(
            """
            
            IT IS THE MOMENT OF TRUTH.
            
            HOW DO YOU WANT TO KILL THE BULL
        """.trimIndent()
        )
    
        val k =
            (6 - bullQuality.level) * 10 * RandomNumbers.nextFloat() / ((picadoresSuccess + toreadoresSuccess) * 5 * passNumber)
    
        val chance = when (stdInput(KillMethod.values())) {
            KillMethod.OverHorns -> .8
            KillMethod.Chest -> .2
            null -> {
                println("YOU PANICKED.  THE BULL GORED YOU.")
                return KillResult.Fail
            }
        }
        return if (k <= chance) {
            println("YOU KILLED THE BULL!")
            bullDeath = BullDeath.Dead
            KillResult.Success
        } else {
            println("THE BULL HAS GORED YOU!")
            KillResult.Fail
        }
    }
    
    interface InputOption {
        val input: String
    }
    
    enum class Cape(override val input: String) : InputOption {
        Veronica("0"),
        Outside("1"),
        Swirl("2"),
    }
    
    enum class KillMethod(override val input: String) : InputOption {
        OverHorns("4"),
        Chest("5"),
    }
    
    private fun  restrictedInput(values: Array, errorMessage: String): T {
        do {
            stdInput(values)?.let { return it }
            println(errorMessage)
        } while (true)
    }
    
    private fun  stdInput(values: Array): T? {
        print("? ")
        val z1 = readln()
        return values.firstOrNull { z1 == it.input }
    }
    
    enum class Yorn(override val input: String) : InputOption {
        YES("YES"), NO("NO");
    
        companion object {
            fun input(): Yorn {
                return restrictedInput(values(), "YES OR NO")
            }
        }
    }
    
    enum class FirstAct(val str: String) {
        picadores("PICADORES"), toreadores("TOREADORES");
    
        override fun toString() = str
    }
    
    fun fight(firstAct: FirstAct): Float {
    
        val b = 3.0 / bullQuality.level * RandomNumbers.nextFloat()
        val firstActQuality = when {
            b < .37 -> Quality.Awful
            b < .5 -> Quality.Poor
            b < .63 -> Quality.Fair
            b < .87 -> Quality.Good
            else -> Quality.Superb
        }
        val c = firstActQuality.level / 10f
        val t = firstActQuality.level
        println("THE $firstAct DID A $firstActQuality JOB.")
    
        if (t >= 4f) {
            if (t == 5f) {
                if (firstAct != FirstAct.toreadores) {
                    println("${fna.asInteger} OF THE HORSES OF THE $firstAct KILLED.")
                }
                println("${fna.asInteger} OF THE $firstAct KILLED.")
            } else {
                println(
                    when (fna) {
                        true -> "ONE OF THE $firstAct WAS KILLED."
                        false -> "NO $firstAct WERE KILLED."
                    }
                )
            }
        }
        println()
    
        return c
    }
    
    private fun instructions() {
        print("DO YOU WANT INSTRUCTIONS? ")
        if (readln().trim() != "NO") {
            println("HELLO, ALL YOU BLOODLOVERS AND AFICIONADOS.")
            println("HERE IS YOUR BIG CHANCE TO KILL A BULL.")
            println()
            println("ON EACH PASS OF THE BULL, YOU MAY TRY")
            println("0 - VERONICA (DANGEROUS INSIDE MOVE OF THE CAPE)")
            println("1 - LESS DANGEROUS OUTSIDE MOVE OF THE CAPE")
            println("2 - ORDINARY SWIRL OF THE CAPE.")
            println()
            println("INSTEAD OF THE ABOVE, YOU MAY TRY TO KILL THE BULL")
            println("ON ANY TURN: 4 (OVER THE HORNS), 5 (IN THE CHEST).")
            println("BUT IF I WERE YOU,")
            println("I WOULDN'T TRY IT BEFORE THE SEVENTH PASS.")
            println()
            println("THE CROWD WILL DETERMINE WHAT AWARD YOU DESERVE")
            println("(POSTHUMOUSLY IF NECESSARY).")
            println("THE BRAVER YOU ARE, THE BETTER THE AWARD YOU RECEIVE.")
            println()
            println("THE BETTER THE JOB THE PICADORES AND TOREADORES DO,")
            println("THE BETTER YOUR CHANCES ARE.")
        }
        repeat(2) {
            println()
        }
    }
    
    fun intro() {
        println(" ".repeat(34) + "BULL")
        println(" ".repeat(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY")
        println()
        println()
        println()
    }
    
    
    ================================================
    FILE: 17_Bullfight/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 17_Bullfight/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 17_Bullfight/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 17_Bullfight/python/bullfight.py
    ================================================
    import math
    import random
    from typing import Dict, List, Literal, Tuple, Union
    
    
    def print_n_newlines(n: int) -> None:
        for _ in range(n):
            print()
    
    
    def determine_player_kills(
        bull_quality: int,
        player_type: Literal["TOREAD", "PICADO"],
        plural_form: Literal["ORES", "RES"],
        job_qualities: List[str],
    ) -> float:
        bull_performance = 3 / bull_quality * random.random()
        if bull_performance < 0.37:
            job_quality_factor = 0.5
        elif bull_performance < 0.5:
            job_quality_factor = 0.4
        elif bull_performance < 0.63:
            job_quality_factor = 0.3
        elif bull_performance < 0.87:
            job_quality_factor = 0.2
        else:
            job_quality_factor = 0.1
        job_quality = math.floor(10 * job_quality_factor + 0.2)  # higher is better
        print(f"THE {player_type}{plural_form} DID A {job_qualities[job_quality]} JOB.")
        if job_quality >= 4:
            if job_quality == 5:
                if random.choice([True, False]):
                    print(f"ONE OF THE {player_type}{plural_form} WAS KILLED.")
            else:
                if player_type != "TOREAD":
                    killed_horses = random.randint(1, 2)
                    print(
                        f"{killed_horses} OF THE HORSES OF THE {player_type}{plural_form} KILLED."
                    )
                killed_players = random.randint(1, 2)
                print(f"{killed_players} OF THE {player_type}{plural_form} KILLED.")
        print()
        return job_quality_factor
    
    
    def calculate_final_score(
        move_risk_sum: float, job_quality_by_round: Dict[int, float], bull_quality: int
    ) -> float:
        quality = (
            4.5
            + move_risk_sum / 6
            - (job_quality_by_round[1] + job_quality_by_round[2]) * 2.5
            + 4 * job_quality_by_round[4]
            + 2 * job_quality_by_round[5]
            - (job_quality_by_round[3] ** 2) / 120
            - bull_quality
        ) * random.random()
        if quality < 2.4:
            return 0
        elif quality < 4.9:
            return 1
        elif quality < 7.4:
            return 2
        else:
            return 3
    
    
    def print_header() -> None:
        print(" " * 34 + "BULL")
        print(" " * 15 + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY")
        print_n_newlines(2)
    
    
    def print_instructions() -> None:
        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.")
    
    
    def print_intro() -> None:
        print_header()
        want_instructions = input("DO YOU WANT INSTRUCTIONS? ")
        if want_instructions != "NO":
            print_instructions()
        print_n_newlines(2)
    
    
    def ask_bool(prompt: str) -> bool:
        while True:
            answer = input(prompt).lower()
            if answer == "yes":
                return True
            elif answer == "no":
                return False
            else:
                print("INCORRECT ANSWER - - PLEASE TYPE 'YES' OR 'NO'.")
    
    
    def ask_int() -> int:
        while True:
            foo = float(input())
            if foo != float(int(abs(foo))):  # we actually want an integer
                print("DON'T PANIC, YOU IDIOT!  PUT DOWN A CORRECT NUMBER")
            elif foo < 3:
                break
        return int(foo)
    
    
    def did_bull_hit(
        bull_quality: int,
        cape_move: int,
        job_quality_by_round: Dict[int, float],
        move_risk_sum: float,
    ) -> Tuple[bool, float]:
        # The bull quality is a grade: The lower the grade, the better the bull
        if cape_move == 0:
            move_risk: Union[int, float] = 3
        elif cape_move == 1:
            move_risk = 2
        else:
            move_risk = 0.5
        move_risk_sum += move_risk
        bull_strength = 6 - bull_quality
        bull_hit_factor = (  # the higher the factor, the more "likely" it hits
            (bull_strength + move_risk / 10)
            * random.random()
            / (
                (
                    job_quality_by_round[1]
                    + job_quality_by_round[2]
                    + job_quality_by_round[3] / 10
                )
                * 5
            )
        )
        bull_hit = bull_hit_factor >= 0.51
        return bull_hit, move_risk_sum
    
    
    def handle_bullkill_attempt(
        kill_method: int,
        job_quality_by_round: Dict[int, float],
        bull_quality: int,
        gore: int,
    ) -> int:
        if kill_method not in [4, 5]:
            print("YOU PANICKED.  THE BULL GORED YOU.")
            gore = 2
        else:
            bull_strength = 6 - bull_quality
            kill_probability = (
                bull_strength
                * 10
                * random.random()
                / (
                    (job_quality_by_round[1] + job_quality_by_round[2])
                    * 5
                    * job_quality_by_round[3]
                )
            )
            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
                return gore
        return gore
    
    
    def final_message(
        job_quality_by_round: Dict[int, float], bull_quality: int, move_risk_sum: float
    ) -> None:
        print_n_newlines(3)
        if job_quality_by_round[4] == 0:
            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:
            if job_quality_by_round[4] == 2:
                print("THE CROWD CHEERS WILDLY!")
            elif job_quality_by_round[5] == 2:
                print("THE CROWD CHEERS!")
                print()
            print("THE CROWD AWARDS YOU")
            score = calculate_final_score(move_risk_sum, job_quality_by_round, bull_quality)
            if score == 0:
                print("NOTHING AT ALL.")
            elif score == 1:
                print("ONE EAR OF THE BULL.")
            elif score == 2:
                print("BOTH EARS OF THE BULL!")
                print("OLE!")
            else:
                print("OLE!  YOU ARE 'MUY HOMBRE'!! OLE!  OLE!")
            print()
            print("ADIOS")
            print_n_newlines(3)
    
    
    def main() -> None:
        print_intro()
        move_risk_sum: float = 1
        job_quality_by_round: Dict[int, float] = {4: 1, 5: 1}
        job_quality = ["", "SUPERB", "GOOD", "FAIR", "POOR", "AWFUL"]
        bull_quality = random.randint(
            1, 5
        )  # the lower the number, the more powerful the bull
        print(f"YOU HAVE DRAWN A {job_quality[bull_quality]} BULL.")
        if bull_quality > 4:
            print("YOU'RE LUCKY.")
        elif bull_quality < 2:
            print("GOOD LUCK.  YOU'LL NEED IT.")
            print()
        print()
    
        # Round 1: Run Picadores
        player_type: Literal["TOREAD", "PICADO"] = "PICADO"
        plural_form: Literal["ORES", "RES"] = "RES"
        job_quality_factor = determine_player_kills(
            bull_quality, player_type, plural_form, job_quality
        )
        job_quality_by_round[1] = job_quality_factor
    
        # Round 2: Run Toreadores
        player_type = "TOREAD"
        plural_form = "ORES"
        determine_player_kills(bull_quality, player_type, plural_form, job_quality)
        job_quality_by_round[2] = job_quality_factor
        print_n_newlines(2)
    
        # Round 3
        job_quality_by_round[3] = 0
        while True:
            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? ")
                if not run_from_ring:
                    print("CAPE MOVE? ", end="")
            else:
                print("THE BULL IS CHARGING AT YOU!  YOU ARE THE MATADOR--")
                run_from_ring = ask_bool("DO YOU WANT TO KILL THE BULL? ")
                if not run_from_ring:
                    print("WHAT MOVE DO YOU MAKE WITH THE CAPE? ", end="")
            gore = 0
            if not run_from_ring:
                cape_move = ask_int()
                bull_hit, move_risk_sum = did_bull_hit(
                    bull_quality, cape_move, job_quality_by_round, move_risk_sum
                )
                if bull_hit:
                    gore = 1
                else:
                    continue
            else:
                print()
                print("IT IS THE MOMENT OF TRUTH.")
                print()
                kill_method = int(input("HOW DO YOU TRY TO KILL THE BULL? "))
                gore = handle_bullkill_attempt(
                    kill_method, job_quality_by_round, bull_quality, gore
                )
                if gore == 0:
                    break
            if gore > 0:
                if gore == 1:
                    print("THE BULL HAS GORED YOU!")
                death = False
                while True:
                    if random.randint(1, 2) == 1:
                        print("YOU ARE DEAD.")
                        job_quality_by_round[4] = 1.5
                        death = True
                        break
                    else:
                        print("YOU ARE STILL ALIVE.")
                        print()
                        print("DO YOU RUN FROM THE RING? ", end="")
                        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
                                death = True
                                break
                            else:
                                print("YOU ARE GORED AGAIN!")
                if death:
                    break
    
        final_message(job_quality_by_round, bull_quality, move_risk_sum)
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 17_Bullfight/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 17_Bullfight/vbnet/Bullfight.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Bullfight", "Bullfight.vbproj", "{38805A6B-C251-4C45-9832-B8E2F3F6436F}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{38805A6B-C251-4C45-9832-B8E2F3F6436F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{38805A6B-C251-4C45-9832-B8E2F3F6436F}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{38805A6B-C251-4C45-9832-B8E2F3F6436F}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{38805A6B-C251-4C45-9832-B8E2F3F6436F}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 17_Bullfight/vbnet/Bullfight.vbproj
    ================================================
    
      
        Exe
        Bullfight
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 17_Bullfight/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 18_Bullseye/README.md
    ================================================
    ### Bullseye
    
    In this game, up to 20 players throw darts at a target with 10-, 20-, 30-, and 40-point zones. The objective is to get 200 points.
    
    You have a choice of three methods of throwing:
    
    | Throw | Description        | Probable Score            |
    |-------|--------------------|---------------------------|
    | 1     | Fast overarm       | Bullseye or complete miss |
    | 2     | Controlled overarm | 10, 20, or 30 points      |
    | 3     | Underarm           | Anything                  |
    
    You will find after playing a while that different players will swear by different strategies. However, considering the expected score per throw by always using throw 3:
    
    | Score (S) | Probability (P) | S x P |
    |-----------|-----------------|-------|
    |     40    |  1.00-.95 = .05 |   2   |
    |     30    |  .95-.75 = .20  |   6   |
    |     30    |  .75-.45 = .30  |   6   |
    |     10    |  .45-.05 = .40  |   4   |
    |     0     |  .05-.00 = .05  |   0   |
    
    Expected score per throw = 18
    
    Calculate the expected score for the other throws and you may be surprised!
    
    The program was written by David Ahl of Creative Computing.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=34)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=49)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    
    ================================================
    FILE: 18_Bullseye/bullseye.bas
    ================================================
    5 PRINT TAB(32);"BULLSEYE"
    10 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    20 PRINT:PRINT:PRINT
    30 PRINT "IN THIS GAME, UP TO 20 PLAYERS THROW DARTS AT A TARGET"
    40 PRINT "WITH 10, 20, 30, AND 40 POINT ZONES.  THE OBJECTIVE IS"
    50 PRINT "TO GET 200 POINTS.": PRINT
    60 PRINT "THROW",TAB(20);"DESCRIPTION";TAB(45);"PROBABLE SCORE"
    70 PRINT" 1";TAB(20);"FAST OVERARM";TAB(45);"BULLSEYE OR COMPLETE MISS"
    80 PRINT" 2";TAB(20);"CONTROLLED OVERARM";TAB(45);"10, 20 OR 30 POINTS"
    90 PRINT" 3";TAB(20);"UNDERARM";TAB(45);"ANYTHING":PRINT
    100 DIM A$(20),S(20),W(10): M=0: R=0: FOR I=1 TO 20: S(I)=0: NEXT I
    110 INPUT "HOW MANY PLAYERS";N: PRINT
    120 FOR I=1 TO N
    130 PRINT "NAME OF PLAYER #";I;:INPUT A$(I)
    140 NEXT I
    150 R=R+1: PRINT: PRINT "ROUND";R:PRINT "---------"
    160 FOR I=1 TO N
    170 PRINT: PRINT A$(I)"'S THROW";: INPUT T
    180 IF T<1 OR T>3 THEN PRINT "INPUT 1, 2, OR 3!": GOTO 170
    190 ON T GOTO 200, 210, 200
    200 P1=.65: P2=.55: P3=.5: P4=.5: GOTO 230
    210 P1=.99: P2=.77: P3=.43: P4=.01: GOTO 230
    220 P1=.95: P2=.75: P3=.45: P4=.05
    230 U=RND(1)
    240 IF U>=P1 THEN PRINT "BULLSEYE!!  40 POINTS!":B=40: GOTO 290
    250 IF U>=P2 THEN PRINT "30-POINT ZONE!":B=30: GOTO 290
    260 IF U>=P3 THEN PRINT "20-POINT ZONE":B=20: GOTO 290
    270 IF U>=P4 THEN PRINT "WHEW!  10 POINTS.":B=10: GOTO 290
    280 PRINT "MISSED THE TARGET!  TOO BAD.": B=0
    290 S(I)=S(I)+B: PRINT "TOTAL SCORE =";S(I): NEXT I
    300 FOR I=1 TO N
    310 IF S(I)>=200 THEN M=M+1: W(M)=I
    320 NEXT I
    330 IF M=0 THEN 150
    340 PRINT: PRINT "WE HAVE A WINNER!!": PRINT
    350 FOR I=1 TO M: PRINT A$(W(I));" SCORED";S(W(I));"POINTS.": NEXT I
    360 PRINT: PRINT "THANKS FOR THE GAME.": END
    
    
    ================================================
    FILE: 18_Bullseye/csharp/Bullseye.csproj
    ================================================
    
    
      
        Exe
        net6.0
        enable
        enable
      
    
    
    
    
    ================================================
    FILE: 18_Bullseye/csharp/Bullseye.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bullseye", "Bullseye.csproj", "{04C164DB-594F-41C4-BC0E-0A203A5536C7}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{04C164DB-594F-41C4-BC0E-0A203A5536C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{04C164DB-594F-41C4-BC0E-0A203A5536C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{04C164DB-594F-41C4-BC0E-0A203A5536C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{04C164DB-594F-41C4-BC0E-0A203A5536C7}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 18_Bullseye/csharp/BullseyeGame.cs
    ================================================
    namespace Bullseye
    {
        /// 
        /// Class encompassing the game
        /// 
        public class BullseyeGame
        {
            private readonly List _players;
    
            // define a constant for the winning score so that it is
            // easy to change again in the future
            private const int WinningScore = 200;
    
            public BullseyeGame()
            {
                // create the initial list of players; list is empty, but
                // the setup of the game will add items to this list
                _players = new List();
            }
    
            public void Run()
            {
                PrintIntroduction();
    
                SetupGame();
    
                PlayGame();
    
                PrintResults();
            }
    
            private void SetupGame()
            {
                // First, allow the user to enter how many players are going
                // to play. This could be weird if the user enters negative
                // numbers, words, or too many players, so there are some
                // extra checks on the input to make sure the user didn't do
                // anything too crazy. Loop until the user enters valid input.
                bool validPlayerCount;
                int playerCount;
                do
                {
                    Console.WriteLine();
                    Console.Write("HOW MANY PLAYERS? ");
                    string? input = Console.ReadLine();
    
                    // assume the user has entered something incorrect - the
                    // next steps will validate the input
                    validPlayerCount = false;
    
                    if (Int32.TryParse(input, out playerCount))
                    {
                        if (playerCount > 0 && playerCount <= 20)
                        {
                            validPlayerCount = true;
                        }
                        else
                        {
                            Console.WriteLine("YOU MUST ENTER A NUMBER BETWEEN 1 AND 20!");
                        }
                    }
                    else
                    {
                        Console.WriteLine("YOU MUST ENTER A NUMBER");
                    }
    
                }
                while (!validPlayerCount);
    
                // Next, allow the user to enter names for the players; as each
                // name is entered, create a Player object to track the name
                // and their score, and save the object to the list in this class
                // so the rest of the game has access to the set of players
                for (int i = 0; i < playerCount; i++)
                {
                    string? playerName = String.Empty;
                    do
                    {
                        Console.Write($"NAME OF PLAYER #{i+1}? ");
                        playerName = Console.ReadLine();
    
                        // names can be any sort of text, so allow whatever the user
                        // enters as long as it isn't a blank space
                    }
                    while (String.IsNullOrWhiteSpace(playerName));
    
                    _players.Add(new Player(playerName));
                }
            }
    
            private void PlayGame()
            {
                Random random = new Random(DateTime.Now.Millisecond);
    
                int round = 0;
                bool isOver = false;
                do
                {
                    // starting a new round, increment the counter
                    round++;
                    Console.WriteLine($"ROUND {round}");
                    Console.WriteLine("--------------");
    
                    foreach (Player player in _players)
                    {
                        // ask the user how they want to throw
                        Console.Write($"{player.Name.ToUpper()}'S THROW: ");
                        string? input = Console.ReadLine();
    
                        // based on the input, figure out the probabilities
                        int[] probabilities;
                        switch (input)
                        {
                            case "1":
                            {
                                probabilities = new int[] { 65, 55, 50, 50 };
                                break;
                            }
                            case "2":
                            {
                                probabilities = new int[] { 99, 77, 43, 1 };
                                break;
                            }
                            case "3":
                            {
                                probabilities = new int[] { 95, 75, 45, 5 };
                                break;
                            }
                            default:
                            {
                                // in case the user types something bad, pretend it's
                                // as if they tripped over themselves while throwing
                                // the dart - they'll either hit a bullseye or completely
                                // miss
                                probabilities = new int[] { 95, 95, 95, 95 };
                                Console.Write("TRIP! ");
                                break;
                            }
                        }
    
    
                        // Next() returns a number in the range: min <= num < max, so specify 101
                        // as the maximum so that 100 is a number that could be returned
                        int chance = random.Next(0, 101);
    
                        if (chance > probabilities[0])
                        {
                            player.Score += 40;
                            Console.WriteLine("BULLSEYE!!  40 POINTS!");
                        }
                        else if (chance > probabilities[1])
                        {
                            player.Score += 30;
                            Console.WriteLine("30-POINT ZONE!");
                        }
                        else if (chance > probabilities[2])
                        {
                            player.Score += 20;
                            Console.WriteLine("20-POINT ZONE");
                        }
                        else if (chance > probabilities[3])
                        {
                            player.Score += 10;
                            Console.WriteLine("WHEW!  10 POINTS.");
                        }
                        else
                        {
                            // missed it
                            Console.WriteLine("MISSED THE TARGET!  TOO BAD.");
                        }
    
                        // check to see if the player has won - if they have, then
                        // break out of the loops
                        if (player.Score > WinningScore)
                        {
                            Console.WriteLine();
                            Console.WriteLine("WE HAVE A WINNER!!");
                            Console.WriteLine($"{player.Name.ToUpper()} SCORED {player.Score} POINTS.");
                            Console.WriteLine();
    
                            isOver = true; // out of the do/while round loop
                            break; // out of the foreach (player) loop
                        }
    
                        Console.WriteLine();
                    }
                }
                while (!isOver);
            }
    
            private void PrintResults()
            {
                // For bragging rights, print out all the scores, but sort them
                // by who had the highest score
                var sorted = _players.OrderByDescending(p => p.Score);
    
                // padding is used to get things to line up nicely - the results
                // should look something like:
                //      PLAYER       SCORE
                //      Bravo          210
                //      Charlie         15
                //      Alpha            1
                Console.WriteLine("PLAYER       SCORE");
                foreach (var player in sorted)
                {
                    Console.WriteLine($"{player.Name.PadRight(12)} {player.Score.ToString().PadLeft(5)}");
                }
    
                Console.WriteLine();
                Console.WriteLine("THANKS FOR THE GAME.");
            }
    
            private void PrintIntroduction()
            {
                Console.WriteLine(Title);
                Console.WriteLine();
                Console.WriteLine(Introduction);
                Console.WriteLine();
                Console.WriteLine(Operations);
            }
    
            private const string Title = @"
                        BULLSEYE
        CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY";
    
            private const string Introduction = @"
    IN THIS GAME, UP TO 20 PLAYERS THROW DARTS AT A TARGET
    WITH 10, 20, 30, AND 40 POINT ZONES.  THE OBJECTIVE IS
    TO GET 200 POINTS.";
    
            private const string Operations = @"
    THROW   DESCRIPTION         PROBABLE SCORE
      1     FAST OVERARM        BULLSEYE OR COMPLETE MISS
      2     CONTROLLED OVERARM  10, 20, OR 30 POINTS
      3     UNDERARM            ANYTHING";
        }
    }
    
    
    ================================================
    FILE: 18_Bullseye/csharp/Player.cs
    ================================================
    namespace Bullseye
    {
        /// 
        /// Object to track the name and score of a player
        /// 
        public class Player
        {
            /// 
            /// Creates a play with the given name
            /// 
            /// Name of the player
            public Player(string name)
            {
                Name = name;
                Score = 0;
            }
    
            /// 
            /// Name of the player
            /// 
            public string Name { get; private set; }
    
            /// 
            /// Current score of the player
            /// 
            public int Score { get; set; }
        }
    }
    
    
    ================================================
    FILE: 18_Bullseye/csharp/Program.cs
    ================================================
    using System;
    
    namespace Bullseye
    {
        public static class Program
        {
            // Entry point to the application; create an instance of the
            // game class and call Run()
            public static void Main(string[] args)
            {
                new BullseyeGame().Run();
            }
        }
    }
    
    
    ================================================
    FILE: 18_Bullseye/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 18_Bullseye/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 18_Bullseye/java/src/Bullseye.java
    ================================================
    import java.util.ArrayList;
    import java.util.Scanner;
    
    /**
     * Game of Bullseye
     * 

    * Based on the Basic game of Bullseye here * https://github.com/coding-horror/basic-computer-games/blob/main/18%20Bullseye/bullseye.bas *

    * Note: The idea was to create a version of 1970's Basic game in Java, without introducing * new features - no additional text, error checking, etc has been added. */ public class Bullseye { // Used for formatting output public static final int FIRST_IDENT = 10; public static final int SECOND_IDENT = 30; public static final int THIRD_INDENT = 30; // Used to decide throw result public static final double[] SHOT_ONE = new double[]{.65, .55, .5, .5}; public static final double[] SHOT_TWO = new double[]{.99, .77, .43, .01}; public static final double[] SHOT_THREE = new double[]{.95, .75, .45, .05}; private enum GAME_STATE { STARTING, START_GAME, PLAYING, GAME_OVER } private GAME_STATE gameState; private final ArrayList players; private final Shot[] shots; // Used for keyboard input private final Scanner kbScanner; private int round; public Bullseye() { gameState = GAME_STATE.STARTING; players = new ArrayList<>(); // Save the random chances of points based on shot type shots = new Shot[3]; shots[0] = new Shot(SHOT_ONE); shots[1] = new Shot(SHOT_TWO); shots[2] = new Shot(SHOT_THREE); // Initialise kb scanner kbScanner = new Scanner(System.in); } /** * Main game loop */ public void play() { do { switch (gameState) { // Show an introduction the first time the game is played. case STARTING: intro(); gameState = GAME_STATE.START_GAME; break; // Start the game, set the number of players, names and round case START_GAME: int numberOfPlayers = chooseNumberOfPlayers(); for (int i = 0; i < numberOfPlayers; i++) { String name = displayTextAndGetInput("NAME OF PLAYER #" + (i + 1) + "? "); Player player = new Player(name); this.players.add(player); } this.round = 1; gameState = GAME_STATE.PLAYING; break; // Playing round by round until we have a winner case PLAYING: System.out.println(); System.out.println("ROUND " + this.round); System.out.println("======="); // Each player takes their turn for (Player player : players) { int playerThrow = getPlayersThrow(player); int points = calculatePlayerPoints(playerThrow); player.addScore(points); System.out.println("TOTAL SCORE = " + player.getScore()); } boolean foundWinner = false; // Check if any player won for (Player thePlayer : players) { int score = thePlayer.getScore(); if (score >= 200) { if (!foundWinner) { System.out.println("WE HAVE A WINNER!!"); System.out.println(); foundWinner = true; } System.out.println(thePlayer.getName() + " SCORED " + thePlayer.getScore() + " POINTS"); } } if (foundWinner) { System.out.println("THANKS FOR THE GAME."); gameState = GAME_STATE.GAME_OVER; } else { // No winner found, continue on with the next round this.round++; } break; } } while (gameState != GAME_STATE.GAME_OVER); } /** * Display info about the game */ private void intro() { System.out.println("BULLSEYE"); System.out.println("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println(); System.out.println("IN THIS GAME, UP TO 20 PLAYERS THROW DARTS AT A TARGET"); System.out.println("WITH 10, 20, 30, AND 40 POINT ZONES. THE OBJECTIVE IS"); System.out.println("TO GET 200 POINTS."); System.out.println(); System.out.println(paddedString("THROW", "DESCRIPTION", "PROBABLE SCORE")); System.out.println(paddedString("1", "FAST OVERARM", "BULLSEYE OR COMPLETE MISS")); System.out.println(paddedString("2", "CONTROLLED OVERARM", "10, 20 OR 30 POINTS")); System.out.println(paddedString("3", "UNDERARM", "ANYTHING")); } /** * Calculate the players score * Score is based on the type of shot plus a random factor * * @param playerThrow 1,2, or 3 indicating the type of shot * @return player score */ private int calculatePlayerPoints(int playerThrow) { // -1 is because of 0 base Java array double p1 = this.shots[playerThrow - 1].getShot(0); double p2 = this.shots[playerThrow - 1].getShot(1); double p3 = this.shots[playerThrow - 1].getShot(2); double p4 = this.shots[playerThrow - 1].getShot(3); double random = Math.random(); int points; if (random >= p1) { System.out.println("BULLSEYE!! 40 POINTS!"); points = 40; // If the throw was 1 (bullseye or missed, then make it missed // N.B. This is a fix for the basic code which for shot type 1 // allowed a bullseye but did not make the score zero if a bullseye // was not made (which it should have done). } else if (playerThrow == 1) { System.out.println("MISSED THE TARGET! TOO BAD."); points = 0; } else if (random >= p2) { System.out.println("30-POINT ZONE!"); points = 30; } else if (random >= p3) { System.out.println("20-POINT ZONE"); points = 20; } else if (random >= p4) { System.out.println("WHEW! 10 POINTS."); points = 10; } else { System.out.println("MISSED THE TARGET! TOO BAD."); points = 0; } return points; } /** * Get players shot 1,2, or 3 - ask again if invalid input * * @param player the player we are calculating the throw on * @return 1, 2, or 3 indicating the players shot */ private int getPlayersThrow(Player player) { boolean inputCorrect = false; String theThrow; do { theThrow = displayTextAndGetInput(player.getName() + "'S THROW "); if (theThrow.equals("1") || theThrow.equals("2") || theThrow.equals("3")) { inputCorrect = true; } else { System.out.println("INPUT 1, 2, OR 3!"); } } while (!inputCorrect); return Integer.parseInt(theThrow); } /** * Get players guess from kb * * @return players guess as an int */ private int chooseNumberOfPlayers() { return Integer.parseInt((displayTextAndGetInput("HOW MANY PLAYERS? "))); } /* * Print a message on the screen, then accept input from Keyboard. * * @param text message to be displayed on screen. * @return what was typed by the player. */ private String displayTextAndGetInput(String text) { System.out.print(text); return kbScanner.next(); } /** * Format three strings to a given number of spaces * Replacing the original basic code which used tabs * * @param first String to print in pos 1 * @param second String to print in pos 2 * @param third String to print in pos 3 * @return formatted string */ private String paddedString(String first, String second, String third) { String output = String.format("%1$" + FIRST_IDENT + "s", first); output += String.format("%1$" + SECOND_IDENT + "s", second); output += String.format("%1$" + THIRD_INDENT + "s", third); return output; } } ================================================ FILE: 18_Bullseye/java/src/BullseyeGame.java ================================================ public class BullseyeGame { public static void main(String[] args) { Bullseye bullseye = new Bullseye(); bullseye.play(); } } ================================================ FILE: 18_Bullseye/java/src/Player.java ================================================ /** * A Player in the game - consists of name and score * */ public class Player { private final String name; private int score; Player(String name) { this.name = name; this.score = 0; } public void addScore(int score) { this.score += score; } public String getName() { return name; } public int getScore() { return score; } } ================================================ FILE: 18_Bullseye/java/src/Shot.java ================================================ /** * This class records the percentage chance of a given type of shot * scoring specific points * see Bullseye class points calculation method where its used */ public class Shot { double[] chances; // Array of doubles are passed for a specific type of shot Shot(double[] shots) { chances = new double[shots.length]; System.arraycopy(shots, 0, chances, 0, shots.length); } public double getShot(int index) { return chances[index]; } } ================================================ FILE: 18_Bullseye/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 18_Bullseye/javascript/bullseye.html ================================================ BULLSEYE

    
    
    
    
    
    
    ================================================
    FILE: 18_Bullseye/javascript/bullseye.js
    ================================================
    // BULLSEYE
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    var as = [];
    var s = [];
    var w = [];
    
    // Main program
    async function main()
    {
        print(tab(32) + "BULLSEYE\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        print("IN THIS GAME, UP TO 20 PLAYERS THROW DARTS AT A TARGET\n");
        print("WITH 10, 20, 30, AND 40 POINT ZONES.  THE OBJECTIVE IS\n");
        print("TO GET 200 POINTS.\n");
        print("\n");
        print("THROW\t\tDESCRIPTION\t\tPROBABLE SCORE\n");
        print("1\t\tFAST OVERARM\t\tBULLSEYE OR COMPLETE MISS\n");
        print("2\t\tCONTROLLED OVERARM\t10, 20 OR 30 POINTS\n");
        print("3\t\tUNDERARM\t\tANYTHING\n");
        print("\n");
        m = 0;
        r = 0;
        for (i = 1; i <= 20; i++)
            s[i] = 0;
        print("HOW MANY PLAYERS");
        n = parseInt(await input());
        print("\n");
        for (i = 1; i <= n; i++) {
            print("NAME OF PLAYER #" + i);
            as[i] = await input();
        }
        do {
            r++;
            print("\n");
            print("ROUND " + r + "\n");
            print("---------\n");
            for (i = 1; i <= n; i++) {
                do {
                    print("\n");
                    print(as[i] + "'S THROW");
                    t = parseInt(await input());
                    if (t < 1 || t > 3)
                        print("INPUT 1, 2, OR 3!\n");
                } while (t < 1 || t > 3) ;
                if (t == 1) {
                    p1 = 0.65;
                    p2 = 0.55;
                    p3 = 0.5;
                    p4 = 0.5;
                } else if (t == 2) {
                    p1 = 0.99;
                    p2 = 0.77;
                    p3 = 0.43;
                    p4 = 0.01;
                } else {
                    p1 = 0.95;
                    p2 = 0.75;
                    p3 = 0.45;
                    p4 = 0.05;
                }
                u = Math.random();
                if (u >= p1) {
                    print("BULLSEYE!!  40 POINTS!\n");
                    b = 40;
                } else if (u >= p2) {
                    print("30-POINT ZONE!\n");
                    b = 30;
                } else if (u >= p3) {
                    print("20-POINT ZONE\n");
                    b = 20;
                } else if (u >= p4) {
                    print("WHEW!  10 POINT.\n");
                    b = 10;
                } else {
                    print("MISSED THE TARGET!  TOO BAD.\n");
                    b = 0;
                }
                s[i] += b;
                print("TOTAL SCORE = " + s[i] + "\n");
            }
            for (i = 1; i <= n; i++) {
                if (s[i] >= 200) {
                    m++;
                    w[m] = i;
                }
            }
        } while (m == 0) ;
        print("\n");
        print("WE HAVE A WINNER!!\n");
        print("\n");
        for (i = 1; i <= m; i++)
            print(as[w[i]] + " SCORED " + s[w[i]] + " POINTS.\n");
        print("\n");
        print("THANKS FOR THE GAME.\n");
    }
    
    main();
    
    
    ================================================
    FILE: 18_Bullseye/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 18_Bullseye/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 18_Bullseye/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 18_Bullseye/perl/bullseye.pl
    ================================================
    #!/usr/bin/perl
    use strict;
    
    
    print ' 'x 32 . "BULLSEYE\n";
    print ' 'x 15 . "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n";
    print "\n\n\n";
    
    print "IN THIS GAME, UP TO 20 PLAYERS THROW DARTS AT A TARGET\n";
    print "WITH 10, 20, 30, AND 40 POINT ZONES. THE OBJECTIVE IS\n";
    print "TO GET 200 POINTS.\n\n";
    
    print "THROW\t\tDESCRIPTION\t\tPROBABLE SCORE\n";
    print" 1\t\tFAST OVERARM\t\tBULLSEYE OR COMPLETE MISS\n";
    print" 2\t\tCONTROLLED OVERARM\t10, 20 OR 30 POINTS\n";
    print" 3\t\tUNDERARM\t\tANYTHING\n\n";
    
    my @A; my $M=0; my $R=0; my @S; my @W; for (my $I=1; $I<=20; $I++) { $S[$I]= 0; }
    print "HOW MANY PLAYERS? "; chomp(my $N = ); print "\n";
    for (my $I=1; $I<=$N; $I++) {
    	print "NAME OF PLAYER #$I? "; chomp($A[$I] = );
    	}
    
    do {
    	$R= $R+1; print "\n"; print "ROUND $R---------\n";
    	for (my $I=1; $I<=$N; $I++) {
    		my $Flag=0;
    		my $T;
    		do {
    			print "\n"; print "$A[$I]'S THROW? "; chomp($T = );
    			if ($T<1 || $T>3) { print "INPUT 1, 2, OR 3!\n"; $Flag=0; }
    				else { $Flag=1; }
    			} until ($Flag==1);
    
    		my $P1; my $P2; my $P3; my $P4;
    		if ($T==1) { $P1=.65; $P2=.55; $P3=.5; $P4=.5; }
    		if ($T==2) { $P1=.99; $P2=.77; $P3=.43; $P4=.01; }
    		if ($T==3) { $P1=.95; $P2=.75; $P3=.45; $P4=.05; }
    
    		my $B=0;
    		my $U= rand(1);
    		if ($U>= $P1) { print "BULLSEYE!! 40 POINTS!\n"; $B=40; }
    			elsif ($U>= $P2) { print "30-POINT ZONE!\n"; $B=30; }
    			elsif ($U>= $P3) { print "20-POINT ZONE\n"; $B=20; }
    			elsif ($U>= $P4) { print "WHEW! 10 POINTS.\n"; $B=10; }
    			else { print "MISSED THE TARGET! TOO BAD.\n"; $B=0; }
    		$S[$I]= $S[$I]+$B; print "TOTAL SCORE =$S[$I]";
    		}
    
    	for (my $I=1; $I<=$N; $I++) {
    		if ($S[$I]>=200) { $M= $M+1; $W[$M]= $I; }
    		}
    	} until ($M!=0);
    
    print "\n"; print "WE HAVE A WINNER!!\n\n";
    for (my $I=1; $I<=$M; $I++) { print $A[$W[$I]]." SCORED ".$S[$W[$I]]." POINTS.\n"; }
    print "\n"; print "THANKS FOR THE GAME.\n"; exit;
    
    
    ================================================
    FILE: 18_Bullseye/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 18_Bullseye/python/bullseye.py
    ================================================
    import random
    from dataclasses import dataclass
    from typing import List
    
    
    @dataclass
    class Player:
        name: str
        score: int = 0
    
    
    def print_intro() -> None:
        print(" " * 32 + "BULLSEYE")
        print(" " * 15 + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY")
        print("\n" * 3, end="")
        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", end="")
        print(" " * 20 + "DESCRIPTION", end="")
        print(" " * 45 + "PROBABLE SCORE")
        print(" 1", end="")
        print(" " * 20 + "FAST OVERARM", end="")
        print(" " * 45 + "BULLSEYE OR COMPLETE MISS")
        print(" 2", end="")
        print(" " * 20 + "CONTROLLED OVERARM", end="")
        print(" " * 45 + "10, 20 OR 30 POINTS")
        print(" 3", end="")
        print(" " * 20 + "UNDERARM", end="")
        print(" " * 45 + "ANYTHING")
        print()
    
    
    def print_outro(players: List[Player], winners: List[int]) -> None:
        print()
        print("WE HAVE A WINNER!!")
        print()
        for winner in winners:
            print(f"{players[winner].name} SCORED {players[winner].score} POINTS.")
        print()
        print("THANKS FOR THE GAME.")
    
    
    def main() -> None:
        print_intro()
        players: List[Player] = []
    
        winners: List[int] = []  # will point to indices of player_names
    
        nb_players = int(input("HOW MANY PLAYERS? "))
        for _ in range(nb_players):
            player_name = input("NAME OF PLAYER #")
            players.append(Player(player_name))
    
        round_number = 0
        while not winners:
            round_number += 1
            print()
            print(f"ROUND {round_number}---------")
            for player in players:
                print()
                while True:
                    throw = int(input(f"{player.name}'S THROW? "))
                    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
                    probability_3 = 0.5
                    probability_4 = 0.5
                elif throw == 2:
                    probability_1 = 0.99
                    probability_2 = 0.77
                    probability_3 = 0.43
                    probability_4 = 0.01
                elif throw == 3:
                    probability_1 = 0.95
                    probability_2 = 0.75
                    probability_3 = 0.45
                    probability_4 = 0.05
                throwing_luck = random.random()
                if throwing_luck >= probability_1:
                    print("BULLSEYE!!  40 POINTS!")
                    points = 40
                elif throwing_luck >= probability_2:
                    print("30-POINT ZONE!")
                    points = 30
                elif throwing_luck >= probability_3:
                    print("20-POINT ZONE")
                    points = 20
                elif throwing_luck >= probability_4:
                    print("WHEW!  10 POINTS.")
                    points = 10
                else:
                    print("MISSED THE TARGET!  TOO BAD.")
                    points = 0
                player.score += points
                print(f"TOTAL SCORE = {player.score}")
            winners.extend(
                player_index
                for player_index, player in enumerate(players)
                if player.score > 200
            )
        print_outro(players, winners)
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 18_Bullseye/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 18_Bullseye/rust/Cargo.toml
    ================================================
    [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"
    
    ================================================
    FILE: 18_Bullseye/rust/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Rust](https://www.rust-lang.org/) by Anthony Rubick [AnthonyMichaelTDM](https://github.com/AnthonyMichaelTDM)
    
    ================================================
    FILE: 18_Bullseye/rust/src/lib.rs
    ================================================
    /*
     lib.rs contains all the logic of the program
    */
    
    use std::{error::Error, fmt::Display, str::FromStr, io::{self, Write}};
    
    use rand::Rng;
    
    /// handles setup for the game
    pub struct Config {
        players: Vec,
    }
    impl Config {
        /// creates and returns a new Config from user input
        pub fn new() -> Result> {
            //DATA
            let mut config: Config = Config { players: Vec::new() };
            let num_players: usize;
    
            //get data from user input
    
            //get num players
            //input looop
            num_players = loop {
                match get_number_from_input("HOW MANY PLAYERS? ", 1, 0) {
                    Ok(num) => break num,
                    Err(e) => {
                        println!("{}",e);
                        continue;
                    },
                }
            };
    
            //get names of all players
            for id in 1..=num_players {
                let name = get_string_from_user_input( format!("NAME OF PLAYER#{}: ", id).as_str())?;
                config.players.push(Player::from(&name));
            }
    
            //return new config
            return Ok(config);
        }
    }
    pub struct Player {
        name: String,
        score: usize,
    }
    impl Player {
        fn from(name: &str) -> Player {
            return Player { name: String::from(name), score: 0 };
        }
    }
    
    /// run the program
    pub fn run(config: &mut Config) -> Result<(), Box> {
        //DATA
        let mut round = 1;
        let mut rng = rand::thread_rng();
    
        //gameloop
        loop {
            //print round
            println!("");
            println!("");
            println!("ROUND {}", round);
            println!("---------");
    
            //each players play
            for player in config.players.iter_mut() {
                //prompt user for players move
                //input loop
                let throw = loop {
                    match get_number_from_input( format!("{}'S THROW (input 1, 2, or 3): ", player.name).as_str(), 1, 3) {
                        Ok(t) => break t,
                        Err(err) => {
                            println!("{}", err);
                            continue;
                        },
                    };
                };
    
                //get probabilities of the various outcomes based on the throw
                let p_bullseye;
                let p_30_point_zone;
                let p_20_point_zone;
                let p_10_point_zone;
                match throw {
                    1 => {
                        p_bullseye = 0.65;
                        p_30_point_zone = 0.55;
                        p_20_point_zone = 0.5;
                        p_10_point_zone = 0.5;
                    },
                    2 => {
                        p_bullseye = 0.99;
                        p_30_point_zone = 0.77;
                        p_20_point_zone = 0.43;
                        p_10_point_zone = 0.01;
                    },
                    _ => {
                        p_bullseye = 0.95;
                        p_30_point_zone = 0.75;
                        p_20_point_zone = 0.45;
                        p_10_point_zone = 0.05;
                    },
                }
    
                //determine results
                let roll = rng.gen::();
                if roll >= p_bullseye{
                    println!("BULLSEYE!!  40 POINTS!");
                    player.score += 40;
                } else if roll >= p_30_point_zone {
                    println!("30-POINT ZONE!");
                    player.score += 30;
                } else if roll >= p_20_point_zone {
                    println!("20-POINT ZONE");
                    player.score += 20;
                } else if roll >= p_10_point_zone {
                    println!("WHEW!  10 POINTS.");
                    player.score += 10;
                } else {
                    println!("MISSED THE TARGET!  TOO BAD.");
                }
    
                //print their score
                println!("TOTAL SCORE = {}", player.score);
            }
    
            //stuff to do before next round
            if config.players.iter().any(|player| player.score >= 200) {
                //print congradulations and scores
                println!("\nWE HAVE A WINNER!!\n");
                for player in config.players.iter() {
                    println!("{} SCORED {} POINTS", player.name, player.score);
                }
                //exit loop
                break;
            } else {
                //otherwise do another round
                round += 1;
            }
        }
    
        
        
    
        //return to main
        Ok(())
    }
    
    /// gets a string from user input
    fn get_string_from_user_input(prompt: &str) -> Result> {
        //DATA
        let mut raw_input = String::new();
    
        //print prompt
        print!("{}", prompt);
        //make sure it's printed before getting input
        io::stdout().flush().unwrap();
    
        //read user input from standard input, and store it to raw_input, then return it or an error as needed
        raw_input.clear(); //clear input
        match io::stdin().read_line(&mut raw_input) {
            Ok(_num_bytes_read) => return Ok(String::from(raw_input.trim())),
            Err(err) => return Err(format!("ERROR: CANNOT READ INPUT!: {}", err).into()),
        }
    }
    
    /// generic function to get a number from the passed string (user input)
    /// pass a min lower  than the max to have minimun and maximun bounds
    /// pass a min higher than the max to only have a minumum bound
    /// pass a min equal   to  the max to only have a maximun bound
    /// 
    /// Errors:
    /// no number on user input
    fn get_number_from_input(prompt: &str, min:T, max:T) -> Result> {
        //DATA
        let raw_input: String;
        let processed_input: String;
    
        
        //input looop
        raw_input = loop {
            match get_string_from_user_input(prompt) {
                Ok(input) => break input,
                Err(e) => {
                    eprintln!("{}",e);
                    continue;
                },
            }
        };
    
        //filter out num-numeric characters from user input
        processed_input = raw_input.chars().filter(|c| c.is_numeric()).collect();
    
        //from input, try to read a number
        match processed_input.trim().parse() {
            Ok(i) => {
                //what bounds must the input fall into
                if min < max {  //have a min and max bound: [min,max]
                    if i >= min && i <= max {//is input valid, within bounds
                        return Ok(i); //exit the loop with the value i, returning it
                    } else { //print error message specific to this case
                        return Err(format!("ONLY BETWEEN {} AND {}, PLEASE!", min, max).into());
                    } 
                } else if min > max { //only a min bound: [min, infinity)
                    if i >= min {
                        return Ok(i);
                    } else {
                        return Err(format!("NO LESS THAN {}, PLEASE!", min).into());
                    }
                } else { //only a max bound: (-infinity, max]
                    if i <= max {
                        return Ok(i);
                    } else {
                        return Err(format!("NO MORE THAN {}, PLEASE!", max).into());
                    }
                }
            },
            Err(_e) => return Err(format!("Error: couldn't find a valid number in {}",raw_input).into()),
        }
    }
    
    
    ================================================
    FILE: 18_Bullseye/rust/src/main.rs
    ================================================
    /*
        The responsibilities that remain in the main function after separating concerns
        should be limited to the following:
     - Setting up any configuration
     - Calling a run function in lib.rs
     - Handling the error if run returns an error
    */
    
    use std::process;       //allows for some better error handling
    
    mod lib;
    use lib::Config;
    
    /// main function
    fn main() {
        //greet user
        welcome();
    
        // set up other configuration
        let mut config = Config::new().unwrap_or_else(|err| {
            eprintln!("Problem configuring program: {}", err);
            process::exit(1);
        });
    
        // run the program
        if let Err(e) = lib::run(&mut config) {
            eprintln!("Application Error: {}", e); //use the eprintln! macro to output to standard error
            process::exit(1); //exit the program with an error code
        }
    
        //end of program
        println!("THANKS FOR PLAYING!");
    }
    
    /// prints the welcome/start message
    fn welcome() {
        println!("
                                   BULLSEYE
                  CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY
    
    
    
        IN THIS GAME, UP TO 20 PLAYERS THROW DARTS AT A TARGET
        WITH 10, 20, 30, AND 40 POINT ZONES.  THE OBJECTIVE IS
        TO GET 200 POINTS.
        
        | Throw | Description        | Probable Score            |
        |-------|--------------------|---------------------------|
        | 1     | Fast overarm       | Bullseye or complete miss |
        | 2     | Controlled overarm | 10, 20, or 30 points      |
        | 3     | Underarm           | Anything                  |
    
        ");
    }
    
    
    ================================================
    FILE: 18_Bullseye/rust/src/note on separation of converns for rust projects.md
    ================================================
     # Separation of concerns for Binary Projects
    
    The organizational problem of allocating responsibility for multiple tasks to the main function is common to many projects. As a result, the Rust community has developed a process to use as a guideline for splitting the separate concerns of a binary program when main starts getting large. 
    
     ## The process has the following steps:
     - Split your program into a main.rs and a lib.rs and move your program’s logic to lib.rs.
     - As long as your command line logic is small, it can remain in main.rs.
     - When the command line logic starts getting complicated, extract it from main.rs and move it to lib.rs.
    
     ## The responsibilities that remain in the main function after this process should be limited to the following:
     - Calling the command line or input parsing logic with the argument values
     - Setting up any other configuration
     - Calling a run function in lib.rs
     - Handling the error if run returns an error
    
    This pattern is about separating concerns: main.rs handles running the program, and lib.rs handles all the logic of the task at hand. Because you can’t test the main function directly, this structure lets you test all of your program’s logic by moving it into functions in lib.rs. The only code that remains in main.rs will be small enough to verify its correctness by reading it.
    
    ================================================
    FILE: 18_Bullseye/vbnet/Bullseye.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Bullseye", "Bullseye.vbproj", "{0CFD308F-EB9C-4A05-B742-5EE7C912A5E4}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{0CFD308F-EB9C-4A05-B742-5EE7C912A5E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{0CFD308F-EB9C-4A05-B742-5EE7C912A5E4}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{0CFD308F-EB9C-4A05-B742-5EE7C912A5E4}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{0CFD308F-EB9C-4A05-B742-5EE7C912A5E4}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 18_Bullseye/vbnet/Bullseye.vbproj
    ================================================
    
      
        Exe
        Bullseye
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 18_Bullseye/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 19_Bunny/README.md
    ================================================
    ### Bunny
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=35)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=50)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 19_Bunny/bunny.bas
    ================================================
    10 PRINT TAB(33);"BUNNY"
    20 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    30 PRINT: PRINT: PRINT
    100 REM  "BUNNY" FROM AHL'S 'BASIC COMPUTER GAMES'
    110 REM
    120 FOR I=0 TO 4: READ B(I): NEXT I
    130 GOSUB 260
    140 L=64: REM  ASCII LETTER CODE...
    150 REM
    160 PRINT
    170 READ X: IF X<0 THEN 160
    175 IF X>128 THEN 240
    180 PRINT TAB(X);: READ Y
    190 FOR I=X TO Y: J=I-5*INT(I/5)
    200 PRINT CHR$(L+B(J));
    210 NEXT I
    220 GOTO 170
    230 REM
    240 GOSUB 260: GOTO 450
    250 REM
    260 FOR I=1 TO 6: PRINT CHR$(10);: NEXT I
    270 RETURN
    280 REM
    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
    440 REM
    450 END
    
    
    ================================================
    FILE: 19_Bunny/csharp/BasicData.cs
    ================================================
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace Bunny
    {
        internal class BasicData
        {
            private readonly int[] data;
    
            private int index;
    
            public BasicData(int[] data)
            {
                this.data = data;
                index = 0;
            }
            public int Read()
            {
                return data[index++];
            }
        }
    }
    
    
    ================================================
    FILE: 19_Bunny/csharp/Bunny.cs
    ================================================
    namespace Bunny
    {
        internal class Bunny
        {
            private const int asciiBase = 64;
            private readonly int[] bunnyData = {
                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
            };
    
            public void Run()
            {
                PrintString(33, "BUNNY");
                PrintString(15, "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
                PrintLines(3);
    
                // Set up a BASIC-ish data object
                BasicData data = new (bunnyData);
    
                // Get the first five data values into an array.
                // These are the characters we are going to print.
                // Unlike the original program, we are only converting
                // them to ASCII once.
                var a = new char[5];
                for (var i = 0; i < 5; ++i)
                {
                    a[i] = (char)(asciiBase + data.Read());
                }
                PrintLines(6);
    
                PrintLines(1);
                var col = 0;
                while (true)
                {
                    var x = data.Read();
                    if (x < 0) // Start a new line
                    {
                        PrintLines(1);
                        col = 0;
                        continue;
                    }
                    if (x > 128) break; // End processing
                    col += PrintSpaces(x - col); // Move to TAB position x (sort of)
                    var y = data.Read(); // Read the next value
                    for (var i = x; i <= y; ++i)
                    {
                        // var j = i - 5 * (i / 5); // BASIC didn't have a modulus operator
                        Console.Write(a[i % 5]);
                        // Console.Write(a[col % 5]); // This works, too
                        ++col;
                    }
                }
                PrintLines(6);
            }
            private static void PrintLines(int count)
            {
                for (var i = 0; i < count; ++i)
                    Console.WriteLine();
            }
            private static int PrintSpaces(int count)
            {
                for (var i = 0; i < count; ++i)
                    Console.Write(' ');
                return count;
            }
            public static void PrintString(int tab, string value, bool newLine = true)
            {
                PrintSpaces(tab);
                Console.Write(value);
                if (newLine) Console.WriteLine();
            }
    
        }
    }
    
    
    ================================================
    FILE: 19_Bunny/csharp/Bunny.csproj
    ================================================
    
      
        Exe
        net6.0
        10
        enable
        enable
      
    
    
    
    ================================================
    FILE: 19_Bunny/csharp/Bunny.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bunny", "Bunny.csproj", "{6685F5CF-20F2-4682-B187-50105BD44906}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{6685F5CF-20F2-4682-B187-50105BD44906}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{6685F5CF-20F2-4682-B187-50105BD44906}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{6685F5CF-20F2-4682-B187-50105BD44906}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{6685F5CF-20F2-4682-B187-50105BD44906}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 19_Bunny/csharp/Program.cs
    ================================================
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace Bunny
    {
        public static class Program
        {
            public static void Main()
            {
                new Bunny().Run();
            }
        }
    }
    
    
    ================================================
    FILE: 19_Bunny/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 19_Bunny/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 19_Bunny/java/src/Bunny.java
    ================================================
    import java.util.ArrayList;
    import java.util.Arrays;
    
    /**
     * Bunny
     * 

    * Based on the Basic program Bunny * https://github.com/coding-horror/basic-computer-games/blob/main/19%20Bunny/bunny.bas *

    * Note: The idea was to create a version of the 1970's Basic game in Java, without introducing * new features - no additional text, error checking, etc has been added. */ public class Bunny { // First 4 elements are the text BUNNY, so skip those public static final int REAL_DATA_START_POS = 5; // Data for characters is not representative of three ASCII character, so we have // to add 64 to it as per original program design. public static final int CONVERT_TO_ASCII = 64; public static final int EOF = 4096; //End of file public static final int EOL = -1; // End of line // Contains the data to draw the picture private final ArrayList data; public Bunny() { data = loadData(); } /** * Show an intro, then draw the picture. */ public void process() { intro(); // First 5 characters of data spells out BUNNY, so add this to a string StringBuilder bunnyBuilder = new StringBuilder(); for (int i = 0; i < REAL_DATA_START_POS; i++) { // Convert the data to the character representation for output // Ascii A=65, B=66 - see loadData method bunnyBuilder.append(Character.toChars(data.get(i) + CONVERT_TO_ASCII)); } // We now have the string to be used in the output String bunny = bunnyBuilder.toString(); int pos = REAL_DATA_START_POS; // Point to the start of the actual data int previousPos = 0; // Loop until we reach a number indicating EOF while (true) { // This is where we want to start drawing int first = data.get(pos); if (first == EOF) { break; } if (first == EOL) { System.out.println(); previousPos = 0; // Move to the next element in the ArrayList pos++; continue; } // Because we are not using screen positioning, we just add an appropriate // numbers of spaces from where we want to be, and where we last outputted something System.out.print(addSpaces(first - previousPos)); // We use this next time around the loop previousPos = first; // Move to next element pos++; // This is where we want to stop drawing/ int second = data.get(pos); // Now we loop through the number of characters to draw using // the starting and ending point. for (int i = first; i <= second; i++) { // Cycle through the actual number of characters but use the // remainder operator to ensure we only use characters from the // bunny string System.out.print(bunny.charAt(i % bunny.length())); // Advance where we were at. previousPos += 1; } // Point to next data element pos++; } System.out.println(); } private void intro() { System.out.println(addSpaces(33) + "BUNNY"); System.out.println(addSpaces(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println(); } /** * Return a string of x spaces * * @param spaces number of spaces required * @return String with number of spaces */ private String addSpaces(int spaces) { char[] spacesTemp = new char[spaces]; Arrays.fill(spacesTemp, ' '); return new String(spacesTemp); } /** * Original Basic program had the data in DATA format. * We're importing all the data into an array for ease of processing. * Format of data is * characters 0-4 is the letters that will be used in the output. 64 + the value represents the ASCII character * ASCII code 65 = A, 66 = B, etc. so 2+64=66 (B), 21+64=85 (U) and so on. * Then we next have pairs of numbers. * Looking at the this data * 1,2,-1,0,2,45,50,-1 * That reads as * 1,2 = draw characters - in this case BU * -1 = go to a new line * 0,2 = DRAW BUN * 45,50 = DRAW BUNNYB starting at position 45 * and so on. * 4096 is EOF * * @return ArrayList of type Integer containing the data */ private ArrayList loadData() { ArrayList theData = new ArrayList<>(); // This is the data faithfully added from the original basic program. // Notes: // The first 5 ints are ASCII character (well 64 is added to make them ASCII chars we can output). theData.addAll(Arrays.asList(2, 21, 14, 14, 25)); theData.addAll(Arrays.asList(1, 2, -1, 0, 2, 45, 50, -1, 0, 5, 43, 52, -1, 0, 7, 41, 52, -1)); theData.addAll(Arrays.asList(1, 9, 37, 50, -1, 2, 11, 36, 50, -1, 3, 13, 34, 49, -1, 4, 14, 32, 48, -1)); theData.addAll(Arrays.asList(5, 15, 31, 47, -1, 6, 16, 30, 45, -1, 7, 17, 29, 44, -1, 8, 19, 28, 43, -1)); theData.addAll(Arrays.asList(9, 20, 27, 41, -1, 10, 21, 26, 40, -1, 11, 22, 25, 38, -1, 12, 22, 24, 36, -1)); theData.addAll(Arrays.asList(13, 34, -1, 14, 33, -1, 15, 31, -1, 17, 29, -1, 18, 27, -1)); theData.addAll(Arrays.asList(19, 26, -1, 16, 28, -1, 13, 30, -1, 11, 31, -1, 10, 32, -1)); theData.addAll(Arrays.asList(8, 33, -1, 7, 34, -1, 6, 13, 16, 34, -1, 5, 12, 16, 35, -1)); theData.addAll(Arrays.asList(4, 12, 16, 35, -1, 3, 12, 15, 35, -1, 2, 35, -1, 1, 35, -1)); theData.addAll(Arrays.asList(2, 34, -1, 3, 34, -1, 4, 33, -1, 6, 33, -1, 10, 32, 34, 34, -1)); theData.addAll(Arrays.asList(14, 17, 19, 25, 28, 31, 35, 35, -1, 15, 19, 23, 30, 36, 36, -1)); theData.addAll(Arrays.asList(14, 18, 21, 21, 24, 30, 37, 37, -1, 13, 18, 23, 29, 33, 38, -1)); theData.addAll(Arrays.asList(12, 29, 31, 33, -1, 11, 13, 17, 17, 19, 19, 22, 22, 24, 31, -1)); theData.addAll(Arrays.asList(10, 11, 17, 18, 22, 22, 24, 24, 29, 29, -1)); theData.addAll(Arrays.asList(22, 23, 26, 29, -1, 27, 29, -1, 28, 29, -1, 4096)); return theData; } public static void main(String[] args) { Bunny bunny = new Bunny(); bunny.process(); } } ================================================ FILE: 19_Bunny/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 19_Bunny/javascript/bunny.html ================================================ BUNNY

    
    
    
    
    
    
    ================================================
    FILE: 19_Bunny/javascript/bunny.js
    ================================================
    // BUNNY
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    function print(str)
    {
    	document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function tab(space)
    {
    	var str = "";
    	while (space-- > 0)
    		str += " ";
    	return str;
    }
    
    var bunny_string = ["B","U","N","N","Y"];
    
    var bunny_data = [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];
    
    print(tab(32) + "BUNNY\n");
    print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
    print("\n");
    print("\n");
    print("\n");
    
    var l = 64;	// ASCII letter code
    var pos = 0;
    
    print("\n");
    
    var str = "";
    for (var pos = 0; bunny_data[pos] < 128; pos++) {
    	if (bunny_data[pos] < 0) {
    		print(str + "\n");
    		str = "";
    		continue;
    	}
    	while (str.length < bunny_data[pos])
    		str += " ";
    	for (var i = bunny_data[pos]; i <= bunny_data[pos + 1]; i++)
    		str += bunny_string[i % 5];
    	pos++;
    }
    
    
    ================================================
    FILE: 19_Bunny/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 19_Bunny/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 19_Bunny/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 19_Bunny/perl/bunny.pl
    ================================================
    #!/usr/bin/perl
    use strict;
    
    print ' 'x 33 . "BUNNY\n";
    print ' 'x 15 . "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n";
    print "\n"; print "\n"; print "\n";
    #REM "BUNNY" FROM AHL'$S 'BASIC COMPUTER GAMES';
    
    # This data contains the letter Bunny A=1, B=2, C=3...
    my @A= (2,21,14,14,25);
    
    # This data structure contains the pair of start and finish absolute position to show letters.
    # -1 means next line, whatever more than 128 will stop the data read.
    my @B= (
    	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
    	);
    
    
    &ENTERS();
    my $L= 64; #REM ASCII LETTER CODE...
    print "\n";
    
    my $P=0; #Position read iterator
    my $Line= ' 'x 60;
    while (1) {
    	my $X= $B[$P]; $P++; #Read start position.
    	if ($X<0) { print "$Line\n";; $Line= ' 'x 60; next; }
    	if ($X>128) { last; }
    	my $Y= $B[$P]; $P++; #Read end position.
    
    	for (my $I=$X; $I<=$Y; $I++) { my $J=$I-5*int($I/5);
    		substr($Line, $I, 1, chr($L+$A[$J])); #You can change $I for $X to get a nice bunny shadow.
    		}
    	}
    
    &ENTERS();
    exit;
    
    
    sub ENTERS { #GOSUB 260
    	for (my $I=1; $I<=6; $I++) { print chr(10); }
    	}
    
    
    ================================================
    FILE: 19_Bunny/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 19_Bunny/python/bunny.py
    ================================================
    #!/usr/bin/env python3
    
    
    import json
    
    # This data is meant to be read-only, so we are storing it in a tuple
    with open("data.json") as f:
        DATA = tuple(json.load(f))
    
    
    def print_intro() -> None:
        print(" " * 33 + "BUNNY")
        print(" " * 15 + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY")
        print("\n\n")
    
    
    def main() -> None:
        print_intro()
    
        # Using an iterator will give us a similar interface to BASIC's READ
        # command. Instead of READ, we will call 'next(data)' to fetch the next element.
        data = iter(DATA)
    
        # Read the first 5 numbers. These correspond to letters of the alphabet.
        # B=2, U=21, N=14, N=14, Y=25
    
        # Usually, list comprehensions are good for transforming each element in a sequence.
        # In this case, we are using range to repeat the call to next(data) 5 times. The underscore (_)
        # indicates that the values from range are discarded.
        bunny = [next(data) for _ in range(5)]
        L = 64
    
        # Interpretting a stream of data is a very common software task. We've already intepretted
        # the first 5 numbers as letters of the alphabet (with A being 1). Now, we are going to
        # combine this with a different interpretation of the following data to draw on the screen.
        # The drawing data is essentially a series of horizontal line segments given as begin and end
        # offsets.
        while True:
            command = next(data)
    
            if command < 0:
                print()
                continue
    
            if command > 128:
                break
    
            # If we've reached this portion of the code, 'command' indicates the 'start'
            # position of a line segment.
            start = command
            # Position cursor at start
            print(" " * start, end="")
    
            # The following number, indicates the end of the segment.
            end = next(data)
            # Unlike FOR I=X TO Y, the 'stop' argument of 'range' is non-inclusive, so we must add 1
            for i in range(start, end + 1, 1):
                # Cycle through the letters in "BUNNY" as we draw line
                j = i - 5 * int(i / 5)
                print(chr(L + bunny[j]), end="")
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 19_Bunny/python/data.json
    ================================================
    [
        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
    ]
    
    
    ================================================
    FILE: 19_Bunny/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    There are two versions of this program here:
    
    * `bunny-faithful.rb` tries to be faithful to the design of the original
      BASIC program.
    * `bunny-modern.rb` takes more advantage of the features of modern
      tools and languages.
    
    
    ================================================
    FILE: 19_Bunny/ruby/bunny-faithful.rb
    ================================================
    #!/bin/env ruby
    
    # Bunny - Print a large ASCII-pixel bunny icon made up of the letters
    # of the word BUNNY.
    
    # This is a recreation of bunny.bas in Ruby that attempts to remain
    # relatively faithful to the design of the original program.
    #
    # The BASIC version works by storing the image as a series of pairs of
    # numbers containing ranges of text columns that will need to be
    # filled in with non-blank characters.
    #
    # For example, the first few entries in the block of DATA statements are:
    #
    #       1,2,-1,0,2,45,50,-1 ...
    #
    #           * 1,2 means "write letters from columns 1 to 2
    #           * -1 starts a new line
    #           * 0,2 draws letters between columns 0 and 2
    #           * 45,50 draws letters between columns 45 and 50
    #           * -1 starts a new line
    #
    # ...and so on.
    #
    # We keep the data statements as they are and redraw the image using
    # them.  (Well, we drop the last one because it's the end-of-data flag
    # and Ruby is perfectly effective at finding the end of the list.)
    #
    # One tricky bit: BASIC has a function called 'tab()' which sets the
    # output column to the given position and which the BASIC version uses
    # to pick the columns to write to.  Ruby doesn't have an equivalent
    # feature (well, not without a *lot* more complexity).  Fortunately,
    # the data always draws from left to right so we just keep track of
    # the last column written to and then add some spaces to advance to
    # where we need to be.
    #
    
    
    # Do the thing.  (We put it in a function to keep from spewing global
    # variables all over the place.  It's not really necessary here but
    # it's good practice.)
    def main
    
      # Print the heading.  Note the highly advanced lower-case letters.
      puts ' '*33 + "Bunny"
      puts ' '*15 + "Creative Computing  Morristown, New Jersey"
    
      # Print blank lines.
      print "\n\n\n"
    
      # The positions to write; this is ripped from the BASIC program's
      # DATA statements.
      positions = [
        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,
      ]
    
      # The text we're writing.
      text = "BUNNY"
    
      # Draw the bunny.
      last_pos = 0
      while positions.size > 0
        first = positions.shift
    
        # If we've found -1, start a new line
        if first == -1
          puts
          last_pos = 0
          next
        end
    
        # Advance to start of the range
        print ' '*(first - last_pos)
        last_pos = first
    
        # Now, draw pixels:
        second = positions.shift
        for i in first .. second
          print text[i % text.size] # choose the letter according to the column
          last_pos += 1
        end
      end
    
      # Print the final blank line
      puts
    end
    
    main
    
    
    ================================================
    FILE: 19_Bunny/ruby/bunny-modern.rb
    ================================================
    #!/bin/env ruby
    
    # Bunny - Print a large ASCII-pixel bunny icon made up of the letters
    # of the word BUNNY.
    
    # This is a recreation of bunny.bas in Ruby that takes advantage of
    # the language's features to make the code easier to understand and
    # modify.
    #
    # Instead of storing the image as a set of ranges, we just store it as
    # ASCII art, then replace the pixes with the appropriate letters when
    # printing.  In addition to being simpler, this also lets you modify
    # the image much more easily.
    
    
    # We assume a screen width of 80.  (With modern consoles, doing this
    # accurately gets complex and it's really not worth the bother for
    # this little program so 80 columns it is.)
    ScreenWidth = 80
    
    
    # The bunny image.  Totally not the logo of a magazine that was edgy
    # in the 1970s.
    Bunny = <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
    }
    
    
    
    ================================================
    FILE: 19_Bunny/vbnet/Bunny.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Bunny", "Bunny.vbproj", "{E9098ADF-4B31-4082-9102-2A5BB8B40C6C}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{E9098ADF-4B31-4082-9102-2A5BB8B40C6C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{E9098ADF-4B31-4082-9102-2A5BB8B40C6C}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{E9098ADF-4B31-4082-9102-2A5BB8B40C6C}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{E9098ADF-4B31-4082-9102-2A5BB8B40C6C}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 19_Bunny/vbnet/Bunny.vbproj
    ================================================
    
      
        Exe
        Bunny
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 19_Bunny/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 20_Buzzword/README.md
    ================================================
    ### Buzzword
    
    This program is an invaluable aid for preparing speeches and briefings about educational technology. This buzzword generator provides sets of three highly-acceptable words to work into your material. Your audience will never know that the phrases don’t really mean much of anything because they sound so great! Full instructions for running are given in the program.
    
    This version of Buzzword was written by David Ahl.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=36)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=51)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 20_Buzzword/buzzword.bas
    ================================================
    10 PRINT TAB(26);"BUZZWORD GENERATOR"
    20 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    30 PRINT:PRINT:PRINT
    40 PRINT "THIS PROGRAM PRINTS HIGHLY ACCEPTABLE PHRASES IN"
    50 PRINT "'EDUCATOR-SPEAK' THAT YOU CAN WORK INTO REPORTS"
    60 PRINT "AND SPEECHES.  WHENEVER A QUESTION MARK IS PRINTED,"
    70 PRINT "TYPE A 'Y' FOR ANOTHER PHRASE OR 'N' TO QUIT."
    80 PRINT:PRINT:PRINT "HERE'S THE FIRST PHRASE:"
    90 DIM A$(40)
    100 FOR I=1 TO 39 : READ A$(I) : NEXT I
    110 PRINT A$(INT(13*RND(1)+1));" ";
    120 PRINT A$(INT(13*RND(1)+14));" ";
    130 PRINT A$(INT(13*RND(1)+27)) : PRINT
    150 INPUT Y$ : IF Y$="Y" THEN 110
    160 GOTO 999
    200 DATA "ABILITY","BASAL","BEHAVIORAL","CHILD-CENTERED"
    210 DATA "DIFFERENTIATED","DISCOVERY","FLEXIBLE","HETEROGENEOUS"
    220 DATA "HOMOGENEOUS","MANIPULATIVE","MODULAR","TAVISTOCK"
    230 DATA "INDIVIDUALIZED","LEARNING","EVALUATIVE","OBJECTIVE"
    240 DATA "COGNITIVE","ENRICHMENT","SCHEDULING","HUMANISTIC"
    250 DATA "INTEGRATED","NON-GRADED","TRAINING","VERTICAL AGE"
    260 DATA "MOTIVATIONAL","CREATIVE","GROUPING","MODIFICATION"
    270 DATA "ACCOUNTABILITY","PROCESS","CORE CURRICULUM","ALGORITHM"
    280 DATA "PERFORMANCE","REINFORCEMENT","OPEN CLASSROOM","RESOURCE"
    290 DATA "STRUCTURE","FACILITY","ENVIRONMENT"
    999 PRINT "COME BACK WHEN YOU NEED HELP WITH ANOTHER REPORT!":END
    
    
    ================================================
    FILE: 20_Buzzword/csharp/Buzzword.csproj
    ================================================
    
    
      
        Exe
        net5.0
      
    
    
    
    
    ================================================
    FILE: 20_Buzzword/csharp/Buzzword.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.810.15
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Buzzword", "Buzzword.csproj", "{E342DEB2-F009-47FD-85F6-E84965EAAB56}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{E342DEB2-F009-47FD-85F6-E84965EAAB56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{E342DEB2-F009-47FD-85F6-E84965EAAB56}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{E342DEB2-F009-47FD-85F6-E84965EAAB56}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{E342DEB2-F009-47FD-85F6-E84965EAAB56}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {52F15A7F-671A-470D-91CE-F3B629B989B7}
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 20_Buzzword/csharp/Program.cs
    ================================================
    using System;
    
    namespace Buzzword
    {
        class Program
        {
            /// 
            /// Displays header.
            /// 
            static void Header()
            {
                Console.WriteLine("Buzzword generator".PadLeft(26));
                Console.WriteLine("Creating Computing Morristown, New Jersey".PadLeft(15));
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine();
            }
    
            // Information for the user about possible key input.
            static string keys = "type a 'Y' for another phrase or 'N' to quit";
    
            /// 
            /// Displays instructions.
            /// 
            static void Instructions()
            {
                Console.WriteLine("This program prints highly acceptable phrases in\n"
                + "'educator-speak' that you can work into reports\n"
                + "and speeches. Whenever a question mark is printed,\n"
                + $"{keys}.");
                Console.WriteLine();
                Console.WriteLine();
                Console.Write("Here's the first phrase:");
            }
    
            static string[] Words = new[]
                { "ability", "basal", "behavioral", "child-centered",
                "differentiated", "discovery", "flexible", "heterogenous",
                "homogeneous", "manipulative", "modular", "tavistock",
                "individualized", "learning", "evaluative", "objective",
                "cognitive", "enrichment", "scheduling", "humanistic",
                "integrated", "non-graded", "training", "vertical age",
                "motivational", "creative", "grouping", "modification",
                "accountability", "process", "core curriculum", "algorithm",
                "performance", "reinforcement", "open classroom", "resource",
                "structure", "facility", "environment" };
    
            /// 
            /// Capitalizes first letter of given string.
            /// 
            /// 
            /// string
            static string Capitalize(string input)
            {
                if (string.IsNullOrWhiteSpace(input))
                    return string.Empty;
    
                return char.ToUpper(input[0]) + input[1..];
            }
    
            // Seed has been calculated to get the same effect as in original,
            // at least in first phrase
            static readonly Random rnd = new Random(1486);
    
            /// 
            /// Generates random phrase from words available in Words array.
            /// 
            /// String representing random phrase where first letter is capitalized.
            static string GeneratePhrase()
            {
                // Indexing from 0, so had to decrease generated numbers
                return $"{Capitalize(Words[rnd.Next(13)])} "
                    + $"{Words[rnd.Next(13, 26)]} "
                    + $"{Words[rnd.Next(26, 39)]}";
            }
    
            /// 
            /// Handles user input. On wrong input it displays information about
            /// valid keys in infinite loop.
            /// 
            /// True if user pressed 'Y', false if 'N'.
            static bool Decision()
            {
                while (true)
                {
                    Console.Write("?");
                    var answer = Console.ReadKey();
                    if (answer.Key == ConsoleKey.Y)
                        return true;
                    else if (answer.Key == ConsoleKey.N)
                        return false;
                    else
                        Console.WriteLine($"\n{keys}");
                }
            }
    
            static void Main(string[] args)
            {
                Header();
                Instructions();
    
                while (true)
                {
                    Console.WriteLine();
                    Console.WriteLine(GeneratePhrase());
                    Console.WriteLine();
    
                    if (!Decision())
                        break;
                }
    
                Console.WriteLine("\nCome back when you need help with another report!");
            }
        }
    }
    
    
    ================================================
    FILE: 20_Buzzword/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 20_Buzzword/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 20_Buzzword/java/src/Buzzword.java
    ================================================
    import java.util.Scanner;
    
    public class Buzzword {
    
    	public static void main(final String[] args) {
    		try (
    			// Scanner is a Closeable so it must be closed
    			// before the program ends.
    			final Scanner scanner = new Scanner(System.in);
    		) {
    			final BuzzwordSupplier buzzwords = new BuzzwordSupplier();
    			final UserInterface userInterface = new UserInterface(
    					scanner, System.out, buzzwords);
    			userInterface.run();
    		}
    	}
    }
    
    
    ================================================
    FILE: 20_Buzzword/java/src/BuzzwordSupplier.java
    ================================================
    import java.util.Random;
    import java.util.function.Supplier;
    
    /**
     * A string supplier that provides an endless stream of random buzzwords.
     */
    public class BuzzwordSupplier implements Supplier {
    
    	private static final String[] SET_1 = {
    			"ABILITY","BASAL","BEHAVIORAL","CHILD-CENTERED",
    			"DIFFERENTIATED","DISCOVERY","FLEXIBLE","HETEROGENEOUS",
    			"HOMOGENEOUS","MANIPULATIVE","MODULAR","TAVISTOCK",
    			"INDIVIDUALIZED" };
    
    	private static final String[] SET_2 = {
    			"LEARNING","EVALUATIVE","OBJECTIVE",
    			"COGNITIVE","ENRICHMENT","SCHEDULING","HUMANISTIC",
    			"INTEGRATED","NON-GRADED","TRAINING","VERTICAL AGE",
    			"MOTIVATIONAL","CREATIVE" };
    
    	private static final String[] SET_3 = {
    			"GROUPING","MODIFICATION", "ACCOUNTABILITY","PROCESS",
    			"CORE CURRICULUM","ALGORITHM", "PERFORMANCE",
    			"REINFORCEMENT","OPEN CLASSROOM","RESOURCE", "STRUCTURE",
    			"FACILITY","ENVIRONMENT" };
    
    	private final Random random = new Random();
    
    	/**
    	 * Create a buzzword by concatenating a random word from each of the
    	 * three word sets.
    	 */
    	@Override
    	public String get() {
    		return SET_1[random.nextInt(SET_1.length)] + ' ' +
    				SET_2[random.nextInt(SET_2.length)] + ' ' +
    				SET_3[random.nextInt(SET_3.length)];
    	}
    }
    
    
    ================================================
    FILE: 20_Buzzword/java/src/UserInterface.java
    ================================================
    import java.io.PrintStream;
    import java.util.Scanner;
    import java.util.function.Supplier;
    
    /**
     * A command line user interface that outputs a buzzword every
     * time the user requests a new one.
     */
    public class UserInterface implements Runnable {
    
    	/**
    	 * Input from the user.
    	 */
    	private final Scanner input;
    
    	/**
    	 * Output to the user.
    	 */
    	private final PrintStream output;
    
    	/**
    	 * The buzzword generator.
    	 */
    	private final Supplier buzzwords;
    
    	/**
    	 * Create a new user interface.
    	 *
    	 * @param input The input scanner with which the user gives commands.
    	 * @param output The output to show messages to the user.
    	 * @param buzzwords The buzzword supplier.
    	 */
    	public UserInterface(final Scanner input,
    			final PrintStream output,
    			final Supplier buzzwords) {
    		this.input = input;
    		this.output = output;
    		this.buzzwords = buzzwords;
    	}
    
    	@Override
    	public void run() {
    		output.println("              BUZZWORD GENERATOR");
    		output.println("   CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
    		output.println();
    		output.println();
    		output.println();
    		output.println("THIS PROGRAM PRINTS HIGHLY ACCEPTABLE PHRASES IN");
    		output.println("'EDUCATOR-SPEAK' THAT YOU CAN WORK INTO REPORTS");
    		output.println("AND SPEECHES.  WHENEVER A QUESTION MARK IS PRINTED,");
    		output.println("TYPE A 'Y' FOR ANOTHER PHRASE OR 'N' TO QUIT.");
    		output.println();
    		output.println();
    		output.println("HERE'S THE FIRST PHRASE:");
    
    		do {
    			output.println(buzzwords.get());
    			output.println();
    			output.print("?");
    		} while ("Y".equals(input.nextLine().toUpperCase()));
    
    		output.println("COME BACK WHEN YOU NEED HELP WITH ANOTHER REPORT!");
    	}
    }
    
    
    ================================================
    FILE: 20_Buzzword/javascript/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells)
    
    
    ================================================
    FILE: 20_Buzzword/javascript/buzzword.html
    ================================================
    
    
    BUZZWORD
    
    
    
    
    
    
    
    
    
    
    ================================================
    FILE: 20_Buzzword/javascript/buzzword.js
    ================================================
    // BUZZWORD
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    var a = ["",
             "ABILITY","BASAL","BEHAVIORAL","CHILD-CENTERED",
             "DIFFERENTIATED","DISCOVERY","FLEXIBLE","HETEROGENEOUS",
             "HOMOGENEOUS","MANIPULATIVE","MODULAR","TAVISTOCK",
             "INDIVIDUALIZED","LEARNING","EVALUATIVE","OBJECTIVE",
             "COGNITIVE","ENRICHMENT","SCHEDULING","HUMANISTIC",
             "INTEGRATED","NON-GRADED","TRAINING","VERTICAL AGE",
             "MOTIVATIONAL","CREATIVE","GROUPING","MODIFICATION",
             "ACCOUNTABILITY","PROCESS","CORE CURRICULUM","ALGORITHM",
             "PERFORMANCE","REINFORCEMENT","OPEN CLASSROOM","RESOURCE",
             "STRUCTURE","FACILITY","ENVIRONMENT",
             ];
    
    // Main program
    async function main()
    {
        print(tab(26) + "BUZZWORD GENERATOR\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        print("THIS PROGRAM PRINTS HIGHLY ACCEPTABLE PHRASES IN\n");
        print("'EDUCATOR-SPEAK' THAT YOU CAN WORK INTO REPORTS\n");
        print("AND SPEECHES.  WHENEVER A QUESTION MARK IS PRINTED,\n");
        print("TYPE A 'Y' FOR ANOTHER PHRASE OR 'N' TO QUIT.\n");
        print("\n");
        print("\n");
        print("HERE'S THE FIRST PHRASE:\n");
        do {
            print(a[Math.floor(Math.random() * 13 + 1)] + " ");
            print(a[Math.floor(Math.random() * 13 + 14)] + " ");
            print(a[Math.floor(Math.random() * 13 + 27)] + "\n");
            print("\n");
            y = await input();
        } while (y == "Y") ;
        print("COME BACK WHEN YOU NEED HELP WITH ANOTHER REPORT!\n");
    }
    
    main();
    
    
    ================================================
    FILE: 20_Buzzword/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 20_Buzzword/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 20_Buzzword/lua/buzzword.lua
    ================================================
    --- Buzzword
    --- Ported by Brian Wilkins.
    --- Updated for modern buzzwords and corporate-speak
    
    print [[
                            BUZZWORD GENERATOR
                  CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY
    
    ]]
    
    print [[
    
    
    THIS PROGRAM PRINTS HIGHLY ACCEPTABLE PHRASES IN
    'EDUCATOR-SPEAK' THAT YOU CAN WORK INTO REPORTS
    AND SPEECHES.  WHENEVER A QUESTION MARK IS PRINTED,
    TYPE A 'Y' FOR ANOTHER PHRASE OR 'N' TO QUIT.
    ]]
    
    local phraseList = {"ABILITY","BASAL","BEHAVIORAL","CHILD-CENTERED",
                   "DIFFERENTIATED","DISCOVERY","FLEXIBLE","HETEROGENEOUS",
                   "HOMOGENEOUS","MANIPULATIVE","MODULAR","TAVISTOCK",
                   "INDIVIDUALIZED","LEARNING","EVALUATIVE","OBJECTIVE",
                   "COGNITIVE","ENRICHMENT","SCHEDULING","HUMANISTIC",
                   "INTEGRATED","NON-GRADED","TRAINING","VERTICAL AGE",
                   "MOTIVATIONAL","CREATIVE","GROUPING","MODIFICATION",
                   "ACCOUNTABILITY","PROCESS","CORE CURRICULUM","ALGORITHM",
                   "PERFORMANCE","REINFORCEMENT","OPEN CLASSROOM","RESOURCE",
                   "STRUCTURE","FACILITY","ENVIRONMENT"}
    
    --- Credit to https://stackoverflow.com/a/33468353/19232282
    --- for the pickPhrase function
    local copyPhraseList = {}
    
    function pickPhrase()
       local i
       -- make a copy of the original table if we ran out of phrases
       if #copyPhraseList == 0 then
          for k,v in pairs(phraseList) do
             copyPhraseList[k] = v
          end
       end
                   
       -- pick a random element from the copy  
       i = math.random(#copyPhraseList)
       phrase = copyPhraseList[i] 
                   
       -- remove phrase from copy
       table.remove(copyPhraseList, i)
                   
       return phrase
    end
    
    --- Reused from Bagels.lua
    function getInput(prompt)
        io.write(prompt)
        io.flush()
        local input = io.read("l") 
        if not input then  --- test for EOF
            print("COME BACK WHEN YOU NEED HELP WITH ANOTHER REPORT!")
            os.exit(0)
        end
        return input
    end
    
    for i=1,3,1 do
        ::phrasepick::
        io.write (pickPhrase() .. " ")
        io.write (pickPhrase() .. " ")
        io.write (pickPhrase())
        print()
        io.stdin:flush()
        local response = getInput("? ")
        if response:match("[yY].*") then
           goto phrasepick
        else
           print("COME BACK WHEN YOU NEED HELP WITH ANOTHER REPORT!")
           os.exit(0)
        end
    end
    
    
    
    ================================================
    FILE: 20_Buzzword/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 20_Buzzword/perl/buzzword.pl
    ================================================
    #!/usr/bin/perl
    use strict;
    
    print ' 'x26 . "BUZZWORD GENERATOR\n";
    print ' 'x15 . "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n";
    print "\n\n\n";
    
    print "THIS PROGRAM PRINTS HIGHLY ACCEPTABLE PHRASES IN\n";
    print "'EDUCATOR-SPEAK' THAT YOU CAN WORK INTO REPORTS\n";
    print "AND SPEECHES. WHENEVER A QUESTION MARK IS PRINTED,\n";
    print "TYPE A 'Y' FOR ANOTHER PHRASE OR 'N' TO QUIT.\n";
    print "\n\n"; print "HERE'S THE FIRST PHRASE; ";
    
    my @A= ("",
    	"ABILITY","BASAL","BEHAVIORAL","CHILD-CENTERED",
    	"DIFFERENTIATED","DISCOVERY","FLEXIBLE","HETEROGENEOUS",
    	"HOMOGENEOUS","MANIPULATIVE","MODULAR","TAVISTOCK",
    	"INDIVIDUALIZED","LEARNING","EVALUATIVE","OBJECTIVE",
    	"COGNITIVE","ENRICHMENT","SCHEDULING","HUMANISTIC",
    	"INTEGRATED","NON-GRADED","TRAINING","VERTICAL AGE",
    	"MOTIVATIONAL","CREATIVE","GROUPING","MODIFICATION",
    	"ACCOUNTABILITY","PROCESS","CORE CURRICULUM","ALGORITHM",
    	"PERFORMANCE","REINFORCEMENT","OPEN CLASSROOM","RESOURCE",
    	"STRUCTURE","FACILITY","ENVIRONMENT"
    	);
    
    my $Y;
    do {
    	print $A[int(13*rand(1)+1)]." ";
    	print $A[int(13*rand(1)+14)]." ";
    	print $A[int(13*rand(1)+27)]; print "\n";
    	print "? ";
    	chomp ($Y = );
    	} until ($Y ne "Y");
    
    print "COME BACK WHEN YOU NEED HELP WITH ANOTHER REPORT!\n";
    exit;
    
    
    ================================================
    FILE: 20_Buzzword/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 20_Buzzword/python/buzzword.py
    ================================================
    """
    Buzzword Generator
    
    From: BASIC Computer Games (1978)
          Edited by David H. Ahl
    
    "This program is an invaluable aid for preparing speeches and
     briefings about education technology.  This buzzword generator
     provides sets of three highly-acceptable words to work into your
     material.  Your audience will never know that the phrases don't
     really mean much of anything because they sound so great!  Full
     instructions for running are given in the program.
    
    "This version of Buzzword was written by David Ahl."
    
    
    Python port by Jeff Jetton, 2019
    """
    
    
    import random
    
    
    def main() -> None:
        words = [
            [
                "Ability",
                "Basal",
                "Behavioral",
                "Child-centered",
                "Differentiated",
                "Discovery",
                "Flexible",
                "Heterogeneous",
                "Homogenous",
                "Manipulative",
                "Modular",
                "Tavistock",
                "Individualized",
            ],
            [
                "learning",
                "evaluative",
                "objective",
                "cognitive",
                "enrichment",
                "scheduling",
                "humanistic",
                "integrated",
                "non-graded",
                "training",
                "vertical age",
                "motivational",
                "creative",
            ],
            [
                "grouping",
                "modification",
                "accountability",
                "process",
                "core curriculum",
                "algorithm",
                "performance",
                "reinforcement",
                "open classroom",
                "resource",
                "structure",
                "facility",
                "environment",
            ],
        ]
    
        # Display intro text
        print("\n           Buzzword Generator")
        print("Creative Computing  Morristown, New Jersey")
        print("\n\n")
        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.")
        print("\n\nHere's the first phrase:")
    
        still_running = True
        while still_running:
            phrase = ""
            for section in words:
                if len(phrase) > 0:
                    phrase += " "
                phrase += section[random.randint(0, len(section) - 1)]
    
            print(phrase)
            print()
    
            response = input("? ")
            try:
                if response.upper()[0] != "Y":
                    still_running = False
            except Exception:
                still_running = False
    
        print("Come back when you need help with another report!\n")
    
    
    if __name__ == "__main__":
        main()
    
    ######################################################################
    #
    # 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 accomodates 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!
    #
    #
    # Ideas for Modifications
    #
    #   Try adding more/different words.  Better yet, add a third
    #   dimnension to our WORDS tuple to add new sets of words that
    #   might pertain to different fields.  What would business buzzwords
    #   be? Engineering buzzwords?  Art/music buzzwords?  Let the user
    #   choose a field and pick the buzzwords accordingly.
    #
    ######################################################################
    
    
    ================================================
    FILE: 20_Buzzword/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 20_Buzzword/ruby/buzzword.rb
    ================================================
    ######################################################################
    #
    # Buzzword Generator
    #
    # From: BASIC Computer Games (1978)
    #       Edited by David H. Ahl
    #
    # "This program is an invaluable aid for preparing speeches and
    #  briefings about education technology.  This buzzword generator
    #  provides sets of three highly-acceptable words to work into your
    #  material.  Your audience will never know that the phrases don't
    #  really mean much of anything because they sound so great!  Full
    #  instructions for running are given in the program.
    #
    # "This version of Buzzword was written by David Ahl."
    #
    #
    # Ruby port by Leslie Viljoen, 2021
    #
    ######################################################################
    
    WORDS = [["Ability", "Basal", "Behavioral", "Child-centered",
               "Differentiated", "Discovery", "Flexible", "Heterogeneous",
               "Homogenous", "Manipulative", "Modular", "Tavistock",
               "Individualized"],
    
              ["learning", "evaluative", "objective", "cognitive",
               "enrichment", "scheduling", "humanistic", "integrated",
               "non-graded", "training", "vertical age", "motivational",
               "creative"] ,
    
              ["grouping", "modification", "accountability", "process",
               "core curriculum", "algorithm", "performance",
               "reinforcement", "open classroom", "resource", "structure",
               "facility","environment"]]
    
    
    # Display intro text
    
    puts "\n           Buzzword Generator"
    puts "Creative Computing  Morristown, New Jersey"
    puts "\n\n"
    puts "This program prints highly acceptable phrases in"
    puts "'educator-speak' that you can work into reports"
    puts "and speeches.  Whenever a question mark is printed,"
    puts "type a 'Y' for another phrase or 'N' to quit."
    puts "\n\nHere's the first phrase:"
    
    loop do
        phrase = []
    
        prefix, body, postfix = WORDS
    
        phrase << prefix[rand(prefix.length)]
        phrase << body[rand(body.length)]
        phrase << postfix[rand(postfix.length)]
    
        puts phrase.join(' ')
        puts "\n"
    
        print "?"
        response = gets
    
        break unless response.upcase.start_with?('Y')
    end
    
    puts "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).
    #
    #   Instead, we're storing the words for each section in three
    #   separate arrays. That makes it easy to loop through the sections
    #   to stitch the phrase together, and it easily accomodates 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!
    #
    #
    # Ideas for Modifications
    #
    #   Try adding more/different words. Better yet, add a third
    #   array to the WORDS array to add new sets of words that
    #   might pertain to different fields. What would business buzzwords
    #   be? Engineering buzzwords?  Art/music buzzwords?  Let the user
    #   choose a field and pick the buzzwords accordingly.
    #
    ######################################################################
    
    
    ================================================
    FILE: 20_Buzzword/rust/Cargo.toml
    ================================================
    [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"
    
    
    ================================================
    FILE: 20_Buzzword/rust/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Rust](https://www.rust-lang.org/)
    
    
    ================================================
    FILE: 20_Buzzword/rust/src/main.rs
    ================================================
    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!
    //
    /////////////////////////////////////////////////////////////////////////
    
    
    ================================================
    FILE: 20_Buzzword/vbnet/Buzzword.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Buzzword", "Buzzword.vbproj", "{B2BD53F2-82C3-4729-BA82-DB96E18F8666}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{B2BD53F2-82C3-4729-BA82-DB96E18F8666}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{B2BD53F2-82C3-4729-BA82-DB96E18F8666}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{B2BD53F2-82C3-4729-BA82-DB96E18F8666}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{B2BD53F2-82C3-4729-BA82-DB96E18F8666}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 20_Buzzword/vbnet/Buzzword.vbproj
    ================================================
    
      
        Exe
        Buzzword
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 20_Buzzword/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 21_Calendar/README.md
    ================================================
    ### Calendar
    
    This program prints out a calendar for any year. You must specify the starting day of the week of the year:
    - 0: Sunday
    - -1: Monday
    - -2: Tuesday
    - -3: Wednesday
    - -4: Thursday
    - -5: Friday
    - -6: Saturday
    
    You can determine this by using the program WEEKDAY. You must also make two changes for leap years. The program listing describes the necessary changes. Running the program produces a nice 12-month calendar.
    
    The program was written by Geoffrey Chase of the Abbey, Portsmouth, Rhode Island.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=37)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=52)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    - 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.
    
    
    ================================================
    FILE: 21_Calendar/calendar.bas
    ================================================
    10 PRINT TAB(32);"CALENDAR"
    20 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    30 PRINT:PRINT:PRINT
    100 REM     VALUES FOR 1979 - SEE NOTES
    110 DIM M(12)
    120 FOR I=1 TO 6: PRINT CHR$(10);: NEXT I
    130 D=-1: REM 1979 STARTS ON MONDAY (0=SUN, -1=MON, -2=TUES...)
    140 S=0
    150 REM     READ DAYS OF EACH MONTH
    160 FOR N=0 TO 12: READ M(N): NEXT N
    170 REM
    180 FOR N=1 TO 12
    190 PRINT: PRINT: S=S+M(N-1)
    200 PRINT "**";S;TAB(7);
    210 FOR I=1 TO 18: PRINT "*";: NEXT I
    220 ON N GOTO 230,240,250,260,270,280,290,300,310,320,330,340
    230 PRINT " JANUARY ";: GOTO 350
    240 PRINT " FEBRUARY";: GOTO 350
    250 PRINT "  MARCH  ";: GOTO 350
    260 PRINT "  APRIL  ";: GOTO 350
    270 PRINT "   MAY   ";: GOTO 350
    280 PRINT "   JUNE  ";: GOTO 350
    290 PRINT "   JULY  ";: GOTO 350
    300 PRINT "  AUGUST ";: GOTO 350
    310 PRINT "SEPTEMBER";: GOTO 350
    320 PRINT " OCTOBER ";: GOTO 350
    330 PRINT " NOVEMBER";: GOTO 350
    340 PRINT " DECEMBER";
    350 FOR I=1 TO 18: PRINT "*";: NEXT I
    360 PRINT 365-S;"**";
    370 REM   366-S;     ON LEAP YEARS
    380 PRINT CHR$(10): PRINT "     S       M       T       W";
    390 PRINT "       T       F       S"
    400 PRINT
    410 FOR I=1 TO 59: PRINT "*";: NEXT I
    420 REM
    430 FOR W=1 TO 6
    440 PRINT CHR$(10)
    450 PRINT TAB(4)
    460 REM
    470 FOR G=1 TO 7
    480 D=D+1
    490 D2=D-S
    500 IF D2>M(N) THEN 580
    510 IF D2>0 THEN PRINT D2;
    520 PRINT TAB(4+8*G);
    530 NEXT G
    540 REM
    550 IF D2=M(N) THEN 590
    560 NEXT W
    570 REM
    580 D=D-G
    590 NEXT N
    600 REM
    610 FOR I=1 TO 6: PRINT CHR$(10);: NEXT I
    620 DATA 0,31,28,31,30,31,30,31,31,30,31,30,31
    630 REM  0,31,29,  ..., ON LEAP YEARS
    640 END
    
    
    ================================================
    FILE: 21_Calendar/csharp/Calendar.csproj
    ================================================
    
    
      
        Exe
        net5.0
      
    
    
    
    
    ================================================
    FILE: 21_Calendar/csharp/Calendar.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.31613.86
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Calendar", "Calendar.csproj", "{1EADFB6C-4496-42C2-A80F-84FEC3F061D1}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{1EADFB6C-4496-42C2-A80F-84FEC3F061D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{1EADFB6C-4496-42C2-A80F-84FEC3F061D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{1EADFB6C-4496-42C2-A80F-84FEC3F061D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{1EADFB6C-4496-42C2-A80F-84FEC3F061D1}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {1B423C35-492F-4AF8-97FC-BEC69B44EC8D}
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 21_Calendar/csharp/Program.cs
    ================================================
    using System;
    
    /*
     21_Calendar in C# for basic-computer-games
     Converted by luminoso-256
    */
    
    namespace _21_calendar
    {
        class Program
        {
            //basic has a TAB function. We do not by default, so we make our own!
            static string Tab(int numspaces)
            {
                string space = "";
                //loop as many times as there are spaces specified, and add a space each time
                while (numspaces > 0)
                {
                    //add the space
                    space += " ";
                    //decrement the loop variable so we don't keep going forever!
                    numspaces--;
                }
                return space;
            }
    
            static void Main(string[] args)
            {
                // print the "title" of our program
                // the usage of Write*Line* means we do not have to specify a newline (\n)
                Console.WriteLine(Tab(32) + "CALENDAR");
                Console.WriteLine(Tab(15) + "CREATE COMPUTING  MORRISTOWN, NEW JERSEY");
                //give us some space.
                Console.WriteLine("");
                Console.WriteLine("");
                Console.WriteLine("");
    
                //establish some variables needed to print out a calculator
    
                //the length of each month in days. On a leap year, the start of this would be
                // 0, 31, 29 to account for Feb. the 0 at the start is for days elapsed to work right in Jan.
                int[] monthLengths = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; // m in original source
    
                //the starting day of the month. in 1979 this was monday
                // 0 = sun, -1 = mon, -2 = tue, -3 = wed, etc.
                int day = -1; // called d in original source
    
                //how much time in the year has gone by?
                int elapsed = 0; // called s in original source
    
                //loop through printing all the months.
                for (int month = 1; month <= 12; month++) //month is called n in original source
                {
                    //pad some space
                    Console.WriteLine("");
                    Console.WriteLine("");
                    //increment days elapsed
                    elapsed += monthLengths[month - 1];
                    //build our header for this month of the calendar
                    string header = "** " + elapsed;
                    //add padding as needed
                    while (header.Length < 7)
                    {
                        header += " ";
                    }
                    for (int i = 1; i <= 18; i++)
                    {
                        header += "*";
                    }
                    //determine what month it is, add text accordingly
                    switch (month) {
                        case 1: header += " JANUARY "; break;
                        case 2: header += " FEBRUARY"; break;
                        case 3: header += "  MARCH  "; break;
                        case 4: header += "  APRIL  "; break;
                        case 5: header += "   MAY   "; break;
                        case 6: header += "   JUNE  "; break;
                        case 7: header += "   JULY  "; break;
                        case 8: header += "  AUGUST "; break;
                        case 9: header += "SEPTEMBER"; break;
                        case 10: header += " OCTOBER "; break;
                        case 11: header += " NOVEMBER"; break;
                        case 12: header += " DECEMBER"; break;
                    }
                    //more padding
                    for (int i = 1; i <= 18; i++)
                    {
                        header += "*";
                    }
                    header += "  ";
                    // how many days left till the year's over?
                    header += (365 - elapsed) + " **"; // on leap years 366
                    Console.WriteLine(header);
                    //dates
                    Console.WriteLine("     S       M       T       W       T       F       S");
                    Console.WriteLine(" ");
    
                    string weekOutput = "";
                    for (int i = 1; i <= 59; i++)
                    {
                        weekOutput += "*";
                    }
                    //init some vars ahead of time
                    int g = 0;
                    int d2 = 0;
                    //go through the weeks and days
                    for (int week = 1; week <= 6; week++)
                    {
                        Console.WriteLine(weekOutput);
                        weekOutput = "    ";
                        for (g = 1; g <= 7; g++)
                        {
                            //add one to the day
                            day++;
                            d2 = day - elapsed;
                            //check if we're done with this month
                            if (d2 > monthLengths[month])
                            {
                                week = 6;
                                break;
                            }
                            //should we print this day?
                            if (d2 > 0)
                            {
                                weekOutput += d2;
                            }
                            //padding
                            while (weekOutput.Length < 4 + 8 * g)
                            {
                                weekOutput += " ";
                            }
                        }
                        if (d2 == monthLengths[month])
                        {
                            day += g;
                            break;
                        }
                    }
                    day -= g;
                    Console.WriteLine(weekOutput);
                }
            }
        }
    }
    
    
    ================================================
    FILE: 21_Calendar/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 21_Calendar/java/Calendar.java
    ================================================
    /**
     * Game of Calendar
     * 

    * Based on the BASIC game of Calendar here * https://github.com/coding-horror/basic-computer-games/blob/main/21%20Calendar/calendar.bas *

    * Note: The idea was to create a version of the 1970's BASIC game in Java, without introducing * new features - no additional text, error checking, etc has been added. * * Converted from BASIC to Java by Darren Cardenas. */ public class Calendar { private static final int NUM_WEEK_ROWS = 6; private static final int NUM_DAYS_PER_WEEK = 7; private static final int NUM_MONTHS_PER_YEAR = 12; private static final int[] daysPerMonth = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; public void play() { showIntro(); startGame(); } // End of method play private static void showIntro() { System.out.println(" ".repeat(31) + "CALENDAR"); System.out.println(" ".repeat(14) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println("\n\n"); } // End of method showIntro private void startGame() { int dayOfMonth = 0; int dayOfWeek = 0; int dayOfYear = 0; int daysTotal = 0; int index = 0; int month = 0; int row = 0; String lineContent = ""; for (index = 1; index <= 6; index++) { System.out.println(""); } daysTotal = -1; dayOfYear = 0; System.out.println(""); // Begin loop through all months for (month = 1; month <= NUM_MONTHS_PER_YEAR; month++) { System.out.println(""); dayOfYear = dayOfYear + daysPerMonth[month - 1]; lineContent = String.format("** %-3d" + "*".repeat(18), dayOfYear); switch (month) { case 1: lineContent += " JANUARY "; break; case 2: lineContent += " FEBRUARY"; break; case 3: lineContent += " MARCH "; break; case 4: lineContent += " APRIL "; break; case 5: lineContent += " MAY "; break; case 6: lineContent += " JUNE "; break; case 7: lineContent += " JULY "; break; case 8: lineContent += " AUGUST "; break; case 9: lineContent += "SEPTEMBER"; break; case 10: lineContent += " OCTOBER "; break; case 11: lineContent += " NOVEMBER"; break; case 12: lineContent += " DECEMBER"; break; default: break; } lineContent += "*".repeat(18) + " " + (365 - dayOfYear) + "**"; System.out.println(lineContent); System.out.println(""); System.out.print(" S M T W"); System.out.println(" T F S"); System.out.println(""); System.out.println("*".repeat(59)); // Begin loop through each week row for (row = 1; row <= NUM_WEEK_ROWS; row++) { System.out.println(""); lineContent = " "; // Begin loop through days of the week for (dayOfWeek = 1; dayOfWeek <= NUM_DAYS_PER_WEEK; dayOfWeek++) { daysTotal++; dayOfMonth = daysTotal - dayOfYear; if (dayOfMonth > daysPerMonth[month]) { row = 6; break; } if (dayOfMonth > 0) { lineContent += dayOfMonth; } while (lineContent.length() < (4 + 8 * dayOfWeek)) { lineContent += " "; } } // End loop through days of the week if (dayOfMonth == daysPerMonth[month]) { row = 6; daysTotal += dayOfWeek; System.out.println(lineContent); break; } System.out.println(lineContent); } // End loop through each week row daysTotal -= dayOfWeek; } // End loop through all months for (index = 1; index <= 6; index++) { System.out.println(""); } } // End of method startGame public static void main(String[] args) { Calendar game = new Calendar(); game.play(); } // End of method main } // End of class Calendar ================================================ FILE: 21_Calendar/java/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Oracle Java](https://openjdk.java.net/) ================================================ FILE: 21_Calendar/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 21_Calendar/javascript/calendar.html ================================================ CALENDAR

    
    
    
    
    
    
    ================================================
    FILE: 21_Calendar/javascript/calendar.js
    ================================================
    // CALENDAR
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    function print(str)
    {
    	document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function tab(space)
    {
    	var str = "";
    	while (space-- > 0)
    		str += " ";
    	return str;
    }
    
    print(tab(32) + "CALENDAR\n");
    print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
    print("\n");
    print("\n");
    print("\n");
    
    //       0, 31, 29  ON LEAP YEARS
    var m = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
    
    // VALUES FOR 1979 - SEE NOTES
    for (i = 1; i <= 6; i++)
    	print("\n");
    
    d = -1;	// 1979 starts on Monday (0 = Sun, -1 = Monday, -2 = Tuesday)
    s = 0;
    
    for (n = 1; n <= 12; n++) {
    	print("\n");
    	print("\n");
    	s = s + m[n - 1];
    	str = "**" + s;
    	while (str.length < 7)
    		str += " ";
    	for (i = 1; i <= 18; i++)
    		str += "*";
    	switch (n) {
    		case  1:	str += " JANUARY "; break;
    		case  2:	str += " FEBRUARY"; break;
    		case  3:	str += "  MARCH  "; break;
    		case  4:	str += "  APRIL  "; break;
    		case  5:	str += "   MAY   "; break;
    		case  6:	str += "   JUNE  "; break;
    		case  7:	str += "   JULY  "; break;
    		case  8:	str += "  AUGUST "; break;
    		case  9:	str += "SEPTEMBER"; break;
    		case 10:	str += " OCTOBER "; break;
    		case 11:	str += " NOVEMBER"; break;
    		case 12:	str += " DECEMBER"; break;
    	}
    	for (i = 1; i <= 18; i++)
    		str += "*";
    	str += (365 - s) + "**";
    	     // 366 - s on leap years
    	print(str + "\n");
    	print("     S       M       T       W       T       F       S\n");
    	print("\n");
    	str = "";
    	for (i = 1; i <= 59; i++)
    		str += "*";
    	for (week = 1; week <= 6; week++) {
    		print(str + "\n");
    		str = "    ";
    		for (g = 1; g <= 7; g++) {
    			d++;
    			d2 = d - s;
    			if (d2 > m[n]) {
    				week = 6;
    				break;
    			}
    			if (d2 > 0)
    				str += d2;
    			while (str.length < 4 + 8 * g)
    				str += " ";
    		}
    		if (d2 == m[n]) {
    			d += g;
    			break;
    		}
    	}
    	d -= g;
    	print(str + "\n");
    }
    
    
    ================================================
    FILE: 21_Calendar/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 21_Calendar/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 21_Calendar/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    Actually, this is not so much a port as a complete rewrite, making use of
    Perl's Posix time functionality. The calendar is for the current year (not
    1979), but you can get another year by specifying it on the command line, e.g.
    
     `perl 21_Calendar/perl/calendar.pl 2001`
    
    It *may* even produce output in languages other than English. But the
    leftmost column will still be Sunday, even in locales where it is
    typically Monday.
    
    
    ================================================
    FILE: 21_Calendar/perl/calendar.pl
    ================================================
    #!/usr/bin/env perl
    
    use 5.010;      # To get 'state' and 'say'
    
    use strict;     # Require explicit declaration of variables
    use warnings;   # Enable optional compiler warnings
    
    use English;    # Use more friendly names for Perl's magic variables
    use POSIX qw{ strftime };
    use Term::ReadLine;     # Prompt and return user input
    use Time::Local ();
    
    BEGIN {
        *time_gm =
            Time::Local->can( 'timegm_modern' ) ||
            Time::Local->can( 'timegm' );
    }
    
    our $VERSION = '0.000_01';
    
    use constant COLUMN_WIDTH       => 6;
    use constant SECONDS_PER_DAY    => 86400;
    
    binmode STDOUT, ':encoding(utf-8)';
    
    my $year = @ARGV ? $ARGV[0] : ( localtime )[5] + 1900;
    my $is_leap_year = is_leap_year( $year );
    my $year_len = 365 + $is_leap_year;
    print <<'EOD';
                                    CALENDAR
                   Creative Computing  Morristown, New Jersey
    
    
    EOD
    
    my @mon_len = ( 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 );
    $mon_len[1] += $is_leap_year;
    
    foreach my $month ( 0 .. 11 ) {
        my $epoch = time_gm( 0, 0, 0, 1, $month, $year );
        my @start_time = gmtime( $epoch );
        my ( $week_day, $year_day ) = @start_time[ 6, 7 ];
        my $label = strftime( '%B %Y', @start_time );
        $label .= ' ' x ( ( 14 - length $label ) / 2 );
        printf "\n** %3d ****** %14s ****** %3d **\n",
        $year_day, $label, $year_len - $year_day;
        {
            my $day = 1 + ( 7 - $week_day ) % 7;
            foreach my $wd ( 0 .. 6 ) {
                my $ep = time_gm( 0, 0, 0, $day + $wd, $month, $year );
                printf '%*s', COLUMN_WIDTH, strftime( '%a', gmtime $ep );
            }
            print "\n";
        }
        say '*' x ( COLUMN_WIDTH * 7 );
        print ' ' x ( COLUMN_WIDTH * $week_day );
        my $month_day = 1;
        while ( $week_day < 7 ) {
            printf '%*d', COLUMN_WIDTH, $month_day++;
            $week_day++;
        }
        print "\n";
        $week_day = 0;
        while ( $month_day <= $mon_len[$month] ) {
            printf '%*d', COLUMN_WIDTH, $month_day++;
            $week_day++;
            unless ( $week_day % 7 ) {
                print "\n";
                $week_day = 0;
            }
        }
        print "\n" if $week_day;
    
    }
    
    sub is_leap_year {
        my ( $year ) = 1;
        return 0 if $year % 4;
        return 1 if $year % 100;
        return 0 if $year % 400;
        return 1;
    }
    
    __END__
    
    =head1 TITLE
    
    calendar - Play the game 'Calendar' from Basic Computer Games
    
    =head1 SYNOPSIS
    
     calendar.pl
    
    =head1 DETAILS
    
    This Perl script is a port of calendar, which is the 21st
    entry in Basic Computer Games.
    
    Actually, it is not so much a port as a complete rewrite, making use of
    Perl's Posix time functionality. The calendar is for the current year
    (not 1979), but you can get another year by specifying it on the command
    line, e.g.
    
     perl 21_Calendar/perl/calendar.pl 2001
    
    It B even produce output in languages other than English. But the
    leftmost column will still be Sunday, even in locales where it is
    typically Monday.
    
    =head1 PORTED BY
    
    Thomas R. Wyant, III F
    
    =head1 COPYRIGHT AND LICENSE
    
    Copyright (C) 2022 by Thomas R. Wyant, III
    
    This program is free software; you can redistribute it and/or modify it
    under the same terms as Perl 5.10.0. For more details, see the Artistic
    License 1.0 at
    L, and/or the
    Gnu GPL at L.
    
    This program is distributed in the hope that it will be useful, but
    without any warranty; without even the implied warranty of
    merchantability or fitness for a particular purpose.
    
    =cut
    
    # ex: set expandtab tabstop=4 textwidth=72 :
    
    
    ================================================
    FILE: 21_Calendar/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 21_Calendar/python/calendar.py
    ================================================
    """
    Calendar
    
    From: BASIC Computer Games (1978)
          Edited by David Ahl#
    
       This program prints out a calendar
    for any year. You must specify the
    starting day of the week of the year in
    statement 130. (Sunday(0), Monday
    (-1), Tuesday(-2), etc.) You can determine
    this by using the program WEEKDAY.
    You must also make two changes
    for leap years in statement 360 and 620.
    The program listing describes the necessary
    changes. Running the program produces a
    nice 12-month calendar.
       The program was written by Geofrey
    Chase of the Abbey, Portsmouth, Rhode Island.
    """
    
    from typing import Tuple
    
    
    def parse_input() -> Tuple[int, bool]:
        """
        function to parse input for weekday and leap year boolean
        """
    
        days_mapping = {
            "sunday": 0,
            "monday": -1,
            "tuesday": -2,
            "wednesday": -3,
            "thursday": -4,
            "friday": -5,
            "saturday": -6,
        }
    
        day = 0
        leap_day = False
    
        correct_day_input = False
        while not correct_day_input:
            weekday = input("INSERT THE STARTING DAY OF THE WEEK OF THE YEAR:")
    
            for day_k in days_mapping:
                if weekday.lower() in day_k:
                    day = days_mapping[day_k]
                    correct_day_input = True
                    break
    
        while True:
            leap = input("IS IT A LEAP YEAR?:")
    
            if "y" in leap.lower():
                leap_day = True
                break
    
            if "n" in leap.lower():
                leap_day = False
                break
    
        return day, leap_day
    
    
    def calendar(weekday: int, leap_year: bool) -> None:
        """
        function to print a year's calendar.
    
        input:
            _weekday_: int - the initial day of the week (0=SUN, -1=MON, -2=TUES...)
            _leap_year_: bool - indicates if the year is a leap year
        """
        months_days = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
        days = "S        M        T        W        T        F        S\n"
        sep = "*" * 59
        years_day = 365
        d = weekday
    
        if leap_year:
            months_days[2] = 29
            years_day = 366
    
        months_names = [
            " JANUARY ",
            " FEBRUARY",
            "  MARCH  ",
            "  APRIL  ",
            "   MAY   ",
            "   JUNE  ",
            "   JULY  ",
            "  AUGUST ",
            "SEPTEMBER",
            " OCTOBER ",
            " NOVEMBER",
            " DECEMBER",
        ]
    
        days_count = 0  # S in the original program
    
        # main loop
        for n in range(1, 13):
            days_count += months_days[n - 1]
            print(
                f"** {days_count} ****************** {months_names[n - 1]} "
                f"****************** {years_day - days_count} **\n"
            )
            print(days)
            print(sep)
    
            for _ in range(1, 7):
                print("\n")
                for g in range(1, 8):  # noqa
                    d += 1
                    d2 = d - days_count
    
                    if d2 > months_days[n]:
                        break
    
                    if d2 <= 0:
                        print("  ", end="       ")
                    elif d2 < 10:
                        print(f" {d2}", end="       ")
                    else:
                        print(f"{d2}", end="       ")
                print()
    
                if d2 >= months_days[n]:
                    break
    
            if d2 > months_days[n]:
                d -= g
    
            print("\n")
    
        print("\n")
    
    
    def main() -> None:
        print(" " * 32 + "CALENDAR")
        print(" " * 15 + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY")
        print("\n" * 11)
    
        day, leap_year = parse_input()
        calendar(day, leap_year)
    
    
    if __name__ == "__main__":
        main()
    
    ########################################################
    #
    # Porting notes:
    #
    # It has been added an input at the beginning of the
    # program so the user can specify the first day of the
    # week of the year and if the year is leap or not.
    #
    ########################################################
    
    
    ================================================
    FILE: 21_Calendar/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 21_Calendar/ruby/calendar.rb
    ================================================
    class Calendar
      def parse_input
        days_mapping = {
          "sunday": 0,
          "monday": -1,
          "tuesday": -2,
          "wednesday": -3,
          "thursday": -4,
          "friday": -5,
          "saturday": -6,
        }
    
        day = 0
        leap_day = false
        correct_day_input = false
    
        while !correct_day_input 
          print "INSERT THE STARTING DAY OF THE WEEK OF THE YEAR: "
          weekday = gets.chomp!
    
          days_mapping.each do |k, v|
            if k.to_s == weekday.downcase.to_s
              day = v
              correct_day_input = true
              break
            end
          end
        end
    
        while true
          print "IS IT A LEAP YEAR?: "
          leap = gets.chomp!
          if "y" == leap.downcase.to_s
            leap_day = true
            break
          end
    
          if "n" == leap.downcase.to_s
            leap_day = false
            break
          end
        end
    
        return day, leap_day
      end
    
      def start(weekday, leap_year)
        months_days = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
        days = "S        M        T        W        T        F        S\n"
        sep = "*" * 59
        years_day = 365
        d = weekday
    
        if leap_year
          months_days[2] = 29
          years_day = 366
        end
    
        months_names = [
          " JANUARY ",
          " FEBRUARY",
          "  MARCH  ",
          "  APRIL  ",
          "   MAY   ",
          "   JUNE  ",
          "   JULY  ",
          "  AUGUST ",
          "SEPTEMBER",
          " OCTOBER ",
          " NOVEMBER",
          " DECEMBER",
        ]
    
        days_count = 0
    
        for n in (1..12) do
          days_count += months_days[n - 1]
          print "** #{days_count} ****************** #{months_names[n - 1]} ****************** #{years_day - days_count} **\n"
          print days
          print sep
          for nnn in (1..6) do
            print "\n"
            for g in (1..7) do
              d += 1
              d2 = d - days_count
    
              break if d2 > months_days[n]
    
              if d2 <= 0
                print "         "
              elsif d2 < 10
                print " #{d2}       "
              else
                print "#{d2}       "
              end
            end
    
            print "\n\n"
    
            break if d2 >= months_days[n]
          end
          d -= g if d2 > months_days[n]
          print "\n"
        end
        print "\n"
      end
    end
    
    if __FILE__ == $0
      calendar = Calendar.new
      input = calendar.parse_input
      calendar.start(input[0], input[1])
    end
    
    ================================================
    FILE: 21_Calendar/rust/Cargo.toml
    ================================================
    [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]
    
    
    ================================================
    FILE: 21_Calendar/rust/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Rust](https://www.rust-lang.org/) by [Uğur Küpeli](https://github.com/ugurkupeli)
    
    ================================================
    FILE: 21_Calendar/rust/src/main.rs
    ================================================
    use std::io::stdin;
    
    const WIDTH: usize = 64;
    const DAYS_WIDTH: usize = WIDTH / 8;
    const MONTH_WIDTH: usize = WIDTH - (DAYS_WIDTH * 2);
    const DAY_NUMS_WIDTH: usize = WIDTH / 7;
    
    const DAYS: [&str; 7] = [
        "SUNDAY",
        "MONDAY",
        "TUESDAY",
        "WEDNESDAY",
        "THURSDAY",
        "FRIDAY",
        "SATURDAY",
    ];
    
    fn main() {
        println!("\n\t\t CALENDAR");
        println!("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n");
    
        let (starting_day, leap_year) = prompt();
        let (months, total_days) = get_months_and_days(leap_year);
    
        let mut days_passed = 0;
        let mut current_day_index = DAYS.iter().position(|d| *d == starting_day).unwrap();
    
        for (month, days) in months {
            print_header(month, days_passed, total_days - days_passed);
            print_days(&mut current_day_index, days);
            days_passed += days as u16;
            println!("\n");
        }
    }
    
    fn prompt() -> (String, bool) {
        let mut day = String::new();
    
        loop {
            println!("\nFirst day of the year?");
            if let Ok(_) = stdin().read_line(&mut day) {
                day = day.trim().to_uppercase();
                if DAYS.contains(&day.as_str()) {
                    break;
                } else {
                    day.clear();
                }
            }
        }
    
        let mut leap = false;
    
        loop {
            println!("Is this a leap year?");
            let mut input = String::new();
            if let Ok(_) = stdin().read_line(&mut input) {
                match input.to_uppercase().trim() {
                    "Y" | "YES" => {
                        leap = true;
                        break;
                    }
                    "N" | "NO" => break,
                    _ => (),
                }
            }
        }
    
        println!();
        (day, leap)
    }
    
    fn get_months_and_days(leap_year: bool) -> (Vec<(String, u8)>, u16) {
        let months = [
            "JANUARY",
            "FEBUARY",
            "MARCH",
            "APRIL",
            "MAY",
            "JUNE",
            "JULY",
            "AUGUST",
            "SEPTEMBER",
            "OCTOBER",
            "NOVEMBER",
            "DECEMBER",
        ];
    
        let mut months_with_days = Vec::new();
        let mut total_days: u16 = 0;
    
        for (i, month) in months.iter().enumerate() {
            let days = if i == 1 {
                if leap_year {
                    29u8
                } else {
                    28
                }
            } else if if i < 7 { (i % 2) == 0 } else { (i % 2) != 0 } {
                31
            } else {
                30
            };
    
            total_days += days as u16;
            months_with_days.push((month.to_string(), days));
        }
    
        (months_with_days, total_days)
    }
    
    fn print_between(s: String, w: usize, star: bool) {
        let s = format!(" {s} ");
        if star {
            print!("{:*^w$}", s);
            return;
        }
        print!("{:^w$}", s);
    }
    
    fn print_header(month: String, days_passed: u16, days_left: u16) {
        print_between(days_passed.to_string(), DAYS_WIDTH, true);
        print_between(month.to_string(), MONTH_WIDTH, true);
        print_between(days_left.to_string(), DAYS_WIDTH, true);
        println!();
    
        for d in DAYS {
            let d = d.chars().nth(0).unwrap();
            print_between(d.to_string(), DAY_NUMS_WIDTH, false);
        }
        println!();
    
        println!("{:*>WIDTH$}", "");
    }
    
    fn print_days(current_day_index: &mut usize, days: u8) {
        let mut current_date = 1u8;
    
        print!("{:>w$}", " ", w = DAY_NUMS_WIDTH * *current_day_index);
    
        for _ in 1..=days {
            print_between(current_date.to_string(), DAY_NUMS_WIDTH, false);
    
            if ((*current_day_index + 1) % 7) == 0 {
                *current_day_index = 0;
                println!();
            } else {
                *current_day_index += 1;
            }
    
            current_date += 1;
        }
    }
    
    
    ================================================
    FILE: 21_Calendar/vbnet/Calendar.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Calendar", "Calendar.vbproj", "{00B21257-3E25-4150-AA6D-266350688663}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{00B21257-3E25-4150-AA6D-266350688663}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{00B21257-3E25-4150-AA6D-266350688663}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{00B21257-3E25-4150-AA6D-266350688663}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{00B21257-3E25-4150-AA6D-266350688663}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 21_Calendar/vbnet/Calendar.vbproj
    ================================================
    
      
        Exe
        Calendar
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 21_Calendar/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 22_Change/README.md
    ================================================
    ### Change
    
    In this program, the computer pretends it is the cashier at your friendly neighborhood candy store. You tell it the cost of the item(s) you are buying, the amount of your payment, and it will automatically (!) determine your correct change. Aren’t machines wonderful? Dennis Lunder of People’s Computer Company wrote this program.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=39)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=54)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 22_Change/change.bas
    ================================================
    2 PRINT TAB(33);"CHANGE"
    4 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    5 PRINT:PRINT:PRINT
    6 PRINT "I, YOUR FRIENDLY MICROCOMPUTER, WILL DETERMINE"
    8 PRINT "THE CORRECT CHANGE FOR ITEMS COSTING UP TO $100."
    9 PRINT:PRINT
    10 PRINT "COST OF ITEM";:INPUT A:PRINT "AMOUNT OF PAYMENT";:INPUT P
    20 C=P-A:M=C:IF C<>0 THEN 90
    25 PRINT "CORRECT AMOUNT, THANK YOU."
    30 GOTO 400
    90 IF C>0 THEN 120
    95 PRINT "SORRY, YOU HAVE SHORT-CHANGED ME $";A-P
    100 GOTO 10
    120 PRINT "YOUR CHANGE, $";C
    130 D=INT(C/10)
    140 IF D=0 THEN 155
    150 PRINT D;"TEN DOLLAR BILL(S)"
    155 C=M-(D*10)
    160 E=INT(C/5)
    170 IF E=0 THEN 185
    180 PRINT E;"FIVE DOLLARS BILL(S)"
    185 C=M-(D*10+E*5)
    190 F=INT(C)
    200 IF F=0 THEN 215
    210 PRINT F;"ONE DOLLAR BILL(S)"
    215 C=M-(D*10+E*5+F)
    220 C=C*100
    225 N=C
    230 G=INT(C/50)
    240 IF G=0 THEN 255
    250 PRINT G;"ONE HALF DOLLAR(S)"
    255 C=N-(G*50)
    260 H=INT(C/25)
    270 IF H=0 THEN 285
    280 PRINT H;"QUARTER(S)"
    285 C=N-(G*50+H*25)
    290 I=INT(C/10)
    300 IF I=0 THEN 315
    310 PRINT I;"DIME(S)"
    315 C=N-(G*50+H*25+I*10)
    320 J=INT(C/5)
    330 IF J=0 THEN 345
    340 PRINT J;"NICKEL(S)"
    345 C=N-(G*50+H*25+I*10+J*5)
    350 K=INT(C+.5)
    360 IF K=0 THEN 380
    370 PRINT K;"PENNY(S)"
    380 PRINT "THANK YOU, COME AGAIN."
    390 PRINT:PRINT
    400 GOTO 10
    410 END
    
    
    ================================================
    FILE: 22_Change/csharp/Change.csproj
    ================================================
    
    
      
        Exe
        net5.0
      
    
    
    
    
    ================================================
    FILE: 22_Change/csharp/Change.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.810.16
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Change", "Change.csproj", "{AE094667-8496-4ECF-8B42-B1648EE26073}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{AE094667-8496-4ECF-8B42-B1648EE26073}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{AE094667-8496-4ECF-8B42-B1648EE26073}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{AE094667-8496-4ECF-8B42-B1648EE26073}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{AE094667-8496-4ECF-8B42-B1648EE26073}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {65684CBD-CD74-46AF-8E9E-0F69DCF72697}
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 22_Change/csharp/Program.cs
    ================================================
    using System;
    
    namespace Change
    {
        class Program
        {
            /// 
            /// Prints header.
            /// 
            static void Header()
            {
                Console.WriteLine("Change".PadLeft(33));
                Console.WriteLine("Creative Computing Morristown, New Jersey".PadLeft(15));
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine("I, your friendly microcomputer, will determine\n"
                + "the correct change for items costing up to $100.");
                Console.WriteLine();
                Console.WriteLine();
            }
    
            /// 
            /// Gets user input for price and payment.
            /// 
            /// 
            /// False if any input can't be parsed to double. Price and payment returned would be 0.
            /// True if it was possible to parse inputs into doubles. Price and payment returned
    	    /// would be as provided by the user.
    	    /// 
            static (bool status, double price, double payment) GetInput()
            {
                Console.Write("Cost of item? ");
                var priceString = Console.ReadLine();
                if (!double.TryParse(priceString, out double price))
                {
                    Console.WriteLine($"{priceString} isn't a number!");
                    return (false, 0, 0);
                }
    
                Console.Write("Amount of payment? ");
                var paymentString = Console.ReadLine();
                if (!double.TryParse(paymentString, out double payment))
                {
                    Console.WriteLine($"{paymentString} isn't a number!");
                    return (false, 0, 0);
                }
    
                return (true, price, payment);
            }
    
            /// 
            /// Prints bills and coins for given change.
            /// 
            /// 
            static void PrintChange(double change)
            {
                var tens = (int)(change / 10);
                if (tens > 0)
                    Console.WriteLine($"{tens} ten dollar bill(s)");
    
                var temp = change - (tens * 10);
                var fives = (int)(temp / 5);
                if (fives > 0)
                    Console.WriteLine($"{fives} five dollar bill(s)");
    
                temp -= fives * 5;
                var ones = (int)temp;
                if (ones > 0)
                    Console.WriteLine($"{ones} one dollar bill(s)");
    
                temp -= ones;
                var cents = temp * 100;
                var half = (int)(cents / 50);
                if (half > 0)
                    Console.WriteLine($"{half} one half dollar(s)");
    
                temp = cents - (half * 50);
                var quarters = (int)(temp / 25);
                if (quarters > 0)
                    Console.WriteLine($"{quarters} quarter(s)");
    
                temp -= quarters * 25;
                var dimes = (int)(temp / 10);
                if (dimes > 0)
                    Console.WriteLine($"{dimes} dime(s)");
    
                temp -= dimes * 10;
                var nickels = (int)(temp / 5);
                if (nickels > 0)
                    Console.WriteLine($"{nickels} nickel(s)");
    
                temp -= nickels * 5;
                var pennies = (int)(temp + 0.5);
                if (pennies > 0)
                    Console.WriteLine($"{pennies} penny(s)");
            }
    
            static void Main(string[] args)
            {
                Header();
    
                while (true)
                {
                    (bool result, double price, double payment) = GetInput();
                    if (!result)
                        continue;
    
                    var change = payment - price;
                    if (change == 0)
                    {
                        Console.WriteLine("Correct amount, thank you!");
                        continue;
                    }
    
                    if (change < 0)
                    {
                        Console.WriteLine($"Sorry, you have short-changed me ${price - payment:N2}!");
                        continue;
                    }
    
                    Console.WriteLine($"Your change ${change:N2}");
                    PrintChange(change);
                    Console.WriteLine("Thank you, come again!");
                    Console.WriteLine();
                }
            }
        }
    }
    
    
    ================================================
    FILE: 22_Change/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 22_Change/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 22_Change/java/src/Change.java
    ================================================
    import java.util.Arrays;
    import java.util.Scanner;
    
    /**
     * Game of Change
     * 

    * Based on the Basic game of Change here * https://github.com/coding-horror/basic-computer-games/blob/main/22%20Change/change.bas *

    * Note: The idea was to create a version of the 1970's Basic game in Java, without introducing * new features - no additional text, error checking, etc has been added. */ public class Change { // Used for keyboard input private final Scanner kbScanner; private enum GAME_STATE { START_GAME, INPUT, CALCULATE, END_GAME, GAME_OVER } // Current game state private GAME_STATE gameState; // Amount of change needed to be given private double change; public Change() { kbScanner = new Scanner(System.in); gameState = GAME_STATE.START_GAME; } /** * Main game loop */ public void play() { do { switch (gameState) { case START_GAME: intro(); gameState = GAME_STATE.INPUT; break; case INPUT: double costOfItem = displayTextAndGetNumber("COST OF ITEM "); double amountPaid = displayTextAndGetNumber("AMOUNT OF PAYMENT "); change = amountPaid - costOfItem; if (change == 0) { // No change needed System.out.println("CORRECT AMOUNT, THANK YOU."); gameState = GAME_STATE.END_GAME; } else if (change < 0) { System.out.println("YOU HAVE SHORT-CHANGES ME $" + (costOfItem - amountPaid)); // Don't change game state so it will loop back and try again } else { // Change needed. gameState = GAME_STATE.CALCULATE; } break; case CALCULATE: System.out.println("YOUR CHANGE, $" + change); calculateChange(); gameState = GAME_STATE.END_GAME; break; case END_GAME: System.out.println("THANK YOU, COME AGAIN"); System.out.println(); gameState = GAME_STATE.INPUT; } } while (gameState != GAME_STATE.GAME_OVER); } /** * Calculate and output the change required for the purchase based on * what money was paid. */ private void calculateChange() { double originalChange = change; int tenDollarBills = (int) change / 10; if (tenDollarBills > 0) { System.out.println(tenDollarBills + " TEN DOLLAR BILL(S)"); } change = originalChange - (tenDollarBills * 10); int fiveDollarBills = (int) change / 5; if (fiveDollarBills > 0) { System.out.println(fiveDollarBills + " FIVE DOLLAR BILL(S)"); } change = originalChange - (tenDollarBills * 10 + fiveDollarBills * 5); int oneDollarBills = (int) change; if (oneDollarBills > 0) { System.out.println(oneDollarBills + " ONE DOLLAR BILL(S)"); } change = originalChange - (tenDollarBills * 10 + fiveDollarBills * 5 + oneDollarBills); change = change * 100; double cents = change; int halfDollars = (int) change / 50; if (halfDollars > 0) { System.out.println(halfDollars + " ONE HALF DOLLAR(S)"); } change = cents - (halfDollars * 50); int quarters = (int) change / 25; if (quarters > 0) { System.out.println(quarters + " QUARTER(S)"); } change = cents - (halfDollars * 50 + quarters * 25); int dimes = (int) change / 10; if (dimes > 0) { System.out.println(dimes + " DIME(S)"); } change = cents - (halfDollars * 50 + quarters * 25 + dimes * 10); int nickels = (int) change / 5; if (nickels > 0) { System.out.println(nickels + " NICKEL(S)"); } change = cents - (halfDollars * 50 + quarters * 25 + dimes * 10 + nickels * 5); int pennies = (int) (change + .5); if (pennies > 0) { System.out.println(pennies + " PENNY(S)"); } } private void intro() { System.out.println(simulateTabs(33) + "CHANGE"); System.out.println(simulateTabs(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println(); System.out.println("I, YOUR FRIENDLY MICROCOMPUTER, WILL DETERMINE"); System.out.println("THE CORRECT CHANGE FOR ITEMS COSTING UP TO $100."); System.out.println(); } /* * Print a message on the screen, then accept input from Keyboard. * Converts input to a Double * * @param text message to be displayed on screen. * @return what was typed by the player. */ private double displayTextAndGetNumber(String text) { return Double.parseDouble(displayTextAndGetInput(text)); } /* * Print a message on the screen, then accept input from Keyboard. * * @param text message to be displayed on screen. * @return what was typed by the player. */ private String displayTextAndGetInput(String text) { System.out.print(text); return kbScanner.next(); } /** * Simulate the old basic tab(xx) command which indented text by xx spaces. * * @param spaces number of spaces required * @return String with number of spaces */ private String simulateTabs(int spaces) { char[] spacesTemp = new char[spaces]; Arrays.fill(spacesTemp, ' '); return new String(spacesTemp); } } ================================================ FILE: 22_Change/java/src/ChangeGame.java ================================================ public class ChangeGame { public static void main(String[] args) { Change change = new Change(); change.play(); } } ================================================ FILE: 22_Change/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 22_Change/javascript/change.html ================================================ CHANGE

    
    
    
    
    
    
    ================================================
    FILE: 22_Change/javascript/change.js
    ================================================
    // CHANGE
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    // Main program
    async function main()
    {
        print(tab(33) + "CHANGE\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        print("I, YOUR FRIENDLY MICROCOMPUTER, WILL DETERMINE\n");
        print("THE CORRECT CHANGE FOR ITEMS COSTING UP TO $100.\n");
        print("\n");
        print("\n");
        while (1) {
            print("COST OF ITEM");
            a = parseFloat(await input());
            print("AMOUNT OF PAYMENT");
            p = parseFloat(await input());
            c = p - a;
            m = c;
            if (c == 0) {
                print("CORRECT AMOUNT, THANK YOU.\n");
            } else {
                print("YOUR CHANGE, $" + c + "\n");
                d = Math.floor(c / 10);
                if (d)
                    print(d + " TEN DOLLAR BILL(S)\n");
                c -= d * 10;
                e = Math.floor(c / 5);
                if (e)
                    print(e + " FIVE DOLLAR BILL(S)\n");
                c -= e * 5;
                f = Math.floor(c);
                if (f)
                    print(f + " ONE DOLLAR BILL(S)\n");
                c -= f;
                c *= 100;
                g = Math.floor(c / 50);
                if (g)
                    print(g + " ONE HALF DOLLAR(S)\n");
                c -= g * 50;
                h = Math.floor(c / 25);
                if (h)
                    print(h + " QUARTER(S)\n");
                c -= h * 25;
                i = Math.floor(c / 10);
                if (i)
                    print(i + " DIME(S)\n");
                c -= i * 10;
                j = Math.floor(c / 5);
                if (j)
                    print(j + " NICKEL(S)\n");
                c -= j * 5;
                k = Math.floor(c + 0.5);
                if (k)
                    print(k + " PENNY(S)\n");
                print("THANK YOU, COME AGAIN.\n");
                print("\n");
                print("\n");
            }
        }
    }
    
    main();
    
    
    ================================================
    FILE: 22_Change/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 22_Change/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 22_Change/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 22_Change/perl/change.pl
    ================================================
    #!/usr/bin/perl
    
    use v5.24; # for say and use strict
    use warnings;
    
    sub get_pennies {
      my $query = shift;
    
      print "$query? ";
      my $in = <>;
      chomp $in;
      $in =~ /([\d.]+)/; # the first match of digits and decimal points
      return int( $1 * 100 );
    }
    
    sub make_change {
      my $change = shift;
    
      state %change_options = (
        'Penny' => { value => 1, plural => 'Pennies' },
        'Nickel' => { value => 5 },
        'Dime' => { value => 10 },
        'Quarter' => { value => 25 },
        'One Half Dollar' => { value => 50 },
        'One Dollar Bill' => { value => 100 * 1 },
        'Five Dollar Bill' => { value => 100 * 5 },
        '10 Dollar Bill' => { value => 100 * 10 },
      );
    
      foreach my $unit ( sort { $change_options{$b}->{value} <=> $change_options{$a}->{value} } keys %change_options ) {
        my $value = $change_options{$unit}->{value};
        next if $value > $change;
        my $number = int( $change / $value );
        if ( $number > 1 ) {
          $unit = exists $change_options{$unit}->{plural} ? $change_options{$unit}->{plural} : "${unit}s";
        }
        say "$number $unit";
        $change -= $number * $value;
      }
    }
    
    print <<'__END_OF_INTRO';
                                  Change
                   Creative Computing  Morristown, New Jersey
    
    
    I, Your friendly microcomputer, will determine
    the correct change for items costing up to $100.
    
    
    __END_OF_INTRO
    
    while ( 1 ) {
      my $cost = get_pennies( 'Cost of item' );
      my $payment = get_pennies( 'Amount of payment');
    
      my $change = $payment - $cost;
      my $change_formatted = sprintf( "%.2f", $change / 100 );
      if ( $change == 0 ) {
        say 'Correct amount, thank you.';
      } elsif ( $change < 0 ) {
        say 'Sorry, you have short-changed me $', abs($change_formatted);
      } else {
        say 'Your change, $', $change_formatted;
        make_change( $change );
        say "Thank you, come again\n\n";
      }
    }
    
    
    ================================================
    FILE: 22_Change/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 22_Change/python/change.py
    ================================================
    """
    CHANGE
    
    Change calculator
    
    Port by Dave LeCompte
    """
    
    PAGE_WIDTH = 64
    
    
    def print_centered(msg: str) -> None:
        spaces = " " * ((PAGE_WIDTH - len(msg)) // 2)
        print(spaces + msg)
    
    
    def print_header(title: str) -> None:
        print_centered(title)
        print_centered("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n\n\n")
    
    
    def print_introduction() -> None:
        print("I, YOUR FRIENDLY MICROCOMPUTER, WILL DETERMINE")
        print("THE CORRECT CHANGE FOR ITEMS COSTING UP TO $100.\n\n")
    
    
    def pennies_to_dollar_string(p: float) -> str:
        d = p / 100
        return f"${d:0.2f}"
    
    
    def compute_change() -> None:
        print("COST OF ITEM?")
        cost = float(input())
        print("AMOUNT OF PAYMENT?")
        payment = float(input())
    
        change_in_pennies = round((payment - cost) * 100)
        if change_in_pennies == 0:
            print("CORRECT AMOUNT, THANK YOU.")
            return
    
        if change_in_pennies < 0:
            short = -change_in_pennies / 100
    
            print(f"SORRY, YOU HAVE SHORT-CHANGED ME ${short:0.2f}")
            print()
            return
    
        print(f"YOUR CHANGE, {pennies_to_dollar_string(change_in_pennies)}")
    
        d = change_in_pennies // 1000
        if d > 0:
            print(f"{d} TEN DOLLAR BILL(S)")
        change_in_pennies -= d * 1000
    
        e = change_in_pennies // 500
        if e > 0:
            print(f"{e} FIVE DOLLAR BILL(S)")
        change_in_pennies -= e * 500
    
        f = change_in_pennies // 100
        if f > 0:
            print(f"{f} ONE DOLLAR BILL(S)")
        change_in_pennies -= f * 100
    
        g = change_in_pennies // 50
        if g > 0:
            print("ONE HALF DOLLAR")
        change_in_pennies -= g * 50
    
        h = change_in_pennies // 25
        if h > 0:
            print(f"{h} QUARTER(S)")
        change_in_pennies -= h * 25
    
        i = change_in_pennies // 10
        if i > 0:
            print(f"{i} DIME(S)")
        change_in_pennies -= i * 10
    
        j = change_in_pennies // 5
        if j > 0:
            print(f"{j} NICKEL(S)")
        change_in_pennies -= j * 5
    
        if change_in_pennies > 0:
            print(f"{change_in_pennies} PENNY(S)")
    
    
    def main() -> None:
        print_header("CHANGE")
        print_introduction()
    
        while True:
            compute_change()
            print("THANK YOU, COME AGAIN.\n\n")
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 22_Change/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 22_Change/rust/Cargo.toml
    ================================================
    [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]
    
    
    ================================================
    FILE: 22_Change/rust/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Rust](https://www.rust-lang.org/) by Anthony Rubick [AnthonyMichaelTDM](https://github.com/AnthonyMichaelTDM)
    
    
    ================================================
    FILE: 22_Change/rust/src/main.rs
    ================================================
    use std::io::{self, stdout, Write};
    
    fn main() {
        //DATA
        let mut cost_in_cents:i16;
        let mut payment_in_cents:i16;
        let mut amount_owed_in_cents:i16;
    
        let mut hundred:i16;
        let mut fifty:i16;
        let mut twenty:i16;
        let mut ten:i16;
        let mut five:i16;
        let mut one:i16;
        let mut quarters:i16;
        let mut dimes:i16;
        let mut nickles:i16;
        let mut pennies:i16;
    
        //print welcome message
        welcome();
    
        //print prompt
        println!("I, YOUR FRIENDLY MICROCOMPUTER, WILL DETERMINE");
        println!("THE CORRECT CHANGE FOR ITEMS COSTING UP TO ${}.00.",i16::MAX/100);
    
        //game loop 
        loop {
            //get cost of items
            cost_in_cents = get_dollar_value_in_cents_from_user("COST OF ITEM:\t\t$");
            //get amount they already paid
            payment_in_cents = get_dollar_value_in_cents_from_user("AMOUNT OF PAYMENT:\t$");
    
            //calculate amount they owe
            amount_owed_in_cents = payment_in_cents - cost_in_cents;
    
            //check whether the payment is equal to, less than, or greater than, the cost
            if cost_in_cents == payment_in_cents {
                println!("CORRECT AMOUNT, THANK YOU.");
                continue;
            }
            else if payment_in_cents < cost_in_cents{ //amount_owed_in_cents is less than 0
                println!(
                    "SORRY, YOU HAVE SHORT-CHANGED ME ${}.{}",
                    -amount_owed_in_cents/100,//leading digits
                    -amount_owed_in_cents%100,//trailing digits
                );
                continue;
            }
            else {
                println!("YOUR CHANGE, ${}.{}", amount_owed_in_cents/100, amount_owed_in_cents%100);
            }
    
            //calculate change due
            //hundred dollar bills owed
            hundred = amount_owed_in_cents / (100*100);
            if hundred > 0 {println!("HUNDRED DOLLAR BILL(S): {}", hundred);}
            amount_owed_in_cents = amount_owed_in_cents % (100*100);
    
            //fifty dollar bills owed
            fifty = amount_owed_in_cents / (50*100);
            if fifty > 0 {println!("FIFTY DOLLAR BILL(S): {}", fifty);}
            amount_owed_in_cents = amount_owed_in_cents % (50*100);
    
            //twenty dollar bills owed
            twenty = amount_owed_in_cents / (20*100);
            if twenty > 0 {println!("TWENTY DOLLAR BILL(S): {}", twenty);}
            amount_owed_in_cents = amount_owed_in_cents % (20*100);
    
            //ten dollar bills owed
            ten = amount_owed_in_cents / (10*100);
            if ten > 0 {println!("TEN DOLLAR BILL(S): {}", ten);}
            amount_owed_in_cents = amount_owed_in_cents % (10*100);
    
            //five dollar bills owed
            five = amount_owed_in_cents / (5*100);
            if five > 0 {println!("FIVE DOLLAR BILL(S): {}", five);}
            amount_owed_in_cents = amount_owed_in_cents % (5*100);
    
            //one dollar bills owed
            one = amount_owed_in_cents / (1*100);
            if one > 0 {println!("ONE DOLLAR BILL(S): {}", one);}
            amount_owed_in_cents = amount_owed_in_cents % (1*100);
    
            //quarters owed
            quarters = amount_owed_in_cents / 25;
            if quarters > 0 {println!("QUARTER(S): {}", quarters);}
            amount_owed_in_cents = amount_owed_in_cents % 25;
    
            //dimes owed
            dimes = amount_owed_in_cents / 10;
            if dimes > 0 {println!("DIME(S): {}", dimes);}
            amount_owed_in_cents = amount_owed_in_cents % 10;
    
            //nickles owed
            nickles = amount_owed_in_cents / 5;
            if nickles > 0 {println!("NICKEL(S): {}", nickles);}
            amount_owed_in_cents = amount_owed_in_cents % 5;
    
            //pennies owed
            pennies = amount_owed_in_cents / 1;
            if pennies > 0 {println!("PENNY(S): {}", pennies);}
    
            //print ending message
            println!("THANK YOU, COME AGAIN.\n\n");
        }
    }
    
    /**
     * print welcome message
     */
    fn welcome() {
        println!("\t\t\t\tCHANGE\n\t      CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n\n\n");
    }
    
    /**
     * get number of money from user input
     */
    fn get_dollar_value_in_cents_from_user(prompt:&str) -> i16 {
        let mut value:i16;
        //input loop
        loop {
            //data
            let mut raw_input = String::new();
    
            //print prompt
            print!("{}",prompt);
            //flush std out // allows prompt to be on same line as input
            stdout().flush().expect("failed to flush");
    
            //get input
            io::stdin().read_line(&mut raw_input).expect("failed to read input");
            //filter out characters that aren't numbers or '.'
            let mut no_prior_periods = true;
            raw_input = raw_input.chars().filter(|c| {
                if c.eq_ignore_ascii_case(&'.') && no_prior_periods {
                    no_prior_periods = false;
                    true
                } else {
                    c.is_ascii_digit()
                }
            }).collect();
    
            //should only be (at most) 1 .
            if !raw_input.contains(".") { raw_input += ".00";} //if there are none, add one
    
            //ensure there are at least 2 trailing digits
            if raw_input[raw_input.find('.').unwrap_or(raw_input.len())..].len() <= 2 { //if a slice of the string from the . onwards is less than or equal to 2, add two 0's to the end
                raw_input += "00"
            }
            //truncate the trailing digits to 2 digits
            raw_input = raw_input[..=raw_input.find('.').unwrap_or(raw_input.len()-2)+2].to_string(); //raw_input = a slice of raw_input from the start to 2 past the . 
            
            //remove the '.' and convert the string to an integer
            raw_input = raw_input.chars().filter(|c| c.is_ascii_digit()).collect();
            match raw_input.parse::().ok() {
                Some(v) => {value = v;}
                None => {
                    println!("INPUT OUTSIDE OF ACCEPTABLE RANGE, TRY AGAIN");
                    continue;
                },
            }
    
            //println!("{}",value);
    
            if value <= 0 {continue;}
            else {return value;}
        }
    }
    
    
    ================================================
    FILE: 22_Change/vbnet/Change.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Change", "Change.vbproj", "{77FC724B-EA88-4419-824B-32366FFE9608}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{77FC724B-EA88-4419-824B-32366FFE9608}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{77FC724B-EA88-4419-824B-32366FFE9608}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{77FC724B-EA88-4419-824B-32366FFE9608}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{77FC724B-EA88-4419-824B-32366FFE9608}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 22_Change/vbnet/Change.vbproj
    ================================================
    
      
        Exe
        Change
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 22_Change/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 23_Checkers/README.md
    ================================================
    ### Checkers
    
    This program plays checkers. The pieces played by the computer are marked with an “X”, yours are marked “O”. A move is made by specifying the coordinates of the piece to be moved (X, Y). Home (0,0) is in the bottom left and X specifies distance to the right of home (i.e., column) and Y specifies distance above home (i.e. row). You then specify where you wish to move to.
    
    The original version of the program by Alan Segal was not able to recognize (or permit) a double or triple jump. If you tried one, it was likely that your piece would disappear altogether!
    
    Steve North of Creative Computing rectified this problem and Lawrence Neal contributed modifications to allow the program to tell which player has won the game. The computer does not play a particularly good game but we leave it to _you_ to improve that.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=40)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=55)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    The file `checkers.annotated.bas` contains an indented and annotated
    version of the source code.  This is no longer valid BASIC code but
    should be more readable.
    
    #### Porting Notes
    
     - 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, 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)
    
    
    ================================================
    FILE: 23_Checkers/checkers.annotated.bas
    ================================================
         # Annotated version of CHECKERS.BAS, modified to improve readability.
         #
         # I've made the following changes:
         #
         #  1. Added many comments and blank lines.
         #  2. Separated each statement into its own line.
         #  3. Indented loops, conditionals and subroutines.
         #  4. Turned *SOME* conditionals and loops into
         #     structured-BASIC-style if/endif and loop/endloop blocks.
         #  5. Switched to using '#' to delimit comments.
         #  6. Subroutines now begin with "Sub_Start"
         #  7. All non-string text has been converted to lower-case
         #  8. All line numbers that are not jump destinations have been removed.
         #
         # This has helped me make sense of the code.  I hope it will also help you.
         #
    
         # Print the banner
         print tab(32);"CHECKERS"
         print tab(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
    
         # Declare the "globals":
    
         # The current move: (rating, current x, current y, new x, new y)
         # 'rating' represents how good the move is; higher is better.
         dim r(4)
         r(0)=-99       # 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.
         dim s(7,7)
    
         g=-1           # constant holding -1
    
         # Initialize the board.  Data is 2 length-wise strips repeated.
         data 1,0,1,0,0,0,-1,0,0,1,0,0,0,-1,0,-1,15
         for x=0 to 7
           for y=0 to 7
             read j
             if j=15 then 180
             s(x,y)=j
             goto 200
    180      restore
             read s(x,y)
    200  next y,x
    
    230  # Start of game loop.  First, my turn.
    
         # 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 x=0 to 7
           for y=0 to 7
    
             # Skip if this is empty or an opponent's piece
             if s(x,y) > -1 then 350
    
             # If this is one of my ordinary pieces, analyze possible
             # forward moves.
             if s(x,y) = -1 then
               for a=-1 to 1 step 2
                 b=g
                 gosub 650
               next a
             endif
    
             # If this is one of my kings, analyze possible forward
             # and backward moves.
             if s(x,y) = -2 then
               for a=-1 to 1 step 2
                 for b=-1 to 1 step 2
                   gosub 650
               next b,a
             endif
    
    350  next y,x
         goto 1140  # Skip the subs
    
    
         # Analyze a move from (x,y) to (x+a, y+b) and schedule it if it's
         # the best candidate so far.
    650  Sub_Start
           u=x+a
           v=y+b
    
           # Done if it's off the board
           if u<0 or u>7 or v<0 or v>7 then 870
    
           # Consider the destination if it's empty
           if s(u,v) = 0 then
             gosub 910
             goto 870
           endif
    
           # If it's got an opponent's piece, jump it instead
           if s(u,v) > 0
    
               # Restore u and v, then return if it's off the board
               u=u+a
               v=v+b
               if u<0 or v<0 or u>7 or v>7 then 870
    
               # Otherwise, consider u,v
               if s(u,v)=0 then gosub 910
           endif
    870  return
    
         # 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 array 'r'.
    910  Sub_Start
    
           # q is the score; it starts at 0
    
           # +2 if it promotes this piece
           if v=0 and s(x,y)=-1 then q=q+2
    
           # +5 if it takes an opponent's piece
           if abs(y-v)=2 then q=q+5
    
           # -2 if the piece is moving away from the top boundary
           if y=7 then q=q-2
    
           # +1 for putting the piece against a vertical boundary
           if u=0 or u=7 then q=q+1
    
           for c=-1 to 1 step 2
             if u+c < 0 or u+c > 7 or v+g < 0 then 1080
    
             # +1 for each adjacent friendly piece
             if s(u+c, v+g) < 0 then
               q=q+1
               goto 1080
             endif
    
             # Prevent out-of-bounds testing
             if u-c < 0 or u-c > 7 or v-g > 7 then 1080
    
             # -2 for each opponent piece that can now take this piece here
             if s(u+c,v+g) > 0 and(s(u-c,v-g)=0 or(u-c=x and v-g=y))then q=q-2
    1080   next c
    
           # Use this move if it's better than the previous best
           if q>r(0) then
             r(0)=q
             r(1)=x
             r(2)=y
             r(3)=u
             r(4)=v
           endif
    
           q=0  # reset the score
         return
    
    1140 if r(0)=-99 then 1880  # Game is lost if no move could be found.
    
         # Print the computer's move.  (Note: chr$(30) is an ASCII RS
         # (record separator) code; probably no longer relevant.)
         print chr$(30)"FROM"r(1);r(2)"TO"r(3);r(4);
         r(0)=-99
    
         # Make the computer's move.  If the piece finds its way to the
         # end of the board, crown it.
    1240 if r(4)=0 then
           s(r(3),r(4))=-2
           goto 1420
         endif
         s(r(3),r(4))=s(r(1),r(2))
         s(r(1),r(2))=0
    
         # If the piece has jumped 2 squares, it means the computer has
         # taken an opponents' piece.
         if abs(r(1)-r(3)) == 2 then
           s((r(1)+r(3))/2,(r(2)+r(4))/2)=0     # Delete the opponent's piece
    
           # See if we can jump again.  Evaluate all possible moves.
           x=r(3)
           y=r(4)
           for a=-2 to 2 step 4
             if s(x,y)=-1 then
               b=-2
               gosub 1370
             endif
             if s(x,y)=-2 then
               for b=-2 to 2 step 4
                 gosub 1370
               next b
             endif
           next a
    
           # If we've found a move, go back and make that one as well
           if r(0) <> -99 then
             print "TO" r(3); r(4);
             r(0)=-99
             goto 1240
           endif
    
           goto 1420   # Skip the sub
    
           # If (u,v) is in the bounds, evaluate it as a move using
           # the sub at 910
    1370   Sub_Start
             u=x+a
             v=y+b
             if u<0 or u>7 or v<0 or v>7 then 1400
             if s(u,v)=0 and s(x+a/2,y+b/2)>0 then gosub 910
    1400   return
    
    1420 endif
    
         # Now, print the board
         print
         print
         print
         for y=7 to 0 step-1
           for x=0 to 7
             i=5*x
             print tab(i);
             if s(x,y)=0 then print".";
             if s(x,y)=1 then print"O";
             if s(x,y)=-1 then print"X";
             if s(x,y)=-2 then print"X*";
             if s(x,y)=2 then print"O*";
           next x
           print" "
           print
         next y
         print
    
         # Check if either player is out of pieces.  If so, announce the
         # winner.
         for l=0 to 7
           for m=0 to 7
             if s(l,m)=1 or s(l,m)=2 then z=1
             if s(l,m)=-1 or s(l,m)=-2 then t=1
           next m
         next l
         if z<>1 then 1885
         if t<>1 then 1880
    
         # Prompt the player for their move.
         z=0
         t=0
    1590 input "FROM";e,h
         x=e
         y=h
         if s(x,y)<=0 then 1590
    1670 input "TO";a,b
         x=a
         y=b
         if s(x,y)=0 and abs(a-e)<=2 and abs(a-e)=abs(b-h)then 1700
         print chr$(7)chr$(11);     # bell, vertical tab; invalid move
         goto 1670
    
    1700 i=46       # Not used; probably a bug
    1750 loop
           # Make the move and stop unless it might be a jump.
           s(a,b) = s(e,h)
           s(e,h) = 0
           if abs(e-a) <> 2 then break
    
           # Remove the piece jumped over
           s((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.
    1802   input "+TO";a1,b1
           if a1 < 0 then break
           if s(a1,b1) <> 0 or abs(a1-a) <>2  or abs(b1-b) <> 2 then 1802
    
           # Update the move variables to correspond to the next jump
           e=a
           h=b
           a=a1
           b=b1
    
           i=i+15   # Not used; probably a bug
         endloop
    
         # If the player has reached the end of the board, crown this piece
    1810 if b=7 then s(a,b)=2
    
         # And play the next turn.
         goto 230
    
         # Endgame:
    1880 print
         print "YOU WIN."
         end
    
    1885 print
         print "I WIN."
         end
    
    
    ================================================
    FILE: 23_Checkers/checkers.bas
    ================================================
    5 PRINT TAB(32);"CHECKERS"
    10 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    15 PRINT:PRINT:PRINT
    20 PRINT "THIS IS THE GAME OF CHECKERS.  THE COMPUTER IS X,"
    25 PRINT "AND YOU ARE O.  THE COMPUTER WILL MOVE FIRST."
    30 PRINT "SQUARES ARE REFERRED TO BY A COORDINATE SYSTEM."
    35 PRINT "(0,0) IS THE LOWER LEFT CORNER"
    40 PRINT "(0,7) IS THE UPPER LEFT CORNER"
    45 PRINT "(7,0) IS THE LOWER RIGHT CORNER"
    50 PRINT "(7,7) IS THE UPPER RIGHT CORNER"
    55 PRINT "THE COMPUTER WILL TYPE '+TO' WHEN YOU HAVE ANOTHER"
    60 PRINT "JUMP.  TYPE TWO NEGATIVE NUMBERS IF YOU CANNOT JUMP."
    65 PRINT:PRINT:PRINT
    80 DIM R(4),S(7,7):G=-1:R(0)=-99
    90 DATA 1,0,1,0,0,0,-1,0,0,1,0,0,0,-1,0,-1,15
    120 FOR X=0 TO 7:FOR Y=0 TO 7:READ J:IF J=15 THEN 180
    160 S(X,Y)=J:GOTO 200
    180 RESTORE:READ S(X,Y)
    200 NEXT Y,X
    230 FOR X=0 TO 7:FOR Y=0 TO 7:IF S(X,Y)>-1 THEN 350
    310 IF S(X,Y)=-1 THEN FOR A=-1 TO 1 STEP 2:B=G:GOSUB 650:NEXT A
    330 IF S(X,Y)=-2 THEN FOR A=-1 TO 1 STEP 2:FOR B=-1 TO 1 STEP 2:GOSUB 650:NEXT B,A
    350 NEXT Y,X:GOTO 1140
    650 U=X+A:V=Y+B:IF U<0 OR U>7 OR V<0 OR V>7 THEN 870
    740 IF S(U,V)=0 THEN GOSUB 910:GOTO 870
    770 IF S(U,V)<0 THEN 870
    790 U=U+A:V=V+B:IF U<0 OR V<0 OR U>7 OR V>7 THEN 870
    850 IF S(U,V)=0 THEN GOSUB 910
    870 RETURN
    910 IF V=0 AND S(X,Y)=-1 THEN Q=Q+2
    920 IF ABS(Y-V)=2 THEN Q=Q+5
    960 IF Y=7 THEN Q=Q-2
    980 IF U=0 OR U=7 THEN Q=Q+1
    1030 FOR C=-1 TO 1 STEP 2:IF U+C<0 OR U+C>7 OR V+G<0 THEN 1080
    1035 IF S(U+C,V+G)<0 THEN Q=Q+1:GOTO 1080
    1040 IF U-C<0 OR U-C>7 OR V-G>7 THEN 1080
    1045 IF S(U+C,V+G)>0 AND(S(U-C,V-G)=0 OR(U-C=X AND V-G=Y))THEN Q=Q-2
    1080 NEXT C:IF Q>R(0)THEN R(0)=Q:R(1)=X:R(2)=Y:R(3)=U:R(4)=V
    1100 Q=0:RETURN
    1140 IF R(0)=-99 THEN 1880
    1230 PRINT CHR$(30)"FROM"R(1);R(2)"TO"R(3);R(4);:R(0)=-99
    1240 IF R(4)=0 THEN S(R(3),R(4))=-2:GOTO 1420
    1250 S(R(3),R(4))=S(R(1),R(2))
    1310 S(R(1),R(2))=0:IF ABS(R(1)-R(3))<>2 THEN 1420
    1330 S((R(1)+R(3))/2,(R(2)+R(4))/2)=0
    1340 X=R(3):Y=R(4):IF S(X,Y)=-1 THEN B=-2:FOR A=-2 TO 2 STEP 4:GOSUB 1370
    1350 IF S(X,Y)=-2 THEN FOR A=-2 TO 2 STEP 4:FOR B=-2 TO 2 STEP 4:GOSUB 1370:NEXT B
    1360 NEXT A:IF R(0)<>-99 THEN PRINT"TO"R(3);R(4);:R(0)=-99:GOTO 1240
    1365 GOTO 1420
    1370 U=X+A:V=Y+B:IF U<0 OR U>7 OR V<0 OR V>7 THEN 1400
    1380 IF S(U,V)=0 AND S(X+A/2,Y+B/2)>0 THEN GOSUB 910
    1400 RETURN
    1420 PRINT:PRINT:PRINT:FOR Y=7 TO 0 STEP-1:FOR X=0 TO 7:I=5*X:PRINT TAB(I);
    1430 IF S(X,Y)=0 THEN PRINT".";
    1470 IF S(X,Y)=1 THEN PRINT"O";
    1490 IF S(X,Y)=-1 THEN PRINT"X";
    1510 IF S(X,Y)=-2 THEN PRINT"X*";
    1530 IF S(X,Y)=2 THEN PRINT"O*";
    1550 NEXT X:PRINT" ":PRINT:NEXT Y:PRINT
    1552 FOR L=0 TO 7
    1554 FOR M=0 TO 7
    1556 IF S(L,M)=1 OR S(L,M)=2 THEN Z=1
    1558 IF S(L,M)=-1 OR S(L,M)=-2 THEN T=1
    1560 NEXT M
    1562 NEXT L
    1564 IF Z<>1 THEN 1885
    1566 IF T<>1 THEN 1880
    1570 Z=0: T=0
    1590 INPUT "FROM";E,H:X=E:Y=H:IF S(X,Y)<=0 THEN 1590
    1670 INPUT "TO";A,B:X=A:Y=B
    1680 IF S(X,Y)=0 AND ABS(A-E)<=2 AND ABS(A-E)=ABS(B-H)THEN 1700
    1690 PRINT CHR$(7)CHR$(11);:GOTO 1670
    1700 I=46
    1750 S(A,B)=S(E,H):S(E,H)=0:IF ABS(E-A)<>2 THEN 1810
    1800 S((E+A)/2,(H+B)/2)=0
    1802 INPUT "+TO";A1,B1:IF A1<0 THEN 1810
    1804 IF S(A1,B1)<>0 OR ABS(A1-A)<>2 OR ABS(B1-B)<>2 THEN 1802
    1806 E=A:H=B:A=A1:B=B1:I=I+15:GOTO 1750
    1810 IF B=7 THEN S(A,B)=2
    1830 GOTO 230
    1880 PRINT: PRINT "YOU WIN.": END
    1885 PRINT: PRINT "I WIN.": END
    
    
    ================================================
    FILE: 23_Checkers/csharp/Checkers.csproj
    ================================================
    
      
        Exe
        net6.0
        10
        enable
        enable
      
    
    
    
    ================================================
    FILE: 23_Checkers/csharp/Checkers.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Checkers", "Checkers.csproj", "{52D0DDFC-1A0D-4E57-A4FC-428EC717313C}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{52D0DDFC-1A0D-4E57-A4FC-428EC717313C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{52D0DDFC-1A0D-4E57-A4FC-428EC717313C}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{52D0DDFC-1A0D-4E57-A4FC-428EC717313C}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{52D0DDFC-1A0D-4E57-A4FC-428EC717313C}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 23_Checkers/csharp/Program.cs
    ================================================
    /*********************************************************************************
     * CHECKERS
     * ported from BASIC https://www.atariarchives.org/basicgames/showpage.php?page=41
     *
     * Porting philosophy
     * 1) Adhere to the original as much as possible
     * 2) Attempt to be understandable by Novice progammers
     *
     * There are no classes or Object Oriented design patterns used in this implementation.
     * Everything is written procedurally, using only top-level functions. Hopefully, this
     * will be approachable for someone who wants to learn C# syntax without experience with
     * Object Oriented concepts. Similarly, basic data structures have been chosen over more
     * powerful collection types.  Linq/lambda syntax is also excluded.
     *
     * C# Concepts contained in this example:
     *    Loops (for, foreach, while, and do)
     *    Multidimensional arrays
     *    Tuples
     *    Nullables
     *    IEnumerable (yield return / yield break)
     *
     * The original had multiple implementations of logic, like determining valid jump locations.
     * This has been refactored to reduce unnecessary code duplication.
     *********************************************************************************/
    #region Display functions
    void SkipLines(int count)
    {
        for (int i = 0; i < count; i++)
        {
            Console.WriteLine();
        }
    }
    
    void PrintBoard(int[,] state)
    {
        SkipLines(3);
        for (int y = 7; y >= 0; y--)
        {
            for (int x = 0; x < 8; x++)
            {
                switch(state[x,y])
                {
                    case -2:
                        Console.Write("X*");
                        break;
                    case -1:
                        Console.Write("X ");
                        break;
                    case 0:
                        Console.Write(". ");
                        break;
                    case 1:
                        Console.Write("O ");
                        break;
                    case 2:
                        Console.Write("O*");
                        break;
                }
                Console.Write("   ");
            }
            Console.WriteLine();
        }
    }
    
    void WriteCenter(string text)
    {
        const int LineLength = 80;
        var spaces = (LineLength - text.Length) / 2;
        Console.WriteLine($"{"".PadLeft(spaces)}{text}");
    }
    
    void ComputerWins()
    {
        Console.WriteLine("I WIN.");
    }
    void PlayerWins()
    {
        Console.WriteLine("YOU WIN.");
    }
    
    void WriteIntroduction()
    {
        WriteCenter("CHECKERS");
        WriteCenter("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
        SkipLines(3);
        Console.WriteLine("THIS IS THE GAME OF CHECKERS. THE COMPUTER IS X,");
        Console.WriteLine("AND YOU ARE O.  THE COMPUTER WILL MOVE FIRST.");
        Console.WriteLine("SQUARES ARE REFERRED TO BY A COORDINATE SYSTEM.");
        Console.WriteLine("(0,0) IS THE LOWER LEFT CORNER");
        Console.WriteLine("(0,7) IS THE UPPER LEFT CORNER");
        Console.WriteLine("(7,0) IS THE LOWER RIGHT CORNER");
        Console.WriteLine("(7,7) IS THE UPPER RIGHT CORNER");
        Console.WriteLine("THE COMPUTER WILL TYPE '+TO' WHEN YOU HAVE ANOTHER");
        Console.WriteLine("JUMP.  TYPE TWO NEGATIVE NUMBERS IF YOU CANNOT JUMP.");
        SkipLines(3);
    }
    #endregion
    
    #region State validation functions
    bool IsPointOutOfBounds(int x)
    {
        return x < 0 || x > 7;
    }
    
    bool IsOutOfBounds((int x, int y) position)
    {
        return IsPointOutOfBounds(position.x) || IsPointOutOfBounds(position.y);
    }
    
    bool IsJumpMove((int x, int y) from, (int x, int y) to)
    {
        return Math.Abs(from.y - to.y) == 2;
    }
    
    bool IsValidPlayerMove(int[,] state, (int x, int y) from, (int x, int y) to)
    {
        if (state[to.x, to.y] != 0)
        {
            return false;
        }
        var deltaX = Math.Abs(to.x - from.x);
        var deltaY = Math.Abs(to.y - from.y);
        if (deltaX != 1 && deltaX != 2)
        {
            return false;
        }
        if (deltaX != deltaY)
        {
            return false;
        }
        if (state[from.x, from.y] == 1 && Math.Sign(to.y - from.y) <= 0)
        {
            // only kings can move downwards
            return false;
        }
        if (deltaX == 2)
        {
            var jump = GetJumpedPiece(from, to);
            if (state[jump.x, jump.y] >= 0)
            {
                // no valid piece to jump
                return false;
            }
        }
        return true;
    }
    
    bool CheckForComputerWin(int[,] state)
    {
        bool playerAlive = false;
        foreach (var piece in state)
        {
            if (piece > 0)
            {
                playerAlive = true;
                break;
            }
        }
        return !playerAlive;
    }
    
    bool CheckForPlayerWin(int[,] state)
    {
        bool computerAlive = false;
        foreach (var piece in state)
        {
            if (piece < 0)
            {
                computerAlive = true;
                break;
            }
        }
        return !computerAlive;
    }
    #endregion
    
    #region Board "arithmetic"
    /// 
    /// Get the Coordinates of a jumped piece
    /// 
    (int x, int y) GetJumpedPiece((int x, int y) from, (int x, int y) to)
    {
        var midX = (to.x + from.x) / 2;
        var midY = (to.y + from.y) / 2;
        return (midX, midY);
    }
    /// 
    /// Apply a directional vector "direction" to location "from"
    /// return resulting location
    /// direction will contain: (-1,-1), (-1, 1), ( 1,-1), ( 1, 1)
    /// /// 
    (int x, int y) GetLocation((int x , int y) from, (int x, int y) direction)
    {
        return (x: from.x + direction.x, y: from.y + direction.y);
    }
    #endregion
    
    #region State change functions
    /// 
    /// Alter current "state" by moving a piece from "from" to "to"
    /// This method does not verify that the move being made is valid
    /// This method works for both player moves and computer moves
    /// 
    int[,] ApplyMove(int[,] state, (int x, int y) from, (int x, int y) to)
    {
        state[to.x, to.y] = state[from.x, from.y];
        state[from.x, from.y] = 0;
    
        if (IsJumpMove(from, to))
        {
            // a jump was made
            // remove the jumped piece from the board
            var jump = GetJumpedPiece(from, to);
            state[jump.x, jump.y] = 0;
        }
        return state;
    }
    /// 
    /// At the end of a turn (either player or computer) check to see if any pieces
    /// reached the final row.  If so, change them to kings (crown)
    /// 
    int[,] CrownKingPieces(int[,] state)
    {
        for (int x = 0; x < 8; x++)
        {
            // check the bottom row if computer has a piece in it
            if (state[x, 0] == -1)
            {
                state[x, 0] = -2;
            }
            // check the top row if the player has a piece in it
            if (state[x, 7] == 1)
            {
                state[x, 7] = 2;
            }
        }
        return state;
    }
    #endregion
    
    #region Computer Logic
    /// 
    /// Given a current location "from", determine if a move exists in a given vector, "direction"
    /// direction will contain: (-1,-1), (-1, 1), ( 1,-1), ( 1, 1)
    /// return "null" if no move is possible in this direction
    /// 
    (int x, int y)? GetCandidateMove(int[,] state, (int x, int y) from, (int x, int y) direction)
    {
        var to = GetLocation(from, direction);
        if (IsOutOfBounds(to))
            return null;
        if (state[to.x, to.y] > 0)
        {
            // potential jump
            to = GetLocation(to, direction);
            if (IsOutOfBounds(to))
                return null;
        }
        if (state[to.x, to.y] != 0)
            // space already occupied by another piece
            return null;
    
        return to;
    }
    /// 
    /// Calculate a rank for a given potential move
    /// The higher the rank value, the better the move is considered to be
    /// 
    int RankMove(int[,] state, (int x, int y) from, (int x, int y) to)
    {
        int rank = 0;
    
        if (to.y == 0 && state[from.x, from.y] == -1)
        {
            // getting a king
            rank += 2;
        }
        if (IsJumpMove(from, to))
        {
            // making a jump
            rank += 5;
        }
        if (from.y == 7)
        {
            // leaving home row
            rank -= 2;
        }
        if (to.x == 0 || to.x == 7)
        {
            // move to edge of board
            rank += 1;
        }
        // look to the row in front of the potential destination for
        for (int c = -1; c <=1; c+=2)
        {
            var inFront = GetLocation(to, (c, -1));
            if (IsOutOfBounds(inFront))
                continue;
            if (state[inFront.x, inFront.y] < 0)
            {
                // protected by our piece in front
                rank++;
                continue;
            }
            var inBack = GetLocation(to, (-c, 1));
            if (IsOutOfBounds(inBack))
            {
                continue;
            }
            if ((state[inFront.x, inFront.y] > 0) &&
                (state[inBack.x, inBack.y] == 0) || (inBack == from))
            {
                // the player can jump us
                rank -= 2;
            }
        }
        return rank;
    };
    
    /// 
    /// Returns an enumeration of possible moves that can be made by the given piece "from"
    /// If no moves, can be made, the enumeration will be empty
    /// 
    IEnumerable<(int x, int y)> GetPossibleMoves(int[,] state, (int x, int y) from)
    {
        int maxB;
        switch (state[from.x, from.y])
        {
            case -2:
                // kings can go backwards too
                maxB = 1;
                break;
            case -1:
                maxB = -1;
                break;
            default:
                // not one of our pieces
                yield break;
        }
    
        for (int a = -1; a <= 1; a += 2)
        {
            // a
            // -1 = left
            // +1 = right
            for (int b = -1; b <= maxB; b += 2)
            {
                // b
                // -1 = forwards
                // +1 = backwards (only kings allowed to make this move)
                var to = GetCandidateMove(state, from, (a, b));
                if (to == null)
                {
                    // no valid move in this direction
                    continue;
                }
                yield return to.Value;
            }
        }
    }
    /// 
    /// Determine the best move from a list of candidate moves "possibleMoves"
    /// Returns "null" if no move can be made
    /// 
    ((int x, int y) from, (int x, int y) to)? GetBestMove(int[,] state, IEnumerable<((int x, int y) from, (int x, int y) to)> possibleMoves)
    {
        int? bestRank = null;
        ((int x, int y) from, (int x, int y) to)? bestMove = null;
    
        foreach (var move in possibleMoves)
        {
            int rank = RankMove(state, move.from, move.to);
    
            if (bestRank == null || rank > bestRank)
            {
                bestRank = rank;
                bestMove = move;
            }
        }
    
        return bestMove;
    }
    
    /// 
    /// Examine the entire board and record all possible moves
    /// Return the best move found, if one exists
    /// Returns "null" if no move found
    /// 
    ((int x, int y) from, (int x, int y) to)? CalculateMove(int[,] state)
    {
        var possibleMoves = new List<((int x, int y) from, (int x, int y) to)>();
        for (int x = 0; x < 8; x++)
        {
            for (int y = 0; y < 8; y++)
            {
                var from = (x, y);
                foreach (var to in GetPossibleMoves(state, from))
                {
                    possibleMoves.Add((from, to));
                }
            }
        }
        var bestMove = GetBestMove(state, possibleMoves);
        return bestMove;
    }
    
    /// 
    /// The logic behind the Computer's turn
    /// Look for valid moves and possible subsequent moves
    /// 
    (bool moveMade, int[,] state) ComputerTurn(int[,] state)
    {
        // Get best move available
        var move = CalculateMove(state);
        if (move == null)
        {
            // No move can be made
            return (false, state);
        }
        var from = move.Value.from;
        Console.Write($"FROM {from.x} {from.y} ");
        // Continue to make moves until no more valid moves can be made
        while (move != null)
        {
            var to = move.Value.to;
            Console.WriteLine($"TO {to.x} {to.y}");
            state = ApplyMove(state, from, to);
            if (!IsJumpMove(from, to))
                break;
    
            // check for double / triple / etc. jump
            var possibleMoves = new List<((int x, int y) from, (int x, int y) to)>();
            from = to;
            foreach (var candidate in GetPossibleMoves(state, from))
            {
                if (IsJumpMove(from, candidate))
                {
                    possibleMoves.Add((from, candidate));
                }
            }
            // Get best jump move
            move = GetBestMove(state, possibleMoves);
        }
        // apply crowns to any new Kings
        state = CrownKingPieces(state);
        return (true, state);
    }
    #endregion
    
    #region Player Logic
    /// 
    /// Get input from the player in the form "x,y" where x and y are integers
    /// If invalid input is received, return null
    /// If input is valid, return the coordinate of the location
    /// 
    (int x, int y)? GetCoordinate(string prompt)
    {
        Console.Write(prompt + "? ");
        var input = Console.ReadLine();
        // split the string into multiple parts
        var parts = input?.Split(",");
        if (parts?.Length != 2)
            // must be exactly 2 parts
            return null;
        int x;
        if (!int.TryParse(parts[0], out x))
            // first part is not a number
            return null;
        int y;
        if (!int.TryParse(parts[1], out y))
            //second part is not a number
            return null;
    
        return (x, y);
    }
    
    /// 
    /// Get the move from the player.
    /// return a tuple of "from" and "to" representing a valid move
    ///
    /// 
    ((int x, int y) from, (int x,int y) to) GetPlayerMove(int[,] state)
    {
        // The original program has some issues regarding user input
        // 1)  There are minimal data sanity checks in the original:
        //     a)  FROM piece must be owned by player
        //     b)  TO location must be empty
        //     c)  the FROM and TO x's must be less than 2 squares away
        //     d)  the FROM and TO y's must be same distance as x's
        //     No checks are made for direction, if a jump is valid, or
        //     if the piece even moves.
        // 2)  Once a valid FROM is selected, a TO must be selected.
        //     If there are no valid TO locations, you are soft-locked
        // This approach is intentionally different from the original
        // but maintains the original intent as much as possible
        // 1)  Select a FROM location
        // 2)  If FROM is invalid, return to step 1
        // 3)  Select a TO location
        // 4)  If TO is invalid or the implied move is invalid,
        //     return to step 1
    
    
        // There is still currently no way for the player to indicate that no move can be made
        // This matches the original logic, but is a candidate for a refactor
    
        do
        {
            var from = GetCoordinate("FROM");
            if ((from != null)
                && !IsOutOfBounds(from.Value)
                && (state[from.Value.x, from.Value.y] > 0))
            {
                // we have a valid "from" location
                var to = GetCoordinate("TO");
                if ((to != null)
                    && !IsOutOfBounds(to.Value)
                    && IsValidPlayerMove(state, from.Value, to.Value))
                {
                    // we have a valid "to" location
                    return (from.Value, to.Value);
                }
            }
        } while (true);
    }
    
    /// 
    /// Get a subsequent jump from the player if they can / want to
    /// returns a move ("from", "to") if a player jumps
    /// returns null if a player does not make another move
    /// The player must input negative numbers for the coordinates to indicate
    /// that no more moves are to be made.  This matches the original implementation
    /// 
    ((int x, int y) from, (int x, int y) to)? GetPlayerSubsequentJump(int[,] state, (int x, int y) from)
    {
        do
        {
            var to = GetCoordinate("+TO");
            if ((to != null)
                && !IsOutOfBounds(to.Value)
                && IsValidPlayerMove(state, from, to.Value)
                && IsJumpMove(from, to.Value))
            {
                // we have a valid "to" location
                return (from, to.Value); ;
            }
    
            if (to != null && to.Value.x < 0 && to.Value.y < 0)
            {
                // player has indicated to not make any more moves
                return null;
            }
        }
        while (true);
    }
    
    /// 
    /// The logic behind the Player's turn
    /// Get the player input for a move
    /// Get subsequent jumps, if possible
    /// 
    int [,] PlayerTurn(int[,] state)
    {
        var move = GetPlayerMove(state);
        do
        {
            state = ApplyMove(state, move.from, move.to);
            if (!IsJumpMove(move.from, move.to))
            {
                // If player doesn't make a jump move, no further moves are possible
                break;
            }
            var nextMove = GetPlayerSubsequentJump(state, move.to);
            if (nextMove == null)
            {
                // another jump is not made
                break;
            }
            move = nextMove.Value;
        }
        while (true);
        // check to see if any kings need crowning
        state = CrownKingPieces(state);
        return state;
    }
    #endregion
    
    /*****************************************************************************
     *
     * Main program starts here
     *
     ****************************************************************************/
    
    WriteIntroduction();
    
    // initalize state -  empty spots initialize to 0
    // set player pieces to 1, computer pieces to -1
    // turn your head to the right to visualize the board.
    // kings will be represented by -2 (for computer) and 2 (for player)
    int[,] state = new int[8, 8] {
        { 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 },
    };
    
    while (true)
    {
        bool moveMade;
        (moveMade, state) = ComputerTurn(state);
        if (!moveMade)
        {
            // In the original program the computer wins if it cannot make a move
            // I believe the player should win in this case, assuming the player can make a move.
            // if neither player can make a move, the game should be draw.
            // I have left it as the original logic for now.
            ComputerWins();
            break;
        }
        PrintBoard(state);
        if (CheckForComputerWin(state))
        {
            ComputerWins();
            break;
        }
        state = PlayerTurn(state);
        if (CheckForPlayerWin(state))
        {
            PlayerWins();
            break;
        }
    }
    
    
    ================================================
    FILE: 23_Checkers/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 23_Checkers/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 23_Checkers/javascript/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells)
    
    
    ================================================
    FILE: 23_Checkers/javascript/checkers.html
    ================================================
    
    
    CHECKERS
    
    
    
    
    
    
    
    
    
    
    ================================================
    FILE: 23_Checkers/javascript/checkers.js
    ================================================
    // CHECKERS
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    // x,y = origin square
    // a,b = movement direction
    function try_computer()
    {
        u = x + a;
        v = y + b;
        if (u < 0 || u > 7 || v < 0 || v > 7)
            return;
        if (s[u][v] == 0) {
            eval_move();
            return;
        }
        if (s[u][v] < 0)	// Cannot jump over own pieces
            return;
        u += a;
        u += b;
        if (u < 0 || u > 7 || v < 0 || v > 7)
            return;
        if (s[u][v] == 0)
            eval_move();
    }
    
    // x,y = origin square
    // u,v = target square
    function eval_move()
    {
        if (v == 0 && s[x][y] == -1)
            q += 2;
        if (Math.abs(y - v) == 2)
            q += 5;
        if (y == 7)
            q -= 2;
        if (u == 0 || u == 7)
            q++;
        for (c = -1; c <= 1; c += 2) {
            if (u + c < 0 || u + c > 7 || v + g < 0)
                continue;
            if (s[u + c][v + g] < 0) {	// Computer piece
                q++;
                continue;
            }
            if (u - c < 0 || u - c > 7 || v - g > 7)
                continue;
            if (s[u + c][v + g] > 0 && (s[u - c][v - g] == 0 || (u - c == x && v - g == y)))
                q -= 2;
        }
        if (q > r[0]) {	// Best movement so far?
            r[0] = q;	// Take note of score
            r[1] = x;	// Origin square
            r[2] = y;
            r[3] = u;	// Target square
            r[4] = v;
        }
        q = 0;
    }
    
    function more_captures() {
        u = x + a;
        v = y + b;
        if (u < 0 || u > 7 || v < 0 || v > 7)
            return;
        if (s[u][v] == 0 && s[x + a / 2][y + b / 2] > 0)
            eval_move();
    }
    
    var r = [-99, 0, 0, 0, 0];
    var s = [];
    
    for (x = 0; x <= 7; x++)
        s[x] = [];
    
    var g = -1;
    var data = [1, 0, 1, 0, 0, 0, -1, 0, 0, 1, 0, 0, 0, -1, 0, -1, 15];
    var p = 0;
    var q = 0;
    
    // Main program
    async function main()
    {
        print(tab(32) + "CHECKERS\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\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("\n");
        print("\n");
        print("\n");
        for (x = 0; x <= 7; x++) {
            for (y = 0; y <= 7; y++) {
                if (data[p] == 15)
                    p = 0;
                s[x][y] = data[p];
                p++;
            }
        }
        while (1) {
    
            // Search the board for the best movement
            for (x = 0; x <= 7; x++) {
                for (y = 0; y <= 7; y++) {
                    if (s[x][y] > -1)
                        continue;
                    if (s[x][y] == -1) {	// Piece
                        for (a = -1; a <= 1; a += 2) {
                            b = g;	// Only advances
                            try_computer();
                        }
                    } else if (s[x][y] == -2) {	// King
                        for (a = -1; a <= 1; a += 2) {
                            for (b = -1; b <= 1; b += 2) {
                                try_computer();
                            }
                        }
                    }
                }
            }
            if (r[0] == -99) {
                print("\n");
                print("YOU WIN.\n");
                break;
            }
            print("FROM " + r[1] + "," + r[2] + " TO " + r[3] + "," + r[4]);
            r[0] = -99;
            while (1) {
                if (r[4] == 0) {	// Computer reaches the bottom
                    s[r[3]][r[4]] = -2;	// King
                    break;
                }
                s[r[3]][r[4]] = s[r[1]][r[2]];	// Move
                s[r[1]][r[2]] = 0;
                if (Math.abs(r[1] - r[3]) == 2) {
                    s[(r[1] + r[3]) / 2][(r[2] + r[4]) / 2] = 0;	// Capture
                    x = r[3];
                    y = r[4];
                    if (s[x][y] == -1) {
                        b = -2;
                        for (a = -2; a <= 2; a += 4) {
                            more_captures();
                        }
                    } else if (s[x][y] == -2) {
                        for (a = -2; a <= 2; a += 4) {
                            for (b = -2; b <= 2; b += 4) {
                                more_captures();
                            }
                        }
                    }
                    if (r[0] != -99) {
                        print(" TO " + r[3] + "," + r[4]);
                        r[0] = -99;
                        continue;
                    }
                }
                break;
            }
            print("\n");
            print("\n");
            print("\n");
            for (y = 7; y >= 0; y--) {
                str = "";
                for (x = 0; x <= 7; x++) {
                    if (s[x][y] == 0)
                        str += ".";
                    if (s[x][y] == 1)
                        str += "O";
                    if (s[x][y] == -1)
                        str += "X";
                    if (s[x][y] == -2)
                        str += "X*";
                    if (s[x][y] == 2)
                        str += "O*";
                    while (str.length % 5)
                        str += " ";
                }
                print(str + "\n");
                print("\n");
            }
            print("\n");
            z = 0;
            t = 0;
            for (l = 0; l <= 7; l++) {
                for (m = 0; m <= 7; m++) {
                    if (s[l][m] == 1 || s[l][m] == 2)
                        z = 1;
                    if (s[l][m] == -1 || s[l][m] == -2)
                        t = 1;
                }
            }
            if (z != 1) {
                print("\n");
                print("I WIN.\n");
                break;
            }
            if (t != 1) {
                print("\n");
                print("YOU WIN.\n");
                break;
            }
            do {
                print("FROM");
                e = await input();
                h = parseInt(e.substr(e.indexOf(",") + 1));
                e = parseInt(e);
                x = e;
                y = h;
            } while (s[x][y] <= 0) ;
            do {
                print("TO");
                a = await input();
                b = parseInt(a.substr(a.indexOf(",") + 1));
                a = parseInt(a);
                x = a;
                y = b;
                if (s[x][y] == 0 && Math.abs(a - e) <= 2 && Math.abs(a - e) == Math.abs(b - h))
                    break;
                print("WHAT?\n");
            } while (1) ;
            i = 46;
            do {
                s[a][b] = s[e][h]
                s[e][h] = 0;
                if (Math.abs(e - a) != 2)
                    break;
                s[(e + a) / 2][(h + b) / 2] = 0;
                while (1) {
                    print("+TO");
                    a1 = await input();
                    b1 = parseInt(a1.substr(a1.indexOf(",") + 1));
                    a1 = parseInt(a1);
                    if (a1 < 0)
                        break;
                    if (s[a1][b1] == 0 && Math.abs(a1 - a) == 2 && Math.abs(b1 - b) == 2)
                        break;
                }
                if (a1 < 0)
                    break;
                e = a;
                h = b;
                a = a1;
                b = b1;
                i += 15;
            } while (1);
            if (b == 7)	// Player reaches top
                s[a][b] = 2;	// Convert to king
        }
    }
    
    main();
    
    
    ================================================
    FILE: 23_Checkers/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 23_Checkers/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 23_Checkers/perl/README.md
    ================================================
    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.
    
    
    ================================================
    FILE: 23_Checkers/perl/checkers.pl
    ================================================
    #!/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);
    }
    
    
    ================================================
    FILE: 23_Checkers/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 23_Checkers/python/checkers.py
    ================================================
    """
    CHECKERS
    
    How about a nice game of checkers?
    
    Ported by Dave LeCompte
    """
    
    from typing import Iterator, NamedTuple, Optional, Tuple
    
    PAGE_WIDTH = 64
    
    HUMAN_PLAYER = 1
    COMPUTER_PLAYER = -1
    HUMAN_PIECE = 1
    HUMAN_KING = 2
    COMPUTER_PIECE = -1
    COMPUTER_KING = -2
    EMPTY_SPACE = 0
    
    TOP_ROW = 7
    BOTTOM_ROW = 0
    
    
    class MoveRecord(NamedTuple):
        quality: int
        start_x: int
        start_y: int
        dest_x: int
        dest_y: int
    
    
    def print_centered(msg: str) -> None:
        spaces = " " * ((PAGE_WIDTH - len(msg)) // 2)
        print(spaces + msg)
    
    
    def print_header(title: str) -> None:
        print_centered(title)
        print_centered("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n\n\n")
    
    
    def get_coordinates(prompt: str) -> Tuple[int, int]:
        err_msg = "ENTER COORDINATES in X,Y FORMAT"
        while True:
            print(prompt)
            response = input()
            if "," not in response:
                print(err_msg)
                continue
    
            try:
                x, y = (int(c) for c in response.split(","))
            except ValueError:
                print(err_msg)
                continue
    
            return x, y
    
    
    def is_legal_board_coordinate(x: int, y: int) -> bool:
        return (0 <= x <= 7) and (0 <= y <= 7)
    
    
    class Board:
        def __init__(self) -> None:
            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
                    self.spaces[x][2] = HUMAN_PIECE
                    self.spaces[x][0] = HUMAN_PIECE
                else:
                    self.spaces[x][7] = COMPUTER_PIECE
                    self.spaces[x][5] = COMPUTER_PIECE
                    self.spaces[x][1] = HUMAN_PIECE
    
        def __str__(self) -> str:
            pieces = {
                EMPTY_SPACE: ".",
                HUMAN_PIECE: "O",
                HUMAN_KING: "O*",
                COMPUTER_PIECE: "X",
                COMPUTER_KING: "X*",
            }
    
            s = "\n\n\n"
            for y in range(7, -1, -1):
                for x in range(0, 8):
                    piece_str = pieces[self.spaces[x][y]]
                    piece_str += " " * (5 - len(piece_str))
                    s += piece_str
                s += "\n"
            s += "\n\n"
    
            return s
    
        def get_spaces(self) -> Iterator[Tuple[int, int]]:
            for x in range(0, 8):
                for y in range(0, 8):
                    yield x, y
    
        def get_spaces_with_computer_pieces(self) -> Iterator[Tuple[int, int]]:
            for x, y in self.get_spaces():
                contents = self.spaces[x][y]
                if contents < 0:
                    yield x, y
    
        def get_spaces_with_human_pieces(self) -> Iterator[Tuple[int, int]]:
            for x, y in self.get_spaces():
                contents = self.spaces[x][y]
                if contents > 0:
                    yield x, y
    
        def get_legal_deltas_for_space(self, x: int, y: int) -> Iterator[Tuple[int, int]]:
            contents = self.spaces[x][y]
            for delta_x in (-1, 1):
                if contents == COMPUTER_PIECE:
                    yield (delta_x, -1)
                else:
                    for delta_y in (-1, 1):
                        yield (delta_x, delta_y)
    
        def get_legal_moves(self, x: int, y: int) -> Iterator[MoveRecord]:
            for delta_x, delta_y in self.get_legal_deltas_for_space(x, y):
                new_move_record = self.check_move(x, y, delta_x, delta_y)
    
                if new_move_record is not None:
                    yield new_move_record
    
        def pick_computer_move(self) -> Optional[MoveRecord]:
            move_record = None
    
            for start_x, start_y in self.get_spaces_with_computer_pieces():
                for delta_x, delta_y in self.get_legal_deltas_for_space(start_x, start_y):
                    new_move_record = self.check_move(start_x, start_y, delta_x, delta_y)
    
                    if new_move_record is None:
                        continue
    
                    if (move_record is None) or (
                        new_move_record.quality > move_record.quality
                    ):
                        move_record = new_move_record
    
            return move_record
    
        def check_move(
            self, start_x: int, start_y: int, delta_x: int, delta_y: int
        ) -> Optional[MoveRecord]:
            new_x = start_x + delta_x
            new_y = start_y + delta_y
            if not is_legal_board_coordinate(new_x, new_y):
                return None
    
            contents = self.spaces[new_x][new_y]
            if contents == EMPTY_SPACE:
                return self.evaluate_move(start_x, start_y, new_x, new_y)
            if contents < 0:
                return None
    
            # check jump landing space, which is an additional dx, dy from new_x, newy
            landing_x = new_x + delta_x
            landing_y = new_y + delta_y
    
            if not is_legal_board_coordinate(landing_x, landing_y):
                return None
            if self.spaces[landing_x][landing_y] == EMPTY_SPACE:
                return self.evaluate_move(start_x, start_y, landing_x, landing_y)
            return None
    
        def evaluate_move(
            self, start_x: int, start_y: int, dest_x: int, dest_y: int
        ) -> MoveRecord:
            quality = 0
            if dest_y == 0 and self.spaces[start_x][start_y] == COMPUTER_PIECE:
                # promoting is good
                quality += 2
            if abs(dest_y - start_y) == 2:
                # jumps are good
                quality += 5
            if start_y == 7:
                # prefer to defend back row
                quality -= 2
            if dest_x in {0, 7}:
                # moving to edge column
                quality += 1
            for delta_x in (-1, 1):
                if not is_legal_board_coordinate(dest_x + delta_x, dest_y - 1):
                    continue
    
                if self.spaces[dest_x + delta_x][dest_y - 1] < 0:
                    # moving into "shadow" of another computer piece
                    quality += 1
    
                if not is_legal_board_coordinate(dest_x - delta_x, dest_y + 1):
                    continue
    
                if (
                    (self.spaces[dest_x + delta_x][dest_y - 1] > 0)
                    and (self.spaces[dest_x - delta_x][dest_y + 1] == EMPTY_SPACE)
                    or ((dest_x - delta_x == start_x) and (dest_y + 1 == start_y))
                ):
                    # we are moving up to a human checker that could jump us
                    quality -= 2
            return MoveRecord(quality, start_x, start_y, dest_x, dest_y)
    
        def remove_r_pieces(self, move_record: MoveRecord) -> None:
            self.remove_pieces(
                move_record.start_x,
                move_record.start_y,
                move_record.dest_x,
                move_record.dest_y,
            )
    
        def remove_pieces(
            self, start_x: int, start_y: int, dest_x: int, dest_y: int
        ) -> None:
            self.spaces[dest_x][dest_y] = self.spaces[start_x][start_y]
            self.spaces[start_x][start_y] = EMPTY_SPACE
    
            if abs(dest_x - start_x) == 2:
                mid_x = (start_x + dest_x) // 2
                mid_y = (start_y + dest_y) // 2
                self.spaces[mid_x][mid_y] = EMPTY_SPACE
    
        def play_computer_move(self, move_record: MoveRecord) -> None:
            print(
                f"FROM {move_record.start_x} {move_record.start_y} TO {move_record.dest_x} {move_record.dest_y}"
            )
    
            while True:
                if move_record.dest_y == BOTTOM_ROW:
                    # KING ME
                    self.remove_r_pieces(move_record)
                    self.spaces[move_record.dest_x][move_record.dest_y] = COMPUTER_KING
                    return
                else:
                    self.spaces[move_record.dest_x][move_record.dest_y] = self.spaces[
                        move_record.start_x
                    ][move_record.start_y]
                    self.remove_r_pieces(move_record)
    
                    if abs(move_record.dest_x - move_record.start_x) != 2:
                        return
    
                    landing_x = move_record.dest_x
                    landing_y = move_record.dest_y
    
                    best_move = None
                    if self.spaces[landing_x][landing_y] == COMPUTER_PIECE:
                        for delta_x in (-2, 2):
                            test_record = self.try_extend(landing_x, landing_y, delta_x, -2)
                            if (move_record is not None) and (
                                (best_move is None)
                                or (move_record.quality > best_move.quality)
                            ):
                                best_move = test_record
                    else:
                        assert self.spaces[landing_x][landing_y] == COMPUTER_KING
                        for delta_x in (-2, 2):
                            for delta_y in (-2, 2):
                                test_record = self.try_extend(
                                    landing_x, landing_y, delta_x, delta_y
                                )
                                if (move_record is not None) and (
                                    (best_move is None)
                                    or (move_record.quality > best_move.quality)
                                ):
                                    best_move = test_record
    
                    if best_move is None:
                        return
                    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
        ) -> Optional[MoveRecord]:
            new_x = start_x + delta_x
            new_y = start_y + delta_y
    
            if not is_legal_board_coordinate(new_x, new_y):
                return None
    
            jumped_x = start_x + delta_x // 2
            jumped_y = start_y + delta_y // 2
    
            if (self.spaces[new_x][new_y] == EMPTY_SPACE) and (
                self.spaces[jumped_x][jumped_y] > 0
            ):
                return self.evaluate_move(start_x, start_y, new_x, new_y)
            return None
    
        def get_human_move(self) -> Tuple[int, int, int, int]:
            is_king = False
    
            while True:
                start_x, start_y = get_coordinates("FROM?")
    
                legal_moves = list(self.get_legal_moves(start_x, start_y))
                if not legal_moves:
                    print(f"({start_x}, {start_y}) has no legal moves. Choose again.")
                    continue
                if self.spaces[start_x][start_y] > 0:
                    break
    
            is_king = self.spaces[start_x][start_y] == HUMAN_KING
    
            while True:
                dest_x, dest_y = get_coordinates("TO?")
    
                if (not is_king) and (dest_y < start_y):
                    # CHEATER! Trying to move non-king backwards
                    continue
                is_free = self.spaces[dest_x][dest_y] == 0
                within_reach = abs(dest_x - start_x) <= 2
                is_diagonal_move = abs(dest_x - start_x) == abs(dest_y - start_y)
                if is_free and within_reach and is_diagonal_move:
                    break
            return start_x, start_y, dest_x, dest_y
    
        def get_human_extension(
            self, start_x: int, start_y: int
        ) -> Tuple[bool, Optional[Tuple[int, int, int, int]]]:
            is_king = self.spaces[start_x][start_y] == HUMAN_KING
    
            while True:
                dest_x, dest_y = get_coordinates("+TO?")
    
                if dest_x < 0:
                    return False, None
                if (not is_king) and (dest_y < start_y):
                    # CHEATER! Trying to move non-king backwards
                    continue
                if (
                    (self.spaces[dest_x][dest_y] == EMPTY_SPACE)
                    and (abs(dest_x - start_x) == 2)
                    and (abs(dest_y - start_y) == 2)
                ):
                    return True, (start_x, start_y, dest_x, dest_y)
    
        def play_human_move(
            self, start_x: int, start_y: int, dest_x: int, dest_y: int
        ) -> None:
            self.remove_pieces(start_x, start_y, dest_x, dest_y)
    
            if dest_y == TOP_ROW:
                # KING ME
                self.spaces[dest_x][dest_y] = HUMAN_KING
    
        def check_pieces(self) -> bool:
            if not list(self.get_spaces_with_computer_pieces()):
                print_human_won()
                return False
            if not list(self.get_spaces_with_computer_pieces()):
                print_computer_won()
                return False
            return True
    
    
    def print_instructions() -> None:
        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.\n\n\n")
    
    
    def print_human_won() -> None:
        print("\nYOU WIN.")
    
    
    def print_computer_won() -> None:
        print("\nI WIN.")
    
    
    def play_game() -> None:
        board = Board()
    
        while True:
            move_record = board.pick_computer_move()
            if move_record is None:
                print_human_won()
                return
            board.play_computer_move(move_record)
    
            print(board)
    
            if not board.check_pieces():
                return
    
            start_x, start_y, dest_x, dest_y = board.get_human_move()
            board.play_human_move(start_x, start_y, dest_x, dest_y)
            if abs(dest_x - start_x) == 2:
                while True:
                    extend, move = board.get_human_extension(dest_x, dest_y)
                    assert move is not None
                    if not extend:
                        break
                    start_x, start_y, dest_x, dest_y = move
                    board.play_human_move(start_x, start_y, dest_x, dest_y)
    
    
    def main() -> None:
        print_header("CHECKERS")
        print_instructions()
    
        play_game()
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 23_Checkers/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    This version preserves the underlying algorithms and functionality of
    the original while using more modern programming constructs
    (functions, classes, symbols) and providing much more detailed
    comments.  It also fixes some (but not all) of the bugs.
    
    
    ================================================
    FILE: 23_Checkers/ruby/checkers.rb
    ================================================
    #!/usr/bin/env ruby
    
    # Checkers in Ruby, Version 1
    #
    # This version of the game attempts to preserve the underlying
    # algorithm(s) and feel of the BASIC version while using more modern
    # coding techniques.  Specifically:
    #
    # 1. The major data structures (the board and current move, known as S
    #    and R in the BASIC version) have been turned into classes.  In
    #    addition, I made a class for coordinates so that you don't always
    #    have to deal with pairs of numbers.
    #
    # 2. Much of the functionality associated with this data has been moved
    #    into methods of these classes in line with the philosophy that objects
    #    are smart data.
    #
    # 3. While I've kept the board as a single object (not global, though),
    #    this program will create many Move objects (i.e. copies of the move
    #    under consideration) rather than operating on a single global
    #    instance.
    #
    # 4. The rest of the code has been extracted into Ruby functions with
    #    all variables as local as reasonably possible.
    #
    # 5. Pieces are now represented with Symbols instead of integers; this
    #    made it *much* easier to understand what was going on.
    #
    # 6. There are various internal sanity checks.  They fail by throwing
    #    a string as an exception.  (This is generally frowned upon if
    #    you're going to catch the exception later but we never do that;
    #    an exception here means a bug in the software and the way to fix
    #    that is to fix the program.)
    #
    # And probably other stuff.
    #
    
    
    # Note: I've ordered the various major definitions here from (roughly)
    # general to specific so that if you read the code starting from the
    # beginning, you'll (hopefully) get a big-picture view first and then
    # get into details.  Normally, I'd order things by topic and define
    # things before using them, which is a better ordering.  So in this
    # case, do what I say and not what I do.
    
    
    #
    # Some global constants
    #
    
    BOARD_TEXT_INDENT = 30  # Number of spaces to indent the board when printing
    
    # Various constants related to the game of Checkers.
    #
    # (Yes, they're obvious but if you see BOARD_WIDTH, you know this is
    # related to board dimensions in a way that you wouldn't if you saw
    # '8'.)
    BOARD_WIDTH = 8
    KING_ROW_X = 0
    KING_ROW_Y = BOARD_WIDTH - 1
    
    
    
    # This is the mainline routine of the program.  Ruby doesn't require
    # that you put this in a function but this way, your local variables
    # are contained here.  It's also neater, IMO.
    #
    # The name 'main' isn't special; it's just a function.  The last line
    # of this program is a call to it.
    def main
      print < move.from.y
          return false if !is_me && move.to.y < move.from.y
        end
    
        # If jumping, that there's an opponent's piece between the start
        # and end.
        return false if
          move.jump? &&
          (empty_at?(move.midpoint) || opponent_at?(move.midpoint) != is_me)
    
        # Otherwise, it's legal
        return true
      end
    
      # Perform 'move' on the board.  'move' must be legal; the player
      # performing it is determined by the move's starting ('from')
      # position.
      def make_move!(move)
        piece = self[move.from]
    
        # Sanity check
        raise "Illegal move: #{move}" unless legal_move?(piece.downcase == :x,move)
    
        # Promote the piece if it's reached the end row
        piece = piece.upcase if
          (piece == :x && move.to.y == KING_ROW_X) ||
          (piece == :o && move.to.y == KING_ROW_Y)
    
        # And do the move
        self[move.to] = piece
        self[move.from] = :_
    
        # Remove the piece jumped over if this is a jump
        self[move.midpoint] = :_ if move.jump?
      end
    
    
      # Return the best (i.e. likely to win) move possible for the
      # piece (mine) at 'coord'.
      def bestMoveFrom(coord, mustJump)
        so_far = Move.invalid
        return so_far unless coord.valid?
    
        offsets = [ [-1, -1], [1, -1]]
        offsets += [ [-1, 1], [1, 1]] if king_at?(coord)
    
        for ofx, ofy in offsets
          new_coord = coord.by(ofx, ofy)
    
          if opponent_at?(new_coord)
            new_coord = new_coord.by(ofx, ofy)
          elsif mustJump
            next
          end
    
          next unless new_coord.valid?
    
          so_far = so_far.betterOf( ratedMove(coord, new_coord) )
        end
    
        return so_far
      end
    
    
      # Create and return a move for *me* from Coords 'from' to 'to' with
      # its 'rating' set to how good the move looks according to criteria
      # used by the BASIC version of this program.  If the move is
      # illegal, returns an invalid Move object.
      def ratedMove(from, to)
        return Move.invalid unless legal_move?(true, Move.new(from, to))
    
        rating = 0
    
        # +2 if it promotes this piece
        rating += 2 if to.y == 0
    
        # +50 if it takes the opponent's piece.  (Captures are mandatory
        # so we ensure that a capture will always outrank a non-capture.)
        rating += 4 if (from.y - to.y).abs == 2
    
        # -2 if we're moving away from the king row
        rating -= 2 if from.y == BOARD_WIDTH - 1
    
        # +1 for putting the piece against a vertical boundary
        rating += 1 if to.x == 0 || to.x == BOARD_WIDTH - 1
    
        # +1 for each friendly piece behind this one
        [-1, 1].each {|c|
          rating += 1 if mine_at?( to.by(c, -1) )
        }
    
        # -2 for each opponent's piece that can now capture this one.
        # (This includes a piece that may be captured when moving here;
        # this is a bug.)
        [ -1, 1].each {|c|
          there = to.by(c, -1)
          opposite = to.by(-c, 1)
          rating -= 2 if
            opponent_at?(there) && (empty_at?(opposite) || opposite == from)
        }
    
        return Move.new(from, to, rating)
      end
    end
    
    
    # Class to hold the X and Y coordinates of a position on the board.
    #
    # Coord objects are immutable--that is, they never change after
    # creation.  Instead, you will always get a modified copy back.
    class Coord
    
      # Coordinates are readable
      attr_reader :x, :y
    
      # Initialize
      def initialize(x, y)
        @x, @y = [x,y]
      end
    
      # Test if this move is on the board.
      def valid?
        return x >= 0 && y >= 0 && x < BOARD_WIDTH && y < BOARD_WIDTH
      end
    
      # Test if this Coord is equal to another Coord.
      def ==(other)
        return other.class == self.class && other.x == @x && other.y == y
      end
    
      # Return a string that describes this Coord in a human-friendly way.
      def to_s
        return "(#{@x},#{@y})"
      end
    
      # Return a new Coord whose x and y coordinates have been adjusted by
      # arguments 'x' and 'y'.
      def by(x, y)
        return Coord.new(@x + x, @y + y)
      end
    end
    
    
    # Class to represent a move by a player between two positions,
    # possibly with a rating that can be used to select the best of a
    # collection of moves.
    #
    # An (intentionally) invalid move will have a value of nil for both
    # 'from' and 'to'.  Most methods other than 'valid?' assume the Move
    # is valid.
    class Move
      # Readable fields:
      attr_reader :from, :to, :rating
    
      # The initializer; -99 is the lowest rating from the BASIC version
      # so we use that here as well.
      def initialize(from, to, rating = -99)
        @from, @to, @rating = [from, to, rating]
    
        # Sanity check; the only invalid Move tolerated is the official
        # one (i.e. with nil for each endpoint.)
        raise "Malformed Move: #{self}" if @from && @to && !valid?
      end
    
      # Return an invalid Move object.
      def self.invalid
        return self.new(nil, nil, -99)
      end
    
      # Return true if this is a valid move (i.e. as close to legal as we
      # can determine without seeing the board.)
      def valid?
        # Not valid if @from or @to is nil
        return false unless @from && @to
    
        # Not valid unless both endpoints are on the board
        return false unless @from.valid? && @to.valid?
    
        # Not valid unless it's a diagonal move by 1 or 2 squares
        dx, dy = delta
        return false if dx.abs != dy.abs || (dx.abs != 1 && dx.abs != 2)
    
        # Otherwise, valid
        return true
      end
    
      # Return true if this move is a jump, false otherwise
      def jump?
        return valid? && magnitude() == 2
      end
    
      # Return the coordinates of the piece being jumped over by this
      # move.
      def midpoint
        raise "Called 'midpoint' on a non-jump move!" unless jump?
        dx, dy = delta
        return @from.by(dx / dx.abs, dy / dy.abs)
      end
    
      # Return the better-rated of self or otherMove.
      def betterOf(otherMove)
        return otherMove if !valid?
        return rating > otherMove.rating ? self : otherMove
      end
    
      # Return a human-friendly string representing this move.
      def to_s
        return "[NOMOVE]" if !@from && !@to     # Well-known invalid move
    
        jumpover = jump? ?
                     "-> #{midpoint} ->"
                   : "->"
    
        return "#{@from} #{jumpover} #{to}#{valid? ? '' : ' [INVALID]'}"
      end
    
      private
    
      # Return the distance (x, y) between the 'from' and 'to' locations.
      def delta
        return [to.x - from.x, to.y - from.y]
      end
    
      # Return the number of squares this move will take the piece (either
      # 1 or 2).
      def magnitude
        # Note: we assume that this move is a legal move (and therefore
        # diagonal); otherwise, this may not be correct.
        return (to.x - from.x).abs
      end
    end
    
    
    
    # Start the game
    main()
    
    
    ================================================
    FILE: 23_Checkers/vbnet/Checkers.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Checkers", "Checkers.vbproj", "{92B871EF-4871-40C0-ADDE-406301B937B9}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{92B871EF-4871-40C0-ADDE-406301B937B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{92B871EF-4871-40C0-ADDE-406301B937B9}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{92B871EF-4871-40C0-ADDE-406301B937B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{92B871EF-4871-40C0-ADDE-406301B937B9}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 23_Checkers/vbnet/Checkers.vbproj
    ================================================
    
      
        Exe
        Checkers
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 23_Checkers/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 24_Chemist/README.md
    ================================================
    ### Chemist
    
    The fictitious chemical, kryptocyanic acid, can only be diluted by the ratio of 7 parts water to 3 parts acid. Any other ratio causes an unstable compound which soon explodes. Given an amount of acid, you must determine how much water to add to the dilution. If you’re more than 5% off, you lose one of your nine lives. The program continues to play until you lose all nine lives or until it is interrupted.
    
    It was originally written by Wayne Teeter of Ridgecrest, California.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=42)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=57)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### 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)
    
    #### External Links
     - C: https://github.com/ericfischer/basic-computer-games/blob/main/24%20Chemist/c/chemist.c
    
    
    ================================================
    FILE: 24_Chemist/chemist.bas
    ================================================
    3 PRINT TAB(33);"CHEMIST"
    6 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    8 PRINT:PRINT:PRINT
    10 PRINT "THE FICTITIOUS CHEMICAL KRYPTOCYANIC ACID CAN ONLY BE"
    20 PRINT "DILUTED BY THE RATIO OF 7 PARTS WATER TO 3 PARTS ACID."
    30 PRINT "IF ANY OTHER RATIO IS ATTEMPTED, THE ACID BECOMES UNSTABLE"
    40 PRINT "AND SOON EXPLODES.  GIVEN THE AMOUNT OF ACID, YOU MUST"
    50 PRINT "DECIDE WHO MUCH WATER TO ADD FOR DILUTION.  IF YOU MISS"
    60 PRINT "YOU FACE THE CONSEQUENCES."
    100 A=INT(RND(1)*50)
    110 W=7*A/3
    120 PRINT A;"LITERS OF KRYPTOCYANIC ACID.  HOW MUCH WATER";
    130 INPUT R
    140 D=ABS(W-R)
    150 IF D>W/20 THEN 200
    160 PRINT " GOOD JOB! YOU MAY BREATHE NOW, BUT DON'T INHALE THE FUMES!"
    170 PRINT
    180 GOTO 100
    200 PRINT " SIZZLE!  YOU HAVE JUST BEEN DESALINATED INTO A BLOB"
    210 PRINT " OF QUIVERING PROTOPLASM!"
    220 T=T+1
    230 IF T=9 THEN 260
    240 PRINT " HOWEVER, YOU MAY TRY AGAIN WITH ANOTHER LIFE."
    250 GOTO 100
    260 PRINT " YOUR 9 LIVES ARE USED, BUT YOU WILL BE LONG REMEMBERED FOR"
    270 PRINT " YOUR CONTRIBUTIONS TO THE FIELD OF COMIC BOOK CHEMISTRY."
    280 END
    
    
    ================================================
    FILE: 24_Chemist/csharp/Chemist.csproj
    ================================================
    
    
      
        Exe
        net5.0
      
    
    
    
    
    ================================================
    FILE: 24_Chemist/csharp/Chemist.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.31005.135
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Chemist", "Chemist.csproj", "{C16545E8-E078-4C69-B7CA-9D821C944252}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{C16545E8-E078-4C69-B7CA-9D821C944252}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{C16545E8-E078-4C69-B7CA-9D821C944252}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{C16545E8-E078-4C69-B7CA-9D821C944252}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{C16545E8-E078-4C69-B7CA-9D821C944252}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {4AFDA581-82B1-42C7-9C9C-26F6B4288584}
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 24_Chemist/csharp/Program.cs
    ================================================
    using System;
    const int maxLives = 9;
    
    WriteCentred("Chemist");
    WriteCentred("Creative Computing, Morristown, New Jersey");
    Console.WriteLine(@"
    
    
    The fictitious chemical kryptocyanic acid can only be
    diluted by the ratio of 7 parts water to 3 parts acid.
    If any other ratio is attempted, the acid becomes unstable
    and soon explodes.  Given the amount of acid, you must
    decide who much water to add for dilution.  If you miss
    you face the consequences.
    ");
    
    var random = new Random();
    int livesUsed = 0;
    while (livesUsed < maxLives)
    {
        int krypto = random.Next(1, 50);
        double water = krypto * 7.0 / 3.0;
    
        Console.WriteLine($"{krypto} Liters of kryptocyanic acid.  How much water?");
        double answer = double.Parse(Console.ReadLine());
    
        double diff = Math.Abs(answer - water);
        if (diff <= water / 20)
        {
            Console.WriteLine("Good job! You may breathe now, but don't inhale the fumes"!);
            Console.WriteLine();
        }
        else
        {
            Console.WriteLine("Sizzle!  You have just been desalinated into a blob\nof quivering protoplasm!");
            Console.WriteLine();
            livesUsed++;
    
            if (livesUsed < maxLives)
                Console.WriteLine("However, you may try again with another life.");
        }
    }
    Console.WriteLine($"Your {maxLives} lives are used, but you will be long remembered for\nyour contributions to the field of comic book chemistry.");
    
    static void WriteCentred(string text)
    {
        int indent = (Console.WindowWidth + text.Length) / 2;
        Console.WriteLine($"{{0,{indent}}}", text);
    }
    
    
    ================================================
    FILE: 24_Chemist/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 24_Chemist/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 24_Chemist/java/src/Chemist.java
    ================================================
    import java.util.Arrays;
    import java.util.Scanner;
    
    /**
     * Game of Chemist
     * 

    * Based on the Basic game of Chemist here * https://github.com/coding-horror/basic-computer-games/blob/main/24%20Chemist/chemist.bas *

    * Note: The idea was to create a version of the 1970's Basic game in Java, without introducing * new features - no additional text, error checking, etc has been added. */ public class Chemist { public static final int MAX_LIVES = 9; // Used for keyboard input private final Scanner kbScanner; private enum GAME_STATE { START_GAME, INPUT, BLOWN_UP, SURVIVED, GAME_OVER } // Current game state private GAME_STATE gameState; private int timesBlownUp; public Chemist() { kbScanner = new Scanner(System.in); gameState = GAME_STATE.START_GAME; } /** * Main game loop */ public void play() { do { switch (gameState) { case START_GAME: intro(); timesBlownUp = 0; gameState = GAME_STATE.INPUT; break; case INPUT: int amountOfAcid = (int) (Math.random() * 50); int correctAmountOfWater = (7 * amountOfAcid) / 3; int water = displayTextAndGetNumber(amountOfAcid + " LITERS OF KRYPTOCYANIC ACID. HOW MUCH WATER? "); // Calculate if the player mixed enough water int result = Math.abs(correctAmountOfWater - water); // Ratio of water wrong? if (result > (correctAmountOfWater / 20)) { gameState = GAME_STATE.BLOWN_UP; } else { // Got the ratio correct gameState = GAME_STATE.SURVIVED; } break; case BLOWN_UP: System.out.println(" SIZZLE! YOU HAVE JUST BEEN DESALINATED INTO A BLOB"); System.out.println(" OF QUIVERING PROTOPLASM!"); timesBlownUp++; if (timesBlownUp < MAX_LIVES) { System.out.println(" HOWEVER, YOU MAY TRY AGAIN WITH ANOTHER LIFE."); gameState = GAME_STATE.INPUT; } else { System.out.println(" YOUR " + MAX_LIVES + " LIVES ARE USED, BUT YOU WILL BE LONG REMEMBERED FOR"); System.out.println(" YOUR CONTRIBUTIONS TO THE FIELD OF COMIC BOOK CHEMISTRY."); gameState = GAME_STATE.GAME_OVER; } break; case SURVIVED: System.out.println(" GOOD JOB! YOU MAY BREATHE NOW, BUT DON'T INHALE THE FUMES!"); System.out.println(); gameState = GAME_STATE.INPUT; break; } } while (gameState != GAME_STATE.GAME_OVER); } private void intro() { System.out.println(simulateTabs(33) + "CHEMIST"); System.out.println(simulateTabs(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println(); System.out.println("THE FICTITIOUS CHEMICAL KRYPTOCYANIC ACID CAN ONLY BE"); System.out.println("DILUTED BY THE RATIO OF 7 PARTS WATER TO 3 PARTS ACID."); System.out.println("IF ANY OTHER RATIO IS ATTEMPTED, THE ACID BECOMES UNSTABLE"); System.out.println("AND SOON EXPLODES. GIVEN THE AMOUNT OF ACID, YOU MUST"); System.out.println("DECIDE WHO MUCH WATER TO ADD FOR DILUTION. IF YOU MISS"); System.out.println("YOU FACE THE CONSEQUENCES."); } /* * Print a message on the screen, then accept input from Keyboard. * Converts input to an Integer * * @param text message to be displayed on screen. * @return what was typed by the player. */ private int displayTextAndGetNumber(String text) { return Integer.parseInt(displayTextAndGetInput(text)); } /* * Print a message on the screen, then accept input from Keyboard. * * @param text message to be displayed on screen. * @return what was typed by the player. */ private String displayTextAndGetInput(String text) { System.out.print(text); return kbScanner.next(); } /** * Simulate the old basic tab(xx) command which indented text by xx spaces. * * @param spaces number of spaces required * @return String with number of spaces */ private String simulateTabs(int spaces) { char[] spacesTemp = new char[spaces]; Arrays.fill(spacesTemp, ' '); return new String(spacesTemp); } } ================================================ FILE: 24_Chemist/java/src/ChemistGame.java ================================================ public class ChemistGame { public static void main(String[] args) { Chemist chemist = new Chemist(); chemist.play(); } } ================================================ FILE: 24_Chemist/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 24_Chemist/javascript/chemist.html ================================================ CHEMIST

    
    
    
    
    
    
    ================================================
    FILE: 24_Chemist/javascript/chemist.js
    ================================================
    // CHEMIST
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    // Main program
    async function main()
    {
        print(tab(33) + "CHEMIST\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        print("THE FICTITIOUS CHECMICAL KRYPTOCYANIC ACID CAN ONLY BE\n");
        print("DILUTED BY THE RATIO OF 7 PARTS WATER TO 3 PARTS ACID.\n");
        print("IF ANY OTHER RATIO IS ATTEMPTED, THE ACID BECOMES UNSTABLE\n");
        print("AND SOON EXPLODES.  GIVEN THE AMOUNT OF ACID, YOU MUST\n");
        print("DECIDE WHO MUCH WATER TO ADD FOR DILUTION.  IF YOU MISS\n");
        print("YOU FACE THE CONSEQUENCES.\n");
        t = 0;
        while (1) {
            a = Math.floor(Math.random() * 50);
            w = 7 * a / 3;
            print(a + " LITERS OF KRYPTOCYANIC ACID.  HOW MUCH WATER");
            r = parseFloat(await input());
            d = Math.abs(w - r);
            if (d > w / 20) {
                print(" SIZZLE!  YOU HAVE JUST BEEN DESALINATED INTO A BLOB\n");
                print(" OF QUIVERING PROTOPLASM!\n");
                t++;
                if (t == 9)
                    break;
                print(" HOWEVER, YOU MAY TRY AGAIN WITH ANOTHER LIFE.\n");
            } else {
                print(" GOOD JOB! YOU MAY BREATHE NOW, BUT DON'T INHALE THE FUMES!\n");
                print("\n");
            }
        }
        print(" YOUR 9 LIVES ARE USED, BUT YOU WILL BE LONG REMEMBERED FOR\n");
        print(" YOUR CONTRIBUTIONS TO THE FIELD OF COMIC BOOK CHEMISTRY.\n");
    }
    
    main();
    
    
    ================================================
    FILE: 24_Chemist/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 24_Chemist/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 24_Chemist/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 24_Chemist/perl/chemist.pl
    ================================================
    #!/usr/bin/perl
    use strict;
    
    print ' 'x33 . "CHEMIST\n";
    print ' 'x15 . "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n";
    print "\n"; print "\n"; print "\n";
    print "THE FICTITIOUS CHECMICAL KRYPTOCYANIC ACID CAN ONLY BE\n";
    print "DILUTED BY THE RATIO OF 7 PARTS WATER TO 3 PARTS ACID.\n";
    print "IF ANY OTHER RATIO IS ATTEMPTED, THE ACID BECOMES UNSTABLE\n";
    print "AND SOON EXPLODES. GIVEN THE AMOUNT OF ACID, YOU MUST\n";
    print "DECIDE HOW MUCH WATER TO ADD FOR DILUTION. IF YOU MISS\n";
    print "YOU FACE THE CONSEQUENCES.\n";
    
    my $T=0;
    while ($T<9) {
    	my $A= int(rand(50) + 1);
    	my $W= 7*$A/3;
    	print " $A LITERS OF KRYPTOCYANIC ACID. HOW MUCH WATER? ";
    	chomp(my $R = );
    	my $D= abs($W-$R);
    	if ($D>$W/20) {
    		print "SIZZLE! YOU HAVE JUST BEEN DESALINATED INTO A BLOB\n";
    		print "OF QUIVERING PROTOPLASM!\n";
    		print "HOWEVER, YOU MAY TRY AGAIN WITH ANOTHER LIFE.\n";
    		print "\n";
    		$T++;
    	} else {
    		print "GOOD JOB! YOU MAY BREATHE NOW, BUT DON'T INHALE THE FUMES!\n";
    		print "\n";
    	}
    }
    
    print "YOUR 9 LIVES ARE USED, BUT YOU WILL BE LONG REMEMBERED FOR\n";
    print "YOUR CONTRIBUTIONS TO THE FIELD OF COMIC BOOK CHEMISTRY.\n";
    exit;
    
    
    ================================================
    FILE: 24_Chemist/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 24_Chemist/python/chemist.py
    ================================================
    """
    CHEMIST
    
    A math game posing as a chemistry word problem.
    
    Ported by Dave LeCompte
    """
    
    import random
    
    MAX_LIVES = 9
    
    
    def play_scenario() -> bool:
        acid_amount = random.randint(1, 50)
    
        water_amount = 7 * acid_amount / 3
    
        print(f"{acid_amount} LITERS OF KRYPTOCYANIC ACID.  HOW MUCH WATER?")
    
        response = float(input())
    
        difference = abs(water_amount - response)
    
        acceptable_difference = water_amount / 20
    
        if difference > acceptable_difference:
            show_failure()
    
            return False
        else:
            show_success()
    
            return True
    
    
    def show_failure() -> None:
        print(" SIZZLE!  YOU HAVE JUST BEEN DESALINATED INTO A BLOB")
        print(" OF QUIVERING PROTOPLASM!")
    
    
    def show_success() -> None:
        print(" GOOD JOB! YOU MAY BREATHE NOW, BUT DON'T INHALE THE FUMES!\n")
    
    
    def show_ending() -> None:
        print(f" YOUR {MAX_LIVES} LIVES ARE USED, BUT YOU WILL BE LONG REMEMBERED FOR")
        print(" YOUR CONTRIBUTIONS TO THE FIELD OF COMIC BOOK CHEMISTRY.")
    
    
    def main() -> None:
        print(" " * 33 + "CHEMIST")
        print(" " * 15 + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n\n\n")
    
        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 WHO MUCH WATER TO ADD FOR DILUTION.  IF YOU MISS")
        print("YOU FACE THE CONSEQUENCES.")
    
        lives_used = 0
    
        while True:
            success = play_scenario()
    
            if not success:
                lives_used += 1
    
                if lives_used == MAX_LIVES:
                    show_ending()
                    return
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 24_Chemist/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 24_Chemist/rust/Cargo.toml
    ================================================
    [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"
    
    
    ================================================
    FILE: 24_Chemist/rust/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Rust](https://www.rust-lang.org/) by Anthony Rubick [AnthonyMichaelTDM](https://github.com/AnthonyMichaelTDM)
    
    
    ================================================
    FILE: 24_Chemist/rust/src/lib.rs
    ================================================
    /*
     lib.rs contains all the logic of the program
    */
    use rand::{Rng, prelude::thread_rng}; //rng
    use std::error::Error; //better errors
    use std::io::{self, Write}; //io interactions
    use std::{str::FromStr, fmt::Display}; //traits
    
    //DATA
    
    /// handles setup for the game
    pub struct Config {
    }
    impl Config {
        /// creates and returns a new Config from user input
        pub fn new() -> Result> {
            //DATA
            let config: Config = Config {
            };
            
            //return new config
            return Ok(config);
        }
    }
    
    /// run the program
    pub fn run(_config: &Config) -> Result<(), Box> {
        //DATA
        let mut rng = thread_rng();
        let mut lives: i8 = 9;
    
        let mut amount_of_acid:i8;
    
        let mut guess:f32;
        let mut answer:f32;
        
        let mut error:f32; 
    
        //Game loop
        loop {
            //initialize variables
            amount_of_acid =  rng.gen_range(1..50);
            answer = 7.0 * (amount_of_acid as f32/3.0);
    
            //print starting message / conditions
            println!();
            //get guess
            guess = loop {
                match get_number_from_input(&format!("{} Liters of Kryptocyanic acid.  How much water? ", amount_of_acid),0.0,-1.0) {
                    Ok(num) => break num,
                    Err(err) => {
                        eprintln!("{}",err);
                        continue;
                    },
                }
            };
    
            //calculate error
            error = (answer as f32 - guess).abs() / guess;
            
            println!("answer: {} | error: {}%", answer,error*100.);
    
            //check guess against answer
            if error > 0.05 { //error > 5%
                println!(" Sizzle!  You may have just been desalinated into a blob");
                println!(" of quivering protoplasm!");
                //update lives
                lives -= 1;
    
                if lives <= 0 {
                    println!(" Your 9 lives are used, but you will be long remembered for");
                    println!(" your contributions to the field of comic book chemistry.");
                    break;
                }
                else {
                    println!(" However, you may try again with another life.")
                } 
            } else {
                println!(" Good job!  You may breathe now, but don't inhale the fumes!");
                println!();
            }
        }
    
        //return to main
        Ok(())
    }
    
    /// gets a string from user input
    fn get_string_from_user_input(prompt: &str) -> Result> {
        //DATA
        let mut raw_input = String::new();
    
        //print prompt
        print!("{}", prompt);
        //make sure it's printed before getting input
        io::stdout().flush().expect("couldn't flush stdout");
    
        //read user input from standard input, and store it to raw_input, then return it or an error as needed
        raw_input.clear(); //clear input
        match io::stdin().read_line(&mut raw_input) {
            Ok(_num_bytes_read) => return Ok(String::from(raw_input.trim())),
            Err(err) => return Err(format!("ERROR: CANNOT READ INPUT!: {}", err).into()),
        }
    }
    /// generic function to get a number from the passed string (user input)
    /// pass a min lower  than the max to have minimum and maximum bounds
    /// pass a min higher than the max to only have a minimum bound
    /// pass a min equal   to  the max to only have a maximum bound
    /// 
    /// Errors:
    /// no number on user input
    fn get_number_from_input(prompt: &str, min:T, max:T) -> Result> {
        //DATA
        let raw_input: String;
        let processed_input: String;
    
        
        //input loop
        raw_input = loop {
            match get_string_from_user_input(prompt) {
                Ok(input) => break input,
                Err(e) => {
                    eprintln!("{}",e);
                    continue;
                },
            }
        };
    
        //filter out non-numeric characters from user input
        processed_input = raw_input.chars().filter(|c| c.is_numeric()).collect();
    
        //from input, try to read a number
        match processed_input.trim().parse() {
            Ok(i) => {
                //what bounds must the input fall into
                if min < max {  //have a min and max bound: [min,max]
                    if i >= min && i <= max {//is input valid, within bounds
                        return Ok(i); //exit the loop with the value i, returning it
                    } else { //print error message specific to this case
                        return Err(format!("ONLY BETWEEN {} AND {}, PLEASE!", min, max).into());
                    } 
                } else if min > max { //only a min bound: [min, infinity)
                    if i >= min {
                        return Ok(i);
                    } else {
                        return Err(format!("NO LESS THAN {}, PLEASE!", min).into());
                    }
                } else { //only a max bound: (-infinity, max]
                    if i <= max {
                        return Ok(i);
                    } else {
                        return Err(format!("NO MORE THAN {}, PLEASE!", max).into());
                    }
                }
            },
            Err(_e) => return Err(format!("Error: couldn't find a valid number in {}",raw_input).into()),
        }
    }
    
    
    ================================================
    FILE: 24_Chemist/rust/src/main.rs
    ================================================
    use std::process;//allows for some better error handling
    
    mod lib; //allows access to lib.rs
    use lib::Config;
    
    /// main function
    /// responsibilities:
    /// - Calling the command line logic with the argument values
    /// - Setting up any other configuration
    /// - Calling a run function in lib.rs
    /// - Handling the error if run returns an error
    fn main() {
        //greet user
        welcome();
    
        // set up other configuration
        let mut config = Config::new().unwrap_or_else(|err| {
            eprintln!("Problem configuring program: {}", err);
            process::exit(1);
        });
    
        // run the program
        if let Err(e) = lib::run(&mut config) {
            eprintln!("Application Error: {}", e); //use the eprintln! macro to output to standard error
            process::exit(1); //exit the program with an error code
        }
    
        //end of program
        println!("THANKS FOR PLAYING!");
    }
    
    /// print the welcome message
    fn welcome() {
        println!("
                                    Chemist
                  CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY
    
    
    The fictitious chemical kryptocyanic acid can only be
    diluted by the ratio of 7 parts water to 3 parts acid.
    If any other ratio is attempted, the acid becomes unstable
    and soon explodes.  Given the amount of acid, you must
    decide how much water to add for dilution.  If you miss
    you face the consequences.
        ");
    }
    
    
    ================================================
    FILE: 24_Chemist/vbnet/Chemist.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Chemist", "Chemist.vbproj", "{25BEFBB8-58A1-4691-9CE6-D8C770F5189D}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{25BEFBB8-58A1-4691-9CE6-D8C770F5189D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{25BEFBB8-58A1-4691-9CE6-D8C770F5189D}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{25BEFBB8-58A1-4691-9CE6-D8C770F5189D}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{25BEFBB8-58A1-4691-9CE6-D8C770F5189D}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 24_Chemist/vbnet/Chemist.vbproj
    ================================================
    
      
        Exe
        Chemist
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 24_Chemist/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 25_Chief/README.md
    ================================================
    ### Chief
    
    In the words of the program author, John Graham, “CHIEF is designed to give people (mostly kids) practice in the four operations (addition, multiplication, subtraction, and division).
    
    It does this while giving people some fun. And then, if the people are wrong, it shows them how they should have done it.
    
    CHIEF was written by John Graham of Upper Brookville, New York.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=43)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=58)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 25_Chief/chief.bas
    ================================================
    2 PRINT TAB(30) "CHIEF"
    4 PRINT TAB(15) "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    6 PRINT:PRINT:PRINT
    10 PRINT "I AM CHIEF NUMBERS FREEK, THE GREAT INDIAN MATH GOD."
    20 PRINT "ARE YOU READY TO TAKE THE TEST YOU CALLED ME OUT FOR";
    30 INPUT A$
    40 IF A$="YES" THEN 60
    50 PRINT "SHUT UP, PALE FACE WITH WISE TONGUE."
    60 PRINT " TAKE A NUMBER AND ADD 3. DIVIDE THIS NUMBER BY 5 AND"
    70 PRINT "MULTIPLY BY 8. DIVIDE BY 5 AND ADD THE SAME. SUBTRACT 1."
    80 PRINT "  WHAT DO YOU HAVE";
    90 INPUT B
    100 LET C = (B+1-5)*5/8*5-3
    110 PRINT "I BET YOUR NUMBER WAS" C". AM I RIGHT";
    120 INPUT D$
    130 IF D$="YES" THEN 500
    140 PRINT "WHAT WAS YOUR ORIGINAL NUMBER";
    150 INPUT K
    155 LET F=K+3
    160 LET G=F/5
    170 LET H=G*8
    180 LET I=H/5+5
    190 LET J=I-1
    200 PRINT "SO YOU THINK YOU'RE SO SMART, EH?"
    210 PRINT "NOW WATCH."
    230 PRINT K"PLUS 3 EQUALS"F". THIS DIVIDED BY 5 EQUALS"G";"
    240 PRINT "THIS TIMES 8 EQUALS"H". IF WE DIVIDE BY 5 AND ADD 5,"
    250 PRINT "WE GET"I", WHICH, MINUS 1, EQUALS"J"."
    260 PRINT "NOW DO YOU BELIEVE ME";
    270 INPUT Z$
    290 IF Z$="YES" THEN 500
    295 PRINT "YOU HAVE MADE ME MAD!!!"
    300 PRINT "THERE MUST BE A GREAT LIGHTNING BOLT!"
    310 PRINT:PRINT
    330 FOR X=30 TO 22 STEP -1
    340 PRINT TAB(X) "X X"
    350 NEXT X
    360 PRINT TAB(21) "X XXX"
    370 PRINT TAB(20) "X   X"
    380 PRINT TAB(19) "XX X"
    390 FOR Y=20 TO 13 STEP -1
    400 PRINT TAB(Y) "X X"
    410 NEXT Y
    420 PRINT TAB(12) "XX"
    430 PRINT TAB(11) "X"
    440 PRINT TAB(10) "*"
    450 PRINT:PRINT"#########################":PRINT
    470 PRINT "I HOPE YOU BELIEVE ME NOW, FOR YOUR SAKE!!"
    480 GOTO 520
    500 PRINT "BYE!!!"
    520 END
    
    
    ================================================
    FILE: 25_Chief/csharp/Chief.csproj
    ================================================
    
      
        Exe
        net6.0
        10
        enable
        enable
      
    
      
        
      
    
      
        
      
    
    
    
    ================================================
    FILE: 25_Chief/csharp/Chief.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chief", "Chief.csproj", "{AE650092-EEF2-4747-91F4-C8311AD16A4B}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{AE650092-EEF2-4747-91F4-C8311AD16A4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{AE650092-EEF2-4747-91F4-C8311AD16A4B}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{AE650092-EEF2-4747-91F4-C8311AD16A4B}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{AE650092-EEF2-4747-91F4-C8311AD16A4B}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 25_Chief/csharp/Game.cs
    ================================================
    using static Chief.Resources.Resource;
    
    namespace Chief;
    
    internal class Game
    {
        private readonly IReadWrite _io;
    
        public Game(IReadWrite io)
        {
            _io = io;
        }
    
        internal void Play()
        {
            DoIntroduction();
    
            var result = _io.ReadNumber(Prompts.Answer);
    
            if (_io.ReadYes(Formats.Bet, Math.CalculateOriginal(result)))
            {
                _io.Write(Streams.Bye);
                return;
            }
    
            var original = _io.ReadNumber(Prompts.Original);
    
            _io.WriteLine(Math.ShowWorking(original));
    
            if (_io.ReadYes(Prompts.Believe))
            {
                _io.Write(Streams.Bye);
                return;
            }
    
            _io.Write(Streams.Lightning);
        }
    
        private void DoIntroduction()
        {
            _io.Write(Streams.Title);
            if (!_io.ReadYes(Prompts.Ready))
            {
                _io.Write(Streams.ShutUp);
            }
    
            _io.Write(Streams.Instructions);
        }
    }
    
    
    ================================================
    FILE: 25_Chief/csharp/IReadWriteExtensions.cs
    ================================================
    namespace Chief;
    
    internal static class IReadWriteExtensions
    {
        internal static bool ReadYes(this IReadWrite io, string format, Number value) =>
            io.ReadYes(string.Format(format, value));
        internal static bool ReadYes(this IReadWrite io, string prompt) =>
            io.ReadString(prompt).Equals("Yes", StringComparison.InvariantCultureIgnoreCase);
    }
    
    ================================================
    FILE: 25_Chief/csharp/Math.cs
    ================================================
    using static Chief.Resources.Resource;
    
    namespace Chief;
    
    public static class Math
    {
        public static float CalculateOriginal(float result) => (result + 1 - 5) * 5 / 8 * 5 - 3;
    
        public static string ShowWorking(Number value) =>
            string.Format(
                Formats.Working,
                value,
                value += 3,
                value /= 5,
                value *= 8,
                value = value / 5 + 5,
                value - 1);
    }
    
    ================================================
    FILE: 25_Chief/csharp/Program.cs
    ================================================
    global using Games.Common.IO;
    global using Chief;
    
    new Game(new ConsoleIO()).Play();
    
    ================================================
    FILE: 25_Chief/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 25_Chief/csharp/Resources/Answer.txt
    ================================================
      What do you have
    
    ================================================
    FILE: 25_Chief/csharp/Resources/Believe.txt
    ================================================
    Now do you believe me
    
    ================================================
    FILE: 25_Chief/csharp/Resources/Bet.txt
    ================================================
    I bet your number was{0}. Am I right
    
    ================================================
    FILE: 25_Chief/csharp/Resources/Bye.txt
    ================================================
    Bye!!!
    
    
    ================================================
    FILE: 25_Chief/csharp/Resources/Instructions.txt
    ================================================
     Take a number and add 3. Divide this number by 5 and
    multiply by 8. Divide by 5 and add the same. Subtract 1.
    
    
    ================================================
    FILE: 25_Chief/csharp/Resources/Lightning.txt
    ================================================
    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!!
    
    
    ================================================
    FILE: 25_Chief/csharp/Resources/Original.txt
    ================================================
    What was your original number
    
    ================================================
    FILE: 25_Chief/csharp/Resources/Ready.txt
    ================================================
    I am Chief Numbers Freek, the great Indian math god.
    Are you ready to take the test you called me out for
    
    ================================================
    FILE: 25_Chief/csharp/Resources/Resource.cs
    ================================================
    using System.Reflection;
    using System.Runtime.CompilerServices;
    
    namespace Chief.Resources;
    
    internal static class Resource
    {
        internal static class Streams
        {
            public static Stream Bye => GetStream();
            public static Stream Instructions => GetStream();
            public static Stream Lightning => GetStream();
            public static Stream ShutUp => GetStream();
            public static Stream Title => GetStream();
        }
    
        internal static class Formats
        {
            public static string Bet => GetString();
            public static string Working => GetString();
        }
    
        internal static class Prompts
        {
            public static string Answer => GetString();
            public static string Believe => GetString();
            public static string Original => GetString();
            public static string Ready => GetString();
        }
    
        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($"Chief.Resources.{name}.txt")
                ?? throw new ArgumentException($"Resource stream {name} does not exist", nameof(name));
    }
    
    ================================================
    FILE: 25_Chief/csharp/Resources/ShutUp.txt
    ================================================
    Shut up, pale face with wise tongue.
    
    ================================================
    FILE: 25_Chief/csharp/Resources/Title.txt
    ================================================
                                      Chief
                   Creative Computing  Morristown, New Jersey
    
    
    
    
    
    ================================================
    FILE: 25_Chief/csharp/Resources/Working.txt
    ================================================
    So you think you're so smart, eh?
    Now watch.
    {0}plus 3 equals{1}. This divided by 5 equals{2};
    This times by 8 equals{3}. If we divide by 5 and add 5,
    we get{4}, which, minus 1, equals{5}.
    
    
    ================================================
    FILE: 25_Chief/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 25_Chief/java/src/Chief.java
    ================================================
    import java.util.Arrays;
    import java.util.Scanner;
    
    /**
     * Game of Chief
     * 

    * Based on the Basic game of Hurkle here * https://github.com/coding-horror/basic-computer-games/blob/main/25%20Chief/chief.bas *

    * Note: The idea was to create a version of the 1970's Basic game in Java, without introducing * new features - no additional text, error checking, etc has been added. */ public class Chief { private enum GAME_STATE { STARTING, READY_TO_START, ENTER_NUMBER, CALCULATE_AND_SHOW, END_GAME, GAME_OVER } private GAME_STATE gameState; // The number the computer determines to be the players starting number private double calculatedNumber; // Used for keyboard input private final Scanner kbScanner; public Chief() { gameState = GAME_STATE.STARTING; // Initialise kb scanner kbScanner = new Scanner(System.in); } /** * Main game loop */ public void play() { do { switch (gameState) { // Show an introduction the first time the game is played. case STARTING: intro(); gameState = GAME_STATE.READY_TO_START; break; // show an message to start case READY_TO_START: if (!yesEntered(displayTextAndGetInput("ARE YOU READY TO TAKE THE TEST YOU CALLED ME OUT FOR? "))) { System.out.println("SHUT UP, PALE FACE WITH WISE TONGUE."); } instructions(); gameState = GAME_STATE.ENTER_NUMBER; break; // Enter the number to be used to calculate case ENTER_NUMBER: double playerNumber = Double.parseDouble( displayTextAndGetInput(" WHAT DO YOU HAVE? ")); // Exact same formula used in the original game to calculate the players original number calculatedNumber = (playerNumber + 1 - 5) * 5 / 8 * 5 - 3; gameState = GAME_STATE.CALCULATE_AND_SHOW; break; // Enter the number to be used to calculate case CALCULATE_AND_SHOW: if (yesEntered( displayTextAndGetInput("I BET YOUR NUMBER WAS " + calculatedNumber + ". AM I RIGHT? "))) { gameState = GAME_STATE.END_GAME; } else { // Player did not agree, so show the breakdown double number = Double.parseDouble( displayTextAndGetInput(" WHAT WAS YOUR ORIGINAL NUMBER? ")); double f = number + 3; double g = f / 5; double h = g * 8; double i = h / 5 + 5; double j = i - 1; System.out.println("SO YOU THINK YOU'RE SO SMART, EH?"); System.out.println("NOW WATCH."); System.out.println(number + " PLUS 3 EQUALS " + f + ". DIVIDED BY 5 EQUALS " + g); System.out.println("TIMES 8 EQUALS " + h + ". IF WE DIVIDE BY 5 AND ADD 5,"); System.out.println("WE GET " + i + ", WHICH, MINUS 1, EQUALS " + j + "."); if (yesEntered(displayTextAndGetInput("NOW DO YOU BELIEVE ME? "))) { gameState = GAME_STATE.END_GAME; } else { // Time for a lightning bolt. System.out.println("YOU HAVE MADE ME MAD!!!"); System.out.println("THERE MUST BE A GREAT LIGHTNING BOLT!"); System.out.println(); for (int x = 30; x >= 22; x--) { System.out.println(tabbedSpaces(x) + "X X"); } System.out.println(tabbedSpaces(21) + "X XXX"); System.out.println(tabbedSpaces(20) + "X X"); System.out.println(tabbedSpaces(19) + "XX X"); for (int y = 20; y >= 13; y--) { System.out.println(tabbedSpaces(y) + "X X"); } System.out.println(tabbedSpaces(12) + "XX"); System.out.println(tabbedSpaces(11) + "X"); System.out.println(tabbedSpaces(10) + "*"); System.out.println(); System.out.println("#########################"); System.out.println(); System.out.println("I HOPE YOU BELIEVE ME NOW, FOR YOUR SAKE!!"); gameState = GAME_STATE.GAME_OVER; } } break; // Sign off message for cases where the Chief is not upset case END_GAME: System.out.println("BYE!!!"); gameState = GAME_STATE.GAME_OVER; break; // GAME_OVER State does not specifically have a case } } while (gameState != GAME_STATE.GAME_OVER); } /** * Simulate tabs by building up a string of spaces * * @param spaces how many spaces are there to be * @return a string with the requested number of spaces */ private String tabbedSpaces(int spaces) { char[] repeat = new char[spaces]; Arrays.fill(repeat, ' '); return new String(repeat); } private void instructions() { System.out.println(" TAKE A NUMBER AND ADD 3. DIVIDE NUMBER BY 5 AND"); System.out.println("MULTIPLY BY 8. DIVIDE BY 5 AND ADD THE SAME. SUBTRACT 1."); } /** * Basic information about the game */ private void intro() { System.out.println("CHIEF"); System.out.println("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println(); System.out.println("I AM CHIEF NUMBERS FREEK, THE GREAT INDIAN MATH GOD."); } /** * Returns true if a given string is equal to at least one of the values specified in the call * to the stringIsAnyValue method * * @param text string to search * @return true if string is equal to one of the varargs */ private boolean yesEntered(String text) { return stringIsAnyValue(text, "Y", "YES"); } /** * Returns true if a given string contains at least one of the varargs (2nd parameter). * Note: Case insensitive comparison. * * @param text string to search * @param values varargs of type string containing values to compare * @return true if one of the varargs arguments was found in text */ private boolean stringIsAnyValue(String text, String... values) { // Cycle through the variable number of values and test each for (String val : values) { if (text.equalsIgnoreCase(val)) { return true; } } // no matches return false; } /* * Print a message on the screen, then accept input from Keyboard. * * @param text message to be displayed on screen. * @return what was typed by the player. */ private String displayTextAndGetInput(String text) { System.out.print(text); return kbScanner.next(); } } ================================================ FILE: 25_Chief/java/src/ChiefGame.java ================================================ public class ChiefGame { public static void main(String[] args) { Chief chief = new Chief(); chief.play(); } } ================================================ FILE: 25_Chief/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 25_Chief/javascript/chief.html ================================================ CHIEF

    
    
    
    
    
    
    ================================================
    FILE: 25_Chief/javascript/chief.js
    ================================================
    // CHIEF
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    // Main program
    async function main()
    {
        print(tab(30) + "CHIEF\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        print("I AM CHIEF NUMBERS FREEK, THE GREAT INDIAN MATH GOD.\n");
        print("ARE YOU READY TO TAKE THE TEST YOU CALLED ME OUT FOR");
        a = await input();
        if (a.substr(0, 1) != "Y")
            print("SHUT UP, PALE FACE WITH WIE TONGUE.\n");
        print(" TAKE A NUMBER AND ADD 3. DIVIDE THIS NUMBER BY 5 AND\n");
        print("MULTIPLY BY 8. DIVIDE BY 5 AND ADD THE SAME. SUBTRACT 1.\n");
        print("  WHAT DO YOU HAVE");
        b = parseFloat(await input());
        c = (b + 1 - 5) * 5 / 8 * 5 - 3;
        print("I BET YOUR NUMBER WAS " + Math.floor(c + 0.5) + ". AM I RIGHT");
        d = await input();
        if (d.substr(0, 1) != "Y") {
            print("WHAT WAS YOUR ORIGINAL NUMBER");
            k = parseFloat(await input());
            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?\n");
            print("NOW WATCH.\n");
            print(k + " PLUS 3 EQUALS " + f + ". THIS DIVIDED BY 5 EQUALS " + g + ";\n");
            print("THIS TIMES 8 EQUALS " + h + ". IF WE DIVIDE BY 5 AND ADD 5,\n");
            print("WE GET " + i + ", WHICH, MINUS 1, EQUALS " + j + ".\n");
            print("NOW DO YOU BELIEVE ME");
            z = await input();
            if (z.substr(0, 1) != "Y") {
                print("YOU HAVE MADE ME MAD!!!\n");
                print("THERE MUST BE A GREAT LIGHTNING BOLT!\n");
                print("\n");
                print("\n");
                for (x = 30; x >= 22; x--)
                    print(tab(x) + "X X\n");
                print(tab(21) + "X XXX\n");
                print(tab(20) + "X   X\n");
                print(tab(19) + "XX X\n");
                for (y = 20; y >= 13; y--)
                    print(tab(y) + "X X\n");
                print(tab(12) + "XX\n");
                print(tab(11) + "X\n");
                print(tab(10) + "*\n");
                print("\n");
                print("#########################\n");
                print("\n");
                print("I HOPE YOU BELIEVE ME NOW, FOR YOUR SAKE!!\n");
                return;
            }
        }
        print("BYE!!!\n");
    }
    
    main();
    
    
    ================================================
    FILE: 25_Chief/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 25_Chief/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/) by Alex Conconi
    
    ---
    
    ### Lua porting notes
    
    - I did not like the old Western movie language style in the game introduction
    and decided to tone it down, even if this deviates from the original BASIC
    version.
    
    - The `craps_game` function contains the main game logic: it
      - prints the game credits and presents the intro question;
      - asks for the end result and computes the original numer;
      - calls `explain_solution` to print the various steps of the computation;
      - presents the outro question and prints a `bolt` if necessary.
    
    - Added basic input validation to accept only valid integers for numeric input.
    
    - Minor formatting edits (lowercase, punctuation).
    
    - Any answer to a "yes or no" question is regarded as "yes" if the input line
    starts with 'y' or 'Y', else no.
    
    
    ================================================
    FILE: 25_Chief/lua/chief.lua
    ================================================
    --[[
    Chief
    
    From: BASIC Computer Games (1978)
    Edited by David H. Ahl
    
        In the words of the program author, John Graham, “CHIEF is designed to
        give people (mostly kids) practice in the four operations (addition,
        multiplication, subtraction, and division).
    
        It does this while giving people some fun. And then, if the people are
        wrong, it shows them how they should have done it.
    
        CHIEF was written by John Graham of Upper Brookville, New York.
    
        
    Lua port by Alex Conconi, 2022.
    ]]--
    
    
    --- Helper function for tabulating messages.
    local function space(n) return string.rep(" ", n) end
    
    
    --- Generates a multi-line string representing a lightning bolt
    local function bolt()
        local bolt_lines = {}
        for n = 29, 21, -1 do
            table.insert(bolt_lines, space(n) .. "x x")
        end
        table.insert(bolt_lines, space(20) .. "x xxx")
        table.insert(bolt_lines, space(19) .. "x   x")
        table.insert(bolt_lines, space(18) .. "xx x")
        for n = 19, 12, -1 do
            table.insert(bolt_lines, space(n) .. "x x")
        end
        table.insert(bolt_lines, space(11) .. "xx")
        table.insert(bolt_lines, space(10) .. "x")
        table.insert(bolt_lines, space(9) .. "*\n")
        table.insert(bolt_lines, string.rep("#", 25) .. "\n")
        return table.concat(bolt_lines, "\n")
    end
    
    
    --- Print the prompt and read a yes/no answer from stdin.
    local function ask_yes_or_no(prompt)
        io.stdout:write(prompt .. " ")
        local answer = string.lower(io.stdin:read("*l"))
        -- any line starting with a 'y' or 'Y' is considered a 'yes'
        return answer:sub(1, 1) == "y"
    end
    
    
    --- Print the prompt and read a valid number from stdin.
    local function ask_number(prompt)
        io.stdout:write(prompt .. " ")
        while true do
            local n = tonumber(io.stdin:read("*l"))
            if n then
                return n
            else
                print("Enter a valid number.")
            end
        end
    end
    
    
    --- Explain the solution to persuade the player.
    local function explain_solution()
        local k = ask_number("What was your original number?")
        -- For clarity we kept the same variable names of the original BASIC version
        local f = k + 3
        local g = f / 5
        local h = g * 8
        local i = h / 5 + 5
        local 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 .. ".")
    end
    
    
    --- Main game function.
    local function chief_game()
        --- Print game introduction and challenge
        print(space(29) .. "Chief")
        print(space(14) .. "Creative Computing  Morristown, New Jersey\n\n")
        print("I am Chief Numbers Freek, the great math god.")
        if not ask_yes_or_no("Are you ready to take the test you called me out for?") then
            print("Shut up, wise tongue.")
        end
    
        -- Print how to obtain the end result.
        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.")
    
        -- Ask the result end and reverse calculate the original number.
        local end_result = ask_number(" What do you have?")
        local original_number = (end_result + 1 - 5) * 5 / 8 * 5 - 3
    
        -- If it is an integer we do not want to print any zero decimals.
        local int_part, dec_part = math.modf(original_number)
        if dec_part == 0 then original_number = int_part end
    
        -- If the player challenges the answer, print the explanation.
        if not ask_yes_or_no("I bet your number was " .. original_number .. ". Am I right?") then
            explain_solution()
            -- If the player does not accept the explanation, zap them.
            if not ask_yes_or_no("Now do you believe me?") then
                print("YOU HAVE MADE ME MAD!!!")
                print("THERE MUST BE A GREAT LIGHTNING BOLT!\n\n")
                print(bolt())
                print("I hope you believe me now, for your sake!!")
            end
        end
    end
    
    --- Run the game.
    chief_game()
    
    
    ================================================
    FILE: 25_Chief/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 25_Chief/perl/chief.pl
    ================================================
    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    print ' ' x 30 . "CHIEF\n";
    print ' ' x 15 . "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n";
    print "\n\n\n";
    
    print "I AM CHIEF NUMBERS FREEK, THE GREAT INDIAN MATH GOD.\n";
    print "ARE YOU READY TO TAKE THE TEST YOU CALLED ME OUT FOR?\n";
    
    chomp( my $A = uc  );
    print "SHUT UP, PALE FACE WITH WISE TONGUE.\n" unless ( $A eq 'YES' );
    
    print " TAKE A NUMBER AND ADD 3. DIVIDE THIS NUMBER BY 5 AND\n";
    print "MULTIPLY BY 8. DIVIDE BY 5 AND ADD THE SAME. SUBTRACT 1.\n";
    print "  WHAT DO YOU HAVE?\n";
    
    chomp( my $B =  );
    my $C = ( $B + 1 - 5 ) * 5 / 8 * 5 - 3;
    
    print "I BET YOUR NUMBER WAS $C. AM I RIGHT?\n";
    
    chomp( my $D = uc  );
    if ( $D eq 'YES' ) {
        print "BYE!!!\n";
        exit;
    }
    
    print "WHAT WAS YOUR ORIGINAL NUMBER?\n";
    
    chomp( my $K =  );
    my $F = $K + 3;
    my $G = $F / 5;
    my $H = $G * 8;
    my $I = $H / 5 + 5;
    my $J = $I - 1;
    
    print "SO YOU THINK YOU'RE SO SMART, EH?\n";
    print "NOW WATCH.\n";
    print "$K PLUS 3 EQUALS $F. THIS DIVIDED BY 5 EQUALS $G;\n";
    print "THIS TIMES 8 EQUALS $H. IF WE DIVIDE BY 5 AND ADD 5,\n";
    print "WE GET $I , WHICH, MINUS 1, EQUALS $J.\n";
    print "NOW DO YOU BELIEVE ME?\n";
    
    chomp( my $Z = uc  );
    if ( $Z eq 'YES' ) {
        print "BYE!!!\n";
        exit;
    }
    
    print "YOU HAVE MADE ME MAD!!!\n";
    print "THERE MUST BE A GREAT LIGHTNING BOLT!\n\n\n";
    
    for my $i ( reverse 22 .. 30 ) {
        print ' ' x $i . "X X\n";
    }
    print ' ' x 21 . "X XXX\n";
    print ' ' x 20 . "X   X\n";
    print ' ' x 19 . "XX X\n";
    for my $i ( reverse 13 .. 20 ) {
        print ' ' x $i . "X X\n";
    }
    print ' ' x 12 . "XX\n";
    print ' ' x 11 . "X\n";
    print ' ' x 10 . "*\n";
    print "\n#########################\n\n";
    print "I HOPE YOU BELIEVE ME NOW, FOR YOUR SAKE!!\n";
    
    
    ================================================
    FILE: 25_Chief/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 25_Chief/python/chief.py
    ================================================
    def print_lightning_bolt() -> None:
        print("*" * 36)
        n = 24
        while n > 16:
            print(" " * n + "x x")
            n -= 1
        print(" " * 16 + "x xxx")
        print(" " * 15 + "x   x")
        print(" " * 14 + "xxx x")
        n -= 1
        while n > 8:
            print(" " * n + "x x")
            n -= 1
        print(" " * 8 + "xx")
        print(" " * 7 + "x")
        print("*" * 36)
    
    
    def print_solution(n: float) -> None:
        print(f"\n{n} plus 3 gives {n + 3}. This Divided by 5 equals {(n + 3) / 5}")
        print(f"This times 8 gives {((n + 3) / 5) * 8}. If we divide 5 and add 5.")
        print(
            f"We get {(((n + 3) / 5) * 8) / 5 + 5}, "
            f"which, minus 1 equals {((((n + 3) / 5) * 8) / 5 + 5) - 1}"
        )
    
    
    def game() -> None:
        print("\nTake a Number and ADD 3. Now, Divide this number by 5 and")
        print("multiply by 8. Now, Divide by 5 and add the same. Subtract 1")
    
        you_have = float(input("\nWhat do you have? "))
        comp_guess = (((you_have - 4) * 5) / 8) * 5 - 3
        first_guess_right = input(
            f"\nI bet your number was {comp_guess} was I right(Yes or No)? "
        )
    
        if first_guess_right.lower() == "yes":
            print("\nHuh, I Knew I was unbeatable")
            print("And here is how i did it")
            print_solution(comp_guess)
        else:
            original_number = float(input("\nHUH!! what was you original number? "))
    
            if original_number == comp_guess:
                print("\nThat was my guess, AHA i was right")
                print(
                    "Shamed to accept defeat i guess, don't worry you can master mathematics too"
                )
                print("Here is how i did it")
                print_solution(comp_guess)
            else:
                print("\nSo you think you're so smart, EH?")
                print("Now, Watch")
                print_solution(original_number)
    
                believe_me = input("\nNow do you believe me? ")
    
                if believe_me.lower() == "yes":
                    print("\nOk, Lets play again sometime bye!!!!")
                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()
    
    
    if __name__ == "__main__":
        print("I am CHIEF NUMBERS FREEK, The GREAT INDIAN MATH GOD.")
        play = input("\nAre you ready to take the test you called me out for(Yes or No)? ")
        if play.lower() == "yes":
            game()
        else:
            print("Ok, Nevermind. Let me go back to my great slumber, Bye")
            input()
    
    
    ================================================
    FILE: 25_Chief/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 25_Chief/ruby/chief.rb
    ================================================
    def tab(size)
      str = ''
      size.times do
        str += ' '
      end
    
      str
    end
    
    def input
      gets.chomp
    end
    
    def bye
      print "BYE!!!\n"
    end
    
    def main
      print tab(30), "CHIEF\n"
      print tab(15), "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n"
      print "\n"
      print "\n"
      print "\n"
      print "I AM CHIEF NUMBERS FREEK, THE GREAT INDIAN MATH GOD.\n"
      print "ARE YOU READY TO TAKE THE TEST YOU CALLED ME OUT FOR\n"
    
      a = input
      if a != 'YES'
        print "SHUT UP, PALE FACE WITH WISE TONGUE.\n"
      end
    
      print " TAKE A NUMBER AND ADD 3. DIVIDE THIS NUMBER BY 5 AND\n"
      print "MULTIPLY BY 8. DIVIDE BY 5 AND ADD THE SAME. SUBTRACT 1.\n"
      print  "  WHAT DO YOU HAVE\n"
    
      b = input.to_f
      c = ((b + 1 - 5) * 5 / 8 * 5 -3).to_f
      print "I BET YOUR NUMBER WAS #{c}. AM I RIGHT\n"
    
      d = input
      if d == 'YES'
        return bye
      end
    
      print "WHAT WAS YOUR ORIGINAL NUMBER\n"
    
      k = input.to_f
      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?\n"
      print "NOW WATCH.\n"
      print k, " PLUS 3 EQUALS ", f, ". THIS DIVIDED BY 5 EQUALS ", g, ";\n"
      print "THIS TIMES 8 EQUALS ", h, ". IF WE DIVIDE BY 5 AND ADD 5,\n"
      print "WE GET ", i, ", WHICH, MINUS 1, EQUALS ", j, ".\n"
      print "NOW DO YOU BELIEVE ME\n"
    
      z = input
      if z == 'YES'
        return bye
      end
    
      print "YOU HAVE MADE ME MAD!!!\n"
      print "THERE MUST BE A GREAT LIGHTNING BOLT!\n"
      print "\n"
      print "\n"
    
      x = 30
      while x >= 22
        print tab(x), "X X\n"
        x -= 1
      end
    
      print tab(21), "X XXX\n"
      print tab(20), "X   X\n"
      print tab(19), "XX X\n"
    
      y = 20
      while y >= 13
        print tab(y), "X X\n"
        y -= 1
      end
    
      print tab(12), "XX\n"
      print tab(11), "X\n"
      print tab(10), "*\n"
    
      print "\n"
      print "#########################\n"
      print "\n"
      print "I HOPE YOU BELIEVE ME NOW, FOR YOUR SAKE!!\n"
      return bye
    end
    
    main
    
    
    ================================================
    FILE: 25_Chief/rust/Cargo.toml
    ================================================
    [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]
    
    
    ================================================
    FILE: 25_Chief/rust/README.md
    ================================================
    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)
    
    ================================================
    FILE: 25_Chief/rust/src/main.rs
    ================================================
    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
    ///////////////////////////////////////////////////////////////////
    
    
    ================================================
    FILE: 25_Chief/vbnet/Chief.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Chief", "Chief.vbproj", "{9BAA28D9-1DCF-42DA-934A-CD3EC5E26E7C}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{9BAA28D9-1DCF-42DA-934A-CD3EC5E26E7C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{9BAA28D9-1DCF-42DA-934A-CD3EC5E26E7C}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{9BAA28D9-1DCF-42DA-934A-CD3EC5E26E7C}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{9BAA28D9-1DCF-42DA-934A-CD3EC5E26E7C}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 25_Chief/vbnet/Chief.vbproj
    ================================================
    
      
        Exe
        Chief
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 25_Chief/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 26_Chomp/README.md
    ================================================
    ### Chomp
    
    This program is an adaptation of a mathematical game originally described by Martin Gardner in the January 1973 issue of _Scientific American_. Up to a 9x9 grid is set up by you with the upper left square in a poison square. This grid is the cookie. Players alternately chomp away at the cookie from the lower right. To take a chomp, input a row and column number of one of the squares remaining on the cookie. All of the squares below and to the right of that square, including that square, disappear.
    
    Any number of people can play — the computer is only the moderator; it is not a player. Two-person strategies are interesting to work out but strategies when three or more people are playing are the real challenge.
    
    The computer version of the game was written by Peter Sessions of People’s Computer Company.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=44)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=59)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 26_Chomp/chomp.bas
    ================================================
    10 PRINT TAB(33);"CHOMP"
    20 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    30 PRINT:PRINT:PRINT
    40 DIM A(10,10)
    100 REM *** THE GAME OF CHOMP *** COPYRIGHT PCC 1973 ***
    110 PRINT
    120 PRINT "THIS IS THE GAME OF CHOMP (SCIENTIFIC AMERICAN, JAN 1973)"
    130 PRINT "DO YOU WANT THE RULES (1=YES, 0=NO!)";
    140 INPUT R
    150 IF R=0 THEN 340
    160 F=1
    170 R=5
    180 C=7
    190 PRINT "CHOMP IS FOR 1 OR MORE PLAYERS (HUMANS ONLY)."
    200 PRINT
    210 PRINT "HERE'S HOW A BOARD LOOKS (THIS ONE IS 5 BY 7):"
    220 GOSUB 540
    230 PRINT
    240 PRINT "THE BOARD IS A BIG COOKIE - R ROWS HIGH AND C COLUMNS"
    250 PRINT "WIDE. YOU INPUT R AND C AT THE START. IN THE UPPER LEFT"
    260 PRINT "CORNER OF THE COOKIE IS A POISON SQUARE (P). THE ONE WHO"
    270 PRINT "CHOMPS THE POISON SQUARE LOSES. TO TAKE A CHOMP, TYPE THE"
    280 PRINT "ROW AND COLUMN OF ONE OF THE SQUARES ON THE COOKIE."
    290 PRINT "ALL OF THE SQUARES BELOW AND TO THE RIGHT OF THAT SQUARE"
    300 PRINT "(INCLUDING THAT SQUARE, TOO) DISAPPEAR -- CHOMP!!"
    310 PRINT "NO FAIR CHOMPING SQUARES THAT HAVE ALREADY BEEN CHOMPED,"
    320 PRINT "OR THAT ARE OUTSIDE THE ORIGINAL DIMENSIONS OF THE COOKIE."
    330 PRINT
    340 PRINT "HERE WE GO..."
    350 REM
    360 F=0
    370 FOR I=1 TO 10
    372 FOR J=1 TO 10
    375 A(I,J)=0
    377 NEXT J
    379 NEXT I
    380 PRINT
    390 PRINT "HOW MANY PLAYERS";
    400 INPUT P
    410 I1=0
    420 PRINT "HOW MANY ROWS";
    430 INPUT R
    440 IF R <= 9 THEN 470
    450 PRINT "TOO MANY ROWS (9 IS MAXIMUM). NOW, ";
    460 GOTO 420
    470 PRINT "HOW MANY COLUMNS";
    480 INPUT C
    490 IF C <= 9 THEN 530
    500 PRINT "TOO MANY COLUMNS (9 IS MAXIMUM). NOW, ";
    510 GOTO 470
    530 PRINT
    540 FOR I=1 TO R
    550 FOR J=1 TO C
    560 A(I,J)=1
    570 NEXT J
    580 NEXT I
    590 A(1,1)=-1
    600 REM PRINT THE BOARD
    610 PRINT
    620 PRINT TAB(7);"1 2 3 4 5 6 7 8 9"
    630 FOR I=1 TO R
    640 PRINT I;TAB(7);
    650 FOR J=1 TO C
    660 IF A(I,J)=-1 THEN 700
    670 IF A(I,J)=0 THEN 720
    680 PRINT "* ";
    690 GOTO 710
    700 PRINT "P ";
    710 NEXT J
    720 PRINT
    730 NEXT I
    740 PRINT
    750 IF F=0 THEN 770
    760 RETURN
    770 REM GET CHOMPS FOR EACH PLAYER IN TURN
    780 LET I1=I1+1
    790 LET P1=I1-INT(I1/P)*P
    800 IF P1 <> 0 THEN 820
    810 P1=P
    820 PRINT "PLAYER";P1
    830 PRINT "COORDINATES OF CHOMP (ROW,COLUMN)";
    840 INPUT R1,C1
    850 IF R1<1 THEN 920
    860 IF R1>R THEN 920
    870 IF C1<1 THEN 920
    880 IF C1>C THEN 920
    890 IF A(R1,C1)=0 THEN 920
    900 IF A(R1,C1)=-1 THEN 1010
    910 GOTO 940
    920 PRINT "NO FAIR. YOU'RE TRYING TO CHOMP ON EMPTY SPACE!"
    930 GOTO 820
    940 FOR I=R1 TO R
    950 FOR J=C1 TO C
    960 A(I,J)=0
    970 NEXT J
    980 NEXT I
    990 GOTO 610
    1000 REM END OF GAME DETECTED IN LINE 900
    1010 PRINT "YOU LOSE, PLAYER";P1
    1020 PRINT
    1030 PRINT "AGAIN (1=YES, 0=NO!)";
    1040 INPUT R
    1050 IF R=1 THEN 340
    1060 END
    
    
    ================================================
    FILE: 26_Chomp/csharp/Chomp.csproj
    ================================================
    
      
        Exe
        net6.0
        10
        enable
        enable
      
    
      
        
      
    
      
        
      
    
    
    
    ================================================
    FILE: 26_Chomp/csharp/Chomp.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chomp", "Chomp.csproj", "{35863FB3-002D-4618-B993-51A51A586BCC}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{35863FB3-002D-4618-B993-51A51A586BCC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{35863FB3-002D-4618-B993-51A51A586BCC}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{35863FB3-002D-4618-B993-51A51A586BCC}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{35863FB3-002D-4618-B993-51A51A586BCC}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 26_Chomp/csharp/Cookie.cs
    ================================================
    using System.Text;
    
    namespace Chomp;
    
    internal class Cookie
    {
        private readonly int _rowCount;
        private readonly int _columnCount;
        private readonly char[][] _bits;
    
        public Cookie(int rowCount, int columnCount)
        {
            _rowCount = rowCount;
            _columnCount = columnCount;
    
            // The calls to Math.Max here are to duplicate the original behaviour
            // when negative values are given for the row or column count.
            _bits = new char[Math.Max(_rowCount, 1)][];
            for (int row = 0; row < _bits.Length; row++)
            {
                _bits[row] = Enumerable.Repeat('*', Math.Max(_columnCount, 1)).ToArray();
            }
            _bits[0][0] = 'P';
        }
    
        public bool TryChomp(int row, int column, out char chomped)
        {
            if (row < 1 || row > _rowCount || column < 1 || column > _columnCount || _bits[row - 1][column - 1] == ' ')
            {
                chomped = default;
                return false;
            }
    
            chomped = _bits[row - 1][column - 1];
    
            for (int r = row; r <= _rowCount; r++)
            {
                for (int c = column; c <= _columnCount; c++)
                {
                    _bits[r - 1][c - 1] = ' ';
                }
            }
    
            return true;
        }
    
        public override string ToString()
        {
            var builder = new StringBuilder().AppendLine("       1 2 3 4 5 6 7 8 9");
            for (int row = 1; row <= _bits.Length; row++)
            {
                builder.Append(' ').Append(row).Append("     ").AppendLine(string.Join(' ', _bits[row - 1]));
            }
            return builder.ToString();
        }
    }
    
    ================================================
    FILE: 26_Chomp/csharp/Game.cs
    ================================================
    namespace Chomp;
    
    internal class Game
    {
        private readonly IReadWrite _io;
    
        public Game(IReadWrite io)
        {
            _io = io;
        }
    
        internal void Play()
        {
            _io.Write(Resource.Streams.Introduction);
            if (_io.ReadNumber("Do you want the rules (1=Yes, 0=No!)") != 0)
            {
                _io.Write(Resource.Streams.Rules);
            }
    
            while (true)
            {
                _io.Write(Resource.Streams.HereWeGo);
    
                var (playerCount, rowCount, columnCount) = _io.ReadParameters();
    
                var loser = Play(new Cookie(rowCount, columnCount), new PlayerNumber(playerCount));
    
                _io.WriteLine(string.Format(Resource.Formats.YouLose, loser));
    
                if (_io.ReadNumber("Again (1=Yes, 0=No!)") != 1) { break; }
            }
        }
    
        private PlayerNumber Play(Cookie cookie, PlayerNumber player)
        {
            while (true)
            {
                _io.WriteLine(cookie);
    
                var poisoned = Chomp(cookie, player);
    
                if (poisoned) { return player; }
    
                player++;
            }
        }
    
        private bool Chomp(Cookie cookie, PlayerNumber player)
        {
            while (true)
            {
                _io.WriteLine(string.Format(Resource.Formats.Player, player));
    
                var (row, column) = _io.Read2Numbers(Resource.Prompts.Coordinates);
    
                if (cookie.TryChomp((int)row, (int)column, out char chomped))
                {
                    return chomped == 'P';
                }
    
                _io.Write(Resource.Streams.NoFair);
            }
        }
    }
    
    
    ================================================
    FILE: 26_Chomp/csharp/IOExtensions.cs
    ================================================
    namespace Chomp;
    
    internal static class IOExtensions
    {
        public static (float, int, int) ReadParameters(this IReadWrite io)
            => (
                (int)io.ReadNumber(Resource.Prompts.HowManyPlayers),
                io.ReadNumberWithMax(Resource.Prompts.HowManyRows, 9, Resource.Strings.TooManyRows),
                io.ReadNumberWithMax(Resource.Prompts.HowManyColumns, 9, Resource.Strings.TooManyColumns)
            );
    
        private static int ReadNumberWithMax(this IReadWrite io, string initialPrompt, int max, string reprompt)
        {
            var prompt = initialPrompt;
    
            while (true)
            {
                var response = io.ReadNumber(prompt);
                if (response <= 9) { return (int)response; }
    
                prompt = $"{reprompt} {initialPrompt.ToLowerInvariant()}";
            }
        }
    }
    
    ================================================
    FILE: 26_Chomp/csharp/PlayerNumber.cs
    ================================================
    namespace Chomp;
    
    internal class PlayerNumber
    {
        private readonly float _playerCount;
        private int _counter;
        private float _number;
    
        // The original code does not constrain playerCount to be an integer
        public PlayerNumber(float playerCount)
        {
            _playerCount = playerCount;
            _number = 0;
            Increment();
        }
    
        public static PlayerNumber operator ++(PlayerNumber number) => number.Increment();
    
        private PlayerNumber Increment()
        {
    		if (_playerCount == 0) { throw new DivideByZeroException(); }
    
            // The increment logic here is the same as the original program, and exhibits
            // interesting behaviour when _playerCount is not an integer.
            _counter++;
            _number = _counter - (float)Math.Floor(_counter / _playerCount) * _playerCount;
            if (_number == 0) { _number = _playerCount; }
            return this;
        }
    
        public override string ToString() => (_number >= 0 ? " " : "") + _number.ToString();
    }
    
    ================================================
    FILE: 26_Chomp/csharp/Program.cs
    ================================================
    global using Games.Common.IO;
    global using Chomp.Resources;
    using Chomp;
    
    new Game(new ConsoleIO()).Play();
    
    ================================================
    FILE: 26_Chomp/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 26_Chomp/csharp/Resources/Coordinates.txt
    ================================================
    Coordinates of Chomp (row, column)
    
    ================================================
    FILE: 26_Chomp/csharp/Resources/HereWeGo.txt
    ================================================
    Here we go...
    
    
    
    ================================================
    FILE: 26_Chomp/csharp/Resources/HowManyColumns.txt
    ================================================
    How many columns
    
    ================================================
    FILE: 26_Chomp/csharp/Resources/HowManyPlayers.txt
    ================================================
    How many players
    
    ================================================
    FILE: 26_Chomp/csharp/Resources/HowManyRows.txt
    ================================================
    How many rows
    
    ================================================
    FILE: 26_Chomp/csharp/Resources/Introduction.txt
    ================================================
                                     Chomp
                   Creative Computing  Morristown, New Jersey
    
    
    
    This is the game of Chomp (Scientific American, Jan 1973)
    
    
    ================================================
    FILE: 26_Chomp/csharp/Resources/NoFair.txt
    ================================================
    No fair. You're trying to chomp on empty space!
    
    
    ================================================
    FILE: 26_Chomp/csharp/Resources/Player.txt
    ================================================
    Player{0}
    
    ================================================
    FILE: 26_Chomp/csharp/Resources/Resource.cs
    ================================================
    using System.Reflection;
    using System.Runtime.CompilerServices;
    
    namespace Chomp.Resources;
    
    internal static class Resource
    {
        internal static class Streams
        {
            public static Stream HereWeGo => GetStream();
            public static Stream Introduction => GetStream();
            public static Stream Rules => GetStream();
            public static Stream NoFair => GetStream();
        }
    
        internal static class Formats
        {
            public static string Player => GetString();
            public static string YouLose => GetString();
        }
    
        internal static class Prompts
        {
            public static string Coordinates => GetString();
            public static string HowManyPlayers => GetString();
            public static string HowManyRows => GetString();
            public static string HowManyColumns => GetString();
            public static string TooManyColumns => GetString();
        }
    
        internal static class Strings
        {
            public static string TooManyColumns => GetString();
            public static string TooManyRows => GetString();
        }
    
        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}'.");
    }
    
    ================================================
    FILE: 26_Chomp/csharp/Resources/Rules.txt
    ================================================
    Chomp is for 1 or more players (humans only).
    
    Here's how a board looks (this one is 5 by 7):
    
           1 2 3 4 5 6 7 8 9
     1     P * * * * * *
     2     * * * * * * *
     3     * * * * * * *
     4     * * * * * * *
     5     * * * * * * *
    
    
    The board is a big cookie - R rows high and C columns
    wide. You input R and C at the start. In the upper left
    corner of the cookie is a poison square (P). The one who
    chomps the poison square loses. To take a chomp, type the
    row and column of one of the squares on the cookie.
    All of the squares below and to the right of that square
    (including that square, too) disappear -- Chomp!!
    No fair chomping on squares that have already been chomped,
    or that are outside the original dimensions of the cookie.
    
    
    
    ================================================
    FILE: 26_Chomp/csharp/Resources/TooManyColumns.txt
    ================================================
    Too many rows (9 is maximum). Now,
    
    ================================================
    FILE: 26_Chomp/csharp/Resources/TooManyRows.txt
    ================================================
    Too many rows (9 is maximum). Now,
    
    ================================================
    FILE: 26_Chomp/csharp/Resources/YouLose.txt
    ================================================
    You lose, player{0}
    
    
    ================================================
    FILE: 26_Chomp/java/Chomp.java
    ================================================
    import java.util.Scanner;
    public class Chomp{
    	int rows;
    	int cols;
    	int numberOfPlayers;
    	int []board;
    	Scanner scanner;
    	Chomp(){
    		System.out.println("\t\t\t\tCHOMP");
    		System.out.println("\t\tCREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
    		System.out.println("THIS IS THE GAME OF CHOMP (SCIENTIFIC AMERICAN, JAN 1973)");
    		System.out.print("Do you want the rules (1=Yes, 0=No!)  ");
    
    		scanner = new Scanner(System.in);
    		int choice = scanner.nextInt();
    		if(choice != 0){
    			System.out.println("Chomp is for 1 or more players (Humans only).\n");
    			System.out.println("Here's how a board looks (This one is 5 by 7):");
    			System.out.println("\t1 2 3 4 5 6 7");
    			System.out.println(" 1     P * * * * * *\n 2     * * * * * * *\n 3     * * * * * * *\n 4     * * * * * * *\n 5     * * * * * * *");
    			System.out.println("\nThe board is a big cookie - R rows high and C columns \nwide. You input R and C at the start. In the upper left\ncorner of the cookie is a poison square (P). The one who\nchomps the poison square loses. To take a chomp, type the\nrow and column of one of the squares on the cookie.\nAll of the squares below and to the right of that square\n(Including that square, too) disappear -- CHOMP!!\nNo fair chomping squares that have already been chomped,\nor that are outside the original dimensions of the cookie.\n");
    			System.out.println("Here we go...\n");
    		}
    		startGame();
    	}
    
    	private void startGame(){
    		System.out.print("How many players ");
    		numberOfPlayers = scanner.nextInt();
    		while(numberOfPlayers < 2){
    			System.out.print("How many players ");
                    	numberOfPlayers = scanner.nextInt();
    		}
    		System.out.print("How many rows ");
    		rows = scanner.nextInt();
    		while(rows<=0 || rows >9){
    			if(rows <= 0){
    				System.out.println("Minimun 1 row is required !!");
    			}
    			else{
    				System.out.println("Too many rows(9 is maximum). ");
    			}
    			System.out.print("How many rows ");
    			rows = scanner.nextInt();
    		}
    		System.out.print("How many columns ");
                    cols = scanner.nextInt();
                    while(cols<=0 || cols >9){
                            if(cols <= 0){
                                    System.out.println("Minimun 1 column is required !!");
                            }
                            else{
                                    System.out.println("Too many columns(9 is maximum). ");
                            }
                            System.out.print("How many columns ");
                            cols = scanner.nextInt();
                    }
    		board = new int[rows];
    		for(int i=0;irows || x <1 || y>cols || y<1 || board[x-1]= y){
    					board[i] = y-1;
    				}
    			}
    			printBoard();
    			move((player+1)%numberOfPlayers);
    		}
    	}
    
    
    	public static void main(String []args){
    		new Chomp();
    	}
    }
    
    
    ================================================
    FILE: 26_Chomp/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 26_Chomp/javascript/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells)
    
    
    ================================================
    FILE: 26_Chomp/javascript/chomp.html
    ================================================
    
    
    CHOMP
    
    
    
    
    
    
    
    
    
    
    ================================================
    FILE: 26_Chomp/javascript/chomp.js
    ================================================
    // CHOMP
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    var a = [];
    var r;
    var c;
    
    function init_board()
    {
        for (i = 1; i <= r; i++)
            for (j = 1; j <= c; j++)
                a[i][j] = 1;
        a[1][1] = -1;
    }
    
    function show_board()
    {
        print("\n");
        print(tab(7) + "1 2 3 4 5 6 7 8 9\n");
        for (i = 1; i <= r; i++) {
            str = i + tab(6);
            for (j = 1; j <= c; j++) {
                if (a[i][j] == -1)
                    str += "P ";
                else if (a[i][j] == 0)
                    break;
                else
                    str += "* ";
            }
            print(str + "\n");
        }
        print("\n");
    }
    
    // Main program
    async function main()
    {
        print(tab(33) + "CHOMP\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        for (i = 1; i <= 10; i++)
            a[i] = [];
        // *** THE GAME OF CHOMP *** COPYRIGHT PCC 1973 ***
        print("\n");
        print("THIS IS THE GAME OF CHOMP (SCIENTIFIC AMERICAN, JAN 1973)\n");
        print("DO YOU WANT THE RULES (1=YES, 0=NO!)");
        r = parseInt(await input());
        if (r != 0) {
            f = 1;
            r = 5;
            c = 7;
            print("CHOMP IS FOR 1 OR MORE PLAYERS (HUMANS ONLY).\n");
            print("\n");
            print("HERE'S HOW A BOARD LOOKS (THIS ONE IS 5 BY 7):\n");
            init_board();
            show_board();
            print("\n");
            print("THE BOARD IS A BIG COOKIE - R ROWS HIGH AND C COLUMNS\n");
            print("WIDE. YOU INPUT R AND C AT THE START. IN THE UPPER LEFT\n");
            print("CORNER OF THE COOKIE IS A POISON SQUARE (P). THE ONE WHO\n");
            print("CHOMPS THE POISON SQUARE LOSES. TO TAKE A CHOMP, TYPE THE\n");
            print("ROW AND COLUMN OF ONE OF THE SQUARES ON THE COOKIE.\n");
            print("ALL OF THE SQUARES BELOW AND TO THE RIGHT OF THAT SQUARE\n");
            print("INCLUDING THAT SQUARE, TOO) DISAPPEAR -- CHOMP!!\n");
            print("NO FAIR CHOMPING SQUARES THAT HAVE ALREADY BEEN CHOMPED,\n");
            print("OR THAT ARE OUTSIDE THE ORIGINAL DIMENSIONS OF THE COOKIE.\n");
            print("\n");
        }
        while (1) {
            print("HERE WE GO...\n");
            f = 0;
            for (i = 1; i <= 10; i++) {
                a[i] = [];
                for (j = 1; j <= 10; j++) {
                    a[i][j] = 0;
                }
            }
            print("\n");
            print("HOW MANY PLAYERS");
            p = parseInt(await input());
            i1 = 0;
            while (1) {
                print("HOW MANY ROWS");
                r = parseInt(await input());
                if (r <= 9)
                    break;
                print("TOO MANY ROWS (9 IS MAXIMUM). NOW ");
            }
            while (1) {
                print("HOW MANY COLUMNS");
                c = parseInt(await input());
                if (c <= 9)
                    break;
                print("TOO MANY COLUMNS (9 IS MAXIMUM). NOW ");
            }
            print("\n");
            init_board();
            while (1) {
                // Print the board
                show_board();
                // Get chomps for each player in turn
                i1++;
                p1 = i1 - Math.floor(i1 / p) * p;
                if (p1 == 0)
                    p1 = p;
                while (1) {
                    print("PLAYER " + p1 + "\n");
                    print("COORDINATES OF CHOMP (ROW,COLUMN)");
                    str = await input();
                    r1 = parseInt(str);
                    c1 = parseInt(str.substr(str.indexOf(",") + 1));
                    if (r1 >= 1 && r1 <= r && c1 >= 1 && c1 <= c && a[r1][c1] != 0)
                        break;
                    print("NO FAIR. YOU'RE TRYING TO CHOMP ON EMPTY SPACE!\n");
                }
                if (a[r1][c1] == -1)
                    break;
                for (i = r1; i <= r; i++)
                    for (j = c1; j <= c; j++)
                        a[i][j] = 0;
            }
            // End of game detected
            print("YOU LOSE, PLAYER " + p1 + "\n");
            print("\n");
            print("AGAIN (1=YES, 0=NO!)");
            r = parseInt(await input());
            if (r != 1)
                break;
        }
    }
    
    main();
    
    
    ================================================
    FILE: 26_Chomp/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 26_Chomp/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 26_Chomp/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 26_Chomp/perl/chomp.pl
    ================================================
    #!/usr/bin/perl
    
    
    my @cookie;
    
    &main;
    
    sub main {
    	my $answer = 1;
    	until ($answer == 0) {
    		$answer=&game_play;
    	}
    }
    
    sub game_play {
    
    	# the initial game set up
    	&print_intro;
    	my ($players,$rows,$cols) = &get_user_info;
    	&create_gameboard($rows,$cols);
    	&print_gameboard($rows,$cols);
    	my $game_over = 0;
    	my $player = 0;
    
    	# continuous loop until the poison pill is swallowed
    	until ($game_over == 1) {
    		if ($player > ($players-1)) {		#checks to make sure we're just looping thru valid players
    			$player = 0;
    		}
    		$player++;
    		my ($user_row,$user_col) = &get_player_row_col($player,$rows,$cols);
    		if ($cookie[$user_row][$user_col] == -1) {
    			print "YOU LOSE, PLAYER $player\n\n";
    			print "AGAIN (1=YES, 0=NO!)\n";
    			my $answer=;
    			chomp($answer);
    			return($answer);
    		}
    		&modify_gameboard($rows,$cols,$user_row,$user_col);
    		&print_gameboard($rows,$cols);
    	}
    
    }
    
    sub get_player_row_col {
    	my ($player,$row,$col) = @_;
    	my @coords;
    	my $validity="invalid";
    	# Getting coordinates from user
    	until ($validity eq "valid") {
    		print "PLAYER $player COORDINATES OF CHOMP (ROW,COLUMN)\n";
    		my $input=;
    		chomp($input);
    		@coords = split/,/,$input;
    
    		#verifying coordinates are valid
    		if ($coords[0] < 1 || $coords[0] > $row || $coords[1] < 1 || $coords[1] > $col || $cookie[$coords[0]][$coords[1]] == 0) {
    			print "NO FAIR. YOU'RE TRYING TO CHOMP ON EMPTY SPACE!\n";
    		}
    		else {
    			$validity="valid";
    		}
    	}
    	return($coords[0],$coords[1]);
    }
    
    sub get_user_info {
    	my ($players,$rows,$cols)=0;
    	until ($players > 0) {
    		print "HOW MANY PLAYERS\n";
    		$players=;
    		chomp($players);
    	}
    	until ($rows > 0 && $rows < 10) {
    		print "HOW MANY ROWS\n";
    		$rows=;
    		chomp($rows);
    		if ($rows > 9) {
    			print "TOO MANY ROWS (9 IS MAXIMUM). NOW, ";
    		}
    	}
    	until ($cols > 0 && $cols < 10) {
    		print "HOW MANY COLUMNS\n";
    		$cols=;
    		chomp($cols);
    		if ($cols > 9) {
    			print "TOO MANY COLUMNS (9 IS MAXIMUM). NOW, ";
    		}
    	}
    	return($players,$rows,$cols);
    }
    
    sub print_intro{
    	print ' ' x 33 . "CHOMP\n";
    	print ' ' x 15 . "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n\n";
    	print "THIS IS THE GAME OF CHOMP (SCIENTIFIC AMERICAN, JAN 1973)\n";
    	print "DO YOU WANT THE RULES (1=YES, 0=NO!)";
    	my $answer = ;
    	chomp($answer);
    	if ($answer == 0) {
    		return;
    	}
    	else {
    	 	print "CHOMP IS FOR 1 OR MORE PLAYERS (HUMANS ONLY).\n\n";
    	 	print "HERE'S HOW A BOARD LOOKS (THIS ONE IS 5 BY 7):\n";
    		&create_gameboard(5,7);
    		&print_gameboard(5,7);
    	 	print "THE BOARD IS A BIG COOKIE - R ROWS HIGH AND C COLUMNS\n";
    	 	print "WIDE. YOU INPUT R AND C AT THE START. IN THE UPPER LEFT\n";
    	 	print "CORNER OF THE COOKIE IS A POISON SQUARE (P). THE ONE WHO\n";
    	 	print "CHOMPS THE POISON SQUARE LOSES. TO TAKE A CHOMP, TYPE THE\n";
    	 	print "ROW AND COLUMN OF ONE OF THE SQUARES ON THE COOKIE.\n";
    	 	print "ALL OF THE SQUARES BELOW AND TO THE RIGHT OF THAT SQUARE\n";
    	 	print "(INCLUDING THAT SQUARE, TOO) DISAPPEAR -- CHOMP!!\n";
    	 	print "NO FAIR CHOMPING SQUARES THAT HAVE ALREADY BEEN CHOMPED,\n";
    	 	print "OR THAT ARE OUTSIDE THE ORIGINAL DIMENSIONS OF THE COOKIE.\n\n";
    	 	print "HERE WE GO...\n";
    		undef @cookie;
    	}
    }
    
    #initial creation of the gameboard
    sub create_gameboard {
    	my $rows = shift;
    	my $cols = shift;
    	foreach my $row (1..($rows)) {
    		foreach my $col (1..($cols)) {
    			$cookie[$row][$col]=1;
    		}
    	}
    	$cookie[1][1]=-1;
    }
    
    #modification of the gameboard based on the input from the player
    sub modify_gameboard {
    	my ($rows,$cols,$user_row,$user_col) = @_;
    	foreach my $row ($user_row..($rows)) {
    		foreach my $col ($user_col..($cols)) {
    			$cookie[$row][$col]="  ";
    		}
    	}
    }
    
    #prints the gameboard based on the current state of the gameboard
    sub print_gameboard {
    	my ($rows,$cols) = @_;
    	foreach my $col (1..$cols) {
    		if ($col == $cols) {
    			print "$col\n";
    		}
    		elsif ($col == 1) {
    			print "\t  $col ";
    		}
    		else {
    			print "$col ";
    		}
    	}
    	foreach my $row (1..($rows)) {
    		print "\t$row ";
    		foreach my $col (1..($cols)) {
    			if ($cookie[$row][$col] == 1) {
    				print "* ";
    			}
    			if ($cookie[$row][$col] == 0) {
    				print "  ";
    			}
    			if ($cookie[$row][$col] == -1) {
    				print "P ";
    			}
    		}
    		print "\n";
    	}
    }
    
    
    ================================================
    FILE: 26_Chomp/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 26_Chomp/python/chomp.py
    ================================================
    #!/usr/bin/env python3
    
    """
    CHOMP
    
    Converted from BASIC to Python by Trevor Hobson
    """
    
    
    class Canvas:
        """For drawing the cookie"""
    
        def __init__(self, width=9, height=9, fill="*") -> None:
            self._buffer = []
            for _ in range(height):
                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"]
            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:
            if not 1 <= r <= len(self._buffer) or not 1 <= c <= len(self._buffer[0]):
                return "Empty"
            elif self._buffer[r - 1][c - 1] == " ":
                return "Empty"
            elif self._buffer[r - 1][c - 1] == "P":
                return "Poison"
            else:
                for row in range(r - 1, len(self._buffer)):
                    for column in range(c - 1, len(self._buffer[row])):
                        self._buffer[row][column] = " "
                return "Chomp"
    
    
    def play_game() -> None:
        """Play one round of the game"""
        players = 0
        while players == 0:
            try:
                players = int(input("How many players "))
    
            except ValueError:
                print("Please enter a number.")
        rows = 0
        while rows == 0:
            try:
                rows = int(input("How many rows "))
                if rows > 9 or rows < 1:
                    rows = 0
                    print("Too many rows (9 is maximum).")
    
            except ValueError:
                print("Please enter a number.")
        columns = 0
        while columns == 0:
            try:
                columns = int(input("How many columns "))
                if columns > 9 or columns < 1:
                    columns = 0
                    print("Too many columns (9 is maximum).")
    
            except ValueError:
                print("Please enter a number.")
        cookie = Canvas(width=columns, height=rows)
        player = 0
        alive = True
        while alive:
            print()
            print(cookie.render())
            print()
            player += 1
            if player > players:
                player = 1
            while True:
                print("Player", player)
                player_row = -1
                player_column = -1
                while player_row == -1 or player_column == -1:
                    try:
                        coordinates = [
                            int(item)
                            for item in input("Coordinates of chomp (Row, Column) ").split(
                                ","
                            )
                        ]
                        player_row = coordinates[0]
                        player_column = coordinates[1]
    
                    except (ValueError, IndexError):
                        print("Please enter valid coordinates.")
                result = cookie.chomp(player_row, player_column)
                if result == "Empty":
                    print("No fair. You're trying to chomp on empty space!")
                elif result == "Poison":
                    print("\nYou lose player", player)
                    alive = False
                    break
                else:
                    break
    
    
    def main() -> None:
        print(" " * 33 + "CHOMP")
        print(" " * 15 + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n")
        print("THIS IS THE GAME OF CHOMP (SCIENTIFIC AMERICAN, JAN 1973)")
        if input("Do you want the rules (1=Yes, 0=No!) ") != "0":
            print("Chomp is for 1 or more players (Humans only).\n")
            print("Here's how a board looks (This one is 5 by 7):")
            example = Canvas(width=7, height=5)
            print(example.render())
            print("\nThe 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.\n")
            print("Here we go...")
    
        keep_playing = True
        while keep_playing:
            play_game()
            keep_playing = input("\nAgain (1=Yes, 0=No!) ") == "1"
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 26_Chomp/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 26_Chomp/vbnet/Chomp.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Chomp", "Chomp.vbproj", "{F7F8E933-9679-4393-94F0-46F47D81B025}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{F7F8E933-9679-4393-94F0-46F47D81B025}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{F7F8E933-9679-4393-94F0-46F47D81B025}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{F7F8E933-9679-4393-94F0-46F47D81B025}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{F7F8E933-9679-4393-94F0-46F47D81B025}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 26_Chomp/vbnet/Chomp.vbproj
    ================================================
    
      
        Exe
        Chomp
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 26_Chomp/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 27_Civil_War/README.md
    ================================================
    ### Civil War
    
    This simulation is based on 14 battles in the Civil War. Facts and figures are based on the actual occurrence. If you follow the same strategy used in the actual battle, the results will be the same. Generally, this is a good strategy since the generals in the Civil War were fairly good military strategists. However, you can frequently outperform the Civil War generals, particularly in cases where they did not have good enemy intelligence and consequently followed a poor course of action. Naturally, it helps to know your Civil War history, although the computer gives you the rudiments.
    
    After each of the 14 battles, your casualties are compared to the actual casualties of the battle, and you are told whether you win or lose the battle.
    
    You may play Civil War alone in which case the program simulates the Union general. Or two players may play in which case the computer becomes the moderator.
    
    Civil War was written in 1968 by three Students in Lexington High School, Massachusetts: L. Cram, L. Goodie, and D. Hibbard. It was modified into a 2-player game by G. Paul and R. Hess of TIES, St. Paul, Minnesota.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=46)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=61)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### 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)
    
    
    ================================================
    FILE: 27_Civil_War/civilwar.bas
    ================================================
    2 PRINT TAB(26) "CIVIL WAR"
    4 PRINT TAB(15) "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    6 PRINT : PRINT : PRINT
    20 REM ORIGINAL GAME DESIGN: CRAM, GOODIE, HIBBARD LEXINGTON H.S.
    30 REM MODIFICATIONS: G. PAUL, R. HESS (TIES), 1973
    50  DIM S(4),C$(14),M1(14),M2(14),C1(14),C2(14),M(14)
    60  REM UNION INFO ON LIKELY CONFEDERATE STRATEGY
    70  S(1)=25 : S(2)=25 : S(3)=25 : S(4)=25
    82  REM READ HISTORICAL DATA.
    84  FOR D=1 TO 14
    86  READ C$(D),M1(D),M2(D),C1(D),C2(D),M(D)
    88  NEXT D
    89  LET D=RND(-1)
    90  PRINT
    100  PRINT "DO YOU WANT INSTRUCTIONS";
    110  INPUT X$
    120  IF X$="YES" THEN 160
    130  IF X$="NO" THEN 370
    140  PRINT "YES OR NO -- ";
    150  GOTO 110
    160  PRINT : PRINT : PRINT : PRINT
    170  PRINT "THIS IS A CIVIL WAR SIMULATION."
    180  PRINT "TO PLAY TYPE A RESPONSE WHEN THE COMPUTER ASKS."
    190  PRINT "REMEMBER THAT ALL FACTORS ARE INTERRELATED AND THAT YOUR"
    200  PRINT "RESPONSES COULD CHANGE HISTORY. FACTS AND FIGURES USED ARE"
    210  PRINT "BASED ON THE ACTUAL OCCURRENCE. MOST BATTLES TEND TO RESULT"
    220  PRINT "AS THEY DID IN THE CIVIL WAR, BUT IT ALL DEPENDS ON YOU!!"
    230  PRINT
    240  PRINT "THE OBJECT OF THE GAME IS TO WIN AS MANY BATTLES AS ";
    245  PRINT "POSSIBLE."
    250  PRINT
    260  PRINT "YOUR CHOICES FOR DEFENSIVE STRATEGY ARE:"
    270  PRINT "        (1) ARTILLERY ATTACK"
    280  PRINT "        (2) FORTIFICATION AGAINST FRONTAL ATTACK"
    290  PRINT "        (3) FORTIFICATION AGAINST FLANKING MANEUVERS"
    300  PRINT "        (4) FALLING BACK"
    310  PRINT " YOUR CHOICES FOR OFFENSIVE STRATEGY ARE:"
    320  PRINT "        (1) ARTILLERY ATTACK"
    330  PRINT "        (2) FRONTAL ATTACK"
    340  PRINT "        (3) FLANKING MANEUVERS"
    350  PRINT "        (4) ENCIRCLEMENT"
    360  PRINT "YOU MAY SURRENDER BY TYPING A '5' FOR YOUR STRATEGY."
    370  PRINT : PRINT : PRINT : PRINT "ARE THERE TWO GENERALS PRESENT ";
    380  PRINT "(ANSWER YES OR NO)";
    390  INPUT B$
    400  IF B$="YES" THEN 430
    410  IF B$ <> "NO" THEN 380
    420  PRINT : PRINT "YOU ARE THE CONFEDERACY.   GOOD LUCK!"
    425  PRINT
    430  LET D=1
    440  IF B$ <> "YES" THEN 460
    450  LET D=2
    460  PRINT "SELECT A BATTLE BY TYPING A NUMBER FROM 1 TO 14 ON"
    470  PRINT "REQUEST.  TYPE ANY OTHER NUMBER TO END THE SIMULATION."
    480  PRINT "BUT '0' BRINGS BACK EXACT PREVIOUS BATTLE SITUATION"
    490  PRINT "ALLOWING YOU TO REPLAY IT"
    500  PRINT
    510  PRINT "NOTE: A NEGATIVE FOOD$ ENTRY CAUSES THE PROGRAM TO "
    520  PRINT "USE THE ENTRIES FROM THE PREVIOUS BATTLE"
    530  PRINT
    540  PRINT "AFTER REQUESTING A BATTLE, DO YOU WISH ";
    550  PRINT "BATTLE DESCRIPTIONS ";
    560  PRINT "(ANSWER YES OR NO)";
    570  INPUT X$
    580  IF X$="YES" THEN 600
    590  IF X$ <> "NO" THEN 560
    600  L=0:W=0:R1=0:Q1=0:M3=0:M4=0:P1=0:P2=0:T1=0:T2=0
    610  F(2)=0:H(2)=0:B(2)=0:R2=0:Q2=0:C6=0:F=0:W0=0:Y=0:Y2=0:U=0:U2=0
    620  PRINT : PRINT : PRINT
    630  PRINT "WHICH BATTLE DO YOU WISH TO SIMULATE";
    640  INPUT A
    650  IF A <> 0 THEN 660
    655  IF R <> 0 THEN 1140
    660  IF A <=0 THEN 2860
    665  IF A >= 15 THEN 2860
    670  LET C$=C$(A)
    680  LET M1=M1(A)
    690  LET M2=M2(A)
    700  LET C1=C1(A)
    710  LET C2=C2(A)
    720  LET M=M(A)
    960  LET U=0
    970  REM  INFLATION CALC
    980  LET I1=10+(L-W)*2
    990  LET I2=10+(W-L)*2
    1000  REM - MONEY AVAILABLE
    1010  LET D(1)=100*INT((M1*(100-I1)/2000)*(1+(R1-Q1)/(R1+1))+.5)
    1020  LET D(2)=100*INT(M2*(100-I2)/2000+.5)
    1030  IF B$ <> "YES" THEN 1050
    1040  LET D(2)=100*INT((M2*(100-I2)/2000)*(1+(R2-Q2)/(R2+1))+.5)
    1050  REM - MEN   AVAILABLE
    1060  LET M5=INT(M1*(1+(P1-T1)/(M3+1)))
    1070  LET M6=INT(M2*(1+(P2-T2)/(M4+1)))
    1080  LET F1=5*M1/6
    1090  PRINT : PRINT : PRINT : PRINT : PRINT
    1100  PRINT "THIS IS THE BATTLE OF ";C$
    1110  IF X$="NO" THEN 1150
    1120  IF A>11 THEN 1130
    1125  ON A GOTO 3580,3620,3650,3690,3720,3750,3780,3800,3830,3860,3890
    1130  ON A-11 GOTO 3920,3950,3980
    1140  PRINT C$" INSTANT REPLAY"
    1150  PRINT
    1160  PRINT " ","CONFEDERACY"," UNION"
    1170  PRINT "MEN"," "M5," "M6
    1180  PRINT "MONEY","$";D(1),"$";D(2)
    1190  PRINT "INFLATION"," ";I1+15;"%"," ";I2;"%"
    1195  PRINT
    1200  REM - ONLY IN PRINTOUT IS CONFED INFLATION = I1+15%
    1210  REM - IF TWO GENERALS, INPUT CONFED. FIRST
    1220  FOR I=1 TO D
    1230  IF B$ <> "YES" THEN 1260
    1240  IF I=2 THEN 1260
    1250  PRINT "CONFEDERATE GENERAL---";
    1260  PRINT "HOW MUCH DO YOU WISH TO SPEND FOR"
    1270  PRINT " - FOOD......";
    1280  INPUT F
    1290  IF F >= 0 THEN 1360
    1300  IF R1 <> 0 THEN 1330
    1310  PRINT "NO PREVIOUS ENTRIES"
    1320  GOTO 1270
    1330  PRINT "ASSUME YOU WANT TO KEEP SAME ALLOCATIONS"
    1340  PRINT
    1350  GOTO 1510
    1360  LET F(I)=F
    1370  PRINT " - SALARIES..";
    1380  INPUT H(I)
    1390  LET N=1
    1400  IF H(I)<0 THEN 1490
    1410  PRINT " - AMMUNITION";
    1420  INPUT B(I)
    1430  LET N=2
    1440  IF B(I)<0 THEN 1490
    1450  PRINT
    1460  IF F(I)+H(I)+B(I) <= D(I) THEN 1510
    1470  PRINT "THINK AGAIN! YOU HAVE ONLY $"D(I)
    1480  GOTO 1270
    1490  PRINT "NEGATIVE VALUES NOT ALLOWED."
    1500  ON N GOTO 1370,1410
    1510  IF B$ <> "YES" THEN 1550
    1520  IF I=2 THEN 1550
    1530  PRINT "UNION GENERAL---";
    1540  NEXT I
    1550  FOR Z=1 TO D
    1560  IF B$ <> "YES" THEN 1620
    1570  ON Z GOTO 1580,1600
    1580  PRINT "CONFEDERATE ";
    1590  GOTO 1620
    1600  PRINT "      UNION ";
    1610  REM - FIND MORALE
    1620  LET O=((2*F(Z)^2+H(Z)^2)/F1^2+1)
    1630  IF O<10 THEN 1660
    1640  PRINT "MORALE IS HIGH"
    1650  GOTO 1700
    1660  IF O<5 THEN 1690
    1670  PRINT "MORALE IS FAIR"
    1680  GOTO 1700
    1690  PRINT "MORALE IS POOR"
    1700  IF B$ <> "YES" THEN 1760
    1710  LET O(Z)=O
    1720  NEXT Z
    1730  LET O2=O(2)
    1740  LET O=O(1)
    1750  PRINT "CONFEDERATE GENERAL---";
    1760  REM - ACTUAL OFF/DEF BATTLE SITUATION
    1770  IF M <> 3 THEN 1800
    1780  PRINT "YOU ARE ON THE OFFENSIVE"
    1790  GOTO 1840
    1800  IF M <> 1 THEN 1830
    1810  PRINT "YOU ARE ON THE DEFENSIVE"
    1820  GOTO 1840
    1830  PRINT "BOTH SIDES ARE ON THE OFFENSIVE "
    1840  PRINT
    1850  REM - CHOOSE STRATEGIES
    1860  IF B$ <> "YES" THEN 1910
    1870  FOR I=1 TO 2
    1880  ON I GOTO 1890,1920
    1890  PRINT "CONFEDERATE STRATEGY ";
    1900  GOTO 1920
    1910  PRINT "YOUR STRATEGY ";
    1920  INPUT Y
    1930  IF ABS(Y-3)<3 THEN 1960
    1940  PRINT "STRATEGY";Y;"NOT ALLOWED."
    1950  GOTO 1910
    1960  IF B$="YES" THEN 2000
    1970  IF Y=5 THEN 2830
    1980  GOSUB 3110
    1990  GOTO 2170
    2000  IF I=2 THEN 2040
    2010  LET Y1=Y
    2020  PRINT "UNION STRATEGY ";
    2030  NEXT I
    2040  LET Y2=Y
    2050  LET Y=Y1
    2060  IF Y2=5 THEN 2020
    2070  REM : SIMULATED LOSSES-NORTH
    2080  LET C6=(2*C2/5)*(1+1/(2*(ABS(Y2-Y)+1)))
    2090  LET C6=C6*(1.28+(5*M2/6)/(B(2)+1))
    2100  LET C6=INT(C6*(1+1/O2)+.5)
    2110  REM - IF LOSS > MEN PRESENT, RESCALE LOSSES
    2120  LET E2=100/O2
    2130  IF INT(C6+E2) "YES" THEN 2530
    2320  PRINT "COMPARED TO THE ACTUAL CASUALTIES AT "C$
    2330  PRINT "CONFEDERATE:"INT(100*(C5/C1)+.5)"% OF THE ORIGINAL"
    2340  PRINT "UNION:      "INT(100*(C6/C2)+.5)"% OF THE ORIGINAL"
    2350  PRINT
    2360  REM - 1 WHO WON
    2370  IF U <> 1 THEN 2380
    2375  IF U2=1 THEN 2460
    2380  IF U=1 THEN 2420
    2390  IF U2=1 THEN 2440
    2400  IF C5+E=C6+E2 THEN 2460
    2410  IF C5+E 0 THEN 3180
    3140  INPUT Y2
    3150  IF Y2 <=0 THEN 3160
    3155  IF Y2<5 THEN 3290
    3160  PRINT "ENTER 1 , 2 ,3 , OR 4 (USUALLY PREVIOUS UNION STRATEGY)"
    3170  GOTO 3140
    3180  LET S0=0
    3190  LET R=100*RND(0)
    3200  FOR I=1 TO 4
    3210  LET S0=S0+S(I)
    3220  REM - IF ACTUAL STRATEGY INFO IS IN PROGRAM DATA STATEMENTS
    3230  REM   THEN R-100 IS EXTRA WEIGHT GIVEN TO THAT STATEGY.
    3240  IF R Wins + Draws + Losses;
            public bool Surrendered { get; private set; } // Y, Y2 == 5
    
            public int CumulativeHistoricCasualties { get; private set; } // P1, P2
            public int CumulativeSimulatedCasualties { get; private set; } // T1, T2
            public int CumulativeHistoricMen { get; private set; } // M3, M4
    
            private int income; // R1, R2
            private int moneySpent; // Q1, Q2
    
            private bool IsFirstBattle => income == 0;
    
            // This battle
            private int historicMen; // M1, M2
            public int HistoricCasualties { get; private set; }
    
            public int Money { get; private set; } // D(n)
            public int Men { get; private set; } // M5, M6
            public int Inflation { get; private set; } // I1, I2
            public int InflationDisplay => Side == Side.Confederate ? Inflation + 15 : Inflation; // Confederate inflation is shown with 15 added - no idea why!
    
            private readonly Dictionary allocations = new(); // F(n), H(n), B(n) for food, salaries, ammunition
    
            public int Strategy { get; protected set; } // Y1, Y2
    
            public double Morale => (2.0 * allocations[Resource.Food] * allocations[Resource.Food] + allocations[Resource.Salaries] * allocations[Resource.Salaries]) / (reducedAvailableMen * reducedAvailableMen + 1); // O, O2
    
            public int Casualties { get; protected set; } // C5, C6
            public int Desertions { get; protected set; } // E, E2
            public int MenLost => Casualties + Desertions;
            public bool AllLost { get; private set; } // U, U2
    
            private double reducedAvailableMen; // F1
    
            protected virtual double FractionUnspent => (income - moneySpent) / (income + 1.0);
    
            public void PrepareBattle(int men, int casualties)
            {
                historicMen = men;
                HistoricCasualties = casualties;
                Inflation = 10 + (Losses - Wins) * 2;
                Money = 100 * (int)(men * (100 - Inflation) / 2000.0 * (1 + FractionUnspent) + 0.5);
                Men = (int)(men * 1 + (CumulativeHistoricCasualties - CumulativeSimulatedCasualties) / (CumulativeHistoricMen + 1.0));
                reducedAvailableMen = men * 5.0 / 6.0;
            }
    
            public virtual void AllocateResources()
            {
                Console.WriteLine($"{Side} General ---\nHow much do you wish to spend for");
                while (true)
                {
                    foreach (Resource resource in Enum.GetValues())
                    {
                        if (EnterResource(resource))
                            break;
                    }
                    if (allocations.Values.Sum() <= Money)
                        return;
                    Console.WriteLine($"Think again! You have only ${Money}");
                }
            }
    
            private bool EnterResource(Resource resource)
            {
                while (true)
                {
                    Console.WriteLine($" - {resource}");
                    switch ((int.TryParse(Console.ReadLine(), out int val), val))
                    {
                        case (false, _):
                            Console.WriteLine("Not a valid number");
                            break;
                        case (_, < 0):
                            Console.WriteLine("Negative values not allowed");
                            break;
                        case (_, 0) when IsFirstBattle:
                            Console.WriteLine("No previous entries");
                            break;
                        case (_, 0):
                            Console.WriteLine("Assume you want to keep same allocations");
                            return true;
                        case (_, > 0):
                            allocations[resource] = val;
                            return false;
                    }
                }
            }
    
            public virtual void DisplayMorale()
            {
                Console.WriteLine($"{Side} morale is {Morale switch { < 5 => "Poor", < 10 => "Fair", _ => "High" }}");
            }
    
            public virtual bool ChooseStrategy(bool isReplay) => EnterStrategy(true, "(1-5)");
    
            protected bool EnterStrategy(bool canSurrender, string hint)
            {
                Console.WriteLine($"{Side} strategy {hint}");
                while (true)
                {
                    switch ((int.TryParse(Console.ReadLine(), out int val), val))
                    {
                        case (false, _):
                            Console.WriteLine("Not a valid number");
                            break;
                        case (_, 5) when canSurrender:
                            Surrendered = true;
                            Console.WriteLine($"The {Side} general has surrendered");
                            return true;
                        case (_, < 1 or >= 5):
                            Console.WriteLine($"Strategy {val} not allowed.");
                            break;
                        default:
                            Strategy = val;
                            return false;
                    }
                }
            }
    
            public virtual void CalculateLosses(Army opponent)
            {
                AllLost = false;
                int stratFactor = 2 * (Math.Abs(Strategy - opponent.Strategy) + 1);
                Casualties = (int)Math.Round(HistoricCasualties * 0.4 * (1 + 1.0 / stratFactor) * (1 + 1 / Morale) * (1.28 + reducedAvailableMen / (allocations[Resource.Ammunition] + 1)));
                Desertions = (int)Math.Round(100 / Morale);
    
                // If losses > men present, rescale losses
                if (MenLost > Men)
                {
                    Casualties = 13 * Men / 20;
                    Desertions = Men - Casualties;
                    AllLost = true;
                }
            }
    
            public void RecordResult(Side winner)
            {
                if (winner == Side)
                    Wins++;
                else if (winner == Side.Both)
                    Draws++;
                else
                    Losses++;
    
                CumulativeSimulatedCasualties += MenLost;
                CumulativeHistoricCasualties += HistoricCasualties;
                moneySpent += allocations.Values.Sum();
                income += historicMen * (100 - Inflation) / 20;
                CumulativeHistoricMen += historicMen;
    
                LearnStrategy();
            }
    
            protected virtual void LearnStrategy() { }
    
            public void DisplayWarResult(Army opponent)
            {
                Console.WriteLine("\n\n\n\n");
                Console.WriteLine($"The {Side} general has won {Wins} battles and lost {Losses}");
                Side winner = (Surrendered, opponent.Surrendered, Wins < Losses) switch
                {
                    (_, true, _) => Side,
                    (true, _, _) or (_, _, true) => opponent.Side,
                    _ => Side
                };
                Console.WriteLine($"The {winner} general has won the war\n");
            }
    
            public virtual void DisplayStrategies() { }
        }
    
        class ComputerArmy : Army
        {
            public int[] StrategyProb { get; } = { 25, 25, 25, 25 }; // S(n)
            private readonly Random strategyRng = new();
    
            public ComputerArmy(Side side) : base(side) { }
    
            protected override double FractionUnspent => 0.0;
    
            public override void AllocateResources() { }
    
            public override void DisplayMorale() { }
    
            public override bool ChooseStrategy(bool isReplay)
            {
                if (isReplay)
                    return EnterStrategy(false, $"(1-4; usually previous {Side} strategy)");
    
                // Basic code comments say "If actual strategy info is in  data then r-100 is extra weight given to that strategy" but there's no data or code to do it.
                int strategyChosenProb = strategyRng.Next(100); // 0-99
                int sumProbs = 0;
                for (int i = 0; i < 4; i++)
                {
                    sumProbs += StrategyProb[i];
                    if (strategyChosenProb < sumProbs)
                    {
                        Strategy = i + 1;
                        break;
                    }
                }
                Console.WriteLine($"{Side} strategy is {Strategy}");
                return false;
            }
    
            protected override void LearnStrategy()
            {
                // Learn  present strategy, start forgetting old ones
                // - present strategy gains 3 * s, others lose s probability points, unless a strategy falls below 5 %.
                const int s = 3;
                int presentGain = 0;
                for (int i = 0; i < 4; i++)
                {
                    if (StrategyProb[i] >= 5)
                    {
                        StrategyProb[i] -= s;
                        presentGain += s;
                    }
                }
                StrategyProb[Strategy - 1] += presentGain;
            }
    
            public override void CalculateLosses(Army opponent)
            {
                Casualties = (int)(17.0 * HistoricCasualties * opponent.HistoricCasualties / (opponent.Casualties * 20));
                Desertions = (int)(5 * opponent.Morale);
            }
    
            public override void DisplayStrategies()
            {
                ConsoleUtils.WriteWordWrap($"\nIntelligence suggests that the {Side} general used strategies 1, 2, 3, 4 in the following percentages:");
                Console.WriteLine(string.Join(", ", StrategyProb));
            }
        }
    }
    
    
    ================================================
    FILE: 27_Civil_War/csharp/Battle.cs
    ================================================
    using System;
    using System.Collections.Generic;
    
    namespace CivilWar
    {
        public enum Side { Confederate, Union, Both }
        public enum Option { Battle, Replay, Quit }
    
        public record Battle(string Name, int[] Men, int[] Casualties, Side Offensive, string Description)
        {
            public static readonly List Historic = new()
            {
                new("Bull Run", new[] { 18000, 18500 }, new[] { 1967, 2708 }, Side.Union, "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."),
                new("Shiloh", new[] { 40000, 44894 }, new[] { 10699, 13047 }, Side.Both, "April 6-7, 1862.  The confederate surprise attack at Shiloh failed due to poor organization."),
                new("Seven Days", new[] { 95000, 115000 }, new[] { 20614, 15849 }, Side.Both, "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."),
                new("Second Bull Run", new[] { 54000, 63000 }, new[] { 10000, 14000 }, Side.Confederate, "Aug 29-30, 1862.  The combined confederate forces under Lee and Jackson drove the union forces back into Washington."),
                new("Antietam", new[] { 40000, 50000 }, new[] { 10000, 12000 }, Side.Both, "Sept 17, 1862.  The south failed to incorporate Maryland into the confederacy."),
                new("Fredericksburg", new[] { 75000, 120000 }, new[] { 5377, 12653 }, Side.Union, "Dec 13, 1862.  The confederacy under Lee successfully repulsed an attack by the union under Gen. Burnside."),
                new("Murfreesboro", new[] { 38000, 45000 }, new[] { 11000, 12000 }, Side.Union, "Dec 31, 1862.  The south under Gen. Bragg won a close battle."),
                new("Chancellorsville", new[] { 32000, 90000 }, new[] { 13000, 17197 }, Side.Confederate, "May 1-6, 1863.  The south had a costly victory and lost one of their outstanding generals, 'stonewall' Jackson."),
                new("Vicksburg", new[] { 50000, 70000 }, new[] { 12000, 19000 }, Side.Union, "July 4, 1863.  Vicksburg was a costly defeat for the south because it gave the union access to the Mississippi."),
                new("Gettysburg", new[] { 72500, 85000 }, new[] { 20000, 23000 }, Side.Both, "July 1-3, 1863.  A southern mistake by Gen. Lee at Gettysburg cost them one of the most crucial battles of the war."),
                new("Chickamauga", new[] { 66000, 60000 }, new[] { 18000, 16000 }, Side.Confederate, "Sept. 15, 1863. Confusion in a forest near Chickamauga led to a costly southern victory."),
                new("Chattanooga", new[] { 37000, 60000 }, new[] { 36700, 5800 }, Side.Confederate, "Nov. 25, 1863. After the south had sieged Gen. Rosencrans’ army for three months, Gen. Grant broke the siege."),
                new("Spotsylvania", new[] { 62000, 110000 }, new[] { 17723, 18000 }, Side.Confederate, "May 5, 1864.  Grant's plan to keep Lee isolated began to fail here, and continued at Cold Harbor and Petersburg."),
                new("Atlanta", new[] { 65000, 100000 }, new[] { 8500, 3700 }, Side.Union, "August, 1864.  Sherman and three veteran armies converged on Atlanta and dealt the death blow to the confederacy."),
            };
    
            public static (Option, Battle?) SelectBattle()
            {
                Console.WriteLine("\n\n\nWhich battle do you wish to simulate?");
                return int.Parse(Console.ReadLine() ?? "") switch
                {
                    0 => (Option.Replay, null),
                    >0 and <15 and int n  => (Option.Battle, Historic[n-1]),
                    _ => (Option.Quit, null)
                };
            }
        }
    }
    
    
    ================================================
    FILE: 27_Civil_War/csharp/CivilWar.csproj
    ================================================
    
    
      
        Exe
        net5.0
        enable
      
    
    
    
    
    ================================================
    FILE: 27_Civil_War/csharp/CivilWar.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.31005.135
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CivilWar", "CivilWar.csproj", "{4EE5EFE7-2D60-464A-9F8D-4BBD2A14AAC7}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{4EE5EFE7-2D60-464A-9F8D-4BBD2A14AAC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{4EE5EFE7-2D60-464A-9F8D-4BBD2A14AAC7}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{4EE5EFE7-2D60-464A-9F8D-4BBD2A14AAC7}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{4EE5EFE7-2D60-464A-9F8D-4BBD2A14AAC7}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {59BCA2DE-D5C7-40F7-BB99-B5C8D2416D8B}
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 27_Civil_War/csharp/ConsoleUtils.cs
    ================================================
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace CivilWar
    {
        static class ConsoleUtils
        {
            public static bool InputYesOrNo()
            {
                while (true)
                {
                    var answer = Console.ReadLine();
                    switch (answer?.ToLower())
                    {
                        case "no":
                            return false;
                        case "yes":
                            return true;
                        default:
                            Console.WriteLine("(Answer Yes or No)");
                            break;
                    }
                }
            }
    
            public static void WriteWordWrap(string text)
            {
                var line = new StringBuilder(Console.WindowWidth);
                foreach (var paragraph in text.Split(Environment.NewLine))
                {
                    line.Clear();
                    foreach (var word in paragraph.Split(' '))
                    {
                        if (line.Length + word.Length < Console.WindowWidth)
                        {
                            if (line.Length > 0)
                                line.Append(' ');
                            line.Append(word);
                        }
                        else
                        {
                            Console.WriteLine(line.ToString());
                            line.Clear().Append(word);
                        }
                    }
                    Console.WriteLine(line.ToString());
                }
            }
    
            public static void WriteTable(ICollection items, List> rows, bool transpose = false)
            {
                int cols = items.Count + 1;
                var content = rows.Select(r => r.Format(items)).ToList();
                if (transpose)
                {
                    content = Enumerable.Range(0, cols).Select(col => content.Select(r => r[col]).ToArray()).ToList();
                    cols = rows.Count;
                }
                var colWidths = Enumerable.Range(0, cols).Select(col => content.Max(c => c[col].Length)).ToArray();
    
                foreach (var row in content)
                {
                    for (int col = 0; col < cols; col++)
                    {
                        var space = new string(' ', colWidths[col] - row[col].Length);
                        row[col] = col == 0 ? row[col] + space : space + row[col]; // left-align first col; right-align other cols
                    }
                }
    
                var sb = new StringBuilder();
                var horizBars = colWidths.Select(w => new string('═', w)).ToArray();
    
                void OneRow(string[] cells, char before, char between, char after)
                {
                    sb.Append(before);
                    sb.AppendJoin(between, cells);
                    sb.Append(after);
                    sb.AppendLine();
                }
    
                OneRow(horizBars, '╔', '╦', '╗');
                bool first = true;
                foreach (var row in content)
                {
                    if (first)
                        first = false;
                    else
                        OneRow(horizBars, '╠', '╬', '╣');
                    OneRow(row, '║', '║', '║');
                }
                OneRow(horizBars, '╚', '╩', '╝');
    
                Console.WriteLine(sb.ToString());
            }
    
            public record TableRow(string Name, Func Data, string Before = "", string After = "")
            {
                private string FormatItem(T item) => $" {Before}{Data(item)}{After} ";
    
                public string[] Format(IEnumerable items) => items.Select(FormatItem).Prepend($" {Name} ").ToArray();
            }
        }
    }
    
    
    ================================================
    FILE: 27_Civil_War/csharp/GameOptions.cs
    ================================================
    using System;
    
    using static CivilWar.ConsoleUtils;
    
    namespace CivilWar
    {
        public record GameOptions(bool TwoPlayers, bool ShowDescriptions)
        {
            public static GameOptions Input()
            {
                Console.WriteLine(
    @"                          Civil War
                   Creative Computing, Morristown, New Jersey
    
    
    Do you want instructions?");
    
                const string instructions = @"This is a civil war simulation.
    To play type a response when the computer asks.
    Remember that all factors are interrelated and that your responses could change history. Facts and figures used are based on the actual occurrence. Most battles tend to result as they did in the civil war, but it all depends on you!!
    
    The object of the game is to win as many battles as possible.
    
    Your choices for defensive strategy are:
            (1) artillery attack
            (2) fortification against frontal attack
            (3) fortification against flanking maneuvers
            (4) falling back
    Your choices for offensive strategy are:
            (1) artillery attack
            (2) frontal attack
            (3) flanking maneuvers
            (4) encirclement
    You may surrender by typing a '5' for your strategy.";
    
                if (InputYesOrNo())
                    WriteWordWrap(instructions);
    
                Console.WriteLine("\n\nAre there two generals present?");
                bool twoPlayers = InputYesOrNo();
                if (!twoPlayers)
                    Console.WriteLine("\nYou are the confederacy.  Good luck!\n");
    
                WriteWordWrap(
                @"Select a battle by typing a number from 1 to 14 on request.  Type any other number to end the simulation. But '0' brings back exact previous battle situation allowing you to replay it.
    
    Note: a negative Food$ entry causes the program to use the entries from the previous battle
    
    After requesting a battle, do you wish battle descriptions (answer yes or no)");
                bool showDescriptions = InputYesOrNo();
    
                return new GameOptions(twoPlayers, showDescriptions);
            }
        }
    }
    
    
    ================================================
    FILE: 27_Civil_War/csharp/Program.cs
    ================================================
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using CivilWar;
    
    var options = GameOptions.Input();
    var armies = new List { new Army(Side.Confederate), options.TwoPlayers ? new Army(Side.Union) : new ComputerArmy(Side.Union) };
    
    Battle? battle = null;
    while (OneBattle(ref battle)) { }
    DisplayResult();
    
    bool OneBattle(ref Battle? previous)
    {
        var (option, selected) = Battle.SelectBattle();
        var (battle, isReplay, quit) = option switch
        {
            Option.Battle => (selected!, false, false),
            Option.Replay when previous != null => (previous, true, false), // can't replay if no previous battle
            _ => (null!, false, true),
        };
        if (quit)
            return false;
    
        if (!isReplay)
        {
            Console.WriteLine($"This is the battle of {battle.Name}.");
            if (options.ShowDescriptions)
                ConsoleUtils.WriteWordWrap(battle.Description);
            armies.ForEach(a => a.PrepareBattle(battle.Men[(int)a.Side], battle.Casualties[(int)a.Side]));
        }
    
        ConsoleUtils.WriteTable(armies, new()
        {
            new("", a => a.Side),
            new("Men", a => a.Men),
            new("Money", a => a.Money, Before: "$"),
            new("Inflation", a => a.InflationDisplay, After: "%")
        });
    
        armies.ForEach(a => a.AllocateResources());
        armies.ForEach(a => a.DisplayMorale());
    
        string offensive = battle.Offensive switch
        {
            Side.Confederate => "You are on the offensive",
            Side.Union => "You are on the defensive",
            _ => "Both sides are on the offensive"
        };
        Console.WriteLine($"Confederate general---{offensive}");
    
        if (armies.Any(a => a.ChooseStrategy(isReplay)))
        {
            return false; // someone surrendered
        }
        armies[0].CalculateLosses(armies[1]);
        armies[1].CalculateLosses(armies[0]);
    
        ConsoleUtils.WriteTable(armies, new()
        {
            new("", a => a.Side),
            new("Casualties", a => a.Casualties),
            new("Desertions", a => a.Desertions),
        });
        if (options.TwoPlayers)
        {
            var oneDataCol = new[] { 1 };
            Console.WriteLine($"Compared to the actual casualties at {battle.Name}");
            ConsoleUtils.WriteTable(oneDataCol, armies.Select(a => new ConsoleUtils.TableRow(
                a.Side.ToString(),
                _ => $"{(double)a.Casualties / battle.Casualties[(int)a.Side]}", After: "% of the original")
            ).ToList());
        }
    
        Side winner;
        switch (armies[0].AllLost, armies[1].AllLost, armies[0].MenLost - armies[1].MenLost)
        {
            case (true, true, _) or (false, false, 0):
                Console.WriteLine("Battle outcome unresolved");
                winner = Side.Both; // Draw
                break;
            case (false, true, _) or (false, false, < 0):
                Console.WriteLine($"The Confederacy wins {battle.Name}");
                winner = Side.Confederate;
                break;
            case (true, false, _) or (false, false, > 0):
                Console.WriteLine($"The Union wins {battle.Name}");
                winner = Side.Union;
                break;
        }
        if (!isReplay)
        {
            armies.ForEach(a => a.RecordResult(winner));
        }
        Console.WriteLine("---------------");
        previous = battle;
        return true;
    }
    
    void DisplayResult()
    {
        armies[0].DisplayWarResult(armies[1]);
    
        int battles = armies[0].BattlesFought;
        if (battles > 0)
        {
            Console.WriteLine($"For the {battles} battles fought (excluding reruns)");
    
            ConsoleUtils.WriteTable(armies, new()
            {
                new("", a => a.Side),
                new("Historical Losses", a => a.CumulativeHistoricCasualties),
                new("Simulated Losses", a => a.CumulativeSimulatedCasualties),
                new("  % of original", a => ((double)a.CumulativeSimulatedCasualties / a.CumulativeHistoricCasualties).ToString("p2"))
            }, transpose: true);
    
            armies[1].DisplayStrategies();
        }
    }
    
    
    ================================================
    FILE: 27_Civil_War/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 27_Civil_War/java/.gitignore
    ================================================
    *.class
    *.iml
    .idea/
    
    
    ================================================
    FILE: 27_Civil_War/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 27_Civil_War/java/src/CivilWar.java
    ================================================
    import java.io.PrintStream;
    import java.util.InputMismatchException;
    import java.util.List;
    import java.util.Scanner;
    import java.util.function.Function;
    import java.util.function.Predicate;
    
    import static java.util.stream.Collectors.joining;
    import static java.util.stream.IntStream.range;
    
    @SuppressWarnings("SpellCheckingInspection")
    public class CivilWar {
    
        private final PrintStream out;
        private final List data;
    
        private final BattleResults results;
    
        private BattleState currentBattle;
        private int numGenerals;
        private int battleNumber;
        private boolean wantBattleDescriptions;
        private final int[] strategies;
    
        private int confedStrategy;
        private int unionStrategy;
    
        private final ArmyPair resources;
        private final ArmyPair totalExpectedCasualties;
        private final ArmyPair totalCasualties;
        private final ArmyPair revenue;
        private final ArmyPair inflation;
        private final ArmyPair totalExpenditure;
        private final ArmyPair totalTroops;
    
        private boolean excessiveConfederateLosses;
        private boolean excessiveUnionLosses;
    
        private boolean confedSurrender;
        private boolean unionSurrender;
    
        private final static String YES_NO_REMINDER = "(ANSWER YES OR NO)";
        private final static Predicate YES_NO_CHECKER = i -> isYes(i) || isNo(i);
    
        /**
         * ORIGINAL GAME DESIGN: CRAM, GOODIE, HIBBARD LEXINGTON H.S.
         * MODIFICATIONS: G. PAUL, R. HESS (TIES), 1973
         */
        public static void main(String[] args) {
            var x = new CivilWar(System.out);
            x.showCredits();
    
            // LET D=RND(-1) ???
    
            System.out.print("DO YOU WANT INSTRUCTIONS? ");
    
            if (isYes(inputString(YES_NO_CHECKER, YES_NO_REMINDER))) {
                x.showHelp();
            }
    
            x.gameLoop();
        }
    
        private void gameLoop() {
            out.println();
            out.println();
            out.println();
            out.print("ARE THERE TWO GENERALS PRESENT (ANSWER YES OR NO)? ");
    
            if (isYes(inputString(YES_NO_CHECKER, YES_NO_REMINDER))) {
                this.numGenerals = 2;
            } else {
                this.numGenerals = 1;
                out.println();
                out.println("YOU ARE THE CONFEDERACY.   GOOD LUCK!");
                out.println();
            }
    
            out.println("SELECT A BATTLE BY TYPING A NUMBER FROM 1 TO 14 ON");
            out.println("REQUEST.  TYPE ANY OTHER NUMBER TO END THE SIMULATION.");
            out.println("BUT '0' BRINGS BACK EXACT PREVIOUS BATTLE SITUATION");
            out.println("ALLOWING YOU TO REPLAY IT");
            out.println();
            out.println("NOTE: A NEGATIVE FOOD$ ENTRY CAUSES THE PROGRAM TO ");
            out.println("USE THE ENTRIES FROM THE PREVIOUS BATTLE");
            out.println();
    
            out.print("AFTER REQUESTING A BATTLE, DO YOU WISH BATTLE DESCRIPTIONS (ANSWER YES OR NO)? ");
    
            this.wantBattleDescriptions = isYes(inputString(YES_NO_CHECKER, YES_NO_REMINDER));
    
            while (true) {
                var battle = startBattle();
                if (battle == null) {
                    break;
                }
    
                this.currentBattle = battle;
    
                offensiveLogic(battle.data);
    
                calcLosses(battle);
    
                reset();
    
                if (this.confedSurrender) {
                    out.println("THE CONFEDERACY HAS SURRENDERED");
                } else if (unionSurrender) {  // FIXME Is this actually possible? 2850
                    out.println("THE UNION HAS SURRENDERED.");
                }
            }
    
            complete();
        }
    
        private BattleState startBattle() {
            out.println();
            out.println();
            out.println();
            out.print("WHICH BATTLE DO YOU WISH TO SIMULATE ? ");
    
            var battleNumber = inputInt(i -> i >= 1 || (i == 0 && this.currentBattle != null), i -> "BATTLE " + i + " NOT ALLOWED.");
    
            if (battleNumber == 0) {
                out.println(this.currentBattle.data.name + " INSTANT REPLAY");
                return this.currentBattle;
            }
    
            if (battleNumber > this.data.size()) {  // TYPE ANY OTHER NUMBER TO END THE SIMULATION
                return null;
            }
    
            this.battleNumber = battleNumber;
    
            var battle = this.data.get(this.battleNumber - 1);
            var battleState = new BattleState(battle);
    
    
            excessiveConfederateLosses = false;
    
            // INFLATION CALC
            // REM - ONLY IN PRINTOUT IS CONFED INFLATION = I1+15%
            inflation.confederate = 10 + (results.union - results.confederate) * 2;
            inflation.union = 10 + (results.confederate - results.union) * 2;
    
            // MONEY AVAILABLE
            resources.confederate.budget = 100 * (int) Math.floor((battle.troops.confederate * (100.0 - inflation.confederate) / 2000) * (1 + (revenue.confederate - totalExpenditure.confederate) / (revenue.confederate + 1.0)) + .5);
    
            // MEN AVAILABLE
            battleState.F1 = 5 * battle.troops.confederate / 6.0;
    
            if (this.numGenerals == 2) {
                resources.union.budget = 100 * (int) Math.floor((battle.troops.union * (100.0 - inflation.union) / 2000) * (1 + (revenue.union - totalExpenditure.union) / (revenue.union + 1.0)) + .5);
            } else {
                resources.union.budget = 100 * (int) Math.floor(battle.troops.union * (100.0 - inflation.union) / 2000 + .5);
            }
    
            out.println();
            out.println();
            out.println();
            out.println();
            out.println();
            out.println("THIS IS THE BATTLE OF " + battle.name);
    
            if (this.wantBattleDescriptions) {
                for (var eachLine : battle.blurb) {
                    out.println(eachLine);
                }
            }
    
            out.println();
            out.println("          CONFEDERACY     UNION");
            out.println("MEN         " + getConfedTroops(battle) + "          " + getUnionTroops(battle));
            out.println("MONEY     $ " + resources.confederate.budget + "       $ " + resources.union.budget);
            out.println("INFLATION   " + (inflation.confederate + 15) + "%          " + inflation.union + "%");
    
            // ONLY IN PRINTOUT IS CONFED INFLATION = I1+15%
            // IF TWO GENERALS, INPUT CONFED. FIRST
    
            var terminalInput = new Scanner(System.in);
    
            for (int i = 0; i < numGenerals; i++) {
                out.println();
    
                ArmyResources currentResources;
    
                if (this.numGenerals == 1 || i == 0) {
                    out.print("CONFEDERATE GENERAL --- ");
                    currentResources = resources.confederate;
                } else {
                    out.print("UNION GENERAL --- ");
                    currentResources = resources.union;
                }
    
                var validInputs = false;
                while (!validInputs) {
                    out.println("HOW MUCH DO YOU WISH TO SPEND FOR");
                    out.print("- FOOD...... ? ");
                    var food = terminalInput.nextInt();
                    if (food == 0) {
                        if (this.revenue.confederate != 0) {
                            out.println("ASSUME YOU WANT TO KEEP SAME ALLOCATIONS");
                            out.println();
                        }
                    } else {
                        currentResources.food = food;
                    }
    
                    out.print("- SALARIES.. ? ");
                    currentResources.salaries = terminalInput.nextInt();
    
                    out.print("- AMMUNITION ? ");
                    currentResources.ammunition = terminalInput.nextInt();  // FIXME Retry if -ve
    
                    if (currentResources.getTotal() > currentResources.budget) {
                        out.println("THINK AGAIN! YOU HAVE ONLY $" + currentResources.budget);
                    } else {
                        validInputs = true;
                    }
                }
            }
    
            out.println();
    
            // Record Morale
            out.println(range(0, numGenerals).mapToObj(i -> moraleForArmy(battleState, i)).collect(joining(", ")));
    
            out.println();
    
            return battleState;
        }
    
        private int getUnionTroops(HistoricalDatum battle) {
            return (int) Math.floor(battle.troops.union * (1 + (totalExpectedCasualties.union - totalCasualties.union) / (totalTroops.union + 1.0)));
        }
    
        private int getConfedTroops(HistoricalDatum battle) {
            return (int) Math.floor(battle.troops.confederate * (1 + (totalExpectedCasualties.confederate - totalCasualties.confederate) / (totalTroops.confederate + 1.0)));
        }
    
        private String moraleForArmy(BattleState battleState, int armyIdx) {
            var builder = new StringBuilder();
    
            ArmyResources currentResources;
    
            if (this.numGenerals == 1 || armyIdx == 0) {
                builder.append("CONFEDERATE ");
                currentResources = resources.confederate;
            } else {
                builder.append("UNION ");
                currentResources = resources.union;
            }
    
            // FIND MORALE
            currentResources.morale = (2 * Math.pow(currentResources.food, 2) + Math.pow(currentResources.salaries, 2)) / Math.pow(battleState.F1, 2) + 1;
            if (currentResources.morale >= 10) {
                builder.append("MORALE IS HIGH");
            } else if (currentResources.morale >= 5) {
                builder.append("MORALE IS FAIR");
            } else {
                builder.append("MORALE IS POOR");
            }
    
            return builder.toString();
        }
    
        private enum OffensiveStatus {
            DEFENSIVE("YOU ARE ON THE DEFENSIVE"), OFFENSIVE("YOU ARE ON THE OFFENSIVE"), BOTH_OFFENSIVE("BOTH SIDES ARE ON THE OFFENSIVE");
    
            private final String label;
    
            OffensiveStatus(String label) {
                this.label = label;
            }
        }
    
        private void offensiveLogic(HistoricalDatum battle) {
            out.print("CONFEDERATE GENERAL---");
            // ACTUAL OFF/DEF BATTLE SITUATION
            out.println(battle.offensiveStatus.label);
    
            // CHOOSE STRATEGIES
    
            if (numGenerals == 2) {
                out.print("CONFEDERATE STRATEGY ? ");
            } else {
                out.print("YOUR STRATEGY ? ");
            }
    
            confedStrategy = inputInt(i -> i >= 1 && i <= 5, i -> "STRATEGY " + i + " NOT ALLOWED.");
            if (confedStrategy == 5) {  // 1970
                confedSurrender = true;
            }
    
            if (numGenerals == 2) {
                out.print("UNION STRATEGY ? ");
    
                unionStrategy = inputInt(i -> i >= 1 && i <= 5, i -> "STRATEGY " + i + " NOT ALLOWED.");
                if (unionStrategy == 5) {  // 1970
                    unionSurrender = true;
                }
            } else {
                unionStrategy();
            }
        }
    
        // 2070  REM : SIMULATED LOSSES-NORTH
        private UnionLosses simulateUnionLosses(HistoricalDatum battle) {
            var losses = (2.0 * battle.expectedCasualties.union / 5) * (1 + 1.0 / (2 * (Math.abs(unionStrategy - confedStrategy) + 1)));
            losses = losses * (1.28 + (5.0 * battle.troops.union / 6) / (resources.union.ammunition + 1));
            losses = Math.floor(losses * (1 + 1 / resources.union.morale) + 0.5);
            // IF LOSS > MEN PRESENT, RESCALE LOSSES
            var moraleFactor = 100 / resources.union.morale;
    
            if (Math.floor(losses + moraleFactor) >= getUnionTroops(battle)) {
                losses = Math.floor(13.0 * getUnionTroops(battle) / 20);
                moraleFactor = 7 * losses / 13;
                excessiveUnionLosses = true;
            }
    
            return new UnionLosses((int) losses, (int) Math.floor(moraleFactor));
        }
    
        // 2170: CALCULATE SIMULATED LOSSES
        private void calcLosses(BattleState battle) {
            // 2190
            out.println();
            out.println("            CONFEDERACY    UNION");
    
            var C5 = (2 * battle.data.expectedCasualties.confederate / 5) * (1 + 1.0 / (2 * (Math.abs(unionStrategy - confedStrategy) + 1)));
            C5 = (int) Math.floor(C5 * (1 + 1.0 / resources.confederate.morale) * (1.28 + battle.F1 / (resources.confederate.ammunition + 1.0)) + .5);
            var E = 100 / resources.confederate.morale;
    
            if (C5 + 100 / resources.confederate.morale >= battle.data.troops.confederate * (1 + (totalExpectedCasualties.confederate - totalCasualties.confederate) / (totalTroops.confederate + 1.0))) {
                C5 = (int) Math.floor(13.0 * battle.data.troops.confederate / 20 * (1 + (totalExpectedCasualties.union - totalCasualties.confederate) / (totalTroops.confederate + 1.0)));
                E = 7 * C5 / 13.0;
                excessiveConfederateLosses = true;
            }
    
            /////  2270
    
            final UnionLosses unionLosses;
    
            if (this.numGenerals == 1) {
                unionLosses = new UnionLosses((int) Math.floor(17.0 * battle.data.expectedCasualties.union * battle.data.expectedCasualties.confederate / (C5 * 20)), (int) Math.floor(5 * resources.confederate.morale));
            } else {
                unionLosses = simulateUnionLosses(battle.data);
            }
    
            out.println("CASUALTIES:  " + rightAlignInt(C5) + "        " + rightAlignInt(unionLosses.losses));
            out.println("DESERTIONS:  " + rightAlignInt(E) + "        " + rightAlignInt(unionLosses.desertions));
            out.println();
    
            if (numGenerals == 2) {
                out.println("COMPARED TO THE ACTUAL CASUALTIES AT " + battle.data.name);
                out.println("CONFEDERATE: " + (int) Math.floor(100 * (C5 / (double) battle.data.expectedCasualties.confederate) + 0.5) + " % OF THE ORIGINAL");
                out.println("UNION:       " + (int) Math.floor(100 * (unionLosses.losses / (double) battle.data.expectedCasualties.union) + 0.5) + " % OF THE ORIGINAL");
    
                out.println();
    
                // REM - 1 WHO WON
                var winner = findWinner(C5 + E, unionLosses.losses + unionLosses.desertions);
                switch (winner) {
                    case UNION -> {
                        out.println("THE UNION WINS " + battle.data.name);
                        results.union++;
                    }
                    case CONFED -> {
                        out.println("THE CONFEDERACY WINS " + battle.data.name);
                        results.confederate++;
                    }
                    case INDECISIVE -> {
                        out.println("BATTLE OUTCOME UNRESOLVED");
                        results.indeterminate++;
                    }
                }
            } else {
                out.println("YOUR CASUALTIES WERE " + Math.floor(100 * (C5 / (double) battle.data.expectedCasualties.confederate) + 0.5) + "% OF THE ACTUAL CASUALTIES AT " + battle.data.name);
    
                // FIND WHO WON
    
                if (excessiveConfederateLosses) {
                    out.println("YOU LOSE " + battle.data.name);
    
                    if (this.battleNumber != 0) {
                        results.union++;
                    }
                } else {
                    out.println("YOU WIN " + battle.data.name);
                    // CUMULATIVE BATTLE FACTORS WHICH ALTER HISTORICAL RESOURCES AVAILABLE.IF A REPLAY DON'T UPDATE.
                    results.confederate++;
                }
            }
    
            if (this.battleNumber != 0) {
                totalCasualties.confederate += (int) (C5 + E);
                totalCasualties.union += unionLosses.losses + unionLosses.desertions;
                totalExpectedCasualties.confederate += battle.data.expectedCasualties.confederate;
                totalExpectedCasualties.union += battle.data.expectedCasualties.union;
                totalExpenditure.confederate += resources.confederate.getTotal();
                totalExpenditure.union += resources.union.getTotal();
                revenue.confederate += battle.data.troops.confederate * (100 - inflation.confederate) / 20;
                revenue.union += battle.data.troops.union * (100 - inflation.union) / 20;
                totalTroops.confederate += battle.data.troops.confederate;
                totalTroops.union += battle.data.troops.union;
    
                updateStrategies(this.confedStrategy);
            }
        }
    
        // 2790
        private void reset() {
            excessiveConfederateLosses = excessiveUnionLosses = false;
    
            out.println("---------------");
        }
    
        // 2820  REM------FINISH OFF
        private void complete() {
            out.println();
            out.println();
            out.println();
            out.println();
            out.println();
            out.println();
            out.println("THE CONFEDERACY HAS WON " + results.confederate + " BATTLES AND LOST " + results.union);
    
            if (this.unionStrategy == 5) {
                out.println("THE CONFEDERACY HAS WON THE WAR");
            }
    
            if (this.confedStrategy == 5 || results.confederate <= results.union) {
                out.println("THE UNION HAS WON THE WAR");
            }
    
            out.println();
    
            // FIXME 2960  IF R1=0 THEN 3100
    
            out.println("FOR THE " + results.getTotal() + " BATTLES FOUGHT (EXCLUDING RERUNS)");
            out.println("                       CONFEDERACY    UNION");
            out.println("HISTORICAL LOSSES      " + (int) Math.floor(totalExpectedCasualties.confederate + .5) + "          " + (int) Math.floor(totalExpectedCasualties.union + .5));
            out.println("SIMULATED LOSSES       " + (int) Math.floor(totalCasualties.confederate + .5) + "          " + (int) Math.floor(totalCasualties.union + .5));
            out.println();
            out.println("    % OF ORIGINAL      " + (int) Math.floor(100 * ((double) totalCasualties.confederate / totalExpectedCasualties.confederate) + .5) + "             " + (int) Math.floor(100 * ((double) totalCasualties.union / totalExpectedCasualties.union) + .5));
    
            if (this.numGenerals == 1) {
                out.println();
                out.println("UNION INTELLIGENCE SUGGESTS THAT THE SOUTH USED ");
                out.println("STRATEGIES 1, 2, 3, 4 IN THE FOLLOWING PERCENTAGES");
                out.println(this.strategies[0] + "," + this.strategies[1] + "," + this.strategies[2] + "," + this.strategies[3]);
            }
        }
    
        private Winner findWinner(double confLosses, double unionLosses) {
            if (this.excessiveConfederateLosses && this.excessiveUnionLosses) {
                return Winner.INDECISIVE;
            }
    
            if (this.excessiveConfederateLosses) {
                return Winner.UNION;
            }
    
            if (this.excessiveUnionLosses || confLosses < unionLosses) {
                return Winner.CONFED;
            }
    
            if (confLosses == unionLosses) {
                return Winner.INDECISIVE;
            }
    
            return Winner.UNION;  // FIXME Really? 2400-2420 ?
        }
    
        private enum Winner {
            CONFED, UNION, INDECISIVE
        }
    
        private void unionStrategy() {
            // 3130 ... so you can only input / override Union strategy on re-run??
            if (this.battleNumber == 0) {
                out.print("UNION STRATEGY ? ");
                var terminalInput = new Scanner(System.in);
                unionStrategy = terminalInput.nextInt();
                if (unionStrategy < 0) {
                    out.println("ENTER 1, 2, 3, OR 4 (USUALLY PREVIOUS UNION STRATEGY)");
                    // FIXME Retry Y2 input !!!
                }
    
                if (unionStrategy < 5) {  // 3155
                    return;
                }
            }
    
            var S0 = 0;
            var r = 100 * Math.random();
    
            for (unionStrategy = 1; unionStrategy <= 4; unionStrategy++) {
                S0 += this.strategies[unionStrategy - 1];
                // IF ACTUAL STRATEGY INFO IS IN PROGRAM DATA STATEMENTS THEN R-100 IS EXTRA WEIGHT GIVEN TO THAT STATEGY.
                if (r < S0) {
                    break;
                }
            }
            // IF ACTUAL STRAT. IN,THEN HERE IS Y2= HIST. STRAT.
            out.println("UNION STRATEGY IS " + unionStrategy);
        }
    
        public CivilWar(PrintStream out) {
            this.out = out;
    
            this.results = new BattleResults();
    
            this.totalCasualties = new ArmyPair<>(0, 0);
            this.totalExpectedCasualties = new ArmyPair<>(0, 0);
            this.totalExpenditure = new ArmyPair<>(0, 0);
            this.totalTroops = new ArmyPair<>(0, 0);
    
            this.revenue = new ArmyPair<>(0, 0);
            this.inflation = new ArmyPair<>(0, 0);
    
            this.resources = new ArmyPair<>(new ArmyResources(), new ArmyResources());
    
            // UNION INFO ON LIKELY CONFEDERATE STRATEGY
            this.strategies = new int[]{25, 25, 25, 25};
    
            // READ HISTORICAL DATA.
            // HISTORICAL DATA...CAN ADD MORE (STRAT.,ETC) BY INSERTING DATA STATEMENTS AFTER APPRO. INFO, AND ADJUSTING READ
            this.data = List.of(
                    new HistoricalDatum("BULL RUN", new ArmyPair<>(18000, 18500), new ArmyPair<>(1967, 2708), OffensiveStatus.DEFENSIVE, new String[]{"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."}),
                    new HistoricalDatum("SHILOH", new ArmyPair<>(40000, 44894), new ArmyPair<>(10699, 13047), OffensiveStatus.OFFENSIVE, new String[]{"APRIL 6-7, 1862.  THE CONFEDERATE SURPRISE ATTACK AT", "SHILOH FAILED DUE TO POOR ORGANIZATION."}),
                    new HistoricalDatum("SEVEN DAYS", new ArmyPair<>(95000, 115000), new ArmyPair<>(20614, 15849), OffensiveStatus.OFFENSIVE, new String[]{"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."}),
                    new HistoricalDatum("SECOND BULL RUN", new ArmyPair<>(54000, 63000), new ArmyPair<>(10000, 14000), OffensiveStatus.BOTH_OFFENSIVE, new String[]{"AUG 29-30, 1862.  THE COMBINED CONFEDERATE FORCES UNDER", " LEE", "AND JACKSON DROVE THE UNION FORCES BACK INTO WASHINGTON."}),
                    new HistoricalDatum("ANTIETAM", new ArmyPair<>(40000, 50000), new ArmyPair<>(10000, 12000), OffensiveStatus.OFFENSIVE, new String[]{"SEPT 17, 1862.  THE SOUTH FAILED TO INCORPORATE MARYLAND", "INTO THE CONFEDERACY."}),
                    new HistoricalDatum("FREDERICKSBURG", new ArmyPair<>(75000, 120000), new ArmyPair<>(5377, 12653), OffensiveStatus.DEFENSIVE, new String[]{"DEC 13, 1862.  THE CONFEDERACY UNDER LEE SUCCESSFULLY", "REPULSED AN ATTACK BY THE UNION UNDER GEN. BURNSIDE."}),
                    new HistoricalDatum("MURFREESBORO", new ArmyPair<>(38000, 45000), new ArmyPair<>(11000, 12000), OffensiveStatus.DEFENSIVE, new String[]{"DEC 31, 1862.  THE SOUTH UNDER GEN. BRAGG WON A CLOSE BATTLE."}),
                    new HistoricalDatum("CHANCELLORSVILLE", new ArmyPair<>(32000, 90000), new ArmyPair<>(13000, 17197), OffensiveStatus.BOTH_OFFENSIVE, new String[]{"MAY 1-6, 1863.  THE SOUTH HAD A COSTLY VICTORY AND LOST", "ONE OF THEIR OUTSTANDING GENERALS, 'STONEWALL' JACKSON."}),
                    new HistoricalDatum("VICKSBURG", new ArmyPair<>(50000, 70000), new ArmyPair<>(12000, 19000), OffensiveStatus.DEFENSIVE, new String[]{"JULY 4, 1863.  VICKSBURG WAS A COSTLY DEFEAT FOR THE SOUTH", "BECAUSE IT GAVE THE UNION ACCESS TO THE MISSISSIPPI."}),
                    new HistoricalDatum("GETTYSBURG", new ArmyPair<>(72500, 85000), new ArmyPair<>(20000, 23000), OffensiveStatus.OFFENSIVE, new String[]{"JULY 1-3, 1863.  A SOUTHERN MISTAKE BY GEN. LEE AT GETTYSBURG", "COST THEM ONE OF THE MOST CRUCIAL BATTLES OF THE WAR."}),
                    new HistoricalDatum("CHICKAMAUGA", new ArmyPair<>(66000, 60000), new ArmyPair<>(18000, 16000), OffensiveStatus.BOTH_OFFENSIVE, new String[]{"SEPT. 15, 1863. CONFUSION IN A FOREST NEAR CHICKAMAUGA LED", "TO A COSTLY SOUTHERN VICTORY."}),
                    new HistoricalDatum("CHATTANOOGA", new ArmyPair<>(37000, 60000), new ArmyPair<>(36700, 5800), OffensiveStatus.BOTH_OFFENSIVE, new String[]{"NOV. 25, 1863. AFTER THE SOUTH HAD SIEGED GEN. ROSENCRANS'", "ARMY FOR THREE MONTHS, GEN. GRANT BROKE THE SIEGE."}),
                    new HistoricalDatum("SPOTSYLVANIA", new ArmyPair<>(62000, 110000), new ArmyPair<>(17723, 18000), OffensiveStatus.BOTH_OFFENSIVE, new String[]{"MAY 5, 1864.  GRANT'S PLAN TO KEEP LEE ISOLATED BEGAN TO", "FAIL HERE, AND CONTINUED AT COLD HARBOR AND PETERSBURG."}),
                    new HistoricalDatum("ATLANTA", new ArmyPair<>(65000, 100000), new ArmyPair<>(8500, 3700), OffensiveStatus.DEFENSIVE, new String[]{"AUGUST, 1864.  SHERMAN AND THREE VETERAN ARMIES CONVERGED", "ON ATLANTA AND DEALT THE DEATH BLOW TO THE CONFEDERACY."})
            );
        }
    
        private void showCredits() {
            out.println(" ".repeat(26) + "CIVIL WAR");
            out.println(" ".repeat(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
            out.println();
            out.println();
            out.println();
        }
    
        private void updateStrategies(int strategy) {
            // REM LEARN  PRESENT STRATEGY, START FORGETTING OLD ONES
            // REM - PRESENT STRATEGY OF SOUTH GAINS 3*S, OTHERS LOSE S
            // REM   PROBABILITY POINTS, UNLESS A STRATEGY FALLS BELOW 5%.
    
            var S = 3;
            var S0 = 0;
            for (int i = 0; i < 4; i++) {
                if (this.strategies[i] <= 5) {
                    continue;
                }
    
                this.strategies[i] -= S;
                S0 += S;
    
            }
            this.strategies[strategy - 1] += S0;
    
        }
    
        private void showHelp() {
            out.println();
            out.println();
            out.println();
            out.println();
            out.println("THIS IS A CIVIL WAR SIMULATION.");
            out.println("TO PLAY TYPE A RESPONSE WHEN THE COMPUTER ASKS.");
            out.println("REMEMBER THAT ALL FACTORS ARE INTERRELATED AND THAT YOUR");
            out.println("RESPONSES COULD CHANGE HISTORY. FACTS AND FIGURES USED ARE");
            out.println("BASED ON THE ACTUAL OCCURRENCE. MOST BATTLES TEND TO RESULT");
            out.println("AS THEY DID IN THE CIVIL WAR, BUT IT ALL DEPENDS ON YOU!!");
            out.println();
            out.println("THE OBJECT OF THE GAME IS TO WIN AS MANY BATTLES AS ");
            out.println("POSSIBLE.");
            out.println();
            out.println("YOUR CHOICES FOR DEFENSIVE STRATEGY ARE:");
            out.println("        (1) ARTILLERY ATTACK");
            out.println("        (2) FORTIFICATION AGAINST FRONTAL ATTACK");
            out.println("        (3) FORTIFICATION AGAINST FLANKING MANEUVERS");
            out.println("        (4) FALLING BACK");
            out.println(" YOUR CHOICES FOR OFFENSIVE STRATEGY ARE:");
            out.println("        (1) ARTILLERY ATTACK");
            out.println("        (2) FRONTAL ATTACK");
            out.println("        (3) FLANKING MANEUVERS");
            out.println("        (4) ENCIRCLEMENT");
            out.println("YOU MAY SURRENDER BY TYPING A '5' FOR YOUR STRATEGY.");
        }
    
        private static final int MAX_NUM_LENGTH = 6;
    
        private String rightAlignInt(int number) {
            var s = String.valueOf(number);
            return " ".repeat(MAX_NUM_LENGTH - s.length()) + s;
        }
    
        private String rightAlignInt(double number) {
            return rightAlignInt((int) Math.floor(number));
        }
    
        private static String inputString(Predicate validator, String reminder) {
            while (true) {
                try {
                    var input = new Scanner(System.in).nextLine();
                    if (validator.test(input)) {
                        return input;
                    }
                } catch (InputMismatchException e) {
                    // Ignore
                }
                System.out.println(reminder);
            }
        }
    
        private static int inputInt(Predicate validator, Function reminder) {
            while (true) {
                try {
                    var input = new Scanner(System.in).nextInt();
                    if (validator.test(input)) {
                        return input;
                    }
                    System.out.println(reminder.apply(input));
                } catch (InputMismatchException e) {
                    System.out.println(reminder.apply(0));
                }
            }
        }
    
        private static boolean isYes(String s) {
            if (s == null) {
                return false;
            }
            var uppercase = s.toUpperCase();
            return uppercase.equals("Y") || uppercase.equals("YES");
        }
    
        private static boolean isNo(String s) {
            if (s == null) {
                return false;
            }
            var uppercase = s.toUpperCase();
            return uppercase.equals("N") || uppercase.equals("NO");
        }
    
        private static class BattleState {
            private final HistoricalDatum data;
            private double F1;
    
            public BattleState(HistoricalDatum data) {
                this.data = data;
            }
        }
    
        private static class ArmyPair {
            private T confederate;
            private T union;
    
            public ArmyPair(T confederate, T union) {
                this.confederate = confederate;
                this.union = union;
            }
        }
    
        private static class BattleResults {
            private int confederate;
            private int union;
            private int indeterminate;
    
            public int getTotal() {
                return confederate + union + indeterminate;
            }
        }
    
        private static class ArmyResources {
            private int food;
            private int salaries;
            private int ammunition;
            private int budget;
    
            private double morale;  // TODO really here?
    
            public int getTotal() {
                return this.food + this.salaries + this.ammunition;
            }
        }
    
        private record HistoricalDatum(String name, ArmyPair troops,
                                       ArmyPair expectedCasualties,
                                       OffensiveStatus offensiveStatus, String[] blurb) {
        }
    
        private record UnionLosses(int losses, int desertions) {
        }
    }
    
    
    ================================================
    FILE: 27_Civil_War/javascript/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells)
    
    
    ================================================
    FILE: 27_Civil_War/javascript/civilwar.html
    ================================================
    
    
    CIVIL WAR
    
    
    
    
    
    
    
    
    
    
    ================================================
    FILE: 27_Civil_War/javascript/civilwar.js
    ================================================
    // CIVIL WAR
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    // Historical data...can add more (strat., etc) by inserting
    // data statements after appro. info, and adjusting read
    //                      0 - C$     1-M1  2-M2  3-C1 4-C2 5-D
    var historical_data = [,
                           ["BULL RUN",18000,18500,1967,2708,1],
                           ["SHILOH",40000.,44894.,10699,13047,3],
                           ["SEVEN DAYS",95000.,115000.,20614,15849,3],
                           ["SECOND BULL RUN",54000.,63000.,10000,14000,2],
                           ["ANTIETAM",40000.,50000.,10000,12000,3],
                           ["FREDERICKSBURG",75000.,120000.,5377,12653,1],
                           ["MURFREESBORO",38000.,45000.,11000,12000,1],
                           ["CHANCELLORSVILLE",32000,90000.,13000,17197,2],
                           ["VICKSBURG",50000.,70000.,12000,19000,1],
                           ["GETTYSBURG",72500.,85000.,20000,23000,3],
                           ["CHICKAMAUGA",66000.,60000.,18000,16000,2],
                           ["CHATTANOOGA",37000.,60000.,36700.,5800,2],
                           ["SPOTSYLVANIA",62000.,110000.,17723,18000,2],
                           ["ATLANTA",65000.,100000.,8500,3700,1]];
    var sa = [];
    var da = [];
    var fa = [];
    var ha = [];
    var ba = [];
    var oa = [];
    
    // Main program
    async function main()
    {
        print(tab(26) + "CIVIL WAR\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        // Original game design: Cram, Goodie, Hibbard Lexington H.S.
        // Modifications: G. Paul, R. Hess (Ties), 1973
        // Union info on likely confederate strategy
        sa[1] = 25;
        sa[2] = 25;
        sa[3] = 25;
        sa[4] = 25;
        d = Math.random();
        print("\n");
        print("DO YOU WANT INSTRUCTIONS");
        while (1) {
            str = await input();
            if (str == "YES" || str == "NO")
                break;
            print("YES OR NO -- \n");
        }
        if (str == "YES") {
            print("\n");
            print("\n");
            print("\n");
            print("\n");
            print("THIS IS A CIVIL WAR SIMULATION.\n");
            print("TO PLAY TYPE A RESPONSE WHEN THE COMPUTER ASKS.\n");
            print("REMEMBER THAT ALL FACTORS ARE INTERRELATED AND THAT YOUR\n");
            print("RESPONSES COULD CHANGE HISTORY. FACTS AND FIGURES USED ARE\n");
            print("BASED ON THE ACTUAL OCCURRENCE. MOST BATTLES TEND TO RESULT\n");
            print("AS THEY DID IN THE CIVIL WAR, BUT IT ALL DEPENDS ON YOU!!\n");
            print("\n");
            print("THE OBJECT OF THE GAME IS TO WIN AS MANY BATTLES AS ");
            print("POSSIBLE.\n");
            print("\n");
            print("YOUR CHOICES FOR DEFENSIVE STRATEGY ARE:\n");
            print("        (1) ARTILLERY ATTACK\n");
            print("        (2) FORTIFICATION AGAINST FRONTAL ATTACK\n");
            print("        (3) FORTIFICATION AGAINST FLANKING MANEUVERS\n");
            print("        (4) FALLING BACK\n");
            print(" YOUR CHOICES FOR OFFENSIVE STRATEGY ARE:\n");
            print("        (1) ARTILLERY ATTACK\n");
            print("        (2) FRONTAL ATTACK\n");
            print("        (3) FLANKING MANEUVERS\n");
            print("        (4) ENCIRCLEMENT\n");
            print("YOU MAY SURRENDER BY TYPING A '5' FOR YOUR STRATEGY.\n");
        }
        print("\n");
        print("\n");
        print("\n");
        print("ARE THERE TWO GENERALS PRESENT ");
        while (1) {
            print("(ANSWER YES OR NO)");
            bs = await input();
            if (bs == "YES") {
                d = 2;
                break;
            } else if (bs == "NO") {
                print("\n");
                print("YOU ARE THE CONFEDERACY.   GOOD LUCK!\n");
                print("\n");
                d = 1;
                break;
            }
        }
        print("SELECT A BATTLE BY TYPING A NUMBER FROM 1 TO 14 ON\n");
        print("REQUEST.  TYPE ANY OTHER NUMBER TO END THE SIMULATION.\n");
        print("BUT '0' BRINGS BACK EXACT PREVIOUS BATTLE SITUATION\n");
        print("ALLOWING YOU TO REPLAY IT\n");
        print("\n");
        print("NOTE: A NEGATIVE FOOD$ ENTRY CAUSES THE PROGRAM TO \n");
        print("USE THE ENTRIES FROM THE PREVIOUS BATTLE\n");
        print("\n");
        print("AFTER REQUESTING A BATTLE, DO YOU WISH ");
        print("BATTLE DESCRIPTIONS ");
        while (1) {
            print("(ANSWER YES OR NO)");
            xs = await input();
            if (xs == "YES" || xs == "NO")
                break;
        }
        l = 0;
        w = 0;
        r1 = 0;
        q1 = 0;
        m3 = 0;
        m4 = 0;
        p1 = 0;
        p2 = 0;
        t1 = 0;
        t2 = 0;
        for (i = 1; i <= 2; i++) {
            da[i] = 0;
            fa[i] = 0;
            ha[i] = 0;
            ba[i] = 0;
            oa[i] = 0;
        }
        r2 = 0;
        q2 = 0;
        c6 = 0;
        f = 0;
        w0 = 0;
        y = 0;
        y2 = 0;
        u = 0;
        u2 = 0;
        while (1) {
            print("\n");
            print("\n");
            print("\n");
            print("WHICH BATTLE DO YOU WISH TO SIMULATE");
            a = parseInt(await input());
            if (a < 1 || a > 14)
                break;
            if (a != 0 || r == 0) {
                cs = historical_data[a][0];
                m1 = historical_data[a][1];
                m2 = historical_data[a][2];
                c1 = historical_data[a][3];
                c2 = historical_data[a][4];
                m = historical_data[a][5];
                u = 0;
                // Inflation calc
                i1 = 10 + (l - w) * 2;
                i2 = 10 + (w - l) * 2;
                // Money available
                da[1] = 100 * Math.floor((m1 * (100 - i1) / 2000) * (1 + (r1 - q1) / (r1 + 1)) + 0.5);
                da[2] = 100 * Math.floor(m2 * (100 - i2) / 2000 + 0.5);
                if (bs == "YES") {
                    da[2] = 100 * Math.floor((m2 * (100 - i2) / 2000) * (1 + (r2 - q2) / (r2 + 1)) + 0.5);
                }
                // Men available
                m5 = Math.floor(m1 * (1 + (p1 - t1) / (m3 + 1)));
                m6 = Math.floor(m2 * (1 + (p2 - t2) / (m4 + 1)));
                f1 = 5 * m1 / 6;
                print("\n");
                print("\n");
                print("\n");
                print("\n");
                print("\n");
                print("THIS IS THE BATTLE OF " + cs + "\n");
                if (xs != "NO") {
                    switch (a) {
                        case 1:
                            print("JULY 21, 1861.  GEN. BEAUREGARD, COMMANDING THE SOUTH, MET\n");
                            print("UNION FORCES WITH GEN. MCDOWELL IN A PREMATURE BATTLE AT\n");
                            print("BULL RUN. GEN. JACKSON HELPED PUSH BACK THE UNION ATTACK.\n");
                            break;
                        case 2:
                            print("APRIL 6-7, 1862.  THE CONFEDERATE SURPRISE ATTACK AT\n");
                            print("SHILOH FAILED DUE TO POOR ORGANIZATION.\n");
                            break;
                        case 3:
                            print("JUNE 25-JULY 1, 1862.  GENERAL LEE (CSA) UPHELD THE\n");
                            print("OFFENSIVE THROUGHOUT THE BATTLE AND FORCED GEN. MCCLELLAN\n");
                            print("AND THE UNION FORCES AWAY FROM RICHMOND.\n");
                            break;
                        case 4:
                            print("AUG 29-30, 1862.  THE COMBINED CONFEDERATE FORCES UNDER LEE\n");
                            print("AND JACKSON DROVE THE UNION FORCES BACK INTO WASHINGTON.\n");
                            break;
                        case 5:
                            print("SEPT 17, 1862.  THE SOUTH FAILED TO INCORPORATE MARYLAND\n");
                            print("INTO THE CONFEDERACY.\n");
                            break;
                        case 6:
                            print("DEC 13, 1862.  THE CONFEDERACY UNDER LEE SUCCESSFULLY\n");
                            print("REPULSED AN ATTACK BY THE UNION UNDER GEN. BURNSIDE.\n");
                            break;
                        case 7:
                            print("DEC 31, 1862.  THE SOUTH UNDER GEN. BRAGG WON A CLOSE BATTLE.\n");
                            break;
                        case 8:
                            print("MAY 1-6, 1863.  THE SOUTH HAD A COSTLY VICTORY AND LOST\n");
                            print("ONE OF THEIR OUTSTANDING GENERALS, 'STONEWALL' JACKSON.\n");
                            break;
                        case 9:
                            print("JULY 4, 1863.  VICKSBURG WAS A COSTLY DEFEAT FOR THE SOUTH\n");
                            print("BECAUSE IT GAVE THE UNION ACCESS TO THE MISSISSIPPI.\n");
                            break;
                        case 10:
                            print("JULY 1-3, 1863.  A SOUTHERN MISTAKE BY GEN. LEE AT GETTYSBURG\n");
                            print("COST THEM ONE OF THE MOST CRUCIAL BATTLES OF THE WAR.\n");
                            break;
                        case 11:
                            print("SEPT. 15, 1863. CONFUSION IN A FOREST NEAR CHICKAMAUGA LED\n");
                            print("TO A COSTLY SOUTHERN VICTORY.\n");
                            break;
                        case 12:
                            print("NOV. 25, 1863. AFTER THE SOUTH HAD SIEGED GEN. ROSENCRANS'\n");
                            print("ARMY FOR THREE MONTHS, GEN. GRANT BROKE THE SIEGE.\n");
                            break;
                        case 13:
                            print("MAY 5, 1864.  GRANT'S PLAN TO KEEP LEE ISOLATED BEGAN TO\n");
                            print("FAIL HERE, AND CONTINUED AT COLD HARBOR AND PETERSBURG.\n");
                            break;
                        case 14:
                            print("AUGUST, 1864.  SHERMAN AND THREE VETERAN ARMIES CONVERGED\n");
                            print("ON ATLANTA AND DEALT THE DEATH BLOW TO THE CONFEDERACY.\n");
                            break;
                    }
                }
            } else {
                print(cs + " INSTANT REPLAY\n");
            }
            print("\n");
            print(" \tCONFEDERACY\t UNION\n"),
            print("MEN\t  " + m5 + "\t\t " + m6 + "\n");
            print("MONEY\t $" + da[1] + "\t\t$" + da[2] + "\n");
            print("INFLATION\t " + (i1 + 15) + "%\t " + i2 + "%\n");
            print("\n");
            // ONLY IN PRINTOUT IS CONFED INFLATION = I1 + 15%
            // IF TWO GENERALS, INPUT CONFED, FIRST
            for (i = 1; i <= d; i++) {
                if (bs == "YES" && i == 1)
                    print("CONFEDERATE GENERAL---");
                print("HOW MUCH DO YOU WISH TO SPEND FOR\n");
                while (1) {
                    print(" - FOOD......");
                    f = parseInt(await input());
                    if (f < 0) {
                        if (r1 == 0) {
                            print("NO PREVIOUS ENTRIES\n");
                            continue;
                        }
                        print("ASSUME YOU WANT TO KEEP SAME ALLOCATIONS\n");
                        print("\n");
                        break;
                    }
                    fa[i] = f;
                    while (1) {
                        print(" - SALARIES..");
                        ha[i] = parseInt(await input());
                        if (ha[i] >= 0)
                            break;
                        print("NEGATIVE VALUES NOT ALLOWED.\n");
                    }
                    while (1) {
                        print(" - AMMUNITION");
                        ba[i] = parseInt(await input());
                        if (ba[i] >= 0)
                            break;
                        print("NEGATIVE VALUES NOT ALLOWED.\n");
                    }
                    print("\n");
                    if (fa[i] + ha[i] + ba[i] > da[i]) {
                        print("THINK AGAIN! YOU HAVE ONLY $" + da[i] + "\n");
                    } else {
                        break;
                    }
                }
                if (bs != "YES" || i == 2)
                    break;
                print("UNION GENERAL---");
            }
            for (z = 1; z <= d; z++) {
                if (bs == "YES") {
                    if (z == 1)
                        print("CONFEDERATE ");
                    else
                        print("      UNION ");
                }
                // Find morale
                o = ((2 * Math.pow(fa[z], 2) + Math.pow(ha[z], 2)) / Math.pow(f1, 2) + 1);
                if (o >= 10) {
                    print("MORALE IS HIGH\n");
                } else if (o >= 5) {
                    print("MORALE IS FAIR\n");
                } else {
                    print("MORALE IS POOR\n");
                }
                if (bs != "YES")
                    break;
                oa[z] = o;
            }
            o2 = oa[2];
            o = oa[1];
            print("CONFEDERATE GENERAL---");
            // Actual off/def battle situation
            if (m == 3) {
                print("YOU ARE ON THE OFFENSIVE\n");
            } else if (m == 1) {
                print("YOU ARE ON THE DEFENSIVE\n");
            } else {
                print("BOTH SIDES ARE ON THE OFFENSIVE \n");
            }
            print("\n");
            // Choose strategies
            if (bs != "YES") {
                print("YOUR STRATEGY ");
                while (1) {
                    y = parseInt(await input());
                    if (Math.abs(y - 3) < 3)
                        break;
                    print("STRATEGY " + y + " NOT ALLOWED.\n");
                }
                if (y == 5) {
                    print("THE CONFEDERACY HAS SURRENDERED.\n");
                    break;
                }
                // Union strategy is computer choesn
                print("UNION STRATEGY IS ");
                if (a == 0) {
                    while (1) {
                        y2 = parseInt(await input());
                        if (y2 > 0 && y2 < 5)
                            break;
                        print("ENTER 1, 2, 3, OR 4 (USUALLY PREVIOUS UNION STRATEGY)\n");
                    }
                } else {
                    s0 = 0;
                    r = Math.random() * 100;
                    for (i = 1; i <= 4; i++) {
                        s0 += sa[i];
                        // If actual strategy info is in program data statements
                        // then r-100 is extra weight given to that strategy.
                        if (r < s0)
                            break;
                    }
                    y2 = i;
                    print(y2 + "\n");
                }
            } else {
                for (i = 1; i <= 2; i++) {
                    if (i == 1)
                        print("CONFEDERATE STRATEGY ");
                    while (1) {
                        y = parseInt(await input());
                        if (Math.abs(y - 3) < 3)
                            break;
                        print("STRATEGY " + y + " NOT ALLOWED.\n");
                    }
                    if (i == 2) {
                        y2 = y;
                        y = y1;
                        if (y2 != 5)
                            break;
                    } else {
                        y1 = y;
                    }
                    print("UNION STRATEGY ");
                }
                // Simulated losses - North
                c6 = (2 * c2 / 5) * (1 + 1 / (2 * (Math.abs(y2 - y) + 1)));
                c6 = c6 * (1.28 + (5 * m2 / 6) / (ba[2] + 1));
                c6 = Math.floor(c6 * (1 + 1 / o2) + 0.5);
                // If loss > men present, rescale losses
                e2 = 100 / o2;
                if (Math.floor(c6 + e2) >= m6) {
                    c6 = Math.floor(13 * m6 / 20);
                    e2 = 7 * c6 / 13;
                    u2 = 1;
                }
            }
            // Calculate simulated losses
            print("\n");
            print("\n");
            print("\n");
            print("\t\tCONFEDERACY\tUNION\n");
            c5 = (2 * c1 / 5) * (1 + 1 / (2 * (Math.abs(y2 - y) + 1)));
            c5 = Math.floor(c5 * (1 + 1 / o) * (1.28 + f1 / (ba[1] + 1)) + 0.5);
            e = 100 / o;
            if (c5 + 100 / o >= m1 * (1 + (p1 - t1) / (m3 + 1))) {
                c5 = Math.floor(13 * m1 / 20 * (1 + (p1 - t1) / (m3 + 1)));
                e = 7 * c5 / 13;
                u = 1;
            }
            if (d == 1) {
                c6 = Math.floor(17 * c2 * c1 / (c5 * 20));
                e2 = 5 * o;
            }
            print("CASUALTIES\t" + c5 + "\t\t" + c6 + "\n");
            print("DESERTIONS\t" + Math.floor(e) + "\t\t" + Math.floor(e2) + "\n");
            print("\n");
            if (bs == "YES") {
                print("COMPARED TO THE ACTUAL CASUALTIES AT " + cs + "\n");
                print("CONFEDERATE: " + Math.floor(100 * (c5 / c1) + 0.5) + "% OF THE ORIGINAL\n");
                print("UNION:       " + Math.floor(100 * (c6 / c2) + 0.5) + "% OF THE ORIGINAL\n");
            }
            print("\n");
            // 1 Who one
            if (u == 1 && u2 == 1 || (u != 1 && u2 != 1 && c5 + e == c6 + e2)) {
                print("BATTLE OUTCOME UNRESOLVED\n");
                w0++;
            } else if (u == 1 || (u != 1 && u2 != 1 && c5 + e > c6 + e2)) {
                print("THE UNION WINS " + cs + "\n");
                if (a != 0)
                    l++;
            } else  {
                print("THE CONFEDERACY WINS " + cs + "\n");
                if (a != 0)
                    w++;
            }
            // Lines 2530 to 2590 from original are unreachable.
            if (a != 0) {
                t1 += c5 + e;
                t2 += c6 + e2;
                p1 += c1;
                p2 += c2;
                q1 += fa[1] + ha[1] + ba[1];
                q2 += fa[2] + ha[2] + ba[2];
                r1 += m1 * (100 - i1) / 20;
                r2 += m2 * (100 - i2) / 20;
                m3 += m1;
                m4 += m2;
                // Learn present strategy, start forgetting old ones
                // present startegy of south gains 3*s, others lose s
                // probability points, unless a strategy falls below 5%.
                s = 3;
                s0 = 0;
                for (i = 1; i <= 4; i++) {
                    if (sa[i] <= 5)
                        continue;
                    sa[i] -= 5;
                    s0 += s;
                }
                sa[y] += s0;
            }
            u = 0;
            u2 = 0;
            print("---------------");
            continue;
        }
        print("\n");
        print("\n");
        print("\n");
        print("\n");
        print("\n");
        print("\n");
        print("THE CONFEDERACY HAS WON " + w + " BATTLES AND LOST " + l + "\n");
        if (y == 5 || (y2 != 5 && w <= l)) {
            print("THE UNION HAS WON THE WAR\n");
        } else {
            print("THE CONFEDERACY HAS WON THE WAR\n");
        }
        print("\n");
        if (r1) {
            print("FOR THE " + (w + l + w0) + " BATTLES FOUGHT (EXCLUDING RERUNS)\n");
            print(" \t \t ");
            print("CONFEDERACY\t UNION\n");
            print("HISTORICAL LOSSES\t" + Math.floor(p1 + 0.5) + "\t" + Math.floor(p2 + 0.5) + "\n");
            print("SIMULATED LOSSES\t" + Math.floor(t1 + 0.5) + "\t" + Math.floor(t2 + 0.5) + "\n");
            print("\n");
            print("    % OF ORIGINAL\t" + Math.floor(100 * (t1 / p1) + 0.5) + "\t" + Math.floor(100 * (t2 / p2) + 0.5) + "\n");
            if (bs != "YES") {
                print("\n");
                print("UNION INTELLIGENCE SUGGEST THAT THE SOUTH USED \n");
                print("STRATEGIES 1, 2, 3, 4 IN THE FOLLOWING PERCENTAGES\n");
                print(sa[1] + " " + sa[2] + " " + sa[3] + " " + sa[4] + "\n");
            }
        }
    }
    
    main();
    
    
    ================================================
    FILE: 27_Civil_War/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 27_Civil_War/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 27_Civil_War/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 27_Civil_War/python/Civilwar.py
    ================================================
    """
    Original game design: Cram, Goodie, Hibbard Lexington H.S.
    Modifications: G. Paul, R. Hess (Ties), 1973
    """
    import enum
    import math
    import random
    from dataclasses import dataclass
    from typing import Dict, List, Literal, Tuple
    
    
    class AttackState(enum.Enum):
        DEFENSIVE = 1
        BOTH_OFFENSIVE = 2
        OFFENSIVE = 3
    
    
    CONF = 1
    UNION = 2
    
    
    @dataclass
    class PlayerStat:
        food: float = 0
        salaries: float = 0
        ammunition: float = 0
    
        desertions: float = 0
        casualties: float = 0
        morale: float = 0
        strategy: int = 0
        available_men: int = 0
        available_money: int = 0
    
        army_c: float = 0
        army_m: float = 0  # available_men ????
        inflation: float = 0
    
        r: float = 0
        t: float = 0  # casualties + desertions
        q: float = 0  # accumulated cost?
        p: float = 0
        m: float = 0
    
        is_player = False
        excessive_losses = False
    
        def set_available_money(self):
            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
            )
    
        def get_cost(self) -> float:
            return self.food + self.salaries + self.ammunition
    
        def get_army_factor(self) -> float:
            return 1 + (self.p - self.t) / (self.m + 1)
    
        def get_present_men(self) -> float:
            return self.army_m * self.get_army_factor()
    
    
    def simulate_losses(player1: PlayerStat, player2: PlayerStat) -> float:
        """Simulate losses of player 1"""
        tmp = (2 * player1.army_c / 5) * (
            1 + 1 / (2 * (abs(player1.strategy - player2.strategy) + 1))
        )
        tmp = tmp * (1.28 + (5 * player1.army_m / 6) / (player1.ammunition + 1))
        return math.floor(tmp * (1 + 1 / player1.morale) + 0.5)
    
    
    def update_army(player: PlayerStat, enemy: PlayerStat, use_factor=False) -> None:
        player.casualties = simulate_losses(player, enemy)
        player.desertions = 100 / player.morale
    
        loss = player.casualties + player.desertions
        if not use_factor:
            present_men: float = player.available_men
        else:
            present_men = player.get_present_men()
        if loss >= present_men:
            factor = player.get_army_factor()
            if not use_factor:
                factor = 1
            player.casualties = math.floor(13 * player.army_m / 20 * factor)
            player.desertions = 7 * player.casualties / 13
            player.excessive_losses = True
    
    
    def get_choice(prompt: str, choices: List[str]) -> str:
        while True:
            choice = input(prompt)
            if choice in choices:
                break
        return choice
    
    
    def get_morale(stat: PlayerStat, enemy: PlayerStat) -> float:
        """Higher is better"""
        enemy_strength = 5 * enemy.army_m / 6
        return (2 * math.pow(stat.food, 2) + math.pow(stat.salaries, 2)) / math.pow(
            enemy_strength, 2
        ) + 1
    
    
    def main() -> None:
        battles = [
            [
                "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.",
            ],
            [
                "APRIL 6-7, 1862.  THE CONFEDERATE SURPRISE ATTACK AT",
                "SHILOH FAILED DUE TO POOR ORGANIZATION.",
            ],
            [
                "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.",
            ],
            [
                "AUG 29-30, 1862.  THE COMBINED CONFEDERATE FORCES UNDER LEE",
                "AND JACKSON DROVE THE UNION FORCES BACK INTO WASHINGTON.",
            ],
            [
                "SEPT 17, 1862.  THE SOUTH FAILED TO INCORPORATE MARYLAND",
                "INTO THE CONFEDERACY.",
            ],
            [
                "DEC 13, 1862.  THE CONFEDERACY UNDER LEE SUCCESSFULLY",
                "REPULSED AN ATTACK BY THE UNION UNDER GEN. BURNSIDE.",
            ],
            ["DEC 31, 1862.  THE SOUTH UNDER GEN. BRAGG WON A CLOSE BATTLE."],
            [
                "MAY 1-6, 1863.  THE SOUTH HAD A COSTLY VICTORY AND LOST",
                "ONE OF THEIR OUTSTANDING GENERALS, 'STONEWALL' JACKSON.",
            ],
            [
                "JULY 4, 1863.  VICKSBURG WAS A COSTLY DEFEAT FOR THE SOUTH",
                "BECAUSE IT GAVE THE UNION ACCESS TO THE MISSISSIPPI.",
            ],
            [
                "JULY 1-3, 1863.  A SOUTHERN MISTAKE BY GEN. LEE AT GETTYSBURG",
                "COST THEM ONE OF THE MOST CRUCIAL BATTLES OF THE WAR.",
            ],
            [
                "SEPT. 15, 1863. CONFUSION IN A FOREST NEAR CHICKAMAUGA LED",
                "TO A COSTLY SOUTHERN VICTORY.",
            ],
            [
                "NOV. 25, 1863. AFTER THE SOUTH HAD SIEGED GEN. ROSENCRANS'",
                "ARMY FOR THREE MONTHS, GEN. GRANT BROKE THE SIEGE.",
            ],
            [
                "MAY 5, 1864.  GRANT'S PLAN TO KEEP LEE ISOLATED BEGAN TO",
                "FAIL HERE, AND CONTINUED AT COLD HARBOR AND PETERSBURG.",
            ],
            [
                "AUGUST, 1864.  SHERMAN AND THREE VETERAN ARMIES CONVERGED",
                "ON ATLANTA AND DEALT THE DEATH BLOW TO THE CONFEDERACY.",
            ],
        ]
    
        historical_data: List[Tuple[str, float, float, float, int, AttackState]] = [
            ("", 0, 0, 0, 0, AttackState.DEFENSIVE),
            ("BULL RUN", 18000, 18500, 1967, 2708, AttackState.DEFENSIVE),
            ("SHILOH", 40000.0, 44894.0, 10699, 13047, AttackState.OFFENSIVE),
            ("SEVEN DAYS", 95000.0, 115000.0, 20614, 15849, AttackState.OFFENSIVE),
            ("SECOND BULL RUN", 54000.0, 63000.0, 10000, 14000, AttackState.BOTH_OFFENSIVE),
            ("ANTIETAM", 40000.0, 50000.0, 10000, 12000, AttackState.OFFENSIVE),
            ("FREDERICKSBURG", 75000.0, 120000.0, 5377, 12653, AttackState.DEFENSIVE),
            ("MURFREESBORO", 38000.0, 45000.0, 11000, 12000, AttackState.DEFENSIVE),
            ("CHANCELLORSVILLE", 32000, 90000.0, 13000, 17197, AttackState.BOTH_OFFENSIVE),
            ("VICKSBURG", 50000.0, 70000.0, 12000, 19000, AttackState.DEFENSIVE),
            ("GETTYSBURG", 72500.0, 85000.0, 20000, 23000, AttackState.OFFENSIVE),
            ("CHICKAMAUGA", 66000.0, 60000.0, 18000, 16000, AttackState.BOTH_OFFENSIVE),
            ("CHATTANOOGA", 37000.0, 60000.0, 36700.0, 5800, AttackState.BOTH_OFFENSIVE),
            ("SPOTSYLVANIA", 62000.0, 110000.0, 17723, 18000, AttackState.BOTH_OFFENSIVE),
            ("ATLANTA", 65000.0, 100000.0, 8500, 3700, AttackState.DEFENSIVE),
        ]
        confederate_strategy_prob_distribution = {}
    
        # What do you spend money on?
        stats: Dict[int, PlayerStat] = {
            CONF: PlayerStat(),
            UNION: PlayerStat(),
        }
    
        print(" " * 26 + "CIVIL WAR")
        print(" " * 15 + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n\n\n")
    
        # Union info on likely confederate strategy
        confederate_strategy_prob_distribution[1] = 25
        confederate_strategy_prob_distribution[2] = 25
        confederate_strategy_prob_distribution[3] = 25
        confederate_strategy_prob_distribution[4] = 25
        print()
        show_instructions = get_choice(
            "DO YOU WANT INSTRUCTIONS? YES OR NO -- ", ["YES", "NO"]
        )
    
        if show_instructions == "YES":
            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.")
    
        print()
        print()
        print()
        print("ARE THERE TWO GENERALS PRESENT ", end="")
        two_generals = get_choice("(ANSWER YES OR NO) ", ["YES", "NO"]) == "YES"
        stats[CONF].is_player = True
        if two_generals:
            party: Literal[1, 2] = 2  # number of players in the game
            stats[UNION].is_player = True
        else:
            party = 1
            print()
            print("YOU ARE THE CONFEDERACY.   GOOD LUCK!")
            print()
    
        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()
        print("AFTER REQUESTING A BATTLE, DO YOU WISH ", end="")
        print("BATTLE DESCRIPTIONS ", end="")
        xs = get_choice("(ANSWER YES OR NO) ", ["YES", "NO"])
        confederacy_lost = 0
        confederacy_win = 0
        for i in [CONF, UNION]:
            stats[i].p = 0
            stats[i].m = 0
            stats[i].t = 0
            stats[i].available_money = 0
            stats[i].food = 0
            stats[i].salaries = 0
            stats[i].ammunition = 0
            stats[i].strategy = 0
            stats[i].excessive_losses = False
        confederacy_unresolved = 0
        random_nb: float = 0
        while True:
            print()
            print()
            print()
            simulated_battle_index = int(
                get_choice(
                    "WHICH BATTLE DO YOU WISH TO SIMULATE? (0-14) ",
                    [str(i) for i in range(15)],
                )
            )
            if simulated_battle_index < 1 or simulated_battle_index > 14:
                break
            if simulated_battle_index != 0 or random_nb == 0:
                loaded_battle = historical_data[simulated_battle_index]
                battle_name = loaded_battle[0]
                stats[CONF].army_m = loaded_battle[1]
                stats[UNION].army_m = loaded_battle[2]
                stats[CONF].army_c = loaded_battle[3]
                stats[UNION].army_c = loaded_battle[4]
                stats[CONF].excessive_losses = False
    
                # Inflation calc
                stats[CONF].inflation = 10 + (confederacy_lost - confederacy_win) * 2
                stats[UNION].inflation = 10 + (confederacy_win - confederacy_lost) * 2
    
                # Money and Men available
                for i in [CONF, UNION]:
                    stats[i].set_available_money()
                    stats[i].available_men = math.floor(stats[i].get_army_factor())
                print()
                print()
                print()
                print()
                print()
                print(f"THIS IS THE BATTLE OF {battle_name}")
                if xs != "NO":
                    print("\n".join(battles[simulated_battle_index - 1]))
    
            else:
                print(f"{battle_name} INSTANT REPLAY")
    
            print()
            print("          CONFEDERACY\t UNION")
            print(f"MEN       {stats[CONF].available_men}\t\t {stats[UNION].available_men}")
            print(
                f"MONEY    ${stats[CONF].available_money}\t${stats[UNION].available_money}"
            )
            print(f"INFLATION {stats[CONF].inflation + 15}%\t\t {stats[UNION].inflation}%")
            print()
            # ONLY IN PRINTOUT IS CONFED INFLATION = I1 + 15 %
            # IF TWO GENERALS, INPUT CONFED, FIRST
            for player_index in range(1, party + 1):
                if two_generals and player_index == 1:
                    print("CONFEDERATE GENERAL---", end="")
                print("HOW MUCH DO YOU WISH TO SPEND FOR")
                while True:
                    food_input = int(input(" - FOOD...... ? "))
                    if food_input < 0:
                        if stats[CONF].r == 0:
                            print("NO PREVIOUS ENTRIES")
                            continue
                        print("ASSUME YOU WANT TO KEEP SAME ALLOCATIONS")
                        print()
                        break
                    stats[player_index].food = food_input
                    while True:
                        stats[player_index].salaries = int(input(" - SALARIES.. ? "))
                        if stats[player_index].salaries >= 0:
                            break
                        print("NEGATIVE VALUES NOT ALLOWED.")
                    while True:
                        stats[player_index].ammunition = int(input(" - AMMUNITION ? "))
                        if stats[player_index].ammunition >= 0:
                            break
                        print("NEGATIVE VALUES NOT ALLOWED.")
                    print()
                    if stats[player_index].get_cost() > stats[player_index].available_money:
                        print(
                            f"THINK AGAIN! YOU HAVE ONLY ${stats[player_index].available_money}"
                        )
                    else:
                        break
    
                if not two_generals or player_index == 2:
                    break
                print("UNION GENERAL---", end="")
    
            for player_index in range(1, party + 1):
                if two_generals:
                    if player_index == 1:
                        print("CONFEDERATE ", end="")
                    else:
                        print("      UNION ", end="")
                morale = get_morale(stats[player_index], stats[1 + player_index % 2])
    
                if morale >= 10:
                    print("MORALE IS HIGH")
                elif morale >= 5:
                    print("MORALE IS FAIR")
                else:
                    print("MORALE IS POOR")
                if not two_generals:
                    break
                stats[player_index].morale = morale  # type: ignore
    
            stats[UNION].morale = get_morale(stats[UNION], stats[CONF])
            stats[CONF].morale = get_morale(stats[CONF], stats[UNION])
            print("CONFEDERATE GENERAL---")
            # Actual off/def battle situation
            if loaded_battle[5] == AttackState.OFFENSIVE:
                print("YOU ARE ON THE OFFENSIVE")
            elif loaded_battle[5] == AttackState.DEFENSIVE:
                print("YOU ARE ON THE DEFENSIVE")
            else:
                print("BOTH SIDES ARE ON THE OFFENSIVE")
    
            print()
            # Choose strategies
            if not two_generals:
                while True:
                    stats[CONF].strategy = int(input("YOUR STRATEGY "))
                    if abs(stats[CONF].strategy - 3) < 3:
                        break
                    print(f"STRATEGY {stats[CONF].strategy} NOT ALLOWED.")
                if stats[CONF].strategy == 5:
                    print("THE CONFEDERACY HAS SURRENDERED.")
                    break
                # Union strategy is computer chosen
                if simulated_battle_index == 0:
                    while True:
                        stats[UNION].strategy = int(input("UNION STRATEGY IS "))
                        if stats[UNION].strategy > 0 and stats[UNION].strategy < 5:
                            break
                        print("ENTER 1, 2, 3, OR 4 (USUALLY PREVIOUS UNION STRATEGY)")
                else:
                    s0 = 0
                    random_nb = random.random() * 100
                    for player_index in range(1, 5):
                        s0 += confederate_strategy_prob_distribution[player_index]
                        # If actual strategy info is in program data statements
                        # then r-100 is extra weight given to that strategy.
                        if random_nb < s0:
                            break
                    stats[UNION].strategy = player_index
                    print(stats[UNION].strategy)
            else:
                for player_index in [1, 2]:
                    if player_index == 1:
                        print("CONFEDERATE STRATEGY ? ", end="")
                    while True:
                        stats[CONF].strategy = int(input())
                        if abs(stats[CONF].strategy - 3) < 3:
                            break
                        print(f"STRATEGY {stats[CONF].strategy} NOT ALLOWED.")
                        print("YOUR STRATEGY ? ", end="")
                    if player_index == 2:
                        stats[UNION].strategy = stats[CONF].strategy
                        stats[CONF].strategy = previous_strategy  # type: ignore # noqa: F821
                        if stats[UNION].strategy != 5:
                            break
                    else:
                        previous_strategy = stats[CONF].strategy  # noqa: F841
                    print("UNION STRATEGY ? ", end="")
    
                update_army(stats[UNION], stats[CONF], use_factor=False)
    
            # Calculate simulated losses
            print()
            print()
            print()
            print("\t\tCONFEDERACY\tUNION")
            update_army(stats[CONF], stats[UNION], use_factor=True)
    
            if party == 1:
                stats[UNION].casualties = math.floor(
                    17
                    * stats[UNION].army_c
                    * stats[CONF].army_c
                    / (stats[CONF].casualties * 20)
                )
                stats[CONF].desertions = 5 * morale
    
            print(
                "CASUALTIES\t"
                + str(stats[CONF].casualties)
                + "\t\t"
                + str(stats[UNION].casualties)
            )
            print(
                "DESERTIONS\t"
                + str(math.floor(stats[CONF].desertions))
                + "\t\t"
                + str(math.floor(stats[UNION].desertions))
            )
            print()
            if two_generals:
                print("COMPARED TO THE ACTUAL CASUALTIES AT " + str(battle_name))
                print(
                    "CONFEDERATE: "
                    + str(
                        math.floor(
                            100 * (stats[CONF].casualties / stats[CONF].army_c) + 0.5
                        )
                    )
                    + "% OF THE ORIGINAL"
                )
                print(
                    "UNION:       "
                    + str(
                        math.floor(
                            100 * (stats[UNION].casualties / stats[UNION].army_c) + 0.5
                        )
                    )
                    + "% OF THE ORIGINAL"
                )
    
            print()
            # Find who won
            if (
                stats[CONF].excessive_losses
                and stats[UNION].excessive_losses
                or (
                    not stats[CONF].excessive_losses
                    and not stats[UNION].excessive_losses
                    and stats[CONF].casualties + stats[CONF].desertions
                    == stats[UNION].casualties + stats[CONF].desertions
                )
            ):
                print("BATTLE OUTCOME UNRESOLVED")
                confederacy_unresolved += 1
            elif stats[CONF].excessive_losses or (
                not stats[CONF].excessive_losses
                and not stats[UNION].excessive_losses
                and stats[CONF].casualties + stats[CONF].desertions
                > stats[UNION].casualties + stats[CONF].desertions
            ):
                print(f"THE UNION WINS {battle_name}")
                if simulated_battle_index != 0:
                    confederacy_lost += 1
            else:
                print(f"THE CONFEDERACY WINS {battle_name}")
                if simulated_battle_index != 0:
                    confederacy_win += 1
    
            # Lines 2530 to 2590 from original are unreachable.
            if simulated_battle_index != 0:
                for i in [CONF, UNION]:
                    stats[i].t += stats[i].casualties + stats[i].desertions
                    stats[i].p += stats[i].army_c
                    stats[i].q += stats[i].get_cost()
                    stats[i].r += stats[i].army_m * (100 - stats[i].inflation) / 20
                    stats[i].m += stats[i].army_m
                # 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 player_index in range(1, 5):
                    if confederate_strategy_prob_distribution[player_index] <= 5:
                        continue
                    confederate_strategy_prob_distribution[player_index] -= 5
                    s0 += s
                confederate_strategy_prob_distribution[stats[CONF].strategy] += s0
    
            stats[CONF].excessive_losses = False
            stats[UNION].excessive_losses = False
            print("---------------")
            continue
    
        print()
        print()
        print()
        print()
        print()
        print()
        print(
            f"THE CONFEDERACY HAS WON {confederacy_win} BATTLES AND LOST {confederacy_lost}"
        )
        if stats[CONF].strategy == 5 or (
            stats[UNION].strategy != 5 and confederacy_win <= confederacy_lost
        ):
            print("THE UNION HAS WON THE WAR")
        else:
            print("THE CONFEDERACY HAS WON THE WAR")
        print()
        if stats[CONF].r > 0:
            print(
                f"FOR THE {confederacy_win + confederacy_lost + confederacy_unresolved} BATTLES FOUGHT (EXCLUDING RERUNS)"
            )
            print(" \t \t ")
            print("CONFEDERACY\t UNION")
            print(
                f"HISTORICAL LOSSES\t{math.floor(stats[CONF].p + 0.5)}\t{math.floor(stats[UNION].p + 0.5)}"
            )
            print(
                f"SIMULATED LOSSES\t{math.floor(stats[CONF].t + 0.5)}\t{math.floor(stats[UNION].t + 0.5)}"
            )
            print()
            print(
                f"    % OF ORIGINAL\t{math.floor(100 * (stats[CONF].t / stats[CONF].p) + 0.5)}\t{math.floor(100 * (stats[UNION].t / stats[UNION].p) + 0.5)}"
            )
            if not two_generals:
                print()
                print("UNION INTELLIGENCE SUGGEST THAT THE SOUTH USED")
                print("STRATEGIES 1, 2, 3, 4 IN THE FOLLOWING PERCENTAGES")
                print(
                    f"{confederate_strategy_prob_distribution[CONF]} {confederate_strategy_prob_distribution[UNION]} {confederate_strategy_prob_distribution[3]} {confederate_strategy_prob_distribution[4]}"
                )
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 27_Civil_War/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 27_Civil_War/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 27_Civil_War/vbnet/CivilWar.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "CivilWar", "CivilWar.vbproj", "{D726A817-AF69-43B9-9092-876453960C19}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{D726A817-AF69-43B9-9092-876453960C19}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{D726A817-AF69-43B9-9092-876453960C19}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{D726A817-AF69-43B9-9092-876453960C19}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{D726A817-AF69-43B9-9092-876453960C19}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 27_Civil_War/vbnet/CivilWar.vbproj
    ================================================
    
      
        Exe
        CivilWar
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 27_Civil_War/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 28_Combat/README.md
    ================================================
    ### Combat
    
    In this game, you are fighting a small-scale war with the computer. You have 72,000 troops which you first ust distribute among your Army, Navy, and Air Force. You may distribute them in any way you choose as long as you don’t use more than 72,000.
    
    You then attack your opponent (the computer) and input which service and the number of men you wish to use. The computer then tells you the outcome of the battle, gives you the current statistics and allows you to determine your next move.
    
    After the second battle, it is decided from the total statistics whether you win or lose or if a treaty is signed.
    
    This program was created by Bob Dores of Milton, Massachusetts.
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=50)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=65)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### 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)
    
    
    ================================================
    FILE: 28_Combat/combat.bas
    ================================================
    1 PRINT TAB(33);"COMBAT"
    2 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    3 PRINT: PRINT: PRINT
    4 PRINT "I AM AT WAR WITH YOU.": PRINT "WE HAVE 72000 SOLDIERS APIECE."
    5 PRINT:PRINT "DISTRIBUTE YOUR FORCES."
    6 PRINT ,"ME","  YOU"
    7 PRINT "ARMY",30000,
    8 INPUT A
    9 PRINT "NAVY",20000,
    10 INPUT B
    11 PRINT "A. F.",22000,
    12 INPUT C
    13 IF A+B+C>72000 THEN 5
    14 D=30000
    15 E=20000
    16 F=22000
    17 PRINT "YOU ATTACK FIRST. TYPE (1) FOR ARMY; (2) FOR NAVY;"
    18 PRINT "AND (3) FOR AIR FORCE."
    19 INPUT Y
    20 PRINT "HOW MANY MEN"
    21 INPUT X
    22 IF X<0 THEN 20
    23 ON Y GOTO 100,200,300
    100 IF X>A THEN 20
    105 IF XB THEN 20
    210 IF XC THEN 20
    310 IF XA THEN 1030
    1610 IF TB THEN 1030
    1710 IF TC THEN 1030
    1810 IF T>F/2 THEN 1830
    1820 GOTO 1850
    1830 PRINT "MY NAVY AND AIR FORCE IN A COMBINED ATTACK LEFT"
    1831 PRINT "YOUR COUNTRY IN SHAMBLES."
    1835 A=A/3
    1837 B=B/3
    1840 C=C/3
    1845 GOTO 2000
    1850 PRINT "ONE OF YOUR PLANES CRASHED INTO MY HOUSE. I AM DEAD."
    1851 PRINT "MY COUNTRY FELL APART."
    1860 GOTO 2010
    2000 PRINT
    2001 PRINT "FROM THE RESULTS OF BOTH OF YOUR ATTACKS,"
    2002 IF A+B+C>3/2*(D+E+F) THEN 2010
    2005 IF A+B+C<2/3*(D+E+F) THEN 2015
    2006 PRINT "THE TREATY OF PARIS CONCLUDED THAT WE TAKE OUR"
    2007 PRINT "RESPECTIVE COUNTRIES AND LIVE IN PEACE."
    2008 GOTO 2020
    2010 PRINT "YOU WON, OH! SHUCKS!!!!"
    2012 GOTO 2020
    2015 PRINT "YOU LOST-I CONQUERED YOUR COUNTRY.  IT SERVES YOU"
    2016 PRINT "RIGHT FOR PLAYING THIS STUPID GAME!!!"
    2020 END
    
    
    ================================================
    FILE: 28_Combat/csharp/ArmedForces.cs
    ================================================
    using System;
    
    namespace Game
    {
        /// 
        /// Represents the armed forces for a country.
        /// 
        public record ArmedForces
        {
            /// 
            /// Gets the number of men and women in the army.
            /// 
            public int Army { get; init; }
    
            /// 
            /// Gets the number of men and women in the navy.
            /// 
            public int Navy { get; init; }
    
            /// 
            /// Gets the number of men and women in the air force.
            /// 
            public int AirForce { get; init; }
    
            /// 
            /// Gets the total number of troops in the armed forces.
            /// 
            public int TotalTroops => Army + Navy + AirForce;
    
            /// 
            /// Gets the number of men and women in the given branch.
            /// 
            public int this[MilitaryBranch branch] =>
                branch switch
                {
                    MilitaryBranch.Army     => Army,
                    MilitaryBranch.Navy     => Navy,
                    MilitaryBranch.AirForce => AirForce,
                    _                       => throw new ArgumentException("INVALID BRANCH")
                };
        }
    }
    
    
    ================================================
    FILE: 28_Combat/csharp/Ceasefire.cs
    ================================================
    using System;
    
    namespace Game
    {
        /// 
        /// Represents the state of the game after reaching a ceasefire.
        /// 
        public sealed class Ceasefire : WarState
        {
            /// 
            /// Gets a flag indicating whether the player achieved absolute victory.
            /// 
            public override bool IsAbsoluteVictory { get; }
    
            /// 
            /// Gets the outcome of the war.
            /// 
            public override WarResult? FinalOutcome
            {
                get
                {
                    if (IsAbsoluteVictory || PlayerForces.TotalTroops > 3 / 2 * ComputerForces.TotalTroops)
                        return WarResult.PlayerVictory;
                    else
                    if (PlayerForces.TotalTroops < 2 / 3 * ComputerForces.TotalTroops)
                        return WarResult.ComputerVictory;
                    else
                        return WarResult.PeaceTreaty;
                }
            }
    
            /// 
            /// Initializes a new instance of the Ceasefire class.
            /// 
            /// 
            /// The computer's forces.
            /// 
            /// 
            /// The player's forces.
            /// 
            /// 
            /// Indicates whether the player acheived absolute victory (defeating
            /// the computer without destroying its military).
            /// 
            public Ceasefire(ArmedForces computerForces, ArmedForces playerForces, bool absoluteVictory = false)
                : base(computerForces, playerForces)
            {
                IsAbsoluteVictory = absoluteVictory;
            }
    
            protected override (WarState nextState, string message) AttackWithArmy(int attackSize) =>
                throw new InvalidOperationException("THE WAR IS OVER");
    
            protected override (WarState nextState, string message) AttackWithNavy(int attackSize) =>
                throw new InvalidOperationException("THE WAR IS OVER");
    
            protected override (WarState nextState, string message) AttackWithAirForce(int attackSize) =>
                throw new InvalidOperationException("THE WAR IS OVER");
        }
    }
    
    
    ================================================
    FILE: 28_Combat/csharp/Combat.csproj
    ================================================
    
      
        Exe
        net5.0
      
    
    
    
    ================================================
    FILE: 28_Combat/csharp/Combat.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.31321.278
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Combat", "Combat.csproj", "{F7CEEC00-CF2C-436C-883B-55A20C21AB93}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{F7CEEC00-CF2C-436C-883B-55A20C21AB93}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{F7CEEC00-CF2C-436C-883B-55A20C21AB93}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{F7CEEC00-CF2C-436C-883B-55A20C21AB93}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{F7CEEC00-CF2C-436C-883B-55A20C21AB93}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {1EBA7488-1DA6-4B0B-8234-F10A65E96BDB}
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 28_Combat/csharp/Controller.cs
    ================================================
    using System;
    
    namespace Game
    {
        /// 
        /// Contains functions for interacting with the user.
        /// 
        public class Controller
        {
            /// 
            /// Gets the player's initial armed forces distribution.
            /// 
            /// 
            /// The computer's initial armed forces.
            /// 
            public static ArmedForces GetInitialForces(ArmedForces computerForces)
            {
                var playerForces = default(ArmedForces);
    
                // BUG: This loop allows the player to assign negative values to
                //  some branches, leading to strange results.
                do
                {
                    View.ShowDistributeForces();
    
                    View.PromptArmySize(computerForces.Army);
                    var army = InputInteger();
    
                    View.PromptNavySize(computerForces.Navy);
                    var navy = InputInteger();
    
                    View.PromptAirForceSize(computerForces.AirForce);
                    var airForce = InputInteger();
    
                    playerForces = new ArmedForces
                    {
                        Army     = army,
                        Navy     = navy,
                        AirForce = airForce
                    };
                }
                while (playerForces.TotalTroops > computerForces.TotalTroops);
    
                return playerForces;
            }
    
            /// 
            /// Gets the military branch for the user's next attack.
            /// 
            public static MilitaryBranch GetAttackBranch(WarState state, bool isFirstTurn)
            {
                if (isFirstTurn)
                    View.PromptFirstAttackBranch();
                else
                    View.PromptNextAttackBranch(state.ComputerForces, state.PlayerForces);
    
                // If the user entered an invalid branch number in the original
                // game, the code fell through to the army case.  We'll preserve
                // that behaviour here.
                return Console.ReadLine() switch
                {
                    "2" => MilitaryBranch.Navy,
                    "3" => MilitaryBranch.AirForce,
                    _   => MilitaryBranch.Army
                };
            }
    
            /// 
            /// Gets a valid attack size from the player for the given branch
            /// of the armed forces.
            /// 
            /// 
            /// The number of troops available.
            /// 
            public static int GetAttackSize(int troopsAvailable)
            {
                var attackSize = 0;
    
                do
                {
                    View.PromptAttackSize();
                    attackSize = InputInteger();
                }
                while (attackSize < 0 || attackSize > troopsAvailable);
    
                return attackSize;
            }
    
            /// 
            /// Gets an integer value from the user.
            /// 
            public static int InputInteger()
            {
                var value = default(int);
    
                while (!Int32.TryParse(Console.ReadLine(), out value))
                    View.PromptValidInteger();
    
                return value;
            }
        }
    }
    
    
    ================================================
    FILE: 28_Combat/csharp/FinalCampaign.cs
    ================================================
    namespace Game
    {
        /// 
        /// Represents the state of the game during the final campaign of the war.
        /// 
        public sealed class FinalCampaign : WarState
        {
            /// 
            /// Initializes a new instance of the FinalCampaign class.
            /// 
            /// 
            /// The computer's forces.
            /// 
            /// 
            /// The player's forces.
            /// 
            public FinalCampaign(ArmedForces computerForces, ArmedForces playerForces)
                : base(computerForces, playerForces)
            {
            }
    
            protected override (WarState nextState, string message) AttackWithArmy(int attackSize)
            {
                if (attackSize < ComputerForces.Army / 2)
                {
                    return
                    (
                        new Ceasefire(
                            ComputerForces,
                            PlayerForces with
                            {
                                Army = PlayerForces.Army - attackSize
                            }),
                        "I WIPED OUT YOUR ATTACK!"
                    );
                }
                else
                {
                    return
                    (
                        new Ceasefire(
                            ComputerForces with
                            {
                                Army = 0
                            },
                            PlayerForces),
                        "YOU DESTROYED MY ARMY!"
                    );
                }
            }
    
            protected override (WarState nextState, string message) AttackWithNavy(int attackSize)
            {
                if (attackSize < ComputerForces.Navy / 2)
                {
                    return
                    (
                        new Ceasefire(
                            ComputerForces,
                            PlayerForces with
                            {
                                Army = PlayerForces.Army / 4,
                                Navy = PlayerForces.Navy / 2
                            }),
                        "I SUNK TWO OF YOUR BATTLESHIPS, AND MY AIR FORCE\n" +
                        "WIPED OUT YOUR UNGAURDED CAPITOL."
                    );
                }
                else
                {
                    return
                    (
                        new Ceasefire(
                            ComputerForces with
                            {
                                AirForce = 2 * ComputerForces.AirForce / 3,
                                Navy     = ComputerForces.Navy / 2
                            },
                            PlayerForces),
                        "YOUR NAVY SHOT DOWN THREE OF MY XIII PLANES,\n" +
                        "AND SUNK THREE BATTLESHIPS."
                    );
                }
            }
    
            protected override (WarState nextState, string message) AttackWithAirForce(int attackSize)
            {
                // BUG? Usually, larger attacks lead to better outcomes.
                //  It seems odd that the logic is suddenly reversed here,
                //  but this could be intentional.
                if (attackSize > ComputerForces.AirForce / 2)
                {
                    return
                    (
                        new Ceasefire(
                            ComputerForces,
                            PlayerForces with
                            {
                                Army     = PlayerForces.Army  / 3,
                                Navy     = PlayerForces.Navy / 3,
                                AirForce = PlayerForces.AirForce / 3
                            }),
                        "MY NAVY AND AIR FORCE IN A COMBINED ATTACK LEFT\n" +
                        "YOUR COUNTRY IN SHAMBLES."
                    );
                }
                else
                {
                    return
                    (
                        new Ceasefire(
                            ComputerForces,
                            PlayerForces,
                            absoluteVictory: true),
                        "ONE OF YOUR PLANES CRASHED INTO MY HOUSE. I AM DEAD.\n" +
                        "MY COUNTRY FELL APART."
                    );
                }
            }
        }
    }
    
    
    ================================================
    FILE: 28_Combat/csharp/InitialCampaign.cs
    ================================================
    namespace Game
    {
        /// 
        /// Represents the state of the game during the initial campaign of the war.
        /// 
        public sealed class InitialCampaign : WarState
        {
            /// 
            /// Initializes a new instance of the InitialCampaign class.
            /// 
            /// 
            /// The computer's forces.
            /// 
            /// 
            /// The player's forces.
            /// 
            public InitialCampaign(ArmedForces computerForces, ArmedForces playerForces)
                : base(computerForces, playerForces)
            {
            }
    
            protected override (WarState nextState, string message) AttackWithArmy(int attackSize)
            {
                // BUG: Why are we comparing attack size to the size of our own
                //   military?  This leads to some truly absurd results if our
                //   army is tiny.
                if (attackSize < PlayerForces.Army / 3)
                {
                    return
                    (
                        new FinalCampaign(
                            ComputerForces,
                            PlayerForces with
                            {
                                Army = PlayerForces.Army - attackSize
                            }),
                        $"YOU LOST {attackSize} MEN FROM YOUR ARMY."
                    );
                }
                else
                if (attackSize < 2 * PlayerForces.Army / 3)
                {
                    return
                    (
                        new FinalCampaign(
                            ComputerForces with
                            {
                                // BUG: Clearly not what we claim below...
                                Army = 0
                            },
                            PlayerForces with
                            {
                                Army = PlayerForces.Army - attackSize / 3
                            }),
                        $"YOU LOST {attackSize / 3} MEN, BUT I LOST {2 * ComputerForces.Army / 3}"
                    );
                }
                else
                {
                    // BUG? This is identical to the third outcome when attacking
                    //  with the navy.  It seems unlikely that this was the
                    //  intent.  Probably line 115 in the original source was
                    //  supposed to say "GOTO 170" instead of "GOTO 270".
                    //  (Line 170 is conspicuously absent.)
                    return
                    (
                        new FinalCampaign(
                            ComputerForces with
                            {
                                Navy = 2 * ComputerForces.Navy / 3
                            },
                            PlayerForces with
                            {
                                Army     = PlayerForces.Army / 3,
                                AirForce = PlayerForces.AirForce / 3
                            }),
                        "YOU SUNK ONE OF MY PATROL BOATS, BUT I WIPED OUT TWO\n" +
                        "OF YOUR AIR FORCE BASES AND 3 ARMY BASES."
                    );
                }
            }
    
            protected override (WarState nextState, string message) AttackWithNavy(int attackSize)
            {
                if (attackSize < ComputerForces.Navy / 3)
                {
                    return
                    (
                        new FinalCampaign(
                            ComputerForces,
                            PlayerForces with
                            {
                                Navy = PlayerForces.Navy - attackSize
                            }),
                        "YOUR ATTACK WAS STOPPED!"
                    );
                }
                else
                if (attackSize < 2 * ComputerForces.Navy / 3)
                {
                    return
                    (
                        new FinalCampaign(
                            ComputerForces with
                            {
                                Navy = ComputerForces.Navy / 3
                            },
                            PlayerForces),
                        $"YOU DESTROYED {2 * ComputerForces.Navy / 3} OF MY ARMY."
                    );
                }
                else
                {
                    return
                    (
                        new FinalCampaign(
                            ComputerForces with
                            {
                                Navy = 2 * ComputerForces.Navy / 3
                            },
                            PlayerForces with
                            {
                                Army     = PlayerForces.Army / 3,
                                AirForce = PlayerForces.AirForce / 3
                            }),
                        "YOU SUNK ONE OF MY PATROL BOATS, BUT I WIPED OUT TWO\n" +
                        "OF YOUR AIR FORCE BASES AND 3 ARMY BASES."
                    );
                }
            }
    
            protected override (WarState nextState, string message) AttackWithAirForce(int attackSize)
            {
                // BUG: Why are we comparing the attack size to the size of
                //  our own air force?  Surely we meant to compare to the
                //  computer's air force.
                if (attackSize < PlayerForces.AirForce / 3)
                {
                    return
                    (
                        new FinalCampaign(
                            ComputerForces,
                            PlayerForces with
                            {
                                AirForce = PlayerForces.AirForce - attackSize
                            }),
                        "YOUR ATTACK WAS WIPED OUT."
                    );
                }
                else
                if (attackSize < 2 * PlayerForces.AirForce / 3)
                {
                    return
                    (
                        new FinalCampaign(
                            ComputerForces with
                            {
                                Army     = 2 * ComputerForces.Army / 3,
                                Navy     = ComputerForces.Navy / 3,
                                AirForce = ComputerForces.AirForce / 3
                            },
                            PlayerForces),
                        "WE HAD A DOGFIGHT. YOU WON - AND FINISHED YOUR MISSION."
                    );
                }
                else
                {
    
                    return
                    (
                        new FinalCampaign(
                            ComputerForces with
                            {
                                Army = 2 * ComputerForces.Army / 3
                            },
                            PlayerForces with
                            {
                                Army = PlayerForces.Army / 4,
                                Navy = PlayerForces.Navy / 3
                            }),
                        "YOU WIPED OUT ONE OF MY ARMY PATROLS, BUT I DESTROYED" +
                        "TWO NAVY BASES AND BOMBED THREE ARMY BASES."
                    );
                }
            }
        }
    }
    
    
    ================================================
    FILE: 28_Combat/csharp/MilitaryBranch.cs
    ================================================
    namespace Game
    {
        /// 
        /// Enumerates the different branches of the military.
        /// 
        public enum MilitaryBranch
        {
            Army,
            Navy,
            AirForce
        }
    }
    
    
    ================================================
    FILE: 28_Combat/csharp/Program.cs
    ================================================
    namespace Game
    {
        class Program
        {
            static void Main()
            {
                View.ShowBanner();
                View.ShowInstructions();
    
                var computerForces = new ArmedForces { Army = 30000, Navy = 20000, AirForce = 22000 };
                var playerForces   = Controller.GetInitialForces(computerForces);
    
                var state = (WarState) new InitialCampaign(computerForces, playerForces);
                var isFirstTurn = true;
    
                while (!state.FinalOutcome.HasValue)
                {
                    var branch = Controller.GetAttackBranch(state, isFirstTurn);
                    var attackSize = Controller.GetAttackSize(state.PlayerForces[branch]);
    
                    var (nextState, message) = state.LaunchAttack(branch, attackSize);
                    View.ShowMessage(message);
    
                    state = nextState;
                    isFirstTurn = false;
                }
    
                View.ShowResult(state);
            }
        }
    }
    
    
    ================================================
    FILE: 28_Combat/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    The original BASIC code has a surprising number of bugs for such a small program.
    For the sake of preserving the original behaviour, I've left them in place and
    commented the ones I noticed.
    
    
    ================================================
    FILE: 28_Combat/csharp/View.cs
    ================================================
    using System;
    
    namespace Game
    {
        /// 
        /// Contains functions for displaying information to the user.
        /// 
        public static class View
        {
            public static void ShowBanner()
            {
                Console.WriteLine("                                 COMBAT");
                Console.WriteLine("               CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
            }
    
            public static void ShowInstructions()
            {
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine("I AM AT WAR WITH YOU.");
                Console.WriteLine("WE HAVE 72000 SOLDIERS APIECE.");
            }
    
            public static void ShowDistributeForces()
            {
                Console.WriteLine();
                Console.WriteLine("DISTRIBUTE YOUR FORCES.");
                Console.WriteLine("\tME\t  YOU");
            }
    
            public static void ShowMessage(string message)
            {
                Console.WriteLine(message);
            }
    
            public static void ShowResult(WarState finalState)
            {
                if (!finalState.IsAbsoluteVictory)
                {
                    Console.WriteLine();
                    Console.WriteLine("FROM THE RESULTS OF BOTH OF YOUR ATTACKS,");
                }
    
                switch (finalState.FinalOutcome)
                {
                case WarResult.ComputerVictory:
                    Console.WriteLine("YOU LOST-I CONQUERED YOUR COUNTRY.  IT SERVES YOU");
                    Console.WriteLine("RIGHT FOR PLAYING THIS STUPID GAME!!!");
                    break;
                case WarResult.PlayerVictory:
                    Console.WriteLine("YOU WON, OH! SHUCKS!!!!");
                    break;
                case WarResult.PeaceTreaty:
                    Console.WriteLine("THE TREATY OF PARIS CONCLUDED THAT WE TAKE OUR");
                    Console.WriteLine("RESPECTIVE COUNTRIES AND LIVE IN PEACE.");
                    break;
                }
            }
    
            public static void PromptArmySize(int computerArmySize)
            {
                Console.Write($"ARMY\t{computerArmySize}\t? ");
            }
    
            public static void PromptNavySize(int computerNavySize)
            {
                Console.Write($"NAVY\t{computerNavySize}\t? ");
            }
    
            public static void PromptAirForceSize(int computerAirForceSize)
            {
                Console.Write($"A. F.\t{computerAirForceSize}\t? ");
            }
    
            public static void PromptFirstAttackBranch()
            {
                Console.WriteLine("YOU ATTACK FIRST. TYPE (1) FOR ARMY; (2) FOR NAVY;");
                Console.WriteLine("AND (3) FOR AIR FORCE.");
                Console.Write("? ");
            }
    
            public static void PromptNextAttackBranch(ArmedForces computerForces, ArmedForces playerForces)
            {
                // BUG: More of a nit-pick really, but the order of columns in the
                //  table is reversed from what we showed when distributing troops.
                //  The tables should be consistent.
                Console.WriteLine();
                Console.WriteLine("\tYOU\tME");
                Console.WriteLine($"ARMY\t{playerForces.Army}\t{computerForces.Army}");
                Console.WriteLine($"NAVY\t{playerForces.Navy}\t{computerForces.Navy}");
                Console.WriteLine($"A. F.\t{playerForces.AirForce}\t{computerForces.AirForce}");
    
                Console.WriteLine("WHAT IS YOUR NEXT MOVE?");
                Console.WriteLine("ARMY=1  NAVY=2  AIR FORCE=3");
                Console.Write("? ");
            }
    
            public static void PromptAttackSize()
            {
                Console.WriteLine("HOW MANY MEN");
                Console.Write("? ");
            }
    
            public static void PromptValidInteger()
            {
                Console.WriteLine("ENTER A VALID INTEGER VALUE");
            }
        }
    }
    
    
    ================================================
    FILE: 28_Combat/csharp/WarResult.cs
    ================================================
    namespace Game
    {
        /// 
        /// Enumerates the possible outcomes of the war.
        /// 
        public enum WarResult
        {
            ComputerVictory,
    
            PlayerVictory,
    
            PeaceTreaty
        }
    }
    
    
    ================================================
    FILE: 28_Combat/csharp/WarState.cs
    ================================================
    using System;
    
    namespace Game
    {
        /// 
        /// Represents the current state of the war.
        /// 
        public abstract class WarState
        {
            /// 
            /// Gets the computer's armed forces.
            /// 
            public ArmedForces ComputerForces { get; }
    
            /// 
            /// Gets the player's armed forces.
            /// 
            public ArmedForces PlayerForces { get; }
    
            /// 
            /// Gets a flag indicating whether this state represents absolute
            /// victory for the player.
            /// 
            public virtual bool IsAbsoluteVictory => false;
    
            /// 
            /// Gets the final outcome of the war.
            /// 
            /// 
            /// If the war is ongoing, this property will be null.
            /// 
            public virtual WarResult? FinalOutcome => null;
    
            /// 
            /// Initializes a new instance of the state class.
            /// 
            /// 
            /// The computer's forces.
            /// 
            /// 
            /// The player's forces.
            /// 
            public WarState(ArmedForces computerForces, ArmedForces playerForces) =>
                (ComputerForces, PlayerForces) = (computerForces, playerForces);
    
            /// 
            /// Launches an attack.
            /// 
            /// 
            /// The branch of the military to use for the attack.
            /// 
            /// 
            /// The number of men and women to use for the attack.
            /// 
            /// 
            /// The new state of the game resulting from the attack and a message
            /// describing the result.
            /// 
            public (WarState nextState, string message) LaunchAttack(MilitaryBranch branch, int attackSize) =>
                branch switch
                {
                    MilitaryBranch.Army     => AttackWithArmy(attackSize),
                    MilitaryBranch.Navy     => AttackWithNavy(attackSize),
                    MilitaryBranch.AirForce => AttackWithAirForce(attackSize),
                    _               => throw new ArgumentException("INVALID BRANCH")
                };
    
            /// 
            /// Conducts an attack with the player's army.
            /// 
            /// 
            /// The number of men and women used in the attack.
            /// 
            /// 
            /// The new game state and a message describing the result.
            /// 
            protected abstract (WarState nextState, string message) AttackWithArmy(int attackSize);
    
            /// 
            /// Conducts an attack with the player's navy.
            /// 
            /// 
            /// The number of men and women used in the attack.
            /// 
            /// 
            /// The new game state and a message describing the result.
            /// 
            protected abstract (WarState nextState, string message) AttackWithNavy(int attackSize);
    
            /// 
            /// Conducts an attack with the player's air force.
            /// 
            /// 
            /// The number of men and women used in the attack.
            /// 
            /// 
            /// The new game state and a message describing the result.
            /// 
            protected abstract (WarState nextState, string message) AttackWithAirForce(int attackSize);
        }
    }
    
    
    ================================================
    FILE: 28_Combat/java/Combat.java
    ================================================
    import java.lang.Math;
    import java.util.Scanner;
    
    /**
     * Game of Combat
     * 

    * Based on the BASIC game of Combat here * https://github.com/coding-horror/basic-computer-games/blob/main/28%20Combat/combat.bas *

    * Note: The idea was to create a version of the 1970's BASIC game in Java, without introducing * new features - no additional text, error checking, etc has been added. * * Converted from BASIC to Java by Darren Cardenas. */ public class Combat { private static final int MAX_UNITS = 72000; // Maximum number of total units per player private final Scanner scan; // For user input private boolean planeCrashWin = false; private int usrArmy = 0; // Number of user Army units private int usrNavy = 0; // Number of user Navy units private int usrAir = 0; // Number of user Air Force units private int cpuArmy = 30000; // Number of cpu Army units private int cpuNavy = 20000; // Number of cpu Navy units private int cpuAir = 22000; // Number of cpu Air Force units public Combat() { scan = new Scanner(System.in); } // End of constructor Combat public void play() { showIntro(); getForces(); attackFirst(); attackSecond(); } // End of method play private static void showIntro() { System.out.println(" ".repeat(32) + "COMBAT"); System.out.println(" ".repeat(14) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println("\n\n"); System.out.println("I AM AT WAR WITH YOU."); System.out.println("WE HAVE " + MAX_UNITS + " SOLDIERS APIECE.\n"); } // End of method showIntro private void getForces() { do { System.out.println("DISTRIBUTE YOUR FORCES."); System.out.println(" ME YOU"); System.out.print("ARMY " + cpuArmy + " ? "); usrArmy = scan.nextInt(); System.out.print("NAVY " + cpuNavy + " ? "); usrNavy = scan.nextInt(); System.out.print("A. F. " + cpuAir + " ? "); usrAir = scan.nextInt(); } while ((usrArmy + usrNavy + usrAir) > MAX_UNITS); // Avoid exceeding the maximum number of total units } // End of method getForces private void attackFirst() { int numUnits = 0; int unitType = 0; do { System.out.println("YOU ATTACK FIRST. TYPE (1) FOR ARMY; (2) FOR NAVY;"); System.out.println("AND (3) FOR AIR FORCE."); System.out.print("? "); unitType = scan.nextInt(); } while ((unitType < 1) || (unitType > 3)); // Avoid out-of-range values do { System.out.println("HOW MANY MEN"); System.out.print("? "); numUnits = scan.nextInt(); } while ((numUnits < 0) || // Avoid negative values ((unitType == 1) && (numUnits > usrArmy)) || // Avoid exceeding the number of available Army units ((unitType == 2) && (numUnits > usrNavy)) || // Avoid exceeding the number of available Navy units ((unitType == 3) && (numUnits > usrAir))); // Avoid exceeding the number of available Air Force units // Begin handling deployment type switch (unitType) { case 1: // Army deployed if (numUnits < (usrArmy / 3.0)) { // User deployed less than one-third of their Army units System.out.println("YOU LOST " + numUnits + " MEN FROM YOUR ARMY."); usrArmy = usrArmy - numUnits; } else if (numUnits < (2.0 * usrArmy / 3.0)) { // User deployed less than two-thirds of their Army units System.out.println("YOU LOST " + (int) Math.floor(numUnits / 3.0) + " MEN, BUT I LOST " + (int) Math.floor(2.0 * cpuArmy / 3.0)); usrArmy = (int) Math.floor(usrArmy - numUnits / 3.0); cpuArmy = 0; } else { // User deployed two-thirds or more of their Army units System.out.println("YOU SUNK ONE OF MY PATROL BOATS, BUT I WIPED OUT TWO"); System.out.println("OF YOUR AIR FORCE BASES AND 3 ARMY BASES."); usrArmy = (int) Math.floor(usrArmy / 3.0); usrAir = (int) Math.floor(usrAir / 3.0); cpuNavy = (int) Math.floor(2.0 * cpuNavy / 3.0); } break; case 2: // Navy deployed if (numUnits < (cpuNavy / 3.0)) { // User deployed less than one-third relative to cpu Navy units System.out.println("YOUR ATTACK WAS STOPPED!"); usrNavy = usrNavy - numUnits; } else if (numUnits < (2.0 * cpuNavy / 3.0)) { // User deployed less than two-thirds relative to cpu Navy units System.out.println("YOU DESTROYED " + (int) Math.floor(2.0 * cpuNavy / 3.0) + " OF MY ARMY."); cpuNavy = (int) Math.floor(cpuNavy / 3.0); } else { // User deployed two-thirds or more relative to cpu Navy units System.out.println("YOU SUNK ONE OF MY PATROL BOATS, BUT I WIPED OUT TWO"); System.out.println("OF YOUR AIR FORCE BASES AND 3 ARMY BASES."); usrArmy = (int) Math.floor(usrArmy / 3.0); usrAir = (int) Math.floor(usrAir / 3.0); cpuNavy = (int) Math.floor(2.0 * cpuNavy / 3.0); } break; case 3: // Air Force deployed if (numUnits < (usrAir / 3.0)) { // User deployed less than one-third of their Air Force units System.out.println("YOUR ATTACK WAS WIPED OUT."); usrAir = usrAir - numUnits; } else if (numUnits < (2.0 * usrAir / 3.0)) { // User deployed less than two-thirds of their Air Force units System.out.println("WE HAD A DOGFIGHT. YOU WON - AND FINISHED YOUR MISSION."); cpuArmy = (int) Math.floor(2.0 * cpuArmy / 3.0); cpuNavy = (int) Math.floor(cpuNavy / 3.0); cpuAir = (int) Math.floor(cpuAir / 3.0); } else { // User deployed two-thirds or more of their Air Force units System.out.println("YOU WIPED OUT ONE OF MY ARMY PATROLS, BUT I DESTROYED"); System.out.println("TWO NAVY BASES AND BOMBED THREE ARMY BASES."); usrArmy = (int) Math.floor(usrArmy / 4.0); usrNavy = (int) Math.floor(usrNavy / 3.0); cpuArmy = (int) Math.floor(2.0 * cpuArmy / 3.0); } break; } // End handling deployment type } // End of method attackFirst private void attackSecond() { int numUnits = 0; int unitType = 0; System.out.println(""); System.out.println(" YOU ME"); System.out.print("ARMY "); System.out.format("%-14s%s\n", usrArmy, cpuArmy); System.out.print("NAVY "); System.out.format("%-14s%s\n", usrNavy, cpuNavy); System.out.print("A. F. "); System.out.format("%-14s%s\n", usrAir, cpuAir); do { System.out.println("WHAT IS YOUR NEXT MOVE?"); System.out.println("ARMY=1 NAVY=2 AIR FORCE=3"); System.out.print("? "); unitType = scan.nextInt(); } while ((unitType < 1) || (unitType > 3)); // Avoid out-of-range values do { System.out.println("HOW MANY MEN"); System.out.print("? "); numUnits = scan.nextInt(); } while ((numUnits < 0) || // Avoid negative values ((unitType == 1) && (numUnits > usrArmy)) || // Avoid exceeding the number of available Army units ((unitType == 2) && (numUnits > usrNavy)) || // Avoid exceeding the number of available Navy units ((unitType == 3) && (numUnits > usrAir))); // Avoid exceeding the number of available Air Force units // Begin handling deployment type switch (unitType) { case 1: // Army deployed if (numUnits < (cpuArmy / 2.0)) { // User deployed less than half relative to cpu Army units System.out.println("I WIPED OUT YOUR ATTACK!"); usrArmy = usrArmy - numUnits; } else { // User deployed half or more relative to cpu Army units System.out.println("YOU DESTROYED MY ARMY!"); cpuArmy = 0; } break; case 2: // Navy deployed if (numUnits < (cpuNavy / 2.0)) { // User deployed less than half relative to cpu Navy units System.out.println("I SUNK TWO OF YOUR BATTLESHIPS, AND MY AIR FORCE"); System.out.println("WIPED OUT YOUR UNGUARDED CAPITOL."); usrArmy = (int) Math.floor(usrArmy / 4.0); usrNavy = (int) Math.floor(usrNavy / 2.0); } else { // User deployed half or more relative to cpu Navy units System.out.println("YOUR NAVY SHOT DOWN THREE OF MY XIII PLANES,"); System.out.println("AND SUNK THREE BATTLESHIPS."); cpuAir = (int) Math.floor(2.0 * cpuAir / 3.0); cpuNavy = (int) Math.floor(cpuNavy / 2.0); } break; case 3: // Air Force deployed if (numUnits > (cpuAir / 2.0)) { // User deployed more than half relative to cpu Air Force units System.out.println("MY NAVY AND AIR FORCE IN A COMBINED ATTACK LEFT"); System.out.println("YOUR COUNTRY IN SHAMBLES."); usrArmy = (int) Math.floor(usrArmy / 3.0); usrNavy = (int) Math.floor(usrNavy / 3.0); usrAir = (int) Math.floor(usrAir / 3.0); } else { // User deployed half or less relative to cpu Air Force units System.out.println("ONE OF YOUR PLANES CRASHED INTO MY HOUSE. I AM DEAD."); System.out.println("MY COUNTRY FELL APART."); planeCrashWin = true; } break; } // End handling deployment type // Suppress message for plane crashes if (planeCrashWin == false) { System.out.println(""); System.out.println("FROM THE RESULTS OF BOTH OF YOUR ATTACKS,"); } // User wins if ((planeCrashWin == true) || ((usrArmy + usrNavy + usrAir) > ((int) Math.floor((3.0 / 2.0 * (cpuArmy + cpuNavy + cpuAir)))))) { System.out.println("YOU WON, OH! SHUCKS!!!!"); } // User loses else if ((usrArmy + usrNavy + usrAir) < ((int) Math.floor((2.0 / 3.0 * (cpuArmy + cpuNavy + cpuAir))))) { // User loss System.out.println("YOU LOST-I CONQUERED YOUR COUNTRY. IT SERVES YOU"); System.out.println("RIGHT FOR PLAYING THIS STUPID GAME!!!"); } // Peaceful outcome else { System.out.println("THE TREATY OF PARIS CONCLUDED THAT WE TAKE OUR"); System.out.println("RESPECTIVE COUNTRIES AND LIVE IN PEACE."); } } // End of method attackSecond public static void main(String[] args) { Combat combat = new Combat(); combat.play(); } // End of method main } // End of class Combat ================================================ FILE: 28_Combat/java/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Oracle Java](https://openjdk.java.net/) ================================================ FILE: 28_Combat/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 28_Combat/javascript/combat.html ================================================ COMBAT

    
    
    
    
    
    
    ================================================
    FILE: 28_Combat/javascript/combat.js
    ================================================
    // COMBAT
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    // Main program
    async function main()
    {
        print(tab(33) + "COMBAT\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        print("I AM AT WAR WITH YOU.\n");
        print("WE HAVE 72000 SOLDIERS APIECE.\n");
        do {
            print("\n");
            print("DISTRIBUTE YOUR FORCES.\n");
            print("\tME\t  YOU\n");
            print("ARMY\t30000\t");
            a = parseInt(await input());
            print("NAVY\t20000\t");
            b = parseInt(await input());
            print("A. F.\t22000\t");
            c = parseInt(await input());
        } while (a + b + c > 72000) ;
        d = 30000;
        e = 20000;
        f = 22000;
        print("YOU ATTACK FIRST. TYPE (1) FOR ARMY; (2) FOR NAVY;\n");
        print("AND (3) FOR AIR FORCE.\n");
        y = parseInt(await input());
        do {
            print("HOW MANY MEN\n");
            x = parseInt(await input());
        } while ((y == 1 && x > a) || (y == 2 && x > b) || (y == 3 && x > c)) ;
        switch (y) {
            case 1:
                if (x < a / 3.0) {
                    print("YOU LOST " + x + " MEN FROM YOUR ARMY.\n");
                    a -= x;
                    break;
                }
                if (x < 2 * a / 3) {
                    print("YOU LOST " + Math.floor(x / 3.0) + " MEN, BUT I LOST " + Math.floor(2 * d / 3.0) + "\n");
                    a = Math.floor(a - x / 3.0);
                    d = 0;
                    break;
                }
                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");
                a = Math.floor(a / 3.0);
                c = Math.floor(c / 3.0);
                e = Math.floor(2 * e / 3.0);
                break;
            case 2:
                if (x < e / 3) {
                    print("YOUR ATTACK WAS STOPPED!\n");
                    b -= x;
                    break;
                }
                if (x < 2 * e / 3) {
                    print("YOU DESTROYED " + Math.floor(2 * e / 3.0) + " OF MY ARMY.\n");
                    e = Math.floor(e / 3.0);
                    break;
                }
                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");
                a = Math.floor(a / 3.0);
                c = Math.floor(c / 3.0);
                e = Math.floor(2 * e / 3.0);
                break;
            case 3:
                if (x < c / 3.0) {
                    print("YOUR ATTACK WAS WIPED OUT.\n");
                    c -= x;
                    break;
                }
                if (x < 2 * c / 3) {
                    print("WE HAD A DOGFIGHT. YOU WON - AND FINISHED YOUR MISSION.\n");
                    d = Math.floor(2 * d / 3.0);
                    e = Math.floor(e / 3.0);
                    f = Math.floor(f / 3.0);
                    break;
                }
                print("YOU WIPED OUT ONE OF MY ARMY PATROLS, BUT I DESTROYED\n");
                print("TWO NAVY BASES AND BOMBED THREE ARMY BASES.\n");
                a = Math.floor(a / 4);
                b = Math.floor(b / 3.0);
                d = Math.floor(2 * d / 3.0);
                break;
        }
        print("\n");
        print("\tYOU\tME\n");
        print("ARMY\t" + a + "\t" + d + "\n");
        print("NAVY\t" + b + "\t" + e + "\n");
        print("A. F.\t" + c + "\t" + f + "\n");
        print("WHAT IS YOUR NEXT MOVE?\n");
        print("ARMY=1  NAVY=2  AIR FORCE=3\n");
        g = parseInt(await input());
        do {
            print("HOW MANY MEN\n");
            t = parseInt(await input());
        } while (t < 0 || (g == 1 && t > a) || (g == 2 && t > b) || (g == 3 && t > c)) ;
        crashed = false;
        switch (g) {
            case 1:
                if (t < d / 2) {
                    print("I WIPED OUT YOUR ATTACK!\n");
                    a -= t;
                } else {
                    print("YOU DESTROYED MY ARMY!\n");
                    d = 0;
                }
                break;
            case 2:
                if (t < e / 2) {
                    print("I SUNK TWO OF YOUR BATTLESHIPS, AND MY AIR FORCE\n");
                    print("WIPED OUT YOUR UNGUARDED CAPITOL.\n");
                    a /= 4.0;
                    b /= 2.0;
                    break;
                }
                print("YOUR NAVY SHOT DOWN THREE OF MY XIII PLANES.\n");
                print("AND SUNK THREE BATTLESHIPS.\n");
                f = 2 * f / 3;
                e /= 2;
                break;
            case 3:
                if (t > f / 2) {
                    print("MY NAVY AND AIR FORCE IN A COMBINED ATTACK LEFT\n");
                    print("YOUR COUNTRY IN SHAMBLES.\n");
                    a /= 3.0;
                    b /= 3.0;
                    c /= 3.0;
                    break;
                }
                print("ONE OF YOUR PLANES CRASHED INTO MY HOUSE. I AM DEAD.\n");
                print("MY COUNTRY FELL APART.\n");
                crashed = true;
                won = 1;
                break;
        }
        if (!crashed) {
            won = 0;
            print("\n");
            print("FROM THE RESULTS OF BOTH OF YOUR ATTACKS,\n");
            if (a + b + c > 3.0 / 2.0 * (d + e + f))
                won = 1;
            if (a + b + c < 2.0 / 3.0 * (d + e + f))
                won = 2;
        }
        if (won == 0) {
            print("THE TREATY OF PARIS CONCLUDED THAT WE TAKE OUR\n");
            print("RESPECTIVE COUNTRIES AND LIVE IN PEACE.\n");
        } else if (won == 1) {
            print("YOU WON, OH! SHUCKS!!!!\n");
        } else if (won == 2) {
            print("YOU LOST-I CONQUERED YOUR COUNTRY.  IT SERVES YOU\n");
            print("RIGHT FOR PLAYING THIS STUPID GAME!!!\n");
        }
    }
    
    main();
    
    
    ================================================
    FILE: 28_Combat/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 28_Combat/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 28_Combat/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 28_Combat/perl/combat.pl
    ================================================
    #!/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);
    }
    
    
    ================================================
    FILE: 28_Combat/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 28_Combat/python/combat.py
    ================================================
    MAX_UNITS = 72000
    plane_crash_win = False
    usr_army = 0
    usr_navy = 0
    usr_air = 0
    cpu_army = 30000
    cpu_navy = 20000
    cpu_air = 22000
    
    
    def show_intro() -> None:
        global MAX_UNITS
    
        print(" " * 32 + "COMBAT")
        print(" " * 14 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY")
        print("\n\n")
        print("I AM AT WAR WITH YOU.")
        print(f"WE HAVE {str(MAX_UNITS)} SOLDIERS APIECE.")
    
    
    def get_forces() -> None:
        global usr_army, usr_navy, usr_air
    
        while True:
            print("DISTRIBUTE YOUR FORCES.")
            print("              ME              YOU")
            print(f"ARMY           {str(cpu_army)}        ? ", end="")
            usr_army = int(input())
            print(f"NAVY           {str(cpu_navy)}        ? ", end="")
            usr_navy = int(input())
            print(f"A. F.          {str(cpu_air)}        ? ", end="")
            usr_air = int(input())
            if (usr_army + usr_navy + usr_air) <= MAX_UNITS:
                break
    
    
    def attack_first() -> None:
        global usr_army, usr_navy, usr_air
        global cpu_army, cpu_navy, cpu_air
    
        unit_type = 0
    
        while True:
            print("YOU ATTACK FIRST. TYPE (1) FOR ARMY; (2) FOR NAVY;")
            print("AND (3) FOR AIR FORCE.")
            print("?", end=" ")
            unit_type = int(input())
            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 (
                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(f"YOU LOST {str(num_units)} MEN FROM YOUR ARMY.")
                usr_army = usr_army - num_units
            elif num_units < (2 * usr_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:
                print("YOU SUNK ONE OF MY PATROL BOATS, BUT I WIPED OUT TWO")
                print("OF YOUR AIR FORCE BASES AND 3 ARMY BASES.")
                usr_army = int(usr_army / 3)
                usr_air = int(usr_air / 3)
                cpu_navy = int(2 * cpu_navy / 3)
        elif unit_type == 2:
            if num_units < cpu_navy / 3:
                print("YOUR ATTACK WAS STOPPED!")
                usr_navy = usr_navy - num_units
            elif num_units < 2 * cpu_navy / 3:
                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")
                print("OF YOUR AIR FORCE BASES AND 3 ARMY BASES.")
                usr_army = int(usr_army / 3)
                usr_air = int(usr_air / 3)
                cpu_navy = int(2 * cpu_navy / 3)
        elif unit_type == 3:
            if num_units < usr_air / 3:
                print("YOUR ATTACK WAS WIPED OUT.")
                usr_air = usr_air - num_units
            elif num_units < 2 * usr_air / 3:
                print("WE HAD A DOGFIGHT. YOU WON - AND FINISHED YOUR MISSION.")
                cpu_army = int(2 * cpu_army / 3)
                cpu_navy = int(cpu_navy / 3)
                cpu_air = int(cpu_air / 3)
            else:
                print("YOU WIPED OUT ONE OF MY ARMY PATROLS, BUT I DESTROYED")
                print("TWO NAVY BASES AND BOMBED THREE ARMY BASES.")
                usr_army = int(usr_army / 4)
                usr_navy = int(usr_navy / 3)
                cpu_army = int(2 * cpu_army / 3)
    
    
    def attack_second() -> None:
        global usr_army, usr_navy, usr_air, cpu_army, cpu_navy, cpu_air
        global plane_crash_win
        unit_type = 0
    
        print()
        print("              YOU           ME")
        print("ARMY           ", end="")
        print("%-14s%s\n" % (usr_army, cpu_army), end="")
        print("NAVY           ", end="")
        print("%-14s%s\n" % (usr_navy, cpu_navy), end="")
        print("A. F.          ", end="")
        print("%-14s%s\n" % (usr_air, cpu_air), end="")
    
        while True:
            print("WHAT IS YOUR NEXT MOVE?")
            print("ARMY=1  NAVY=2  AIR FORCE=3")
            print("? ", end="")
            unit_type = int(input())
            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 (
                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 < (cpu_army / 2):
                print("I WIPED OUT YOUR ATTACK!")
                usr_army = usr_army - num_units
            else:
                print("YOU DESTROYED MY ARMY!")
                cpu_army = 0
        elif unit_type == 2:
            if num_units < (cpu_navy / 2):
                print("I SUNK TWO OF YOUR BATTLESHIPS, AND MY AIR FORCE")
                print("WIPED OUT YOUR UNGUARDED CAPITOL.")
                usr_army = int(usr_army / 4)
                usr_navy = int(usr_navy / 2)
            else:
                print("YOUR NAVY SHOT DOWN THREE OF MY XIII PLANES,")
                print("AND SUNK THREE BATTLESHIPS.")
                cpu_air = int(2 * cpu_air / 3)
                cpu_navy = int(cpu_navy / 2)
        elif unit_type == 3:
            if num_units > (cpu_air / 2):
                print("MY NAVY AND AIR FORCE IN A COMBINED ATTACK LEFT")
                print("YOUR COUNTRY IN SHAMBLES.")
                usr_army = int(usr_army / 3)
                usr_navy = int(usr_navy / 3)
                usr_air = int(usr_air / 3)
            else:
                print("ONE OF YOUR PLANES CRASHED INTO MY HOUSE. I AM DEAD.")
                print("MY COUNTRY FELL APART.")
                plane_crash_win = True
    
        if not plane_crash_win:
            print()
            print("FROM THE RESULTS OF BOTH OF YOUR ATTACKS,")
    
        if plane_crash_win or (
            (usr_army + usr_navy + usr_air) > (int(3 / 2 * (cpu_army + cpu_navy + cpu_air)))
        ):
            print("YOU WON, OH! SHUCKS!!!!")
        elif (usr_army + usr_navy + usr_air) < int(2 / 3 * (cpu_army + cpu_navy + cpu_air)):
            print("YOU LOST-I CONQUERED YOUR COUNTRY.  IT SERVES YOU")
            print("RIGHT FOR PLAYING THIS STUPID GAME!!!")
        else:
            print("THE TREATY OF PARIS CONCLUDED THAT WE TAKE OUR")
            print("RESPECTIVE COUNTRIES AND LIVE IN PEACE.")
    
    
    def main() -> None:
        show_intro()
        get_forces()
        attack_first()
        attack_second()
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 28_Combat/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 28_Combat/vbnet/Combat.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Combat", "Combat.vbproj", "{B7C67E0E-1493-4957-9FD9-A384D038F024}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{B7C67E0E-1493-4957-9FD9-A384D038F024}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{B7C67E0E-1493-4957-9FD9-A384D038F024}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{B7C67E0E-1493-4957-9FD9-A384D038F024}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{B7C67E0E-1493-4957-9FD9-A384D038F024}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 28_Combat/vbnet/Combat.vbproj
    ================================================
    
      
        Exe
        Combat
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 28_Combat/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 29_Craps/README.md
    ================================================
    ### Craps
    
    This game simulates the game of craps played according to standard Nevada craps table rules. That is:
    1. A 7 or 11 on the first roll wins
    2. A 2, 3, or 12 on the first roll loses
    3. Any other number rolled becomes your “point.”
        - You continue to roll, if you get your point, you win.
        - If you roll a 7, you lose and the dice change hands when this happens.
    
    This version of craps was modified by Steve North of Creative Computing. It is based on an original which appeared one day on a computer at DEC.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=52)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=67)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
        15 LET R=0
    
    `R` is a variable that tracks winnings and losings.  Unlike other games that
    start out with a lump sum of cash to spend this game assumes the user has as
    much money as they want and we only track how much they lost or won.
    
          21 LET T=1
          22 PRINT "PICK A NUMBER AND INPUT TO ROLL DICE";
          23 INPUT Z
          24 LET X=(RND(0))
          25 LET T =T+1
          26 IF T<=Z THEN 24
    
    This block of code does nothing other than try to scramble the random number
    generator. Random number generation is not random, they are generated from the
    previous generated number. Because of the slow speed of these systems back then,
    gaming random number generators was a concern, mostly for gameplay quality.
    If you could know the "seed value" to the generator then you could effectively
    know how to get the exact same dice rolls to happen and change your bet to
    maximize your winnings and minimize your losses.
    
    The first reason this is an example of bad coding practice is the user is asked
    to input a number but no clue is given as to the use of this number. This number
    has no bearing on the game and as we'll see only has bearing on the internal
    implementation of somehow trying to get an un-game-able seed for the random number
    generator (since all future random numbers generated are based off this seed value.)
    
    The `RND(1)` command generates a number from a seed value that is always
    the same, everytime, from when the machine is booted up (old C64 behavior). In
    order to avoid the same dice rolls being generated, a special call to `RND(-TI)`
    would initialize the random generator with something else. But RND(-TI) is not
    a valid command on all systems. So `RND(0)`, which generates a random number
    from the system clock is used. But technically this could be gamed because the
    system clock was driven by the bootup time, there wasn't a BIOS battery on these
    systems that kept an internal real time clock going even when the system was
    turned off, unlike your regular PC. Therefore, in order to ensure as true
    randomness as possible, insert human reaction time by asking for human input.
    
    But a human could just be holding down the enter key on bootup and that would
    just skip any kind of multi-millisecond variance assigned by a natural human
    reaction time. So, paranoia being a great motivator, a number is asked of the
    user to avoid just holding down the enter key which negates the timing variance
    of a human reaction.
    
    What comes next is a bit of nonsense. The block of code loops a counter, recalling
    the `RND(0)` function (and thus reseeding it with the system clock value)
    and then comparing the counter to the user's number input
    in order to bail out of the loop. Because the `RND(0)` function is based off the
    system clock and the loop of code has no branching other than the bailout
    condition, the loop also takes a fixed amount of time to execute, thus making
    repeated calls to `RND(0)` predictive and this scheming to get a better random
    number is pointless. Furthermore, the loop is based on the number the user inputs
    so a huge number like ten million causes a very noticable delay and leaves the
    user wondering if the program has errored. The author could have simply called
    `RND(0)` once and used a prompt that made more sense like asking for the users
    name and then using that name in the game's replies.
    
    It is advised that you use whatever your languages' random number generator
    provides and simply skip trying to recreate this bit of nonsense including
    the user input.
    
          27 PRINT"INPUT THE AMOUNT OF YOUR WAGER.";
          28 INPUT F
          30 PRINT "I WILL NOW THROW THE DICE"
          40 LET E=INT(7*RND(1))
          41 LET S=INT(7*RND(1))
          42 LET X=E+S
          .... a bit later ....
          60 IF X=1 THEN 40
          65 IF X=0 THEN 40
    
    `F` is a variable that represents the users wager for this betting round.
    `E` and `S` represent the two individual and random dice being rolled.
    This code is actually wrong because it returns a value between 0 and 6.
    `X` is the sum of these dice rolls. As you'll see though further down in the
    code, if `X` is zero or one it re-rolls the dice to maintain a potential
    outcome of the sum of two dice between 2 and 12. This skews the normal distribution
    of dice values to favor lower numbers because it does not consider that `E`
    could be zero and `S` could be 2 or higher. To show this skewing of values
    you can run the `distribution.bas` program which creates a histogram of the
    distribution of the bad dice throw code and proper dice throw code.
    
    Here are the results:
    
          DISTRIBUTION OF DICE ROLLS WITH  INT(7*RND(1))  VS  INT(6*RND(1)+1)
          THE INT(7*RND(1)) DISTRIBUTION:
          2             3             4             5             6             7             8             9             10            11            12
          6483          8662          10772         13232         15254         13007         10746         8878          6486          4357          2123
          THE INT(6*RND(1)+1) DISTRIBUTION
          2             3             4             5             6             7             8             9             10            11            12
          2788          5466          8363          11072         13947         16656         13884         11149         8324          5561          2790
    If the dice rolls are fair then we should see the largest occurrence be a 7 and
    the smallest should be 2 and 12. Furthermore the occurrences should be
    symetrical meaning there should be roughly the same amount of 2's as 12's, the
    same amount of 3's as 11's, 4's as 10's and so on until you reach the middle, 7.
    But notice in the skewed dice roll, 6 is the most rolled number not 7, and the
    rest of the numbers are not symetrical, there are many more 2's than 12's.
    So the lesson is test your code.
    
    The proper way to model a dice throw, in almost every language is
        `INT(6*RND(1)+1)` or `INT(6*RND(1))+1`
    
    SideNote: `X` was used already in the
    previous code block discussed but its value was never used. This is another
    poor coding practice: **Don't reuse variable names for different purposes.**
    
          50 IF X=7 THEN 180
          55 IF X=11 THEN 180
          60 IF X=1 THEN 40
          62 IF X=2 THEN 195
          65 IF X=0 THEN 40
          70 IF X=2 THEN 200
          80 IF X=3 THEN 200
          90 IF X=12 THEN 200
          125 IF X=5 THEN 220
          130 IF X =6 THEN 220
          140 IF X=8 THEN 220
          150 IF X=9 THEN 220
          160 IF X =10 THEN 220
          170 IF X=4 THEN 220
    
    This bit of code determines the routing of where to go for payout, or loss.
    Of course, line 60 and 65 are pointless as we've just shown and should be removed
    as long as the correct dice algorithm is also changed.
    
          62 IF X=2 THEN 195
          ....
          70 IF X=2 THEN 200
    The check for a 2 has already been made and the jump is done. Line 70 is
    therefore redundant and can be left out. The purpose of line 62 is only to
    print a special output, "SNAKE EYES!" which we'll see in the next block creates
    duplicate code.
    
    Lines 125-170 are also pointlessly checked because we know previous values have
    been ruled out, only these last values must remain, and they are all going to
    the same place, line 220. Line 125-170 could have simply been replaced with
    `GOTO 220`
    
    
    
          180 PRINT X "- NATURAL....A WINNER!!!!"
          185 PRINT X"PAYS EVEN MONEY, YOU WIN"F"DOLLARS"
          190 GOTO 210
          195 PRINT X"- SNAKE EYES....YOU LOSE."
          196 PRINT "YOU LOSE"F "DOLLARS."
          197 LET F=0-F
          198 GOTO 210
          200 PRINT X " - CRAPS...YOU LOSE."
          205 PRINT "YOU LOSE"F"DOLLARS."
          206 LET F=0-F
          210 LET R= R+F
          211 GOTO 320
    
    This bit of code manages instant wins or losses due to 7,11 or 2,3,12. As
    mentioned previously, lines 196 and 197 are essentially the same as lines
    205 and 206. A simpler code would be just to jump after printing the special
    message of "SNAKE EYES!" to line 205.
    
    Lines 197 and 206 just negate the wager by subtracting it from zero. Just saying
    `F = -F` would have sufficed. Line 210 updates your running total of winnings
    or losses with this bet.
    
          220 PRINT X "IS THE POINT. I WILL ROLL AGAIN"
          230 LET H=INT(7*RND(1))
          231 LET Q=INT(7*RND(1))
          232 LET O=H+Q
          240 IF O=1 THEN 230
          250 IF O=7 THEN 290
          255 IF O=0 THEN 230
    
    This code sets the point, the number you must re-roll to win without rolling
    a 7, the most probable number to roll. Except in this case again, it has the
    same incorrect dice rolling code and therefore 6 is the most probable number
    to roll. The concept of DRY (don't repeat yourself) is a coding practice which
    encourages non-duplication of code because if there is an error in the code, it
    can be fixed in one place and not multiple places like in this code. The scenario
    might be that a programmer sees some wrong code, fixes it, but neglects to
    consider that there might be duplicates of the same wrong code elsewhere.  If
    you practice DRY then you never worry much about behaviors in your code diverging
    due to duplicate code snippets.
    
          260 IF O=X THEN 310
          270 PRINT O " - NO POINT. I WILL ROLL AGAIN"
          280 GOTO 230
          290 PRINT O "- CRAPS. YOU LOSE."
          291 PRINT "YOU LOSE $"F
          292 F=0-F
          293 GOTO 210
          300 GOTO 320
          310 PRINT X"- A WINNER.........CONGRATS!!!!!!!!"
          311 PRINT X "AT 2 TO 1 ODDS PAYS YOU...LET ME SEE..."2*F"DOLLARS"
          312 LET F=2*F
          313 GOTO 210
    
    This is the code to keep rolling until the point is made or a seven is rolled.
    Again we see the negated `F` wager and lose message duplicated. This code could
    have been reorganized using a subroutine, or in BASIC, the GOSUB command, but
    in your language its most likely just known as a function or method. You can
    do a `grep -r 'GOSUB'` from the root directory to see other BASIC programs in
    this set that use GOSUB.
    
    The rest of the code if fairly straight forward, replay the game or end with
    a report of your winnings or losings.
    
    
    ================================================
    FILE: 29_Craps/craps.bas
    ================================================
    5 PRINT TAB(33);"CRAPS"
    10 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    12 PRINT:PRINT:PRINT
    15 LET R=0
    20 PRINT"2,3,12 ARE LOSERS; 4,5,6,8,9,10 ARE POINTS; 7,11 ARE NATURAL WINNERS."
    21 LET T=1
    22 PRINT "PICK A NUMBER AND INPUT TO ROLL DICE";
    23 INPUT Z
    24 LET X=(RND(0))
    25 LET T =T+1
    26 IF T<=Z THEN 24
    27 PRINT"INPUT THE AMOUNT OF YOUR WAGER.";
    28 INPUT F
    30 PRINT "I WILL NOW THROW THE DICE"
    40 LET E=INT(7*RND(1))
    41 LET S=INT(7*RND(1))
    42 LET X=E+S
    50 IF X=7 THEN 180
    55 IF X=11 THEN 180
    60 IF X=1 THEN 40
    62 IF X=2 THEN 195
    65 IF X=0 THEN 40
    70 IF X=2 THEN 200
    80 IF X=3 THEN 200
    90 IF X=12 THEN 200
    125 IF X=5 THEN 220
    130 IF X =6 THEN 220
    140 IF X=8 THEN 220
    150 IF X=9 THEN 220
    160 IF X =10 THEN 220
    170 IF X=4 THEN 220
    180 PRINT X "- NATURAL....A WINNER!!!!"
    185 PRINT X"PAYS EVEN MONEY, YOU WIN"F"DOLLARS"
    190 GOTO 210
    195 PRINT X"- SNAKE EYES....YOU LOSE."
    196 PRINT "YOU LOSE"F "DOLLARS."
    197 LET F=0-F
    198 GOTO 210
    200 PRINT X " - CRAPS...YOU LOSE."
    205 PRINT "YOU LOSE"F"DOLLARS."
    206 LET F=0-F
    210 LET R= R+F
    211 GOTO 320
    220 PRINT X "IS THE POINT. I WILL ROLL AGAIN"
    230 LET H=INT(7*RND(1))
    231 LET Q=INT(7*RND(1))
    232 LET O=H+Q
    240 IF O=1 THEN 230
    250 IF O=7 THEN 290
    255 IF O=0 THEN 230
    260 IF O=X THEN 310
    270 PRINT O " - NO POINT. I WILL ROLL AGAIN"
    280 GOTO 230
    290 PRINT O "- CRAPS. YOU LOSE."
    291 PRINT "YOU LOSE $"F
    292 F=0-F
    293 GOTO 210
    300 GOTO 320
    310 PRINT X"- A WINNER.........CONGRATS!!!!!!!!"
    311 PRINT X "AT 2 TO 1 ODDS PAYS YOU...LET ME SEE..."2*F"DOLLARS"
    312 LET F=2*F
    313 GOTO 210
    320 PRINT " IF YOU WANT TO PLAY AGAIN PRINT 5 IF NOT PRINT 2";
    330 INPUT M
    331 IF R<0 THEN 334
    332 IF R>0 THEN 336
    333 IF R=0 THEN 338
    334 PRINT "YOU ARE NOW UNDER $";-R
    335 GOTO 340
    336 PRINT "YOU ARE NOW AHEAD $";R
    337 GOTO 340
    338 PRINT "YOU ARE NOW EVEN AT 0"
    340 IF M=5 THEN 27
    341 IF R<0 THEN 350
    342 IF R>0 THEN 353
    343 IF R=0 THEN 355
    350 PRINT"TOO BAD, YOU ARE IN THE HOLE. COME AGAIN."
    351 GOTO 360
    353 PRINT"CONGRATULATIONS---YOU CAME OUT A WINNER. COME AGAIN!"
    354 GOTO 360
    355 PRINT"CONGRATULATIONS---YOU CAME OUT EVEN, NOT BAD FOR AN AMATEUR"
    360 END
    
    
    ================================================
    FILE: 29_Craps/csharp/.gitignore
    ================================================
    .vs
    TestResults
    bin
    obj
    
    
    ================================================
    FILE: 29_Craps/csharp/Craps/Craps.csproj
    ================================================
    
    
      
        Exe
        netcoreapp3.1
      
    
    
    
    
    ================================================
    FILE: 29_Craps/csharp/Craps/CrapsGame.cs
    ================================================
    namespace Craps
    {
        public enum Result
        {
            // It's not used in this program but it's often a good idea to include a "none"
            // value in an enum so that you can set an instance of the enum to "invalid" or
            // initialise it to "none of the valid values".
            noResult,
            naturalWin,
            snakeEyesLoss,
            naturalLoss,
            pointLoss,
            pointWin,
        };
    
        class CrapsGame
        {
            private readonly UserInterface ui;
            private Dice dice1 = new Dice();
            private Dice dice2 = new Dice();
    
            public CrapsGame(ref UserInterface ui)
            {
                this.ui = ui;
            }
    
            public Result Play(out int diceRoll)
            {
                diceRoll = dice1.Roll() + dice2.Roll();
    
                if (Win(diceRoll))
                {
                    return Result.naturalWin;
                }
                else if (Lose(diceRoll))
                {
                    return (diceRoll == 2) ? Result.snakeEyesLoss : Result.naturalLoss;
                }
                else
                {
                    var point = diceRoll;
                    ui.Point(point);
    
                    while (true)
                    {
                        var newRoll = dice1.Roll() + dice2.Roll();
                        if (newRoll == point)
                        {
                            diceRoll = newRoll;
                            return Result.pointWin;
                        }
                        else if (newRoll == 7)
                        {
                            diceRoll = newRoll;
                            return Result.pointLoss;
                        }
    
                        ui.NoPoint(newRoll);
                    }
                }
            }
    
            private bool Lose(int diceRoll)
            {
                return diceRoll == 2 || diceRoll == 3 || diceRoll == 12;
            }
    
            private bool Win(int diceRoll)
            {
                return diceRoll == 7 || diceRoll == 11;
            }
        }
    }
    
    
    ================================================
    FILE: 29_Craps/csharp/Craps/Dice.cs
    ================================================
    using System;
    
    
    namespace Craps
    {
        public class Dice
        {
            private Random rand = new Random();
            public readonly int sides;
    
            public Dice()
            {
                sides = 6;
            }
    
            public Dice(int sides)
            {
                this.sides = sides;
            }
    
            public int Roll() => rand.Next(1, sides + 1);
        }
    }
    
    
    ================================================
    FILE: 29_Craps/csharp/Craps/Program.cs
    ================================================
    using System.Diagnostics;
    
    
    
    namespace Craps
    {
        class Program
        {
            static void Main(string[] args)
            {
                var ui = new UserInterface();
                var game = new CrapsGame(ref ui);
                int winnings = 0;
    
                ui.Intro();
    
                do
                {
    	            var bet = ui.PlaceBet();
                    var result = game.Play(out int diceRoll);
    
                    switch (result)
                    {
                        case Result.naturalWin:
                            winnings += bet;
                            break;
    
                        case Result.naturalLoss:
                        case Result.snakeEyesLoss:
                        case Result.pointLoss:
                            winnings -= bet;
                            break;
    
                        case Result.pointWin:
                            winnings += (2 * bet);
                            break;
    
                        // Include a default so that we will be warned if the values of the enum
                        // ever change and we forget to add code to handle the new value.
                        default:
                            Debug.Assert(false); // We should never get here.
                            break;
                    }
    
                    ui.ShowResult(result, diceRoll, bet);
                } while (ui.PlayAgain(winnings));
    
                ui.GoodBye(winnings);
            }
        }
    }
    
    
    ================================================
    FILE: 29_Craps/csharp/Craps/UserInterface.cs
    ================================================
    using System;
    using System.Diagnostics;
    
    
    
    namespace Craps
    {
        public class UserInterface
    	{
            public void Intro()
            {
                Console.WriteLine("                                 CRAPS");
                Console.WriteLine("               CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n\n\n");
                Console.WriteLine("2,3,12 ARE LOSERS; 4,5,6,8,9,10 ARE POINTS; 7,11 ARE NATURAL WINNERS.");
    
                // In the original game a random number would be generated and then thrown away for as many
                // times as the number the user entered. This is presumably something to do with ensuring
                // that different random numbers will be generated each time the program is run.
                //
                // This is not necessary in C#; the random number generator uses the current time as a seed
                // so the results will always be different every time it is run.
                //
                // So that the game exactly matches the original game we ask the question but then ignore
                // the answer.
                Console.Write("PICK A NUMBER AND INPUT TO ROLL DICE ");
                GetInt();
            }
    
            public int PlaceBet()
            {
                Console.Write("INPUT THE AMOUNT OF YOUR WAGER. ");
                int n = GetInt();
                Console.WriteLine("I WILL NOW THROW THE DICE");
    
                return n;
            }
    
            public bool PlayAgain(int winnings)
            {
                // Goodness knows why we have to enter 5 to play
                // again but that's what the original game asked.
                Console.Write("IF YOU WANT TO PLAY AGAIN PRINT 5 IF NOT PRINT 2 ");
    
                bool playAgain = (GetInt() == 5);
    
                if (winnings < 0)
                {
                    Console.WriteLine($"YOU ARE NOW UNDER ${-winnings}");
                }
                else if (winnings > 0)
                {
                    Console.WriteLine($"YOU ARE NOW OVER ${winnings}");
                }
                else
                {
                    Console.WriteLine($"YOU ARE NOW EVEN AT ${winnings}");
                }
    
                return playAgain;
            }
    
            public void GoodBye(int winnings)
            {
                if (winnings < 0)
                {
                    Console.WriteLine("TOO BAD, YOU ARE IN THE HOLE. COME AGAIN.");
                }
                else if (winnings > 0)
                {
                    Console.WriteLine("CONGRATULATIONS---YOU CAME OUT A WINNER. COME AGAIN!");
                }
                else
                {
                    Console.WriteLine("CONGRATULATIONS---YOU CAME OUT EVEN, NOT BAD FOR AN AMATEUR");
                }
            }
    
            public void NoPoint(int diceRoll)
            {
                Console.WriteLine($"{diceRoll} - NO POINT. I WILL ROLL AGAIN ");
            }
    
            public void Point(int point)
            {
                Console.WriteLine($"{point} IS THE POINT. I WILL ROLL AGAIN");
            }
    
            public void ShowResult(Result result, int diceRoll, int bet)
            {
                switch (result)
                {
                    case Result.naturalWin:
                        Console.WriteLine($"{diceRoll} - NATURAL....A WINNER!!!!");
                        Console.WriteLine($"{diceRoll} PAYS EVEN MONEY, YOU WIN {bet} DOLLARS");
                        break;
    
                    case Result.naturalLoss:
                        Console.WriteLine($"{diceRoll} - CRAPS...YOU LOSE.");
                        Console.WriteLine($"YOU LOSE {bet} DOLLARS.");
                        break;
    
                    case Result.snakeEyesLoss:
                        Console.WriteLine($"{diceRoll} - SNAKE EYES....YOU LOSE.");
                        Console.WriteLine($"YOU LOSE {bet} DOLLARS.");
                        break;
    
                    case Result.pointLoss:
                        Console.WriteLine($"{diceRoll} - CRAPS. YOU LOSE.");
                        Console.WriteLine($"YOU LOSE ${bet}");
                        break;
    
                    case Result.pointWin:
                        Console.WriteLine($"{diceRoll} - A WINNER.........CONGRATS!!!!!!!!");
                        Console.WriteLine($"AT 2 TO 1 ODDS PAYS YOU...LET ME SEE... {2 * bet} DOLLARS");
                        break;
    
                    // Include a default so that we will be warned if the values of the enum
                    // ever change and we forget to add code to handle the new value.
                    default:
                        Debug.Assert(false); // We should never get here.
                        break;
                }
            }
    
            private int GetInt()
            {
                while (true)
                {
    	            string input = Console.ReadLine();
                    if (int.TryParse(input, out int n))
                    {
                        return n;
                    }
                    else
                    {
                        Console.Write("ENTER AN INTEGER ");
                    }
                }
            }
        }
    }
    
    
    ================================================
    FILE: 29_Craps/csharp/Craps.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.31112.23
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Craps", "Craps\Craps.csproj", "{783A49D7-DADE-477A-9973-D9457258573B}"
    EndProject
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CrapsTester", "CrapsTester\CrapsTester.csproj", "{44DFE8DB-715F-428E-992D-A97C34D47B98}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{783A49D7-DADE-477A-9973-D9457258573B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{783A49D7-DADE-477A-9973-D9457258573B}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{783A49D7-DADE-477A-9973-D9457258573B}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{783A49D7-DADE-477A-9973-D9457258573B}.Release|Any CPU.Build.0 = Release|Any CPU
    		{44DFE8DB-715F-428E-992D-A97C34D47B98}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{44DFE8DB-715F-428E-992D-A97C34D47B98}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{44DFE8DB-715F-428E-992D-A97C34D47B98}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{44DFE8DB-715F-428E-992D-A97C34D47B98}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {871679FF-B86C-468B-960E-DFC625CDB5D0}
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 29_Craps/csharp/CrapsTester/CrapsTester.csproj
    ================================================
    
    
      
        netcoreapp3.1
    
        false
      
    
      
        
        
        
        
      
    
      
        
      
    
    
    
    
    ================================================
    FILE: 29_Craps/csharp/CrapsTester/CrapsTests.cs
    ================================================
    using Craps;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    
    
    
    namespace CrapsTester
    {
        [TestClass]
        public class DiceTests
        {
            [TestMethod]
            public void SixSidedDiceReturnsValidRolls()
            {
                var dice = new Dice();
                for (int i = 0; i < 100000; i++)
                {
                    var roll = dice.Roll();
                    Assert.IsTrue(roll >= 1 && roll <= dice.sides);
                }
            }
    
            [TestMethod]
            public void TwentySidedDiceReturnsValidRolls()
            {
                var dice = new Dice(20);
                for (int i = 0; i < 100000; i++)
                {
                    var roll = dice.Roll();
                    Assert.IsTrue(roll >= 1 && roll <= dice.sides);
                }
            }
    
            [TestMethod]
            public void DiceRollsAreRandom()
            {
                // Roll 600,000 dice and count how many rolls there are for each side.
    
                var dice = new Dice();
    
                int numOnes = 0;
                int numTwos = 0;
                int numThrees = 0;
                int numFours = 0;
                int numFives = 0;
                int numSixes = 0;
                int numErrors = 0;
    
                for (int i = 0; i < 600000; i++)
                {
                    switch (dice.Roll())
                    {
                        case 1:
                            numOnes++;
                            break;
    
                        case 2:
                            numTwos++;
                            break;
    
                        case 3:
                            numThrees++;
                            break;
    
                        case 4:
                            numFours++;
                            break;
    
                        case 5:
                            numFives++;
                            break;
    
                        case 6:
                            numSixes++;
                            break;
    
                        default:
                            numErrors++;
                            break;
                    }
                }
    
                // We'll assume that a variation of 10% in rolls for the different numbers is random enough.
                // Perfectly random rolling would produce 100000 rolls per side, +/- 5% of this gives the
                // range 90000..110000.
                const int minRolls = 95000;
                const int maxRolls = 105000;
                Assert.IsTrue(numOnes >= minRolls && numOnes <= maxRolls);
                Assert.IsTrue(numTwos >= minRolls && numTwos <= maxRolls);
                Assert.IsTrue(numThrees >= minRolls && numThrees <= maxRolls);
                Assert.IsTrue(numFours >= minRolls && numFours <= maxRolls);
                Assert.IsTrue(numFives >= minRolls && numFives <= maxRolls);
                Assert.IsTrue(numSixes >= minRolls && numSixes <= maxRolls);
                Assert.AreEqual(numErrors, 0);
            }
        }
    }
    
    
    ================================================
    FILE: 29_Craps/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 29_Craps/distributions.bas
    ================================================
    10 PRINT "DISTRIBUTION OF DICE ROLLS WITH  INT(7*RND(1))  VS  INT(6*RND(1)+1)"
    20 DIM A(12)
    30 DIM B(12)
    100 FOR X = 1 TO 100000 : REM CHOOSE A LARGE NUMBER TO GET A FINER GRAINED HISTOGRAM
    140 REM GET A NUMBER FROM 0 TO 6 INCLUSIVE WITH THE INTENT TO THROW AWAY ZEROES.
    150 LET D1 = INT(7*RND(1))
    155 LET D2 = INT(7*RND(1))
    160 LET S1 = D1+D2
    165 REM IF THIS SUM IS LESS THAN TWO THEN TRY AGAIN.
    170 IF S1<2 THEN 150
    199 REM GET A NUMBER FROM 0 TO 5 THEN ADD 1 TO IT TO MAKE IT 1 TO 6
    200 LET D3 = INT(6*RND(1))+1
    210 LET D4 = INT(6*RND(1))+1
    220 LET S2 = D3+D4
    245 REM USE OUR ARRAY AS A HISTOGRAM, COUNTING EACH OCCURRENCE OF DICE ROLL
    250 A(S1) = A(S1) + 1
    260 B(S2) = B(S2) + 1
    290 NEXT X
    300 PRINT "THE INT(7*RND(1)) DISTRIBUTION:"
    310 FOR I = 2 TO 12 :PRINT I,:NEXT:PRINT
    320 FOR I = 2 TO 12 :PRINT A(I),:NEXT:PRINT
    325 PRINT "THE INT(6*RND(1)+1) DISTRIBUTION"
    330 FOR I = 2 TO 12 :PRINT I,:NEXT:PRINT
    340 FOR I = 2 TO 12 :PRINT B(I),:NEXT:PRINT
    
    
    ================================================
    FILE: 29_Craps/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 29_Craps/java/src/Craps.java
    ================================================
    import java.util.Random;
    import java.util.Scanner;
    
    /**
     *  Port of Craps from BASIC to Java 17.
     */
    public class Craps {
      public static final Random random = new Random();
    
      public static void main(String[] args) {
        System.out.println("""
                                                                CRAPS
                                              CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY
    
    
                               2,3,12 ARE LOSERS; 4,5,6,8,9,10 ARE POINTS; 7,11 ARE NATURAL WINNERS.
                               """);
        double winnings = 0.0;
        do {
          winnings = playCraps(winnings);
        } while (stillInterested(winnings));
        winningsReport(winnings);
      }
    
      public static double playCraps(double winnings) {
        double wager = getWager();
        System.out.println("I WILL NOW THROW THE DICE");
        int roll = rollDice();
        double payout = switch (roll) {
          case 7, 11 -> naturalWin(roll, wager);
          case 2, 3, 12 -> lose(roll, wager);
          default -> setPoint(roll, wager);
        };
        return winnings + payout;
      }
    
      public static int rollDice() {
        return random.nextInt(1, 7) + random.nextInt(1, 7);
      }
    
      private static double setPoint(int point, double wager) {
        System.out.printf("%1$ d IS THE POINT. I WILL ROLL AGAIN%n",point);
        return makePoint(point, wager);
      }
    
      private static double makePoint(int point, double wager) {
        int roll = rollDice();
        if (roll == 7)
          return lose(roll, wager);
        if (roll == point)
          return win(roll, wager);
        System.out.printf("%1$ d - NO POINT. I WILL ROLL AGAIN%n", roll);
        return makePoint(point, wager);  // recursive
      }
    
      private static double win(int roll, double wager) {
        double payout = 2 * wager;
        System.out.printf("%1$ d - A WINNER.........CONGRATS!!!!!!!!%n", roll);
        System.out.printf("%1$ d AT 2 TO 1 ODDS PAYS YOU...LET ME SEE...$%2$3.2f%n",
                          roll, payout);
        return payout;
      }
    
      private static double lose(int roll, double wager) {
        String msg = roll == 2 ? "SNAKE EYES.":"CRAPS";
        System.out.printf("%1$ d - %2$s...YOU LOSE.%n", roll, msg);
        System.out.printf("YOU LOSE $%3.2f%n", wager);
        return -wager;
      }
    
      public static double naturalWin(int roll, double wager) {
        System.out.printf("%1$ d - NATURAL....A WINNER!!!!%n", roll);
        System.out.printf("%1$ d PAYS EVEN MONEY, YOU WIN $%2$3.2f%n", roll, wager);
        return wager;
      }
    
      public static void winningsUpdate(double winnings) {
        System.out.println(switch ((int) Math.signum(winnings)) {
          case 1 -> "YOU ARE NOW AHEAD $%3.2f".formatted(winnings);
          case 0 -> "YOU ARE NOW EVEN AT 0";
          default -> "YOU ARE NOW UNDER $%3.2f".formatted(-winnings);
        });
      }
    
      public static void winningsReport(double winnings) {
        System.out.println(
            switch ((int) Math.signum(winnings)) {
              case 1 -> "CONGRATULATIONS---YOU CAME OUT A WINNER. COME AGAIN!";
              case 0 -> "CONGRATULATIONS---YOU CAME OUT EVEN, NOT BAD FOR AN AMATEUR";
              default -> "TOO BAD, YOU ARE IN THE HOLE. COME AGAIN.";
            }
        );
      }
    
      public static boolean stillInterested(double winnings) {
        System.out.print(" IF YOU WANT TO PLAY AGAIN PRINT 5 IF NOT PRINT 2 ");
        int fiveOrTwo = (int)getInput();
        winningsUpdate(winnings);
        return fiveOrTwo == 5;
      }
    
      public static double getWager() {
        System.out.print("INPUT THE AMOUNT OF YOUR WAGER. ");
        return getInput();
      }
    
      public static double getInput() {
        Scanner scanner = new Scanner(System.in);
        System.out.print("> ");
        while (true) {
          try {
            return scanner.nextDouble();
          } catch (Exception ex) {
            try {
              scanner.nextLine(); // flush whatever this non number stuff is.
            } catch (Exception ns_ex) { // received EOF (ctrl-d or ctrl-z if windows)
              System.out.println("END OF INPUT, STOPPING PROGRAM.");
              System.exit(1);
            }
          }
          System.out.println("!NUMBER EXPECTED - RETRY INPUT LINE");
          System.out.print("> ");
        }
      }
    }
    
    
    ================================================
    FILE: 29_Craps/javascript/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells)
    
    
    ================================================
    FILE: 29_Craps/javascript/craps.html
    ================================================
    
    
    CRAPS
    
    
    
    
    
    
    
    
    
    
    ================================================
    FILE: 29_Craps/javascript/craps.js
    ================================================
    // CRAPS
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    function roll()
    {
        return Math.floor(6 * Math.random())+1 + Math.floor(6 * Math.random())+1;
    }
    
    // Main program
    async function main()
    {
        print(tab(33) + "CRAPS\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        r = 0;
        print("2,3,12 ARE LOSERS: 4,5,6,8,9,10 ARE POINTS: 7,11 ARE NATURAL WINNERS.\n");
        while (1) {
            print("INPUT THE AMOUNT OF YOUR WAGER.");
            f = parseInt(await input());
            print("I WILL NOW THROW THE DICE\n");
            x = roll();
            if (x == 7 || x == 11) {
                print(x + " - NATURAL....A WINNER!!!!\n");
                print(x + " PAYS EVEN MONEY, YOU WIN " + f + " DOLLARS\n");
                r += f;
            } else if (x == 2) {
                print(x + " - SNAKE EYES....YOU LOSE.\n");
                print("YOU LOSE " + f + " DOLLARS.\n");
                r -= f;
            } else if (x == 3 || x == 12) { // Original duplicates comparison in line 70
                print(x + " - CRAPS....YOU LOSE.\n");
                print("YOU LOSE " + f + " DOLLARS.\n");
                r -= f;
            } else {
                print(x + " IS THE POINT. I WILL ROLL AGAIN\n");
                while (1) {
                    o = roll();
                    if (o == 7) {
                        print(o + " - CRAPS, YOU LOSE.\n");
                        print("YOU LOSE $" + f + "\n");
                        r -= f;
                        break;
                    }
                    if (o == x) {
                        print(x + " - A WINNER.........CONGRATS!!!!!!!!\n");
                        print(x + " AT 2 TO 1 ODDS PAYS YOU...LET ME SEE..." + 2 * f + " DOLLARS\n");
                        r += f * 2;
                        break;
                    }
                    print(o + " - NO POINT. I WILL ROLL AGAIN\n");
                }
            }
            print(" IF YOU WANT TO PLAY AGAIN PRINT 5 IF NOT PRINT 2");
            m = parseInt(await input());
            if (r < 0) {
                print("YOU ARE NOW UNDER $" + -r + "\n");
            } else if (r > 0) {
                print("YOU ARE NOW AHEAD $" + r + "\n");
            } else {
                print("YOU ARE NOW EVEN AT 0\n");
            }
            if (m != 5)
                break;
        }
        if (r < 0) {
            print("TOO BAD, YOU ARE IN THE HOLE. COME AGAIN.\n");
        } else if (r > 0) {
            print("CONGRATULATIONS---YOU CAME OUT A WINNER. COME AGAIN!\n");
        } else {
            print("CONGRATULATIONS---YOU CAME OUT EVEN, NOT BAD FOR AN AMATEUR\n");
        }
    
    }
    
    main();
    
    
    ================================================
    FILE: 29_Craps/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 29_Craps/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/) by Alex Conconi
    
    ---
    
    ### Lua porting notes
    
    -  The `craps_main` function contains the main game loop, which iteratively
    plays craps rounds by calling `play_round` and tracks winnings and losings.
    - Replaced the original routine that tries to scramble the random number
    generator with a proper seed initializer in Lua: `math.randomseed(os.time())`
    (as advised in the general porting notes). 
    - Added basic input validation to accept only positive integers for the
    wager and the answer to the "If you want to play again print 5" question.
    - "If you want to play again print 5 if not print 2" reads a bit odd but
    we decided to leave it as is and stay true to the BASIC original version.
    
    ================================================
    FILE: 29_Craps/lua/craps.lua
    ================================================
    --[[
    Craps
    
    From: BASIC Computer Games (1978)
    Edited by David H. Ahl
    
        This game simulates the games of craps played according to standard
        Nevada craps table rules. That is:
        1. A 7 or 11 on the first roll wins
        2. A 2, 3, or 12 on the first roll loses
        3. Any other number rolled becomes your "point." You continue to roll;
        if you get your point you win. If you roll a 7, you lose and the dice
        change hands when this happens.
    
        This version of craps was modified by Steve North of Creative Computing.
        It is based on an original which appeared one day one a computer at DEC.
    
    
    Lua port by Alex Conconi, 2022
    --]]
    
    
    --- Throw two dice and return their sum.
    local function throw_dice()
        return math.random(1, 6) + math.random(1, 6)
    end
    
    
    --- Print prompt and read a number > 0 from stdin.
    local function input_number(prompt)
        while true do
            io.write(prompt)
            local number = tonumber(io.stdin:read("*l"))
            if number and number > 0 then
                return number
            else
                print("Please enter a number greater than zero.")
            end
        end
    end
    
    
    --- Print custom balance message depending on balance value
    local function print_balance(balance, under_msg, ahead_msg, even_msg)
        if balance < 0 then
            print(under_msg)
        elseif balance > 0 then
            print(ahead_msg)
        else
            print(even_msg)
        end
    end
    
    
    --- Play a round and return winnings or losings.
    local function play_round()
        -- Input the wager
        local wager = input_number("Input the amount of your wager: ")
    
        -- Roll the die for the first time.
        print("I will now throw the dice")
        local first_roll = throw_dice()
    
        -- A 7 or 11 on the first roll wins.
        if first_roll == 7 or first_roll == 11 then
            print(string.format("%d - natural.... a winner!!!!", first_roll))
            print(string.format("%d pays even money, you win %d dollars", first_roll, wager))
            return wager
        end
    
        -- A 2, 3, or 12 on the first roll loses.
        if first_roll == 2 or first_roll == 3 or first_roll == 12 then
            if first_roll == 2 then
                -- Special 'you lose' message for 'snake eyes'
                print(string.format("%d - snake eyes.... you lose.", first_roll))
            else
                -- Default 'you lose' message
                print(string.format("%d - craps.... you lose.", first_roll))
            end
            print(string.format("You lose %d dollars", wager))
            return -wager
        end
    
        -- Any other number rolled becomes the "point".
        -- Continue to roll until rolling a 7 or point.
        print(string.format("%d is the point. I will roll again", first_roll))
        while true do
            local second_roll = throw_dice()
            if second_roll == first_roll then
                -- Player gets point and wins
                print(string.format("%d - a winner.........congrats!!!!!!!!", first_roll))
                print(string.format("%d at 2 to 1 odds pays you...let me see... %d dollars", first_roll, 2 * wager))
                return 2 * wager
            end
            if second_roll == 7 then
                -- Player rolls a 7 and loses
                print(string.format("%d - craps. You lose.", second_roll))
                print(string.format("You lose $ %d", wager))
                return -wager
            end
            --  Continue to roll
            print(string.format("%d  - no point. I will roll again", second_roll))
        end
    end
    
    
    --- Main game function.
    local function craps_main()
        -- Print the introduction to the game
        print(string.rep(" ", 32) .. "Craps")
        print(string.rep(" ", 14) .. "Creative Computing  Morristown, New Jersey\n\n")
        print("2,3,12 are losers; 4,5,6,8,9,10 are points; 7,11 are natural winners.")
    
        -- Initialize random number generator seed
        math.randomseed(os.time())
    
        -- Initialize balance to track winnings and losings
        local balance = 0
    
        -- Main game loop
        local keep_playing = true
        while keep_playing do
            -- Play a round
            balance = balance + play_round()
    
            -- If player's answer is 5, then stop playing
            keep_playing = (input_number("If you want to play again print 5 if not print 2: ") == 5)
    
            -- Print an update on winnings or losings
            print_balance(
                balance,
                string.format("You are now under $%d", -balance),
                string.format("You are now ahead $%d", balance),
                "You are now even at 0"
            )
        end
    
        -- Game over, print the goodbye message
        print_balance(
            balance,
            "Too bad, you are in the hole. Come again.",
            "Congratulations---you came out a winner. Come again.",
            "Congratulations---you came out even, not bad for an amateur"
        )
    end
    
    
    --- Run the game.
    craps_main()
    
    
    ================================================
    FILE: 29_Craps/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 29_Craps/perl/craps.pl
    ================================================
    #!/usr/bin/perl
    #
    
    my $bank = 0;
    
    &main;
    
    sub main {
    	&print_intro;
    	my $continue=5;
    	until ($continue != 5) {
    		$continue=&game_play;
    		&print_bank;
    	}
    	&final_bank;
    }
    
    sub game_play {
    	my $point = 0;
    	my $continue = 1;
    	print "INPUT THE AMOUNT OF YOUR WAGER.\n";
    	chomp(my $wager=);
    	print "I WILL NOW THROW THE DICE\n";
    	until ($continue == 0) {
    		my $roll = &roll_dice;
    		$continue = &check_value($roll,$wager);
    	}
    	print "IF YOU WANT TO PLAY AGAIN PRINT 5 IF NOT PRINT 2\n";
    	chomp(my $ans=);
    	return $ans;
    }
    
    sub print_bank {
    	if ($bank < 0) {
    		print "YOU ARE NOW UNDER \$$bank\n";
    	}
    	elsif ($bank > 0) {
    		print "YOU ARE NOW AHEAD \$$bank\n";
    	}
    	else {
    		print "YOU ARE EVEN AT 0\n";
    	}
    }
    
    sub final_bank {
    	if ($bank < 0) {
    		print "TOO BAD, YOU ARE IN THE HOLE. COME AGAIN\n";
    	}
    	elsif ($bank > 0) {
    		print "CONGRATULATIONS---YOU CAME OUT A WINNER. COME AGAIN!\n";
    	}
    	else {
    		print "CONGRATULATIONS---YOU CAME OUT EVEN. NOT BAD FOR AN AMATEUR!\n";
    	}
    }
    
    sub check_value {
    	my $roll = shift;
    	my $wager = shift;
    	if ($roll == 7 || $roll == 11) {
    		print "$roll - NATURAL....A WINNER!!!!\n";
    		print "$roll PAYS EVEN MONEY, YOU WIN $wager DOLLARS\n";
    		$bank += $wager;
    		return 0;
    	}
    	elsif ($roll == 2 || $roll == 3 || $roll == 12) {
    		if ($roll == 2) {
    			print "$roll - SNAKE EYES....YOU LOSE.\n";
    			print "YOU LOSE $wager DOLLARS.\n";
    		}
    		else {
    			print "$roll - CRAPS...YOU LOSE.\n";
    			print "YOU LOSE $wager DOLLARS.\n";
    		}
    		$bank -= $wager;
    		return 0;
    	}
    	else {
    		my $point = $roll;
    		print "$point IS THE POINT. I WILL ROLL AGAIN\n";
    		until (1==2) {
    			$roll = &roll_dice;
    			if ($roll == 7) {
    				print "$roll YOU LOSE $wager\n";
    				$bank -= $wager;
    				return 0;
    			}
    			elsif ($roll == $point) {
    				print "$roll - A WINNER..........CONGRATS!!!!!!!!\n";
    				my $payout = $wager * 2;
    				print "$roll AT 2 TO 1 ODDS PAYS YOU...LET ME SEE...$payout DOLLARS\n";
    				$bank += $payout;
    				return 0;
    			}
    			else {
    				print "$roll - NO POINT. I WILL ROLL AGAIN\n";
    				sleep(1);
    			}
    		}
    	}
    }
    
    sub roll_dice {
    	my $die1 = 1+int rand(6);
    	my $die2 = 1+int rand(6);
    	return ($die1 + $die2);
    }
    
    sub print_intro {
    	print ' ' x 33 . "CRAPS\n";
    	print ' ' x 15 . "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n\n\n";
    	print "2,3,12 ARE LOSERS; 4,5,6,8,9,10 ARE POINTS; 7,11 ARE NATURAL WINNERS.\n";
    }
    
    
    ================================================
    FILE: 29_Craps/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 29_Craps/python/craps.py
    ================================================
    #!/usr/bin/env python3
    """This game simulates the games of craps played according to standard Nevada craps table rules.
    
    That is:
    
    1. A 7 or 11 on the first roll wins
    2. A 2, 3, or 12 on the first roll loses
    3. Any other number rolled becomes your "point." You continue to roll; if you get your point you win. If you
       roll a 7, you lose and the dice change hands when this happens.
    
    This version of craps was modified by Steve North of Creative Computing. It is based on an original which
    appeared one day one a computer at DEC.
    """
    from random import randint
    
    
    def throw_dice() -> int:
        return randint(1, 6) + randint(1, 6)
    
    
    def main() -> None:
        print(" " * 33 + "Craps")
        print(" " * 15 + "Creative Computing  Morristown, New Jersey\n\n\n")
    
        winnings = 0
        print("2,3,12 are losers; 4,5,6,8,9,10 are points; 7,11 are natural winners.")
    
        play_again = True
        while play_again:
            wager = int(input("Input the amount of your wager: "))
    
            print("I will now throw the dice")
            roll_1 = throw_dice()
    
            if roll_1 in [7, 11]:
                print(f"{roll_1} - natural.... a winner!!!!")
                print(f"{roll_1} pays even money, you win {wager} dollars")
                winnings += wager
            elif roll_1 == 2:
                print(f"{roll_1} - snake eyes.... you lose.")
                print(f"You lose {wager} dollars")
                winnings -= wager
            elif roll_1 in [3, 12]:
                print(f"{roll_1} - craps.... you lose.")
                print(f"You lose {wager} dollars")
                winnings -= wager
            else:
                print(f"{roll_1} is the point. I will roll again")
                roll_2 = 0
                while roll_2 not in [roll_1, 7]:
                    roll_2 = throw_dice()
                    if roll_2 == 7:
                        print(f"{roll_2} - craps. You lose.")
                        print(f"You lose $ {wager}")
                        winnings -= wager
                    elif roll_2 == roll_1:
                        print(f"{roll_1} - a winner.........congrats!!!!!!!!")
                        print(
                            f"{roll_1} at 2 to 1 odds pays you...let me see... {2 * wager} dollars"
                        )
                        winnings += 2 * wager
                    else:
                        print(f"{roll_2} - no point. I will roll again")
    
            m = input("  If you want to play again print 5 if not print 2: ")
            if winnings < 0:
                print(f"You are now under ${-winnings}")
            elif winnings > 0:
                print(f"You are now ahead ${winnings}")
            else:
                print("You are now even at 0")
            play_again = m == "5"
    
        if winnings < 0:
            print("Too bad, you are in the hole. Come again.")
        elif winnings > 0:
            print("Congratulations---you came out a winner. Come again.")
        else:
            print("Congratulations---you came out even, not bad for an amateur")
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 29_Craps/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 29_Craps/ruby/craps.rb
    ================================================
    class CRAPSGAME
    
        # class variables start with a double "@"
        @@standings = 0
    
        def displayHeading
            puts "CRAPS".center(80)
            puts "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY".center(80)
            puts "\n\n\n"
            puts "2,3,12 are losers"
            puts "4,5,6,8,9,10 are points"
            puts "7,11 are natural winners.\n\n"
        end
    
        def displayStanding
            if @@standings < 0
                print "you are in the hole by "
            elsif @@standings == 0
                print "you currently have "
            else
                # show how much money we currently have
                print "you now have won "
            end
               # print the absolute value of the amount in the standings
            puts @@standings.abs.to_s + " dollars"
        end
    
        # dice can come up 2 through 12
        # so return a minimum of 2 and add 0 through 10 to that
        def rollDice
            puts "I will now throw the dice"
            return rand(5) + rand(5) + 2
        end
    
        def placeBet
            print "How much do you want to wager? "
            wager = gets.strip.to_i
            return wager
        end
    
        def loseBetBy amount
            @@standings -= amount
        end
    
        def winBetBy amount
            @@standings += amount
        end
    
        def askQuit?
            print "\nDo you want to play again? "
            # just the first character, make it uppercase
            again = gets.strip.upcase[0]
            return again != "Y"
        end
    
        def pointRoll point, wager
            while true do
                puts " is the point."
                puts " I will roll again when you press Enter."
                waitForIt = gets
                roll = rollDice
                print roll.to_s
    
                # the only critical rolls here are 7 and the previous roll
                # if anything else comes up we roll again.
                case roll.to_i
                    when 7
                        puts " craps - you lose"
                        loseBetBy wager
                        break
                    when point
                        puts " is a winner! congrats!"
                        puts "at 2 to 1 odds pays you " + (2 * wager).to_s + " dollars"
                        winBetBy 2 * wager
                        break
                    else
                        print " no point - " + point.to_s
               end
            end
        end
    
        def play
            displayHeading
    
            while true do
                wagerAmount = placeBet
                roll = rollDice
                print roll.to_s
                case roll
                    when 2
                        puts " snake eyes - you lose"
                        loseBetBy wagerAmount
                    when 3, 12
                        puts " craps - you lose"
                        loseBetBy wagerAmount
                    when 4, 5, 6, 8, 9, 10
                        pointRoll roll, wagerAmount
                    when 7, 11
                        puts " a natural - a winner"
                        puts "pays even money: " + wagerAmount.to_s + " dollars"
                        winBetBy wagerAmount
                end
                displayStanding
                if askQuit?
                    endPlay
                end
            end
        end
    
        def endPlay
            case
                when @@standings < 0
                    puts "Too bad. You are in the hole " + @@standings.abs.to_s + " dollars. Come again."
                when @@standings > 0
                    puts "Congratulations --- You came out a winner of " + @@standings.to_s + " dollars. Come again!"
                when @@standings == 0
                    puts "Congratulations --- You came out even, not bad for an amateur"
            end
            exit
        end
    end
    
    craps = CRAPSGAME.new
    craps.play
    
    
    ================================================
    FILE: 29_Craps/rust/Cargo.toml
    ================================================
    [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"
    
    
    ================================================
    FILE: 29_Craps/rust/src/craps_game.rs
    ================================================
    use crate::util;
    
    enum GameState {
        ComeOut,
        PointRolls,
        GameOver,
    }
    
    pub struct CrapsGame {
        wallet: usize,
        bet: usize,
        point: u8,
        state: GameState,
    }
    
    impl CrapsGame {
        pub fn new() -> Self {
            let wallet = util::read_numeric("\nHow much money do you want to start with?");
    
            CrapsGame {
                wallet,
                bet: 0,
                point: 0,
                state: GameState::ComeOut,
            }
        }
    
        pub fn tick(&mut self) -> bool {
            use GameState::*;
    
            match self.state {
                ComeOut => self.new_round(),
                PointRolls => self.point_roll(),
                GameOver => false,
            }
        }
    
        fn point_roll(&mut self) -> bool {
            let point = self.point;
    
            println!("Rolling {point} wins. 7 loses.");
    
            self.prompt_roll();
            let roll = CrapsGame::roll();
    
            if roll == point {
                self.player_win();
            } else if roll == 7 {
                self.player_lose();
            }
    
            true
        }
    
        fn new_round(&mut self) -> bool {
            println!("\nCome out roll.");
            println!("7 and 11 win. 2, 3 and 12 lose.\n");
    
            loop {
                self.bet = util::read_numeric("Enter your bet:");
    
                if self.bet <= self.wallet {
                    break;
                } else {
                    println!("You don't have that much money!");
                }
            }
    
            self.prompt_roll();
            let point = CrapsGame::roll();
    
            match point {
                11 | 7 => {
                    self.player_win();
                }
                2 | 3 | 12 => {
                    self.player_lose();
                }
                _ => {
                    self.point = point;
                    self.state = GameState::PointRolls
                }
            }
    
            true
        }
    
        fn player_win(&mut self) {
            let bet = self.bet;
    
            println!("You won ${bet}!");
    
            self.wallet += bet;
            self.print_wallet();
    
            self.state = GameState::ComeOut;
        }
    
        fn player_lose(&mut self) {
            let bet = self.bet;
    
            println!("You lost ${bet}!");
    
            self.wallet -= bet;
            self.print_wallet();
    
            if self.wallet == 0 {
                self.game_over();
            } else {
                self.state = GameState::ComeOut;
            }
        }
    
        fn print_wallet(&self) {
            println!("\nYou have ${} in your wallet.", self.wallet);
        }
    
        fn roll() -> u8 {
            use rand::Rng;
    
            let roll = rand::thread_rng().gen_range(2..13);
            println!("\nYou rolled {}.", roll);
    
            roll
        }
    
        fn game_over(&mut self) {
            self.state = GameState::GameOver;
        }
    
        fn prompt_roll(&mut self) {
            use util::Response::*;
    
            let response = util::prompt("Ready to roll?");
    
            match response {
                Yes => (),
                No => self.game_over(),
            }
        }
    }
    
    
    ================================================
    FILE: 29_Craps/rust/src/main.rs
    ================================================
    mod craps_game;
    mod util;
    use crate::craps_game::CrapsGame;
    
    fn main() {
        println!("~~Craps~~");
        println!("Creative Computing Morristown, New Jersey\n");
    
        let mut quit = false;
    
        while !quit {
            let mut game = CrapsGame::new();
    
            loop {
                if !game.tick() {
                    use util::Response::*;
    
                    match util::prompt("New Game?") {
                        Yes => (),
                        No => quit = true,
                    }
    
                    break;
                }
            }
        }
    }
    
    
    ================================================
    FILE: 29_Craps/rust/src/util.rs
    ================================================
    use std::io;
    
    pub enum Response {
        Yes,
        No,
    }
    
    pub fn read_line() -> String {
        let mut input = String::new();
    
        io::stdin()
            .read_line(&mut input)
            .expect("Error reading line.");
    
        input
    }
    
    pub fn read_numeric(message: &str) -> usize {
        loop {
            println!("{}", message);
    
            let mut ok = true;
    
            let input = read_line();
    
            for c in input.trim().chars() {
                if !c.is_numeric() {
                    println!("You can only enter a number!");
                    ok = false;
                    break;
                }
            }
    
            if ok {
                let input = input.trim().parse();
    
                let _ = match input {
                    Ok(i) => return i,
                    Err(e) => {
                        println!("please input a number ({})", e);
                    }
                };
            }
        }
    }
    
    pub fn prompt(msg: &str) -> Response {
        use Response::*;
    
        let mut _r = Response::Yes;
    
        loop {
            println!("\n{}", msg);
    
            let response = read_line().trim().to_uppercase();
    
            match response.as_str() {
                "YES" | "Y" => {
                    _r = Yes;
                    break;
                }
                "NO" | "N" => {
                    _r = No;
                    break;
                }
                _ => println!("Please input (Y)es or (N)o."),
            };
        }
    
        _r
    }
    
    
    ================================================
    FILE: 29_Craps/vbnet/Craps.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Craps", "Craps.vbproj", "{297AA824-815F-4E2B-948C-BDAD2266C5AF}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{297AA824-815F-4E2B-948C-BDAD2266C5AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{297AA824-815F-4E2B-948C-BDAD2266C5AF}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{297AA824-815F-4E2B-948C-BDAD2266C5AF}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{297AA824-815F-4E2B-948C-BDAD2266C5AF}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 29_Craps/vbnet/Craps.vbproj
    ================================================
    
      
        Exe
        Craps
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 29_Craps/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 30_Cube/README.md
    ================================================
    ### Cube
    
    CUBE is a game played on the facing sides of a cube with a side dimension of 2. A location is designated by three numbers — e.g., 1, 2, 1. The object is to travel from 1, 1, 1 to 3, 3, 3 by moving one horizontal or vertical (not diagonal) square at a time without striking one of 5 randomly placed landmines. You are staked to $500; prior to each play of the game you may make a wager whether you will reach your destination. You lose if you hit a mine or try to make an illegal move — i.e., change more than one digit from your previous position.
    
    Cube was created by Jerimac Ratliff of Fort Worth, Texas.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=53)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=68)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (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
    chosen like this:
    
    ```basic
    380 LET A=INT(3*(RND(X)))
    390 IF A<>0 THEN 410
    400 LET A=3
    ```
    
    where line 410 is the start of a similar block of code for the next coordinate. The behaviour of `RND(X)` depends on the
    value of `X`. If `X` is greater than zero then it returns a random value between 0 and 1. If `X` is zero it returns the
    last random value generated, or 0 if no value has yet been generated.
    
    If `X` is 1, therefore, the first line above set `A` to 0, 1, or 2. The next 2 lines replace a 0 with a 3. The
    replacement values varies for the different coordinates with the result that the random selection is biased towards a
    specific set of points. If `X` is 0, the `RND` calls all return 0, so the coordinates are the known. It appears that
    this technique was probably used to allow testing the game with a well-known set of locations for the mines. However, in
    the code as it comes to us, the value of `X` is never set and is thus 0, so the mine locations are never randomized.
    
    The C# port implements the biased randomized mine locations, as seems to be the original intent, but includes a
    command-line switch to enable the deterministic execution as well.
    
    
    ================================================
    FILE: 30_Cube/csharp/Cube.csproj
    ================================================
    
      
        Exe
        net6.0
        10
        enable
        enable
      
    
      
        
      
    
      
        
      
    
    
    
    ================================================
    FILE: 30_Cube/csharp/Cube.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cube", "Cube.csproj", "{CC2F0DBE-EBA0-4275-ACA4-7140E3202889}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{CC2F0DBE-EBA0-4275-ACA4-7140E3202889}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{CC2F0DBE-EBA0-4275-ACA4-7140E3202889}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{CC2F0DBE-EBA0-4275-ACA4-7140E3202889}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{CC2F0DBE-EBA0-4275-ACA4-7140E3202889}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 30_Cube/csharp/Game.cs
    ================================================
    namespace Cube;
    
    internal class Game
    {
        private const int _initialBalance = 500;
        private readonly IEnumerable<(int, int, int)> _seeds = new List<(int, int, int)>
        {
            (3, 2, 3), (1, 3, 3), (3, 3, 2), (3, 2, 3), (3, 1, 3)
        };
        private readonly (float, float, float) _startLocation = (1, 1, 1);
        private readonly (float, float, float) _goalLocation = (3, 3, 3);
    
        private readonly IReadWrite _io;
        private readonly IRandom _random;
    
        public Game(IReadWrite io, IRandom random)
        {
            _io = io;
            _random = random;
        }
    
        public void Play()
        {
            _io.Write(Streams.Introduction);
    
            if (_io.ReadNumber("") != 0)
            {
                _io.Write(Streams.Instructions);
            }
    
            PlaySeries(_initialBalance);
    
            _io.Write(Streams.Goodbye);
        }
    
        private void PlaySeries(float balance)
        {
            while (true)
            {
                var wager = _io.ReadWager(balance);
    
                var gameWon = PlayGame();
    
                if (wager.HasValue)
                {
                    balance = gameWon ? (balance + wager.Value) : (balance - wager.Value);
                    if (balance <= 0)
                    {
                        _io.Write(Streams.Bust);
                        return;
                    }
                    _io.WriteLine(Formats.Balance, balance);
                }
    
                if (_io.ReadNumber(Prompts.TryAgain) != 1) { return; }
            }
        }
    
        private bool PlayGame()
        {
            var mineLocations = _seeds.Select(seed => _random.NextLocation(seed)).ToHashSet();
            var currentLocation = _startLocation;
            var prompt = Prompts.YourMove;
    
            while (true)
            {
                var newLocation = _io.Read3Numbers(prompt);
    
                if (!MoveIsLegal(currentLocation, newLocation)) { return Lose(Streams.IllegalMove); }
    
                currentLocation = newLocation;
    
                if (currentLocation == _goalLocation) { return Win(Streams.Congratulations); }
    
                if (mineLocations.Contains(currentLocation)) { return Lose(Streams.Bang); }
    
                prompt = Prompts.NextMove;
            }
        }
    
        private bool Lose(Stream text)
        {
            _io.Write(text);
            return false;
        }
    
        private bool Win(Stream text)
        {
            _io.Write(text);
            return true;
        }
    
        private bool MoveIsLegal((float, float, float) from, (float, float, float) to)
            => (to.Item1 - from.Item1, to.Item2 - from.Item2, to.Item3 - from.Item3) switch
            {
                ( > 1, _, _) => false,
                (_, > 1, _) => false,
                (_, _, > 1) => false,
                (1, 1, _) => false,
                (1, _, 1) => false,
                (_, 1, 1) => false,
                _ => true
            };
    }
    
    
    ================================================
    FILE: 30_Cube/csharp/IOExtensions.cs
    ================================================
    namespace Cube;
    
    internal static class IOExtensions
    {
        internal static float? ReadWager(this IReadWrite io, float balance)
        {
            io.Write(Streams.Wager);
            if (io.ReadNumber("") == 0) { return null; }
    
            var prompt = Prompts.HowMuch;
    
            while(true)
            {
                var wager = io.ReadNumber(prompt);
                if (wager <= balance) { return wager; }
    
                prompt = Prompts.BetAgain;
            }
        }
    }
    
    
    ================================================
    FILE: 30_Cube/csharp/Program.cs
    ================================================
    global using Games.Common.IO;
    global using Games.Common.Randomness;
    
    global using static Cube.Resources.Resource;
    
    using Cube;
    
    IRandom random = args.Contains("--non-random") ? new ZerosGenerator() : new RandomNumberGenerator();
    
    new Game(new ConsoleIO(), random).Play();
    
    
    ================================================
    FILE: 30_Cube/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    #### Execution
    
    As noted in the main Readme file, the randomization code in the BASIC program has a switch (the variable `X`) that
    allows the game to be run in a deterministic (non-random) mode.
    
    Running the C# port without command-line parameters will play the game with random mine locations.
    
    Running the port with a `--non-random` command-line switch will run the game with non-random mine locations.
    
    
    ================================================
    FILE: 30_Cube/csharp/RandomExtensions.cs
    ================================================
    namespace Cube;
    
    internal static class RandomExtensions
    {
        internal static (float, float, float) NextLocation(this IRandom random, (int, int, int) bias)
            => (random.NextCoordinate(bias.Item1), random.NextCoordinate(bias.Item2), random.NextCoordinate(bias.Item3));
    
        private static float NextCoordinate(this IRandom random, int bias)
        {
            var value = random.Next(3);
            if (value == 0) { value = bias; }
            return value;
        }
    }
    
    ================================================
    FILE: 30_Cube/csharp/Resources/Balance.txt
    ================================================
    You now have {0} dollars.
    
    ================================================
    FILE: 30_Cube/csharp/Resources/Bang.txt
    ================================================
    ******BANG******
    You lose!
    
    
    
    
    ================================================
    FILE: 30_Cube/csharp/Resources/BetAgain.txt
    ================================================
    Tried to fool me; bet again
    
    ================================================
    FILE: 30_Cube/csharp/Resources/Bust.txt
    ================================================
    You bust.
    
    
    ================================================
    FILE: 30_Cube/csharp/Resources/Congratulations.txt
    ================================================
    Congratulations!
    
    
    ================================================
    FILE: 30_Cube/csharp/Resources/Goodbye.txt
    ================================================
    Tough luck!
    
    Goodbye.
    
    
    ================================================
    FILE: 30_Cube/csharp/Resources/HowMuch.txt
    ================================================
    How much 
    
    ================================================
    FILE: 30_Cube/csharp/Resources/IllegalMove.txt
    ================================================
    
    Illegal move. You lose.
    
    
    ================================================
    FILE: 30_Cube/csharp/Resources/Instructions.txt
    ================================================
    This is a game in which you will be playing against the
    random decision od the computer. The field of play is a
    cube of side 3. Any of the 27 locations can be designated
    by inputing three numbers such as 2,3,1. At the start,
    you are automatically at location 1,1,1. The object of
    the game is to get to location 3,3,3. One minor detail:
    the computer will pick, at random, 5 locations at which
    it will play land mines. If you hit one of these locations
    you lose. One other details: you may move only one space
    in one direction each move. For example: from 1,1,2 you
    may move to 2,1,2 or 1,1,3. You may not change
    two of the numbers on the same move. If you make an illegal
    move, you lose and the computer takes the money you may
    have bet on that round.
    
    
    All Yes or No questions will be answered by a 1 for Yes
    or a 0 (zero) for no.
    
    When stating the amount of a wager, print only the number
    of dollars (example: 250)  You are automatically started with
    500 dollars in your account.
    
    Good luck!
    
    
    ================================================
    FILE: 30_Cube/csharp/Resources/Introduction.txt
    ================================================
                                      Cube
                   Creative Computing  Morristown, New Jersey
    
    
    
    Do you want to see the instructions? (Yes--1,No--0)
    
    
    ================================================
    FILE: 30_Cube/csharp/Resources/NextMove.txt
    ================================================
    Next move: 
    
    ================================================
    FILE: 30_Cube/csharp/Resources/Resource.cs
    ================================================
    using System.Reflection;
    using System.Runtime.CompilerServices;
    
    namespace Cube.Resources;
    
    internal static class Resource
    {
        internal static class Streams
        {
            public static Stream Introduction => GetStream();
            public static Stream Instructions => GetStream();
            public static Stream Wager => GetStream();
            public static Stream IllegalMove => GetStream();
            public static Stream Bang => GetStream();
            public static Stream Bust => GetStream();
            public static Stream Congratulations => GetStream();
            public static Stream Goodbye => GetStream();
        }
    
        internal static class Prompts
        {
            public static string HowMuch => GetString();
            public static string BetAgain => GetString();
            public static string YourMove => GetString();
            public static string NextMove => GetString();
            public static string TryAgain => GetString();
        }
    
        internal static class Formats
        {
            public static string Balance => GetString();
        }
    
        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}'.");
    }
    
    ================================================
    FILE: 30_Cube/csharp/Resources/TryAgain.txt
    ================================================
    Do you want to try again 
    
    ================================================
    FILE: 30_Cube/csharp/Resources/Wager.txt
    ================================================
    Want to make a wager
    
    
    ================================================
    FILE: 30_Cube/csharp/Resources/YourMove.txt
    ================================================
    
    It's your move:  
    
    ================================================
    FILE: 30_Cube/csharp/ZerosGenerator.cs
    ================================================
    namespace Cube;
    
    internal class ZerosGenerator : IRandom
    {
        public float NextFloat() => 0;
    
        public float PreviousFloat() => 0;
    
        public void Reseed(int seed) { }
    }
    
    ================================================
    FILE: 30_Cube/cube.bas
    ================================================
    10 PRINT TAB(34);"CUBE"
    20 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    30 PRINT : PRINT : PRINT
    100 PRINT "DO YOU WANT TO SEE THE INSTRUCTIONS? (YES--1,NO--0)"
    110 INPUT B7
    120 IF B7=0 THEN 370
    130 PRINT"THIS IS A GAME IN WHICH YOU WILL BE PLAYING AGAINST THE"
    140 PRINT"RANDOM DECISION OF THE COMPUTER. THE FIELD OF PLAY IS A"
    150 PRINT"CUBE OF SIDE 3. ANY OF THE 27 LOCATIONS CAN BE DESIGNATED"
    160 PRINT"BY INPUTING THREE NUMBERS SUCH AS 2,3,1. AT THE START,"
    170 PRINT"YOU ARE AUTOMATICALLY AT LOCATION 1,1,1. THE OBJECT OF"
    180 PRINT"THE GAME IS TO GET TO LOCATION 3,3,3. ONE MINOR DETAIL:"
    190 PRINT"THE COMPUTER WILL PICK, AT RANDOM, 5 LOCATIONS AT WHICH"
    200 PRINT"IT WILL PLANT LAND MINES. IF YOU HIT ONE OF THESE LOCATIONS"
    210 PRINT"YOU LOSE. ONE OTHER DETAIL: YOU MAY MOVE ONLY ONE SPACE "
    220 PRINT"IN ONE DIRECTION EACH MOVE. FOR  EXAMPLE: FROM 1,1,2 YOU"
    230 PRINT"MAY MOVE TO 2,1,2 OR 1,1,3. YOU MAY NOT CHANGE"
    240 PRINT"TWO OF THE NUMBERS ON THE SAME MOVE. IF YOU MAKE AN ILLEGAL"
    250 PRINT"MOVE, YOU LOSE AND THE COMPUTER TAKES THE MONEY YOU MAY"
    260 PRINT"HAVE BET ON THAT ROUND."
    270 PRINT
    280 PRINT
    290 PRINT"ALL YES OR NO QUESTIONS WILL BE ANSWERED BY A 1 FOR YES"
    300 PRINT"OR A 0 (ZERO) FOR NO."
    310 PRINT
    320 PRINT"WHEN STATING THE AMOUNT OF A WAGER, PRINT ONLY THE NUMBER"
    330 PRINT"OF DOLLARS (EXAMPLE: 250)  YOU ARE AUTOMATICALLY STARTED WITH"
    340 PRINT"500 DOLLARS IN YOUR ACCOUNT."
    350 PRINT
    360 PRINT "GOOD LUCK!"
    370 LET A1=500
    380 LET A=INT(3*(RND(X)))
    390 IF A<>0 THEN 410
    400 LET A=3
    410 LET B=INT(3*(RND(X)))
    420 IF B<>0 THEN 440
    430 LET B=2
    440 LET C=INT(3*(RND(X)))
    450 IF C<>0 THEN 470
    460 LET C=3
    470 LET D=INT(3*(RND(X)))
    480 IF D<>0 THEN 500
    490 LET D=1
    500 LET E=INT(3*(RND(X)))
    510 IF E<>0 THEN 530
    520 LET E=3
    530 LET F=INT(3*(RND(X)))
    540 IF F<>0 THEN 560
    550 LET F=3
    560 LET G=INT(3*(RND(X)))
    570 IF G<>0 THEN 590
    580 LET G=3
    590 LET H=INT(3*(RND(X)))
    600 IF H<>0 THEN 620
    610 LET H=3
    620 LET I=INT(3*(RND(X)))
    630 IF I<>0 THEN 650
    640 LET I=2
    650 LET J=INT(3*(RND(X)))
    660 IF J<>0 THEN 680
    670 LET J=3
    680 LET K=INT(3*(RND(X)))
    690 IF K<>0 THEN 710
    700 LET K=2
    710 LET L=INT(3*(RND(X)))
    720 IF L<>0 THEN 740
    730 LET L=3
    740 LET M=INT(3*(RND(X)))
    750 IF M<>0 THEN 770
    760 LET M=3
    770 LET N=INT(3*(RND(X)))
    780 IF N<>0 THEN 800
    790 LET N=1
    800 LET O=INT (3*(RND(X)))
    810 IF O <>0 THEN 830
    820 LET O=3
    830 PRINT "WANT TO MAKE A WAGER?"
    840 INPUT Z
    850 IF Z=0 THEN 880
    860 PRINT "HOW MUCH ";
    870 INPUT Z1
    876 IF A1W+1 THEN 1030
    950 IF P=W+1 THEN 1000
    960 IF Q>X+1 THEN 1030
    970 IF Q=(X+1) THEN 1010
    980 IF R >(Y+1)  THEN 1030
    990 GOTO 1050
    1000 IF Q>= X+1 THEN 1030
    1010 IF R>=Y+1 THEN 1030
    1020 GOTO 1050
    1030 PRINT:PRINT "ILLEGAL MOVE. YOU LOSE."
    1040 GOTO 1440
    1050 LET W=P
    1060 LET X=Q
    1070 LET Y=R
    1080 IF P=3 THEN 1100
    1090 GOTO 1130
    1100 IF  Q=3 THEN 1120
    1110 GOTO 1130
    1120 IF R=3 THEN 1530
    1130 IF P=A THEN 1150
    1140 GOTO 1180
    1150 IF Q=B THEN 1170
    1160 GOTO 1180
    1170 IF R=C THEN 1400
    1180 IF P=D THEN 1200
    1190 GOTO 1230
    1200 IF Q=E THEN 1220
    1210 GOTO 1230
    1220 IF  R=F THEN 1400
    1230 IF P=G THEN 1250
    1240 GOTO 1280
    1250 IF Q=H THEN 1270
    1260 GOTO 1280
    1270 IF R=I THEN 1400
    1280 IF P=J THEN 1300
    1290 GOTO 1330
    1300 IF Q=K THEN 1320
    1310 GOTO 1330
    1320 IF R=L THEN 1400
    1330 IF P=M THEN 1350
    1340 GOTO 1380
    1350 IF Q=N THEN 1370
    1360 GOTO 1380
    1370 IF R=O THEN 1400
    1380 PRINT "NEXT MOVE: ";
    1390 GOTO 930
    1400 PRINT"******BANG******"
    1410 PRINT "YOU LOSE!"
    1420 PRINT
    1430 PRINT
    1440 IF   Z=0 THEN 1580
    1450 PRINT
    1460 LET Z2=A1-Z1
    1470 IF Z2>0 THEN 1500
    1480 PRINT "YOU BUST."
    1490 GOTO 1610
    1500 PRINT " YOU NOW HAVE"; Z2; "DOLLARS."
    1510 LET A1=Z2
    1520 GOTO 1580
    1522 PRINT"TRIED TO FOOL ME; BET AGAIN";
    1525 GOTO 870
    1530 PRINT"CONGRATULATIONS!"
    1540 IF Z=0 THEN 1580
    1550 LET Z2=A1+Z1
    1560 PRINT "YOU NOW HAVE"; Z2;"DOLLARS."
    1570 LET A1=Z2
    1580 PRINT"DO YOU WANT TO TRY AGAIN ";
    1590 INPUT S
    1600 IF S=1 THEN 380
    1610 PRINT "TOUGH LUCK!"
    1620 PRINT
    1630 PRINT "GOODBYE."
    1640 END
    
    
    ================================================
    FILE: 30_Cube/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 30_Cube/java/src/Cube.java
    ================================================
    import java.io.PrintStream;
    import java.util.HashSet;
    import java.util.Random;
    import java.util.Scanner;
    import java.util.Set;
    
    /**
     * Game of Cube
     * 

    * Based on game of Cube at: * https://github.com/coding-horror/basic-computer-games/blob/main/30_Cube/cube.bas * * */ public class Cube { //Current player location private Location playerLocation; //Current list of mines private Set mines; //System input / output objects private PrintStream out; private Scanner scanner; //Player's current money private int money; /** * Entry point, creates a new Cube object and calls the play method * @param args Java execution arguments, not used in application */ public static void main(String[] args) { new Cube().play(); } public Cube() { out = System.out; scanner = new Scanner(System.in); money = 500; mines = new HashSet<>(5); } /** * Clears mines and places 5 new mines on the board */ private void placeMines() { mines.clear(); Random random = new Random(); for(int i = 0; i < 5; i++) { int x = random.nextInt(1,4); int y = random.nextInt(1,4); int z = random.nextInt(1,4); mines.add(new Location(x,y,z)); } } /** * Runs the entire game until the player runs out of money or chooses to stop */ public void play() { out.println("DO YOU WANT TO SEE INSTRUCTIONS? (YES--1,NO--0)"); if(readParsedBoolean()) { printInstructions(); } do { placeMines(); out.println("WANT TO MAKE A WAGER?"); int wager = 0 ; if(readParsedBoolean()) { out.println("HOW MUCH?"); do { wager = Integer.parseInt(scanner.nextLine()); if(wager > money) { out.println("TRIED TO FOOL ME; BET AGAIN"); } } while(wager > money); } playerLocation = new Location(1,1,1); while(playerLocation.x + playerLocation.y + playerLocation.z != 9) { out.println("\nNEXT MOVE"); String input = scanner.nextLine(); String[] stringValues = input.split(","); if(stringValues.length < 3) { out.println("ILLEGAL MOVE, YOU LOSE."); return; } int x = Integer.parseInt(stringValues[0]); int y = Integer.parseInt(stringValues[1]); int z = Integer.parseInt(stringValues[2]); Location location = new Location(x,y,z); if(x < 1 || x > 3 || y < 1 || y > 3 || z < 1 || z > 3 || !isMoveValid(playerLocation,location)) { out.println("ILLEGAL MOVE, YOU LOSE."); return; } playerLocation = location; if(mines.contains(location)) { out.println("******BANG******"); out.println("YOU LOSE!\n\n"); money -= wager; break; } } if(wager > 0) { out.printf("YOU NOW HAVE %d DOLLARS\n",money); } } while(money > 0 && doAnotherRound()); out.println("TOUGH LUCK!"); out.println("\nGOODBYE."); } /** * Queries the user whether they want to play another round * @return True if the player decides to play another round, * False if the player would not like to play again */ private boolean doAnotherRound() { if(money > 0) { out.println("DO YOU WANT TO TRY AGAIN?"); return readParsedBoolean(); } else { return false; } } /** * Prints the instructions to the game, copied from the original code. */ public void printInstructions() { out.println("THIS IS A GAME IN WHICH YOU WILL BE PLAYING AGAINST THE"); out.println("RANDOM DECISION OF THE COMPUTER. THE FIELD OF PLAY IS A"); out.println("CUBE OF SIDE 3. ANY OF THE 27 LOCATIONS CAN BE DESIGNATED"); out.println("BY INPUTTING THREE NUMBERS SUCH AS 2,3,1. AT THE START"); out.println("YOU ARE AUTOMATICALLY AT LOCATION 1,1,1. THE OBJECT OF"); out.println("THE GAME IS TO GET TO LOCATION 3,3,3. ONE MINOR DETAIL:"); out.println("THE COMPUTER WILL PICK, AT RANDOM, 5 LOCATIONS AT WHICH"); out.println("IT WILL PLANT LAND MINES. IF YOU HIT ONE OF THESE LOCATIONS"); out.println("YOU LOSE. ONE OTHER DETAIL: YOU MAY MOVE ONLY ONE SPACE"); out.println("IN ONE DIRECTION EACH MOVE. FOR EXAMPLE: FROM 1,1,2 YOU"); out.println("MAY MOVE TO 2,1,2 OR 1,1,3. YOU MAY NOT CHANGE"); out.println("TWO OF THE NUMBERS ON THE SAME MOVE. IF YOU MAKE AN ILLEGAL"); out.println("MOVE, YOU LOSE AND THE COMPUTER TAKES THE MONEY YOU MAY"); out.println("\n"); out.println("ALL YES OR NO QUESTIONS WILL BE ANSWERED BY A 1 FOR YES"); out.println("OR A 0 (ZERO) FOR NO."); out.println(); out.println("WHEN STATING THE AMOUNT OF A WAGER, PRINT ONLY THE NUMBER"); out.println("OF DOLLARS (EXAMPLE: 250) YOU ARE AUTOMATICALLY STARTED WITH"); out.println("500 DOLLARS IN YOUR ACCOUNT."); out.println(); out.println("GOOD LUCK!"); } /** * Waits for the user to input a boolean value. This could either be (true,false), (1,0), (y,n), (yes,no), etc. * By default, it will return false * @return Parsed boolean value of the user input */ private boolean readParsedBoolean() { String in = scanner.nextLine(); try { return in.toLowerCase().charAt(0) == 'y' || Boolean.parseBoolean(in) || Integer.parseInt(in) == 1; } catch(NumberFormatException exception) { return false; } } /** * Checks if a move is valid * @param from The point that the player is at * @param to The point that the player wishes to move to * @return True if the player is only moving, at most, 1 location in any direction, False if the move is invalid */ private boolean isMoveValid(Location from, Location to) { return Math.abs(from.x - to.x) + Math.abs(from.y - to.y) + Math.abs(from.z - to.z) <= 1; } public class Location { int x,y,z; public Location(int x, int y, int z) { this.x = x; this.y = y; this.z = z; } /* For use in HashSet and checking if two Locations are the same */ @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Location location = (Location) o; if (x != location.x) return false; if (y != location.y) return false; return z == location.z; } /* For use in the HashSet to accordingly index the set */ @Override public int hashCode() { int result = x; result = 31 * result + y; result = 31 * result + z; return result; } } } ================================================ FILE: 30_Cube/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 30_Cube/javascript/cube.html ================================================ CUBE

    
    
    
    
    
    
    ================================================
    FILE: 30_Cube/javascript/cube.js
    ================================================
    // CUBE
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    // Main program
    async function main()
    {
        print(tab(33) + "CUBE\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        print("DO YOU WANT TO SEE THE INSTRUCTIONS? (YES--1,NO--0)");
        b7 = parseInt(await input());
        if (b7 != 0) {
            print("THIS IS A GAME IN WHICH YOU WILL BE PLAYING AGAINST THE\n");
            print("RANDOM DECISION OF THE COMPUTER. THE FIELD OF PLAY IS A\n");
            print("CUBE OF SIDE 3. ANY OF THE 27 LOCATIONS CAN BE DESIGNATED\n");
            print("BY INPUTING THREE NUMBERS SUCH AS 2,3,1. AT THE START,\n");
            print("YOU ARE AUTOMATICALLY AT LOCATION 1,1,1. THE OBJECT OF\n");
            print("THE GAME IS TO GET TO LOCATION 3,3,3. ONE MINOR DETAIL:\n");
            print("THE COMPUTER WILL PICK, AT RANDOM, 5 LOCATIONS AT WHICH\n");
            print("IT WILL PLANT LAND MINES. IF YOU HIT ONE OF THESE LOCATIONS\n");
            print("YOU LOSE. ONE OTHER DETAIL: YOU MAY MOVE ONLY ONE SPACE \n");
            print("IN ONE DIRECTION EACH MOVE. FOR  EXAMPLE: FROM 1,1,2 YOU\n");
            print("MAY MOVE TO 2,1,2 OR 1,1,3. YOU MAY NOT CHANGE\n");
            print("TWO OF THE NUMBERS ON THE SAME MOVE. IF YOU MAKE AN ILLEGAL\n");
            print("MOVE, YOU LOSE AND THE COMPUTER TAKES THE MONEY YOU MAY\n");
            print("HAVE BET ON THAT ROUND.\n");
            print("\n");
            print("\n");
            print("ALL YES OR NO QUESTIONS WILL BE ANSWERED BY A 1 FOR YES\n");
            print("OR A 0 (ZERO) FOR NO.\n");
            print("\n");
            print("WHEN STATING THE AMOUNT OF A WAGER, PRINT ONLY THE NUMBER\n");
            print("OF DOLLARS (EXAMPLE: 250)  YOU ARE AUTOMATICALLY STARTED WITH\n");
            print("500 DOLLARS IN YOUR ACCOUNT.\n");
            print("\n");
            print("GOOD LUCK!\n");
        }
        a1 = 500;
        while (1) {
            a = Math.floor(3 * Math.random());
            if (a == 0)
                a = 3;
            b = Math.floor(3 * Math.random());
            if (b == 0)
                b = 2;
            c = Math.floor(3 * Math.random());
            if (c == 0)
                c = 3;
            d = Math.floor(3 * Math.random());
            if (d == 0)
                d = 1;
            e = Math.floor(3 * Math.random());
            if (e == 0)
                e = 3;
            f = Math.floor(3 * Math.random());
            if (f == 0)
                f = 3;
            g = Math.floor(3 * Math.random());
            if (g == 0)
                g = 3;
            h = Math.floor(3 * Math.random());
            if (h == 0)
                h = 3;
            i = Math.floor(3 * Math.random());
            if (i == 0)
                i = 2;
            j = Math.floor(3 * Math.random());
            if (j == 0)
                j = 3;
            k = Math.floor(3 * Math.random());
            if (k == 0)
                k = 2;
            l = Math.floor(3 * Math.random());
            if (l == 0)
                l = 3;
            m = Math.floor(3 * Math.random());
            if (m == 0)
                m = 3;
            n = Math.floor(3 * Math.random());
            if (n == 0)
                n = 1;
            o = Math.floor(3 * Math.random());
            if (o == 0)
                o = 3;
            print("WANT TO MAKE A WAGER?");
            z = parseInt(await input());
            if (z != 0) {
                print("HOW MUCH ");
                while (1) {
                    z1 = parseInt(await input());
                    if (a1 < z1) {
                        print("TRIED TO FOOL ME; BET AGAIN");
                    } else {
                        break;
                    }
                }
            }
            w = 1;
            x = 1;
            y = 1;
            print("\n");
            print("IT'S YOUR MOVE:  ");
            while (1) {
                str = await input();
                p = parseInt(str);
                q = parseInt(str.substr(str.indexOf(",") + 1));
                r = parseInt(str.substr(str.lastIndexOf(",") + 1));
                if (p > w + 1 || q > x + 1 || r > y + 1 || (p == w + 1 && (q >= x + 1 || r >= y + 1)) || (q == x + 1 && r >= y + 1)) {
                    print("\n");
                    print("ILLEGAL MOVE, YOU LOSE.\n");
                    break;
                }
                w = p;
                x = q;
                y = r;
                if (p == 3 && q == 3 && r == 3) {
                    won = true;
                    break;
                }
                if (p == a && q == b && r == c
                 || p == d && q == e && r == f
                 || p == g && q == h && r == i
                 || p == j && q == k && r == l
                 || p == m && q == n && r == o) {
                    print("******BANG******");
                    print("YOU LOSE!");
                    print("\n");
                    print("\n");
                    won = false;
                    break;
                }
                print("NEXT MOVE: ");
            }
            if (won) {
                print("CONGRATULATIONS!\n");
                if (z != 0) {
                    z2 = a1 + z1;
                    print("YOU NOW HAVE " + z2 + " DOLLARS.\n");
                    a1 = z2;
                }
            } else {
                if (z != 0) {
                    print("\n");
                    z2 = a1 - z1;
                    if (z2 <= 0) {
                        print("YOU BUST.\n");
                        break;
                    } else {
                        print(" YOU NOW HAVE " + z2 + " DOLLARS.\n");
                        a1 = z2;
                    }
                }
            }
            print("DO YOU WANT TO TRY AGAIN ");
            s = parseInt(await input());
            if (s != 1)
                break;
        }
        print("TOUGH LUCK!\n");
        print("\n");
        print("GOODBYE.\n");
    }
    
    main();
    
    
    ================================================
    FILE: 30_Cube/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 30_Cube/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 30_Cube/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 30_Cube/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 30_Cube/python/cube.py
    ================================================
    #!/usr/bin/env python3
    
    """
    CUBE
    
    Converted from BASIC to Python by Trevor Hobson
    """
    
    import random
    from typing import Tuple
    
    
    def mine_position() -> Tuple[int, int, int]:
        return (random.randint(1, 3), random.randint(1, 3), random.randint(1, 3))
    
    
    def parse_move(move: str) -> Tuple[int, int, int]:
        coordinates = [int(item) for item in move.split(",")]
        if len(coordinates) == 3:
            return tuple(coordinates)  # type: ignore
        raise ValueError
    
    
    def play_game() -> None:
        """Play one round of the game"""
    
        money = 500
        print("\nYou have", money, "dollars.")
        while True:
            mines = []
            for _ in range(5):
                while True:
                    mine = mine_position()
                    if (
                        mine not in mines
                        and mine != (1, 1, 1)
                        and mine != (3, 3, 3)
                    ):
                        break
                mines.append(mine)
            wager = -1
            while wager == -1:
                try:
                    wager = int(input("\nHow much do you want to wager? "))
                    if not 0 <= wager <= money:
                        wager = -1
                        print("Tried to fool me; bet again")
                except ValueError:
                    print("Please enter a number.")
            prompt = "\nIt's your move: "
            position = (1, 1, 1)
            while True:
                move = (-1, -1, -1)
                while move == (-1, -1, -1):
                    try:
                        move = parse_move(input(prompt))
                    except (ValueError, IndexError):
                        print("Please enter valid coordinates.")
                if (
                    abs(move[0] - position[0])
                    + abs(move[1] - position[1])
                    + abs(move[2] - position[2])
                ) > 1:
                    print("\nIllegal move. You lose")
                    money = money - wager
                    break
                elif (
                    move[0] not in [1, 2, 3]
                    or move[1] not in [1, 2, 3]
                    or move[2] not in [1, 2, 3]
                ):
                    print("\nIllegal move. You lose")
                    money = money - wager
                    break
                elif move == (3, 3, 3):
                    print("\nCongratulations!")
                    money = money + wager
                    break
                elif move in mines:
                    print("\n******BANG******")
                    print("You lose!")
                    money = money - wager
                    break
                else:
                    position = move
                    prompt = "\nNext move: "
            if money > 0:
                print("\nYou now have", money, "dollars.")
                if not input("Do you want to try again ").lower().startswith("y"):
                    break
            else:
                print("\nYou bust.")
        print("\nTough luck")
        print("\nGoodbye.")
    
    
    def print_instructions() -> None:
        print("\nThis is a game in which you will be playing against the")
        print("random decisions of the computer. The field of play is a")
        print("cube of side 3. Any of the 27 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, 5 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.\n")
        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.\n")
        print("Good luck!")
    
    
    def main() -> None:
        print(" " * 34 + "CUBE")
        print(" " * 15 + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n")
        if input("Do you want to see the instructions ").lower().startswith("y"):
            print_instructions()
    
        keep_playing = True
        while keep_playing:
            play_game()
            keep_playing = input("\nPlay again? (yes or no) ").lower().startswith("y")
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 30_Cube/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 30_Cube/ruby/cube.rb
    ================================================
    $landmines = Array.new
    
    $currentLocation = "111"
    
    $standings = 500 # starting amount
    
    def getYesOrNoResponseTo prompt
        print prompt
        # strip leading and trailing whitespace from entry
        yesno = gets.strip.upcase[0]
        yesno == "Y"
    end
    
    def greeting
        puts "CUBE".center(80)
        puts "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY".center(80)
        puts "\n\n\n"
        response = getYesOrNoResponseTo "Do you want to see the INSTRUCTIONS?"
        if response
            puts "This is a game in which you will be playing against the"
            puts "random decision of the computer. The field of play is a"
            puts "cube of size 3. Any of your 27 locations can be designated"
            puts "by inputting three numbers such as 231."
            puts "At the start you are automatically at location 1,1,1.\n"
            puts "The object of the game is to get to location 3,3,3.\n"
    
            puts "\nONE MINOR DETAIL:"
            puts "The computer will pick five random locations at which it will"
            puts "plant land mines. if you hit one of these locations you lose.\n"
    
            puts "\nONE OTHER DETAIL:"
            puts "You may move only one space in one direction each move."
            puts "For example: from 1,1,2 you may move to 2,1,2 or 1,1,3."
            puts "You may not change more than one number on the same move."
            puts "If you make an illegal move, you lose and the computer takes"
            puts "the money you may have bet on that round."
            puts ""
            puts "When stating the amount of a wager, enter only the number"
            puts "of dollars (example: 250)  You are automatically started with"
            puts "500 dollars in your account."
            puts
            puts "Good luck!"
        end
    end
    
    def landMindStringFrom x, y, z
        landMine = x.to_s + y.to_s + z.to_s
        return landMine
    end
    
    def assignLandMines
        $landmines.clear
    
        # put five unique entries into the landmines array
        while $landmines.size < 5 do
            a = rand(3)+1
            b = rand(3)+1
            c = rand(3)+1
            landmine = landMindStringFrom(a, b, c)
            if !$landmines.include?(landmine) && landmine != "333"
    # puts landmine     # debugging
                $landmines.push landmine
            end
        end
        $currentLocation = "111"
    end
    
    def initializePot
        $standings = 500 # starting amount
    end
    
    def startGame
        assignLandMines
        displayStandings
        response = getYesOrNoResponseTo "WANT TO MAKE A WAGER? "
        if response
            print "HOW MUCH? "
            while true do
            wager = gets.strip.tr('^0-9', '').to_i
                if $standings < wager
                    puts "TRIED TO FOOL ME; BET AGAIN ";
                else
                    break
                end
            end
        else
            wager = 0
        end
    
        # start at location 1,1,1
        $currentLocation = "111"
        return wager
    end
    
    def goodbye
        puts "TOUGH LUCK!"
        puts ""
        puts "GOODBYE."
        exit
    end
    
    def bust
        puts "YOU BUST."
        goodbye
    end
    
    def tryAgain
        again = getYesOrNoResponseTo "WANT TO TRY AGAIN? "
        if not again
            exit
        end
    end
    
    def isLegalMove? newLocation
        # test for illegal moves
        # can only change one variable per move
        # newLocation is the proposed new position
        # can only move one space from the current position
    
        moveX = newLocation[0].to_i
        moveY = newLocation[1].to_i
        moveZ = newLocation[2].to_i
    
        # currentX, currentY, currentZ contains the current position
        currentX = $currentLocation[0].to_i
        currentY = $currentLocation[1].to_i
        currentZ = $currentLocation[2].to_i
    
        isLegalMove = true
        errorString = ""
       # ensure we're not moving off the cube
        if not (moveX.between?(1,3) && moveY.between?(1,3) && moveZ.between?(1,3))
            errorString = "You moved off the cube!"
            return errorString
        end
    
        # test for only one move from current position
        if not moveX.between?(currentX-1,currentX+1)
            isLegalMove = false
        end
        if not moveY.between?(currentY-1,currentY+1)
            isLegalMove = false
        end
        if not moveZ.between?(currentZ-1,currentZ+1)
            isLegalMove = false
        end
            if not isLegalMove
                errorString = "You've gone too far"
            end
    
        # only allow change to one variable at a time
        if isLegalMove
            if moveX != currentX
                if moveY != currentY or moveZ != currentZ
                    isLegalMove = false
                end
            end
            if moveY != currentY
                if moveX != currentX or moveZ != currentZ
                    isLegalMove = false
                end
            end
            if moveZ != currentZ
                if moveY != currentY or moveX != currentX
                    isLegalMove = false
                end
            end
            if not isLegalMove
                errorString = "You made too many changes"
            end
       end
    
        return errorString
    end
    
    def displayStandings
        print "You now have " + $standings.to_s
        if $standings > 1
            puts " dollars"
        else
            puts " dollar"
        end
    end
    
    def didWin? location
        location == "333"
    end
    
    def youWin amount
        $standings += amount
        puts "*** You win " + amount.to_s + " dollars! ***\n\n"
        displayStandings
        tryAgain
        assignLandMines
        puts "*** new cube ***"
        puts "different landmine locations"
        puts "starting over at location 111"
    end
    
    def youLose amount
        # subtract the bet amount from the standings
        if amount > 0
            puts "You lose " + amount.to_s + " dollars!\n\n"
            $standings -= amount
        end
        if $standings <= 0
            # no money left, so end the game
            bust
        else
            displayStandings
        end
        tryAgain
        $currentLocation = "111"
        puts "starting over at location 111"
    end
    
    def landMine betAmount
        puts "******BANG******"
        puts "You hit a land mine at " + $currentLocation + "!"
        youLose betAmount
    end
    
    def gameLoop betAmount
        while true do
            puts ""
            print "IT'S YOUR MOVE:  "
            # allow only integers: strip anything else from input
            moveToLocation = gets.strip.tr('^0-9', '')
    
            # test for illegal moves
            # can only change one variable per move
            # moveToLocation is the proposed new position
    
            error = isLegalMove?(moveToLocation)
            if error == ""
                # assign the new position
                $currentLocation = moveToLocation
    
                # test for win
                if didWin?(moveToLocation)
                    youWin betAmount
                end
    
                # haven't won yet, test the land mines
                if $landmines.include? moveToLocation
                    landMine betAmount
                end
    
            else
                puts "Illegal move: " + error
                youLose betAmount
            end
    
        end
    end
    
    
    greeting
    initializePot
    gameLoop startGame
    
    
    ================================================
    FILE: 30_Cube/rust/Cargo.toml
    ================================================
    [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"
    
    
    ================================================
    FILE: 30_Cube/rust/src/game.rs
    ================================================
    use crate::util;
    
    pub type Position = (u8, u8, u8);
    
    pub struct Game {
        wallet: usize,
        bet: Option,
        landmines: Vec,
        player: Position,
    }
    
    impl Game {
        pub fn new() -> Self {
            Game {
                wallet: 500,
                bet: None,
                landmines: util::get_landmines(),
                player: (1, 1, 1),
            }
        }
    
        pub fn play(&mut self) -> bool {
            self.bet = self.get_bet();
    
            let mut first_move = true;
            let mut result = (false, "******BANG!******\nYOU LOSE");
    
            loop {
                let msg = if first_move {
                    first_move = false;
                    "ITS YOUR MOVE"
                } else {
                    "NEXT MOVE"
                };
    
                let (ok, p) = self.ask_position(msg);
    
                if ok {
                    if p == (3, 3, 3) {
                        result.0 = true;
                        result.1 = "CONGRATULATIONS!";
                        break;
                    } else if self.landmines.contains(&p) {
                        break;
                    } else {
                        self.player = p;
                    }
                } else {
                    result.1 = "ILLEGAL MOVE\nYOU LOSE.";
                    break;
                }
            }
    
            println!("{}", result.1);
            self.calculate_wallet(result.0);
            self.reset_game();
    
            if self.wallet <= 0 {
                println!("YOU ARE BROKE!");
                return false;
            }
    
            return util::prompt_bool("DO YOU WANT TO TRY AGAIN?");
        }
    
        fn get_bet(&self) -> Option {
            loop {
                if util::prompt_bool("WANT TO MAKE A WAGER?") {
                    let b = util::prompt_number("HOW MUCH?");
    
                    if b != 0 && b <= self.wallet {
                        return Some(b);
                    } else {
                        println!("YOU CAN'T BET THAT!");
                    }
                } else {
                    return None;
                };
            }
        }
    
        fn ask_position(&self, msg: &str) -> (bool, Position) {
            if let Some(p) = util::prompt_position(msg, self.player) {
                return (true, p);
            }
            return (false, (0, 0, 0));
        }
    
        fn calculate_wallet(&mut self, win: bool) {
            if let Some(b) = self.bet {
                if win {
                    self.wallet += b;
                } else {
                    self.wallet -= b;
                }
                self.bet = None;
                println!("YOU NOW HAVE {} DOLLARS", self.wallet);
            }
        }
    
        fn reset_game(&mut self) {
            self.player = (1, 1, 1);
            self.landmines.clear();
            self.landmines = util::get_landmines();
        }
    }
    
    
    ================================================
    FILE: 30_Cube/rust/src/main.rs
    ================================================
    use crate::game::Game;
    
    mod game;
    mod util;
    
    fn main() {
        println!("\n\n\t\tCUBE");
        println!("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n");
    
        if util::prompt_bool("DO YOU WANT TO SEE THE INSTRUCTIONS? (YES--1,NO--0)") {
            println!("\nThis is a game in which you will be playing against the");
            println!("random decisions of the computer. The field of play is a");
            println!("cube of side 3. Any of the 27 locations can be designated");
            println!("by inputing three numbers such as 2,3,1. At the start,");
            println!("you are automatically at location 1,1,1. The object of");
            println!("the game is to get to location 3,3,3. One minor detail:");
            println!("the computer will pick, at random, 5 locations at which");
            println!("it will plant land mines. If you hit one of these locations");
            println!("you lose. One other detail: You may move only one space");
            println!("in one direction each move. For example: From 1,1,2 you");
            println!("may move to 2,1,2 or 1,1,3. You may not change");
            println!("two of the numbers on the same move. If you make an illegal");
            println!("move, you lose and the computer takes the money you may");
            println!("have bet on that round.\n");
            println!("When stating the amount of a wager, print only the number");
            println!("of dollars (example: 250) you are automatically started with");
            println!("500 dollars in your account.\n");
            println!("Good luck!\n");
        }
    
        let mut game = Game::new();
    
        loop {
            if !game.play() {
                println!("\nTOUGH LUCK\n\nGOODBYE!\n");
                break;
            }
        }
    }
    
    
    ================================================
    FILE: 30_Cube/rust/src/util.rs
    ================================================
    use std::num::ParseIntError;
    
    use crate::game::Position;
    
    pub fn get_random_position() -> Position {
        (get_random_axis(), get_random_axis(), get_random_axis())
    }
    
    fn get_random_axis() -> u8 {
        rand::Rng::gen_range(&mut rand::thread_rng(), 1..=3)
    }
    
    pub fn get_landmines() -> Vec {
        let mut landmines = Vec::new();
    
        for _ in 0..5 {
            let mut m = get_random_position();
            while landmines.contains(&m) {
                m = get_random_position();
            }
            landmines.push(m);
        }
    
        landmines
    }
    
    fn read_line() -> Result {
        let mut input = String::new();
        std::io::stdin()
            .read_line(&mut input)
            .expect("~~Failed reading line!~~");
        input.trim().parse::()
    }
    
    pub fn prompt_bool(msg: &str) -> bool {
        loop {
            println!("{}", msg);
    
            if let Ok(n) = read_line() {
                if n == 1 {
                    return true;
                } else if n == 0 {
                    return false;
                }
            }
            println!("ENTER YES--1 OR NO--0\n");
        }
    }
    
    pub fn prompt_number(msg: &str) -> usize {
        loop {
            println!("{}", msg);
    
            if let Ok(n) = read_line() {
                return n;
            }
            println!("ENTER A NUMBER\n");
        }
    }
    
    pub fn prompt_position(msg: &str, prev_pos: Position) -> Option {
        loop {
            println!("{}", msg);
    
            let mut input = String::new();
            std::io::stdin()
                .read_line(&mut input)
                .expect("~~Failed reading line!~~");
    
            let input: Vec<&str> = input.trim().split(",").collect();
    
            let pp = [prev_pos.0, prev_pos.1, prev_pos.2];
            let mut pos = Vec::new();
    
            if input.len() != 3 {
                println!("YOU MUST ENTER 3 AXES!");
            } else {
                for a in input {
                    if let Ok(n) = a.parse::() {
                        if n == 0 || n > 3 {
                            println!("YOU MUST ENTER AN AXIS BETWEEN 1 AND 3!");
                        } else {
                            pos.push(n);
                        }
                    } else {
                        println!("INVALID LOCATION.");
                    }
                }
    
                let mut moved = false;
                for (i, p) in pos.iter().enumerate() {
                    let dt = ((*p as isize) - (pp[i] as isize)).abs();
    
                    if dt > 1 {
                        return None;
                    }
    
                    if dt == 1 {
                        if moved {
                            return None;
                        } else {
                            moved = true;
                        }
                    }
                }
            }
    
            if pos.len() == 3 {
                let pos = (pos[0], pos[1], pos[2]);
                if pos == prev_pos {
                    println!("YOU ARE ALREADY THERE!");
                } else {
                    return Some(pos);
                }
            }
        }
    }
    
    
    ================================================
    FILE: 30_Cube/vbnet/Cube.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Cube", "Cube.vbproj", "{C4AA207B-37EC-4746-B634-FBFC9522F3F8}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{C4AA207B-37EC-4746-B634-FBFC9522F3F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{C4AA207B-37EC-4746-B634-FBFC9522F3F8}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{C4AA207B-37EC-4746-B634-FBFC9522F3F8}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{C4AA207B-37EC-4746-B634-FBFC9522F3F8}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 30_Cube/vbnet/Cube.vbproj
    ================================================
    
      
        Exe
        Cube
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 30_Cube/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 31_Depth_Charge/README.md
    ================================================
    ### Depth Charge
    
    In this program you are captain of the destroyer USS Computer. An enemy submarine has been causing trouble and your mission is to destroy it. You may select the seize of the “cube” of water you wish to search in. The computer then determines how many depth charges you get to destroy the submarine.
    
    Each depth charge is exploded by you specifying a trio of numbers; the first two are the surface coordinates (X,Y), the third is the depth. After each depth charge, your sonar observer will tell you where the explosion was relative to the submarine.
    
    Dana Noftle wrote this program while a student at Acton High School, Acton, Massachusetts.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=55)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=70)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 31_Depth_Charge/csharp/Controller.cs
    ================================================
    using System;
    
    namespace DepthCharge
    {
        /// 
        /// Contains functions for reading input from the user.
        /// 
        static class Controller
        {
            /// 
            /// Retrives a dimension for the play area from the user.
            /// 
            /// 
            /// Note that the original BASIC version would allow dimension values
            /// of 0 or less.  We're doing a little extra validation here in order
            /// to avoid strange behaviour.
            /// 
            public static int InputDimension()
            {
                View.PromptDimension();
    
                while (true)
                {
                    if (!Int32.TryParse(Console.ReadLine(), out var dimension))
                        View.ShowInvalidNumber();
                    else
                    if (dimension < 1)
                        View.ShowInvalidDimension();
                    else
                        return dimension;
                }
            }
    
            /// 
            /// Retrieves a set of coordinates from the user.
            /// 
            /// 
            /// The current trail number.
            /// 
            public static (int x, int y, int depth) InputCoordinates(int trailNumber)
            {
                View.PromptGuess(trailNumber);
    
                while (true)
                {
                    var coordinates = Console.ReadLine().Split(',');
    
                    if (coordinates.Length < 3)
                        View.ShowTooFewCoordinates();
                    else
                    if (coordinates.Length > 3)
                        View.ShowTooManyCoordinates();
                    else
                    if (!Int32.TryParse(coordinates[0], out var x) ||
                        !Int32.TryParse(coordinates[1], out var y) ||
                        !Int32.TryParse(coordinates[2], out var depth))
                        View.ShowInvalidNumber();
                    else
                        return (x, y, depth);
                }
            }
    
            /// 
            /// Retrieves the user's intention to play again (or not).
            /// 
            public static bool InputPlayAgain()
            {
                View.PromptPlayAgain();
    
                while (true)
                {
                    switch (Console.ReadLine())
                    {
                        case "Y":
                            return true;
                        case "N":
                            return false;
                        default:
                            View.ShowInvalidYesOrNo();
                            break;
                    }
                }
            }
        }
    }
    
    
    ================================================
    FILE: 31_Depth_Charge/csharp/DepthCharge.csproj
    ================================================
    
    
      
        Exe
        net5.0
        DepthCharge
      
    
    
    
    
    ================================================
    FILE: 31_Depth_Charge/csharp/DepthCharge.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.31129.286
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DepthCharge", "DepthCharge.csproj", "{15CF71F3-72F3-4C81-B54F-139F2A1E3920}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{15CF71F3-72F3-4C81-B54F-139F2A1E3920}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{15CF71F3-72F3-4C81-B54F-139F2A1E3920}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{15CF71F3-72F3-4C81-B54F-139F2A1E3920}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{15CF71F3-72F3-4C81-B54F-139F2A1E3920}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {738F08DD-89E9-44C5-B5AC-3F21C6AEEFA1}
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 31_Depth_Charge/csharp/Program.cs
    ================================================
    using System;
    
    namespace DepthCharge
    {
        class Program
        {
            static void Main(string[] args)
            {
                var random = new Random();
    
                View.ShowBanner();
    
                var dimension = Controller.InputDimension();
                var maximumGuesses = CalculateMaximumGuesses();
    
                View.ShowInstructions(maximumGuesses);
    
                do
                {
                    View.ShowStartGame();
    
                    var submarineCoordinates = PlaceSubmarine();
                    var trailNumber = 1;
                    var guess = (0, 0, 0);
    
                    do
                    {
                        guess = Controller.InputCoordinates(trailNumber);
                        if (guess != submarineCoordinates)
                            View.ShowGuessPlacement(submarineCoordinates, guess);
                    }
                    while (guess != submarineCoordinates && trailNumber++ < maximumGuesses);
    
                    View.ShowGameResult(submarineCoordinates, guess, trailNumber);
                }
                while (Controller.InputPlayAgain());
    
                View.ShowFarewell();
    
                int CalculateMaximumGuesses() =>
                    (int)Math.Log2(dimension) + 1;
    
                (int x, int y, int depth) PlaceSubmarine() =>
                    (random.Next(dimension), random.Next(dimension), random.Next(dimension));
            }
        }
    }
    
    
    ================================================
    FILE: 31_Depth_Charge/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 31_Depth_Charge/csharp/View.cs
    ================================================
    using System;
    
    namespace DepthCharge
    {
        /// 
        /// Contains methods for displaying information to the user.
        /// 
        static class View
        {
            public static void ShowBanner()
            {
                Console.WriteLine("                             DEPTH CHARGE");
                Console.WriteLine("              CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine();
            }
    
            public static void ShowInstructions(int maximumGuesses)
            {
                Console.WriteLine("YOU ARE THE CAPTAIN OF THE DESTROYER USS COMPUTER");
                Console.WriteLine("AN ENEMY SUB HAS BEEN CAUSING YOU TROUBLE.  YOUR");
                Console.WriteLine($"MISSION IS TO DESTROY IT.  YOU HAVE {maximumGuesses} SHOTS.");
                Console.WriteLine("SPECIFY DEPTH CHARGE EXPLOSION POINT WITH A");
                Console.WriteLine("TRIO OF NUMBERS -- THE FIRST TWO ARE THE");
                Console.WriteLine("SURFACE COORDINATES; THE THIRD IS THE DEPTH.");
                Console.WriteLine();
            }
    
            public static void ShowStartGame()
            {
                Console.WriteLine("GOOD LUCK !");
                Console.WriteLine();
            }
    
            public static void ShowGuessPlacement((int x, int y, int depth) actual, (int x, int y, int depth) guess)
            {
                Console.Write("SONAR REPORTS SHOT WAS ");
                if (guess.y > actual.y)
                    Console.Write("NORTH");
                if (guess.y < actual.y)
                    Console.Write("SOUTH");
                if (guess.x > actual.x)
                    Console.Write("EAST");
                if (guess.x < actual.x)
                    Console.Write("WEST");
                if (guess.y != actual.y || guess.x != actual.y)
                    Console.Write(" AND");
                if (guess.depth > actual.depth)
                    Console.Write (" TOO LOW.");
                if (guess.depth < actual.depth)
                    Console.Write(" TOO HIGH.");
                if (guess.depth == actual.depth)
                    Console.Write(" DEPTH OK.");
    
                Console.WriteLine();
            }
    
            public static void ShowGameResult((int x, int y, int depth) submarineLocation, (int x, int y, int depth) finalGuess, int trailNumber)
            {
                Console.WriteLine();
    
                if (submarineLocation == finalGuess)
                {
                    Console.WriteLine($"B O O M ! ! YOU FOUND IT IN {trailNumber} TRIES!");
                }
                else
                {
                    Console.WriteLine("YOU HAVE BEEN TORPEDOED!  ABANDON SHIP!");
                    Console.WriteLine($"THE SUBMARINE WAS AT {submarineLocation.x}, {submarineLocation.y}, {submarineLocation.depth}");
                }
            }
    
            public static void ShowFarewell()
            {
                Console.WriteLine ("OK.  HOPE YOU ENJOYED YOURSELF.");
            }
    
            public static void ShowInvalidNumber()
            {
                Console.WriteLine("PLEASE ENTER A NUMBER");
            }
    
            public static void ShowInvalidDimension()
            {
                Console.WriteLine("PLEASE ENTER A VALID DIMENSION");
            }
    
            public static void ShowTooFewCoordinates()
            {
                Console.WriteLine("TOO FEW COORDINATES");
            }
    
            public static void ShowTooManyCoordinates()
            {
                Console.WriteLine("TOO MANY COORDINATES");
            }
    
            public static void ShowInvalidYesOrNo()
            {
                Console.WriteLine("PLEASE ENTER Y OR N");
            }
    
            public static void PromptDimension()
            {
                Console.Write("DIMENSION OF SEARCH AREA? ");
            }
    
            public static void PromptGuess(int trailNumber)
            {
                Console.WriteLine();
                Console.Write($"TRIAL #{trailNumber}? ");
            }
    
            public static void PromptPlayAgain()
            {
                Console.WriteLine();
                Console.Write("ANOTHER GAME (Y OR N)? ");
            }
        }
    }
    
    
    ================================================
    FILE: 31_Depth_Charge/depthcharge.bas
    ================================================
    2 PRINT TAB(30);"DEPTH CHARGE"
    4 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    6 PRINT: PRINT: PRINT
    20 INPUT "DIMENSION OF SEARCH AREA";G: PRINT
    30 N=INT(LOG(G)/LOG(2))+1
    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
    110 A=INT(G*RND(1)) : B=INT(G*RND(1)) : C=INT(G*RND(1))
    120 FOR D=1 TO N : PRINT : PRINT "TRIAL #";D; : INPUT X,Y,Z
    130 IF ABS(X-A)+ABS(Y-B)+ABS(Z-C)=0 THEN 300
    140 GOSUB 500 : PRINT : NEXT D
    200 PRINT : PRINT "YOU HAVE BEEN TORPEDOED!  ABANDON SHIP!"
    210 PRINT "THE SUBMARINE WAS AT";A;",";B;",";C : GOTO 400
    300 PRINT : PRINT "B O O M ! ! YOU FOUND IT IN";D;"TRIES!"
    400 PRINT : PRINT: INPUT "ANOTHER GAME (Y OR N)";A$
    410 IF A$="Y" THEN 100
    420 PRINT "OK.  HOPE YOU ENJOYED YOURSELF." : GOTO 600
    500 PRINT "SONAR REPORTS SHOT WAS ";
    510 IF Y>B THEN PRINT "NORTH";
    520 IF YA THEN PRINT "EAST";
    540 IF X
    B OR X<>A THEN PRINT " AND"; 560 IF Z>C THEN PRINT " TOO LOW." 570 IF Z * Based on the BASIC game of Depth Charge here * https://github.com/coding-horror/basic-computer-games/blob/main/31%20Depth%20Charge/depthcharge.bas *

    * Note: The idea was to create a version of the 1970's BASIC game in Java, without introducing * new features - no additional text, error checking, etc has been added. * * Converted from BASIC to Java by Darren Cardenas. */ public class DepthCharge { private final Scanner scan; // For user input public DepthCharge() { scan = new Scanner(System.in); } // End of constructor DepthCharge public void play() { showIntro(); startGame(); } // End of method play private static void showIntro() { System.out.println(" ".repeat(29) + "DEPTH CHARGE"); System.out.println(" ".repeat(14) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println("\n\n"); } // End of method showIntro private void startGame() { int searchArea = 0; int shotNum = 0; int shotTotal = 0; int shotX = 0; int shotY = 0; int shotZ = 0; int targetX = 0; int targetY = 0; int targetZ = 0; int tries = 0; String[] userCoordinates; String userResponse = ""; System.out.print("DIMENSION OF SEARCH AREA? "); searchArea = Integer.parseInt(scan.nextLine()); System.out.println(""); shotTotal = (int) (Math.log10(searchArea) / Math.log10(2)) + 1; System.out.println("YOU ARE THE CAPTAIN OF THE DESTROYER USS COMPUTER"); System.out.println("AN ENEMY SUB HAS BEEN CAUSING YOU TROUBLE. YOUR"); System.out.println("MISSION IS TO DESTROY IT. YOU HAVE " + shotTotal + " SHOTS."); System.out.println("SPECIFY DEPTH CHARGE EXPLOSION POINT WITH A"); System.out.println("TRIO OF NUMBERS -- THE FIRST TWO ARE THE"); System.out.println("SURFACE COORDINATES; THE THIRD IS THE DEPTH."); // Begin outer while loop while (true) { System.out.println(""); System.out.println("GOOD LUCK !"); System.out.println(""); targetX = (int) ((searchArea + 1) * Math.random()); targetY = (int) ((searchArea + 1) * Math.random()); targetZ = (int) ((searchArea + 1) * Math.random()); // Begin loop through all shots for (shotNum = 1; shotNum <= shotTotal; shotNum++) { // Get user input System.out.println(""); System.out.print("TRIAL # " + shotNum + "? "); userResponse = scan.nextLine(); // Split on commas userCoordinates = userResponse.split(","); // Assign to integer variables shotX = Integer.parseInt(userCoordinates[0].trim()); shotY = Integer.parseInt(userCoordinates[1].trim()); shotZ = Integer.parseInt(userCoordinates[2].trim()); // Win condition if (Math.abs(shotX - targetX) + Math.abs(shotY - targetY) + Math.abs(shotZ - targetZ) == 0) { System.out.println("B O O M ! ! YOU FOUND IT IN" + shotNum + " TRIES!"); break; } this.getReport(targetX, targetY, targetZ, shotX, shotY, shotZ); System.out.println(""); } // End loop through all shots if (shotNum > shotTotal) { System.out.println(""); System.out.println("YOU HAVE BEEN TORPEDOED! ABANDON SHIP!"); System.out.println("THE SUBMARINE WAS AT " + targetX + "," + targetY + "," + targetZ); } System.out.println(""); System.out.println(""); System.out.print("ANOTHER GAME (Y OR N)? "); userResponse = scan.nextLine(); if (!userResponse.toUpperCase().equals("Y")) { System.out.print("OK. HOPE YOU ENJOYED YOURSELF."); return; } } // End outer while loop } // End of method startGame public void getReport(int a, int b, int c, int x, int y, int z) { System.out.print("SONAR REPORTS SHOT WAS "); // Handle y coordinate if (y > b) { System.out.print("NORTH"); } else if (y < b) { System.out.print("SOUTH"); } // Handle x coordinate if (x > a) { System.out.print("EAST"); } else if (x < a) { System.out.print("WEST"); } if ((y != b) || (x != a)) { System.out.print(" AND"); } // Handle depth if (z > c) { System.out.println(" TOO LOW."); } else if (z < c) { System.out.println(" TOO HIGH."); } else { System.out.println(" DEPTH OK."); } return; } // End of method getReport public static void main(String[] args) { DepthCharge game = new DepthCharge(); game.play(); } // End of method main } // End of class DepthCharge ================================================ FILE: 31_Depth_Charge/java/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Oracle Java](https://openjdk.java.net/) ================================================ FILE: 31_Depth_Charge/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 31_Depth_Charge/javascript/depthcharge.html ================================================ DEPTH CHARGE

    
    
    
    
    
    
    ================================================
    FILE: 31_Depth_Charge/javascript/depthcharge.js
    ================================================
    // DEPTH CHARGE
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    // Main program
    async function main()
    {
        print(tab(30) + "DEPTH CHARGE\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        print("DIMENSION OF THE SEARCH AREA");
        g = Math.floor(await input());
        n = Math.floor(Math.log(g) / Math.log(2)) + 1;
        print("YOU ARE THE CAPTAIN OF THE DESTROYER USS COMPUTER\n");
        print("AN ENEMY SUB HAS BEEN CAUSING YOU TROUBLE.  YOUR\n");
        print("MISSION IS TO DESTROY IT.  YOU HAVE " + n + " SHOTS.\n");
        print("SPECIFY DEPTH CHARGE EXPLOSION POINT WITH A\n");
        print("TRIO OF NUMBERS -- THE FIRST TWO ARE THE\n");
        print("SURFACE COORDINATES; THE THIRD IS THE DEPTH.\n");
        do {
            print("\n");
            print("GOOD LUCK !\n");
            print("\n");
            a = Math.floor(Math.random() * g);
            b = Math.floor(Math.random() * g);
            c = Math.floor(Math.random() * g);
            for (d = 1; d <= n; d++) {
                print("\n");
                print("TRIAL #" + d + " ");
                str = await input();
                x = parseInt(str);
                y = parseInt(str.substr(str.indexOf(",") + 1));
                z = parseInt(str.substr(str.lastIndexOf(",") + 1));
                if (Math.abs(x - a) + Math.abs(y - b) + Math.abs(z - c) == 0)
                    break;
                if (y > b)
                    print("NORTH");
                if (y < b)
                    print("SOUTH");
                if (x > a)
                    print("EAST");
                if (x < a)
                    print("WEST");
                if (y != b || x != a)
                    print(" AND");
                if (z > c)
                    print(" TOO LOW.\n");
                if (z < c)
                    print(" TOO HIGH.\n");
                if (z == c)
                    print(" DEPTH OK.\n");
                print("\n");
            }
            if (d <= n) {
                print("\n");
                print("B O O M ! ! YOU FOUND IT IN " + d + " TRIES!\n");
            } else {
                print("\n");
                print("YOU HAVE BEEN TORPEDOED!  ABANDON SHIP!\n");
                print("THE SUBMARINE WAS AT " + a + "," + b + "," + c + "\n");
            }
            print("\n");
            print("\n");
            print("ANOTHER GAME (Y OR N)");
            str = await input();
        } while (str.substr(0, 1) == "Y") ;
        print("OK.  HOPE YOU ENJOYED YOURSELF.\n");
    }
    
    main();
    
    
    ================================================
    FILE: 31_Depth_Charge/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 31_Depth_Charge/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 31_Depth_Charge/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    ## Conversion
    
    Not a difficult conversion - but a chance to throw in a few ways
    Perl makes life easy.
    
     * To get the sub permission which is a random location in the g x g x g grid we can use:
       * assigning multiple variables in list form ($a,$b,$c) = (?,?,?)
       * where the list on the right hand side is generated with a map function
    
     * We use ternarys to generate the message if you miss the sub.
       * We use join to stitch the pieces of the string together.
       * If we have a ternary where we don't want to return anything we return an empty list rather than an empty string - if you return the latter you still get the padding spaces.
    
    
    ================================================
    FILE: 31_Depth_Charge/perl/depth-charge.pl
    ================================================
    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    print '               Depth Charge
    Creative Computing Morristown, New Jersey
    
    
    Depth Charge Game
    
    Dimensions of Search Area? ';
    
    my $g = ;
    my $n = int( log($g) / log 2 ) + 1;
    print '
    You are the captain of the Destroyer USS Computer
    an enemy sub has been causing you trouble.  Your
    mission is to destroy it.  You have ',$n,' shots.
    Specify depth charge explosion point with a
    trio of number -- the first two are the surface
    co-ordinates; the third is the depth.
    ';
    
    while(1) { ## Repeat until we say no....
      print "\nGood luck!\n\n";
      my ($a,$b,$c) = map { int rand $g } 1..3; ## Get the location
      my $hit = 0; ## Keep track if we have won yet!
      foreach ( 1..$n ) {
        print "\nTrial # $_ ? ";
        my ( $x, $y, $z ) = split m{\D+}, ;
        if( $x==$a && $y==$b && $z==$c ) {
          $hit = 1; ## We have won
          print "\n\nB O O M ! ! You found it in $_ tries!\n";
          last;
        }
        print join q( ), 'Sonar reports show was',
          $y < $b              ? 'South'    : $y > $b ? 'North'   : (),
          $x < $a              ? 'West'     : $x > $a ? 'East'    : (),
          $x == $a && $y == $b ? ()         : 'and' ,
          $z < $c              ? 'too high' : $z > $c ? 'too low' : 'depth OK',
          ".\n";
      }
    
      ## Only show message if we haven't won...
      print "\nYou have been torpedoed!  Abandon ship!\nThe submarine was at $a, $b, $c\n" unless $hit;
    
      print "\n\nAnother game (Y or N)? ";
      last unless  =~ m{Y}i; ## Y or y not typed so leave loop
    }
    ## Say good bye
    print "OK.  Hope you enjoyed yourself.\n\n";
    
    
    ================================================
    FILE: 31_Depth_Charge/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 31_Depth_Charge/python/depth_charge.py
    ================================================
    """
    Original BASIC version as published in Basic Computer Games (1978)
    https://www.atariarchives.org/basicgames/showpage.php?page=55
    
    Converted to Python by Anson VanDoren in 2021
    """
    
    import math
    import random
    from typing import Tuple
    
    
    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(f"{chr(27)}[2J")
    
        # Show the intro text, centered
        print("DEPTH CHARGE".center(45))
        print("Creative Computing  Morristown, New Jersey\n\n".center(45))
    
    
    def get_num_charges() -> Tuple[int, int]:
        print("Depth Charge game\n")
        while True:
            search_area_str = input("Dimensions of search area? ")
    
            # Make sure the input is an integer
            try:
                search_area = int(search_area_str)
                break
            except ValueError:
                print("Must enter an integer number. Please try again...")
    
        num_charges = int(math.log2(search_area)) + 1
        return search_area, num_charges
    
    
    def ask_for_new_game() -> None:
        answer = input("Another game (Y or N): ")
        if answer.lower().strip()[0] == "y":
            main()
        else:
            print("OK. Hope you enjoyed yourself")
            exit()
    
    
    def show_shot_result(shot, location) -> None:
        result = "Sonar reports shot was "
        if shot[1] > location[1]:  # y-direction
            result += "north"
        elif shot[1] < location[1]:  # y-direction
            result += "south"
        if shot[0] > location[0]:  # x-direction
            result += "east"
        elif shot[0] < location[0]:  # x-direction
            result += "west"
        if shot[1] != location[1] or shot[0] != location[0]:
            result += " and "
    
        if shot[2] > location[2]:
            result += "too low."
        elif shot[2] < location[2]:
            result += "too high."
        else:
            result += "depth OK."
        print(result)
        return
    
    
    def get_shot_input() -> Tuple[int, int, int]:
        while True:
            raw_guess = input("Enter coordinates: ")
            try:
                xyz = raw_guess.split()
            except ValueError:
                print("Please enter coordinates separated by spaces")
                print("Example: 3 2 1")
                continue
            try:
                x, y, z = (int(num) for num in xyz)
                return x, y, z
            except ValueError:
                print("Please enter whole numbers only")
    
    
    def play_game(search_area, num_charges) -> None:
        print("\nYou are the captain of the destroyer USS Computer.")
        print("An enemy sub has been causing you trouble. Your")
        print(f"mission is to destroy it. You have {num_charges} 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.")
        print("\nGood luck!\n")
    
        # Generate position for submarine
        a, b, c = (random.randint(0, search_area) for _ in range(3))
    
        # Get inputs until win or lose
        for i in range(num_charges):
            print(f"\nTrial #{i+1}")
            x, y, z = get_shot_input()
    
            if (x, y, z) == (a, b, c):
                print(f"\nB O O M ! ! You found it in {i+1} tries!\n")
                ask_for_new_game()
            else:
                show_shot_result((x, y, z), (a, b, c))
    
        # out of shots
        print("\nYou have been torpedoed! Abandon ship!")
        print(f"The submarine was at {a} {b} {c}")
        ask_for_new_game()
    
    
    def main() -> None:
        search_area, num_charges = get_num_charges()
        play_game(search_area, num_charges)
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 31_Depth_Charge/ruby/.editorconfig
    ================================================
    # EditorConfig is awesome: https://EditorConfig.org
    # .editorconfig
    
    # Please see doc/developer_notes.md
    # If you find anything egregious or missing, please consider submitting a pull request
    # to https://github.com/theias/ias_package_shell
    
    
    # top-most EditorConfig file
    root = true
    
    # Sensible defaults for everything
    [*]
    charset = utf-8
    end_of_line = lf
    insert_final_newline = true
    
    # JavaScript
    [**.js]
    indent_style = space
    indent_size = 2
    insert_final_newline = true
    
    # Ruby
    [**.rb]
    indent_style = space
    indent_size = 2
    trim_trailing_whitespace = true
    
    # Python
    [**.py]
    charset = utf-8
    indent_style = space
    indent_size = 4
    insert_final_newline = true
    
    # Perl
    [**.pl]
    charset = utf-8
    insert_final_newline = true
    [**.pm]
    charset = utf-8
    insert_final_newline = true
    
    # PHP
    [**.php]
    charset = utf-8
    indent_size = 4
    indent_style = space
    end_of_line = lf
    trim_trailing_whitespace = true
    insert_final_newline = true
    
    # Makefiles
    [Makefile]
    indent_style = tab
    
    [**.gmk]
    indent_style = tab
    
    # Configuration Files
    # Matches the exact files either package.json or .travis.yml
    [{package.json,.travis.yml}]
    indent_style = space
    indent_size = 2
    
    # Diff files
    [*.{diff,patch}]
    trim_trailing_whitespace = false
    
    
    ================================================
    FILE: 31_Depth_Charge/ruby/.gitignore
    ================================================
    # Package Shell Specific Things
    
    ## Build Process
    
    /build
    
    ## Transient files
    
    /src/input
    /src/output
    /src/log
    /drop
    
    # Programming Languages
    
    ## Java
    /src/java/**/*.class
    
    ## Rakudo / Perl6
    /src/**/.precomp
    
    ## PHP
    /vendor/
    
    ## Python
    *.swp
    __pycache__/
    *.pyc
    *.egg-info
    /dist
    
    ## Ruby
    *.gem
    .bundle
    
    # Editors
    
    ## Dia
    *.dia.autosave
    
    ## Emacs
    \#*\#
    .\#*
    
    
    ## Vi / Vim
    *.swp
    
    # Filesystem Artifacts
    
    ## Mac
    
    .DS_Store
    
    ## NFS
    
    .nfs*
    
    
    ================================================
    FILE: 31_Depth_Charge/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 31_Depth_Charge/ruby/depthcharge.rb
    ================================================
    #!/usr/bin/ruby
    
    class DepthCharge
      def run_game
        output_title
    
        loop do
          puts "----------"
          print_instructions
          setup_game
          puts
          game_loop
          break unless get_input_another_game
        end
    
        puts "OK.  HOPE YOU ENJOYED YOURSELF."
      end
    
      def output_title
        puts "--- DEPTH CHARGE ---"
        puts "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
        puts
      end
    
      def get_input_y_or_n(message)
        loop do
          print message
    
          value = gets.chomp
    
          if value.downcase == "y"
            return true
          elsif value.downcase == "n"
            return false
          end
    
          puts "PLEASE ENTER Y/y OR N/n..."
          puts
        end
      end
    
      def get_input_positive_integer(message)
        loop do
          print message
          value = gets.chomp
    
          if value == "d"
            debug_game
            next
          end
    
          the_input = Integer(value) rescue 0
    
          if the_input < 1
            puts "PLEASE ENTER A POSITIVE NUMBER"
            puts
            next
          end
    
          return the_input
        end
      end
    
      def print_instructions
        puts <<~INSTRUCTIONS
          YOU ARE THE CAPTAIN OF THE DESTROYER USS COMPUTER
          AN ENEMY SUB HAS BEEN CAUSING YOU TROUBLE.  YOUR
          MISSION IS TO DESTROY IT.
    
          SPECIFY DEPTH CHARGE EXPLOSION POINT WITH A
          TRIO OF NUMBERS -- THE FIRST TWO ARE THE
          SURFACE COORDINATES (X, Y):
              WEST < X < EAST
              SOUTH < Y < NORTH
    
          THE THIRD IS THE DEPTH (Z):
            SHALLOW < Z < DEEP
    
          GOOD LUCK !
    
        INSTRUCTIONS
      end
    
      def debug_game
        puts "@enemy_x: %d" % @enemy_x
        puts "@enemy_y: %d" % @enemy_y
        puts "@enemy_z: %d" % @enemy_z
        puts "@num_tries: %d" % @num_tries
        puts "@trial: %d" % @trial
        puts
      end
    
      def setup_game
        @search_area_dimension = get_input_positive_integer("DIMENSION OF SEARCH AREA: ")
    
        @num_tries = Integer(Math.log(@search_area_dimension) / Math.log(2) + 1)
        setup_enemy
      end
    
      def setup_enemy
        @enemy_x = rand(1..@search_area_dimension)
        @enemy_y = rand(1..@search_area_dimension)
        @enemy_z = rand(1..@search_area_dimension)
      end
    
      def game_loop
        for @trial in 1..@num_tries do
          output_game_status()
    
          @shot_x = get_input_positive_integer("X: ")
          @shot_y = get_input_positive_integer("Y: ")
          @shot_z = get_input_positive_integer("Z: ")
    
    
          distance = (@enemy_x - @shot_x).abs +
            (@enemy_y - @shot_y).abs +
            (@enemy_z - @shot_z).abs
    
          if distance == 0
            you_win
            return
          else
            missed_shot
          end
        end
    
        puts
    
        you_lose
      end
    
      def output_game_status
        puts "YOU HAVE %d SHOTS REMAINING." % @num_tries - @trial + 1
        puts "TRIAL \#%d" % @trial
      end
    
      def you_win
        puts "\nB O O M ! ! YOU FOUND IT IN %d TRIES!" % @trial
        puts
      end
    
      def missed_shot
        missed_directions = []
    
        if @shot_x > @enemy_x
          missed_directions.push('TOO FAR EAST')
        elsif @shot_x < @enemy_x
          missed_directions.push('TOO FAR WEST')
        end
    
        if @shot_y > @enemy_y
          missed_directions.push('TOO FAR NORTH')
        elsif @shot_y < @enemy_y
          missed_directions.push('TOO FAR SOUTH')
        end
    
        if @shot_z > @enemy_z
          missed_directions.push('TOO DEEP')
        elsif @shot_z < @enemy_z
          missed_directions.push('TOO SHALLOW')
        end
    
        puts "SONAR REPORTS SHOT WAS: "
        puts "\t#{missed_directions.join("\n\t")}"
      end
    
      def you_lose
        puts "YOU HAVE BEEN TORPEDOED!  ABANDON SHIP!"
        puts "THE SUBMARINE WAS AT %d %d %d" % [@enemy_x, @enemy_y, @enemy_z]
      end
    
      def get_input_another_game
        return get_input_y_or_n("ANOTHER GAME (Y OR N): ")
      end
    end
    
    game = DepthCharge.new
    game.run_game
    
    
    ================================================
    FILE: 31_Depth_Charge/rust/Cargo.toml
    ================================================
    [package]
    name = "rust"
    version = "0.1.0"
    edition = "2021"
    
    [dependencies]
    rand = "0.9.0"
    
    ================================================
    FILE: 31_Depth_Charge/rust/src/main.rs
    ================================================
    /** 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 X
    B 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; } } ================================================ FILE: 31_Depth_Charge/vbnet/DepthCharge.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30114.105 MinimumVisualStudioVersion = 10.0.40219.1 Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "DepthCharge", "DepthCharge.vbproj", "{7F8404FE-7CF2-46AC-B4D4-2CAB5EF3FB2F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {7F8404FE-7CF2-46AC-B4D4-2CAB5EF3FB2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7F8404FE-7CF2-46AC-B4D4-2CAB5EF3FB2F}.Debug|Any CPU.Build.0 = Debug|Any CPU {7F8404FE-7CF2-46AC-B4D4-2CAB5EF3FB2F}.Release|Any CPU.ActiveCfg = Release|Any CPU {7F8404FE-7CF2-46AC-B4D4-2CAB5EF3FB2F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal ================================================ FILE: 31_Depth_Charge/vbnet/DepthCharge.vbproj ================================================ Exe DepthCharge net6.0 16.9 ================================================ FILE: 31_Depth_Charge/vbnet/README.md ================================================ Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET) ================================================ FILE: 32_Diamond/README.md ================================================ ### Diamond This program fills an 8.5x11 piece of paper with diamonds (plotted on a hard-copy terminal, of course). The program asks for an odd number to be input in the range 5 to 31. The diamonds printed will be this number of characters high and wide. The number of diamonds across the page will vary from 12 for 5-character wide diamonds to 1 for a diamond 31-characters wide. You can change the content of the pattern if you wish. The program was written by David Ahl of Creative Computing. --- As published in Basic Computer Games (1978): - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=56) - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=71) Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html #### Porting Notes (please note any difficulties or challenges in porting here) ================================================ FILE: 32_Diamond/csharp/Diamond.csproj ================================================ Exe net6.0 10 enable enable ================================================ FILE: 32_Diamond/csharp/Diamond.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30114.105 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Diamond", "Diamond.csproj", "{44B406C8-70F0-4183-B19A-5B045A1AEBA4}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {44B406C8-70F0-4183-B19A-5B045A1AEBA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {44B406C8-70F0-4183-B19A-5B045A1AEBA4}.Debug|Any CPU.Build.0 = Debug|Any CPU {44B406C8-70F0-4183-B19A-5B045A1AEBA4}.Release|Any CPU.ActiveCfg = Release|Any CPU {44B406C8-70F0-4183-B19A-5B045A1AEBA4}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal ================================================ FILE: 32_Diamond/csharp/Pattern.cs ================================================ using System.Text; using static Diamond.Resources.Resource; namespace Diamond; internal class Pattern { private readonly IReadWrite _io; public Pattern(IReadWrite io) { _io = io; io.Write(Streams.Introduction); } public void Draw() { var diamondSize = _io.ReadNumber(Prompts.TypeNumber); _io.WriteLine(); var diamondCount = (int)(60 / diamondSize); var diamondLines = new List(GetDiamondLines(diamondSize)).AsReadOnly(); for (int patternRow = 0; patternRow < diamondCount; patternRow++) { for (int diamondRow = 0; diamondRow < diamondLines.Count; diamondRow++) { var line = new StringBuilder(); for (int patternColumn = 0; patternColumn < diamondCount; patternColumn++) { line.PadToLength((int)(patternColumn * diamondSize)).Append(diamondLines[diamondRow]); } _io.WriteLine(line); } } } public static IEnumerable GetDiamondLines(float size) { for (var i = 1; i <= size; i += 2) { yield return GetLine(i); } for (var i = size - 2; i >= 1; i -= 2) { yield return GetLine(i); } string GetLine(float i) => string.Concat( new string(' ', (int)(size - i) / 2), new string('C', Math.Min((int)i, 2)), new string('!', Math.Max(0, (int)i - 2))); } } ================================================ FILE: 32_Diamond/csharp/Program.cs ================================================ global using Games.Common.IO; using Diamond; new Pattern(new ConsoleIO()).Draw(); ================================================ FILE: 32_Diamond/csharp/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/) ================================================ FILE: 32_Diamond/csharp/Resources/Introduction.txt ================================================ Diamond Creative Computing Morristown, New Jersey ================================================ FILE: 32_Diamond/csharp/Resources/Resource.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; namespace Diamond.Resources; internal static class Resource { internal static class Streams { public static Stream Introduction => GetStream(); } internal static class Prompts { public static string TypeNumber => GetString(); } 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}'."); } ================================================ FILE: 32_Diamond/csharp/Resources/Rules.txt ================================================ Chomp is for 1 or more players (humans only). Here's how a board looks (this one is 5 by 7): 1 2 3 4 5 6 7 8 9 1 P * * * * * * 2 * * * * * * * 3 * * * * * * * 4 * * * * * * * 5 * * * * * * * The board is a big cookie - R rows high and C columns wide. You input R and C at the start. In the upper left corner of the cookie is a poison square (P). The one who chomps the poison square loses. To take a chomp, type the row and column of one of the squares on the cookie. All of the squares below and to the right of that square (including that square, too) disappear -- Chomp!! No fair chomping on squares that have already been chomped, or that are outside the original dimensions of the cookie. ================================================ FILE: 32_Diamond/csharp/Resources/TypeNumber.txt ================================================ For a pretty diamond pattern, type in an odd number between 5 and 21 ================================================ FILE: 32_Diamond/csharp/StringBuilderExtensions.cs ================================================ using System.Text; namespace Diamond; internal static class StringBuilderExtensions { internal static StringBuilder PadToLength(this StringBuilder builder, int length) => builder.Append(' ', length - builder.Length); } ================================================ FILE: 32_Diamond/diamond.bas ================================================ 1 PRINT TAB(33);"DIAMOND" 2 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 3 PRINT:PRINT:PRINT 4 PRINT "FOR A PRETTY DIAMOND PATTERN," 5 INPUT "TYPE IN AN ODD NUMBER BETWEEN 5 AND 21";R:PRINT 6 Q=INT(60/R):A$="CC" 8 FOR L=1 TO Q 10 X=1:Y=R:Z=2 20 FOR N=X TO Y STEP Z 25 PRINT TAB((R-N)/2); 28 FOR M=1 TO Q 29 C=1 30 FOR A=1 TO N 32 IF C>LEN(A$) THEN PRINT "!";:GOTO 50 34 PRINT MID$(A$,C,1); 36 C=C+1 50 NEXT A 53 IF M=Q THEN 60 55 PRINT TAB(R*M+(R-N)/2); 56 NEXT M 60 PRINT 70 NEXT N 83 IF X<>1 THEN 95 85 X=R-2:Y=1:Z=-2 90 GOTO 20 95 NEXT L 99 END ================================================ FILE: 32_Diamond/java/Diamond.java ================================================ import java.util.Scanner; /** * Game of Diamond *

    * Based on the BASIC game of Diamond here * https://github.com/coding-horror/basic-computer-games/blob/main/32%20Diamond/diamond.bas *

    * Note: The idea was to create a version of the 1970's BASIC game in Java, without introducing * new features - no additional text, error checking, etc has been added. * * Converted from BASIC to Java by Darren Cardenas. */ public class Diamond { private static final int LINE_WIDTH = 60; private static final String PREFIX = "CC"; private static final char SYMBOL = '!'; private final Scanner scan; // For user input public Diamond() { scan = new Scanner(System.in); } // End of constructor Diamond public void play() { showIntro(); startGame(); } // End of method play private void showIntro() { System.out.println(" ".repeat(32) + "DIAMOND"); System.out.println(" ".repeat(14) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println("\n\n"); } // End of method showIntro private void startGame() { int body = 0; int column = 0; int end = 0; int fill = 0; int increment = 2; int numPerSide = 0; int prefixIndex = 0; int row = 0; int start = 1; int userNum = 0; String lineContent = ""; // Get user input System.out.println("FOR A PRETTY DIAMOND PATTERN,"); System.out.print("TYPE IN AN ODD NUMBER BETWEEN 5 AND 21? "); userNum = scan.nextInt(); System.out.println(""); // Calcuate number of diamonds to be drawn on each side of screen numPerSide = (int) (LINE_WIDTH / userNum); end = userNum; // Begin loop through each row of diamonds for (row = 1; row <= numPerSide; row++) { // Begin loop through top and bottom halves of each diamond for (body = start; increment < 0 ? body >= end : body <= end; body += increment) { lineContent = ""; // Add whitespace while (lineContent.length() < ((userNum - body) / 2)) { lineContent += " "; } // Begin loop through each column of diamonds for (column = 1; column <= numPerSide; column++) { prefixIndex = 1; // Begin loop that fills each diamond with characters for (fill = 1; fill <= body; fill++) { // Right side of diamond if (prefixIndex > PREFIX.length()) { lineContent += SYMBOL; } // Left side of diamond else { lineContent += PREFIX.charAt(prefixIndex - 1); prefixIndex++; } } // End loop that fills each diamond with characters // Column finished if (column == numPerSide) { break; } // Column not finishd else { // Add whitespace while (lineContent.length() < (userNum * column + (userNum - body) / 2)) { lineContent += " "; } } } // End loop through each column of diamonds System.out.println(lineContent); } // End loop through top and bottom half of each diamond if (start != 1) { start = 1; end = userNum; increment = 2; } else { start = userNum - 2; end = 1; increment = -2; row--; } } // End loop through each row of diamonds } // End of method startGame public static void main(String[] args) { Diamond diamond = new Diamond(); diamond.play(); } // End of method main } // End of class Diamond ================================================ FILE: 32_Diamond/java/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Oracle Java](https://openjdk.java.net/) ================================================ FILE: 32_Diamond/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 32_Diamond/javascript/diamond.html ================================================ DIAMOND

    
    
    
    
    
    
    ================================================
    FILE: 32_Diamond/javascript/diamond.js
    ================================================
    // DIAMOND
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    // Main program
    async function main()
    {
        print(tab(33) + "DIAMOND\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        print("FOR A PRETTY DIAMOND PATTERN,\n");
        print("TYPE IN AN ODD NUMBER BETWEEN 5 AND 21");
        r = parseInt(await input());
        q = Math.floor(60 / r);
        as = "CC"
        x = 1;
        y = r;
        z = 2;
        for (l = 1; l <= q; l++) {
            for (n = x; z < 0 ? n >= y : n <= y; n += z) {
                str = "";
                while (str.length < (r - n) / 2)
                    str += " ";
                for (m = 1; m <= q; m++) {
                    c = 1;
                    for (a = 1; a <= n; a++) {
                        if (c > as.length)
                            str += "!";
                        else
                            str += as[c++ - 1];
                    }
                    if (m == q)
                        break;
                    while (str.length < r * m + (r - n) / 2)
                        str += " ";
                }
                print(str + "\n");
            }
            if (x != 1) {
                x = 1;
                y = r;
                z = 2;
            } else {
                x = r - 2;
                y = 1;
                z = -2;
                l--;
            }
        }
    }
    
    main();
    
    
    ================================================
    FILE: 32_Diamond/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/), structure inspired from [the Java port](https://github.com/coding-horror/basic-computer-games/blob/main/32_Diamond/java/Diamond.java).
    
    ### How to Run
    1. Install [kotlin command line](https://kotlinlang.org/docs/command-line.html) compiler from JetBrains.
    2. Compile with `kotlinc diamond.kt -include-runtime -d diamond.jar`
    3. Run with `java -jar diamond.jar`
    
    ### Changes from Original
    This version validates that user input is correct.
    
    
    
    ================================================
    FILE: 32_Diamond/kotlin/diamond.kt
    ================================================
    
    /**
     * Game of Diamond
     * 

    * Based on the BASIC game of Diamond * https://github.com/coding-horror/basic-computer-games/blob/main/32%20Diamond/diamond.bas *

    * * Changes From Original: Input is validated. * * Converted from BASIC to Kotlin by Martin Marconcini (@Gryzor) * Inspired in the Java code written by Darren Cardenas. */ fun main() { Diamond().startGame() } class Diamond { init { printIntro() } fun startGame() { var body: Int var end: Int var start: Int = 1 var row: Int = 1 var numPerRow: Int var increment: Int = 2 var lineContent: String var prefixIndex: Int printPrompt() // Read the user input val input = readLine() // Validate input val userInput: Int = try { input?.toInt() ?: -1 } catch (e: NumberFormatException) { -1 } if (!isValid(userInput)) { printInvalidInput() return } // Calculate how many diamonds can horizontally fit in the given space numPerRow = calculateDiamondsPerRow(userInput) end = userInput // Loop throw rows of Diamonds while (row <= numPerRow) { body = start while (canLoop(increment, body, end)) { lineContent = "" // Add white spaces to the "left" of the leftmost diamond. while (lineContent.length < ((userInput - body) / 2)) { lineContent += " " } // Begin loop through each column of diamonds for (col in 1..numPerRow) { prefixIndex = 1 // Begin loop that fills each diamond with characters (not whitespace) for (fill in 1..body) { // Right side of diamond, if the index is greater than the prefix, put a Symbol. if (prefixIndex > PREFIX.length) { lineContent += SYMBOL } // Left side of diamond, pick a Prefix character (-1 since it starts at 0) else { lineContent += PREFIX[prefixIndex - 1] prefixIndex++ } }// End loop that fills each diamond with characters // Is Column finished? if (col == numPerRow) { break } // Column is not finished... else { // Add whitespace on the "right" side of the current diamond, and fill the "left" side of the // next; doesn't fill the space to the right of the rightmost diamond. while (lineContent.length < (userInput * col + (userInput - body) / 2)) { lineContent += " " } } }// End loop through each column of diamonds // Print the current Line println(lineContent) // Increment the body that moves body += increment } //end While Loop throw rows of Diamonds // Increment the current Row of diamonds. row++ if (start != 1) { start = 1 end = userInput increment = 2 } else { // We're rendering the "bottom half" of the total rendering. // Alter the parameters, and decrease the row number so the logic can loop again. start = userInput - 2 end = 1 increment = -2 row-- } } // End loop through each row of diamonds } private fun canLoop(increment: Int, body: Int, end: Int): Boolean = if (increment < 0) body >= end else body <= end private fun calculateDiamondsPerRow(totalDiamonds: Int): Int = LINE_WIDTH / totalDiamonds private fun isValid(input: Int): Boolean = (input in 5..21) && (input % 2 != 0) private fun printInvalidInput() = println("Invalid input") private fun printPrompt() { println( """ FOR A PRETTY DIAMOND PATTERN TYPE IN AN ODD NUMBER BETWEEN 5 AND 21? """.trimIndent() ) println() } private fun printIntro() { println(" ".repeat(32) + "DIAMOND") println(" ".repeat(14) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") println() println() } companion object { const val LINE_WIDTH = 60 const val PREFIX = "CC" const val SYMBOL = "!" } } ================================================ FILE: 32_Diamond/lua/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Lua](https://www.lua.org/) ================================================ FILE: 32_Diamond/perl/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Perl](https://www.perl.org/) ================================================ FILE: 32_Diamond/perl/diamond.pl ================================================ #!/usr/bin/perl use strict; ################ # PORTING NOTES: # * In basic "Tab" function are not spaces, but absolute col position on screen. # * It was too dificult to port this one, couldn't figure out the original algorithm. # * So the algorithm was remake. # print ' 'x 33 . "DIAMOND\n"; print ' 'x 15 . "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n"; print "\n"; print "\n"; print "\n"; print "FOR A PRETTY DIAMOND PATTERN,\n"; print "TYPE IN AN ODD NUMBER BETWEEN 5 AND 21? "; chomp(my $R = ); print "\n"; my $Wid= int(60/$R)+1; my $Dia="CC". "!" x ($R-2); for (my $J=1; $J<$Wid; $J++) { for (my $K=1; $K<($R+2)*2-4; $K+=2) { my $Size= $K; if ($K>$R) { $Size=$R+($R-$K); } my $Chunk= substr($Dia, 0, $Size); for (my $L=1; $L<$Wid; $L++) { my $Space= " " x (($R-$Size)/2); if ($L>1) { $Space.=$Space; } print $Space.$Chunk; } print "\n"; } } exit; ================================================ FILE: 32_Diamond/python/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Python](https://www.python.org/about/) ================================================ FILE: 32_Diamond/python/diamond.py ================================================ """ DIAMOND Prints pretty diamond patterns to the screen. Ported by Dave LeCompte """ def print_diamond(begin_width, end_width, step, width, count) -> None: edge_string = "CC" fill = "!" n = begin_width while True: line_buffer = " " * ((width - n) // 2) for across in range(count): for a in range(n): line_buffer += fill if a >= len(edge_string) else edge_string[a] line_buffer += " " * ( (width * (across + 1) + (width - n) // 2) - len(line_buffer) ) print(line_buffer) if n == end_width: return n += step def main() -> None: print(" " * 33, "DIAMOND") print(" " * 15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n") print("FOR A PRETTY DIAMOND PATTERN,") print("TYPE IN AN ODD NUMBER BETWEEN 5 AND 21") width = int(input()) print() PAGE_WIDTH = 60 count = PAGE_WIDTH // width for _down in range(count): print_diamond(1, width, 2, width, count) print_diamond(width - 2, 1, -2, width, count) print() print() if __name__ == "__main__": main() ================================================ FILE: 32_Diamond/ruby/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Ruby](https://www.ruby-lang.org/en/) ================================================ FILE: 32_Diamond/ruby/diamond.rb ================================================ def intro print " DIAMOND CREATIVE COMPUTING MORRISTOWN, NEW JERSEY FOR A PRETTY DIAMOND PATTERN, TYPE IN AN ODD NUMBER BETWEEN 5 AND 21? " end def get_facets while true number = gets.chomp return number.to_i if /^\d+$/.match(number) puts "!NUMBER EXPECTED - RETRY INPUT LINE" print "? " end end def get_diamond_lines(facets) spacers = (facets - 1) / 2 lines = [' ' * spacers + 'C' + ' ' * spacers] lines += (1...facets).step(2).to_a.map { |v| spacers -= 1 ' ' * spacers + 'CC' + '!' * v + ' ' * spacers } lines + lines[0..-2].reverse end def draw_diamonds(lines) repeat = 60 / lines[0].length (0...repeat).each { lines.map { |l| l * repeat }.each { |l| puts l } } end def main intro facets = get_facets puts lines = get_diamond_lines(facets) draw_diamonds(lines) end trap "SIGINT" do puts; exit 130 end main ================================================ FILE: 32_Diamond/rust/Cargo.toml ================================================ [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] ================================================ FILE: 32_Diamond/rust/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Rust](https://www.rust-lang.org/) by Anthony Rubick [AnthonyMichaelTDM](https://github.com/AnthonyMichaelTDM) ================================================ FILE: 32_Diamond/rust/src/lib.rs ================================================ /* lib.rs contains all the logic of the program */ use std::{error::Error, fmt::Display, str::FromStr, io::{self, Write}}; /// handles setup for the game pub struct Config { diamond_size: isize, } impl Config { /// creates and returns a new Config from user input pub fn new() -> Result> { //DATA let mut config: Config = Config { diamond_size: 0 }; //get data from user input //get num players println!("FOR A PRETTY DIAMOND PATTERN,"); //input looop config.diamond_size = loop { match get_number_from_input("TYPE IN AN ODD NUMBER BETWEEN 5 AND 31 ", 5, 31) { Ok(num) => { //ensure num is odd if num%2 == 0 {continue;} else {break num;} }, Err(e) => { eprintln!("{}",e); continue; }, } }; //return new config return Ok(config); } } /// run the program pub fn run(config: &Config) -> Result<(), Box> { //DATA let line_width: isize = 60; let padding: char = 'C'; let pixel_width: isize = 1; let filling: char = '!'; let border: char = '#'; let width_of_full_diamonds_in_line = (line_width/config.diamond_size) * config.diamond_size; //print top border println!("{}", n_chars(width_of_full_diamonds_in_line+2, border)); //print out diamonds for row in 0..width_of_full_diamonds_in_line { print_diamond_line(config.diamond_size, row, line_width, pixel_width, padding, filling, border); } //print bottom border println!("{}", n_chars(width_of_full_diamonds_in_line+2, border)); //return to main Ok(()) } /// prints the next line of diamonds fn print_diamond_line(diamond_width: isize,row: isize, line_width:isize, pixel_width:isize, padding:char, filling:char, border:char) { //DATA let diamonds_per_row = (line_width/pixel_width) / diamond_width; //let row = row % (diamonds_per_row - 1); let padding_amount; //total amount of padding before and after the filling of each diamond in this row let filling_amount; //amount of "diamond" in each diamond in this row //calculate padding padding_amount = (2 * ( (row%(diamond_width-1)) - (diamond_width/2))).abs(); //calculate filling filling_amount = -padding_amount + diamond_width; //print border before every row print!("{}", border); //for every diamond in this row: for _diamond in 0..diamonds_per_row { //print leading padding print!("{}", n_chars( pixel_width * padding_amount/2, padding ) ); //print filling print!("{}", n_chars( pixel_width * filling_amount , filling ) ); //print trailing padding print!("{}", n_chars( pixel_width * padding_amount/2, padding ) ); } //print border after every row print!("{}", border); //new line println!(""); } /// returns n of the passed character, put into a string fn n_chars(n:isize, character: char) -> String { let mut output = String::new(); for _i in 0..n { output.push(character); } output } /// gets a string from user input fn get_string_from_user_input(prompt: &str) -> Result> { //DATA let mut raw_input = String::new(); //print prompt print!("{}", prompt); //make sure it's printed before getting input io::stdout().flush().unwrap(); //read user input from standard input, and store it to raw_input, then return it or an error as needed raw_input.clear(); //clear input match io::stdin().read_line(&mut raw_input) { Ok(_num_bytes_read) => return Ok(String::from(raw_input.trim())), Err(err) => return Err(format!("ERROR: CANNOT READ INPUT!: {}", err).into()), } } /// generic function to get a number from the passed string (user input) /// pass a min lower than the max to have minimun and maximun bounds /// pass a min higher than the max to only have a minumum bound /// pass a min equal to the max to only have a maximun bound /// /// Errors: /// no number on user input fn get_number_from_input(prompt: &str, min:T, max:T) -> Result> { //DATA let raw_input: String; let processed_input: String; //input looop raw_input = loop { match get_string_from_user_input(prompt) { Ok(input) => break input, Err(e) => { eprintln!("{}",e); continue; }, } }; //filter out num-numeric characters from user input processed_input = raw_input.chars().filter(|c| c.is_numeric()).collect(); //from input, try to read a number match processed_input.trim().parse() { Ok(i) => { //what bounds must the input fall into if min < max { //have a min and max bound: [min,max] if i >= min && i <= max {//is input valid, within bounds return Ok(i); //exit the loop with the value i, returning it } else { //print error message specific to this case return Err(format!("ONLY BETWEEN {} AND {}, PLEASE!", min, max).into()); } } else if min > max { //only a min bound: [min, infinity) if i >= min { return Ok(i); } else { return Err(format!("NO LESS THAN {}, PLEASE!", min).into()); } } else { //only a max bound: (-infinity, max] if i <= max { return Ok(i); } else { return Err(format!("NO MORE THAN {}, PLEASE!", max).into()); } } }, Err(_e) => return Err(format!("Error: couldn't find a valid number in {}",raw_input).into()), } } ================================================ FILE: 32_Diamond/rust/src/main.rs ================================================ use std::process; //allows for some better error handling mod lib; use lib::Config; /// main function /// responsibilities: /// - Calling the command line logic with the argument values /// - Setting up any other configuration /// - Calling a run function in lib.rs /// - Handling the error if run returns an error fn main() { //greet user welcome(); // set up other configuration let config = Config::new().unwrap_or_else(|err| { eprintln!("Problem configuring program: {}", err); process::exit(1); }); // run the program if let Err(e) = lib::run(&config) { eprintln!("Application Error: {}", e); //use the eprintln! macro to output to standard error process::exit(1); //exit the program with an error code } //end of program println!("THANKS FOR PLAYING!"); } /// welcome message fn welcome() { print!(" DIAMOND CREATIVE COMPUTING MORRISTOWN, NEW JERSEY "); } ================================================ FILE: 32_Diamond/vbnet/Diamond.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30114.105 MinimumVisualStudioVersion = 10.0.40219.1 Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Diamond", "Diamond.vbproj", "{87084ED3-F01C-4D7E-8BE7-10C2F3FA148F}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {87084ED3-F01C-4D7E-8BE7-10C2F3FA148F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {87084ED3-F01C-4D7E-8BE7-10C2F3FA148F}.Debug|Any CPU.Build.0 = Debug|Any CPU {87084ED3-F01C-4D7E-8BE7-10C2F3FA148F}.Release|Any CPU.ActiveCfg = Release|Any CPU {87084ED3-F01C-4D7E-8BE7-10C2F3FA148F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal ================================================ FILE: 32_Diamond/vbnet/Diamond.vbproj ================================================ Exe Diamond net6.0 16.9 ================================================ FILE: 32_Diamond/vbnet/README.md ================================================ Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET) ================================================ FILE: 33_Dice/README.md ================================================ ### Dice Not exactly a game, this program simulates rolling a pair of dice a large number of times and prints out the frequency distribution. You simply input the number of rolls. It is interesting to see how many rolls are necessary to approach the theoretical distribution: | | | | |---|------|------------| | 2 | 1/36 | 2.7777...% | | 3 | 2/36 | 5.5555...% | | 4 | 3/36 | 8.3333...% | etc. Daniel Freidus wrote this program while in the seventh grade at Harrison Jr-Sr High School, Harrison, New York. --- As published in Basic Computer Games (1978): - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=57) - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=72) Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html #### Porting Notes (please note any difficulties or challenges in porting here) ================================================ FILE: 33_Dice/csharp/Dice.csproj ================================================ Exe net5.0 ================================================ FILE: 33_Dice/csharp/Dice.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30114.105 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dice", "Dice.csproj", "{D136AC51-DDC0-471A-8EAC-D3C772FA7D51}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {D136AC51-DDC0-471A-8EAC-D3C772FA7D51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D136AC51-DDC0-471A-8EAC-D3C772FA7D51}.Debug|Any CPU.Build.0 = Debug|Any CPU {D136AC51-DDC0-471A-8EAC-D3C772FA7D51}.Release|Any CPU.ActiveCfg = Release|Any CPU {D136AC51-DDC0-471A-8EAC-D3C772FA7D51}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal ================================================ FILE: 33_Dice/csharp/Game.cs ================================================ using System; using System.Linq; namespace BasicComputerGames.Dice { public class Game { private readonly RollGenerator _roller = new RollGenerator(); public void GameLoop() { DisplayIntroText(); // RollGenerator.ReseedRNG(1234); // hard-code seed for repeatabilty during testing do { int numRolls = GetInput(); var counter = CountRolls(numRolls); DisplayCounts(counter); } while (TryAgain()); } private void DisplayIntroText() { Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine("Dice"); Console.WriteLine("Creating Computing, Morristown, New Jersey."); Console.WriteLine(); Console.ForegroundColor = ConsoleColor.DarkGreen; Console.WriteLine("Original code by Danny Freidus."); Console.WriteLine("Originally published in 1978 in the book 'Basic Computer Games' by David Ahl."); Console.WriteLine("Modernized and converted to C# in 2021 by James Curran (noveltheory.com)."); Console.WriteLine(); Console.ForegroundColor = ConsoleColor.Gray; Console.WriteLine("This program simulates the rolling of a pair of dice."); Console.WriteLine("You enter the number of times you want the computer to"); Console.WriteLine("'roll' the dice. Watch out, very large numbers take"); Console.WriteLine("a long time. In particular, numbers over 10 million."); Console.WriteLine(); Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine("Press any key start the game."); Console.ReadKey(true); } private int GetInput() { int num = -1; Console.WriteLine(); do { Console.WriteLine(); Console.Write("How many rolls? "); } while (!Int32.TryParse(Console.ReadLine(), out num)); return num; } private void DisplayCounts(int[] counter) { Console.WriteLine(); Console.WriteLine($"\tTotal\tTotal Number"); Console.WriteLine($"\tSpots\tof Times"); Console.WriteLine($"\t===\t========="); for (var n = 1; n < counter.Length; ++n) { Console.WriteLine($"\t{n + 1,2}\t{counter[n],9:#,0}"); } Console.WriteLine(); } private int[] CountRolls(int x) { var counter = _roller.Rolls().Take(x).Aggregate(new int[12], (cntr, r) => { cntr[r.die1 + r.die2 - 1]++; return cntr; }); return counter; } ///

    /// Prompt the player to try again, and wait for them to press Y or N. /// /// Returns true if the player wants to try again, false if they have finished playing. private bool TryAgain() { Console.ForegroundColor = ConsoleColor.White; Console.WriteLine("Would you like to try again? (Press 'Y' for yes or 'N' for no)"); Console.ForegroundColor = ConsoleColor.Yellow; Console.Write("> "); char pressedKey; // Keep looping until we get a recognised input do { // Read a key, don't display it on screen ConsoleKeyInfo key = Console.ReadKey(true); // Convert to upper-case so we don't need to care about capitalisation pressedKey = Char.ToUpper(key.KeyChar); // Is this a key we recognise? If not, keep looping } while (pressedKey != 'Y' && pressedKey != 'N'); // Display the result on the screen Console.WriteLine(pressedKey); // Return true if the player pressed 'Y', false for anything else. return (pressedKey == 'Y'); } } } ================================================ FILE: 33_Dice/csharp/Program.cs ================================================ namespace BasicComputerGames.Dice { public class Program { public static void Main(string[] args) { // Create an instance of our main Game class Game game = new Game(); // Call its GameLoop function. This will play the game endlessly in a loop until the player chooses to quit. game.GameLoop(); } } } ================================================ FILE: 33_Dice/csharp/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/) by James Curran (http://www.noveltheory.com) ================================================ FILE: 33_Dice/csharp/RollGenerator.cs ================================================ using System; using System.Collections.Generic; namespace BasicComputerGames.Dice { public class RollGenerator { static Random _rnd = new Random(); public static void ReseedRNG(int seed) => _rnd = new Random(seed); public IEnumerable<(int die1, int die2)> Rolls() { while (true) { yield return (_rnd.Next(1, 7), _rnd.Next(1, 7)); } } } } ================================================ FILE: 33_Dice/dice.bas ================================================ 2 PRINT TAB(34);"DICE" 4 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 6 PRINT:PRINT:PRINT 10 DIM F(12) 20 REM DANNY FREIDUS 30 PRINT "THIS PROGRAM SIMULATES THE ROLLING OF A" 40 PRINT "PAIR OF DICE." 50 PRINT "YOU ENTER THE NUMBER OF TIMES YOU WANT THE COMPUTER TO" 60 PRINT "'ROLL' THE DICE. WATCH OUT, VERY LARGE NUMBERS TAKE" 70 PRINT "A LONG TIME. IN PARTICULAR, NUMBERS OVER 5000." 80 FOR Q=1 TO 12 90 F(Q)=0 100 NEXT Q 110 PRINT:PRINT "HOW MANY ROLLS"; 120 INPUT X 130 FOR S=1 TO X 140 A=INT(6*RND(1)+1) 150 B=INT(6*RND(1)+1) 160 R=A+B 170 F(R)=F(R)+1 180 NEXT S 185 PRINT 190 PRINT "TOTAL SPOTS","NUMBER OF TIMES" 200 FOR V=2 TO 12 210 PRINT V,F(V) 220 NEXT V 221 PRINT 222 PRINT:PRINT "TRY AGAIN"; 223 INPUT Z$ 224 IF Z$="YES" THEN 80 240 END ================================================ FILE: 33_Dice/java/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Oracle Java](https://openjdk.java.net/) ================================================ FILE: 33_Dice/java/src/Dice.java ================================================ import java.util.Arrays; import java.util.Scanner; /** * Game of Dice *

    * Based on the Basic game of Dice here * https://github.com/coding-horror/basic-computer-games/blob/main/33%20Dice/dice.bas *

    * Note: The idea was to create a version of the 1970's Basic game in Java, without introducing * new features - no additional text, error checking, etc has been added. */ public class Dice { // Used for keyboard input private final Scanner kbScanner; private enum GAME_STATE { START_GAME, INPUT_AND_CALCULATE, RESULTS, GAME_OVER } // Current game state private GAME_STATE gameState; private int[] spots; public Dice() { kbScanner = new Scanner(System.in); gameState = GAME_STATE.START_GAME; } /** * Main game loop */ public void play() { do { switch (gameState) { case START_GAME: intro(); spots = new int[12]; gameState = GAME_STATE.INPUT_AND_CALCULATE; break; case INPUT_AND_CALCULATE: int howManyRolls = displayTextAndGetNumber("HOW MANY ROLLS? "); for (int i = 0; i < howManyRolls; i++) { int diceRoll = (int) (Math.random() * 6 + 1) + (int) (Math.random() * 6 + 1); // save dice roll in zero based array spots[diceRoll - 1]++; } gameState = GAME_STATE.RESULTS; break; case RESULTS: System.out.println("TOTAL SPOTS" + simulateTabs(8) + "NUMBER OF TIMES"); for (int i = 1; i < 12; i++) { // show output using zero based array System.out.println(simulateTabs(5) + (i + 1) + simulateTabs(20) + spots[i]); } System.out.println(); if (yesEntered(displayTextAndGetInput("TRY AGAIN? "))) { gameState = GAME_STATE.START_GAME; } else { gameState = GAME_STATE.GAME_OVER; } break; } } while (gameState != GAME_STATE.GAME_OVER); } private void intro() { System.out.println(simulateTabs(34) + "DICE"); System.out.println(simulateTabs(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println(); System.out.println("THIS PROGRAM SIMULATES THE ROLLING OF A"); System.out.println("PAIR OF DICE."); System.out.println("YOU ENTER THE NUMBER OF TIMES YOU WANT THE COMPUTER TO"); System.out.println("'ROLL' THE DICE. WATCH OUT, VERY LARGE NUMBERS TAKE"); System.out.println("A LONG TIME. IN PARTICULAR, NUMBERS OVER 5000."); } /* * Print a message on the screen, then accept input from Keyboard. * Converts input to an Integer * * @param text message to be displayed on screen. * @return what was typed by the player. */ private int displayTextAndGetNumber(String text) { return Integer.parseInt(displayTextAndGetInput(text)); } /* * Print a message on the screen, then accept input from Keyboard. * * @param text message to be displayed on screen. * @return what was typed by the player. */ private String displayTextAndGetInput(String text) { System.out.print(text); return kbScanner.next(); } /** * Checks whether player entered Y or YES to a question. * * @param text player string from kb * @return true of Y or YES was entered, otherwise false */ private boolean yesEntered(String text) { return stringIsAnyValue(text, "Y", "YES"); } /** * Check whether a string equals one of a variable number of values * Useful to check for Y or YES for example * Comparison is case insensitive. * * @param text source string * @param values a range of values to compare against the source string * @return true if a comparison was found in one of the variable number of strings passed */ private boolean stringIsAnyValue(String text, String... values) { return Arrays.stream(values).anyMatch(str -> str.equalsIgnoreCase(text)); } /** * Simulate the old basic tab(xx) command which indented text by xx spaces. * * @param spaces number of spaces required * @return String with number of spaces */ private String simulateTabs(int spaces) { char[] spacesTemp = new char[spaces]; Arrays.fill(spacesTemp, ' '); return new String(spacesTemp); } } ================================================ FILE: 33_Dice/java/src/DiceGame.java ================================================ public class DiceGame { public static void main(String[] args) { Dice dice = new Dice(); dice.play(); } } ================================================ FILE: 33_Dice/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 33_Dice/javascript/dice.html ================================================ DICE

    
    
    
    
    
    
    ================================================
    FILE: 33_Dice/javascript/dice.js
    ================================================
    // DICE
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    // Main program
    async function main()
    {
        print(tab(34) + "DICE\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        f = [];
        // Danny Freidus
        print("THIS PROGRAM SIMULATES THE ROLLING OF A\n");
        print("PAIR OF DICE.\n");
        print("YOU ENTER THE NUMBER OF TIMES YOU WANT THE COMPUTER TO\n");
        print("'ROLL' THE DICE.  WATCH OUT, VERY LARGE NUMBERS TAKE\n");
        print("A LONG TIME.  IN PARTICULAR, NUMBERS OVER 5000.\n");
        do {
            for (q = 1; q <= 12; q++)
                f[q] = 0;
            print("\n");
            print("HOW MANY ROLLS");
            x = parseInt(await input());
            for (s = 1; s <= x; s++) {
                a = Math.floor(Math.random() * 6 + 1);
                b = Math.floor(Math.random() * 6 + 1);
                r = a + b;
                f[r]++;
            }
            print("\n");
            print("TOTAL SPOTS\tNUMBER OF TIMES\n");
            for (v = 2; v <= 12; v++) {
                print("\t" + v + "\t" + f[v] + "\n");
            }
            print("\n");
            print("\n");
            print("TRY AGAIN");
            str = await input();
        } while (str.substr(0, 1) == "Y") ;
    }
    
    main();
    
    
    ================================================
    FILE: 33_Dice/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 33_Dice/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/) by Alex Conconi
    
    ---
    
    ### Porting notes for Lua
    
    - This is a straightfoward port with only minor modifications for input
    validation and text formatting.
    
    - The "Try again?" question accepts 'y', 'yes', 'n', 'no' (case insensitive),
    whereas the original BASIC version defaults to no unless 'YES' is typed.
    
    - The "How many rolls?" question presents a more user friendly message
    in case of invalid input.
    
    
    ================================================
    FILE: 33_Dice/lua/dice.lua
    ================================================
    --[[
    Dice
    
    From: BASIC Computer Games (1978)
    Edited by David H. Ahl
    
        "Not exactly a game, this program simulates rolling
        a pair of dice a large number of times and prints out
        the frequency distribution.  You simply input the
        number of rolls.  It is interesting to see how many
        rolls are necessary to approach the theoretical
        distribution:
    
        2  1/36  2.7777...%
        3  2/36  5.5555...%
        4  3/36  8.3333...%
        etc.
    
        "Daniel Freidus wrote this program while in the
         seventh grade at Harrison Jr-Sr High School,
        Harrison, New York."
    
    
    Lua port by Alex Conconi, 2022.
    ]]--
    
    
    local function print_intro()
        print("\n" .. string.rep(" ", 19) .. "Dice")
        print("Creative Computing  Morristown, New Jersey\n\n")
        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.")
    end
    
    
    local function ask_how_many_rolls()
        while true do
            -- Print prompt and read a valid number from stdin
            print("\nHow many rolls?")
            local num_rolls = tonumber(io.stdin:read("*l"))
            if num_rolls then
                return num_rolls
            else
                print("Please enter a valid number.")
            end
        end
    end
    
    
    local function ask_try_again()
        while true do
            -- Print prompt and read a yes/no answer from stdin,
            -- accepting only 'yes', 'y', 'no' or 'n' (case insensitive)
            print("\nTry again? ([y]es / [n]o)")
            local answer = string.lower(io.stdin:read("*l"))
            if answer == "yes" or  answer == "y" then
                return true
            elseif answer == "no" or answer == "n" then
                return false
            else
                print("Please answer '[y]es' or '[n]o'.")
            end
        end
    end
    
    
    local function roll_dice(num_rolls)
        -- Initialize a table to track counts of roll outcomes
        local counts = {}
        for i=2, 12 do
            counts[i] = 0
        end
    
        -- Roll the dice num_rolls times and update outcomes counts accordingly
        for _=1, num_rolls do
            local roll_total = math.random(1, 6) + math.random(1, 6)
            counts[roll_total] = counts[roll_total] + 1
        end
    
        return counts
    end
    
    
    local function print_results(counts)
        print("\nTotal Spots   Number of Times")
        for roll_total, count in pairs(counts) do
            print(string.format(" %-14d%d", roll_total, count))
        end
    end
    
    
    local function dice_main()
        print_intro()
    
        -- initialize the random number generator
        math.randomseed(os.time())
    
        -- main game loop
        local keep_playing = true
        while keep_playing do
            local num_rolls = ask_how_many_rolls()
            local counts = roll_dice(num_rolls)
            print_results(counts)
            keep_playing = ask_try_again()
        end
    end
    
    
    dice_main()
    
    
    ================================================
    FILE: 33_Dice/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 33_Dice/perl/dice.pl
    ================================================
    #!/usr/bin/perl
    use strict;
    
    
    print ' 'x 34 . "DICE\n";
    print ' 'x 15 . "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n";
    print "\n\n\n";
    my @F;
    
    #REM DANNY FREIDUS;
    print "THIS PROGRAM SIMULATES THE ROLLING OF A\n";
    print "PAIR OF DICE.\n";
    print "YOU ENTER THE NUMBER OF TIMES YOU WANT THE COMPUTER TO\n";
    print "'ROLL' THE DICE. WATCH OUT, VERY LARGE NUMBERS TAKE\n";
    print "A LONG TIME. IN PARTICULAR, NUMBERS OVER 5000.\n";
    
    my $X;
    my $Z;
    do {
    	for (my $Q=1; $Q<=12; $Q++) {
    		$F[$Q]=0;
    		}
    	print "\n"; print "HOW MANY ROLLS";
    	print "? "; chomp($X = );
    	for (my $S=1; $S<=$X; $S++) {
    		my $A=int(6*rand(1)+1);
    		my $B=int(6*rand(1)+1);
    		my $R=$A+$B;
    		$F[$R]=$F[$R]+1;
    		}
    	print "\n";
    	print "TOTAL SPOTS\tNUMBER OF TIMES\n";
    	for (my $V=2; $V<=12; $V++) {
    		print "$V\t\t$F[$V]\n";
    		}
    	print "\n";
    	print "\n"; print "TRY AGAIN";
    	print "? "; chomp($Z = );
    	} until (uc($Z) ne "YES");
    exit;
    
    
    ================================================
    FILE: 33_Dice/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 33_Dice/python/dice.py
    ================================================
    """
    Dice
    
    From: BASIC Computer Games (1978)
          Edited by David H. Ahl
    
    "Not exactly a game, this program simulates rolling
     a pair of dice a large number of times and prints out
     the frequency distribution.  You simply input the
     number of rolls.  It is interesting to see how many
     rolls are necessary to approach the theoretical
     distribution:
    
     2  1/36  2.7777...%
     3  2/36  5.5555...%
     4  3/36  8.3333...%
       etc.
    
    "Daniel Freidus wrote this program while in the
     seventh grade at Harrison Jr-Sr High School,
     Harrison, New York."
    
    Python port by Jeff Jetton, 2019
    """
    
    import random
    
    
    def main() -> None:
        # We'll track counts of roll outcomes in a 13-element list.
        # The first two indices (0 & 1) are ignored, leaving just
        # the indices that match the roll values (2 through 12).
        freq = [0] * 13
    
        # Display intro text
        print("\n                   Dice")
        print("Creative Computing  Morristown, New Jersey")
        print("\n\n")
        # "Danny Freidus"
        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.")
    
        still_playing = True
        while still_playing:
            print()
            n = int(input("How many rolls? "))
    
            # Roll the dice n times
            for _ in range(n):
                die1 = random.randint(1, 6)
                die2 = random.randint(1, 6)
                roll_total = die1 + die2
                freq[roll_total] += 1
    
            # Display final results
            print("\nTotal Spots   Number of Times")
            for i in range(2, 13):
                print(" %-14d%d" % (i, freq[i]))
    
            # Keep playing?
            print()
            response = input("Try again? ")
            if len(response) > 0 and response.upper()[0] == "Y":
                # Clear out the frequency list
                freq = [0] * 13
            else:
                # Exit the game loop
                still_playing = False
    
    
    if __name__ == "__main__":
        main()
    
    ########################################################
    #
    # Porting Notes
    #
    #   A fairly straightforward port.  The only change is
    #   in the handling of the user's "try again" response.
    #   The original program only continued if the user
    #   entered "YES", whereas this version will continue
    #   if any word starting with "Y" or "y" is given.
    #
    #   The instruction text--which, like all these ports,
    #   was taken verbatim from the original listing--is
    #   charmingly quaint in its dire warning against
    #   setting the number of rolls too high.  At the time
    #   of this writing, on a fairly slow computer, a
    #   5000-roll run typically clocks in at well under
    #   1/10 of a second!
    #
    #
    # Ideas for Modifications
    #
    #   Have the results include a third column showing
    #   the percent of rolls each count represents.  Or
    #   (better yet) print a low-fi bar graph using
    #   rows of asterisks to represent relative values,
    #   with each asterisk representing one percent,
    #   for example.
    #
    #   Add a column showing the theoretically expected
    #   percentage, for comparison.
    #
    #   Keep track of how much time the series of rolls
    #   takes and add that info to the final report.
    #
    #   What if three (or four, or five...) dice were
    #   rolled each time?
    #
    ########################################################
    
    
    ================================================
    FILE: 33_Dice/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 33_Dice/ruby/dice.rb
    ================================================
    def intro
      puts "                                  DICE
                   CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY
    
    
    
    THIS PROGRAM SIMULATES THE ROLLING OF A
    PAIR OF DICE.
    YOU ENTER THE NUMBER OF TIMES YOU WANT THE COMPUTER TO
    'ROLL' THE DICE.  WATCH OUT, VERY LARGE NUMBERS TAKE
    A LONG TIME.  IN PARTICULAR, NUMBERS OVER 5000.
    
    "
    end
    
    def get_rolls
      while true
        number = gets.chomp
        return number.to_i if /^\d+$/.match(number)
        puts "!NUMBER EXPECTED - RETRY INPUT LINE"
        print "? "
      end
    end
    
    def dice_roll = rand(6) + 1 # ruby 3, woot!
    
    def print_rolls(rolls)
      values = Array.new(11, 0)
      (1..rolls).each { values[dice_roll + dice_roll - 2] += 1 }
      puts "\nTOTAL SPOTS   NUMBER OF TIMES"
      (0..10).each { |k| puts " %-2d            %-2d" % [k + 2, values[k]] }
    end
    
    def main
      intro
      loop do
        print "HOW MANY ROLLS? "
        rolls = get_rolls
    
        print_rolls(rolls)
    
        print "\n\nTRY AGAIN? "
        option = (gets || '').chomp.upcase
        break unless option == 'YES'
        puts
      end
    end
    
    trap "SIGINT" do puts; exit 130 end
    
    main
    
    
    ================================================
    FILE: 33_Dice/rust/Cargo.toml
    ================================================
    [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"
    
    
    ================================================
    FILE: 33_Dice/rust/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Rust](https://www.rust-lang.org/)
    
    
    ================================================
    FILE: 33_Dice/rust/src/main.rs
    ================================================
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //
    // Dice
    //
    // From: BASIC Computer Games (1978)
    //       Edited by David H. Ahl
    //
    // "Not exactly a game, this program simulates rolling
    //  a pair of dice a large number of times and prints out
    //  the frequency distribution.  You simply input the
    //  number of rolls.  It is interesting to see how many
    //  rolls are necessary to approach the theoretical
    //  distribution:
    //
    //  2  1/36  2.7777...%
    //  3  2/36  5.5555...%
    //  4  3/36  8.3333...%
    //    etc.
    //
    // "Daniel Freidus wrote this program while in the
    //  seventh grade at Harrison Jr-Sr High School,
    //  Harrison, New York."
    //
    // Rust Port by Jay, 2022
    //
    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    
    use rand::Rng;
    use std::io::{self, Write};
    
    fn main() {
        let mut frequency: [i32; 13] = [0; 13];
        println!(
            "{: >38}\n{: >57}\n\n",
            "DICE", "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
        );
        // DANNY FREIDUS
        println!("THIS PROGRAM SIMULATES THE ROLLING OF A");
        println!("PAIR OF DICE.");
        println!("YOU ENTER THE NUMBER OF TIMES YOU WANT THE COMPUTER TO");
        println!("'ROLL' THE DICE.  WATCH OUT, VERY LARGE NUMBERS TAKE");
        println!("A LONG TIME.  IN PARTICULAR, NUMBERS OVER 5000.");
        let mut playing = true;
        while playing {
            let n = match readinput(&"HOW MANY ROLLS").trim().parse::() {
                Ok(num) => num,
                Err(_) => {
                    println!("PLEASE ENTER A NUMBER");
                    continue;
                }
            };
            // Dice Rolled n times
            for _i in 0..n {
                let die_1 = rand::thread_rng().gen_range(1..=6);
                let die_2 = rand::thread_rng().gen_range(1..=6);
                let total = die_1 + die_2;
                frequency[total] += 1;
            }
    
            // Results tabel
            println!("\nTOTAL SPOTS    NUMBER OF TIMES");
            for i in 2..13 {
                println!("{:^4}\t\t{}", i, frequency[i]);
            }
    
            // Continue the game
            let reply = readinput("TRY AGAIN").to_ascii_uppercase();
            if reply.starts_with("Y") || reply.eq("YES") {
                frequency = [0; 13];
            } else {
                playing = false;
            }
        }
    }
    
    // function for getting input on same line
    fn readinput(str: &str) -> String {
        print!("\n{}? ", str);
        let mut input = String::new();
        io::stdout().flush().unwrap();
        io::stdin()
            .read_line(&mut input)
            .expect("Failed to get Input");
        input
    }
    
    
    ================================================
    FILE: 33_Dice/vbnet/Dice.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Dice", "Dice.vbproj", "{6410CCD0-0D78-49C9-9B15-70F901A1EB19}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{6410CCD0-0D78-49C9-9B15-70F901A1EB19}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{6410CCD0-0D78-49C9-9B15-70F901A1EB19}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{6410CCD0-0D78-49C9-9B15-70F901A1EB19}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{6410CCD0-0D78-49C9-9B15-70F901A1EB19}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 33_Dice/vbnet/Dice.vbproj
    ================================================
    
      
        Exe
        Dice
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 33_Dice/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 33_Dice/vbnet/program.vb
    ================================================
    Imports System
    
    Module Program
        Sub Main(args As String())
            Const header As String =
    "                   DICE
    CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY
    
    
    
    THIS PROGRAM SIMULATES THE ROLLING OF A
    PAIR OF DICE.
    YOU ENTER THE NUMBER OF TIMES YOU WANT THE COMPUTER TO
    /ROLL/ THE DICE.  WATCH OUT, VERY LARGE NUMBERS TAKE
    A LONG TIME.  IN PARTICULAR, NUMBERS OVER 5000."
    
            Console.WriteLine(header)
    
            Dim D6 As New Random()
            Dim continuePrompt As String = "YES"
            While continuePrompt = "YES"
                Console.Write($"{vbCrLf}HOW MANY ROLLS? ")
                Dim x As Integer = Convert.ToInt32(Console.ReadLine())
                Dim F = Enumerable.Repeat(0, 11).ToList()
                For s As Integer = 0 To x - 1
                    F(D6.Next(6) + D6.Next(6)) += 1
                Next
    
                Console.WriteLine($"{vbCrLf}TOTAL SPOTS   NUMBER OF TIMES")
                For V As Integer = 0 To 10
                    Console.WriteLine($" {V + 2}{vbTab,-8}{F(V)}")
                Next
    
                Console.Write($"{vbCrLf}TRY AGAIN ")
                continuePrompt = Console.ReadLine().ToUpper()
            End While
        End Sub
    End Module
    
    
    ================================================
    FILE: 34_Digits/README.md
    ================================================
    ### Digits
    
    The player writes down a set of 30 numbers (0, 1, or 2) at random prior to playing the game. The computer program, using pattern recognition techniques, attempts to guess the next number in your list.
    
    The computer asks for 10 numbers at a time. It always guesses first and then examines the next number to see if it guessed correctly. By pure luck (or chance or probability), the computer ought to be right 10 times. It is uncanny how much better it generally does than that!
    
    This program originated at Dartmouth; original author unknown.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=58)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=73)
    
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    - 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.
    
    
    ================================================
    FILE: 34_Digits/csharp/Digits.csproj
    ================================================
    
      
        Exe
        net6.0
        10
        enable
        enable
      
    
      
        
      
    
      
        
      
    
    
    
    ================================================
    FILE: 34_Digits/csharp/Digits.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Digits", "Digits.csproj", "{BB89211D-85FB-4FC0-AF62-715459227D7E}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{BB89211D-85FB-4FC0-AF62-715459227D7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{BB89211D-85FB-4FC0-AF62-715459227D7E}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{BB89211D-85FB-4FC0-AF62-715459227D7E}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{BB89211D-85FB-4FC0-AF62-715459227D7E}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 34_Digits/csharp/Game.cs
    ================================================
    namespace Digits;
    
    internal class GameSeries
    {
        private readonly IReadOnlyList _weights = new List { 0, 1, 3 }.AsReadOnly();
    
        private readonly IReadWrite _io;
        private readonly IRandom _random;
    
        public GameSeries(IReadWrite io, IRandom random)
        {
            _io = io;
            _random = random;
        }
    
        internal void Play()
        {
            _io.Write(Streams.Introduction);
    
            if (_io.ReadNumber(Prompts.ForInstructions) != 0)
            {
                _io.Write(Streams.Instructions);
            }
    
            do
            {
                new Game(_io, _random).Play();
            } while (_io.ReadNumber(Prompts.WantToTryAgain) == 1);
    
            _io.Write(Streams.Thanks);
        }
    }
    
    internal class Game
    {
        private readonly IReadWrite _io;
        private readonly Guesser _guesser;
    
        public Game(IReadWrite io, IRandom random)
        {
            _io = io;
            _guesser = new Guesser(random);
        }
    
        public void Play()
        {
            var correctGuesses = 0;
    
            for (int round = 0; round < 3; round++)
            {
                var digits = _io.Read10Digits(Prompts.TenNumbers, Streams.TryAgain);
    
                correctGuesses = GuessDigits(digits, correctGuesses);
            }
    
            _io.Write(correctGuesses switch
            {
                < 10 => Streams.YouWin,
                10 => Streams.ItsATie,
                > 10 => Streams.IWin
            });
        }
    
        private int GuessDigits(IEnumerable digits, int correctGuesses)
        {
            _io.Write(Streams.Headings);
    
            foreach (var digit in digits)
            {
                var guess = _guesser.GuessNextDigit();
                if (guess == digit) { correctGuesses++; }
    
                _io.WriteLine(Formats.GuessResult, guess, digit, guess == digit ? "Right" : "Wrong", correctGuesses);
    
                _guesser.ObserveActualDigit(digit);
            }
    
            return correctGuesses;
        }
    }
    
    
    ================================================
    FILE: 34_Digits/csharp/Guesser.cs
    ================================================
    namespace Digits;
    
    internal class Guesser
    {
        private readonly Memory _matrices = new();
        private readonly IRandom _random;
    
        public Guesser(IRandom random)
        {
            _random = random;
        }
    
        public int GuessNextDigit()
        {
            var currentSum = 0;
            var guess = 0;
    
            for (int i = 0; i < 3; i++)
            {
                var sum = _matrices.GetWeightedSum(i);
                if (sum > currentSum || _random.NextFloat() >= 0.5)
                {
                    currentSum = sum;
                    guess = i;
                }
            }
    
            return guess;
        }
    
        public void ObserveActualDigit(int digit) => _matrices.ObserveDigit(digit);
    }
    
    
    ================================================
    FILE: 34_Digits/csharp/IOExtensions.cs
    ================================================
    namespace Digits;
    
    internal static class IOExtensions
    {
        internal static IEnumerable Read10Digits(this IReadWrite io, string prompt, Stream retryText)
        {
            while (true)
            {
                var numbers = new float[10];
                io.ReadNumbers(prompt, numbers);
    
                if (numbers.All(n => n == 0 || n == 1 || n == 2))
                {
                    return numbers.Select(n => (int)n);
                }    
    
                io.Write(retryText);
            }
        }
    }
    
    ================================================
    FILE: 34_Digits/csharp/Matrix.cs
    ================================================
    namespace Digits;
    
    internal class Matrix
    {
        private readonly int _weight;
        private readonly int[,] _values;
    
        public Matrix(int width, int weight, Func seedFactory)
        {
            _weight = weight;
            _values = new int[width, 3];
            
            for (int i = 0; i < width; i++)
            for (int j = 0; j < 3; j++)
            {
                _values[i, j] = seedFactory.Invoke(i, j);
            }
    
            Index = width - 1;
        }
    
        public int Index { get; set; }
    
        public int GetWeightedValue(int row) => _weight * _values[Index, row];
    
        public int IncrementValue(int row) => _values[Index, row]++;
    }
    
    ================================================
    FILE: 34_Digits/csharp/Memory.cs
    ================================================
    namespace Digits;
    
    public class Memory
    {
        private readonly Matrix[] _matrices;
    
        public Memory()
        {
            _matrices = new[] 
            {
                new Matrix(27, 3, (_, _) => 1),
                new Matrix(9, 1, (i, j) => i == 4 * j ? 2 : 3),
                new Matrix(3, 0, (_, _) => 9)
            };
        }
    
        public int GetWeightedSum(int row) => _matrices.Select(m => m.GetWeightedValue(row)).Sum();
    
        public void ObserveDigit(int digit)
        {
            for (int i = 0; i < 3; i++)
            {
                _matrices[i].IncrementValue(digit);
            }
    
            _matrices[0].Index = _matrices[0].Index % 9 * 3 + digit;
            _matrices[1].Index = _matrices[0].Index % 9;
            _matrices[2].Index = digit;
        }
    }
    
    ================================================
    FILE: 34_Digits/csharp/Program.cs
    ================================================
    global using Digits;
    global using Games.Common.IO;
    global using Games.Common.Randomness;
    global using static Digits.Resources.Resource;
    
    new GameSeries(new ConsoleIO(), new RandomNumberGenerator()).Play();
    
    ================================================
    FILE: 34_Digits/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 34_Digits/csharp/Resources/ForInstructions.txt
    ================================================
    For instructions, type '1', else type '0'
    
    ================================================
    FILE: 34_Digits/csharp/Resources/GuessResult.txt
    ================================================
       {0}              {1}          {2}         {3}
    
    ================================================
    FILE: 34_Digits/csharp/Resources/Headings.txt
    ================================================
    
    My guess      Your no.      Result        No. right
    
    
    
    ================================================
    FILE: 34_Digits/csharp/Resources/IWin.txt
    ================================================
    
    I guessed more than 1/3 of your numbers.
    I win.
    
    
    
    ================================================
    FILE: 34_Digits/csharp/Resources/Instructions.txt
    ================================================
    
    Please take a piece of paper and write down
    the digits '0', '1', or '2' thirty times at random.
    Arrange them in three lines of ten digits each.
    I will ask for then ten at a time.
    I will always guess them first and then look at your
    next number to see if I was right. By pure luck,
    I ought to be right ten times. But I hope to do better
    than that *****
    
    
    
    
    ================================================
    FILE: 34_Digits/csharp/Resources/Introduction.txt
    ================================================
                                     Digits
                   Creative Computing  Morristown, New Jersey
    
    
    
    This is a game of guessing.
    
    
    ================================================
    FILE: 34_Digits/csharp/Resources/ItsATie.txt
    ================================================
    
    I guessed exactly 1/3 of your numbers.
    It's a tie game.
    
    
    
    ================================================
    FILE: 34_Digits/csharp/Resources/Resource.cs
    ================================================
    using System.Reflection;
    using System.Runtime.CompilerServices;
    
    namespace Digits.Resources;
    
    internal static class Resource
    {
        internal static class Streams
        {
            public static Stream Introduction => GetStream();
            public static Stream Instructions => GetStream();
            public static Stream TryAgain => GetStream();
            public static Stream ItsATie => GetStream();
            public static Stream IWin => GetStream();
            public static Stream YouWin => GetStream();
            public static Stream Thanks => GetStream();
            public static Stream Headings => GetStream();
        }
    
        internal static class Prompts
        {
            public static string ForInstructions => GetString();
            public static string TenNumbers => GetString();
            public static string WantToTryAgain => GetString();
        }
    
        internal static class Formats
        {
            public static string GuessResult => GetString();
        }
    
        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}'.");
    }
    
    ================================================
    FILE: 34_Digits/csharp/Resources/TenNumbers.txt
    ================================================
    
    Ten numbers, please
    
    ================================================
    FILE: 34_Digits/csharp/Resources/Thanks.txt
    ================================================
    
    Thanks for the game
    
    
    ================================================
    FILE: 34_Digits/csharp/Resources/TryAgain.txt
    ================================================
    Only use the digits '0', '1', or '2'.
    Let's try again.
    
    
    ================================================
    FILE: 34_Digits/csharp/Resources/WantToTryAgain.txt
    ================================================
    Do you want to try again (1 for yes, 0 for no)
    
    ================================================
    FILE: 34_Digits/csharp/Resources/YouWin.txt
    ================================================
    
    I guessed less than 1/3 of your numbers.
    You beat me.  Congratulations *****
    
    
    
    ================================================
    FILE: 34_Digits/digits.bas
    ================================================
    10 PRINT TAB(33);"DIGITS"
    20 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    30 PRINT:PRINT:PRINT
    210 PRINT "THIS IS A GAME OF GUESSING."
    220 PRINT "FOR INSTRUCTIONS, TYPE '1', ELSE TYPE '0'";
    230 INPUT E
    240 IF E=0 THEN 360
    250 PRINT
    260 PRINT "PLEASE TAKE A PIECE OF PAPER AND WRITE DOWN"
    270 PRINT "THE DIGITS '0', '1', OR '2' THIRTY TIMES AT RANDOM."
    280 PRINT "ARRANGE THEM IN THREE LINES OF TEN DIGITS EACH."
    290 PRINT "I WILL ASK FOR THEN TEN AT A TIME."
    300 PRINT "I WILL ALWAYS GUESS THEM FIRST AND THEN LOOK AT YOUR"
    310 PRINT "NEXT NUMBER TO SEE IF I WAS RIGHT. BY PURE LUCK,"
    320 PRINT "I OUGHT TO BE RIGHT TEN TIMES. BUT I HOPE TO DO BETTER"
    330 PRINT "THAN THAT *****"
    340 PRINT:PRINT
    360 READ A,B,C
    370 DATA 0,1,3
    380 DIM M(26,2),K(2,2),L(8,2)
    400 FOR I=0 TO 26: FOR J=0 TO 2: M(I,J)=1: NEXT J: NEXT I
    410 FOR I=0 TO 2: FOR J=0 TO 2: K(I,J)=9: NEXT J: NEXT I
    420 FOR I=0 TO 8: FOR J=0 TO 2: L(I,J)=3: NEXT J: NEXT I
    450 L(0,0)=2: L(4,1)=2: L(8,2)=2
    480 Z=26: Z1=8: Z2=2
    510 X=0
    520 FOR T=1 TO 3
    530 PRINT
    540 PRINT "TEN NUMBERS, PLEASE";
    550 INPUT N(1),N(2),N(3),N(4),N(5),N(6),N(7),N(8),N(9),N(10)
    560 FOR I=1 TO 10
    570 W=N(I)-1
    580 IF W=SGN(W) THEN 620
    590 PRINT "ONLY USE THE DIGITS '0', '1', OR '2'."
    600 PRINT "LET'S TRY AGAIN.":GOTO 530
    620 NEXT I
    630 PRINT: PRINT "MY GUESS","YOUR NO.","RESULT","NO. RIGHT":PRINT
    660 FOR U=1 TO 10
    670 N=N(U): S=0
    690 FOR J=0 TO 2
    700 S1=A*K(Z2,J)+B*L(Z1,J)+C*M(Z,J)
    710 IF S>S1 THEN 760
    720 IF S10 THEN 980
    940 IF X<10 THEN 1010
    950 PRINT "I GUESSED EXACTLY 1/3 OF YOUR NUMBERS."
    960 PRINT "IT'S A TIE GAME."
    970 GOTO 1030
    980 PRINT "I GUESSED MORE THAN 1/3 OF YOUR NUMBERS."
    990 PRINT "I WIN.": FOR Q=1 TO 10: PRINT CHR$(7);: NEXT Q
    1000 GOTO 1030
    1010 PRINT "I GUESSED LESS THAN 1/3 OF YOUR NUMBERS."
    1020 PRINT "YOU BEAT ME.  CONGRATULATIONS *****"
    1030 PRINT
    1040 PRINT "DO YOU WANT TO TRY AGAIN (1 FOR YES, 0 FOR NO)";
    1060 INPUT X
    1070 IF X=1 THEN 400
    1080 PRINT:PRINT "THANKS FOR THE GAME."
    1090 END
    
    
    ================================================
    FILE: 34_Digits/java/Digits.java
    ================================================
    import java.util.Arrays;
    import java.util.InputMismatchException;
    import java.util.Scanner;
    
    /**
     * DIGITS
     * 

    * Converted from BASIC to Java by Aldrin Misquitta (@aldrinm) */ public class Digits { public static void main(String[] args) { printIntro(); Scanner scan = new Scanner(System.in); boolean showInstructions = readInstructionChoice(scan); if (showInstructions) { printInstructions(); } int a = 0, b = 1, c = 3; int[][] m = new int[27][3]; int[][] k = new int[3][3]; int[][] l = new int[9][3]; boolean continueGame = true; while (continueGame) { for (int[] ints : m) { Arrays.fill(ints, 1); } for (int[] ints : k) { Arrays.fill(ints, 9); } for (int[] ints : l) { Arrays.fill(ints, 3); } l[0][0] = 2; l[4][1] = 2; l[8][2] = 2; int z = 26, z1 = 8, z2 = 2, runningCorrect = 0; for (int t = 1; t <= 3; t++) { boolean validNumbers = false; int[] numbers = new int[0]; while (!validNumbers) { System.out.println(); numbers = read10Numbers(scan); validNumbers = true; for (int number : numbers) { if (number < 0 || number > 2) { System.out.println("ONLY USE THE DIGITS '0', '1', OR '2'."); System.out.println("LET'S TRY AGAIN."); validNumbers = false; break; } } } System.out.printf("\n%-14s%-14s%-14s%-14s", "MY GUESS", "YOUR NO.", "RESULT", "NO. RIGHT"); for (int number : numbers) { int s = 0; int myGuess = 0; for (int j = 0; j <= 2; j++) { //What did the original author have in mind ? The first expression always results in 0 because a is always 0 int s1 = a * k[z2][j] + b * l[z1][j] + c * m[z][j]; if (s < s1) { s = s1; myGuess = j; } else if (s1 == s) { if (Math.random() >= 0.5) { myGuess = j; } } } String result; if (myGuess != number) { result = "WRONG"; } else { runningCorrect++; result = "RIGHT"; m[z][number] = m[z][number] + 1; l[z1][number] = l[z1][number] + 1; k[z2][number] = k[z2][number] + 1; z = z - (z / 9) * 9; z = 3 * z + number; } System.out.printf("\n%-14d%-14d%-14s%-14d", myGuess, number, result, runningCorrect); z1 = z - (z / 9) * 9; z2 = number; } } //print summary report System.out.println(); if (runningCorrect > 10) { System.out.println(); System.out.println("I GUESSED MORE THAN 1/3 OF YOUR NUMBERS."); System.out.println("I WIN.\u0007"); } else if (runningCorrect < 10) { System.out.println("I GUESSED LESS THAN 1/3 OF YOUR NUMBERS."); System.out.println("YOU BEAT ME. CONGRATULATIONS *****"); } else { System.out.println("I GUESSED EXACTLY 1/3 OF YOUR NUMBERS."); System.out.println("IT'S A TIE GAME."); } continueGame = readContinueChoice(scan); } System.out.println("\nTHANKS FOR THE GAME."); } private static boolean readContinueChoice(Scanner scan) { System.out.print("\nDO YOU WANT TO TRY AGAIN (1 FOR YES, 0 FOR NO) ? "); int choice; try { choice = scan.nextInt(); return choice == 1; } catch (InputMismatchException ex) { return false; } finally { scan.nextLine(); } } private static int[] read10Numbers(Scanner scan) { System.out.print("TEN NUMBERS, PLEASE ? "); int[] numbers = new int[10]; for (int i = 0; i < numbers.length; i++) { boolean validInput = false; while (!validInput) { try { int n = scan.nextInt(); validInput = true; numbers[i] = n; } catch (InputMismatchException ex) { System.out.println("!NUMBER EXPECTED - RETRY INPUT LINE"); } finally { scan.nextLine(); } } } return numbers; } private static void printInstructions() { System.out.println("\n"); System.out.println("PLEASE TAKE A PIECE OF PAPER AND WRITE DOWN"); System.out.println("THE DIGITS '0', '1', OR '2' THIRTY TIMES AT RANDOM."); System.out.println("ARRANGE THEM IN THREE LINES OF TEN DIGITS EACH."); System.out.println("I WILL ASK FOR THEN TEN AT A TIME."); System.out.println("I WILL ALWAYS GUESS THEM FIRST AND THEN LOOK AT YOUR"); System.out.println("NEXT NUMBER TO SEE IF I WAS RIGHT. BY PURE LUCK,"); System.out.println("I OUGHT TO BE RIGHT TEN TIMES. BUT I HOPE TO DO BETTER"); System.out.println("THAN THAT *****"); System.out.println(); } private static boolean readInstructionChoice(Scanner scan) { System.out.print("FOR INSTRUCTIONS, TYPE '1', ELSE TYPE '0' ? "); int choice; try { choice = scan.nextInt(); return choice == 1; } catch (InputMismatchException ex) { return false; } finally { scan.nextLine(); } } private static void printIntro() { System.out.println(" DIGITS"); System.out.println(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println("\n\n"); System.out.println("THIS IS A GAME OF GUESSING."); } } ================================================ FILE: 34_Digits/java/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Oracle Java](https://openjdk.java.net/) ================================================ FILE: 34_Digits/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 34_Digits/javascript/digits.html ================================================ DIGITS

    
    
    
    
    
    
    ================================================
    FILE: 34_Digits/javascript/digits.js
    ================================================
    // DIGITS
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    // Main program
    async function main()
    {
        print(tab(33) + "DIGITS\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        print("THIS IS A GAME OF GUESSING.\n");
        print("FOR INSTRUCTIONS, TYPE '1', ELSE TYPE '0'");
        e = parseInt(await input());
        if (e != 0) {
            print("\n");
            print("PLEASE 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");
            print("\n");
            print("\n");
        }
        a = 0;
        b = 1;
        c = 3;
        m = [];
        k = [];
        l = [];
        n = [];
        while (1) {
            for (i = 0; i <= 26; i++) {
                m[i] = [];
                for (j = 0; j <= 2; j++) {
                    m[i][j] = 1;
                }
            }
            for (i = 0; i <= 2; i++) {
                k[i] = [];
                for (j = 0; j <= 2; j++) {
                    k[i][j] = 9;
                }
            }
            for (i = 0; i <= 8; i++) {
                l[i] = [];
                for (j = 0; j <= 2; j++) {
                    l[i][j] = 3;
                }
            }
            l[0][0] = 2;
            l[4][1] = 2;
            l[8][2] = 2;
            z = 26;
            z1 = 8;
            z2 = 2;
            x = 0;
            for (t = 1; t <= 3; t++) {
                while (1) {
                    print("\n");
                    print("TEN NUMBERS, PLEASE");
                    str = await input();
                    for (i = 1; i <= 10; i++) {
                        n[i] = parseInt(str);
                        j = str.indexOf(",");
                        if (j >= 0) {
                            str = str.substr(j + 1);
                        }
                        if (n[i] < 0 || n[i] > 2)
                            break;
                    }
                    if (i <= 10) {
                        print("ONLY USE THE DIGITS '0', '1', OR '2'.\n");
                        print("LET'S TRY AGAIN.\n");
                    } else {
                        break;
                    }
                }
                print("\n");
                print("MY GUESS\tYOUR NO.\tRESULT\tNO. RIGHT\n");
                print("\n");
                for (u = 1; u <= 10; u++) {
                    n2 = n[u];
                    s = 0;
                    for (j = 0; j <= 2; j++) {
                        s1 = a * k[z2][j] + b * l[z1][j] + c * m[z][j];
                        if (s > s1)
                            continue;
                        if (s < s1 || Math.random() >= 0.5) {
                            s = s1;
                            g = j;
                        }
                    }
                    print("  " + g + "\t\t   " + n[u] + "\t\t");
                    if (g == n[u]) {
                        x++;
                        print(" RIGHT\t " + x + "\n");
                        m[z][n2]++;
                        l[z1][n2]++;
                        k[z2][n2]++;
                        z = z % 9;
                        z = 3 * z + n[u];
                    } else {
                        print(" WRONG\t " + x + "\n");
                    }
                    z1 = z % 9;
                    z2 = n[u];
                }
            }
            print("\n");
            if (x > 10) {
                print("I GUESSED MORE THAN 1/3 OF YOUR NUMBERS.\n");
                print("I WIN.\n");
            } else if (x == 10) {
                print("I GUESSED EXACTLY 1/3 OF YOUR NUMBERS.\n");
                print("IT'S A TIE GAME.\n");
            } else {
                print("I GUESSED LESS THAN 1/3 OF YOUR NUMBERS.\n");
                print("YOU BEAT ME.  CONGRATULATIONS *****\n");
            }
            print("\n");
            print("DO YOU WANT TO TRY AGAIN (1 FOR YES, 0 FOR NO)");
            x = parseInt(await input());
            if (x != 1)
                break;
        }
        print("\n");
        print("THANKS FOR THE GAME.\n");
    }
    
    main();
    
    
    ================================================
    FILE: 34_Digits/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 34_Digits/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 34_Digits/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 34_Digits/perl/digits.pl
    ================================================
    #!/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";
    
    
    ================================================
    FILE: 34_Digits/python/Digits.py
    ================================================
    import random
    from typing import List
    
    
    def print_intro() -> None:
        print("                                DIGITS")
        print("              CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY")
        print("\n\n")
        print("THIS IS A GAME OF GUESSING.")
    
    
    def read_instruction_choice() -> bool:
        print("FOR INSTRUCTIONS, TYPE '1', ELSE TYPE '0' ? ")
        try:
            choice = int(input())
            return choice == 1
        except (ValueError, TypeError):
            return False
    
    
    def print_instructions() -> None:
        print("\n")
        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()
    
    
    def read_10_numbers() -> List[int]:
        print("TEN NUMBERS, PLEASE ? ")
        numbers = []
    
        for _ in range(10):
            valid_input = False
            while not valid_input:
                try:
                    n = int(input())
                    valid_input = True
                    numbers.append(n)
                except (TypeError, ValueError):
                    print("!NUMBER EXPECTED - RETRY INPUT LINE")
    
        return numbers
    
    
    def read_continue_choice() -> bool:
        print("\nDO YOU WANT TO TRY AGAIN (1 FOR YES, 0 FOR NO) ? ")
        try:
            choice = int(input())
            return choice == 1
        except (ValueError, TypeError):
            return False
    
    
    def print_summary_report(running_correct: int) -> None:
        print()
        if running_correct > 10:
            print()
            print("I GUESSED MORE THAN 1/3 OF YOUR NUMBERS.")
            print("I WIN.\u0007")
        elif running_correct < 10:
            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.")
    
    
    def main() -> None:
        print_intro()
        if read_instruction_choice():
            print_instructions()
    
        a = 0
        b = 1
        c = 3
    
        m = [[1] * 3 for _ in range(27)]
        k = [[9] * 3 for _ in range(3)]
        l = [[3] * 3 for _ in range(9)]  # noqa: E741
    
        continue_game = True
        while continue_game:
            l[0][0] = 2
            l[4][1] = 2
            l[8][2] = 2
            z: float = 26
            z1: float = 8
            z2 = 2
            running_correct = 0
    
            for _round in range(1, 4):
                valid_numbers = False
                numbers = []
                while not valid_numbers:
                    print()
                    numbers = read_10_numbers()
                    valid_numbers = True
                    for number in numbers:
                        if number < 0 or number > 2:
                            print("ONLY USE THE DIGITS '0', '1', OR '2'.")
                            print("LET'S TRY AGAIN.")
                            valid_numbers = False
                            break
    
                print(
                    "\n%-14s%-14s%-14s%-14s"
                    % ("MY GUESS", "YOUR NO.", "RESULT", "NO. RIGHT")
                )
    
                for number in numbers:
                    s = 0
                    my_guess = 0
                    for j in range(0, 3):
                        # What did the original author have in mind ?
                        # The first expression always results in 0 because a is always 0
                        s1 = a * k[z2][j] + b * l[int(z1)][j] + c * m[int(z)][j]
                        if s < s1:
                            s = s1
                            my_guess = j
                        elif s1 == s and random.random() >= 0.5:
                            my_guess = j
    
                    result = ""
    
                    if my_guess != number:
                        result = "WRONG"
                    else:
                        running_correct += 1
                        result = "RIGHT"
                        m[int(z)][number] = m[int(z)][number] + 1
                        l[int(z1)][number] = l[int(z1)][number] + 1
                        k[int(z2)][number] = k[int(z2)][number] + 1
                        z = z - (z / 9) * 9
                        z = 3 * z + number
                    print(
                        "\n%-14d%-14d%-14s%-14d"
                        % (my_guess, number, result, running_correct)
                    )
    
                    z1 = z - (z / 9) * 9
                    z2 = number
    
            print_summary_report(running_correct)
            continue_game = read_continue_choice()
    
        print("\nTHANKS FOR THE GAME.")
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 34_Digits/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 34_Digits/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 34_Digits/vbnet/Digits.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Digits", "Digits.vbproj", "{837EB2E4-3EE6-447A-8AF7-91CA08841B93}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{837EB2E4-3EE6-447A-8AF7-91CA08841B93}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{837EB2E4-3EE6-447A-8AF7-91CA08841B93}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{837EB2E4-3EE6-447A-8AF7-91CA08841B93}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{837EB2E4-3EE6-447A-8AF7-91CA08841B93}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 34_Digits/vbnet/Digits.vbproj
    ================================================
    
      
        Exe
        Digits
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 34_Digits/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 35_Even_Wins/README.md
    ================================================
    ### Even Wins
    
    This is a game between you and the computer. To play, an odd number of objects (marbles, chips, matches) are placed in a row. You take turns with the computer picking up between one and four objects each turn. The game ends when there are no objects left, and the winner is the one with an even number of objects picked up.
    
    Two versions of this game are included. While to the player they appear similar, the programming approach is quite different. EVEN WINS, the first version, is deterministic — i.e., the computer plays by fixed, good rules and is impossible to beat if you don’t know how to play the game. It always starts with 27 objects, although you may change this.
    
    The second version, GAME OF EVEN WINS, is much more interesting because the computer starts out only knowing the rules of the game. Using simple techniques of artificial intelligence (cybernetics), the computer gradually learns to play this game from its mistakes until it plays a very good game. After 20 games, the computer is a challenge to beat. Variation in the human’s style of play seems to make the computer learn more quickly. If you plot the learning curve of this program, it closely resembles classical human learning curves from psychological experiments.
    
    Eric Peters at DEC wrote the GAME OF EVEN WINS. The original author of EVEN WINS is unknown.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=60)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=75)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 35_Even_Wins/csharp/EvenWins.csproj
    ================================================
    
      
        Exe
        net6.0
        10
        enable
        enable
      
    
    
    
    ================================================
    FILE: 35_Even_Wins/csharp/EvenWins.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EvenWins", "EvenWins.csproj", "{84209510-4089-4147-B220-E41E534A228B}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{84209510-4089-4147-B220-E41E534A228B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{84209510-4089-4147-B220-E41E534A228B}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{84209510-4089-4147-B220-E41E534A228B}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{84209510-4089-4147-B220-E41E534A228B}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 35_Even_Wins/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 35_Even_Wins/evenwins.bas
    ================================================
    1 PRINT TAB(31);"EVEN WINS"
    2 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    3 PRINT:PRINT
    4 Y1=0
    10 M1=0
    20 DIM M(20),Y(20)
    30 PRINT "     THIS IS A TWO PERSON GAME CALLED 'EVEN WINS.'"
    40 PRINT "TO PLAY THE GAME, THE PLAYERS NEED 27 MARBLES OR"
    50 PRINT "OTHER OBJECTS ON A TABLE."
    60 PRINT
    70 PRINT
    80 PRINT "     THE 2 PLAYERS ALTERNATE TURNS, WITH EACH PLAYER"
    90 PRINT "REMOVING FROM 1 TO 4 MARBLES ON EACH MOVE.  THE GAME"
    100 PRINT "ENDS WHEN THERE ARE NO MARBLES LEFT, AND THE WINNER"
    110 PRINT "IS THE ONE WITH AN EVEN NUMBER OF MARBLES."
    120 PRINT
    130 PRINT
    140 PRINT "     THE ONLY RULES ARE THAT (1) YOU MUST ALTERNATE TURNS,"
    150 PRINT "(2) YOU MUST TAKE BETWEEN 1 AND 4 MARBLES EACH TURN,"
    160 PRINT "AND (3) YOU CANNOT SKIP A TURN."
    170 PRINT
    180 PRINT
    190 PRINT
    200 PRINT "     TYPE A '1' IF YOU WANT TO GO FIRST, AND TYPE"
    210 PRINT "A '0' IF YOU WANT ME TO GO FIRST."
    220 INPUT C
    225 PRINT
    230 IF C=0 THEN 250
    240 GOTO 1060
    250 T=27
    260 M=2
    270 PRINT:PRINT "TOTAL=";T:PRINT
    280 M1=M1+M
    290 T=T-M
    300 PRINT "I PICK UP";M;"MARBLES."
    310 IF T=0 THEN 880
    320 PRINT:PRINT "TOTAL=";T
    330 PRINT
    340 PRINT "     AND WHAT IS YOUR NEXT MOVE, MY TOTAL IS";M1
    350 INPUT Y
    360 PRINT
    370 IF Y<1 THEN 1160
    380 IF Y>4 THEN 1160
    390 IF Y<=T THEN 430
    400 PRINT "     YOU HAVE TRIED TO TAKE MORE MARBLES THAN THERE ARE"
    410 PRINT "LEFT.  TRY AGAIN."
    420 GOTO 350
    430 Y1=Y1+Y
    440 T=T-Y
    450 IF T=0 THEN 880
    460 PRINT "TOTAL=";T
    470 PRINT
    480 PRINT "YOUR TOTAL IS";Y1
    490 IF T<.5 THEN 880
    500 R=T-6*INT(T/6)
    510 IF INT(Y1/2)=Y1/2 THEN 700
    520 IF T<4.2 THEN 580
    530 IF R>3.4 THEN 620
    540 M=R+1
    550 M1=M1+M
    560 T=T-M
    570 GOTO 300
    580 M=T
    590 T=T-M
    600 GOTO 830
    610 REM     250 IS WHERE I WIN.
    620 IF R<4.7 THEN 660
    630 IF R>3.5 THEN 660
    640 M=1
    650 GOTO 670
    660 M=4
    670 T=T-M
    680 M1=M1+M
    690 GOTO 300
    700 REM     I AM READY TO ENCODE THE STRAT FOR WHEN OPP TOT IS EVEN
    710 IF R<1.5 THEN 1020
    720 IF R>5.3 THEN 1020
    730 M=R-1
    740 M1=M1+M
    750 T=T-M
    760 IF T<.2 THEN 790
    770 REM     IS # ZERO HERE
    780 GOTO 300
    790 REM     IS = ZERO HERE
    800 PRINT "I PICK UP";M;"MARBLES."
    810 PRINT
    820 GOTO 880
    830 REM    THIS IS WHERE I WIN
    840 PRINT "I PICK UP";M;"MARBLES."
    850 PRINT
    860 PRINT "TOTAL = 0"
    870 M1=M1+M
    880 PRINT "THAT IS ALL OF THE MARBLES."
    890 PRINT
    900 PRINT " MY TOTAL IS";M1;", YOUR TOTAL IS";Y1
    910 PRINT
    920 IF INT(M1/2)=M1/2 THEN 950
    930 PRINT "     YOU WON.  DO YOU WANT TO PLAY"
    940 GOTO 960
    950 PRINT "     I WON.  DO YOU WANT TO PLAY"
    960 PRINT "AGAIN?  TYPE 1 FOR YES AND 0 FOR NO."
    970 INPUT A1
    980 IF A1=0 THEN 1030
    990 M1=0
    1000 Y1=0
    1010 GOTO 200
    1020 GOTO 640
    1030 PRINT
    1040 PRINT "OK.  SEE YOU LATER."
    1050 GOTO 1230
    1060 T=27
    1070 PRINT
    1080 PRINT
    1090 PRINT
    1100 PRINT "TOTAL=";T
    1110 PRINT
    1120 PRINT
    1130 PRINT "WHAT IS YOUR FIRST MOVE";
    1140 INPUT Y
    1150 GOTO 360
    1160 PRINT
    1170 PRINT "THE NUMBER OF MARBLES YOU TAKE MUST BE A POSITIVE"
    1180 PRINT "INTEGER BETWEEN 1 AND 4."
    1190 PRINT
    1200 PRINT "     WHAT IS YOUR NEXT MOVE?"
    1210 PRINT
    1220 GOTO 350
    1230 END
    
    
    ================================================
    FILE: 35_Even_Wins/gameofevenwins.bas
    ================================================
    1 PRINT TAB(28);"GAME OF EVEN WINS"
    2 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    3 PRINT:PRINT
    4 INPUT "DO YOU WANT INSTRUCTIONS (YES OR NO)";A$:PRINT
    5 IF A$="NO" THEN 20
    6 PRINT "THE GAME IS PLAYED AS FOLLOWS:":PRINT
    7 PRINT "AT THE BEGINNING OF THE GAME, A RANDOM NUMBER OF CHIPS ARE"
    8 PRINT "PLACED ON THE BOARD.  THE NUMBER OF CHIPS ALWAYS STARTS"
    9 PRINT "AS AN ODD NUMBER.  ON EACH TURN, A PLAYER MUST TAKE ONE,"
    10 PRINT "TWO, THREE, OR FOUR CHIPS.  THE WINNER IS THE PLAYER WHO"
    11 PRINT "FINISHES WITH A TOTAL NUMBER OF CHIPS THAT IS EVEN."
    12 PRINT "THE COMPUTER STARTS OUT KNOWING ONLY THE RULES OF THE"
    13 PRINT "GAME.  IT GRADUALLY LEARNS TO PLAY WELL.  IT SHOULD BE"
    14 PRINT "DIFFICULT TO BEAT THE COMPUTER AFTER TWENTY GAMES IN A ROW."
    15 PRINT "TRY IT!!!!": PRINT
    16 PRINT "TO QUIT AT ANY TIME, TYPE A '0' AS YOUR MOVE.": PRINT
    20 DIM R(1,5)
    25 L=0: B=0
    30 FOR I=0 TO 5
    40 R(1,I)=4
    50 R(0,I)=4
    60 NEXT I
    70 A=0: B=0
    90 P=INT((13*RND(1)+9)/2)*2+1
    100 IF P=1 THEN 530
    110 PRINT "THERE ARE";P;"CHIPS ON THE BOARD."
    120 E1=E
    130 L1=L
    140 E=(A/2-INT(A/2))*2
    150 L=INT((P/6-INT(P/6))*6+.5)
    160 IF R(E,L)>=P THEN 320
    170 M=R(E,L)
    180 IF M<=0 THEN 370
    190 P=P-M
    200 IF M=1 THEN 510
    210 PRINT "COMPUTER TAKES";M;"CHIPS LEAVING";P;"... YOUR MOVE";
    220 B=B+M
    230 INPUT M
    240 M=INT(M)
    250 IF M<1 THEN 450
    260 IF M>4 THEN 460
    270 IF M>P THEN 460
    280 IF M=P THEN 360
    290 P=P-M
    300 A=A+M
    310 GOTO 100
    320 IF P=1 THEN 550
    330 PRINT "COMPUTER TAKES";P;"CHIPS."
    340 R(E,L)=P
    350 B=B+P
    360 IF B/2=INT(B/2) THEN 420
    370 PRINT "GAME OVER ... YOU WIN!!!": PRINT
    390 IF R(E,L)=1 THEN 480
    400 R(E,L)=R(E,L)-1
    410 GOTO 70
    420 PRINT "GAME OVER ... I WIN!!!": PRINT
    430 GOTO 70
    450 IF M=0 THEN 570
    460 PRINT M;"IS AN ILLEGAL MOVE ... YOUR MOVE";
    470 GOTO 230
    480 IF R(E1,L1)=1 THEN 70
    490 R(E1,L1)=R(E1,L1)-1
    500 GOTO 70
    510 PRINT "COMPUTER TAKES 1 CHIP LEAVING";P;"... YOUR MOVE";
    520 GOTO 220
    530 PRINT "THERE IS 1 CHIP ON THE BOARD."
    540 GOTO 120
    550 PRINT "COMPUTER TAKES 1 CHIP."
    560 GOTO 340
    570 END
    
    
    ================================================
    FILE: 35_Even_Wins/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 35_Even_Wins/javascript/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells)
    
    
    ================================================
    FILE: 35_Even_Wins/javascript/evenwins.html
    ================================================
    
    
    EVEN WINS
    
    
    
    
    
    
    
    
    
    
    ================================================
    FILE: 35_Even_Wins/javascript/evenwins.js
    ================================================
    // EVEN WINS
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    var ma = [];
    var ya = [];
    
    // Main program
    async function main()
    {
        print(tab(31) + "EVEN WINS\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        y1 = 0;
        m1 = 0;
        print("     THIS IS A TWO PERSON GAME CALLED 'EVEN WINS.'\n");
        print("TO PLAY THE GAME, THE PLAYERS NEED 27 MARBLES OR\n");
        print("OTHER OBJECTS ON A TABLE.\n");
        print("\n");
        print("\n");
        print("     THE 2 PLAYERS ALTERNATE TURNS, WITH EACH PLAYER\n");
        print("REMOVING FROM 1 TO 4 MARBLES ON EACH MOVE.  THE GAME\n");
        print("ENDS WHEN THERE ARE NO MARBLES LEFT, AND THE WINNER\n");
        print("IS THE ONE WITH AN EVEN NUMBER OF MARBLES.\n");
        print("\n");
        print("\n");
        print("     THE ONLY RULES ARE THAT (1) YOU MUST ALTERNATE TURNS,\n");
        print("(2) YOU MUST TAKE BETWEEN 1 AND 4 MARBLES EACH TURN,\n");
        print("AND (3) YOU CANNOT SKIP A TURN.\n");
        print("\n");
        print("\n");
        print("\n");
        while (1) {
            print("     TYPE A '1' IF YOU WANT TO GO FIRST, AND TYPE\n");
            print("A '0' IF YOU WANT ME TO GO FIRST.\n");
            c = parseInt(await input());
            print("\n");
            if (c != 0) {
                t = 27;
                print("\n");
                print("\n");
                print("\n");
                print("TOTAL= " + t + "\n");
                print("\n");
                print("\n");
                print("WHAT IS YOUR FIRST MOVE");
                m = 0;
            } else {
                t = 27;
                m = 2;
                print("\n");
                print("TOTAL= " + t + "\n");
                print("\n");
                m1 += m;
                t -= m;
            }
            while (1) {
                if (m) {
                    print("I PICK UP " + m + " MARBLES.\n");
                    if (t == 0)
                        break;
                    print("\n");
                    print("TOTAL= " + t + "\n");
                    print("\n");
                    print("     AND WHAT IS YOUR NEXT MOVE, MY TOTAL IS " + m1 + "\n");
                }
                while (1) {
                    y = parseInt(await input());
                    print("\n");
                    if (y < 1 || y > 4) {
                        print("\n");
                        print("THE NUMBER OF MARBLES YOU MUST TAKE BE A POSITIVE\n");
                        print("INTEGER BETWEEN 1 AND 4.\n");
                        print("\n");
                        print("     WHAT IS YOUR NEXT MOVE?\n");
                        print("\n");
                    } else if (y > t) {
                        print("     YOU HAVE TRIED TO TAKE MORE MARBLES THAN THERE ARE\n");
                        print("LEFT.  TRY AGAIN.\n");
                    } else {
                        break;
                    }
                }
    
                y1 += y;
                t -= y;
                if (t == 0)
                    break;
                print("TOTAL= " + t + "\n");
                print("\n");
                print("YOUR TOTAL IS " + y1 + "\n");
                if (t < 0.5)
                    break;
                r = t % 6;
                if (y1 % 2 != 0) {
                    if (t >= 4.2) {
                        if (r <= 3.4) {
                            m = r + 1;
                            m1 += m;
                            t -= m;
                        } else if (r < 4.7 || r > 3.5) {
                            m = 4;
                            m1 += m;
                            t -= m;
                        } else {
                            m = 1;
                            m1 += m;
                            t -= m;
                        }
                    } else {
                        m = t;
                        t -= m;
                        print("I PICK UP " + m + " MARBLES.\n");
                        print("\n");
                        print("TOTAL = 0\n");
                        m1 += m;
                        break;
                    }
                } else {
                    if (r < 1.5 || r > 5.3) {
                        m = 1;
                        m1 += m;
                        t -= m;
                    } else {
                        m = r - 1;
                        m1 += m;
                        t -= m;
                        if (t < 0.2) {
                            print("I PICK UP " + m + " MARBLES.\n");
                            print("\n");
                            break;
                        }
                    }
                }
            }
            print("THAT IS ALL OF THE MARBLES.\n");
            print("\n");
            print(" MY TOTAL IS " + m1 + ", YOUR TOTAL IS " + y1 +"\n");
            print("\n");
            if (m1 % 2 != 0) {
                print("     YOU WON.  DO YOU WANT TO PLAY\n");
            } else {
                print("     I WON.  DO YOU WANT TO PLAY\n");
            }
            print("AGAIN?  TYPE 1 FOR YES AND 0 FOR NO.\n");
            a1 = parseInt(await input());
            if (a1 == 0)
                break;
            m1 = 0;
            y1 = 0;
        }
        print("\n");
        print("OK.  SEE YOU LATER\n");
    }
    
    main();
    
    
    ================================================
    FILE: 35_Even_Wins/javascript/gameofevenwins.html
    ================================================
    
    
    GAME OF EVEN WINS
    
    
    
    
    
    
    
    
    
    
    ================================================
    FILE: 35_Even_Wins/javascript/gameofevenwins.js
    ================================================
    // GAME OF EVEN WINS
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    var r = [[], []];
    
    // Main program
    async function main()
    {
        print(tab(28) + "GAME OF EVEN WINS\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("DO YOU WANT INSTRUCTIONS (YES OR NO)");
        str = await input();
        print("\n");
        if (str != "NO") {
            print("THE GAME IS PLAYED AS FOLLOWS:\n");
            print("\n");
            print("AT THE BEGINNING OF THE GAME, A RANDOM NUMBER OF CHIPS ARE\n");
            print("PLACED ON THE BOARD.  THE NUMBER OF CHIPS ALWAYS STARTS\n");
            print("AS AN ODD NUMBER.  ON EACH TURN, A PLAYER MUST TAKE ONE,\n");
            print("TWO, THREE, OR FOUR CHIPS.  THE WINNER IS THE PLAYER WHO\n");
            print("FINISHES WITH A TOTAL NUMBER OF CHIPS THAT IS EVEN.\n");
            print("THE COMPUTER STARTS OUT KNOWING ONLY THE RULES OF THE\n");
            print("GAME.  IT GRADUALLY LEARNS TO PLAY WELL.  IT SHOULD BE\n");
            print("DIFFICULT TO BEAT THE COMPUTER AFTER TWENTY GAMES IN A ROW.\n");
            print("TRY IT!!!!\n");
            print("\n");
            print("TO QUIT AT ANY TIME, TYPE A '0' AS YOUR MOVE.\n");
            print("\n");
        }
        l = 0;
        b = 0;
        for (i = 0; i <= 5; i++) {
            r[1][i] = 4;
            r[0][i] = 4;
        }
        while (1) {
            a = 0;
            b = 0;
            e = 0;
            l = 0;
            p = Math.floor((13 * Math.random() + 9) / 2) * 2 + 1;
            while (1) {
                if (p == 1) {
                    print("THERE IS 1 CHIP ON THE BOARD.\n");
                } else {
                    print("THERE ARE " + p + " CHIPS ON THE BOARD.\n");
                }
                e1 = e;
                l1 = l;
                e = a % 2;
                l = p % 6;
                if (r[e][l] < p) {
                    m = r[e][l];
                    if (m <= 0) {
                        m = 1;
                        b = 1;
                        break;
                    }
                    p -= m;
                    if (m == 1)
                        print("COMPUTER TAKES 1 CHIP LEAVING " + p + "... YOUR MOVE");
                    else
                        print("COMPUTER TAKES " + m + " CHIPS LEAVING " + p + "... YOUR MOVE");
                    b += m;
                    while (1) {
                        m = parseInt(await input());
                        if (m == 0)
                            break;
                        if (m < 1 || m > 4 || m > p) {
                            print(m + " IS AN ILLEGAL MOVE ... YOUR MOVE");
                        } else {
                            break;
                        }
                    }
                    if (m == 0)
                        break;
                    if (m == p)
                        break;
                    p -= m;
                    a += m;
                } else {
                    if (p == 1) {
                        print("COMPUTER TAKES 1 CHIP.\n");
                    } else {
                        print("COMPUTER TAKES " + p + " CHIPS.\n");
                    }
                    r[e][l] = p;
                    b += p;
                    break;
                }
            }
            if (m == 0)
                break;
            if (b % 2 != 0) {
                print("GAME OVER ... YOU WIN!!!\n");
                print("\n");
                if (r[e][l] != 1) {
                    r[e][l]--;
                } else if (r[e1][l1] != 1) {
                    r[e1][l1]--;
                }
            } else {
                print("GAME OVER ... I WIN!!!\n");
                print("\n");
            }
        }
    }
    
    main();
    
    
    ================================================
    FILE: 35_Even_Wins/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 35_Even_Wins/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 35_Even_Wins/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 35_Even_Wins/perl/evenwins.pl
    ================================================
    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    &main;
    
    sub main {
    	&print_intro;
    	&game_play;
    }
    
    sub game_play {
    	my $marbles = 27;
    	my $turn = 0;
    	my $player_total = 0;
    	my $computer_total = 0;
    	print "TYPE A '1' IF YOU WANT TO GO FIRST AND TYPE A '0' IF YOU WANT ME TO GO FIRST\n";
    	my $choice = ;
    	chomp($choice);
    	if ($choice == 0) {
    		until ($marbles == 0) {
    
    			my $computer_choice = &computer_select($marbles,$turn,$player_total);
    			$marbles = $marbles - $computer_choice;
    			$computer_total = $computer_total + $computer_choice;
    			print "MY TOTAL IS $computer_total\n";
    
    			print "TOTAL= $marbles\n";
    
    			if ($marbles == 0) {&determine_winner($computer_total,$player_total)};
    
    			my $player_choice = &player_select($marbles,$turn);
    			$marbles = $marbles - $player_choice;
    			$player_total = $player_total + $player_choice;
    			print "YOUR TOTAL IS $player_total\n";
    			$turn++;
    			print "TOTAL= $marbles\n";
    			if ($marbles == 0) {&determine_winner($computer_total,$player_total)};
    		}
    	}
    	elsif ($choice == 1) {
    		until ($marbles == 0) {
    
    			my $player_choice = &player_select($marbles,$turn);
    			$marbles = $marbles - $player_choice;
    			$player_total = $player_total + $player_choice;
    			$turn++;
    			print "YOUR TOTAL IS $player_total\n";
    
    			print "TOTAL= $marbles\n";
    
    			if ($marbles == 0) {&determine_winner($computer_total,$player_total)};
    
    			my $computer_choice = &computer_select($marbles,$turn,$player_total);
    			$marbles = $marbles - $computer_choice;
    			$computer_total = $computer_total + $computer_choice;
    			print "MY TOTAL IS $computer_total\n";
    
    			print "TOTAL= $marbles\n";
    
    			if ($marbles == 0) {&determine_winner($computer_total,$player_total)};
    
    		}
    	}
    }
    
    sub determine_winner {
    	my $computer = shift;
    	my $player = shift;
    	print "THAT IS ALL OF THE MARBLES.\n\n";
    	print "MY TOTAL IS $computer, YOUR TOTAL IS $player\n";
    	if ($player % 2 == 0) {
    		print "     YOU WON.\n";
    	}
    	if ($computer % 2 == 0) {
    		print "     I WON.\n";
    	}
    	my $answer = -1;
    	until ($answer == 1 || $answer == 0) {
    		print "DO YOU WANT TO PLAY AGAIN? TYPE 1 FOR YES AND 0 FOR NO.\n";
    		$answer=;
    		chomp($answer);
    	}
    	if ($answer == 1) {
    		&game_play;
    	}
    	else {
    		print "OK. SEE YOU LATER.\n";
    		exit;
    	}
    }
    
    sub player_select {
    	my $marbles = shift;
    	my $turn = shift;
    	my $validity="invalid";
    	if ($turn == 0) {
    		print "WHAT IS YOUR FIRST MOVE\n";
    	}
    	else {
    		print "WHAT IS YOUR NEXT MOVE\n";
    	}
    	until ($validity eq "valid") {
    		my $num = ;
    		chomp($num);
    		my $validity=&validity_check($marbles,$num);
    		if ($validity eq "valid") {
    			return $num;
    		}
    	}
    }
    
    sub computer_select {
    	my $marbles = shift;
    	my $turn = shift;
    	my $player_total = shift;
    	my $num = 2;
    	my $validity = "invalid";
    	if ($turn == 0) {
    		print "I PICK UP $num MARBLES.\n\n";
    	}
    	else {
    		until ($validity eq "valid") {
    			my $R=$marbles-6*int(($marbles/6));
    
    			if (int($player_total/2) == $player_total/2) {
    				if ($R < 1.5 || $R > 5.3) {
    					$num = 1;
    				}
    				else {
    					$num = $R - 1;
    				}
    			}
    
    			elsif ($marbles < 4.2) {
    				$num = $marbles;
    			}
    			elsif ($R > 3.4) {
    				if ($R < 4.7 || $R > 3.5) {
    					$num = 4;
    				}
    			else {
    					$num = 1;
    				}
    			}
    			$validity=&validity_check($marbles,$num);
    		}
    		print "\nI PICK UP $num MARBLES.\n\n";
    	}
    	return $num;
    }
    
    sub validity_check {
    	my $marbles = shift;
    	my $num = shift;
    	if ($num > $marbles) {
    		print "YOU HAVE TRIED TO TAKE MORE MARBLES THAN THERE ARE LEFT. TRY AGAIN. THERE ARE $marbles MARBLES LEFT\n";
    		return "invalid";
    	}
    	if ($num > 0 && $num <= 4) {
    		return "valid";
    	}
    	if ($num < 1 || $num > 4) {
    		print "THE NUMBER OF MARBLES YOU TAKE MUST BE A POSITIVE INTEGER BETWEEN 1 AND 4\nWHAT IS YOUR NEXT MOVE?\n";
    		return "invalid";
    	}
    	else {
    		return "invalid";
    	}
    }
    
    sub print_intro {
    	print ' ' x 31 . "EVEN WINS\n";
    	print ' ' x 15 . "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n";
    	print "THIS IS A 2 PERSON GAME CALLED 'EVEN WINS'. TO PLAY THE GAME, THE PLAYERS NEED 27 MARBLES OR OTHER OBJECTS ON THE TABLE\n\n";
    	print "THE 2 PLAYERS ALTERNATE TURNS, WITH EACH PLAYER REMOVING FROM 1 TO 4 MARBLES ON EACH MOVE. THE GAME ENDS WHEN THERE ARE NO MARBLES LEFT, AND THE WINNER IS THE ONE WITH AN EVEN NUMBER OF MARBLES\n";
    	print "THE ONLY RULES ARE THAT (1) YOU MUST ALTERNATE TURNS, (2) YOU MUST TAKE BETWEEN 1 AND 4 MARBLES EACH TURN, AND (3) YOU CANNOT SKIP A TURN.\n\n";
    }
    
    
    ================================================
    FILE: 35_Even_Wins/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 35_Even_Wins/python/evenwins.py
    ================================================
    """
    This version of evenwins.bas based on game decscription and does *not*
    follow the source. The computer chooses marbles at random.
    
    For simplicity, global variables are used to store the game state.
    A good exercise would be to replace this with a class.
    The code is not short, but hopefully it is easy for beginners to understand
    and modify.
    
    Infinite loops of the style "while True:" are used to simplify some of the
    code. The "continue" keyword is used in a few places to jump back to the top
    of the loop. The "return" keyword is also used to break out of functions.
    This is generally considered poor style, but in this case it simplifies the
    code and makes it easier to read (at least in my opinion). A good exercise
    would be to remove these infinite loops, and uses of continue, to follow a
    more structured style.
    """
    
    
    from dataclasses import dataclass
    from typing import Literal, Tuple
    
    PlayerType = Literal["human", "computer"]
    
    
    @dataclass
    class MarbleCounts:
        middle: int
        human: int
        computer: int
    
    
    def print_intro() -> None:
        print("Welcome to Even Wins!")
        print("Based on evenwins.bas from Creative Computing")
        print()
        print("Even Wins is a two-person game. You start with")
        print("27 marbles in the middle of the table.")
        print()
        print("Players alternate taking marbles from the middle.")
        print("A player can take 1 to 4 marbles on their turn, and")
        print("turns cannot be skipped. The game ends when there are")
        print("no marbles left, and the winner is the one with an even")
        print("number of marbles.")
        print()
    
    
    def marbles_str(n: int) -> str:
        return "1 marble" if n == 1 else f"{n} marbles"
    
    
    def choose_first_player() -> PlayerType:
        while True:
            ans = input("Do you want to play first? (y/n) --> ")
            if ans == "y":
                return "human"
            elif ans == "n":
                return "computer"
            else:
                print()
                print('Please enter "y" if you want to play first,')
                print('or "n" if you want to play second.')
                print()
    
    
    def toggle_player(whose_turn: PlayerType) -> PlayerType:
        return "computer" if whose_turn == "human" else "human"
    
    
    def to_int(s: str) -> Tuple[bool, int]:
        """Convert a string s to an int, if possible."""
        try:
            n = int(s)
            return True, n
        except Exception:
            return False, 0
    
    
    def print_board(marbles: MarbleCounts) -> None:
        print()
        print(f" marbles in the middle: {marbles.middle} " + marbles.middle * "*")
        print(f"    # marbles you have: {marbles.human}")
        print(f"# marbles computer has: {marbles.computer}")
        print()
    
    
    def human_turn(marbles: MarbleCounts) -> None:
        """get number in range 1 to min(4, marbles.middle)"""
        max_choice = min(4, marbles.middle)
        print("It's your turn!")
        while True:
            s = input(f"Marbles to take? (1 - {max_choice}) --> ")
            ok, n = to_int(s)
            if not ok:
                print(f"\n  Please enter a whole number from 1 to {max_choice}\n")
                continue
            if n < 1:
                print("\n  You must take at least 1 marble!\n")
                continue
            if n > max_choice:
                print(f"\n  You can take at most {marbles_str(max_choice)}\n")
                continue
            print(f"\nOkay, taking {marbles_str(n)} ...")
            marbles.middle -= n
            marbles.human += n
            return
    
    
    def game_over(marbles: MarbleCounts) -> None:
        print()
        print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
        print("!! All the marbles are taken: Game Over!")
        print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
        print()
        print_board(marbles)
        if marbles.human % 2 == 0:
            print("You are the winner! Congratulations!")
        else:
            print("The computer wins: all hail mighty silicon!")
        print()
    
    
    def computer_turn(marbles: MarbleCounts) -> None:
        marbles_to_take = 0
    
        print("It's the computer's turn ...")
        r = marbles.middle - 6 * int(marbles.middle / 6)
    
        if int(marbles.human / 2) == marbles.human / 2:
            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:
            if r < 4.7 or r > 3.5:
                marbles_to_take = 4
        else:
            marbles_to_take = r + 1
    
        print(f"Computer takes {marbles_str(marbles_to_take)} ...")
        marbles.middle -= marbles_to_take
        marbles.computer += marbles_to_take
    
    
    def play_game(whose_turn: PlayerType) -> None:
        marbles = MarbleCounts(middle=27, human=0, computer=0)
        print_board(marbles)
    
        while True:
            if marbles.middle == 0:
                game_over(marbles)
                return
            elif whose_turn == "human":
                human_turn(marbles)
                print_board(marbles)
                whose_turn = toggle_player(whose_turn)
            elif whose_turn == "computer":
                computer_turn(marbles)
                print_board(marbles)
                whose_turn = toggle_player(whose_turn)
            else:
                raise Exception(f"whose_turn={whose_turn} is not 'human' or 'computer'")
    
    
    def main() -> None:
        print_intro()
    
        while True:
            whose_turn = choose_first_player()
            play_game(whose_turn)
    
            print()
            again = input("Would you like to play again? (y/n) --> ").lower()
            if again == "y":
                print("\nOk, let's play again ...\n")
            else:
                print("\nOk, thanks for playing ... goodbye!\n")
                return
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 35_Even_Wins/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 35_Even_Wins/rust/Cargo.toml
    ================================================
    [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]
    
    
    ================================================
    FILE: 35_Even_Wins/rust/src/main.rs
    ================================================
    use std::io;
    
    fn print_intro() {
        println!(
            "Welcome to Even Wins!
    Based on evenwins.bas from Creative Computing
    
    Even Wins is a two-person game. You start with
    27 marbles in the middle of the table.
    
    Players alternate taking marbles from the middle.
    A player can take 1 to 4 marbles on their turn, and
    turns cannot be skipped. The game ends when there are
    no marbles left, and the winner is the one with an even
    number of marbles.
    "
        );
    }
    
    #[derive(Debug)]
    enum PlayerType {
        Human,
        Computer,
    }
    
    #[derive(Debug)]
    struct Game {
        turn: PlayerType,
        middle: u32,
        human: u32,
        computer: u32,
        min_take: u32,
        max_take: u32,
    }
    
    impl Game {
        fn get_max_take(&mut self) -> u32 {
            if self.max_take > self.middle {
                return self.middle;
            }
            return self.max_take;
        }
    
        fn take(&mut self, num: u32) -> bool {
            let max_take = self.get_max_take();
            if num > max_take {
                println!("You can take at most {} marbles", max_take);
                return false;
            }
            if num < self.min_take {
                println!("You must take at least {} marble!", self.min_take);
                return false;
            }
    
            self.middle -= num;
            match self.turn {
                PlayerType::Computer => self.computer += num,
                PlayerType::Human => self.human += num,
            }
            return true;
        }
    
        fn next(&mut self) {
            self.turn = match self.turn {
                PlayerType::Computer => PlayerType::Human,
                PlayerType::Human => PlayerType::Computer,
            }
        }
    
        fn info(&mut self) {
            println!("");
            println!(
                "marbles in the middle: {} **************************",
                self.middle
            );
            println!("# marbles you have: {}", self.human);
            println!("# marbles computer has: {}", self.computer);
            println!("");
        }
    
        fn wininfo(&mut self) {
            if self.middle != 0 {
                return;
            }
            println!("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n!! All the marbles are taken: Game Over!\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
            self.info();
    
            if self.human % 2 == 0 {
                println!("You are the winner! Congratulations!");
            } else {
                println!("The computer wins: all hail mighty silicon!");
            }
    
            println!("");
        }
    }
    
    fn human_play(game: &mut Game) {
        println!("It's your turn!");
        loop {
            let max_take = game.get_max_take();
            println!("Marbles to take? ({} - {}) --> ", game.min_take, max_take);
    
            let mut num = String::new();
            io::stdin()
                .read_line(&mut num)
                .expect("Failed to read line");
    
            let _: u32 = match num.trim().to_uppercase().parse() {
                Ok(num) => {
                    if game.take(num) {
                        println!("Okay, taking {} marble ...", num);
                        break;
                    };
                    println!("");
                    continue;
                }
                _ => {
                    println!("Please enter a whole number from 1 to 4");
                    println!("");
                    continue;
                }
            };
        }
    }
    
    fn compute_play(game: &mut Game) {
        println!("It's the computer's turn ...");
    
        let marbles_to_take: u32;
    
        // the magic 6 and 1.5, 5.3 3.4 4.7 3.5 was copy from python implement
        let r: f32 = (game.middle % 6) as f32;
        if game.human % 2 == 0 {
            if r < 1.5 || r > 5.3 {
                marbles_to_take = 1;
            } else {
                marbles_to_take = (r - 1.0) as u32;
            }
        } else if game.middle <= 4 {
            marbles_to_take = game.middle
        } else if r > 3.4 {
            if r < 4.7 || r > 3.5 {
                marbles_to_take = 4;
            } else {
                marbles_to_take = 1;
            }
        } else {
            marbles_to_take = (r + 1.0) as u32;
        }
    
        game.take(marbles_to_take);
        println!("Computer takes {} marble ...", marbles_to_take);
    }
    
    fn run_game(turn: PlayerType) {
        let mut game = Game {
            turn: turn,
            middle: 27,
            computer: 0,
            human: 0,
            min_take: 1,
            max_take: 4,
        };
    
        while game.middle > 0 {
            match game.turn {
                PlayerType::Computer => {
                    compute_play(&mut game);
                }
                PlayerType::Human => {
                    human_play(&mut game);
                }
            }
            game.info();
            game.next();
        }
        game.wininfo();
    }
    
    fn choose_first_player() -> PlayerType {
        loop {
            println!("Do you want to play first? (y/n) -->");
    
            let mut flag = String::new();
            io::stdin()
                .read_line(&mut flag)
                .expect("Failed to read line");
    
            match flag.trim().to_uppercase().as_str() {
                "Y" => return PlayerType::Human,
                "N" => return PlayerType::Computer,
                _ => {
                    println!("Please enter \"y\" if you want to play first,\nor \"n\" if you want to play second.\n");
                }
            };
        }
    }
    
    fn choose_play_again() -> bool {
        println!("Would you like to play again? (y/n) --> ");
        let mut flag = String::new();
        io::stdin()
            .read_line(&mut flag)
            .expect("Failed to read line");
    
        match flag.trim().to_uppercase().as_str() {
            "Y" => {
                println!("\nOk, let's play again ...\n");
                return true;
            }
            _ => {
                println!("\nOk, thanks for playing ... goodbye!\n");
                return false;
            }
        }
    }
    
    fn main() {
        print_intro();
        loop {
            let first = choose_first_player();
            run_game(first);
    
            if !choose_play_again() {
                return;
            }
        }
    }
    
    
    ================================================
    FILE: 35_Even_Wins/vbnet/EvenWins.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "EvenWins", "EvenWins.vbproj", "{16D44A7A-9C05-4845-8289-3A65A4D978D0}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{16D44A7A-9C05-4845-8289-3A65A4D978D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{16D44A7A-9C05-4845-8289-3A65A4D978D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{16D44A7A-9C05-4845-8289-3A65A4D978D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{16D44A7A-9C05-4845-8289-3A65A4D978D0}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 35_Even_Wins/vbnet/EvenWins.vbproj
    ================================================
    
      
        Exe
        EvenWins
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 35_Even_Wins/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 36_Flip_Flop/README.md
    ================================================
    ### Flip Flop
    
    The object of this game is to change a row of ten X’s
    
    ```
    X X X X X X X X X X
    ```
    
    to a row of ten 0’s
    
    ```
    0 0 0 0 0 0 0 0 0 0
    ```
    
    by typing in a number corresponding to the position of an “X” in the line. On some numbers one position will change while on other numbers, two will change. For example, inputting a 3 may reverse the X and 0 in position 3, but it might possibly reverse some of other position too! You ought to be able to change all 10 in 12 or fewer moves. Can you figure out a good winning strategy?
    
    To reset the line to all X’s (same game), type 0 (zero). To start a new game at any point, type 11.
    
    The original author of this game was Micheal Kass of New Hyde Park, New York.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=63)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=78)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 36_Flip_Flop/csharp/FlipFlop.cs
    ================================================
    // Flip Flop Game
    
    PrintGameInfo();
    
    bool startNewGame = true;
    
    string[] board = new string[] { "X", "X", "X", "X", "X", "X", "X", "X", "X", "X" };
    
    do
    {
        int stepsCount = 0;
        int lastMove = -1;
        int moveIndex;
        int gameSum;
        double gameEntropyRate = Rnd();
        bool toPlay = false;
        bool setNewBoard = true;
    
        Print();
        Print("HERE IS THE STARTING LINE OF X'S.");
        Print();
    
        do
        {
            bool illegalEntry;
            bool equalToLastMove;
    
            if (setNewBoard)
            {
                PrintNewBoard();
                board = new string[] { "X", "X", "X", "X", "X", "X", "X", "X", "X", "X" };
                setNewBoard = false;
                toPlay = true;
            }
    
            stepsCount++;
            gameSum = 0;
    
            // Read User's move
            do
            {
                Write("INPUT THE NUMBER? ");
                var input = Console.ReadLine();
                illegalEntry = !int.TryParse(input, out moveIndex);
    
                if (illegalEntry || moveIndex > 11)
                {
                    illegalEntry = true;
                    Print("ILLEGAL ENTRY--TRY AGAIN.");
                }
            }
            while (illegalEntry);
    
            if (moveIndex == 11)
            {
                // Run new game, To start a new game at any point
                toPlay = false;
                stepsCount = 12;
                startNewGame = true;
            }
    
    
            if (moveIndex == 0)
            {
                // To reset the line to all X, same game
                setNewBoard = true;
                toPlay = false;
            }
    
            if (toPlay)
            {
                board[moveIndex - 1] = board[moveIndex - 1] == "O" ? "X" : "O";
    
                if (lastMove == moveIndex)
                {
                    equalToLastMove = true;
                }
                else
                {
                    equalToLastMove = false;
                    lastMove = moveIndex;
                }
    
                do
                {
                    moveIndex = equalToLastMove
                        ? GetMoveIndexWhenEqualeLastMove(moveIndex, gameEntropyRate)
                        : GetMoveIndex(moveIndex, gameEntropyRate);
    
                    board[moveIndex] = board[moveIndex] == "O" ? "X" : "O";
                }
                while (lastMove == moveIndex && board[moveIndex] == "X");
    
                PrintGameBoard(board);
    
                foreach (var item in board)
                {
                    if (item == "O")
                    {
                        gameSum++;
                    }
                }
            }
        }
        while (stepsCount < 12 && gameSum < 10);
    
        if (toPlay)
        {
            PrintGameResult(gameSum, stepsCount);
    
            Write("DO YOU WANT TO TRY ANOTHER PUZZLE ");
    
            var toContinue = Console.ReadLine();
    
            if (!string.IsNullOrEmpty(toContinue) && toContinue?.ToUpper()[0] == 'N')
            {
                startNewGame = false;
            }
    
            Print();
        }
    }
    while (startNewGame);
    
    void Print(string str = "") => Console.WriteLine(str);
    
    void Write(string value) => Console.Write(value);
    
    string Tab(int pos) => new(' ', pos);
    
    double Rnd() => new Random().NextDouble();
    
    int GetMoveIndex(int moveIndex, double gameEntropyRate)
    {
        double rate = Math.Tan(gameEntropyRate + moveIndex / gameEntropyRate - moveIndex) - Math.Sin(gameEntropyRate / moveIndex) + 336 * Math.Sin(8 * moveIndex);
        return Convert.ToInt32(Math.Floor(10 * (rate - Math.Floor(rate))));
    }
    
    int GetMoveIndexWhenEqualeLastMove(int moveIndex, double gameEntropyRate)
    {
        double rate = 0.592 * (1 / Math.Tan(gameEntropyRate / moveIndex + gameEntropyRate)) / Math.Sin(moveIndex * 2 + gameEntropyRate) - Math.Cos(moveIndex);
        return Convert.ToInt32(Math.Floor(10 * (rate - Math.Floor(rate))));
    }
    
    void PrintNewBoard()
    {
        Print("1 2 3 4 5 6 7 8 9 10");
        Print("X X X X X X X X X X");
        Print();
    }
    
    void PrintGameBoard(string[] board)
    {
        Print("1 2 3 4 5 6 7 8 9 10");
    
        foreach (var item in board)
        {
            Write($"{item} ");
        }
    
        Print();
        Print();
    }
    
    void PrintGameResult(int gameSum, int stepsCount)
    {
        if (gameSum == 10)
        {
            Print($"VERY GOOD.  YOU GUESSED IT IN ONLY {stepsCount} GUESSES.");
        }
        else
        {
            Print($"TRY HARDER NEXT TIME.  IT TOOK YOU {stepsCount} GUESSES.");
        }
    }
    
    void PrintGameInfo()
    {
        Print(Tab(32) + "FLIPFLOP");
        Print(Tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
        Print();
        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).");
    }
    
    
    ================================================
    FILE: 36_Flip_Flop/csharp/FlipFlop.csproj
    ================================================
    
    
      
        Exe
        net6.0
        enable
        enable
      
    
    
    
    
    ================================================
    FILE: 36_Flip_Flop/csharp/FlipFlop.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 17
    VisualStudioVersion = 17.0.32014.148
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FlipFlop", "FlipFlop.csproj", "{192EDAD4-5EF5-4B11-9EB3-B17FFAD0861F}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{192EDAD4-5EF5-4B11-9EB3-B17FFAD0861F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{192EDAD4-5EF5-4B11-9EB3-B17FFAD0861F}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{192EDAD4-5EF5-4B11-9EB3-B17FFAD0861F}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{192EDAD4-5EF5-4B11-9EB3-B17FFAD0861F}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {108E5099-D7AA-4260-B587-1B1FE1AF6B54}
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 36_Flip_Flop/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 36_Flip_Flop/flipflop.bas
    ================================================
    2 PRINT TAB(32);"FLIPFLOP"
    4 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    6 PRINT
    10 REM *** CREATED BY MICHAEL CASS
    15 DIM A$(20)
    20 PRINT "THE OBJECT OF THIS PUZZLE IS TO CHANGE THIS:"
    30 PRINT
    40 PRINT "X X X X X X X X X X"
    50 PRINT
    60 PRINT "TO THIS:"
    70 PRINT
    80 PRINT "O O O O O O O O O O"
    90 PRINT
    100 PRINT "BY TYPING THE NUMBER CORRESPONDING TO THE POSITION OF THE"
    110 PRINT "LETTER ON SOME NUMBERS, ONE POSITION WILL CHANGE, ON"
    120 PRINT "OTHERS, TWO WILL CHANGE.  TO RESET LINE TO ALL X'S, TYPE 0"
    130 PRINT "(ZERO) AND TO START OVER IN THE MIDDLE OF A GAME, TYPE "
    140 PRINT "11 (ELEVEN)."
    170 PRINT
    180 REM
    190 Q=RND(1)
    200 PRINT "HERE IS THE STARTING LINE OF X'S."
    210 PRINT
    220 C=0
    230 PRINT "1 2 3 4 5 6 7 8 9 10"
    240 PRINT "X X X X X X X X X X"
    250 PRINT
    260 REM
    270 FOR X=1 TO 10
    280 A$(X)="X"
    290 NEXT X
    300 GOTO 320
    310 PRINT "ILLEGAL ENTRY--TRY AGAIN."
    320 PRINT "INPUT THE NUMBER";
    330 INPUT N
    340 IF N<>INT(N) THEN 310
    350 IF N=11 THEN 180
    360 IF N>11 THEN 310
    370 IF N=0 THEN 230
    380 IF M=N THEN 510
    390 M=N
    400 IF A$(N)="O" THEN 480
    410 A$(N)="O"
    420 R=TAN(Q+N/Q-N)-SIN(Q/N)+336*SIN(8*N)
    430 N=R-INT(R)
    440 N=INT(10*N)
    450 IF A$(N)="O" THEN 480
    460 A$(N)="O"
    470 GOTO 610
    480 A$(N)="X"
    490 IF M=N THEN 420
    500 GOTO 610
    510 IF A$(N)="O" THEN 590
    520 A$(N)="O"
    530 R=.592*(1/TAN(Q/N+Q))/SIN(N*2+Q)-COS(N)
    540 N=R-INT(R)
    550 N=INT(10*N)
    560 IF A$(N)="O" THEN 590
    570 A$(N)="O"
    580 GOTO 610
    590 A$(N)="X"
    600 IF M=N THEN 530
    610 PRINT "1 2 3 4 5 6 7 8 9 10"
    620 FOR Z=1 TO 10: PRINT A$(Z);" ";: NEXT Z
    630 C=C+1
    640 PRINT
    650 FOR Z=1 TO 10
    660 IF A$(Z)<>"O" THEN 320
    670 NEXT Z
    680 IF C>12 THEN 710
    690 PRINT "VERY GOOD.  YOU GUESSED IT IN ONLY";C;"GUESSES."
    700 GOTO 720
    710 PRINT "TRY HARDER NEXT TIME.  IT TOOK YOU";C;"GUESSES."
    720 PRINT "DO YOU WANT TO TRY ANOTHER PUZZLE";
    730 INPUT X$
    740 IF LEFT$(X$,1)="N" THEN 780
    760 PRINT
    770 GOTO 180
    780 END
    
    
    ================================================
    FILE: 36_Flip_Flop/java/FlipFlop.java
    ================================================
    import java.util.Scanner;
    import java.lang.Math;
    
    /**
     * Game of FlipFlop
     * 

    * Based on the BASIC game of FlipFlop here * https://github.com/coding-horror/basic-computer-games/blob/main/36%20Flip%20Flop/flipflop.bas *

    * Note: The idea was to create a version of the 1970's BASIC game in Java, without introducing * new features - no additional text, error checking, etc has been added. * * Converted from BASIC to Java by Darren Cardenas. */ public class FlipFlop { private final Scanner scan; // For user input private enum Step { RANDOMIZE, INIT_BOARD, GET_NUMBER, ILLEGAL_ENTRY, FLIP_POSITION, SET_X_FIRST, SET_X_SECOND, GENERATE_R_FIRST, GENERATE_R_SECOND, PRINT_BOARD, QUERY_RETRY } public FlipFlop() { scan = new Scanner(System.in); } // End of constructor FlipFlop public void play() { showIntro(); startGame(); } // End of method play private static void showIntro() { System.out.println(" ".repeat(31) + "FLIPFLOP"); System.out.println(" ".repeat(14) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println(""); } // End of method showIntro private void startGame() { double mathVal = 0; double randVal = 0; double tmpVal = 0; int index = 0; int match = 0; int numFlip = 0; int numGuesses = 0; Step nextStep = Step.RANDOMIZE; String userResponse = ""; String[] board = new String[21]; System.out.println("THE OBJECT OF THIS PUZZLE IS TO CHANGE THIS:"); System.out.println(""); System.out.println("X X X X X X X X X X"); System.out.println(""); System.out.println("TO THIS:"); System.out.println(""); System.out.println("O O O O O O O O O O"); System.out.println(""); System.out.println("BY TYPING THE NUMBER CORRESPONDING TO THE POSITION OF THE"); System.out.println("LETTER ON SOME NUMBERS, ONE POSITION WILL CHANGE, ON"); System.out.println("OTHERS, TWO WILL CHANGE. TO RESET LINE TO ALL X'S, TYPE 0"); System.out.println("(ZERO) AND TO START OVER IN THE MIDDLE OF A GAME, TYPE "); System.out.println("11 (ELEVEN)."); System.out.println(""); // Begin outer while loop while (true) { // Begin switch switch (nextStep) { case RANDOMIZE: randVal = Math.random(); System.out.println("HERE IS THE STARTING LINE OF X'S."); System.out.println(""); numGuesses = 0; nextStep = Step.INIT_BOARD; break; case INIT_BOARD: System.out.println("1 2 3 4 5 6 7 8 9 10"); System.out.println("X X X X X X X X X X"); System.out.println(""); // Avoid out of bounds error by starting at zero for (index = 0; index <= 10; index++) { board[index] = "X"; } nextStep = Step.GET_NUMBER; break; case GET_NUMBER: System.out.print("INPUT THE NUMBER? "); userResponse = scan.nextLine(); try { numFlip = Integer.parseInt(userResponse); } catch (NumberFormatException ex) { nextStep = Step.ILLEGAL_ENTRY; break; } // Command to start a new game if (numFlip == 11) { nextStep = Step.RANDOMIZE; break; } if (numFlip > 11) { nextStep = Step.ILLEGAL_ENTRY; break; } // Command to reset the board if (numFlip == 0) { nextStep = Step.INIT_BOARD; break; } if (match == numFlip) { nextStep = Step.FLIP_POSITION; break; } match = numFlip; if (board[numFlip].equals("O")) { nextStep = Step.SET_X_FIRST; break; } board[numFlip] = "O"; nextStep = Step.GENERATE_R_FIRST; break; case ILLEGAL_ENTRY: System.out.println("ILLEGAL ENTRY--TRY AGAIN."); nextStep = Step.GET_NUMBER; break; case GENERATE_R_FIRST: mathVal = Math.tan(randVal + numFlip / randVal - numFlip) - Math.sin(randVal / numFlip) + 336 * Math.sin(8 * numFlip); tmpVal = mathVal - (int)Math.floor(mathVal); numFlip = (int)(10 * tmpVal); if (board[numFlip].equals("O")) { nextStep = Step.SET_X_FIRST; break; } board[numFlip] = "O"; nextStep = Step.PRINT_BOARD; break; case SET_X_FIRST: board[numFlip] = "X"; if (match == numFlip) { nextStep = Step.GENERATE_R_FIRST; } else { nextStep = Step.PRINT_BOARD; } break; case FLIP_POSITION: if (board[numFlip].equals("O")) { nextStep = Step.SET_X_SECOND; break; } board[numFlip] = "O"; nextStep = Step.GENERATE_R_SECOND; break; case GENERATE_R_SECOND: mathVal = 0.592 * (1 / Math.tan(randVal / numFlip + randVal)) / Math.sin(numFlip * 2 + randVal) - Math.cos(numFlip); tmpVal = mathVal - (int)mathVal; numFlip = (int)(10 * tmpVal); if (board[numFlip].equals("O")) { nextStep = Step.SET_X_SECOND; break; } board[numFlip] = "O"; nextStep = Step.PRINT_BOARD; break; case SET_X_SECOND: board[numFlip] = "X"; if (match == numFlip) { nextStep = Step.GENERATE_R_SECOND; break; } nextStep = Step.PRINT_BOARD; break; case PRINT_BOARD: System.out.println("1 2 3 4 5 6 7 8 9 10"); for (index = 1; index <= 10; index++) { System.out.print(board[index] + " "); } numGuesses++; System.out.println(""); for (index = 1; index <= 10; index++) { if (!board[index].equals("O")) { nextStep = Step.GET_NUMBER; break; } } if (nextStep == Step.GET_NUMBER) { break; } if (numGuesses > 12) { System.out.println("TRY HARDER NEXT TIME. IT TOOK YOU " + numGuesses + " GUESSES."); } else { System.out.println("VERY GOOD. YOU GUESSED IT IN ONLY " + numGuesses + " GUESSES."); } nextStep = Step.QUERY_RETRY; break; case QUERY_RETRY: System.out.print("DO YOU WANT TO TRY ANOTHER PUZZLE? "); userResponse = scan.nextLine(); if (userResponse.toUpperCase().charAt(0) == 'N') { return; } System.out.println(""); nextStep = Step.RANDOMIZE; break; default: System.out.println("INVALID STEP"); nextStep = Step.QUERY_RETRY; break; } // End of switch } // End outer while loop } // End of method startGame public static void main(String[] args) { FlipFlop game = new FlipFlop(); game.play(); } // End of method main } // End of class FlipFlop ================================================ FILE: 36_Flip_Flop/java/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Oracle Java](https://openjdk.java.net/) ================================================ FILE: 36_Flip_Flop/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 36_Flip_Flop/javascript/flipflop.html ================================================ FLIPFLOP

    
    
    
    
    
    
    ================================================
    FILE: 36_Flip_Flop/javascript/flipflop.js
    ================================================
    // FLIPFLOP
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    var as = [];
    
    // Main program
    async function main()
    {
        print(tab(32) + "FLIPFLOP\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        // *** Created by Michael Cass
        print("THE OBJECT OF THIS PUZZLE IS TO CHANGE THIS:\n");
        print("\n");
        print("X X X X X X X X X X\n");
        print("\n");
        print("TO THIS:\n");
        print("\n");
        print("O O O O O O O O O O\n");
        print("\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");
        print("\n");
        while (1) {
            start = 1;
            do {
                z = 1;
                if (start == 1) {
                    m = 0;
                    q = Math.random();
                    print("HERE IS THE STARTING LINE OF X'S.\n");
                    print("\n");
                    c = 0;
                    start = 2;
                }
                if (start == 2) {
                    print("1 2 3 4 5 6 7 8 9 10\n");
                    print("X X X X X X X X X X\n");
                    print("\n");
                    for (x = 1; x <= 10; x++)
                        as[x] = "X";
                    start = 0;
                }
                print("INPUT THE NUMBER");
                while (1) {
                    n = parseInt(await input());
                    if (n >= 0 && n <= 11)
                        break;
                    print("ILLEGAL ENTRY--TRY AGAIN.\n");
                }
                if (n == 11) {
                    start = 1;
                    continue;
                }
                if (n == 0) {
                    start = 2;
                    continue;
                }
                if (m != n) {
                    m = n;
                    as[n] = (as[n] == "O" ? "X" : "O");
                    do {
                        r = Math.tan(q + n / q - n) - Math.sin(q / n) + 336 * Math.sin(8 * n);
                        n = r - Math.floor(r);
                        n = Math.floor(10 * n);
                        as[n] = (as[n] == "O" ? "X" : "O");
                    } while (m == n) ;
                } else {
                    as[n] = (as[n] == "O" ? "X" : "O");
                    do {
                        r = 0.592 * (1 / Math.tan(q / n + q)) / Math.sin(n * 2 + q) - Math.cos(n);
                        n = r - Math.floor(r);
                        n = Math.floor(10 * n);
                        as[n] = (as[n] == "O" ? "X" : "O");
                    } while (m == n) ;
                }
                print("1 2 3 4 5 6 7 8 9 10\n");
                for (z = 1; z <= 10; z++)
                    print(as[z] + " ");
                c++;
                print("\n");
                for (z = 1; z <= 10; z++) {
                    if (as[z] != "O")
                        break;
                }
            } while (z <= 10) ;
            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");
            }
            print("DO YOU WANT TO TRY ANOTHER PUZZLE");
            str = await input();
            if (str.substr(0, 1) == "N")
                break;
        }
        print("\n");
    }
    
    main();
    
    
    ================================================
    FILE: 36_Flip_Flop/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 36_Flip_Flop/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 36_Flip_Flop/lua/flipflop-game.lua
    ================================================
    --[[
    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()
    
    
    ================================================
    FILE: 36_Flip_Flop/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 36_Flip_Flop/perl/flipflop.pl
    ================================================
    #!/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);
    }
    
    
    ================================================
    FILE: 36_Flip_Flop/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 36_Flip_Flop/python/flipflop.py
    ================================================
    # Flip Flop
    #
    # The object of this game is to change a row of ten X's
    # X X X X X X X X X X
    # to a row of ten O's:
    # O O O O O O O O O O
    # by typing in a number corresponding
    # to the position of an "X" in the line. On
    # some numbers one position will
    # change while on other numbers, two
    # will change. For example, inputting a 3
    # may reverse the X and O in position 3,
    # but it might possibly reverse some
    # other position too! You ought to be able
    # to change all 10 in 12 or fewer
    # moves. Can you figure out a good win-
    # ning strategy?
    # To reset the line to all X's (same
    # game), type 0 (zero). To start a new
    # game at any point, type 11.
    # The original author of this game was
    # Michael Kass of New Hyde Park, New
    # York.
    import math
    import random
    from typing import Callable, List, Tuple
    
    flip_dict = {"X": "O", "O": "X"}
    
    
    def flip_bits(
        row: List[str], m: int, n: int, r_function: Callable[[int], float]
    ) -> Tuple[List[str], int]:
        """
        Function that flips the positions at the computed steps
        """
        while m == n:
            r = r_function(n)
            n_tmp = r - int(math.floor(r))
            n = int(10 * n_tmp)
            if row[n] == "X":
                row[n] = "O"
                break
            elif row[n] == "O":
                row[n] = "X"
        return row, n
    
    
    def print_instructions() -> None:
        print(" " * 32 + "FLIPFLOP")
        print(" " * 15 + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY")
        print("\n" * 2)
        print("THE OBJECT OF THIS PUZZLE IS TO CHANGE THIS:\n")
        print("X X X X X X X X X X\n")
        print("TO THIS:\n")
        print("O O O O O O O O O O\n")
        print("BY TYPING TH NUMBER CORRESPONDING TO THE POSITION OF THE")
        print("LETTER ON SOME NUMBERS, ONE POSITION WILL CHANGE, ON")
        print("OTHERS, TWO WILL CHANGE. TO RESET LINE TO ALL X'S, TYPE 0")
        print("(ZERO) AND TO START OVER IN THE MIDDLE OF A GAME, TYPE ")
        print("11 (ELEVEN).\n")
    
    
    def main() -> None:
        q = random.random()
    
        print("HERE IS THE STARTING LINE OF X'S.\n")
        # We add an extra 0-th item because this sometimes is set to something
        # but we never check what it is for completion of the puzzle
        row = [""] + ["X"] * 10
        counter_turns = 0
        n = -1
        legal_move = True
        while row[1:] != ["O"] * 10:
            if legal_move:
                print(" ".join([str(i) for i in range(1, 11)]))
                print(" ".join(row[1:]) + "\n")
            m_str = input("INPUT THE NUMBER\n")
            try:
                m = int(m_str)
                if m > 11 or m < 0:
                    raise ValueError()
            except ValueError:
                print("ILLEGAL ENTRY--TRY AGAIN")
                legal_move = False
                continue
            legal_move = True
            if m == 11:
                # completely reset the puzzle
                counter_turns = 0
                row = [""] + ["X"] * 10
                q = random.random()
                continue
            elif m == 0:
                # reset the board, but not the counter or the random number
                row = [""] + ["X"] * 10
            elif m == n:
                row[n] = flip_dict[row[n]]
                r_function = lambda n_t: 0.592 * (1 / math.tan(q / n_t + q)) / math.sin(
                    n_t * 2 + q
                ) - math.cos(n_t)
                row, n = flip_bits(row, m, n, r_function)
            else:
                n = m
                row[n] = flip_dict[row[n]]
                r_function = lambda n_t: (
                    math.tan(q + n_t / q - n_t)
                    - math.sin(n_t * 2 + q)
                    + 336 * math.sin(8 * n_t)
                )
                row, n = flip_bits(row, m, n, r_function)
    
            counter_turns += 1
            print()
    
        if counter_turns <= 12:
            print(f"VERY GOOD. YOU GUESSED IT IN ONLY {counter_turns} GUESSES.")
        else:
            print(f"TRY HARDER NEXT TIME. IT TOOK YOU {counter_turns} GUESSES.")
        return
    
    
    if __name__ == "__main__":
        print_instructions()
    
        another = ""
        while another != "NO":
            main()
            another = input("DO YOU WANT TO TRY ANOTHER PUZZLE\n")
    
    
    ================================================
    FILE: 36_Flip_Flop/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 36_Flip_Flop/ruby/flipflop.rb
    ================================================
    #A class representing the internal state of a single game of flip flop
    # state represents the list of X's (A in the original code)
    # guesses represents the number of guesses the user has made (C in the original code)
    # seed represents the random seed for an instance of the game (Q in the original code)
    Game = Struct.new(:state, :guesses, :seed) do
    
      #The original BASIC program used 1 indexed arrays while Ruby has 0-indexed arrays.
      #We can't use 0 indexed arrays for the flip functions or we'll get divide by zero errors.
      #These convenience functions allow us to modify and access internal game state in a 1-indexed fashion
      def flip_letter(letter_number)
        index = letter_number -1
        if self.state[index] == 'X'
          self.state[index] = 'O'
        else
          self.state[index] = 'X'
        end
      end
    
      def letter_at(letter_number)
        self.state[letter_number - 1]
      end
    end
    
    def print_welcome
      puts 'FLIPFLOP'.center(72)
      puts 'CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY'.center(72)
      puts <<~EOS
    
        THE OBJECT OF THIS PUZZLE IS TO CHANGE THIS:
    
        X X X X X X X X X X
    
        TO THIS:
    
        O O O O O O O O O O
    
        BY TYPING THE NUMBER CORRESPONDING TO THE POSITION OF THE
        LETTER ON SOME NUMBERS, ONE POSITION WILL CHANGE, ON
        OTHERS, TWO WILL CHANGE.  TO RESET LINE TO ALL X'S, TYPE 0
        (ZERO) AND TO START OVER IN THE MIDDLE OF A GAME, TYPE
        11 (ELEVEN).
      EOS
    end
    
    def print_starting_message
      puts <<~EOS
    
      HERE IS THE STARTING LINE OF X'S.
    
      1 2 3 4 5 6 7 8 9 10
      X X X X X X X X X X
    
      EOS
    end
    
    #Create a new game with [X,X,X,X,X,X,X,X,X,X] as the state
    #0 as the number of guesses and a random seed between 0 and 1
    def generate_new_game
      Game.new(Array.new(10, 'X'), 0, rand())
    end
    
    #Given a game, an index, and a shuffle function, flip one or more letters
    def shuffle_board(game, index, shuffle_function)
      n = method(shuffle_function).call(game, index)
    
      if game.letter_at(n) == "O"
        game.flip_letter(n)
        if index == n
          n = shuffle_board(game, index, shuffle_function)
        end
      else
        game.flip_letter(n)
      end
      return n
    end
    
    #Shuffle logic copied from original BASIC code
    def shuffle_function1(game, index)
      r = Math.tan(game.seed + index / game.seed - index) - Math.sin(game.seed / index) + 336 * Math.sin(8 * index)
      n = r - r.floor
      (10 * n).floor
    end
    
    def shuffle_function2(game, index)
      r = 0.592 * (1/ Math.tan(game.seed / index + game.seed)) / Math.sin(index * 2 + game.seed) - Math.cos(index)
      n = r - r.floor
      (10 * n)
    end
    
    def play_game
      print_starting_message
      game = generate_new_game
      working_index = nil
    
      loop do
        puts "INPUT THE NUMBER"
        input = gets.chomp.downcase
    
        #See if the user input a valid integer, fail otherwise
        if numeric_input = Integer(input, exception: false)
    
          #If 11 is entered, we're done with this version of the game
          if numeric_input == 11
            return :restart
          end
    
          if numeric_input > 11
            puts 'ILLEGAL ENTRY--TRY AGAIN.'
            next #illegal entries don't count towards your guesses
          end
    
          if working_index == numeric_input
            game.flip_letter(numeric_input)
            working_index = shuffle_board(game, numeric_input, :shuffle_function2)
          #If 0 is entered, we want to reset the state, but not the random seed or number of guesses and keep playing
          elsif numeric_input == 0
            game.state = Array.new(10, 'X')
          elsif game.letter_at(numeric_input) == "O"
            game.flip_letter(numeric_input)
            if numeric_input == working_index
              working_index = shuffle_board(game, numeric_input, :shuffle_function1)
            end
          else
            game.flip_letter(numeric_input)
            working_index = shuffle_board(game, numeric_input, :shuffle_function1)
          end
        else
          puts 'ILLEGAL ENTRY--TRY AGAIN.'
          next #illegal entries don't count towards your guesses
        end
    
        game.guesses += 1
        puts '1 2 3 4 5 6 7 8 9 10'
        puts game.state.join(' ')
    
        if game.state.all? { |x| x == 'O' }
          if game.guesses > 12
            puts "TRY HARDER NEXT TIME. IT TOOK YOU #{game.guesses} GUESSES."
          else
            puts "VERY GOOD.  YOU GUESSED IT IN ONLY #{game.guesses} GUESSES."
          end
          #game is complete
          return
        end
      end
    end
    
    
    
    #Execution starts
    print_welcome
    loop do
      result = play_game
      if result == :restart
        next
      end
    
      puts 'DO YOU WANT TO TRY ANOTHER PUZZLE'
      if gets.chomp.downcase[0] == 'n'
        break
      end
    end
    
    
    ================================================
    FILE: 36_Flip_Flop/rust/Cargo.toml
    ================================================
    [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"
    morristown = "0.1.3"
    
    
    ================================================
    FILE: 36_Flip_Flop/rust/src/game.rs
    ================================================
    pub struct Game {
        board: [char; 10],
        last_move: u8,
        entropy: f32,
        tries: u8,
    }
    
    impl Game {
        pub fn new() -> Self {
            Game {
                board: ['X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X'],
                last_move: 0,
                entropy: 0.,
                tries: 0,
            }
        }
    
        pub fn play(&mut self) -> bool {
            self.reset_game();
    
            println!("\nHERE IS THE STARTING LINE OF X'S.\n");
            println!("1 2 3 4 5 6 7 8 9 10");
            println!("X X X X X X X X X X\n");
    
            let mut reset = false;
    
            loop {
                self.tries += 1;
    
                match Game::get_number() {
                    0 => self.reset_board(),
                    11 => {
                        reset = true;
                        break;
                    }
                    n => {
                        self.flip(n);
    
                        let other = self.get_other(n, n == self.last_move);
                        if other != n {
                            self.flip(other);
                        }
    
                        println!("other: {}", other);
                        self.last_move = n;
                    }
                }
                self.draw();
    
                if !self.board.iter().any(|c| *c == 'X') {
                    break;
                }
            }
    
            if !reset {
                let t = self.tries;
    
                if t > 12 {
                    println!("TRY HARDER NEXT TIME. IT TOOK YOU {t} GUESSES.");
                } else {
                    println!("VERY GOOD. IT TOOK YOU ONLY {t} GUESSES.");
                }
    
                return morristown::prompt_bool("DO YOU WANT TO TRY ANOTHER PUZZLE?", false);
            }
    
            true
        }
    
        fn flip(&mut self, i: u8) {
            if (1..=10).contains(&i) {
                let i = (i - 1) as usize;
                let char = &mut self.board[i];
    
                match char {
                    'X' => *char = '0',
                    '0' => *char = 'X',
                    _ => println!("INVALID BOARD CHARACTER!"),
                }
            }
        }
    
        fn get_other(&self, m: u8, equals_last_move: bool) -> u8 {
            let e = self.entropy;
            let m = m as f32;
    
            let rate = if equals_last_move {
                0.592 * (1. / (e / m + e).tan()) / (m * 2. + e).sin() - m.cos()
            } else {
                (e + m / e - m).tan() - (e / m).sin() + 336. * (8. * m).sin()
            };
    
            (10. * (rate - rate.floor())).floor() as u8
        }
    
        fn draw(&self) {
            println!("1 2 3 4 5 6 7 8 9 10");
            for c in self.board {
                print!("{c} ");
            }
            println!();
        }
    
        fn get_number() -> u8 {
            loop {
                let n = morristown::prompt_number::("INPUT THE NUMBER?");
                if n > 11 {
                    println!("ILLEGAL ENTRY--TRY AGAIN.");
                } else {
                    return n;
                }
            }
        }
    
        fn reset_board(&mut self) {
            self.board = ['X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X'];
        }
    
        fn reset_game(&mut self) {
            self.reset_board();
            self.last_move = 0;
            self.entropy = rand::random();
            self.tries = 0;
        }
    }
    
    
    ================================================
    FILE: 36_Flip_Flop/rust/src/main.rs
    ================================================
    mod game;
    use crate::game::Game;
    
    fn main() {
        morristown::print_intro("FLIPFLOP");
    
        println!("THE OBJECT OF THIS PUZZLE IS TO CHANGE THIS:\n");
        println!("X X X X X X X X X X\n");
        println!("TO THIS:\n");
        println!("O O O O O O O O O O\n");
        println!("BY TYPING THE NUMBER CORRESPONDING TO THE POSITION OF THE");
        println!("LETTER ON SOME NUMBERS, ONE POSITION WILL CHANGE, ON");
        println!("OTHERS, TWO WILL CHANGE.  TO RESET LINE TO ALL X'S, TYPE 0");
        println!("(ZERO) AND TO START OVER IN THE MIDDLE OF A GAME, TYPE ");
        println!("11 (ELEVEN).");
    
        let mut game = Game::new();
    
        loop {
            if !game.play() {
                break;
            }
        }
    }
    
    
    ================================================
    FILE: 36_Flip_Flop/vbnet/FlipFlop.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "FlipFlop", "FlipFlop.vbproj", "{4A5887DD-FA0C-47EE-B3A2-E334DCE3544C}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{4A5887DD-FA0C-47EE-B3A2-E334DCE3544C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{4A5887DD-FA0C-47EE-B3A2-E334DCE3544C}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{4A5887DD-FA0C-47EE-B3A2-E334DCE3544C}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{4A5887DD-FA0C-47EE-B3A2-E334DCE3544C}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 36_Flip_Flop/vbnet/FlipFlop.vbproj
    ================================================
    
      
        Exe
        FlipFlop
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 36_Flip_Flop/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 37_Football/README.md
    ================================================
    ### Football
    
    Football is probably the most popular simulated sports game. I have seen some people play to elect to play computerized football in preference to watching a football game on television.
    
    Two versions of football are presented. The first is somewhat “traditional” in that you, the player, are playing against the computer. You have a choice of seven offensive plays. On defense the computer seems to play a zone defence, but you have no choice of plays. The computer program presents the necessary rules as you play, and it is also the referee and determines penalties when an infraction is committed. FTBALL was written by John Kemeny at Dartmouth.
    
    IN the second version of football, the computer referees a game played between two human players. Each player gets a list of twenty plays with a code value for each one. This list should be kept confidential from your opponent. The codes can be changes in data. All twenty plays are offensive; a defensive play is specified by defending against a type of offensive play. A defense is good for other similar types of plays, for example, a defense against a flare pass is very good against a screen pass but much less good against a half-back option.
    
    This game was originally written by Raymond Miseyka of Butler, Pennsylvania.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=64)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=79)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 37_Football/csharp/Football.csproj
    ================================================
    
      
        Exe
        net6.0
        10
        enable
        enable
      
    
    
    
    ================================================
    FILE: 37_Football/csharp/Football.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Football", "Football.csproj", "{092442FA-EA04-4A80-AB12-138E18CD480A}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{092442FA-EA04-4A80-AB12-138E18CD480A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{092442FA-EA04-4A80-AB12-138E18CD480A}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{092442FA-EA04-4A80-AB12-138E18CD480A}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{092442FA-EA04-4A80-AB12-138E18CD480A}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 37_Football/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 37_Football/football.bas
    ================================================
    1 PRINT TAB(32);"FOOTBALL"
    2 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    3 PRINT:PRINT:PRINT
    100 REM
    120 DIM A(20),B(20),C(40),H(2),T(2),W(2),X(2),Y(2),Z(2)
    130 DIM M$(2),D(2),P$(20)
    140 PRINT "PRESENTING N.F.U. FOOTBALL (NO FORTRAN USED)"
    145 PRINT:PRINT
    150 INPUT "DO YOU WANT INSTRUCTIONS";A$
    160 IF A$="NO" THEN 290
    165 IF A$<>"YES" THEN 150
    170 PRINT "THIS IS A FOOTBALL GAME FOR TWO TEAMS IN WHICH PLAYERS MUST"
    180 PRINT "PREPARE A TAPE WITH A DATA STATEMENT (1770 FOR TEAM 1,"
    190 PRINT "1780 FOR TEAM 2) IN WHICH EACH TEAM SCRAMBLES NOS. 1-20"
    195 PRINT "THESE NUMBERS ARE THEN ASSIGNED TO TWENTY GIVEN PLAYS."
    200 PRINT"A LIST OF NOS. AND THEIR PLAYS IS PROVIDED WITH"
    210 PRINT "BOTH TEAMS HAVING THE SAME PLAYS. THE MORE SIMILAR THE"
    220 PRINT "PLAYS THE LESS YARDAGE GAINED.  SCORES ARE GIVEN"
    223 PRINT "WHENEVER SCORES ARE MADE. SCORES MAY ALSO BE OBTAINED"
    225 PRINT "BY INPUTTING 99,99 FOR PLAY NOS. TO PUNT OR ATTEMPT A"
    227 PRINT "FIELD GOAL, INPUT 77,77 FOR PLAY NUMBERS. QUESTIONS WILL BE"
    230 PRINT "ASKED THEN. ON 4TH DOWN, YOU WILL ALSO BE ASKED WHETHER"
    240 PRINT "YOU WANT TO PUNT OR ATTEMPT A FIELD GOAL. IF THE ANSWER TO"
    250 PRINT "BOTH QUESTIONS IS NO IT WILL BE ASSUMED YOU WANT TO"
    260 PRINT "TRY AND GAIN YARDAGE. ANSWER ALL QUESTIONS YES OR NO."
    270 PRINT "THE GAME IS PLAYED UNTIL PLAYERS TERMINATE (CONTROL-C)."
    280 PRINT "PLEASE PREPARE A TAPE AND RUN.": STOP
    290 PRINT:PRINT "PLEASE INPUT SCORE LIMIT ON GAME";:INPUT E
    300 FOR I=1 TO 40: READ N: IF I>20 THEN 350
    330 A(N)=I: GOTO 360
    350 B(N)=I-20
    360 C(I)=N: NEXT I
    370 FOR I=1 TO 20: READ P$(I): NEXT I
    380 L=0: T=1
    410 PRINT "TEAM";T;"PLAY CHART"
    420 PRINT "NO.      PLAY"
    430 FOR I=1 TO 20
    440 REM
    450 PRINT C(I+L);TAB(6);P$(I)
    460 NEXT I
    630 L=L+20:T=2
    640 PRINT
    650 PRINT "TEAR OFF HERE----------------------------------------------"
    660 FOR X=1 TO 11: PRINT: NEXT X
    670 FOR Z=1 TO 3000: NEXT Z
    680 IF L=20 THEN 410
    690 D(1)=0: D(2)=3: M$(1)="--->": M$(2)="<---"
    700 H(1)=0: H(2)=0: T(1)=2: T(2)=1
    710 W(1)=-1: W(2)=1: X(1)=100: X(2)=0
    720 Y(1)=1: Y(2)=-1: Z(1)=0: Z(2)=100
    725 GOSUB 1910
    730 PRINT "TEAM 1 DEFENDS 0 YD GOAL -- TEAM 2 DEFENDS 100 YD GOAL."
    740 T=INT(2*RND(1)+1)
    760 PRINT: PRINT "THE COIN IS FLIPPED"
    765 P=X(T)-Y(T)*40
    770 GOSUB 1860: PRINT : PRINT "TEAM";T;"RECEIVES KICK-OFF"
    780 K=INT(26*RND(1)+40)
    790 P=P-Y(T)*K
    794 IF W(T)*P"NO" THEN 830
    850 IF W(T)*P1 THEN 900
    895 IF Y(T)*(P+Y(T)*10)>=X(T) THEN 898
    897 C=4: GOTO 900
    898 C=8
    900 IF C=8 THEN 904
    901 PRINT TAB(27);10-(Y(T)*P-Y(T)*S);"YARDS TO 1ST DOWN"
    902 GOTO 910
    904 PRINT TAB(27);X(T)-Y(T)*P;"YARDS"
    910 GOSUB 1900: IF D=4 THEN 1180
    920 REM
    930 U=INT(3*RND(0)-1): GOTO 940
    936 PRINT "ILLEGAL PLAY NUMBER, CHECK AND"
    940 PRINT "INPUT OFFENSIVE PLAY, DEFENSIVE PLAY";
    950 IF T=2 THEN 970
    960 INPUT P1,P2: GOTO 975
    970 INPUT P2,P1
    975 IF P1=77 THEN 1180
    980 IF P1>20 THEN 1800
    985 IF P1<1 THEN 1800
    990 IF P2>20 THEN 1800
    992 IF P2<1 THEN 1800
    995 P1=INT(P1): P2=INT(P2)
    1000 Y=INT(ABS(A(P1)-B(P2))/19*((X(T)-Y(T)*P+25)*RND(1)-15))
    1005 PRINT: IF T=2 THEN 1015
    1010 IF A(P1)<11 THEN 1048
    1012 GOTO 1020
    1015 IF B(P2)<11 THEN 1048
    1020 IF U<>0 THEN 1035
    1025 PRINT "PASS INCOMPLETE TEAM";T
    1030 Y=0: GOTO 1050
    1035 G=RND(1): IF G>.025 THEN 1040
    1037 IF Y>2 THEN 1045
    1040 PRINT "QUARTERBACK SCRAMBLED": GOTO 1050
    1045 PRINT "PASS COMPLETED": GOTO 1050
    1048 PRINT "THE BALL WAS RUN"
    1050 P=P-W(T)*Y
    1060 PRINT: PRINT "NET YARDS GAINED ON DOWN";D;"ARE ";Y
    1070 G=RND(1): IF G>.025 THEN 1110
    1080 PRINT: PRINT "** LOSS OF POSSESSION FROM TEAM";T;"TO TEAM";T(T)
    1100 GOSUB 1850: PRINT: T=T(T): GOTO 830
    1110 IF Y(T)*P>=X(T) THEN 1320
    1120 IF W(T)*P>=Z(T) THEN 1230
    1130 IF Y(T)*P-Y(T)*S>=10 THEN 880
    1140 D=D+1: IF D<>5 THEN 885
    1160 PRINT: PRINT "CONVERSION UNSUCCESSFUL TEAM";T:T=T(T)
    1170 GOSUB 1850: GOTO 880
    1180 PRINT "DOES TEAM";T;"WANT TO PUNT";: INPUT A$
    1185 IF A$="NO" THEN 1200
    1187 IF A$<>"YES" THEN 1180
    1190 PRINT:PRINT "TEAM";T;"WILL PUNT": G=RND(1): IF G<.025 THEN 1080
    1195 GOSUB 1850: K=INT(25*RND(1)+35): T=T(T): GOTO 790
    1200 PRINT "DOES TEAM";T;"WANT TO ATTEMPT A FIELD GOAL";: INPUT A$
    1210 IF A$="YES" THEN 1640
    1215 IF A$<>"NO" THEN 1200
    1217 GOTO 920
    1230 PRINT: PRINT "SAFETY AGAINST TEAM";T;"**********************OH-OH"
    1240 H(T(T))=H(T(T))+2: GOSUB 1810
    1280 PRINT"TEAM";T;"DO YOU WANT TO PUNT INSTEAD OF A KICKOFF";:INPUT A$
    1290 P=Z(T)-W(T)*20: IF A$="YES" THEN 1190
    1320 PRINT: PRINT "TOUCHDOWN BY TEAM";T;"*********************YEA TEAM"
    1340 Q=7: G=RND(1): IF G>.1 THEN 1380
    1360 Q=6: PRINT "EXTRA POINT NO GOOD": GOTO 1390
    1380 PRINT "EXTRA POINT GOOD"
    1390 H(T)=H(T)+Q: GOSUB 1810
    1420 T=T(T): GOTO 765
    1430 K=INT(9*RND(0)+1)
    1440 R=INT(((X(T)-Y(T)*P+25)*RND(1)-15)/K)
    1460 P=P-W(T)*R
    1480 PRINT:PRINT "RUNBACK TEAM";T;R;"YARDS"
    1485 G=RND(1): IF G<.025 THEN 1080
    1490 IF Y(T)*P>=X(T) THEN 1320
    1500 IF W(T)*P>=Z(T) THEN 1230
    1510 GOTO 880
    1640 PRINT: PRINT "TEAM";T;"WILL ATTEMPT A FIELD GOAL"
    1645 G=RND(1): IF G<.025 THEN 1080
    1650 F=INT(35*RND(1)+20)
    1660 PRINT: PRINT "KICK IS";F;"YARDS LONG"
    1680 P=P-W(T)*F: G=RND(1)
    1690 IF G<.35 THEN 1735
    1700 IF Y(T)*P99 THEN 936
    1810 PRINT: PRINT "TEAM 1 SCORE IS";H(1)
    1820 PRINT "TEAM 2 SCORE IS";H(2): PRINT
    1825 IF H(T)=50 THEN 700
    630 LET Y=INT(50*RND(1)^2)+(1-P)*INT(50*RND(1)^4)
    640 LET X=X+FNF(1)*Y
    650 IF ABS(X-50)>=50 THEN 655
    651 PRINT Y;L$(3);" RUNBACK"
    652 GOTO 720
    655 PRINT L$(4);
    660 GOTO 2600
    700 PRINT "TOUCHBACK FOR ";O$(P);"."
    710 LET X=20+P*60
    720 REM FIRST DOWN
    730 GOSUB 800
    740 LET X1=X
    750 LET D=1
    760 PRINT:PRINT "FIRST DOWN ";O$(P);"***"
    770 PRINT
    780 PRINT
    790 GOTO 860
    800 REM PRINT POSITION
    810 IF X>50 THEN 840
    820 PRINT L$(5);O$(0);X;L$(6)
    830 GOTO 850
    840 PRINT L$(5);O$(1);100-X;L$(6)
    850 RETURN
    860 REM NEW PLAY
    870 LET T=T+1
    880 IF T=30 THEN 1060
    890 IF T<50 THEN 940
    900 IF RND(1)>.2 THEN 940
    910 PRINT "END OF GAME  ***"
    920 PRINT "FINAL SCORE:  ";O$(0);": ";S(0);"  ";O$(1);": ";S(1)
    930 STOP
    940 IF P=1 THEN 1870
    950 PRINT "NEXT PLAY";
    960 INPUT Z
    970 IF Z<>INT(Z) THEN 990
    980 IF ABS(Z-4)<=3 THEN 1010
    990 PRINT "ILLEGAL PLAY NUMBER, RETYPE";
    1000 GOTO 960
    1010 LET F=0
    1020 PRINT L$(Z+6);".  ";
    1030 LET R=RND(1)*(.98+FNF(1)*.02)
    1040 LET R1=RND(1)
    1050 ON Z GOTO 1110,1150,1260,1480,1570,1570,1680
    1060 REM  JEAN'S SPECIAL
    1070 IF RND(1)> 1/3 THEN 940
    1080 PRINT "GAME DELAYED.  DOG ON FIELD."
    1090 PRINT
    1100 GOTO 940
    1110 REM  SIMPLE RUN
    1120 LET Y=INT(24*(R-.5)^3+3)
    1130 IF RND(1)<.05 THEN 1180
    1140 GOTO 2190
    1150 REM  TRICKY RUN
    1160 LET Y=INT(20*R-5)
    1170 IF RND(1)>.1 THEN 2190
    1180 LET F=-1
    1190 LET X3=X
    1200 LET X=X+FNF(1)*Y
    1210 IF ABS(X-50)>=50 THEN 1240
    1220 PRINT "***  FUMBLE AFTER ";
    1230 GOTO 2230
    1240 PRINT "***  FUMBLE."
    1250 GOTO 2450
    1260 REM  SHORT PASS
    1270 LET Y=INT(60*(R1-.5)^3+10)
    1280 IF R<.05 THEN 1330
    1290 IF R<.15 THEN 1390
    1300 IF R<.55 THEN 1420
    1310 PRINT "COMPLETE.  ";
    1320 GOTO 2190
    1330 IF D=4 THEN 1420
    1340 PRINT "INTERCEPTED."
    1350 LET F=-1
    1360 LET X=X+FNF(1)*Y
    1370 IF ABS(X-50)>=50 THEN 2450
    1380 GOTO 2300
    1390 PRINT "PASSER TACKLED.  ";
    1400 LET Y=-INT(10*R1)
    1410 GOTO 2190
    1420 LET Y=0
    1430 IF RND(1)<.3 THEN 1460
    1440 PRINT "INCOMPLETE.  ";
    1450 GOTO 2190
    1460 PRINT "BATTED DOWN.  ";
    1470 GOTO 2190
    1480 REM  LONG PASS
    1490 LET Y=INT(160*(R1-.5)^3+30)
    1500 IF R<.1 THEN 1330
    1510 IF R<.3 THEN 1540
    1520 IF R<.75 THEN 1420
    1530 GOTO 1310
    1540 PRINT "PASSER TACKLED.  ";
    1550 LET Y=-INT(15*R1+3)
    1560 GOTO 2190
    1570 REM  PUNT OR KICK
    1580 LET Y=INT(100*(R-.5)^3+35)
    1590 IF D=4 THEN 1610
    1600 LET Y=INT(Y*1.3)
    1610 PRINT Y;L$(3);" PUNT"
    1620 IF ABS(X+Y*FNF(1)-50)>=50 THEN 1670
    1630 IF D<4 THEN 1670
    1640 LET Y1=INT(R1^2*20)
    1650 PRINT Y1;L$(3);" RUN BACK"
    1660 LET Y=Y-Y1
    1670 GOTO 1350
    1680 REM  PLACE KICK
    1690 LET Y=INT(100*(R-.5)^3+35)
    1700 IF R1>.15 THEN 1750
    1710 PRINT "KICK IS BLOCKED  ***"
    1720 LET X=X-5*FNF(1)
    1730 LET P=1-P
    1740 GOTO 720
    1750 LET X=X+FNF(1)*Y
    1760 IF ABS(X-50)>=60 THEN 1810
    1770 PRINT "KICK IS SHORT."
    1780 IF ABS(X-50)>=50 THEN 2710
    1790 P=1-P
    1800 GOTO 630
    1810 IF R1>.5 THEN 1840
    1820 PRINT "KICK IS OFF TO THE SIDE."
    1830 GOTO 2710
    1840 PRINT "FIELD GOAL ***"
    1850 LET S(P)=S(P)+3
    1860 GOTO 2640
    1870 REM  OPPONENT'S PLAY
    1880 IF D>1 THEN 1940
    1890 IF RND(1)>1/3 THEN 1920
    1900 LET Z=3
    1910 GOTO 1010
    1920 LET Z=1
    1930 GOTO 1010
    1940 IF D=4 THEN 2090
    1950 IF 10+X-X1<5 THEN 1890
    1960 IF X<5 THEN 1890
    1970 IF X<=10 THEN 2160
    1980 IF X>X1 THEN 2020
    1990 LET A=INT(2*RND(1))
    2000 LET Z=2+A*2
    2010 GOTO 1010
    2020 IF D<3 THEN 1990
    2030 IF X<45 THEN 1990
    2040 IF RND(1)>1/4 THEN 2070
    2050 LET Z=6
    2060 GOTO 1010
    2070 LET Z=4
    2080 GOTO 1010
    2090 IF X>30 THEN 2140
    2100 IF 10+X-X1<3 THEN 1890
    2110 IF X<3 THEN 1890
    2120 LET Z=7
    2130 GOTO 1010
    2140 LET Z=5
    2150 GOTO 1010
    2160 LET A=INT(2*RND(1))
    2170 LET Z=2+A
    2180 GOTO 1010
    2190 REM GAIN OR LOSS
    2200 LET X3=X
    2210 LET X=X+FNF(1)*Y
    2220 IF ABS(X-50)>=50 THEN 2450
    2230 IF Y=0 THEN 2250
    2240 PRINT ABS(Y);L$(3);
    2250 PRINT L$(15+SGN(Y))
    2280 IF ABS(X3-50)>40 THEN 2300
    2290 IF RND(1)<.1 THEN 2860
    2300 GOSUB 800
    2310 IF F=0 THEN 2340
    2320 LET P=1-P
    2330 GOTO 740
    2340 IF FNG(1)>=10 THEN 740
    2350 IF D=4 THEN 2320
    2360 LET D=D+1
    2370 PRINT "DOWN: ";D;"     ";
    2380 IF (X1-50)*FNF(1)<40 THEN 2410
    2390 PRINT "GOAL TO GO"
    2400 GOTO 2420
    2410 PRINT "YARDS TO GO: ";10-FNG(1)
    2420 PRINT
    2430 PRINT
    2440 GOTO 860
    2450 REM BALL IN END-ZONE
    2460 IF X>=100 THEN 2490
    2470 LET E=0
    2480 GOTO 2500
    2490 LET E=1
    2500 ON 1+E-F*2+P*4 GOTO 2510,2590,2760,2710,2590,2510,2710,2760
    2510 REM SAFETY
    2520 LET S(1-P)=S(1-P)+2
    2530 PRINT L$(19)
    2540 GOSUB 2800
    2550 PRINT O$(P);" KICKS OFF FROM ITS 20 YARD LINE."
    2560 LET X=20+P*60
    2570 LET P=1-P
    2580 GOTO 590
    2590 REM OFFENSIVE TD
    2600 PRINT L$(17);"***"
    2610 IF RND(1)>.8 THEN 2680
    2620 LET S(P)=S(P)+7
    2630 PRINT "KICK IS GOOD."
    2640 GOSUB 2800
    2650 PRINT O$(P);" KICKS OFF"
    2660 LET P=1-P
    2670 GOTO 580
    2680 PRINT "KICK IS OFF TO THE SIDE"
    2690 LET S(P)=S(P)+6
    2700 GOTO 2640
    2710 REM TOUCHBACK
    2720 PRINT L$(18)
    2730 LET P=1-P
    2740 LET X=20+P*60
    2750 GOTO 720
    2760 REM DEFENSIVE TD
    2770 PRINT L$(17);"FOR ";O$(1-P);"***"
    2780 LET P=1-P
    2790 GOTO 2600
    2800 REM SCORE
    2810 PRINT
    2820 PRINT "SCORE:  ";S(0);" TO ";S(1)
    2830 PRINT
    2840 PRINT
    2850 RETURN
    2860 REM PENALTY
    2870 LET P3=INT(2*RND(1))
    2880 PRINT O$(P3);" OFFSIDES -- PENALTY OF 5 YARDS."
    2890 PRINT
    2900 PRINT
    2910 IF P3=0 THEN 2980
    2920 PRINT "DO YOU ACCEPT THE PENALTY";
    2930 INPUT A$
    2940 IF A$="NO" THEN 2300
    2950 IF A$="YES" THEN 3110
    2960 PRINT "TYPE 'YES' OR 'NO'";
    2970 GOTO 2930
    2980 REM OPPONENT'S STRATEGY ON PENALTY
    2990 IF P=1 THEN 3040
    3000 IF Y<=0 THEN 3080
    3010 IF F<0 THEN 3080
    3020 IF FNG(1)<3*D-2 THEN 3080
    3030 GOTO 3100
    3040 IF Y<=5 THEN 3100
    3050 IF F<0 THEN 3100
    3060 IF D<4 THEN 3080
    3070 IF FNG(1)<10 THEN 3100
    3080 PRINT "PENALTY REFUSED."
    3090 GOTO 2300
    3100 PRINT "PENALTY ACCEPTED."
    3110 LET F=0
    3120 LET D=D-1
    3130 IF P<>P3 THEN 3160
    3140 LET X=X3-FNF(1)*5
    3150 GOTO 2300
    3160 LET X=X3+FNF(1)*5
    3170 GOTO 2300
    3180 END
    
    
    ================================================
    FILE: 37_Football/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 37_Football/javascript/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells)
    
    
    ================================================
    FILE: 37_Football/javascript/football.html
    ================================================
    
    
    FOOTBALL
    
    
    
    
    
    
    
    
    
    
    ================================================
    FILE: 37_Football/javascript/football.js
    ================================================
    // FOOTBALL
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    var 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];
    var aa = [];
    var ba = [];
    var ca = [];
    var ha = [];
    var ta = [];
    var wa = [];
    var xa = [];
    var ya = [];
    var za = [];
    var ms = [];
    var da = [];
    var 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!!!!"];
    var p;
    var t;
    
    function field_headers()
    {
        print("TEAM 1 [0   10   20   30   40   50   60   70   80   90");
        print("   100] TEAM 2\n");
        print("\n");
    }
    
    function separator()
    {
        str = "";
        for (x = 1; x <= 72; x++)
            str += "+";
        print(str + "\n");
    }
    
    function show_ball()
    {
        print(tab(da[t] + 5 + p / 2) + ms[t] + "\n");
        field_headers();
    }
    
    function show_scores()
    {
        print("\n");
        print("TEAM 1 SCORE IS " + ha[1] + "\n");
        print("TEAM 2 SCORE IS " + ha[2] + "\n");
        print("\n");
        if (ha[t] >= e) {
            print("TEAM " + t + " WINS*******************");
            return true;
        }
        return false;
    }
    
    function loss_posession() {
        print("\n");
        print("** LOSS OF POSSESSION FROM TEAM " + t + " TO TEAM " + ta[t] + "\n");
        print("\n");
        separator();
        print("\n");
        t = ta[t];
    }
    
    function touchdown() {
        print("\n");
        print("TOUCHDOWN BY TEAM " + t + " *********************YEA TEAM\n");
        q = 7;
        g = Math.random();
        if (g <= 0.1) {
            q = 6;
            print("EXTRA POINT NO GOOD\n");
        } else {
            print("EXTRA POINT GOOD\n");
        }
        ha[t] = ha[t] + q;
    }
    
    // Main program
    async function main()
    {
        print(tab(32) + "FOOTBALL\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        print("PRESENTING N.F.U. FOOTBALL (NO FORTRAN USED)\n");
        print("\n");
        print("\n");
        while (1) {
            print("DO YOU WANT INSTRUCTIONS");
            str = await input();
            if (str == "YES" || str == "NO")
                break;
        }
        if (str == "YES") {
            print("THIS IS A FOOTBALL GAME FOR TWO TEAMS IN WHICH PLAYERS MUST\n");
            print("PREPARE A TAPE WITH A DATA STATEMENT (1770 FOR TEAM 1,\n");
            print( "1780 FOR TEAM 2) IN WHICH EACH TEAM SCRAMBLES NOS. 1-20\n");
            print("THESE NUMBERS ARE THEN ASSIGNED TO TWENTY GIVEN PLAYS.\n");
            print("A LIST OF NOS. AND THEIR PLAYS IS PROVIDED WITH\n");
            print("BOTH TEAMS HAVING THE SAME PLAYS. THE MORE SIMILAR THE\n");
            print("PLAYS THE LESS YARDAGE GAINED.  SCORES ARE GIVEN\n");
            print("WHENEVER SCORES ARE MADE. SCORES MAY ALSO BE OBTAINED\n");
            print("BY INPUTTING 99,99 FOR PLAY NOS. TO PUNT OR ATTEMPT A\n");
            print("FIELD GOAL, INPUT 77,77 FOR PLAY NUMBERS. QUESTIONS WILL BE\n");
            print("ASKED THEN. ON 4TH DOWN, YOU WILL ALSO BE ASKED WHETHER\n");
            print("YOU WANT TO PUNT OR ATTEMPT A FIELD GOAL. IF THE ANSWER TO\n");
            print("BOTH QUESTIONS IS NO IT WILL BE ASSUMED YOU WANT TO\n");
            print("TRY AND GAIN YARDAGE. ANSWER ALL QUESTIONS YES OR NO.\n");
            print("THE GAME IS PLAYED UNTIL PLAYERS TERMINATE (CONTROL-C).\n");
            print("PLEASE PREPARE A TAPE AND RUN.\n");
        }
        print("\n");
        print("PLEASE INPUT SCORE LIMIT ON GAME");
        e = parseInt(await input());
        for (i = 1; i <= 40; i++) {
            if (i <= 20) {
                aa[player_data[i - 1]] = i;
            } else {
                ba[player_data[i - 1]] = i - 20;
            }
            ca[i] = player_data[i - 1];
        }
        l = 0;
        t = 1;
        do {
            print("TEAM " + t + " PLAY CHART\n");
            print("NO.      PLAY\n");
            for (i = 1; i <= 20; i++) {
                str = "" + ca[i + l];
                while (str.length < 6)
                    str += " ";
                str += ps[i];
                print(str + "\n");
            }
            l += 20;
            t = 2;
            print("\n");
            print("TEAR OFF HERE----------------------------------------------\n");
            for (x = 1; x <= 11; x++)
                print("\n");
        } while (l == 20) ;
        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;
        p = 0;
        field_headers();
        print("TEAM 1 DEFEND 0 YD GOAL -- TEAM 2 DEFENDS 100 YD GOAL.\n");
        t = Math.floor(2 * Math.random() + 1);
        print("\n");
        print("THE COIN IS FLIPPED\n");
        routine = 1;
        while (1) {
            if (routine <= 1) {
                p = xa[t] - ya[t] * 40;
                separator();
                print("\n");
                print("TEAM " + t + " RECEIVES KICK-OFF\n");
                k = Math.floor(26 * Math.random() + 40);
            }
            if (routine <= 2) {
                p = p - ya[t] * k;
            }
            if (routine <= 3) {
                if (wa[t] * p >= za[t] + 10) {
                    print("\n");
                    print("BALL WENT OUT OF ENDZONE --AUTOMATIC TOUCHBACK--\n");
                    p = za[t] - wa[t] * 20;
                    if (routine <= 4)
                        routine = 5;
                } else {
                    print("BALL WENT " + k + " YARDS.  NOW ON " + p + "\n");
                    show_ball();
                }
            }
            if (routine <= 4) {
                while (1) {
                    print("TEAM " + t + " DO YOU WANT TO RUNBACK");
                    str = await input();
                    if (str == "YES" || str == "NO")
                        break;
                }
                if (str == "YES") {
                    k = Math.floor(9 * Math.random() + 1);
                    r = Math.floor(((xa[t] - ya[t] * p + 25) * Math.random() - 15) / k);
                    p = p - wa[t] * r;
                    print("\n");
                    print("RUNBACK TEAM " + t + " " + r + " YARDS\n");
                    g = Math.random();
                    if (g < 0.25) {
                        loss_posession();
                        routine = 4;
                        continue;
                    } else if (ya[t] * p >= xa[t]) {
                        touchdown();
                        if (show_scores())
                            return;
                        t = ta[t];
                        routine = 1;
                        continue;
                    } else if (wa[t] * p >= za[t]) {
                        print("\n");
                        print("SAFETY AGAINST TEAM " + t + " **********************OH-OH\n");
                        ha[ta[t]] = ha[ta[t]] + 2;
                        if (show_scores())
                            return;
                        print("TEAM " + t + " DO YOU WANT TO PUNT INSTEAD OF A KICKOFF");
                        str = await input();
                        p = za[t] - wa[t] * 20;
                        if (str == "YES") {
                            print("\n");
                            print("TEAM " + t + " WILL PUNT\n");
                            g = Math.random();
                            if (g < 0.25) {
                                loss_posession();
                                routine = 4;
                                continue;
                            }
                            print("\n");
                            separator();
                            k = Math.floor(25 * Math.random() + 35);
                            t = ta[t];
                            routine = 2;
                            continue;
                        }
                        touchdown();
                        if (show_scores())
                            return;
                        t = ta[t];
                        routine = 1;
                        continue;
                    } else {
                        routine = 5;
                        continue;
                    }
                } else if (str == "NO") {
                    if (wa[t] * p >= za[t])
                        p = za[t] - wa[t] * 20;
                }
            }
            if (routine <= 5) {
                d = 1;
                s = p;
            }
            if (routine <= 6) {
                str = "";
                for (i = 1; i <= 72; i++)
                    str += "=";
                print(str + "\n");
                print("TEAM " + t + " DOWN " + d + " ON " + p + "\n");
                if (d == 1) {
                    if (ya[t] * (p + ya[t] * 10) >= xa[t])
                        c = 8;
                    else
                        c = 4;
                }
                if (c != 8) {
                    print(tab(27) + (10 - (ya[t] * p - ya[t] * s)) + " YARDS TO 1ST DOWN\n");
                } else {
                    print(tab(27) + (xa[t] - ya[t] * p) + " YARDS\n");
                }
                show_ball();
                if (d == 4)
                    routine = 8;
            }
            if (routine <= 7) {
                u = Math.floor(3 * Math.random() - 1);
                while (1) {
                    print("INPUT OFFENSIVE PLAY, DEFENSIVE PLAY");
                    str = await input();
                    if (t == 1) {
                        p1 = parseInt(str);
                        p2 = parseInt(str.substr(str.indexOf(",") + 1));
                    } else {
                        p2 = parseInt(str);
                        p1 = parseInt(str.substr(str.indexOf(",") + 1));
                    }
                    if (p1 == 99) {
                        if (show_scores())
                            return;
                        if (p1 == 99)
                            continue;
                    }
                    if (p1 < 1 || p1 > 20 || p2 < 1 || p2 > 20) {
                        print("ILLEGAL PLAY NUMBER, CHECK AND\n");
                        continue;
                    }
                    break;
                }
            }
            if (d == 4 || p1 == 77) {
                while (1) {
                    print("DOES TEAM " + t + " WANT TO PUNT");
                    str = await input();
                    if (str == "YES" || str == "NO")
                        break;
                }
                if (str == "YES") {
                    print("\n");
                    print("TEAM " + t + " WILL PUNT\n");
                    g = Math.random();
                    if (g < 0.25) {
                        loss_posession();
                        routine = 4;
                        continue;
                    }
                    print("\n");
                    separator();
                    k = Math.floor(25 * Math.random() + 35);
                    t = ta[t];
                    routine = 2;
                    continue;
                }
                while (1) {
                    print("DOES TEAM " + t + " WANT TO ATTEMPT A FIELD GOAL");
                    str = await input();
                    if (str == "YES" || str == "NO")
                        break;
                }
                if (str == "YES") {
                    print("\n");
                    print("TEAM " + t + " WILL ATTEMPT A FIELD GOAL\n");
                    g = Math.random();
                    if (g < 0.025) {
                        loss_posession();
                        routine = 4;
                        continue;
                    } else {
                        f = Math.floor(35 * Math.random() + 20);
                        print("\n");
                        print("KICK IS " + f + " YARDS LONG\n");
                        p = p - wa[t] * f;
                        g = Math.random();
                        if (g < 0.35) {
                            print("BALL WENT WIDE\n");
                        } else if (ya[t] * p >= xa[t]) {
                            print("FIELD GOLD GOOD FOR TEAM " + t + " *********************YEA");
                            q = 3;
                            ha[t] = ha[t] + q;
                            if (show_scores())
                                return;
                            t = ta[t];
                            routine = 1;
                            continue;
                        }
                        print("FIELD GOAL UNSUCCESFUL TEAM " + t + "-----------------TOO BAD\n");
                        print("\n");
                        separator();
                        if (ya[t] * p < xa[t] + 10) {
                            print("\n");
                            print("BALL NOW ON " + p + "\n");
                            t = ta[t];
                            show_ball();
                            routine = 4;
                            continue;
                        } else {
                            t = ta[t];
                            routine = 3;
                            continue;
                        }
                    }
                } else {
                    routine = 7;
                    continue;
                }
            }
            y = Math.floor(Math.abs(aa[p1] - ba[p2]) / 19 * ((xa[t] - ya[t] * p + 25) * Math.random() - 15));
            print("\n");
            if (t == 1 && aa[p1] < 11 || t == 2 && ba[p2] < 11) {
                print("THE BALL WAS RUN\n");
            } else if (u == 0) {
                print("PASS INCOMPLETE TEAM " + t + "\n");
                y = 0;
            } else {
                g = Math.random();
                if (g <= 0.025 && y > 2) {
                    print("PASS COMPLETED\n");
                } else {
                    print("QUARTERBACK SCRAMBLED\n");
                }
            }
            p = p - wa[t] * y;
            print("\n");
            print("NET YARDS GAINED ON DOWN " + d + " ARE " + y + "\n");
    
            g = Math.random();
            if (g <= 0.025) {
                loss_posession();
                routine = 4;
                continue;
            } else if (ya[t] * p >= xa[t]) {
                touchdown();
                if (show_scores())
                    return;
                t = ta[t];
                routine = 1;
                continue;
            } else if (wa[t] * p >= za[t]) {
                print("\n");
                print("SAFETY AGAINST TEAM " + t + " **********************OH-OH\n");
                ha[ta[t]] = ha[ta[t]] + 2;
                if (show_scores())
                    return;
                print("TEAM " + t + " DO YOU WANT TO PUNT INSTEAD OF A KICKOFF");
                str = await input();
                p = za[t] - wa[t] * 20;
                if (str == "YES") {
                    print("\n");
                    print("TEAM " + t + " WILL PUNT\n");
                    g = Math.random();
                    if (g < 0.25) {
                        loss_posession();
                        routine = 4;
                        continue;
                    }
                    print("\n");
                    separator();
                    k = Math.floor(25 * Math.random() + 35);
                    t = ta[t];
                    routine = 2;
                    continue;
                }
                touchdown();
                if (show_scores())
                    return;
                t = ta[t];
                routine = 1;
            } else if (ya[t] * p - ya[t] * s >= 10) {
                routine = 5;
            } else {
                d++;
                if (d != 5) {
                    routine = 6;
                } else {
                    print("\n");
                    print("CONVERSION UNSUCCESSFUL TEAM " + t + "\n");
                    t = ta[t];
                    print("\n");
                    separator();
                    routine = 5;
                }
            }
        }
    }
    
    main();
    
    
    ================================================
    FILE: 37_Football/javascript/ftball.html
    ================================================
    
    
    FTBALL
    
    
    
    
    
    
    
    
    
    
    ================================================
    FILE: 37_Football/javascript/ftball.js
    ================================================
    // FTBALL
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    var os = [];
    var sa = [];
    var 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"];
    var p;
    var x;
    var x1;
    
    function fnf(x)
    {
        return 1 - 2 * p;
    }
    
    function fng(z)
    {
        return p * (x1 - x) + (1 - p) * (x - x1);
    }
    
    function show_score()
    {
        print("\n");
        print("SCORE:  " + sa[0] + " TO " + sa[1] + "\n");
        print("\n");
        print("\n");
    }
    
    function show_position()
    {
        if (x <= 50) {
            print(ls[5] + os[0] + " " + x + " " + ls[6] + "\n");
        } else {
            print(ls[5] + os[1] + " " + (100 - x) + " " + ls[6] + "\n");
        }
    }
    
    function offensive_td()
    {
        print(ls[17] + "***\n");
        if (Math.random() <= 0.8) {
            sa[p] = sa[p] + 7;
            print("KICK IS GOOD.\n");
        } else {
            print("KICK IS OFF TO THE SIDE\n");
            sa[p] = sa[p] + 6;
        }
        show_score();
        print(os[p] + " KICKS OFF\n");
        p = 1 - p;
    }
    
    // Main program
    async function main()
    {
        print(tab(33) + "FTBALL\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("THIS IS DARTMOUTH CHAMPIONSHIP FOOTBALL.\n");
        print("\n");
        print("YOU WILL QUARTERBACK DARTMOUTH. CALL PLAYS AS FOLLOWS:\n");
        print("1= SIMPLE RUN; 2= TRICKY RUN; 3= SHORT PASS;\n");
        print("4= LONG PASS; 5= PUNT; 6= QUICK KICK; 7= PLACE KICK.\n");
        print("\n");
        print("CHOOSE YOUR OPPONENT");
        os[1] = await input();
        os[0] = "DARMOUTH";
        print("\n");
        sa[0] = 0;
        sa[1] = 0;
        p = Math.floor(Math.random() * 2);
        print(os[p] + " WON THE TOSS\n");
        if (p != 0) {
            print(os[1] + " ELECTS TO RECEIVE.\n");
            print("\n");
        } else {
            print("DO YOU ELECT TO KICK OR RECEIVE");
            while (1) {
                str = await input();
                print("\n");
                if (str == ls[1] || str == ls[2])
                    break;
                print("INCORRECT ANSWER.  PLEASE TYPE 'KICK' OR 'RECEIVE'");
            }
            e = (str == ls[1]) ? 1 : 2;
            if (e == 1)
                p = 1;
        }
        t = 0;
        start = 1;
        while (1) {
            if (start <= 1) {
                x = 40 + (1 - p) * 20;
            }
            if (start <= 2) {
                y = Math.floor(200 * Math.pow((Math.random() - 0.5), 3) + 55);
                print(" " + y + " " + ls[3] + " KICKOFF\n");
                x = x - fnf(1) * y;
                if (Math.abs(x - 50) >= 50) {
                    print("TOUCHBACK FOR " + os[p] + ".\n");
                    x = 20 + p * 60;
                    start = 4;
                } else {
                    start = 3;
                }
            }
            if (start <= 3) {
                y = Math.floor(50 * Math.pow(Math.random(), 2)) + (1 - p) * Math.floor(50 * Math.pow(Math.random(), 4));
                x = x + fnf(1) * y;
                if (Math.abs(x - 50) < 50) {
                    print(" " + y + " " + ls[3] + " RUNBACK\n");
                } else {
                    print(ls[4]);
                    offensive_td();
                    start = 1;
                    continue;
                }
            }
            if (start <= 4) {
                // First down
                show_position();
            }
            if (start <= 5) {
                x1 = x;
                d = 1;
                print("\n");
                print("FIRST DOWN " + os[p] + "***\n");
                print("\n");
                print("\n");
            }
            // New play
            t++;
            if (t == 30) {
                if (Math.random() <= 1.3) {
                    print("GAME DELAYED.  DOG ON FIELD.\n");
                    print("\n");
                }
            }
            if (t >= 50 && Math.random() <= 0.2)
                break;
            if (p != 1) {
                // Opponent's play
                if (d <= 1) {
                    z = Math.random() > 1 / 3 ? 1 : 3;
                } else if (d != 4) {
                    if (10 + x - x1 < 5 || x < 5) {
                        z = Math.random() > 1 / 3 ? 1 : 3;
                    } else if (x <= 10) {
                        a = Math.floor(2 * Math.random());
                        z = 2 + a;
                    } else if (x <= x1 || d < 3 || x < 45) {
                        a = Math.floor(2 * Math.random());
                        z = 2 + a * 2;
                    } else {
                        if (Math.random() > 1 / 4)
                            z = 4;
                        else
                            z = 6;
                    }
                } else {
                    if (x <= 30) {
                        z = 5;
                    } else if (10 + x - x1 < 3 || x < 3) {
                        z = Math.random() > 1 / 3 ? 1 : 3;
                    } else {
                        z = 7;
                    }
                }
            } else {
                print("NEXT PLAY");
                while (1) {
                    z = parseInt(await input());
                    if (Math.abs(z - 4) <= 3)
                        break;
                    print("ILLEGAL PLAY NUMBER, RETYPE");
                }
            }
            f = 0;
            print(ls[z + 6] + ".  ");
            r = Math.random() * (0.98 + fnf(1) * 0.02);
            r1 = Math.random();
            switch (z) {
                case 1: // Simple run
                case 2: // Tricky run
                    if (z == 1) {
                        y = Math.floor(24 * Math.pow(r - 0.5, 3) + 3);
                        if (Math.random() >= 0.05) {
                            routine = 1;
                            break;
                        }
                    } else {
                        y = Math.floor(20 * r - 5);
                        if (Math.random() > 0.1) {
                            routine = 1;
                            break;
                        }
                    }
                    f = -1;
                    x3 = x;
                    x = x + fnf(1) * y;
                    if (Math.abs(x - 50) < 50) {
                        print("***  FUMBLE AFTER ");
                        routine = 2;
                        break;
                    } else {
                        print("***  FUMBLE.\n");
                        routine = 4;
                        break;
                    }
                case 3: // Short pass
                case 4: // Long pass
                    if (z == 3) {
                        y = Math.floor(60 * Math.pow(r1 - 0.5, 3) + 10);
                    } else {
                        y = Math.floor(160 * Math.pow((r1 - 0.5), 3) + 30);
                    }
                    if (z == 3 && r < 0.05 || z == 4 && r < 0.1) {
                        if (d != 4) {
                            print("INTERCEPTED.\n");
                            f = -1;
                            x = x + fnf(1) * y;
                            if (Math.abs(x - 50) >= 50) {
                                routine = 4;
                                break;
                            }
                            routine = 3;
                            break;
                        } else {
                            y = 0;
                            if (Math.random() < 0.3) {
                                print("BATTED DOWN.  ");
                            } else {
                                print("INCOMPLETE.  ");
                            }
                            routine = 1;
                            break;
                        }
                    } else if (z == 4 && r < 0.3) {
                        print("PASSER TACKLED.  ");
                        y = -Math.floor(15 * r1 + 3);
                        routine = 1;
                        break;
                    } else if (z == 3 && r < 0.15) {
                        print("PASSER TACLKED.  ");
                        y = -Math.floor(10 * r1);
                        routine = 1;
                        break;
                    } else if (z == 3 && r < 0.55 || z == 4 && r < 0.75) {
                        y = 0;
                        if (Math.random() < 0.3) {
                            print("BATTED DOWN.  ");
                        } else {
                            print("INCOMPLETE.  ");
                        }
                        routine = 1;
                        break;
                    } else {
                        print("COMPLETE.  ");
                        routine = 1;
                        break;
                    }
                case 5:  // Punt
                case 6:  // Quick kick
                    y = Math.floor(100 * Math.pow((r - 0.5), 3) + 35);
                    if (d != 4)
                        y = Math.floor(y * 1.3);
                    print(" " + y + " " + ls[3] + " PUNT\n");
                    if (Math.abs(x + y * fnf(1) - 50) < 50 && d >= 4) {
                        y1 = Math.floor(Math.pow(r1, 2) * 20);
                        print(" " + y1 + " " + ls[3] + " RUN BACK\n");
                        y = y - y1;
                    }
                    f = -1;
                    x = x + fnf(1) * y;
                    if (Math.abs(x - 50) >= 50) {
                        routine = 4;
                        break;
                    }
                    routine = 3;
                    break;
                case 7: // Place kick
                    y = Math.floor(100 * Math.pow((r - 0.5), 3) + 35);
                    if (r1 <= 0.15) {
                        print("KICK IS BLOCKED  ***\n");
                        x = x - 5 * fnf(1);
                        p = 1 - p;
                        start = 4;
                        continue;
                    }
                    x = x + fnf(1) * y;
                    if (Math.abs(x - 50) >= 60) {
                        if (r1 <= 0.5) {
                            print("KICK IS OFF TO THE SIDE.\n");
                            print(ls[18] + "\n");
                            p = 1 - p;
                            x = 20 + p * 60;
                            start = 4;
                            continue;
                        } else {
                            print("FIELD GOAL ***\n");
                            sa[p] = sa[p] + 3;
                            show_score();
                            print(os[p] + " KICKS OFF\n");
                            p = 1 - p;
                            start = 1;
                            continue;
                        }
                    } else {
                        print("KICK IS SHORT.\n");
                        if (Math.abs(x - 50) >= 50) {
                            // Touchback
                            print(ls[18] + "\n");
                            p = 1 - p;
                            x = 20 + p * 60;
                            start = 4;
                            continue;
                        }
                        p = 1 - p;
                        start = 3;
                        continue;
                    }
    
            }
            // Gain or loss
            if (routine <= 1) {
                x3 = x;
                x = x + fnf(1) * y;
                if (Math.abs(x - 50) >= 50) {
                    routine = 4;
                }
            }
            if (routine <= 2) {
                if (y != 0) {
                    print(" " + Math.abs(y) + " " + ls[3]);
                    if (y < 0)
                        yt = -1;
                    else if (y > 0)
                        yt = 1;
                    else
                        yt = 0;
                    print(ls[15 + yt]);
                    if (Math.abs(x3 - 50) <= 40 && Math.random() < 0.1) {
                        // Penalty
                        p3 = Math.floor(2 * Math.random());
                        print(os[p3] + " OFFSIDES -- PENALTY OF 5 YARDS.\n");
                        print("\n");
                        print("\n");
                        if (p3 != 0) {
                            print("DO YOU ACCEPT THE PENALTY");
                            while (1) {
                                str = await input();
                                if (str == "YES" || str == "NO")
                                    break;
                                print("TYPE 'YES' OR 'NO'");
                            }
                            if (str == "YES") {
                                f = 0;
                                d = d - 1;
                                if (p != p3)
                                    x = x3 + fnf(1) * 5;
                                else
                                    x = x3 - fnf(1) * 5;
                            }
                        } else {
                            // Opponent's strategy on penalty
                            if ((p != 1 && (y <= 0 || f < 0 || fng(1) < 3 * d - 2))
                                || (p == 1 && ((y > 5 && f >= 0) || d < 4 || fng(1) >= 10))) {
                                print("PENALTY REFUSED.\n");
                            } else {
                                print("PENALTY ACCEPTED.\n");
                                f = 0;
                                d = d - 1;
                                if (p != p3)
                                    x = x3 + fnf(1) * 5;
                                else
                                    x = x3 - fnf(1) * 5;
                            }
                        }
                        routine = 3;
                    }
                }
            }
            if (routine <= 3) {
                show_position();
                if (f != 0) {
                    p = 1 - p;
                    start = 5;
                    continue;
                } else if (fng(1) >= 10) {
                    start = 5;
                    continue;
                } else if (d == 4) {
                    p = 1 - p;
                    start = 5;
                    continue;
                } else {
                    d++;
                    print("DOWN: " + d + "     ");
                    if ((x1 - 50) * fnf(1) >= 40) {
                        print("GOAL TO GO\n");
                    } else {
                        print("YARDS TO GO: " + (10 - fng(1)) + "\n");
                    }
                    print("\n");
                    print("\n");
                    start = 6;
                    continue;
                }
            }
            if (routine <= 4) {
                // Ball in end-zone
                e = (x >= 100) ? 1 : 0;
                switch (1 + e - f * 2 + p * 4) {
                    case 1:
                    case 5:
                        // Safety
                        sa[1 - p] = sa[1 - p] + 2;
                        print(ls[19] + "\n");
                        show_score();
                        print(os[p] + " KICKS OFF FROM ITS 20 YARD LINE.\n");
                        x = 20 + p * 60;
                        p = 1 - p;
                        start = 2;
                        continue;
                    case 3:
                    case 6:
                        // Defensive TD
                        print(ls[17] + "FOR " + os[1 - p] + "***\n");
                        p = 1 - p;
                        // Fall-thru
                    case 2:
                    case 8:
                        // Offensive TD
                        print(ls[17] + "***\n");
                        if (Math.random() <= 0.8) {
                            sa[p] = sa[p] + 7;
                            print("KICK IS GOOD.\n");
                        } else {
                            print("KICK IS OFF TO THE SIDE\n");
                            sa[p] = sa[p] + 6;
                        }
                        show_score();
                        print(os[p] + " KICKS OFF\n");
                        p = 1 - p;
                        start = 1;
                        continue;
                    case 4:
                    case 7:
                        // Touchback
                        print(ls[18] + "\n");
                        p = 1 - p;
                        x = 20 + p * 60;
                        start = 4;
                        continue;
                }
            }
        }
        print("END OF GAME  ***\n");
        print("FINAL SCORE:  " + os[0] + ": " + sa[0] + "  " + os[1] + ": " + sa[1] + "\n");
    }
    
    main();
    
    
    ================================================
    FILE: 37_Football/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 37_Football/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 37_Football/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 37_Football/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ## Porting notes
    
    Variables:
    
    * E: score_limit
    * H(2): Scores
    * T(2): Team toggle
    * T: team who currently possesses the ball
    * L: Offset
    * P: Who has the ball
    * K: yards
    * R: Runback current team in yards
    * P$(20): Actions (see data.json)
    
    Functions:
    
    * `P$(I)`: Access index `I` of the `P` array
    * ABS: abs (absolute value)
    * RND(1): random()
    * GOSUB: Execute a function - will jump back to this
    * GOTO: Just jump
    
    Patterns:
    
    * `T=T(T)`: Toggle the team who currently has the ball
    
    
    ================================================
    FILE: 37_Football/python/data.json
    ================================================
    {
        "players": [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],
        "actions": ["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!!!!"]
    }
    
    
    ================================================
    FILE: 37_Football/python/football.py
    ================================================
    """
    FOOTBALL
    
    A game.
    
    Ported to Python by Martin Thoma in 2022.
    The JavaScript version by Oscar Toledo G. (nanochess) was used
    """
    # NOTE: The newlines might be wrong
    
    import json
    from math import floor
    from pathlib import Path
    from random import randint, random
    from typing import List, Tuple
    
    with open(Path(__file__).parent / "data.json") as f:
        data = json.load(f)
    
    player_data = [num - 1 for num in data["players"]]
    actions = data["actions"]
    
    
    aa: List[int] = [-100 for _ in range(20)]
    ba: List[int] = [-100 for _ in range(20)]
    ca: List[int] = [-100 for _ in range(40)]
    score: List[int] = [0, 0]
    ta: Tuple[int, int] = (1, 0)
    wa: Tuple[int, int] = (-1, 1)
    xa: Tuple[int, int] = (100, 0)
    ya: Tuple[int, int] = (1, -1)
    za: Tuple[int, int] = (0, 100)
    marker: Tuple[str, str] = ("--->", "<---")
    t: int = 0
    p: int = 0
    winning_score: int
    
    
    def ask_bool(prompt: str) -> bool:
        while True:
            answer = input(prompt).lower()
            if answer in ["yes", "y"]:
                return True
            elif answer in ["no", "n"]:
                return False
    
    
    def ask_int(prompt: str) -> int:
        while True:
            answer = input(prompt)
            try:
                return int(answer)
            except Exception:
                pass
    
    
    def get_offense_defense() -> Tuple[int, int]:
        while True:
            input_str = input("INPUT OFFENSIVE PLAY, DEFENSIVE PLAY: ")
            try:
                p1, p2 = (int(n) for n in input_str.split(","))
                return p1, p2
            except Exception:
                pass
    
    
    def field_headers() -> None:
        print("TEAM 1 [0   10   20   30   40   50   60   70   80   90   100] TEAM 2")
        print("\n\n")
    
    
    def separator() -> None:
        print("+" * 72 + "\n")
    
    
    def show_ball() -> None:
        da: Tuple[int, int] = (0, 3)
        print(" " * (da[t] + 5 + int(p / 2)) + marker[t] + "\n")
        field_headers()
    
    
    def show_scores() -> bool:
        print()
        print(f"TEAM 1 SCORE IS {score[0]}")
        print(f"TEAM 2 SCORE IS {score[1]}")
        print()
        if score[t] >= winning_score:
            print(f"TEAM {t+1} WINS*******************")
            return True
        return False
    
    
    def loss_posession() -> None:
        global t
        print()
        print(f"** LOSS OF POSSESSION FROM TEAM {t+1} TO TEAM {ta[t]+1}")
        print()
        separator()
        print()
        t = ta[t]
    
    
    def touchdown() -> None:
        print()
        print(f"TOUCHDOWN BY TEAM {t+1} *********************YEA TEAM")
        q = 7
        g = random()
        if g <= 0.1:
            q = 6
            print("EXTRA POINT NO GOOD")
        else:
            print("EXTRA POINT GOOD")
        score[t] = score[t] + q
    
    
    def print_header() -> None:
        print(" " * 32 + "FOOTBALL")
        print(" " * 15 + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n\n\n")
        print("PRESENTING N.F.U. FOOTBALL (NO FORTRAN USED)\n\n")
    
    
    def print_instructions() -> None:
        print(
            """THIS IS A FOOTBALL GAME FOR TWO TEAMS IN WHICH PLAYERS MUST
    PREPARE A TAPE WITH A DATA STATEMENT (1770 FOR TEAM 1,
    1780 FOR TEAM 2) IN WHICH EACH TEAM SCRAMBLES NOS. 1-20
    THESE NUMBERS ARE THEN ASSIGNED TO TWENTY GIVEN PLAYS.
    A LIST OF NOS. AND THEIR PLAYS IS PROVIDED WITH
    BOTH TEAMS HAVING THE SAME PLAYS. THE MORE SIMILAR THE
    PLAYS THE LESS YARDAGE GAINED.  SCORES ARE GIVEN
    WHENEVER SCORES ARE MADE. SCORES MAY ALSO BE OBTAINED
    BY INPUTTING 99,99 FOR PLAY NOS. TO PUNT OR ATTEMPT A
    FIELD GOAL, INPUT 77,77 FOR PLAY NUMBERS. QUESTIONS WILL BE
    ASKED THEN. ON 4TH DOWN, YOU WILL ALSO BE ASKED WHETHER
    YOU WANT TO PUNT OR ATTEMPT A FIELD GOAL. IF THE ANSWER TO
    BOTH QUESTIONS IS NO IT WILL BE ASSUMED YOU WANT TO
    TRY AND GAIN YARDAGE. ANSWER ALL QUESTIONS YES OR NO.
    THE GAME IS PLAYED UNTIL PLAYERS TERMINATE (CONTROL-C).
    PLEASE PREPARE A TAPE AND RUN.
    """
        )
    
    
    def main() -> None:
        global winning_score
        print_header()
        want_instructions = ask_bool("DO YOU WANT INSTRUCTIONS? ")
        if want_instructions:
            print_instructions()
        print()
        winning_score = ask_int("PLEASE INPUT SCORE LIMIT ON GAME: ")
        for i in range(40):
            index = player_data[i - 1]
            if i < 20:
                aa[index] = i
            else:
                ba[index] = i - 20
            ca[i] = index
        offset = 0
        for t in [0, 1]:
            print(f"TEAM {t+1} PLAY CHART")
            print("NO.      PLAY")
            for i in range(20):
                input_str = f"{ca[i + offset]}"
                while len(input_str) < 6:
                    input_str += " "
                input_str += actions[i]
                print(input_str)
            offset += 20
            t = 1
            print()
            print("TEAR OFF HERE----------------------------------------------")
            print("\n" * 10)
    
        field_headers()
        print("TEAM 1 DEFEND 0 YD GOAL -- TEAM 2 DEFENDS 100 YD GOAL.")
        t = randint(0, 1)
        print()
        print("THE COIN IS FLIPPED")
        routine = 1
        while True:
            if routine <= 1:
                p = xa[t] - ya[t] * 40
                separator()
                print(f"TEAM {t+1} RECEIVES KICK-OFF")
                k = floor(26 * random() + 40)
            if routine <= 2:
                p = p - ya[t] * k
            if routine <= 3:
                if wa[t] * p >= za[t] + 10:
                    print("BALL WENT OUT OF ENDZONE --AUTOMATIC TOUCHBACK--")
                    p = za[t] - wa[t] * 20
                    if routine <= 4:
                        routine = 5
                else:
                    print(f"BALL WENT {k} YARDS.  NOW ON {p}")
                    show_ball()
    
            if routine <= 4:
                want_runback = ask_bool(f"TEAM {t+1} DO YOU WANT TO RUNBACK? ")
    
                if want_runback:
                    k = floor(9 * random() + 1)
                    r = floor(((xa[t] - ya[t] * p + 25) * random() - 15) / k)
                    p = p - wa[t] * r
                    print(f"RUNBACK TEAM {t+1} {r} YARDS")
                    g = random()
                    if g < 0.25:
                        loss_posession()
                        routine = 4
                        continue
                    elif ya[t] * p >= xa[t]:
                        touchdown()
                        if show_scores():
                            return
                        t = ta[t]
                        routine = 1
                        continue
                    elif wa[t] * p >= za[t]:
                        print(f"SAFETY AGAINST TEAM {t+1} **********************OH-OH")
                        score[ta[t]] = score[ta[t]] + 2
                        if show_scores():
                            return
    
                        p = za[t] - wa[t] * 20
                        want_punt = ask_bool(
                            f"TEAM {t+1} DO YOU WANT TO PUNT INSTEAD OF A KICKOFF? "
                        )
                        if want_punt:
                            print(f"TEAM {t+1} WILL PUNT")
                            g = random()
                            if g < 0.25:
                                loss_posession()
                                routine = 4
                                continue
    
                            print()
                            separator()
                            k = floor(25 * random() + 35)
                            t = ta[t]
                            routine = 2
                            continue
    
                        touchdown()
                        if show_scores():
                            return
                        t = ta[t]
                        routine = 1
                        continue
                    else:
                        routine = 5
                        continue
    
                else:
                    if wa[t] * p >= za[t]:
                        p = za[t] - wa[t] * 20
    
            if routine <= 5:
                d = 1
                s = p
    
            if routine <= 6:
                print("=" * 72 + "\n")
                print(f"TEAM {t+1} DOWN {d} ON {p}")
                if d == 1:
                    if ya[t] * (p + ya[t] * 10) >= xa[t]:
                        c = 8
                    else:
                        c = 4
    
                if c != 8:
                    yards = 10 - (ya[t] * p - ya[t] * s)
                    print(" " * 27 + f"{yards} YARDS TO 1ST DOWN")
                else:
                    yards = xa[t] - ya[t] * p
                    print(" " * 27 + f"{yards} YARDS")
    
                show_ball()
                if d == 4:
                    routine = 8
    
            if routine <= 7:
                u = floor(3 * random() - 1)
                while True:
                    p1, p2 = get_offense_defense()
                    if t != 1:
                        p2, p1 = p1, p2
    
                    if p1 == 99:
                        if show_scores():
                            return
                        if p1 == 99:
                            continue
    
                    if p1 < 1 or p1 > 20 or p2 < 1 or p2 > 20:
                        print("ILLEGAL PLAY NUMBER, CHECK AND ", end="")
                        continue
    
                    break
                p1 -= 1
                p2 -= 1
    
            if d == 4 or p1 == 77:
                want_punt = ask_bool(f"DOES TEAM {t+1} WANT TO PUNT? ")
    
                if want_punt:
                    print()
                    print(f"TEAM {t+1} WILL PUNT")
                    g = random()
                    if g < 0.25:
                        loss_posession()
                        routine = 4
                        continue
    
                    print()
                    separator()
                    k = floor(25 * random() + 35)
                    t = ta[t]
                    routine = 2
                    continue
    
                attempt_field_goal = ask_bool(
                    f"DOES TEAM {t+1} WANT TO ATTEMPT A FIELD GOAL? "
                )
    
                if attempt_field_goal:
                    print()
                    print(f"TEAM {t+1} WILL ATTEMPT A FIELD GOAL")
                    g = random()
                    if g < 0.025:
                        loss_posession()
                        routine = 4
                        continue
                    else:
                        f = floor(35 * random() + 20)
                        print()
                        print(f"KICK IS {f} YARDS LONG")
                        p = p - wa[t] * f
                        g = random()
                        if g < 0.35:
                            print("BALL WENT WIDE")
                        elif ya[t] * p >= xa[t]:
                            print(
                                f"FIELD GOLD GOOD FOR TEAM {t+1} *********************YEA"
                            )
                            q = 3
                            score[t] = score[t] + q
                            if show_scores():
                                return
                            t = ta[t]
                            routine = 1
                            continue
    
                        print(f"FIELD GOAL UNSUCCESFUL TEAM {t+1}-----------------TOO BAD")
                        print()
                        separator()
                        if ya[t] * p < xa[t] + 10:
                            print()
                            print(f"BALL NOW ON {p}")
                            t = ta[t]
                            show_ball()
                            routine = 4
                            continue
                        else:
                            t = ta[t]
                            routine = 3
                            continue
    
                else:
                    routine = 7
                    continue
    
            y = floor(
                abs(aa[p1] - ba[p2]) / 19 * ((xa[t] - ya[t] * p + 25) * random() - 15)
            )
            print()
            if t == 1 and aa[p1] < 11 or t == 2 and ba[p2] < 11:
                print("THE BALL WAS RUN")
            elif u == 0:
                print(f"PASS INCOMPLETE TEAM {t+1}")
                y = 0
            else:
                g = random()
                if g <= 0.025 and y > 2:
                    print("PASS COMPLETED")
                else:
                    print("QUARTERBACK SCRAMBLED")
    
            p = p - wa[t] * y
            print()
            print(f"NET YARDS GAINED ON DOWN {d} ARE {y}")
    
            g = random()
            if g <= 0.025:
                loss_posession()
                routine = 4
                continue
            elif ya[t] * p >= xa[t]:
                touchdown()
                if show_scores():
                    return
                t = ta[t]
                routine = 1
                continue
            elif wa[t] * p >= za[t]:
                print()
                print(f"SAFETY AGAINST TEAM {t+1} **********************OH-OH")
                score[ta[t]] = score[ta[t]] + 2
                if show_scores():
                    return
                p = za[t] - wa[t] * 20
                want_punt = ask_bool(
                    f"TEAM {t+1} DO YOU WANT TO PUNT INSTEAD OF A KICKOFF? "
                )
                if want_punt:
                    print()
                    print(f"TEAM {t+1} WILL PUNT")
                    g = random()
                    if g < 0.25:
                        loss_posession()
                        routine = 4
                        continue
    
                    print()
                    separator()
                    k = floor(25 * random() + 35)
                    t = ta[t]
                    routine = 2
                    continue
    
                touchdown()
                if show_scores():
                    return
                t = ta[t]
                routine = 1
            elif ya[t] * p - ya[t] * s >= 10:
                routine = 5
            else:
                d += 1
                if d != 5:
                    routine = 6
                else:
                    print()
                    print(f"CONVERSION UNSUCCESSFUL TEAM {t+1}")
                    t = ta[t]
                    print()
                    separator()
                    routine = 5
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 37_Football/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 37_Football/vbnet/Football.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Football", "Football.vbproj", "{5491221D-33D3-4ADF-9E0A-FB58D5C12EE2}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{5491221D-33D3-4ADF-9E0A-FB58D5C12EE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{5491221D-33D3-4ADF-9E0A-FB58D5C12EE2}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{5491221D-33D3-4ADF-9E0A-FB58D5C12EE2}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{5491221D-33D3-4ADF-9E0A-FB58D5C12EE2}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 37_Football/vbnet/Football.vbproj
    ================================================
    
      
        Exe
        Football
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 37_Football/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 38_Fur_Trader/README.md
    ================================================
    ### Fur Trader
    
    You are the leader of a French fur trading expedition in 1776 leaving the Ontario area to sell furs and get supplies for the next year. You have a choice of three forts at which you may trade. The cost of supplies and the amount you recieve for your furs will depend upon the fort you choose. You also specify what types of furs that you have to trade.
    
    The game goes on and on until you elect to trade no longer.
    
    Author of the program is Dan Bachor, University of Calgary, Alberta, Canada.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=69)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=84)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### 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)
    
    
    ================================================
    FILE: 38_Fur_Trader/c/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [ANSI-C](https://en.wikipedia.org/wiki/ANSI_C)
    
    ##### Translator Notes:
    I tried to preserve as much of the original layout and flow of the code
    as possible.  However I did use enumerated types for the Fort numbers
    and Fur types.  I think this was certainly a change for the better, and
    makes the code much easier to read.
    
    I also tried to minimise the use of pointers, and stuck with old-school
    C formatting, because you never know how old the compiler is.
    
    Interestingly the code seems to have a bug around the prices of Fox Furs.
    The commodity-rate for these is stored in the variable `D1`, however some
    paths through the code do not set this price.  So there was a chance of
    using this uninitialised, or whatever the previous loop set.  I don't
    think this was the original authors intent.  So I preserved the original flow
    of the code (using the previous `D1` value), but also catching the
    uninitialised path, and assigning a "best guess" value.
    
    krt@krt.com.au 2020-10-10
    
    
    ================================================
    FILE: 38_Fur_Trader/c/furtrader.c
    ================================================
    
    /*
     * Ported from furtrader.bas to ANSI C (C99) by krt@krt.com.au
     *
     * compile with:
     *    gcc -g -Wall -Werror furtrader.c -o furtrader
     */
    
    #include 
    #include 
    #include 
    #include 
    
    
    /* Constants */
    #define FUR_TYPE_COUNT    4
    #define FUR_MINK          0
    #define FUR_BEAVER        1
    #define FUR_ERMINE        2
    #define FUR_FOX           3
    #define MAX_FURS        190
    const char *FUR_NAMES[FUR_TYPE_COUNT] = { "MINK", "BEAVER", "ERMINE", "FOX" };
    
    #define FORT_TYPE_COUNT 3
    #define FORT_MONTREAL   1
    #define FORT_QUEBEC     2
    #define FORT_NEWYORK    3
    const char *FORT_NAMES[FORT_TYPE_COUNT] = { "HOCHELAGA (MONTREAL)", "STADACONA (QUEBEC)", "NEW YORK" };
    
    
    
    /* Print the words at the specified column */
    void printAtColumn( int column, const char *words )
    {
        int i;
        for ( i=0; i> " );                                 /* prompt the user */
            fgets( buffer, sizeof( buffer ), stdin );        /* read from the console into the buffer */
            result = (int)strtol( buffer, &endstr, 10 );     /* only simple error checking */
    
            if ( endstr == buffer )                          /* was the string -> integer ok? */
                result = -1;
        }
    
        return result;
    }
    
    
    /*
     * Prompt the user for YES/NO input.
     * When input is given, try to work out if it's YES, Yes, yes, Y, etc.
     * And convert to a single upper-case letter
     * Returns a character of 'Y' or 'N'.
     */
    char getYesOrNo()
    {
        char result = '!';
        char buffer[64];   /* somewhere to store user input */
    
        while ( !( result == 'Y' || result == 'N' ) )       /* While the answer was not Yes or No */
        {
            print( "ANSWER YES OR NO" );
            printf( ">> " );
    
            fgets( buffer, sizeof( buffer ), stdin );            /* read from the console into the buffer */
            if ( buffer[0] == 'Y' || buffer[0] == 'y' )
                result = 'Y';
            else if ( buffer[0] == 'N' || buffer[0] == 'n' )
                result = 'N';
        }
    
        return result;
    }
    
    
    
    /*
     * Show the player the choices of Fort, get their input, if the
     * input is a valid choice (1,2,3) return it, otherwise keep
     * prompting the user.
     */
    int getFortChoice()
    {
        int result = 0;
    
        while ( result == 0 )
        {
            print( "" );
            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." );
            print( "ANSWER 1, 2, OR 3." );
    
            result = getNumericInput();   /* get input from the player */
        }
    
        return result;
    }
    
    
    /*
     * Print the description for the fort
     */
    void showFortComment( int which_fort )
    {
        print( "" );
        if ( which_fort == FORT_MONTREAL )
        {
            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 ( which_fort == FORT_QUEBEC )
        {
            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 ( which_fort == FORT_NEWYORK )
        {
            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
        {
            printf( "Internal error #1, fort %d does not exist\n", which_fort );
            exit( 1 );  /* you have a bug */
        }
        print( "" );
    }
    
    
    /*
     * Prompt the player for how many of each fur type they want.
     * Accept numeric inputs, re-prompting on incorrect input values
     */
    void getFursPurchase( int *furs )
    {
        int i;
    
        printf( "YOUR %d FURS ARE DISTRIBUTED AMONG THE FOLLOWING\n", FUR_TYPE_COUNT );
        print( "KINDS OF PELTS: MINK, BEAVER, ERMINE AND FOX." );
        print( "" );
    
        for ( i=0; i MAX_FURS )
                {
                    print( "" );
                    print( "YOU MAY NOT HAVE THAT MANY FURS." );
                    print( "DO NOT TRY TO CHEAT.  I CAN ADD." );
                    print( "YOU MUST START AGAIN." );
                    print( "" );
                    game_state = STATE_STARTING;   /* T/N: Wow, harsh. */
                }
                else
                {
                    game_state = STATE_CHOOSING_FORT;
                }
            }
    
            else if ( game_state == STATE_CHOOSING_FORT )
            {
                which_fort = getFortChoice();
                showFortComment( which_fort );
                print( "DO YOU WANT TO TRADE AT ANOTHER FORT?" );
                yes_or_no = getYesOrNo();
                if ( yes_or_no == 'N' )
                    game_state = STATE_TRAVELLING;
            }
    
            else if ( game_state == STATE_TRAVELLING )
            {
                print( "" );
                if ( which_fort == FORT_MONTREAL )
                {
                    mink_price   = ( ( 0.2 * randFloat() + 0.70 ) * 100 + 0.5 ) / 100;
                    ermine_price = ( ( 0.2 * randFloat() + 0.65 ) * 100 + 0.5 ) / 100;
                    beaver_price = ( ( 0.2 * randFloat() + 0.75 ) * 100 + 0.5 ) / 100;
                    fox_price    = ( ( 0.2 * randFloat() + 0.80 ) * 100 + 0.5 ) / 100;
    
                    print( "SUPPLIES AT FORT HOCHELAGA COST $150.00." );
                    print( "YOUR TRAVEL EXPENSES TO HOCHELAGA WERE $10.00." );
                    player_funds -= 160;
                }
    
                else if ( which_fort == FORT_QUEBEC )
                {
                    mink_price   = ( ( 0.30 * randFloat() + 0.85 ) * 100 + 0.5 ) / 100;
                    ermine_price = ( ( 0.15 * randFloat() + 0.80 ) * 100 + 0.5 ) / 100;
                    beaver_price = ( ( 0.20 * randFloat() + 0.90 ) * 100 + 0.5 ) / 100;
                    fox_price    = ( ( 0.25 * randFloat() + 1.10 ) * 100 + 0.5 ) / 100;
                    event_picker = ( 10 * randFloat() ) + 1;
    
                    if ( event_picker <= 2 )
                    {
                        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." );
                        player_furs[ FUR_BEAVER ] = 0;
                    }
                    else if ( event_picker <= 6 )
                    {
                        print( "YOU ARRIVED SAFELY AT FORT STADACONA." );
                    }
                    else if ( event_picker <= 8 )
                    {
                        print( "YOUR CANOE UPSET IN THE LACHINE RAPIDS.  YOU" );
                        print( "LOST ALL YOUR FURS." );
                        zeroInventory( player_furs );
                    }
                    else if ( event_picker <= 10 )
                    {
                        print( "YOUR FOX PELTS WERE NOT CURED PROPERLY." );
                        print( "NO ONE WILL BUY THEM." );
                        player_furs[ FUR_FOX ] = 0;
                    }
                    else
                    {
                        printf( "Internal Error #3, Out-of-bounds event_picker %d\n", event_picker );
                        exit( 1 );  /* you have a bug */
                    }
    
                    print( "" );
                    print( "SUPPLIES AT FORT STADACONA COST $125.00." );
                    print( "YOUR TRAVEL EXPENSES TO STADACONA WERE $15.00." );
                    player_funds -= 140;
                }
    
                else if ( which_fort == FORT_NEWYORK )
                {
                    mink_price   = ( ( 0.15 * randFloat() + 1.05 ) * 100 + 0.5 ) / 100;
                    ermine_price = ( ( 0.15 * randFloat() + 0.95 ) * 100 + 0.5 ) / 100;
                    beaver_price = ( ( 0.25 * randFloat() + 1.00 ) * 100 + 0.5 ) / 100;
                    if ( fox_price < 0 )
                    {
                        /* Original Bug?  There is no Fox price generated for New York,
                           it will use any previous "D1" price.
                           So if there was no previous value, make one up */
                        fox_price = ( ( 0.25 * randFloat() + 1.05 ) * 100 + 0.5 ) / 100; /* not in orginal code */
                    }
                    event_picker = ( 10 * randFloat() ) + 1;
    
                    if ( event_picker <= 2 )
                    {
                        print( "YOU WERE ATTACKED BY A PARTY OF IROQUOIS." );
                        print( "ALL PEOPLE IN YOUR TRADING GROUP WERE" );
                        print( "KILLED.  THIS ENDS THE GAME." );
                        exit( 0 );
                    }
                    else if ( event_picker <= 6 )
                    {
                        print( "YOU WERE LUCKY.  YOU ARRIVED SAFELY" );
                        print( "AT FORT NEW YORK." );
                    }
                    else if ( event_picker <= 8 )
                    {
                        print( "YOU NARROWLY ESCAPED AN IROQUOIS RAIDING PARTY." );
                        print( "HOWEVER, YOU HAD TO LEAVE ALL YOUR FURS BEHIND." );
                        zeroInventory( player_furs );
                    }
                    else if ( event_picker <= 10 )
                    {
                        mink_price /= 2;
                        fox_price  /= 2;
                        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 %d\n" );
                        exit( 1 );  /* you have a bug */
                    }
    
                    print( "" );
                    print( "SUPPLIES AT NEW YORK COST $85.00." );
                    print( "YOUR TRAVEL EXPENSES TO NEW YORK WERE $25.00." );
                    player_funds -= 105;
                }
    
                else
                {
                    printf( "Internal error #2, fort %d does not exist\n", which_fort );
                    exit( 1 );  /* you have a bug */
                }
    
                /* Calculate sales */
                beaver_value = beaver_price * player_furs[ FUR_BEAVER ];
                fox_value    = fox_price    * player_furs[ FUR_FOX ];
                ermine_value = ermine_price * player_furs[ FUR_ERMINE ];
                mink_value   = mink_price   * player_furs[ FUR_MINK ];
    
                print( "" );
                printf( "YOUR BEAVER SOLD FOR $%6.2f\n", beaver_value );
                printf( "YOUR FOX SOLD FOR    $%6.2f\n", fox_value );
                printf( "YOUR ERMINE SOLD FOR $%6.2f\n", ermine_value );
                printf( "YOUR MINK SOLD FOR   $%6.2f\n", mink_value );
    
                player_funds += beaver_value + fox_value + ermine_value + mink_value;
    
                print( "" );
                printf( "YOU NOW HAVE $ %1.2f INCLUDING YOUR PREVIOUS SAVINGS\n", player_funds );
    
                print( "" );
                print( "DO YOU WANT TO TRADE FURS NEXT YEAR?" );
                yes_or_no = getYesOrNo();
                if ( yes_or_no == 'N' )
                    exit( 0 );             /* STOP */
                else
                    game_state = STATE_TRADING;
    
            }
        }
    
        return 0; /* exit OK */
    }
    
    
    ================================================
    FILE: 38_Fur_Trader/csharp/FurTrader.csproj
    ================================================
    
    
      
        Exe
        netcoreapp3.1
      
    
    
    
    
    ================================================
    FILE: 38_Fur_Trader/csharp/FurTrader.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30804.86
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FurTrader", "FurTrader.csproj", "{1FB826B9-8794-4DB7-B676-B51F177B7B87}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{1FB826B9-8794-4DB7-B676-B51F177B7B87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{1FB826B9-8794-4DB7-B676-B51F177B7B87}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{1FB826B9-8794-4DB7-B676-B51F177B7B87}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{1FB826B9-8794-4DB7-B676-B51F177B7B87}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {DDB24448-50EB-47C6-BDB9-465896A81779}
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 38_Fur_Trader/csharp/Game.cs
    ================================================
    using System;
    
    namespace FurTrader
    {
        public class Game
        {
            /// 
            /// random number generator; no seed to be faithful to original implementation
            /// 
            private Random Rnd { get; } = new Random();
    
            /// 
            /// Generate a price for pelts based off a factor and baseline value
            /// 
            /// Multiplier for the price
            /// The baseline price
            /// A randomised price for pelts
            internal double RandomPriceGenerator(double factor, double baseline)
            {
                var price = (Convert.ToInt32((factor * Rnd.NextDouble() + baseline) * 100d) + 5) / 100d;
                return price;
            }
    
            /// 
            /// Main game loop function. This will play the game endlessly until the player chooses to quit or a GameOver event occurs
            /// 
            /// 
            /// General structure followed from Adam Dawes (@AdamDawes575) implementation of Acey Ducey.");
            /// 
            internal void GameLoop()
            {
                // display instructions to the player
                DisplayIntroText();
    
                var state = new GameState();
    
                // loop for each turn until the player decides not to continue (or has a Game Over event)
                while ((!state.GameOver) && ContinueGame())
                {
                    // clear display at start of each turn
                    Console.Clear();
    
                    // play the next turn; pass game state for details and updates from the turn
                    PlayTurn(state);
                }
    
                // end screen; show some statistics to the player
                Console.Clear();
                Console.WriteLine("Thanks for playing!");
                Console.WriteLine("");
                Console.WriteLine($"Total Expeditions: {state.ExpeditionCount}");
                Console.WriteLine($"Final Amount:      {state.Savings.ToString("c")}");
            }
    
            /// 
            /// Display instructions on how to play the game and wait for the player to press a key.
            /// 
            private void DisplayIntroText()
            {
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.WriteLine("Fur Trader.");
                Console.WriteLine("Creating Computing, Morristown, New Jersey.");
                Console.WriteLine("");
    
                Console.ForegroundColor = ConsoleColor.DarkGreen;
                Console.WriteLine("Originally published in 1978 in the book 'Basic Computer Games' by David Ahl.");
                Console.WriteLine("");
    
                Console.ForegroundColor = ConsoleColor.Gray;
                Console.WriteLine("You are the leader of a French fur trading expedition in 1776 leaving the Lake Ontario area to sell furs and get supplies for the next year.");
                Console.WriteLine("");
                Console.WriteLine("You have a choice of three forts at which you may trade. The cost of supplies and the amount you receive for your furs will depend on the fort that you choose.");
                Console.WriteLine("");
    
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.WriteLine("Press any key start the game.");
                Console.ReadKey(true);
    
            }
    
            /// 
            /// Prompt the player to try again, and wait for them to press Y or N.
            /// 
            /// Returns true if the player wants to try again, false if they have finished playing.
            private bool ContinueGame()
            {
                Console.WriteLine("");
                Console.ForegroundColor = ConsoleColor.White;
                Console.WriteLine("Do you wish to trade furs? ");
                Console.Write("Answer (Y)es or (N)o ");
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.Write("> ");
    
                char pressedKey;
                // Keep looping until we get a recognised input
                do
                {
                    // Read a key, don't display it on screen
                    ConsoleKeyInfo key = Console.ReadKey(true);
                    // Convert to upper-case so we don't need to care about capitalisation
                    pressedKey = Char.ToUpper(key.KeyChar);
                    // Is this a key we recognise? If not, keep looping
                } while (pressedKey != 'Y' && pressedKey != 'N');
    
                // Display the result on the screen
                Console.WriteLine(pressedKey);
    
                // Return true if the player pressed 'Y', false for anything else.
                return (pressedKey == 'Y');
            }
    
            /// 
            /// Play a turn
            /// 
            /// The current game state
            private void PlayTurn(GameState state)
            {
                state.UnasignedFurCount = 190;      /// start with 190 furs each turn
    
                // provide current status to user
                Console.WriteLine(new string('_', 70));
                Console.WriteLine("");
                Console.ForegroundColor = ConsoleColor.White;
                Console.WriteLine("");
                Console.WriteLine($"You have {state.Savings.ToString("c")} savings and {state.UnasignedFurCount} furs to begin the expedition.");
                Console.WriteLine("");
                Console.WriteLine($"Your {state.UnasignedFurCount} furs are distributed among the following kinds of pelts: Mink, Beaver, Ermine, and Fox");
                Console.WriteLine("");
    
                // get input on number of pelts
                Console.ForegroundColor = ConsoleColor.White;
                Console.Write("How many Mink pelts do you have? ");
                state.MinkPelts = GetPelts(state.UnasignedFurCount);
                state.UnasignedFurCount -= state.MinkPelts;
                Console.WriteLine("");
                Console.ForegroundColor = ConsoleColor.White;
                Console.WriteLine($"You have {state.UnasignedFurCount} furs remaining for distribution");
                Console.Write("How many Beaver pelts do you have? ");
                state.BeaverPelts = GetPelts(state.UnasignedFurCount);
                state.UnasignedFurCount -= state.BeaverPelts;
                Console.WriteLine("");
                Console.ForegroundColor = ConsoleColor.White;
                Console.WriteLine($"You have {state.UnasignedFurCount} furs remaining for distribution");
                Console.Write("How many Ermine pelts do you have? ");
                state.ErminePelts = GetPelts(state.UnasignedFurCount);
                state.UnasignedFurCount -= state.ErminePelts;
                Console.WriteLine("");
                Console.ForegroundColor = ConsoleColor.White;
                Console.WriteLine($"You have {state.UnasignedFurCount} furs remaining for distribution");
                Console.Write("How many Fox pelts do you have? ");
                state.FoxPelts = GetPelts(state.UnasignedFurCount);
                state.UnasignedFurCount -= state.FoxPelts;
    
                // get input on which fort to trade with; user gets an opportunity to evaluate and re-select fort after selection until user confirms selection
                do
                {
                    Console.ForegroundColor = ConsoleColor.White;
                    Console.WriteLine("");
                    Console.WriteLine("Do you want to trade your furs at Fort 1, Fort 2, or Fort 3");
                    Console.WriteLine("Fort 1 is Fort Hochelaga (Montreal) and is under the protection of the French army.");
                    Console.WriteLine("Fort 2 is Fort Stadacona (Quebec) and is under the protection of the French army. However, you must make a portage and cross the Lachine rapids.");
                    Console.WriteLine("Fort 3 is Fort New York and is under Dutch control. You must cross through Iroquois land.");
                    Console.WriteLine("");
                    state.SelectedFort = GetSelectedFort();
    
                    DisplaySelectedFortInformation(state.SelectedFort);
    
                } while (TradeAtAnotherFort());
    
                // process the travel to the fort
                ProcessExpeditionOutcome(state);
    
                // display results of expedition (savings change) to the user
                Console.ForegroundColor = ConsoleColor.White;
                Console.Write("You now have ");
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.Write($"{state.Savings.ToString("c")}");
                Console.ForegroundColor = ConsoleColor.White;
                Console.WriteLine(" including your previous savings.");
    
                // update the turn count now that another turn has been played
                state.ExpeditionCount += 1;
            }
    
            /// 
            /// Method to show the expedition costs to the player with some standard formatting
            /// 
            /// The name of the fort traded with
            /// The cost of the supplies at the fort
            /// The travel expenses for the expedition
            internal void DisplayCosts(string fortname, double supplies, double expenses)
            {
                Console.ForegroundColor = ConsoleColor.White;
                Console.Write($"Supplies at {fortname} cost".PadLeft(55));
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine($"{supplies.ToString("c").PadLeft(10)}");
                Console.ForegroundColor = ConsoleColor.White;
                Console.Write($"Your travel expenses to {fortname} were".PadLeft(55));
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine($"{expenses.ToString("c").PadLeft(10)}");
                Console.ForegroundColor = ConsoleColor.White;
            }
    
            /// 
            /// Process the results of the expedition
            /// 
            /// the game state
            private void ProcessExpeditionOutcome(GameState state)
            {
                var beaverPrice = RandomPriceGenerator(0.25d, 1.00d);
                var foxPrice =    RandomPriceGenerator(0.2d , 0.80d);
                var erminePrice = RandomPriceGenerator(0.15d, 0.95d);
                var minkPrice =   RandomPriceGenerator(0.2d , 0.70d);
    
                var fortname = String.Empty;
                var suppliesCost = 0.0d;
                var travelExpenses = 0.0d;
    
                // create a random value 1 to 10 for the different outcomes at each fort
                var p = ((int)(10 * Rnd.NextDouble())) + 1;
                Console.WriteLine("");
    
                switch (state.SelectedFort)
                {
                    case 1:     // outcome for expedition to Fort Hochelaga
                        beaverPrice = RandomPriceGenerator(0.2d, 0.75d);
                        foxPrice =    RandomPriceGenerator(0.2d, 0.80d);
                        erminePrice = RandomPriceGenerator(0.2d, 0.65d);
                        minkPrice =   RandomPriceGenerator(0.2d, 0.70d);
                        fortname = "Fort Hochelaga";
                        suppliesCost = 150.0d;
                        travelExpenses = 10.0d;
                        break;
                    case 2:     // outcome for expedition to Fort Stadacona
                        beaverPrice = RandomPriceGenerator(0.2d , 0.90d);
                        foxPrice =    RandomPriceGenerator(0.2d , 0.80d);
                        erminePrice = RandomPriceGenerator(0.15d, 0.80d);
                        minkPrice =   RandomPriceGenerator(0.3d , 0.85d);
                        fortname = "Fort Stadacona";
                        suppliesCost = 125.0d;
                        travelExpenses = 15.0d;
                        if (p <= 2)
                        {
                            state.BeaverPelts = 0;
                            Console.WriteLine("Your beaver were to heavy to carry across the portage.");
                            Console.WriteLine("You had to leave the pelts but found them stolen when you returned");
                        }
                        else if (p <= 6)
                        {
                            Console.WriteLine("You arrived safely at Fort Stadacona");
                        }
                        else if (p <= 8)
                        {
                            state.BeaverPelts = 0;
                            state.FoxPelts = 0;
                            state.ErminePelts = 0;
                            state.MinkPelts = 0;
                            Console.WriteLine("Your canoe upset in the Lachine Rapids.");
                            Console.WriteLine("Your lost all your furs");
                        }
                        else if (p <= 10)
                        {
                            state.FoxPelts = 0;
                            Console.WriteLine("Your fox pelts were not cured properly.");
                            Console.WriteLine("No one will buy them.");
                        }
                        else
                        {
                            throw new Exception($"Unexpected Outcome p = {p}");
                        }
                        break;
                    case 3:     // outcome for expedition to Fort New York
                        beaverPrice = RandomPriceGenerator(0.2d , 1.00d);
                        foxPrice =    RandomPriceGenerator(0.25d, 1.10d);
                        erminePrice = RandomPriceGenerator(0.2d , 0.95d);
                        minkPrice =   RandomPriceGenerator(0.15d, 1.05d);
                        fortname = "Fort New York";
                        suppliesCost = 80.0d;
                        travelExpenses = 25.0d;
                        if (p <= 2)
                        {
                            state.BeaverPelts = 0;
                            state.FoxPelts = 0;
                            state.ErminePelts = 0;
                            state.MinkPelts = 0;
                            suppliesCost = 0.0d;
                            travelExpenses = 0.0d;
                            Console.WriteLine("You were attacked by a party of Iroquois.");
                            Console.WriteLine("All people in your trading group were killed.");
                            Console.WriteLine("This ends the game (press any key).");
                            Console.ReadKey(true);
                            state.GameOver = true;
                        }
                        else if (p <= 6)
                        {
                            Console.WriteLine("You were lucky. You arrived safely at Fort New York.");
                        }
                        else if (p <= 8)
                        {
                            state.BeaverPelts = 0;
                            state.FoxPelts = 0;
                            state.ErminePelts = 0;
                            state.MinkPelts = 0;
                            Console.WriteLine("You narrowly escaped an Iroquois raiding party.");
                            Console.WriteLine("However, you had to leave all your furs behind.");
                        }
                        else if (p <= 10)
                        {
                            beaverPrice = beaverPrice / 2;
                            minkPrice = minkPrice / 2;
                            Console.WriteLine("Your mink and beaver were damaged on your trip.");
                            Console.WriteLine("You receive only half the current price for these furs.");
                        }
                        else
                        {
                            throw new Exception($"Unexpected Outcome p = {p}");
                        }
                        break;
                    default:
                        break;
                }
    
                var beaverSale = state.BeaverPelts * beaverPrice;
                var foxSale = state.FoxPelts * foxPrice;
                var ermineSale = state.ErminePelts * erminePrice;
                var minkSale = state.MinkPelts * minkPrice;
                var profit = beaverSale + foxSale + ermineSale + minkSale - suppliesCost - travelExpenses;
                state.Savings += profit;
    
                Console.WriteLine("");
                Console.ForegroundColor = ConsoleColor.White;
                Console.Write($"Your {state.BeaverPelts.ToString().PadLeft(3, ' ')} beaver pelts sold @ {beaverPrice.ToString("c")} per pelt for a total");
                Console.ForegroundColor = ConsoleColor.Green;
                Console.WriteLine($"{beaverSale.ToString("c").PadLeft(10)}");
                Console.ForegroundColor = ConsoleColor.White;
                Console.Write($"Your {state.FoxPelts.ToString().PadLeft(3, ' ')} fox    pelts sold @ {foxPrice.ToString("c")} per pelt for a total");
                Console.ForegroundColor = ConsoleColor.Green;
                Console.WriteLine($"{foxSale.ToString("c").PadLeft(10)}");
                Console.ForegroundColor = ConsoleColor.White;
                Console.Write($"Your {state.ErminePelts.ToString().PadLeft(3, ' ')} ermine pelts sold @ {erminePrice.ToString("c")} per pelt for a total");
                Console.ForegroundColor = ConsoleColor.Green;
                Console.WriteLine($"{ermineSale.ToString("c").PadLeft(10)}");
                Console.ForegroundColor = ConsoleColor.White;
                Console.Write($"Your {state.MinkPelts.ToString().PadLeft(3, ' ')} mink   pelts sold @ {minkPrice.ToString("c")} per pelt for a total");
                Console.ForegroundColor = ConsoleColor.Green;
                Console.WriteLine($"{minkSale.ToString("c").PadLeft(10)}");
                Console.WriteLine("");
                DisplayCosts(fortname, suppliesCost, travelExpenses);
                Console.WriteLine("");
                Console.Write($"Profit / Loss".PadLeft(55));
                Console.ForegroundColor = profit >= 0.0d ? ConsoleColor.Green : ConsoleColor.Red;
                Console.WriteLine($"{profit.ToString("c").PadLeft(10)}");
                Console.ForegroundColor = ConsoleColor.White;
                Console.WriteLine("");
            }
    
            private void DisplaySelectedFortInformation(int selectedFort)
            {
                Console.WriteLine("");
                Console.ForegroundColor = ConsoleColor.White;
    
                switch (selectedFort)
                {
                    case 1:    // selected fort details for Fort Hochelaga
                        Console.WriteLine("You have chosen the easiest route.");
                        Console.WriteLine("However, the fort is far from any seaport.");
                        Console.WriteLine("The value you receive for your furs will be low.");
                        Console.WriteLine("The cost of supplies will be higher than at Forts Stadacona or New York");
                        break;
                    case 2:    // selected fort details for Fort Stadacona
                        Console.WriteLine("You have chosen a hard route.");
                        Console.WriteLine("It is, in comparsion, harder than the route to Hochelaga but easier than the route to New York.");
                        Console.WriteLine("You will receive an average value for your furs.");
                        Console.WriteLine("The cost of your supplies will be average.");
                        break;
                    case 3:    // selected fort details for Fort New York
                        Console.WriteLine("You have chosen the most difficult route.");
                        Console.WriteLine("At Fort New York you will receive the higest value for your furs.");
                        Console.WriteLine("The cost of your supplies will be lower than at all the other forts.");
                        break;
                    default:
                        break;
                }
            }
    
            private bool TradeAtAnotherFort()
            {
                Console.ForegroundColor = ConsoleColor.White;
                Console.WriteLine("");
                Console.WriteLine("Do you want to trade at another fort?");
                Console.Write("Answer (Y)es or (N)o ");
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.Write("> ");
    
                char pressedKey;
                // Keep looping until we get a recognised input
                do
                {
                    // Read a key, don't display it on screen
                    ConsoleKeyInfo key = Console.ReadKey(true);
                    // Convert to upper-case so we don't need to care about capitalisation
                    pressedKey = Char.ToUpper(key.KeyChar);
                    // Is this a key we recognise? If not, keep looping
                } while (pressedKey != 'Y' && pressedKey != 'N');
    
                // Display the result on the screen
                Console.WriteLine(pressedKey);
    
                // Return true if the player pressed 'Y', false for anything else.
                return (pressedKey == 'Y');
            }
    
            /// 
            /// Get an amount of pelts from the user
            /// 
            /// The total pelts available
            /// Returns the amount the player selects
            private int GetPelts(int furCount)
            {
                int peltCount;
    
                // loop until the user enters a valid value
                do
                {
                    Console.ForegroundColor = ConsoleColor.Yellow;
                    Console.Write("> ");
                    string input = Console.ReadLine();
    
                    // parse user information to check if it is a valid entry
                    if (!int.TryParse(input, out peltCount))
                    {
                        // invalid entry; message back to user
                        Console.ForegroundColor = ConsoleColor.Red;
                        Console.WriteLine("Sorry, I didn't understand. Please enter the number of pelts.");
    
                        // continue looping
                        continue;
                    }
    
                    // check if plet amount is more than the available pelts
                    if (peltCount > furCount)
                    {
                        // too many pelts selected
                        Console.ForegroundColor = ConsoleColor.Red;
                        Console.WriteLine($"You may not have that many furs. Do not try to cheat. I can add.");
    
                        // continue looping
                        continue;
                    }
    
                    // valid pelt amount entered
                    break;
                } while (true);
    
                // return pelt count to the user
                return peltCount;
            }
    
            /// 
            /// Prompt the user for their selected fort
            /// 
            /// returns the fort the user has selected
            private int GetSelectedFort()
            {
                int selectedFort;
    
                // loop until the user enters a valid value
                do
                {
                    Console.ForegroundColor = ConsoleColor.White;
                    Console.Write("Answer 1, 2, or 3 ");
                    Console.ForegroundColor = ConsoleColor.Yellow;
                    Console.Write("> ");
                    string input = Console.ReadLine();
    
                    // is the user entry valid
                    if (!int.TryParse(input, out selectedFort))
                    {
                        // no, invalid data
                        Console.ForegroundColor = ConsoleColor.Red;
                        Console.WriteLine("Sorry, I didn't understand. Please answer 1, 2, or 3.");
    
                        // continue looping
                        continue;
                    }
    
                    // is the selected fort an option (one, two or three only)
                    if (selectedFort != 1 && selectedFort != 2 && selectedFort != 3)
                    {
                        // no, invalid for selected
                        Console.ForegroundColor = ConsoleColor.Red;
                        Console.WriteLine($"Please answer 1, 2, or 3.");
    
                        // continue looping
                        continue;
                    }
    
                    // valid fort selected, stop looping
                    break;
                } while (true);
    
                // return the players selected fort
                return selectedFort;
            }
        }
    }
    
    
    ================================================
    FILE: 38_Fur_Trader/csharp/GameState.cs
    ================================================
    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace FurTrader
    {
        internal class GameState
        {
            internal bool GameOver { get; set; }
    
            internal double Savings { get; set; }
    
            internal int ExpeditionCount { get; set; }
    
            internal int UnasignedFurCount { get; set; }
    
            internal int[] Pelts { get; private set; }
    
            internal int MinkPelts { get { return this.Pelts[0]; } set { this.Pelts[0] = value; } }
            internal int BeaverPelts { get { return this.Pelts[1]; } set { this.Pelts[1] = value; } }
            internal int ErminePelts { get { return this.Pelts[2]; } set { this.Pelts[2] = value; } }
            internal int FoxPelts { get { return this.Pelts[3]; } set { this.Pelts[3] = value; } }
    
            internal int SelectedFort { get; set; }
    
            internal GameState()
            {
                this.Savings = 600;
                this.ExpeditionCount = 0;
                this.UnasignedFurCount = 190;
                this.Pelts = new int[4];
                this.SelectedFort = 0;
            }
    
            internal void StartTurn()
            {
                this.SelectedFort = 0;              // reset to a default value
                this.UnasignedFurCount = 190;       // each turn starts with 190 furs
                this.Pelts = new int[4];            // reset pelts on each turn
            }
        }
    }
    
    
    ================================================
    FILE: 38_Fur_Trader/csharp/Program.cs
    ================================================
    using System;
    
    namespace FurTrader
    {
        public class Program
        {
            /// 
            /// This function will be called automatically when the application begins
            /// 
            /// 
            public static void Main(string[] args)
            {
                // Create an instance of our main Game class
                var game = new Game();
    
                // Call its GameLoop function. This will play the game endlessly in a loop until the player chooses to quit.
                game.GameLoop();
            }
        }
    }
    
    
    ================================================
    FILE: 38_Fur_Trader/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 38_Fur_Trader/furtrader.bas
    ================================================
    1 DIM F(4)
    2 PRINT TAB(31);"FUR TRADER"
    4 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    6 PRINT: PRINT: PRINT
    15 GOSUB 1091
    16 LET I=600
    17 PRINT "DO YOU WISH TO TRADE FURS?"
    18 GOSUB 1402
    19 IF B$="YES" THEN 100
    20 IF B$="YES " THEN 100
    21 STOP
    100 PRINT
    101 PRINT "YOU HAVE $";I " SAVINGS."
    102 PRINT "AND 190 FURS TO BEGIN THE EXPEDITION."
    261 LET E1=INT((.15*RND(1)+.95)*10^2+.5)/10^2
    262 LET B1=INT((.25*RND(1)+1.00)*10^2+.5)/10^2
    300 PRINT
    301 PRINT "YOUR 190 FURS ARE DISTRIBUTED AMONG THE FOLLOWING"
    302 PRINT "KINDS OF PELTS: MINK, BEAVER, ERMINE AND FOX."
    310 GOSUB 1430
    315 RESTORE
    330 FOR J=1 TO 4
    332 READ B$
    333 PRINT
    335 PRINT "HOW MANY ";B$;" PELTS DO YOU HAVE";
    338 INPUT F(J)
    340 LET F(0)=F(1)+F(2)+F(3)+F(4)
    342 IF F(0)=190 THEN 1100
    344 IF F(0)>190 THEN 500
    348 NEXT J
    350 GOTO 1100
    500 PRINT
    501 PRINT "YOU MAY NOT HAVE THAT MANY FURS."
    502 PRINT "DO NOT TRY TO CHEAT.  I CAN ADD."
    503 PRINT "YOU MUST START AGAIN."
    504 GOTO 15
    508 PRINT
    511 PRINT "DO YOU WANT TO TRADE FURS NEXT YEAR?"
    513 GOTO 18
    1091 PRINT "YOU ARE THE LEADER OF A FRENCH FUR TRADING EXPEDITION IN "
    1092 PRINT "1776 LEAVING THE LAKE ONTARIO AREA TO SELL FURS AND GET"
    1093 PRINT "SUPPLIES FOR THE NEXT YEAR.  YOU HAVE A CHOICE OF THREE"
    1094 PRINT "FORTS AT WHICH YOU MAY TRADE.  THE COST OF SUPPLIES"
    1095 PRINT "AND THE AMOUNT YOU RECEIVE FOR YOUR FURS WILL DEPEND"
    1096 PRINT "ON THE FORT THAT YOU CHOOSE."
    1099 RETURN
    1100 PRINT "YOU MAY TRADE YOUR FURS AT FORT 1, FORT 2,"
    1102 PRINT "OR FORT 3.  FORT 1 IS FORT HOCHELAGA (MONTREAL)"
    1103 PRINT "AND IS UNDER THE PROTECTION OF THE FRENCH ARMY."
    1104 PRINT "FORT 2 IS FORT STADACONA (QUEBEC) AND IS UNDER THE"
    1105 PRINT "PROTECTION OF THE FRENCH ARMY.  HOWEVER, YOU MUST"
    1106 PRINT "MAKE A PORTAGE AND CROSS THE LACHINE RAPIDS."
    1108 PRINT "FORT 3 IS FORT NEW YORK AND IS UNDER DUTCH CONTROL."
    1109 PRINT "YOU MUST CROSS THROUGH IROQUOIS LAND."
    1110 PRINT "ANSWER 1, 2, OR 3."
    1111 INPUT B
    1112 IF B=1 THEN 1120
    1113 IF B=2 THEN 1135
    1115 IF B=3 THEN 1147
    1116 GOTO 1110
    1120 PRINT "YOU HAVE CHOSEN THE EASIEST ROUTE.  HOWEVER, THE FORT"
    1121 PRINT "IS FAR FROM ANY SEAPORT.  THE VALUE"
    1122 PRINT "YOU RECEIVE FOR YOUR FURS WILL BE LOW AND THE COST"
    1123 PRINT "OF SUPPLIES HIGHER THAN AT FORTS STADACONA OR NEW YORK."
    1125 GOSUB 1400
    1129 IF B$="YES" THEN 1110
    1130 GOTO 1160
    1135 PRINT "YOU HAVE CHOSEN A HARD ROUTE.  IT IS, IN COMPARSION,"
    1136 PRINT "HARDER THAN THE ROUTE TO HOCHELAGA BUT EASIER THAN"
    1137 PRINT "THE ROUTE TO NEW YORK.  YOU WILL RECEIVE AN AVERAGE VALUE"
    1138 PRINT "FOR YOUR FURS AND THE COST OF YOUR SUPPLIES WILL BE AVERAGE."
    1141 GOSUB 1400
    1144 IF B$="YES" THEN 1110
    1145 GOTO 1198
    1147 PRINT "YOU HAVE CHOSEN THE MOST DIFFICULT ROUTE.  AT"
    1148 PRINT "FORT NEW YORK YOU WILL RECEIVE THE HIGHEST VALUE"
    1149 PRINT "FOR YOUR FURS.  THE COST OF YOUR SUPPLIES"
    1150 PRINT "WILL BE LOWER THAN AT ALL THE OTHER FORTS."
    1152 GOSUB 1400
    1155 IF B$="YES" THEN 1110
    1156 GOTO 1250
    1160 LET I=I-160
    1169 PRINT
    1174 LET M1=INT((.2*RND(1)+.7)*10^2+.5)/10^2
    1175 LET E1=INT((.2*RND(1)+.65)*10^2+.5)/10^2
    1176 LET B1=INT((.2*RND(1)+.75)*10^2+.5)/10^2
    1177 LET D1=INT((.2*RND(1)+.8)*10^2+.5)/10^2
    1180 PRINT "SUPPLIES AT FORT HOCHELAGA COST $150.00."
    1181 PRINT "YOUR TRAVEL EXPENSES TO HOCHELAGA WERE $10.00."
    1190 GOTO 1410
    1198 LET I=I-140
    1201 PRINT
    1205 LET M1=INT((.3*RND(1)+.85)*10^2+.5)/10^2
    1206 LET E1=INT((.15*RND(1)+.8)*10^2+.5)/10^2
    1207 LET B1=INT((.2*RND(1)+.9)*10^2+.5)/10^2
    1209 LET P=INT(10*RND(1))+1
    1210 IF P<=2 THEN 1216
    1212 IF P<=6 THEN 1224
    1213 IF P<=8 THEN 1226
    1215 IF P<=10 THEN 1235
    1216 LET F(2)=0
    1218 PRINT "YOUR BEAVER WERE TOO HEAVY TO CARRY ACROSS"
    1219 PRINT "THE PORTAGE.  YOU HAD TO LEAVE THE PELTS, BUT FOUND"
    1220 PRINT "THEM STOLEN WHEN YOU RETURNED."
    1221 GOSUB 1244
    1222 GOTO 1414
    1224 PRINT "YOU ARRIVED SAFELY AT FORT STADACONA."
    1225 GOTO 1239
    1226 GOSUB 1430
    1230 PRINT "YOUR CANOE UPSET IN THE LACHINE RAPIDS.  YOU"
    1231 PRINT "LOST ALL YOUR FURS."
    1232 GOSUB 1244
    1233 GOTO 1418
    1235 LET F(4)=0
    1237 PRINT "YOUR FOX PELTS WERE NOT CURED PROPERLY."
    1238 PRINT "NO ONE WILL BUY THEM."
    1239 GOSUB 1244
    1240 GOTO 1410
    1244 PRINT "SUPPLIES AT FORT STADACONA COST $125.00."
    1246 PRINT "YOUR TRAVEL EXPENSES TO STADACONA WERE $15.00."
    1248 RETURN
    1250 LET I=I-105
    1254 PRINT
    1260 LET M1=INT((.15*RND(1)+1.05)*10^2+.5)/10^2
    1263 LET D1=INT((.25*RND(1)+1.1)*10^2+.5)/10^2
    1270 LET P=INT(10*RND(1))+1
    1271 IF P<=2 THEN 1281
    1272 IF P<=6 THEN 1291
    1273 IF P<=8 THEN 1295
    1274 IF P<=10 THEN 1306
    1281 PRINT "YOU WERE ATTACKED BY A PARTY OF IROQUOIS."
    1282 PRINT "ALL PEOPLE IN YOUR TRADING GROUP WERE"
    1283 PRINT "KILLED.  THIS ENDS THE GAME."
    1284 STOP
    1291 PRINT "YOU WERE LUCKY.  YOU ARRIVED SAFELY"
    1292 PRINT "AT FORT NEW YORK."
    1293 GOTO 1311
    1295 GOSUB 1430
    1300 PRINT "YOU NARROWLY ESCAPED AN IROQUOIS RAIDING PARTY."
    1301 PRINT "HOWEVER, YOU HAD TO LEAVE ALL YOUR FURS BEHIND."
    1303 GOSUB 1320
    1304 GOTO 1418
    1306 LET B1=B1/2
    1307 LET M1=M1/2
    1308 PRINT "YOUR MINK AND BEAVER WERE DAMAGED ON YOUR TRIP."
    1309 PRINT "YOU RECEIVE ONLY HALF THE CURRENT PRICE FOR THESE FURS."
    1311 GOSUB 1320
    1312 GOTO 1410
    1320 PRINT "SUPPLIES AT NEW YORK COST $80.00."
    1321 PRINT "YOUR TRAVEL EXPENSES TO NEW YORK WERE $25.00."
    1322 RETURN
    1400 PRINT "DO YOU WANT TO TRADE AT ANOTHER FORT?"
    1402 PRINT "ANSWER YES OR NO",
    1403 INPUT B$
    1404 RETURN
    1410 PRINT
    1412 PRINT "YOUR BEAVER SOLD FOR $";B1*F(2);
    1414 PRINT "YOUR FOX SOLD FOR $";D1*F(4)
    1416 PRINT "YOUR ERMINE SOLD FOR $";E1*F(3);
    1417 PRINT "YOUR MINK SOLD FOR $";M1*F(1)
    1418 LET I=M1*F(1)+B1*F(2)+E1*F(3)+D1*F(4)+I
    1420 PRINT
    1422 PRINT "YOU NOW HAVE $";I;" INCLUDING YOUR PREVIOUS SAVINGS"
    1425 GOTO 508
    1430 FOR J=1 TO 4
    1432 LET F(J)=0
    1434 NEXT J
    1436 RETURN
    2000 DATA "MINK","BEAVER","ERMINE","FOX"
    2046 END
    
    
    ================================================
    FILE: 38_Fur_Trader/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 38_Fur_Trader/java/src/FurTrader.java
    ================================================
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Scanner;
    
    /**
     * Game of Fur Trader
     * 

    * Based on the Basic game of Fur Trader here * https://github.com/coding-horror/basic-computer-games/blob/main/38%20Fur%20Trader/furtrader.bas *

    * Note: The idea was to create a version of the 1970's Basic game in Java, without introducing * new features - no additional text, error checking, etc has been added. */ public class FurTrader { public static final double START_SAVINGS_AMOUNT = 600.0; public static final int STARTING_FURS = 190; public static final int FORT_HOCHELAGA_MONTREAL = 1; public static final int FORT_STADACONA_QUEBEC = 2; public static final int FORT_NEW_YORK = 3; public static final String MINK = "MINK"; public static final int MINK_ENTRY = 0; public static final String BEAVER = "BEAVER"; public static final int BEAVER_ENTRY = 1; public static final String ERMINE = "ERMINE"; public static final int ERMINE_ENTRY = 2; public static final String FOX = "FOX"; public static final int FOX_ENTRY = 3; // Used for keyboard input private final Scanner kbScanner; private enum GAME_STATE { STARTUP, INIT, TRADE_AT_FORT, TRADE_SUMMARY, TRADE_AGAIN, GAME_OVER } // Current game state private GAME_STATE gameState; private double savings; private double minkPrice; private double beaverPrice; private double erminePrice; private double foxPrice; private ArrayList pelts; private boolean playedOnce; public FurTrader() { kbScanner = new Scanner(System.in); gameState = GAME_STATE.INIT; playedOnce = false; } /** * Main game loop */ public void play() { do { switch (gameState) { case INIT: savings = START_SAVINGS_AMOUNT; // Only display initial game heading once if (!playedOnce) { playedOnce = true; gameStartupMessage(); } intro(); if (yesEntered(displayTextAndGetInput("DO YOU WISH TO TRADE FURS? "))) { System.out.println("YOU HAVE $" + formatNumber(savings) + " SAVINGS."); System.out.println("AND " + STARTING_FURS + " FURS TO BEGIN THE EXPEDITION."); // Create a new array of Pelts. pelts = initPelts(); gameState = GAME_STATE.STARTUP; } else { gameState = GAME_STATE.GAME_OVER; } break; case STARTUP: // Reset count of pelts (all types) resetPelts(); // This is where we will go to after processing all pelts. gameState = GAME_STATE.TRADE_AT_FORT; int totalPelts = 0; // Cycle through all types of pelts for (int i = 0; i < pelts.size(); i++) { Pelt pelt = pelts.get(i); int number = getPeltCount(pelt.getName()); totalPelts += number; if (totalPelts > STARTING_FURS) { System.out.println("YOU MAY NOT HAVE THAT MANY FURS."); System.out.println("DO NOT TRY TO CHEAT. I CAN ADD."); System.out.println("YOU MUST START AGAIN."); System.out.println(); // Restart the game gameState = GAME_STATE.INIT; break; } else { // update count entered by player and save back to ArrayList. pelt.setPeltCount(number); pelts.set(i, pelt); // Its possible for the player to put all their pelt allocation // into one or more different pelts. They don't have to use all four types. // If we have an exact count of pelts matching the MAX // don't bother continuing to ask for more. if (totalPelts == STARTING_FURS) { break; } } } // Only move onto the trading part of the game if the player didn't add too many pelts if (gameState != GAME_STATE.STARTUP) { // Set ermine and beaver default prices here, depending on where you trade these // defaults will either be used or overwritten with other values. // check out the tradeAt??? methods for more info. erminePrice = ((.15 * Math.random() + .95) * (Math.pow(10, 2) + .5)) / Math.pow(10, 2); beaverPrice = ((.25 * Math.random() + 1.00) * (Math.pow(10, 2) + .5)) / Math.pow(10, 2); System.out.println(); } break; case TRADE_AT_FORT: extendedTradingInfo(); int answer = displayTextAndGetNumber("ANSWER 1, 2, OR 3. "); System.out.println(); // Now show the details of the fort they are about to trade // and give the player the chance to NOT proceed. // A "No" or false means they do not want to change to another fort if (!confirmFort(answer)) { switch (answer) { case 1: tradeAtFortHochelagaMontreal(); gameState = GAME_STATE.TRADE_SUMMARY; break; case 2: tradeAtFortStadaconaQuebec(); gameState = GAME_STATE.TRADE_SUMMARY; break; case 3: // Did the player and party all die? if (!tradeAtFortNewYork()) { gameState = GAME_STATE.GAME_OVER; } else { gameState = GAME_STATE.TRADE_SUMMARY; } break; } break; } case TRADE_SUMMARY: System.out.println(); double beaverTotal = beaverPrice * pelts.get(BEAVER_ENTRY).getNumber(); System.out.print("YOUR BEAVER SOLD FOR $ " + formatNumber(beaverTotal)); double foxTotal = foxPrice * pelts.get(FOX_ENTRY).getNumber(); System.out.println(simulateTabs(5) + "YOUR FOX SOLD FOR $ " + formatNumber(foxTotal)); double erMineTotal = erminePrice * pelts.get(ERMINE_ENTRY).getNumber(); System.out.print("YOUR ERMINE SOLD FOR $ " + formatNumber(erMineTotal)); double minkTotal = minkPrice * pelts.get(MINK_ENTRY).getNumber(); System.out.println(simulateTabs(5) + "YOUR MINK SOLD FOR $ " + formatNumber(minkTotal)); savings += beaverTotal + foxTotal + erMineTotal + minkTotal; System.out.println(); System.out.println("YOU NOW HAVE $" + formatNumber(savings) + " INCLUDING YOUR PREVIOUS SAVINGS"); gameState = GAME_STATE.TRADE_AGAIN; break; case TRADE_AGAIN: if (yesEntered(displayTextAndGetInput("DO YOU WANT TO TRADE FURS NEXT YEAR? "))) { gameState = GAME_STATE.STARTUP; } else { gameState = GAME_STATE.GAME_OVER; } } } while (gameState != GAME_STATE.GAME_OVER); } /** * Create all pelt types with a count of zero * * @return Arraylist of initialised Pelt objects. */ private ArrayList initPelts() { ArrayList tempPelts = new ArrayList<>(); tempPelts.add(new Pelt(MINK, 0)); tempPelts.add(new Pelt(BEAVER, 0)); tempPelts.add(new Pelt(ERMINE, 0)); tempPelts.add(new Pelt(FOX, 0)); return tempPelts; } /** * Display a message about trading at each fort and confirm if the player wants to trade * at ANOTHER fort * * @param fort the fort in question * @return true if YES was typed by player */ private boolean confirmFort(int fort) { switch (fort) { case FORT_HOCHELAGA_MONTREAL: System.out.println("YOU HAVE CHOSEN THE EASIEST ROUTE. HOWEVER, THE FORT"); System.out.println("IS FAR FROM ANY SEAPORT. THE VALUE"); System.out.println("YOU RECEIVE FOR YOUR FURS WILL BE LOW AND THE COST"); System.out.println("OF SUPPLIES HIGHER THAN AT FORTS STADACONA OR NEW YORK."); break; case FORT_STADACONA_QUEBEC: System.out.println("YOU HAVE CHOSEN A HARD ROUTE. IT IS, IN COMPARSION,"); System.out.println("HARDER THAN THE ROUTE TO HOCHELAGA BUT EASIER THAN"); System.out.println("THE ROUTE TO NEW YORK. YOU WILL RECEIVE AN AVERAGE VALUE"); System.out.println("FOR YOUR FURS AND THE COST OF YOUR SUPPLIES WILL BE AVERAGE."); break; case FORT_NEW_YORK: System.out.println("YOU HAVE CHOSEN THE MOST DIFFICULT ROUTE. AT"); System.out.println("FORT NEW YORK YOU WILL RECEIVE THE HIGHEST VALUE"); System.out.println("FOR YOUR FURS. THE COST OF YOUR SUPPLIES"); System.out.println("WILL BE LOWER THAN AT ALL THE OTHER FORTS."); break; } System.out.println("DO YOU WANT TO TRADE AT ANOTHER FORT?"); return yesEntered(displayTextAndGetInput("ANSWER YES OR NO ")); } /** * Trade at the safest fort - Fort Hochelaga * No chance of anything bad happening, so just calculate amount per pelt * and return */ private void tradeAtFortHochelagaMontreal() { savings -= 160.0; System.out.println(); System.out.println("SUPPLIES AT FORT HOCHELAGA COST $150.00."); System.out.println("YOUR TRAVEL EXPENSES TO HOCHELAGA WERE $10.00."); minkPrice = ((.2 * Math.random() + .7) * (Math.pow(10, 2) + .5)) / Math.pow(10, 2); erminePrice = ((.2 * Math.random() + .65) * (Math.pow(10, 2) + .5)) / Math.pow(10, 2); beaverPrice = ((.2 * Math.random() + .75) * (Math.pow(10, 2) + .5)) / Math.pow(10, 2); foxPrice = ((.2 * Math.random() + .8) * (Math.pow(10, 2) + .5)) / Math.pow(10, 2); } private void tradeAtFortStadaconaQuebec() { savings -= 140.0; System.out.println(); minkPrice = ((.2 * Math.random() + .85) * (Math.pow(10, 2) + .5)) / Math.pow(10, 2); erminePrice = ((.2 * Math.random() + .8) * (Math.pow(10, 2) + .5)) / Math.pow(10, 2); beaverPrice = ((.2 * Math.random() + .9) * (Math.pow(10, 2) + .5)) / Math.pow(10, 2); // What happened during the trip to the fort? int tripResult = (int) (Math.random() * 10) + 1; if (tripResult <= 2) { // Find the Beaver pelt in our Arraylist Pelt beaverPelt = pelts.get(BEAVER_ENTRY); // Pelts got stolen, so update to a count of zero beaverPelt.lostPelts(); // Update it back in the ArrayList pelts.set(BEAVER_ENTRY, beaverPelt); System.out.println("YOUR BEAVER WERE TOO HEAVY TO CARRY ACROSS"); System.out.println("THE PORTAGE. YOU HAD TO LEAVE THE PELTS, BUT FOUND"); System.out.println("THEM STOLEN WHEN YOU RETURNED."); } else if (tripResult <= 6) { System.out.println("YOU ARRIVED SAFELY AT FORT STADACONA."); } else if (tripResult <= 8) { System.out.println("YOUR CANOE UPSET IN THE LACHINE RAPIDS. YOU"); System.out.println("LOST ALL YOUR FURS."); // Clear out all pelts. resetPelts(); } else if (tripResult <= 10) { // Fox pelts not cured System.out.println("YOUR FOX PELTS WERE NOT CURED PROPERLY."); System.out.println("NO ONE WILL BUY THEM."); // Bug because Fox furs were not calculated above in original basic program // Find the Beaver pelt in our Arraylist Pelt foxPelt = pelts.get(FOX_ENTRY); // Pelts got stolen, so update to a count of zero foxPelt.lostPelts(); // Update it back in the ArrayList pelts.set(FOX_ENTRY, foxPelt); } System.out.println("SUPPLIES AT FORT STADACONA COST $125.00."); System.out.println("YOUR TRAVEL EXPENSES TO STADACONA WERE $15.00."); } private boolean tradeAtFortNewYork() { boolean playerAlive = true; savings -= 105.0; System.out.println(); minkPrice = ((.2 * Math.random() + 1.05) * (Math.pow(10, 2) + .5)) / Math.pow(10, 2); foxPrice = ((.2 * Math.random() + 1.1) * (Math.pow(10, 2) + .5)) / Math.pow(10, 2); // What happened during the trip to the fort? int tripResult = (int) (Math.random() * 10) + 1; if (tripResult <= 2) { System.out.println("YOU WERE ATTACKED BY A PARTY OF IROQUOIS."); System.out.println("ALL PEOPLE IN YOUR TRADING GROUP WERE"); System.out.println("KILLED. THIS ENDS THE GAME."); playerAlive = false; } else if (tripResult <= 6) { System.out.println("YOU WERE LUCKY. YOU ARRIVED SAFELY"); System.out.println("AT FORT NEW YORK."); } else if (tripResult <= 8) { System.out.println("YOU NARROWLY ESCAPED AN IROQUOIS RAIDING PARTY."); System.out.println("HOWEVER, YOU HAD TO LEAVE ALL YOUR FURS BEHIND."); // Clear out all pelts. resetPelts(); } else if (tripResult <= 10) { beaverPrice /= 2; minkPrice /= 2; System.out.println("YOUR MINK AND BEAVER WERE DAMAGED ON YOUR TRIP."); System.out.println("YOU RECEIVE ONLY HALF THE CURRENT PRICE FOR THESE FURS."); } if (playerAlive) { System.out.println("SUPPLIES AT NEW YORK COST $80.00."); System.out.println("YOUR TRAVEL EXPENSES TO NEW YORK WERE $25.00."); } return playerAlive; } /** * Reset pelt count for all Pelt types to zero. */ private void resetPelts() { for (int i = 0; i < pelts.size(); i++) { Pelt pelt = pelts.get(i); pelt.lostPelts(); pelts.set(i, pelt); } } /** * Return a pelt object containing the user entered number of pelts. * * @param peltName Name of pelt (Type) * @return number of pelts assigned by player */ private int getPeltCount(String peltName) { return displayTextAndGetNumber("HOW MANY " + peltName + " PELTS DO YOU HAVE? "); } private void extendedTradingInfo() { System.out.println("YOU MAY TRADE YOUR FURS AT FORT 1, FORT 2,"); System.out.println("OR FORT 3. FORT 1 IS FORT HOCHELAGA (MONTREAL)"); System.out.println("AND IS UNDER THE PROTECTION OF THE FRENCH ARMY."); System.out.println("FORT 2 IS FORT STADACONA (QUEBEC) AND IS UNDER THE"); System.out.println("PROTECTION OF THE FRENCH ARMY. HOWEVER, YOU MUST"); System.out.println("MAKE A PORTAGE AND CROSS THE LACHINE RAPIDS."); System.out.println("FORT 3 IS FORT NEW YORK AND IS UNDER DUTCH CONTROL."); System.out.println("YOU MUST CROSS THROUGH IROQUOIS LAND."); System.out.println(); } private void gameStartupMessage() { System.out.println(simulateTabs(31) + "FUR TRADER"); System.out.println(simulateTabs(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println(); } private void intro() { System.out.println("YOU ARE THE LEADER OF A FRENCH FUR TRADING EXPEDITION IN "); System.out.println("1776 LEAVING THE LAKE ONTARIO AREA TO SELL FURS AND GET"); System.out.println("SUPPLIES FOR THE NEXT YEAR. YOU HAVE A CHOICE OF THREE"); System.out.println("FORTS AT WHICH YOU MAY TRADE. THE COST OF SUPPLIES"); System.out.println("AND THE AMOUNT YOU RECEIVE FOR YOUR FURS WILL DEPEND"); System.out.println("ON THE FORT THAT YOU CHOOSE."); System.out.println(); } /** * Format a double number to two decimal points for output. * * @param number double number * @return formatted number as a string */ private String formatNumber(double number) { return String.format("%.2f", number); } /* * Print a message on the screen, then accept input from Keyboard. * Converts input to an Integer * * @param text message to be displayed on screen. * @return what was typed by the player. */ private int displayTextAndGetNumber(String text) { return Integer.parseInt(displayTextAndGetInput(text)); } /* * Print a message on the screen, then accept input from Keyboard. * * @param text message to be displayed on screen. * @return what was typed by the player. */ private String displayTextAndGetInput(String text) { System.out.print(text); return kbScanner.next(); } /** * Simulate the old basic tab(xx) command which indented text by xx spaces. * * @param spaces number of spaces required * @return String with number of spaces */ private String simulateTabs(int spaces) { char[] spacesTemp = new char[spaces]; Arrays.fill(spacesTemp, ' '); return new String(spacesTemp); } /** * Checks whether player entered Y or YES to a question. * * @param text player string from kb * @return true of Y or YES was entered, otherwise false */ private boolean yesEntered(String text) { return stringIsAnyValue(text, "Y", "YES"); } /** * Check whether a string equals one of a variable number of values * Useful to check for Y or YES for example * Comparison is case insensitive. * * @param text source string * @param values a range of values to compare against the source string * @return true if a comparison was found in one of the variable number of strings passed */ private boolean stringIsAnyValue(String text, String... values) { return Arrays.stream(values).anyMatch(str -> str.equalsIgnoreCase(text)); } } ================================================ FILE: 38_Fur_Trader/java/src/FurTraderGame.java ================================================ public class FurTraderGame { public static void main(String[] args) { FurTrader furTrader = new FurTrader(); furTrader.play(); } } ================================================ FILE: 38_Fur_Trader/java/src/Pelt.java ================================================ /** * Pelt object - tracks the name and number of pelts the player has for this pelt type */ public class Pelt { private final String name; private int number; public Pelt(String name, int number) { this.name = name; this.number = number; } public void setPeltCount(int pelts) { this.number = pelts; } public int getNumber() { return this.number; } public String getName() { return this.name; } public void lostPelts() { this.number = 0; } } ================================================ FILE: 38_Fur_Trader/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 38_Fur_Trader/javascript/furtrader.html ================================================ FUR TRADER

    
    
    
    
    
    
    ================================================
    FILE: 38_Fur_Trader/javascript/furtrader.js
    ================================================
    // FUR TRADER
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    var f = [];
    var bs = [, "MINK", "BEAVER", "ERMINE", "FOX"];
    
    function reset_stats()
    {
        for (var j = 1; j <= 4; j++)
            f[j] = 0;
    }
    
    // Main program
    async function main()
    {
        print(tab(31) + "FUR TRADER\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        first_time = true;
        while (1) {
            if (first_time) {
                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");
                i = 600;
                print("DO YOU WISH TO TRADE FURS?\n");
                first_time = false;
            }
            print("ANSWER YES OR NO\t");
            str = await input();
            if (str == "NO")
                break;
            print("\n");
            print("YOU HAVE $" + i + " SAVINGS.\n");
            print("AND 190 FURS TO BEGIN THE EXPEDITION.\n");
            e1 = Math.floor((0.15 * Math.random() + 0.95) * Math.pow(10, 2) + 0.5) / Math.pow(10, 2);
            b1 = Math.floor((0.25 * Math.random() + 1.00) * Math.pow(10, 2) + 0.5) / Math.pow(10, 2);
            print("\n");
            print("YOUR 190 FURS ARE DISTRIBUTED AMONG THE FOLLOWING\n");
            print("KINDS OF PELTS: MINK, BEAVER, ERMINE AND FOX.\n");
            reset_stats();
            for (j = 1; j <= 4; j++) {
                print("\n");
                print("HOW MANY " + bs[j] + " PELTS DO YOU HAVE\n");
                f[j] = parseInt(await input());
                f[0] = f[1] + f[2] + f[3] + f[4];
                if (f[0] == 190)
                    break;
                if (f[0] > 190) {
                    print("\n");
                    print("YOU MAY NOT HAVE THAT MANY FURS.\n");
                    print("DO NOT TRY TO CHEAT.  I CAN ADD.\n");
                    print("YOU MUST START AGAIN.\n");
                    break;
                }
            }
            if (f[0] > 190) {
                first_time = true;
                continue;
            }
            print("YOU 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");
            do {
                print("ANSWER 1, 2, OR 3.\n");
                b = parseInt(await input());
                if (b == 1) {
                    print("YOU 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");
                } else if (b == 2) {
                    print("YOU 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");
                } else {
                    print("YOU 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");
                }
                if (b >= 1 && b <= 3) {
                    print("DO YOU WANT TO TRADE AT ANOTHER FORT?\n");
                    print("ANSWER YES OR NO\t");
                    str = await input();
                    if (str == "YES") {
                        b = 0;
                    }
                }
            } while (b < 1 || b > 3) ;
            show_beaver = true;
            show_all = true;
            if (b == 1) {
                i -= 160;
                print("\n");
                m1 = Math.floor((0.2 * Math.random() + 0.7) * Math.pow(10, 2) + 0.5) / Math.pow(10, 2);
                e1 = Math.floor((0.2 * Math.random() + 0.65) * Math.pow(10, 2) + 0.5) / Math.pow(10, 2);
                b1 = Math.floor((0.2 * Math.random() + 0.75) * Math.pow(10, 2) + 0.5) / Math.pow(10, 2);
                d1 = Math.floor((0.2 * Math.random() + 0.8) * Math.pow(10, 2) + 0.5) / Math.pow(10, 2);
                print("SUPPLIES AT FORT HOCHELAGA COST $150.00.\n");
                print("YOUR TRAVEL EXPENSES TO HOCHELAGA WERE $10.00.\n");
            } else if (b == 2) {
                i -= 140;
                print("\n");
                m1 = Math.floor((0.3 * Math.random() + 0.85) * Math.pow(10, 2) + 0.5) / Math.pow(10, 2);
                e1 = Math.floor((0.15 * Math.random() + 0.8) * Math.pow(10, 2) + 0.5) / Math.pow(10, 2);
                b1 = Math.floor((0.2 * Math.random() + 0.9) * Math.pow(10, 2) + 0.5) / Math.pow(10, 2);
                p = Math.floor(10 * Math.random()) + 1;
                if (p <= 2) {
                    f[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");
                    show_beaver = false;
                } else if (p <= 6) {
                    print("YOU ARRIVED SAFELY AT FORT STADACONA.\n");
                } else if (p <= 8) {
                    reset_stats();
                    print("YOUR CANOE UPSET IN THE LACHINE RAPIDS.  YOU\n");
                    print("LOST ALL YOUR FURS.\n");
                    show_all = false;
                } else if (p <= 10) {
                    f[4] = 0;
                    print("YOUR FOX PELTS WERE NOT CURED PROPERLY.\n");
                    print("NO ONE WILL BUY THEM.\n");
                }
                print("SUPPLIES AT FORT STADACONA COST $125.00.\n");
                print("YOUR TRAVEL EXPENSES TO STADACONA WERE $15.00.\n");
    
                d1 = Math.floor((0.2 * Math.random() + 0.8) * Math.pow(10, 2) + 0.5) / Math.pow(10, 2);
            } else if (b == 3) {
                i -= 105;
                print("\n");
                m1 = Math.floor((0.15 * Math.random() + 1.05) * Math.pow(10, 2) + 0.5) / Math.pow(10, 2);
                d1 = Math.floor((0.25 * Math.random() + 1.1) * Math.pow(10, 2) + 0.5) / Math.pow(10, 2);
                p = Math.floor(10 * Math.random()) + 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");
                    break;
                } else if (p <= 6) {
                    print("YOU WERE LUCKY.  YOU ARRIVED SAFELY\n");
                    print("AT FORT NEW YORK.\n");
                } else if (p <= 8) {
                    reset_stats();
                    print("YOU NARROWLY ESCAPED AN IROQUOIS RAIDING PARTY.\n");
                    print("HOWEVER, YOU HAD TO LEAVE ALL YOUR FURS BEHIND.\n");
                    show_all = false;
                } else if (p <= 10) {
                    b1 /= 2;
                    m1 /= 2;
                    print("YOUR MINK AND BEAVER WERE DAMAGED ON YOUR TRIP.\n");
                    print("YOU RECEIVE ONLY HALF THE CURRENT PRICE FOR THESE FURS.\n");
                }
                print("SUPPLIES AT NEW YORK COST $80.00.\n");
                print("YOUR TRAVEL EXPENSES TO NEW YORK WERE $25.00.\n");
            }
            print("\n");
            if (show_all) {
                if (show_beaver)
                    print("YOUR BEAVER SOLD FOR $" + b1 * f[2] + " ");
                print("YOUR FOX SOLD FOR $" + d1 * f[4] + "\n");
                print("YOUR ERMINE SOLD FOR $" + e1 * f[3] + " ");
                print("YOUR MINK SOLD FOR $" + m1 * f[1] + "\n");
            }
            i += m1 * f[1] + b1 * f[2] + e1 * f[3] + d1 * f[4];
            print("\n");
            print("YOU NOW HAVE $" + i + " INCLUDING YOUR PREVIOUS SAVINGS\n");
            print("\n");
            print("DO YOU WANT TO TRADE FURS NEXT YEAR?\n");
        }
    }
    
    main();
    
    
    ================================================
    FILE: 38_Fur_Trader/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 38_Fur_Trader/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 38_Fur_Trader/perl/README.md
    ================================================
    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.
    
    
    ================================================
    FILE: 38_Fur_Trader/perl/furtrader.pl
    ================================================
    #!/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;
    }
    
    
    ================================================
    FILE: 38_Fur_Trader/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    ##### Translator Notes:
    I tried to preserve as much of the original layout and flow of the code
    as possible.  However I did use quasi enumerated types for the Fort numbers
    and Fur types.  I think this was certainly a change for the better, and
    makes the code much easier to read.
    
    I program in many different languages on a daily basis.  Most languages
    require brackets around expressions, so I just cannot bring myself to
    write an expression without brackets.  IMHO it makes the code easier to study,
    but it does contravene the Python PEP-8 Style guide.
    
    Interestingly the code seems to have a bug around the prices of Fox Furs.
    The commodity-rate for these is stored in the variable `D1`, however some
    paths through the code do not set this price.  So there was a chance of
    using this uninitialised, or whatever the previous loop set.  I don't
    think this was the original authors intent.  So I preserved the original flow
    of the code (using the previous `D1` value), but also catching the
    uninitialised path, and assigning a "best guess" value.
    
    krt@krt.com.au 2020-10-10
    
    
    ================================================
    FILE: 38_Fur_Trader/python/furtrader.py
    ================================================
    #!/usr/bin/env python3
    
    import random  # for generating random numbers
    import sys  # for system function, like exit()
    from typing import List
    
    # global variables for storing player's status
    player_funds: float = 0  # no money
    player_furs = [0, 0, 0, 0]  # no furs
    
    
    # Constants
    FUR_MINK = 0
    FUR_BEAVER = 1
    FUR_ERMINE = 2
    FUR_FOX = 3
    MAX_FURS = 190
    FUR_NAMES = ["MINK", "BEAVER", "ERMINE", "FOX"]
    
    FORT_MONTREAL = 1
    FORT_QUEBEC = 2
    FORT_NEWYORK = 3
    FORT_NAMES = ["HOCHELAGA (MONTREAL)", "STADACONA (QUEBEC)", "NEW YORK"]
    
    
    def show_introduction() -> None:
        """Show the player the introductory message"""
        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()
    
    
    def get_fort_choice() -> int:
        """Show the player the choices of Fort, get their input, if the
        input is a valid choice (1,2,3) return it, otherwise keep
        prompting the user."""
        result = 0
        while result == 0:
            print()
            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.")
            print("ANSWER 1, 2, OR 3.")
    
            player_choice = input(">> ")  # get input from the player
    
            # try to convert the player's string input into an integer
            try:
                result = int(player_choice)  # string to integer
            except Exception:
                # Whatever the player typed, it could not be interpreted as a number
                pass
    
        return result
    
    
    def show_fort_comment(which_fort) -> None:
        """Print the description for the fort"""
        print()
        if which_fort == FORT_MONTREAL:
            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.")
        elif which_fort == FORT_QUEBEC:
            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.")
        elif which_fort == FORT_NEWYORK:
            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:
            print(f"Internal error #1, fort {str(which_fort)} does not exist")
            sys.exit(1)  # you have a bug
        print()
    
    
    def get_yes_or_no() -> str:
        """Prompt the player to enter 'YES' or 'NO'. Keep prompting until
        valid input is entered.  Accept various spellings by only
        checking the first letter of input.
        Return a single letter 'Y' or 'N'"""
        result = ""
        while result not in ("Y", "N"):
            print("ANSWER YES OR NO")
            player_choice = input(">> ")
            player_choice = player_choice.strip().upper()  # trim spaces, make upper-case
            if player_choice.startswith("Y"):
                result = "Y"
            elif player_choice.startswith("N"):
                result = "N"
        return result
    
    
    def get_furs_purchase() -> List[int]:
        """Prompt the player for how many of each fur type they want.
        Accept numeric inputs, re-prompting on incorrect input values"""
        results: List[int] = []
    
        print(f"YOUR {str(MAX_FURS)} FURS ARE DISTRIBUTED AMONG THE FOLLOWING")
        print("KINDS OF PELTS: MINK, BEAVER, ERMINE AND FOX.")
        print()
    
        while len(results) < len(FUR_NAMES):
            print(f"HOW MANY {FUR_NAMES[len(results)]} DO YOU HAVE")
            count_str = input(">> ")
            try:
                count = int(count_str)
                results.append(count)
            except Exception:  # invalid input, prompt again by re-looping
                pass
        return results
    
    
    def main() -> None:
        print(" " * 31 + "FUR TRADER")
        print(" " * 15 + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY")
        print(" " * 15 + "(Ported to Python Oct 2012 krt@krt.com.au)")
        print("\n\n\n")
    
        fox_price = None  # sometimes this takes the "last" price (probably this was a bug)
    
        game_state = "starting"
        while True:
    
            if game_state == "starting":
                show_introduction()
    
                player_funds: float = 600  # Initial player start money
                player_furs = [0, 0, 0, 0]  # Player fur inventory
    
                print("DO YOU WISH TO TRADE FURS?")
                should_trade = get_yes_or_no()
                if should_trade == "N":
                    sys.exit(0)  # STOP
                game_state = "trading"
    
            elif game_state == "trading":
                print()
                print("YOU HAVE $ %1.2f IN SAVINGS" % (player_funds))
                print(f"AND {str(MAX_FURS)} FURS TO BEGIN THE EXPEDITION")
                player_furs = get_furs_purchase()
    
                if sum(player_furs) > MAX_FURS:
                    print()
                    print("YOU MAY NOT HAVE THAT MANY FURS.")
                    print("DO NOT TRY TO CHEAT.  I CAN ADD.")
                    print("YOU MUST START AGAIN.")
                    game_state = "starting"  # T/N: Wow, harsh.
                else:
                    game_state = "choosing fort"
    
            elif game_state == "choosing fort":
                which_fort = get_fort_choice()
                show_fort_comment(which_fort)
                print("DO YOU WANT TO TRADE AT ANOTHER FORT?")
                change_fort = get_yes_or_no()
                if change_fort == "N":
                    game_state = "travelling"
    
            elif game_state == "travelling":
                print()
                if which_fort == FORT_MONTREAL:
                    mink_price = (
                        int((0.2 * random.random() + 0.70) * 100 + 0.5) / 100
                    )  # INT((.2*RND(1)+.7)*10^2+.5)/10^2
                    ermine_price = (
                        int((0.2 * random.random() + 0.65) * 100 + 0.5) / 100
                    )  # INT((.2*RND(1)+.65)*10^2+.5)/10^2
                    beaver_price = (
                        int((0.2 * random.random() + 0.75) * 100 + 0.5) / 100
                    )  # INT((.2*RND(1)+.75)*10^2+.5)/10^2
                    fox_price = (
                        int((0.2 * random.random() + 0.80) * 100 + 0.5) / 100
                    )  # INT((.2*RND(1)+.8)*10^2+.5)/10^2
    
                    print("SUPPLIES AT FORT HOCHELAGA COST $150.00.")
                    print("YOUR TRAVEL EXPENSES TO HOCHELAGA WERE $10.00.")
                    player_funds -= 160
    
                elif which_fort == FORT_QUEBEC:
                    mink_price = (
                        int((0.30 * random.random() + 0.85) * 100 + 0.5) / 100
                    )  # INT((.3*RND(1)+.85)*10^2+.5)/10^2
                    ermine_price = (
                        int((0.15 * random.random() + 0.80) * 100 + 0.5) / 100
                    )  # INT((.15*RND(1)+.8)*10^2+.5)/10^2
                    beaver_price = (
                        int((0.20 * random.random() + 0.90) * 100 + 0.5) / 100
                    )  # INT((.2*RND(1)+.9)*10^2+.5)/10^2
                    fox_price = (
                        int((0.25 * random.random() + 1.10) * 100 + 0.5) / 100
                    )  # INT((.25*RND(1)+1.1)*10^2+.5)/10^2
                    event_picker = int(10 * random.random()) + 1
    
                    if event_picker <= 2:
                        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.")
                        player_furs[FUR_BEAVER] = 0
                    elif event_picker <= 6:
                        print("YOU ARRIVED SAFELY AT FORT STADACONA.")
                    elif event_picker <= 8:
                        print("YOUR CANOE UPSET IN THE LACHINE RAPIDS.  YOU")
                        print("LOST ALL YOUR FURS.")
                        player_furs = [0, 0, 0, 0]
                    elif event_picker <= 10:
                        print("YOUR FOX PELTS WERE NOT CURED PROPERLY.")
                        print("NO ONE WILL BUY THEM.")
                        player_furs[FUR_FOX] = 0
                    else:
                        print(f"Internal Error #3, Out-of-bounds event_picker{str(event_picker)}")
                        sys.exit(1)  # you have a bug
    
                    print()
                    print("SUPPLIES AT FORT STADACONA COST $125.00.")
                    print("YOUR TRAVEL EXPENSES TO STADACONA WERE $15.00.")
                    player_funds -= 140
    
                elif which_fort == FORT_NEWYORK:
                    mink_price = (
                        int((0.15 * random.random() + 1.05) * 100 + 0.5) / 100
                    )  # INT((.15*RND(1)+1.05)*10^2+.5)/10^2
                    ermine_price = (
                        int((0.15 * random.random() + 0.95) * 100 + 0.5) / 100
                    )  # INT((.15*RND(1)+.95)*10^2+.5)/10^2
                    beaver_price = (
                        int((0.25 * random.random() + 1.00) * 100 + 0.5) / 100
                    )  # INT((.25*RND(1)+1.00)*10^2+.5)/10^2
                    if fox_price is None:
                        # Original Bug?  There is no Fox price generated for New York, it will use any previous "D1" price
                        # So if there was no previous value, make one up
                        fox_price = (
                            int((0.25 * random.random() + 1.05) * 100 + 0.5) / 100
                        )  # not in orginal code
                    event_picker = int(10 * random.random()) + 1
    
                    if event_picker <= 2:
                        print("YOU WERE ATTACKED BY A PARTY OF IROQUOIS.")
                        print("ALL PEOPLE IN YOUR TRADING GROUP WERE")
                        print("KILLED.  THIS ENDS THE GAME.")
                        sys.exit(0)
                    elif event_picker <= 6:
                        print("YOU WERE LUCKY.  YOU ARRIVED SAFELY")
                        print("AT FORT NEW YORK.")
                    elif event_picker <= 8:
                        print("YOU NARROWLY ESCAPED AN IROQUOIS RAIDING PARTY.")
                        print("HOWEVER, YOU HAD TO LEAVE ALL YOUR FURS BEHIND.")
                        player_furs = [0, 0, 0, 0]
                    elif event_picker <= 10:
                        mink_price /= 2
                        fox_price /= 2
                        print("YOUR MINK AND BEAVER WERE DAMAGED ON YOUR TRIP.")
                        print("YOU RECEIVE ONLY HALF THE CURRENT PRICE FOR THESE FURS.")
                    else:
                        print(f"Internal Error #4, Out-of-bounds event_picker{str(event_picker)}")
                        sys.exit(1)  # you have a bug
    
                    print()
                    print("SUPPLIES AT NEW YORK COST $85.00.")
                    print("YOUR TRAVEL EXPENSES TO NEW YORK WERE $25.00.")
                    player_funds -= 105
    
                else:
                    print(f"Internal error #2, fort {str(which_fort)} does not exist")
                    sys.exit(1)  # you have a bug
    
                # Calculate sales
                beaver_value = beaver_price * player_furs[FUR_BEAVER]
                fox_value = fox_price * player_furs[FUR_FOX]
                ermine_value = ermine_price * player_furs[FUR_ERMINE]
                mink_value = mink_price * player_furs[FUR_MINK]
    
                print()
                print("YOUR BEAVER SOLD FOR $%6.2f" % (beaver_value))
                print("YOUR FOX SOLD FOR    $%6.2f" % (fox_value))
                print("YOUR ERMINE SOLD FOR $%6.2f" % (ermine_value))
                print("YOUR MINK SOLD FOR   $%6.2f" % (mink_value))
    
                player_funds += beaver_value + fox_value + ermine_value + mink_value
    
                print()
                print(
                    "YOU NOW HAVE $ %1.2f INCLUDING YOUR PREVIOUS SAVINGS" % (player_funds)
                )
    
                print()
                print("DO YOU WANT TO TRADE FURS NEXT YEAR?")
                should_trade = get_yes_or_no()
                if should_trade == "N":
                    sys.exit(0)  # STOP
                else:
                    game_state = "trading"
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 38_Fur_Trader/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 38_Fur_Trader/vbnet/FurTrader.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "FurTrader", "FurTrader.vbproj", "{D8310FB8-132A-4D43-A654-17E5A267DDBD}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{D8310FB8-132A-4D43-A654-17E5A267DDBD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{D8310FB8-132A-4D43-A654-17E5A267DDBD}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{D8310FB8-132A-4D43-A654-17E5A267DDBD}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{D8310FB8-132A-4D43-A654-17E5A267DDBD}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 38_Fur_Trader/vbnet/FurTrader.vbproj
    ================================================
    
      
        Exe
        FurTrader
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 38_Fur_Trader/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 39_Golf/README.md
    ================================================
    ### Golf
    
    This is a single player golf game. In other words it’s you against the golf course (the computer). The program asks for your handicap (maximum of 30) and your area of difficulty. You have a bag of 29 clubs plus a putter. On the course you have to contend with rough, trees, on and off fairway, sand traps, and water hazards. In addition, you can hook, slice, go out of bounds, or hit too far. On putting, you determine the potency factor (or percent of swing). Until you get the swing of the game (no pun intended), you’ll probably was to use a fairly high handicap.
    
    Steve North of Creative Computing modified the original version of this game, the author of which is unknown.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=71)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=86)
    
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### 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)
    
    
    ================================================
    FILE: 39_Golf/csharp/Golf.csproj
    ================================================
    
      
        Exe
        net6.0
        10
        enable
        enable
      
    
    
    
    ================================================
    FILE: 39_Golf/csharp/Golf.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Golf", "Golf.csproj", "{0D91BC87-BADF-445F-876F-38F3A66A3B83}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{0D91BC87-BADF-445F-876F-38F3A66A3B83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{0D91BC87-BADF-445F-876F-38F3A66A3B83}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{0D91BC87-BADF-445F-876F-38F3A66A3B83}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{0D91BC87-BADF-445F-876F-38F3A66A3B83}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 39_Golf/csharp/Program.cs
    ================================================
    //
    //          8""""8 8"""88 8     8""""
    //          8    " 8    8 8     8
    //          8e     8    8 8e    8eeee
    //          88  ee 8    8 88    88
    //          88   8 8    8 88    88
    //          88eee8 8eeee8 88eee 88
    //
    // GOLF
    //
    // C#
    // .NET Core
    // TargetFramework: netcoreapp 3.1
    //
    // Run source:
    // dotnet run
    //
    // Linux compile:
    // dotnet publish --self-contained -c Release -r linux-x64 /p:PublishSingleFile=true /p:PublishTrimmed=true
    //
    // Windows compile:
    // dotnet publish -r win-x64 -c Release /p:PublishSingleFile=true
    //
    //
    // INDEX
    // ----------------- methods
    // constructor
    // NewHole
    // TeeUp
    // Stroke
    // PlotBall
    // InterpretResults
    // ReportCurrentScore
    // FindBall
    // IsOnFairway
    // IsOnGreen
    // IsInHazard
    // IsInRough
    // IsOutOfBounds
    // ScoreCardNewHole
    // ScoreCardRecordStroke
    // ScoreCardGetPreviousStroke
    // ScoreCardGetTotal
    // Ask
    // Wait
    // ReviewBag
    // Quit
    // GameOver
    // ----------------- DATA
    // Clubs
    // CourseInfo
    // ----------------- classes
    // HoleInfo
    // CircleGameObj
    // RectGameObj
    // HoleGeometry
    // Plot
    // ----------------- helper methods
    // GetDistance
    // IsInRectangle
    // ToRadians
    // ToDegrees360
    // Odds
    //
    //  Despite being a text based game, the code uses simple geometry to simulate a course.
    //  Fairways are 40 yard wide rectangles, surrounded by 5 yards of rough around the perimeter.
    //  The green is a circle of 10 yards radius around the cup.
    //  The cup is always at point (0,0).
    //
    //  Using basic trigonometry we can plot the ball's location using the distance of the stroke and
    //  and the angle of deviation (hook/slice).
    //
    //  The stroke distances are based on real world averages of different club types.
    //  Lots of randomization, "business rules", and luck influence the game play.
    //  Probabilities are commented in the code.
    //
    //  note: 'courseInfo', 'clubs', & 'scoreCard' arrays each include an empty object so indexing
    //  can begin at 1. Like all good programmers we count from zero, but in this context,
    //  it's more natural when hole number one is at index one
    //
    //
    //     |-----------------------------|
    //     |            rough            |
    //     |   ----------------------    |
    //     |   |                     |   |
    //     | r |        =  =         | r |
    //     | o |     =        =      | o |
    //     | u |    =    .     =     | u |
    //     | g |    =   green  =     | g |
    //     | h |     =        =      | h |
    //     |   |        =  =         |   |
    //     |   |                     |   |
    //     |   |                     |   |
    //     |   |      Fairway        |   |
    //     |   |                     |   |
    //     |   |               ------    |
    //     |   |            --        -- |
    //     |   |           --  hazard  --|
    //     |   |            --        -- |
    //     |   |               ------    |
    //     |   |                     |   |
    //     |   |                     |   |   out
    //     |   |                     |   |   of
    //     |   |                     |   |   bounds
    //     |   |                     |   |
    //     |   |                     |   |
    //     |            tee              |
    //
    //
    //  Typical green size: 20-30 yards
    //  Typical golf course fairways are 35 to 45 yards wide
    //  Our fairway extends 5 yards past green
    //  Our rough is a 5 yard perimeter around fairway
    //
    //  We calculate the new position of the ball given the ball's point, the distance
    //  of the stroke, and degrees off line (hook or slice).
    //
    //  Degrees off (for a right handed golfer):
    //  Slice: positive degrees = ball goes right
    //  Hook: negative degrees = left goes left
    //
    //  The cup is always at point: 0,0.
    //  We use atan2 to compute the angle between the cup and the ball.
    //  Setting the cup's vector to 0,-1 on a 360 circle is equivalent to:
    //  0 deg = 12 o'clock;  90 deg = 3 o'clock;  180 deg = 6 o'clock;  270 = 9 o'clock
    //  The reverse angle between the cup and the ball is a difference of PI (using radians).
    //
    //  Given the angle and stroke distance (hypotenuse), we use cosine to compute
    //  the opposite and adjacent sides of the triangle, which, is the ball's new position.
    //
    //           0
    //           |
    //    270 - cup - 90
    //           |
    //          180
    //
    //
    //          cup
    //           |
    //           |
    //           | opp
    //           |-----* new position
    //           |    /
    //           |   /
    //      adj  |  /
    //           | /  hyp
    //           |/
    //          tee
    //
    //    <- hook    slice ->
    //
    //
    //  Given the large number of combinations needed to describe a particular stroke / ball location,
    //  we use the technique of "bitwise masking" to describe stroke results.
    //  With bit masking, multiple flags (bits) are combined into a single binary number that can be
    //  tested by applying a mask. A mask is another binary number that isolates a particular bit that
    //  you are interested in. You can then apply your language's bitwise opeartors to test or
    //  set a flag.
    //
    //  Game design by Jason Bonthron, 2021
    //  www.bonthron.com
    //  for my father, Raymond Bonthron, an avid golfer
    //
    //  Inspired by the 1978 "Golf" from "Basic Computer Games"
    //  by Steve North, who modified an existing golf game by an unknown author
    //
    //
    
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Threading;
    
    
    namespace Golf
    {
        using Ball = Golf.CircleGameObj;
        using Hazard = Golf.CircleGameObj;
    
        // --------------------------------------------------------------------------- Program
        class Program
        {
            static void Main(string[] args)
            {
                Golf g = new Golf();
            }
        }
    
    
        // --------------------------------------------------------------------------- Golf
        public class Golf
        {
            Ball BALL;
            int HOLE_NUM = 0;
            int STROKE_NUM = 0;
            int Handicap = 0;
            int PlayerDifficulty = 0;
            HoleGeometry holeGeometry;
    
            // all fairways are 40 yards wide, extend 5 yards beyond the cup, and
            // have 5 yards of rough around the perimeter
            const int FairwayWidth = 40;
            const int FairwayExtension = 5;
            const int RoughAmt = 5;
    
            // ScoreCard records the ball position after each stroke
            // a new list for each hole
            // include a blank list so index 1 == hole 1
    
            List> ScoreCard = new List> { new List() };
    
            static void w(string s) { Console.WriteLine(s); } // WRITE
            Random RANDOM = new Random();
    
    
            // --------------------------------------------------------------- constructor
            public Golf()
            {
                Console.Clear();
                w(" ");
                w("          8\"\"\"\"8 8\"\"\"88 8     8\"\"\"\" ");
                w("          8    \" 8    8 8     8     ");
                w("          8e     8    8 8e    8eeee ");
                w("          88  ee 8    8 88    88    ");
                w("          88   8 8    8 88    88    ");
                w("          88eee8 8eeee8 88eee 88    ");
                w(" ");
                w("Welcome to the Creative Computing Country Club,");
                w("an eighteen hole championship layout located a short");
                w("distance from scenic downtown Lambertville, New Jersey.");
                w("The game will be explained as you play.");
                w("Enjoy your game! See you at the 19th hole...");
                w(" ");
                w("Type QUIT at any time to leave the game.");
                w("Type BAG at any time to review the clubs in your bag.");
                w(" ");
    
                Wait((z) =>
                {
                    w(" ");
                    w("              YOUR BAG");
                    ReviewBag();
                    w("Type BAG at any time to review the clubs in your bag.");
                    w(" ");
    
                    Wait((zz) =>
                    {
                        w(" ");
    
                        Ask("PGA handicaps range from 0 to 30.\nWhat is your handicap?", 0, 30, (i) =>
                        {
                            Handicap = i;
                            w(" ");
    
                            Ask("Common difficulties at golf include:\n1=Hook, 2=Slice, 3=Poor Distance, 4=Trap Shots, 5=Putting\nWhich one is your worst?", 1, 5, (j) =>
                            {
                                PlayerDifficulty = j;
                                Console.Clear();
                                NewHole();
                            });
                        });
                    });
                });
            }
    
    
            // --------------------------------------------------------------- NewHole
            void NewHole()
            {
                HOLE_NUM++;
                STROKE_NUM = 0;
    
                HoleInfo info = CourseInfo[HOLE_NUM];
    
                int yards = info.Yards;  // from tee to cup
                int par = info.Par;
                var cup = new CircleGameObj(0, 0, 0, GameObjType.CUP);
                var green = new CircleGameObj(0, 0, 10, GameObjType.GREEN);
    
                var fairway = new RectGameObj(0 - (FairwayWidth / 2),
                                              0 - (green.Radius + FairwayExtension),
                                              FairwayWidth,
                                              yards + (green.Radius + FairwayExtension) + 1,
                                              GameObjType.FAIRWAY);
    
                var rough = new RectGameObj(fairway.X - RoughAmt,
                                            fairway.Y - RoughAmt,
                                            fairway.Width + (2 * RoughAmt),
                                            fairway.Length + (2 * RoughAmt),
                                            GameObjType.ROUGH);
    
                BALL = new Ball(0, yards, 0, GameObjType.BALL);
    
                ScoreCardStartNewHole();
    
                holeGeometry = new HoleGeometry(cup, green, fairway, rough, info.Hazard);
    
                w("                |> " + HOLE_NUM);
                w("                |        ");
                w("                |        ");
                w("          ^^^^^^^^^^^^^^^");
    
                Console.WriteLine("Hole #{0}. You are at the tee. Distance {1} yards, par {2}.", HOLE_NUM, info.Yards, info.Par);
                w(info.Description);
    
                TeeUp();
            }
    
    
            // --------------------------------------------------------------- TeeUp
            // on the green? automatically select putter
            // otherwise Ask club and swing strength
    
            void TeeUp()
            {
                if (IsOnGreen(BALL) && !IsInHazard(BALL, GameObjType.SAND))
                {
                    var putt = 10;
                    w("[PUTTER: average 10 yards]");
                    var msg = Odds(20) ? "Keep your head down.\n" : "";
    
                    Ask(msg + "Choose your putt potency. (1-10)", 1, 10, (strength) =>
                    {
                        var putter = Clubs[putt];
                        Stroke(Convert.ToDouble((double)putter.Item2 * ((double)strength / 10.0)), putt);
                    });
                }
                else
                {
                    Ask("What club do you choose? (1-10)", 1, 10, (c) =>
                    {
                        var club = Clubs[c];
    
                        w(" ");
                        Console.WriteLine("[{0}: average {1} yards]", club.Item1.ToUpper(), club.Item2);
    
                        Ask("Now gauge your distance by a percentage of a full swing. (1-10)", 1, 10, (strength) =>
                        {
                            Stroke(Convert.ToDouble((double)club.Item2 * ((double)strength / 10.0)), c);
                        });
                    });
                };
            }
    
    
            // -------------------------------------------------------- bitwise Flags
            int dub         = 0b00000000000001;
            int hook        = 0b00000000000010;
            int slice       = 0b00000000000100;
            int passedCup   = 0b00000000001000;
            int inCup       = 0b00000000010000;
            int onFairway   = 0b00000000100000;
            int onGreen     = 0b00000001000000;
            int inRough     = 0b00000010000000;
            int inSand      = 0b00000100000000;
            int inTrees     = 0b00001000000000;
            int inWater     = 0b00010000000000;
            int outOfBounds = 0b00100000000000;
            int luck        = 0b01000000000000;
            int ace         = 0b10000000000000;
    
    
            // --------------------------------------------------------------- Stroke
            void Stroke(double clubAmt, int clubIndex)
            {
                STROKE_NUM++;
    
                var flags = 0b000000000000;
    
                // fore! only when driving
                if ((STROKE_NUM == 1) && (clubAmt > 210) && Odds(30)) { w("\"...Fore !\""); };
    
                // dub
                if (Odds(5)) { flags |= dub; }; // there's always a 5% chance of dubbing it
    
                // if you're in the rough, or sand, you really should be using a wedge
                if ((IsInRough(BALL) || IsInHazard(BALL, GameObjType.SAND)) &&
                    !(clubIndex == 8 || clubIndex == 9))
                {
                    if (Odds(40)) { flags |= dub; };
                };
    
                // trap difficulty
                if (IsInHazard(BALL, GameObjType.SAND) && PlayerDifficulty == 4)
                {
                    if (Odds(20)) { flags |= dub; };
                }
    
                // hook/slice
                // There's 10% chance of a hook or slice
                // if it's a known playerDifficulty then increase chance to 30%
                // if it's a putt & putting is a playerDifficulty increase to 30%
    
                bool randHookSlice = (PlayerDifficulty == 1 ||
                                      PlayerDifficulty == 2 ||
                                      (PlayerDifficulty == 5 && IsOnGreen(BALL))) ? Odds(30) : Odds(10);
    
                if (randHookSlice)
                {
                    if (PlayerDifficulty == 1)
                    {
                        if (Odds(80)) { flags |= hook; } else { flags |= slice; };
                    }
                    else if (PlayerDifficulty == 2)
                    {
                        if (Odds(80)) { flags |= slice; } else { flags |= hook; };
                    }
                    else
                    {
                        if (Odds(50)) { flags |= hook; } else { flags |= slice; };
                    };
                };
    
                // beginner's luck !
                // If handicap is greater than 15, there's a 10% chance of avoiding all errors
                if ((Handicap > 15) && (Odds(10))) { flags |= luck; };
    
                // ace
                // there's a 10% chance of an Ace on a par 3
                if (CourseInfo[HOLE_NUM].Par == 3 && Odds(10) && STROKE_NUM == 1) { flags |= ace; };
    
                // distance:
                // If handicap is < 15, there a 50% chance of reaching club average,
                // a 25% of exceeding it, and a 25% of falling short
                // If handicap is > 15, there's a 25% chance of reaching club average,
                // and 75% chance of falling short
                // The greater the handicap, the more the ball falls short
                // If poor distance is a known playerDifficulty, then reduce distance by 10%
    
                double distance;
                int rnd = RANDOM.Next(1, 101);
    
                if (Handicap < 15)
                {
                    if (rnd <= 25)
                    {
                        distance = clubAmt - (clubAmt * ((double)Handicap / 100.0));
                    }
                    else if (rnd > 25 && rnd <= 75)
                    {
                        distance = clubAmt;
                    }
                    else
                    {
                        distance = clubAmt + (clubAmt * 0.10);
                    };
                }
                else
                {
                    if (rnd <= 75)
                    {
                        distance = clubAmt - (clubAmt * ((double)Handicap / 100.0));
                    }
                    else
                    {
                        distance = clubAmt;
                    };
                };
    
                if (PlayerDifficulty == 3)  // poor distance
                {
                    if (Odds(80)) { distance = (distance * 0.80); };
                };
    
                if ((flags & luck) == luck) { distance = clubAmt; }
    
                // angle
                // For all strokes, there's a possible "drift" of 4 degrees
                // a hooks or slice increases the angle between 5-10 degrees, hook uses negative degrees
                int angle = RANDOM.Next(0, 5);
                if ((flags & slice) == slice) { angle = RANDOM.Next(5, 11); };
                if ((flags & hook) == hook) { angle = 0 - RANDOM.Next(5, 11); };
                if ((flags & luck) == luck) { angle = 0; };
    
                var plot = PlotBall(BALL, distance, Convert.ToDouble(angle));  // calculate a new location
                if ((flags & luck) == luck) { if(plot.Y > 0){ plot.Y = 2; }; };
    
                flags = FindBall(new Ball(plot.X, plot.Y, plot.Offline, GameObjType.BALL), flags);
    
                InterpretResults(plot, flags);
            }
    
    
            // --------------------------------------------------------------- plotBall
            Plot PlotBall(Ball ball, double strokeDistance, double degreesOff)
            {
                var cupVector = new Point(0, -1);
                double radFromCup = Math.Atan2((double)ball.Y, (double)ball.X) - Math.Atan2((double)cupVector.Y, (double)cupVector.X);
                double radFromBall = radFromCup - Math.PI;
    
                var hypotenuse = strokeDistance;
                var adjacent = Math.Cos(radFromBall + ToRadians(degreesOff)) * hypotenuse;
                var opposite = Math.Sqrt(Math.Pow(hypotenuse, 2) - Math.Pow(adjacent, 2));
    
                Point newPos;
                if (ToDegrees360(radFromBall + ToRadians(degreesOff)) > 180)
                {
                    newPos = new Point(Convert.ToInt32(ball.X - opposite),
                                       Convert.ToInt32(ball.Y - adjacent));
                }
                else
                {
                    newPos = new Point(Convert.ToInt32(ball.X + opposite),
                                       Convert.ToInt32(ball.Y - adjacent));
                }
    
                return new Plot(newPos.X, newPos.Y, Convert.ToInt32(opposite));
            }
    
    
            // --------------------------------------------------------------- InterpretResults
            void InterpretResults(Plot plot, int flags)
            {
                int cupDistance = Convert.ToInt32(GetDistance(new Point(plot.X, plot.Y),
                                                              new Point(holeGeometry.Cup.X, holeGeometry.Cup.Y)));
                int travelDistance = Convert.ToInt32(GetDistance(new Point(plot.X, plot.Y),
                                                                 new Point(BALL.X, BALL.Y)));
    
                w(" ");
    
                if ((flags & ace) == ace)
                {
                    w("Hole in One! You aced it.");
                    ScoreCardRecordStroke(new Ball(0, 0, 0, GameObjType.BALL));
                    ReportCurrentScore();
                    return;
                };
    
                if ((flags & inTrees) == inTrees)
                {
                    w("Your ball is lost in the trees. Take a penalty stroke.");
                    ScoreCardRecordStroke(BALL);
                    TeeUp();
                    return;
                };
    
                if ((flags & inWater) == inWater)
                {
                    var msg = Odds(50) ? "Your ball has gone to a watery grave." : "Your ball is lost in the water.";
                    w(msg + " Take a penalty stroke.");
                    ScoreCardRecordStroke(BALL);
                    TeeUp();
                    return;
                };
    
                if ((flags & outOfBounds) == outOfBounds)
                {
                    w("Out of bounds. Take a penalty stroke.");
                    ScoreCardRecordStroke(BALL);
                    TeeUp();
                    return;
                };
    
                if ((flags & dub) == dub)
                {
                    w("You dubbed it.");
                    ScoreCardRecordStroke(BALL);
                    TeeUp();
                    return;
                };
    
                if ((flags & inCup) == inCup)
                {
                    var msg = Odds(50) ? "You holed it." : "It's in!";
                    w(msg);
                    ScoreCardRecordStroke(new Ball(plot.X, plot.Y, 0, GameObjType.BALL));
                    ReportCurrentScore();
                    return;
                };
    
                if (((flags & slice) == slice) &&
                    !((flags & onGreen) == onGreen))
                {
                    var bad = ((flags & outOfBounds) == outOfBounds) ? " badly" : "";
                    Console.WriteLine("You sliced{0}: {1} yards offline.", bad, plot.Offline);
                };
    
                if (((flags & hook) == hook) &&
                    !((flags & onGreen) == onGreen))
                {
                    var bad = ((flags & outOfBounds) == outOfBounds) ? " badly" : "";
                    Console.WriteLine("You hooked{0}: {1} yards offline.", bad, plot.Offline);
                };
    
                if (STROKE_NUM > 1)
                {
                    var prevBall = ScoreCardGetPreviousStroke();
                    var d1 = GetDistance(new Point(prevBall.X, prevBall.Y),
                                         new Point(holeGeometry.Cup.X, holeGeometry.Cup.Y));
                    var d2 = cupDistance;
                    if (d2 > d1) { w("Too much club."); };
                };
    
                if ((flags & inRough) == inRough) { w("You're in the rough."); };
    
                if ((flags & inSand) == inSand) { w("You're in a sand trap."); };
    
                if ((flags & onGreen) == onGreen)
                {
                    var pd = (cupDistance < 4) ? ((cupDistance * 3) + " feet") : (cupDistance + " yards");
                    Console.WriteLine("You're on the green. It's {0} from the pin.", pd);
                };
    
                if (((flags & onFairway) == onFairway) ||
                    ((flags & inRough) == inRough))
                {
                    Console.WriteLine("Shot went {0} yards. It's {1} yards from the cup.", travelDistance, cupDistance);
                };
    
                ScoreCardRecordStroke(new Ball(plot.X, plot.Y, 0, GameObjType.BALL));
    
                BALL = new Ball(plot.X, plot.Y, 0, GameObjType.BALL);
    
                TeeUp();
            }
    
    
            // --------------------------------------------------------------- ReportCurrentScore
            void ReportCurrentScore()
            {
                var par = CourseInfo[HOLE_NUM].Par;
                if (ScoreCard[HOLE_NUM].Count == par + 1) { w("A bogey. One above par."); };
                if (ScoreCard[HOLE_NUM].Count == par) { w("Par. Nice."); };
                if (ScoreCard[HOLE_NUM].Count == (par - 1)) { w("A birdie! One below par."); };
                if (ScoreCard[HOLE_NUM].Count == (par - 2)) { w("An Eagle! Two below par."); };
                if (ScoreCard[HOLE_NUM].Count == (par - 3)) { w("Double Eagle! Unbelievable."); };
    
                int totalPar = 0;
                for (var i = 1; i <= HOLE_NUM; i++) { totalPar += CourseInfo[i].Par; };
    
                w(" ");
                w("-----------------------------------------------------");
                Console.WriteLine(" Total par for {0} hole{1} is: {2}. Your total is: {3}.",
                                  HOLE_NUM,
                                  ((HOLE_NUM > 1) ? "s" : ""), //plural
                                  totalPar,
                                  ScoreCardGetTotal());
                w("-----------------------------------------------------");
                w(" ");
    
                if (HOLE_NUM == 18)
                {
                    GameOver();
                }
                else
                {
                    Thread.Sleep(2000);
                    NewHole();
                };
            }
    
    
            // --------------------------------------------------------------- FindBall
            int FindBall(Ball ball, int flags)
            {
                if (IsOnFairway(ball) && !IsOnGreen(ball)) { flags |= onFairway; }
                if (IsOnGreen(ball)) { flags |= onGreen; }
                if (IsInRough(ball)) { flags |= inRough; }
                if (IsOutOfBounds(ball)) { flags |= outOfBounds; }
                if (IsInHazard(ball, GameObjType.WATER)) { flags |= inWater; }
                if (IsInHazard(ball, GameObjType.TREES)) { flags |= inTrees; }
                if (IsInHazard(ball, GameObjType.SAND))  { flags |= inSand;  }
    
                if (ball.Y < 0) { flags |= passedCup; }
    
                // less than 2, it's in the cup
                var d = GetDistance(new Point(ball.X, ball.Y),
                                    new Point(holeGeometry.Cup.X, holeGeometry.Cup.Y));
                if (d < 2) { flags |= inCup; };
    
                return flags;
            }
    
    
            // --------------------------------------------------------------- IsOnFairway
            bool IsOnFairway(Ball ball)
            {
                return IsInRectangle(ball, holeGeometry.Fairway);
            }
    
    
            // --------------------------------------------------------------- IsOngreen
            bool IsOnGreen(Ball ball)
            {
                var d = GetDistance(new Point(ball.X, ball.Y),
                                    new Point(holeGeometry.Cup.X, holeGeometry.Cup.Y));
                return d < holeGeometry.Green.Radius;
            }
    
    
            // --------------------------------------------------------------- IsInHazard
            bool IsInHazard(Ball ball, GameObjType hazard)
            {
                bool result = false;
                Array.ForEach(holeGeometry.Hazards, (Hazard h) =>
                {
                    var d = GetDistance(new Point(ball.X, ball.Y), new Point(h.X, h.Y));
                    if ((d < h.Radius) && h.Type == hazard) { result = true; };
                });
                return result;
            }
    
    
            // --------------------------------------------------------------- IsInRough
            bool IsInRough(Ball ball)
            {
                return IsInRectangle(ball, holeGeometry.Rough) &&
                    (IsInRectangle(ball, holeGeometry.Fairway) == false);
            }
    
    
            // --------------------------------------------------------------- IsOutOfBounds
            bool IsOutOfBounds(Ball ball)
            {
                return (IsOnFairway(ball) == false) && (IsInRough(ball) == false);
            }
    
    
            // --------------------------------------------------------------- ScoreCardNewHole
            void ScoreCardStartNewHole()
            {
                ScoreCard.Add(new List());
            }
    
    
            // --------------------------------------------------------------- ScoreCardRecordStroke
            void ScoreCardRecordStroke(Ball ball)
            {
                var clone = new Ball(ball.X, ball.Y, 0, GameObjType.BALL);
                ScoreCard[HOLE_NUM].Add(clone);
            }
    
    
            // ------------------------------------------------------------ ScoreCardGetPreviousStroke
            Ball ScoreCardGetPreviousStroke()
            {
                return ScoreCard[HOLE_NUM][ScoreCard[HOLE_NUM].Count - 1];
            }
    
    
            // --------------------------------------------------------------- ScoreCardGetTotal
            int ScoreCardGetTotal()
            {
                int total = 0;
                ScoreCard.ForEach((h) => { total += h.Count; });
                return total;
            }
    
    
            // --------------------------------------------------------------- Ask
            // input from console is always an integer passed to a callback
            // or "quit" to end game
    
            void Ask(string question, int min, int max, Action callback)
            {
                w(question);
                string i = Console.ReadLine().Trim().ToLower();
                if (i == "quit") { Quit(); return; };
                if (i == "bag") { ReviewBag(); };
    
                int n;
                bool success = Int32.TryParse(i, out n);
    
                if (success)
                {
                    if (n >= min && n <= max)
                    {
                        callback(n);
                    }
                    else
                    {
                        Ask(question, min, max, callback);
                    }
                }
                else
                {
                    Ask(question, min, max, callback);
                };
            }
    
    
            // --------------------------------------------------------------- Wait
            void Wait(Action callback)
            {
                w("Press any key to continue.");
    
                ConsoleKeyInfo keyinfo;
                do { keyinfo = Console.ReadKey(true); }
                while (keyinfo.KeyChar < 0);
                Console.Clear();
                callback(0);
            }
    
    
            // --------------------------------------------------------------- ReviewBag
            void ReviewBag()
            {
                w(" ");
                w("  #     Club      Average Yardage");
                w("-----------------------------------");
                w("  1    Driver           250");
                w("  2    3 Wood           225");
                w("  3    5 Wood           200");
                w("  4    Hybrid           190");
                w("  5    4 Iron           170");
                w("  6    7 Iron           150");
                w("  7    9 Iron           125");
                w("  8    Pitching wedge   110");
                w("  9    Sand wedge        75");
                w(" 10    Putter            10");
                w(" ");
            }
    
    
            // --------------------------------------------------------------- Quit
            void Quit()
            {
                w("");
                w("Looks like rain. Goodbye!");
                w("");
                Wait((z) => { });
                return;
            }
    
    
            // --------------------------------------------------------------- GameOver
            void GameOver()
            {
                var net = ScoreCardGetTotal() - Handicap;
                w("Good game!");
                w("Your net score is: " + net);
                w("Let's visit the pro shop...");
                w(" ");
                Wait((z) => { });
                return;
            }
    
    
            // YOUR BAG
            // ======================================================== Clubs
            (string, int)[] Clubs = new (string, int)[] {
                ("",0),
    
                    // name, average yardage
                    ("Driver", 250),
                    ("3 Wood", 225),
                    ("5 Wood", 200),
                    ("Hybrid", 190),
                    ("4 Iron", 170),
                    ("7 Iron", 150),
                    ("9 Iron", 125),
                    ("Pitching wedge", 110),
                    ("Sand wedge", 75),
                    ("Putter", 10)
                    };
    
    
            // THE COURSE
            // ======================================================== CourseInfo
    
            HoleInfo[] CourseInfo = new HoleInfo[]{
                new HoleInfo(0, 0, 0, new Hazard[]{}, ""), // include a blank so index 1 == hole 1
    
    
                // -------------------------------------------------------- front 9
                // hole, yards, par, hazards, (description)
    
                new HoleInfo(1, 361, 4,
                             new Hazard[]{
                                 new Hazard( 20, 100, 10, GameObjType.TREES),
                                 new Hazard(-20,  80, 10, GameObjType.TREES),
                                 new Hazard(-20, 100, 10, GameObjType.TREES)
                             },
                             "There are a couple of trees on the left and right."),
    
                new HoleInfo(2, 389, 4,
                             new Hazard[]{
                                 new Hazard(0, 160, 20, GameObjType.WATER)
                             },
                             "There is a large water hazard across the fairway about 150 yards."),
    
                new HoleInfo(3, 206, 3,
                             new Hazard[]{
                                 new Hazard( 20,  20,  5, GameObjType.WATER),
                                 new Hazard(-20, 160, 10, GameObjType.WATER),
                                 new Hazard( 10,  12,  5, GameObjType.SAND)
                             },
                             "There is some sand and water near the green."),
    
                new HoleInfo(4, 500, 5,
                             new Hazard[]{
                                 new Hazard(-14, 12, 12, GameObjType.SAND)
                             },
                             "There's a bunker to the left of the green."),
    
                new HoleInfo(5, 408, 4,
                             new Hazard[]{
                                 new Hazard(20, 120, 20, GameObjType.TREES),
                                 new Hazard(20, 160, 20, GameObjType.TREES),
                                 new Hazard(10,  20,  5, GameObjType.SAND)
                             },
                             "There are some trees to your right."),
    
                new HoleInfo(6, 359, 4,
                             new Hazard[]{
                                 new Hazard( 14, 0, 4, GameObjType.SAND),
                                 new Hazard(-14, 0, 4, GameObjType.SAND)
                             },
                             ""),
    
                new HoleInfo(7, 424, 5,
                             new Hazard[]{
                                 new Hazard(20, 200, 10, GameObjType.SAND),
                                 new Hazard(10, 180, 10, GameObjType.SAND),
                                 new Hazard(20, 160, 10, GameObjType.SAND)
                             },
                             "There are several sand traps along your right."),
    
                new HoleInfo(8, 388, 4,
                             new Hazard[]{
                                 new Hazard(-20, 340, 10, GameObjType.TREES)
                             },
                             ""),
    
                new HoleInfo(9, 196, 3,
                             new Hazard[]{
                                 new Hazard(-30, 180, 20, GameObjType.TREES),
                                 new Hazard( 14,  -8,  5, GameObjType.SAND)
                             },
                             ""),
    
                // -------------------------------------------------------- back 9
                // hole, yards, par, hazards, (description)
    
                new HoleInfo(10, 400, 4,
                             new Hazard[]{
                                 new Hazard(-14, -8, 5, GameObjType.SAND),
                                 new Hazard( 14, -8, 5, GameObjType.SAND)
                             },
                             ""),
    
                new HoleInfo(11, 560, 5,
                             new Hazard[]{
                                 new Hazard(-20, 400, 10, GameObjType.TREES),
                                 new Hazard(-10, 380, 10, GameObjType.TREES),
                                 new Hazard(-20, 260, 10, GameObjType.TREES),
                                 new Hazard(-20, 200, 10, GameObjType.TREES),
                                 new Hazard(-10, 180, 10, GameObjType.TREES),
                                 new Hazard(-20, 160, 10, GameObjType.TREES)
                             },
                             "Lots of trees along the left of the fairway."),
    
                new HoleInfo(12, 132, 3,
                             new Hazard[]{
                                 new Hazard(-10, 120, 10, GameObjType.WATER),
                                 new Hazard( -5, 100, 10, GameObjType.SAND)
                             },
                             "There is water and sand directly in front of you. A good drive should clear both."),
    
                new HoleInfo(13, 357, 4,
                             new Hazard[]{
                                 new Hazard(-20, 200, 10, GameObjType.TREES),
                                 new Hazard(-10, 180, 10, GameObjType.TREES),
                                 new Hazard(-20, 160, 10, GameObjType.TREES),
                                 new Hazard( 14,  12,  8, GameObjType.SAND)
                             },
                             ""),
    
                new HoleInfo(14, 294, 4,
                             new Hazard[]{
                                 new Hazard(0, 20, 10, GameObjType.SAND)
                             },
                             ""),
    
                new HoleInfo(15, 475, 5,
                             new Hazard[]{
                                 new Hazard(-20, 20, 10, GameObjType.WATER),
                                 new Hazard( 10, 20, 10, GameObjType.SAND)
                             },
                             "Some sand and water near the green."),
    
                new HoleInfo(16, 375, 4,
                             new Hazard[]{
                                 new Hazard(-14, -8, 5, GameObjType.SAND)
                             },
                             ""),
    
                new HoleInfo(17, 180, 3,
                             new Hazard[]{
                                 new Hazard( 20, 100, 10, GameObjType.TREES),
                                 new Hazard(-20,  80, 10, GameObjType.TREES)
                             },
                             ""),
    
                new HoleInfo(18, 550, 5,
                             new Hazard[]{
                                 new Hazard(20, 30, 15, GameObjType.WATER)
                             },
                             "There is a water hazard near the green.")
            };
    
    
            // -------------------------------------------------------- HoleInfo
            class HoleInfo
            {
                public int Hole { get; }
                public int Yards { get; }
                public int Par { get; }
                public Hazard[] Hazard { get; }
                public string Description { get; }
    
                public HoleInfo(int hole, int yards, int par, Hazard[] hazard, string description)
                {
                    Hole = hole;
                    Yards = yards;
                    Par = par;
                    Hazard = hazard;
                    Description = description;
                }
            }
    
    
            public enum GameObjType { BALL, CUP, GREEN, FAIRWAY, ROUGH, TREES, WATER, SAND }
    
    
            // -------------------------------------------------------- CircleGameObj
            public class CircleGameObj
            {
                public GameObjType Type { get; }
                public int X { get; }
                public int Y { get; }
                public int Radius { get; }
    
                public CircleGameObj(int x, int y, int r, GameObjType type)
                {
                    Type = type;
                    X = x;
                    Y = y;
                    Radius = r;
                }
            }
    
    
            // -------------------------------------------------------- RectGameObj
            public class RectGameObj
            {
                public GameObjType Type { get; }
                public int X { get; }
                public int Y { get; }
                public int Width { get; }
                public int Length { get; }
    
                public RectGameObj(int x, int y, int w, int l, GameObjType type)
                {
                    Type = type;
                    X = x;
                    Y = y;
                    Width = w;
                    Length = l;
                }
            }
    
    
            // -------------------------------------------------------- HoleGeometry
            public class HoleGeometry
            {
                public CircleGameObj Cup { get; }
                public CircleGameObj Green { get; }
                public RectGameObj Fairway { get; }
                public RectGameObj Rough { get; }
                public Hazard[] Hazards { get; }
    
                public HoleGeometry(CircleGameObj cup, CircleGameObj green, RectGameObj fairway, RectGameObj rough, Hazard[] haz)
                {
                    Cup = cup;
                    Green = green;
                    Fairway = fairway;
                    Rough = rough;
                    Hazards = haz;
                }
            }
    
    
            // -------------------------------------------------------- Plot
            public class Plot
            {
                public int X { get; }
                public int Y { get; set; }
                public int Offline { get; }
    
                public Plot(int x, int y, int offline)
                {
                    X = x;
                    Y = y;
                    Offline = offline;
                }
            }
    
    
            // -------------------------------------------------------- GetDistance
            // distance between 2 points
            double GetDistance(Point pt1, Point pt2)
            {
                return Math.Sqrt(Math.Pow((pt2.X - pt1.X), 2) + Math.Pow((pt2.Y - pt1.Y), 2));
            }
    
    
            // -------------------------------------------------------- IsInRectangle
            bool IsInRectangle(CircleGameObj pt, RectGameObj rect)
            {
                return ((pt.X > rect.X) &&
                        (pt.X < rect.X + rect.Width) &&
                        (pt.Y > rect.Y) &&
                        (pt.Y < rect.Y + rect.Length));
            }
    
    
            // -------------------------------------------------------- ToRadians
            double ToRadians(double angle) { return angle * (Math.PI / 180.0); }
    
    
            // -------------------------------------------------------- ToDegrees360
            // radians to 360 degrees
            double ToDegrees360(double angle)
            {
                double deg = angle * (180.0 / Math.PI);
                if (deg < 0.0) { deg += 360.0; }
                return deg;
            }
    
    
            // -------------------------------------------------------- Odds
            // chance an integer is <= the given argument
            // between 1-100
            Random RND = new Random();
    
            bool Odds(int x)
            {
                return RND.Next(1, 101) <= x;
            }
        }
    }
    
    
    ================================================
    FILE: 39_Golf/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    There are 2 compiled executables in the compiled/ directory (windows and linux) that you can play right away!
    
    Program.cs contains the C# source code.
    It has been written for .NET Core 3.1
    
    The source code is well documented.
    
    
    ================================================
    FILE: 39_Golf/golf.bas
    ================================================
    1 PRINT TAB(34);"GOLF"
    2 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    3 PRINT:PRINT:PRINT
    4 PRINT "WELCOME TO THE CREATIVE COMPUTING COUNTRY CLUB,"
    5 PRINT "AN EIGHTEEN HOLE CHAMPIONSHIP LAYOUT LOCATED A SHORT"
    6 PRINT "DISTANCE FROM SCENIC DOWNTOWN MORRISTOWN.  THE"
    7 PRINT "COMMENTATOR WILL EXPLAIN THE GAME AS YOU PLAY."
    8 PRINT "ENJOY YOUR GAME; SEE YOU AT THE 19TH HOLE..."
    9 PRINT:PRINT: DIM L(10)
    10 G1=18
    20 G2=0
    30 G3=0
    40 A=0
    50 N=.8
    60 S2=0
    70 F=1
    80 PRINT "WHAT IS YOUR HANDICAP";
    90 INPUT H:PRINT
    100 IF H>30 THEN 470
    110 IF H<0 THEN 470
    120 PRINT "DIFFICULTIES AT GOLF INCLUDE:"
    130 PRINT "0=HOOK, 1=SLICE, 2=POOR DISTANCE, 4=TRAP SHOTS, 5=PUTTING"
    140 PRINT "WHICH ONE (ONLY ONE) IS YOUR WORST";
    150 INPUT T:PRINT
    160 IF T>5 THEN 120
    170 S1=0
    210 REM
    230 L(0)=0
    240 J=0
    245 Q=0
    250 S2=S2+1
    260 K=0
    270 IF F=1 THEN 310
    290 PRINT "YOUR SCORE ON HOLE";F-1;"WAS";S1
    291 GOTO 1750
    292 IF S1>P+2 THEN 297
    293 IF S1=P THEN 299
    294 IF S1=P-1 THEN 301
    295 IF S1=P-2 THEN 303
    296 GOTO 310
    297 PRINT "KEEP YOUR HEAD DOWN."
    298 GOTO 310
    299 PRINT "A PAR.  NICE GOING."
    300 GOTO 310
    301 PRINT "A BIRDIE."
    302 GOTO 310
    303 IF P=3 THEN 306
    304 PRINT "A GREAT BIG EAGLE."
    305 GOTO 310
    306 PRINT "A HOLE IN ONE."
    310 IF F=19 THEN 1710
    315 S1=0
    316 PRINT
    320 IF S1=0 THEN 1590
    330 IF L(0)<1 THEN 1150
    340 X=0
    350 IF L(0)>5 THEN 1190
    360 PRINT "SHOT WENT";D1;"YARDS.  IT'S";D2;"YARDS FROM THE CUP."
    362 PRINT "BALL IS";INT(O);"YARDS OFF LINE... IN ";
    380 GOSUB 400
    390 GOTO 620
    400 IF L(X)=1 THEN 480
    410 IF L(X)=2 THEN 500
    420 IF L(X)=3 THEN 520
    430 IF L(X)=4 THEN 540
    440 IF L(X)=5 THEN 560
    450 IF L(X)=6 THEN 580
    460 PRINT "OUT OF BOUNDS."
    465 GOTO 1690
    470 PRINT "PGA HANDICAPS RANGE FROM 0 TO 30."
    472 GOTO 80
    480 PRINT "FAIRWAY."
    490 GOTO 1690
    500 PRINT "ROUGH."
    510 GOTO 1690
    520 PRINT "TREES."
    530 GOTO 1690
    540 PRINT "ADJACENT FAIRWAY."
    550 GOTO 1690
    560 PRINT "TRAP."
    570 GOTO 1690
    580 PRINT "WATER."
    590 GOTO 1690
    620 IF A=1 THEN 629
    621 PRINT "SELECTION OF CLUBS"
    622 PRINT "YARDAGE DESIRED                       SUGGESTED CLUBS"
    623 PRINT "200 TO 280 YARDS                           1 TO 4"
    624 PRINT "100 TO 200 YARDS                          19 TO 13"
    625 PRINT "  0 TO 100 YARDS                          29 TO 23"
    626 A=1
    629 PRINT "WHAT CLUB DO YOU CHOOSE";
    630 INPUT C
    632 PRINT
    635 IF C<1 THEN 690
    637 IF C>29 THEN 690
    640 IF C>4 THEN 710
    650 IF L(0)<=5 THEN 740
    660 IF C=14 THEN 740
    665 IF C=23 THEN 740
    670 GOTO 690
    680 S1=S1-1
    690 PRINT "THAT CLUB IS NOT IN THE BAG."
    693 PRINT
    700 GOTO 620
    710 IF C<12 THEN 690
    720 C=C-6
    730 GOTO 650
    740 S1=S1+1
    741 W=1
    742 IF C>13 THEN 960
    746 IF INT(F/3)=F/3 THEN 952
    752 IF C<4 THEN 756
    754 GOTO 760
    756 IF L(0)=2 THEN 862
    760 IF S1>7 THEN 867
    770 D1=INT(((30-H)*2.5+187-((30-H)*.25+15)*C/2)+25*RND(1))
    780 D1=INT(D1*W)
    800 IF T=2 THEN 1170
    830 O=(RND(1)/.8)*(2*H+16)*ABS(TAN(D1*.0035))
    840 D2=INT(SQR(O^2+ABS(D-D1)^2))
    850 IF D-D1<0 THEN 870
    860 GOTO 890
    862 PRINT "YOU DUBBED IT."
    864 D1=35
    866 GOTO 830
    867 IF D<200 THEN 1300
    868 GOTO 770
    870 IF D2<20 THEN 890
    880 PRINT "TOO MUCH CLUB. YOU'RE PAST THE HOLE."
    890 B=D
    900 D=D2
    910 IF D2>27 THEN 1020
    920 IF D2>20 THEN 1100
    930 IF D2>.5 THEN 1120
    940 L(0)=9
    950 GOTO 1470
    952 IF S2+Q+(10*(F-1)/18)<(F-1)*(72+((H+1)/.85))/18 THEN 956
    954 GOTO 752
    956 Q=Q+1
    957 IF S1/2<>INT(S1/2) THEN 1011
    958 GOTO 862
    960 PRINT "NOW GAUGE YOUR DISTANCE BY A PERCENTAGE (1 TO 100)"
    961 PRINT "OF A FULL SWING";
    970 INPUT W: W=W/100
    972 PRINT
    980 IF W>1 THEN 680
    985 IF L(0)=5 THEN 1280
    990 IF C=14 THEN 760
    1000 C=C-10
    1010 GOTO 760
    1011 IF D<95 THEN 862
    1012 PRINT "BALL HIT TREE - BOUNCED INTO ROUGH";D-75;"YARDS FROM HOLE."
    1014 D=D-75
    1018 GOTO 620
    1020 IF O<30 THEN 1150
    1022 IF J>0 THEN 1150
    1030 IF T>0 THEN 1070
    1035 S9=(S2+1)/15
    1036 IF INT(S9)=S9 THEN 1075
    1040 PRINT "YOU HOOKED- ";
    1050 L(0)=L(2)
    1055 IF O>45 THEN 1092
    1060 GOTO 320
    1070 S9=(S2+1)/15
    1071 IF INT(S9)=S9 THEN 1040
    1075 PRINT "YOU SLICED- ";
    1080 L(0)=L(1)
    1090 GOTO 1055
    1092 PRINT "BADLY."
    1094 GOTO 320
    1100 L(0)=5
    1110 GOTO 320
    1120 L(0)=8
    1130 D2=INT(D2*3)
    1140 GOTO 1380
    1150 L(0)=1
    1160 GOTO 320
    1170 D1=INT(.85*D1)
    1180 GOTO 830
    1190 IF L(0)>6 THEN 1260
    1200 PRINT "YOUR SHOT WENT INTO THE WATER."
    1210 S1=S1+1
    1220 PRINT "PENALTY STROKE ASSESSED.  HIT FROM PREVIOUS LOCATION."
    1230 J=J+1
    1240 L(0)=1
    1242 D=B
    1250 GOTO 620
    1260 PRINT "YOUR SHOT WENT OUT OF BOUNDS."
    1270 GOTO 1210
    1280 IF T=3 THEN 1320
    1300 D2=1+(3*INT((80/(40-H))*RND(1)))
    1310 GOTO 1380
    1320 IF RND(1)>N THEN 1360
    1330 N=N*.2
    1340 PRINT "SHOT DUBBED, STILL IN TRAP."
    1350 GOTO 620
    1360 N=.8
    1370 GOTO 1300
    1380 PRINT "ON GREEN,";D2;"FEET FROM THE PIN."
    1381 PRINT "CHOOSE YOUR PUTT POTENCY (1 TO 13):";
    1400 INPUT I
    1410 S1=S1+1
    1420 IF S1+1-P>(H*.072)+2 THEN 1470
    1425 IF K>2 THEN 1470
    1428 K=K+1
    1430 IF T=4 THEN 1530
    1440 D2=D2-I*(4+2*RND(1))+1.5
    1450 IF D2<-2 THEN 1560
    1460 IF D2>2 THEN 1500
    1470 PRINT "YOU HOLED IT."
    1472 PRINT
    1480 F=F+1
    1490 GOTO 230
    1500 PRINT "PUTT SHORT."
    1505 D2=INT(D2)
    1510 GOTO 1380
    1530 D2=D2-I*(4+1*RND(1))+1
    1550 GOTO 1450
    1560 PRINT "PASSED BY CUP."
    1570 D2=-D2
    1580 GOTO 1505
    1590 READ D,P,L(1),L(2)
    1595 PRINT
    1600 PRINT "YOU ARE AT THE TEE OFF HOLE";F;"DISTANCE";D;"YARDS, PAR";P
    1605 G3=G3+P
    1620 PRINT "ON YOUR RIGHT IS ";
    1630 X=1
    1640 GOSUB 400
    1650 PRINT "ON YOUR LEFT IS ";
    1660 X=2
    1670 GOSUB 400
    1680 GOTO 620
    1690 RETURN
    1700 DATA 361,4,4,2,389,4,3,3,206,3,4,2,500,5,7,2
    1702 DATA 408,4,2,4,359,4,6,4,424,4,4,2,388,4,4,4
    1704 DATA 196,3,7,2,400,4,7,2,560,5,7,2,132,3,2,2
    1706 DATA 357,4,4,4,294,4,2,4,475,5,2,3,375,4,4,2
    1708 DATA 180,3,6,2,550,5,6,6
    1710 PRINT
    1750 G2=G2+S1
    1760 PRINT "TOTAL PAR FOR";F-1;"HOLES IS";G3;"  YOUR TOTAL IS";G2
    1761 IF G1=F-1 THEN 1770
    1765 GOTO 292
    1770 END
    
    
    ================================================
    FILE: 39_Golf/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 39_Golf/javascript/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells)
    
    
    ================================================
    FILE: 39_Golf/javascript/golf.html
    ================================================
    
    
    GOLF
    
    
    
    
    
    
    
    
    
    
    ================================================
    FILE: 39_Golf/javascript/golf.js
    ================================================
    // GOLF
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    var la = [];
    var f;
    var s1;
    var g2;
    var g3;
    var x;
    
    var hole_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,
    ];
    
    function show_obstacle()
    {
        switch (la[x]) {
            case 1:
                print("FAIRWAY.\n");
                break;
            case 2:
                print("ROUGH.\n");
                break;
            case 3:
                print("TREES.\n");
                break;
            case 4:
                print("ADJACENT FAIRWAY.\n");
                break;
            case 5:
                print("TRAP.\n");
                break;
            case 6:
                print("WATER.\n");
                break;
        }
    }
    
    function show_score()
    {
        g2 += s1;
        print("TOTAL PAR FOR " + (f - 1) + " HOLES IS " + g3 + "  YOUR TOTAL IS " + g2 + "\n");
    }
    
    // Main program
    async function main()
    {
        print(tab(34) + "GOLF\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        print("WELCOME TO THE CREATIVE COMPUTING COUNTRY CLUB,\n");
        print("AN EIGHTEEN HOLE CHAMPIONSHIP LAYOUT LOCATED A SHORT\n");
        print("DISTANCE FROM SCENIC DOWNTOWN MORRISTOWN.  THE\n");
        print("COMMENTATOR WILL EXPLAIN THE GAME AS YOU PLAY.\n");
        print("ENJOY YOUR GAME; SEE YOU AT THE 19TH HOLE...\n");
        print("\n");
        print("\n");
        next_hole = 0;
        g1 = 18;
        g2 = 0;
        g3 = 0;
        a = 0;
        n = 0.8;
        s2 = 0;
        f = 1;
        while (1) {
            print("WHAT IS YOUR HANDICAP");
            h = parseInt(await input());
            print("\n");
            if (h < 0 || h > 30) {
                print("PGA HANDICAPS RANGE FROM 0 TO 30.\n");
            } else {
                break;
            }
        }
        do {
            print("DIFFICULTIES AT GOLF INCLUDE:\n");
            print("0=HOOK, 1=SLICE, 2=POOR DISTANCE, 4=TRAP SHOTS, 5=PUTTING\n");
            print("WHICH ONE (ONLY ONE) IS YOUR WORST");
            t = parseInt(await input());
            print("\n");
        } while (t > 5) ;
        s1 = 0;
        first_routine = true;
        while (1) {
            if (first_routine) {
                la[0] = 0;
                j = 0;
                q = 0;
                s2++;
                k = 0;
                if (f != 1) {
                    print("YOUR SCORE ON HOLE " + (f - 1) + " WAS " + s1 + "\n");
                    show_score();
                    if (g1 == f - 1)    // Completed all holes?
                        return;         // Exit game
                    if (s1 > p + 2) {
                        print("KEEP YOUR HEAD DOWN.\n");
                    } else if (s1 == p) {
                        print("A PAR.  NICE GOING.\n");
                    } else if (s1 == p - 1) {
                        print("A BIRDIE.\n");
                    } else if (s1 == p - 2) {
                        if (p != 3)
                            print("A GREAT BIG EAGLE.\n");
                        else
                            print("A HOLE IN ONE.\n");
                    }
                }
                if (f == 19) {
                    print("\n");
                    show_score();
                    if (g1 == f - 1)
                        return;
                }
                s1 = 0;
                print("\n");
                if (s1 != 0 && la[0] < 1)
                    la[0] = 1;
            }
            if (s1 == 0) {
                d = hole_data[next_hole++];
                p = hole_data[next_hole++];
                la[1] = hole_data[next_hole++];
                la[2] = hole_data[next_hole++];
                print("\n");
                print("YOU ARE AT THE TEE OFF HOLE " + f + " DISTANCE " + d + " YARDS, PAR " + p + "\n");
                g3 += p;
                print("ON YOUR RIGHT IS ");
                x = 1;
                show_obstacle();
                print("ON YOUR LEFT IS ");
                x = 2
                show_obstacle();
            } else {
                x = 0;
                if (la[0] > 5) {
                    if (la[0] > 6) {
                        print("YOUR SHOT WENT OUT OF BOUNDS.\n");
                    } else {
                        print("YOUR SHOT WENT INTO THE WATER.\n");
                    }
                    s1++;
                    print("PENALTY STROKE ASSESSED.  HIT FROM PREVIOUS LOCATION.\n");
                    j++;
                    la[0] = 1;
                    d = b;
                } else {
                    print("SHOT WENT " + d1 + " YARDS.  IT'S " + d2 + " YARDS FROM THE CUP.\n");
                    print("BALL IS " + Math.floor(o) + " YARDS OFF LINE... IN ");
                    show_obstacle();
                }
            }
    
            while (1) {
                if (a != 1) {
                    print("SELECTION OF CLUBS\n");
                    print("YARDAGE DESIRED                       SUGGESTED CLUBS\n");
                    print("200 TO 280 YARDS                           1 TO 4\n");
                    print("100 TO 200 YARDS                          19 TO 13\n");
                    print("  0 TO 100 YARDS                          29 TO 23\n");
                    a = 1;
                }
                print("WHAT CLUB DO YOU CHOOSE");
                c = parseInt(await input());
                print("\n");
                if (c >= 1 && c <= 29 && (c < 5 || c >= 12)) {
                    if (c > 4)
                        c -= 6;
                    if (la[0] <= 5 || c == 14 || c == 23) {
                        s1++;
                        w = 1;
                        if (c <= 13) {
                            if (f % 3 == 0 && s2 + q + (10 * (f - 1) / 18) < (f - 1) * (72 + ((h + 1) / 0.85)) / 18) {
                                q++;
                                if (s1 % 2 != 0 && d >= 95) {
                                    print("BALL HIT TREE - BOUNCED INTO ROUGH " + (d - 75) + " YARDS FROM HOLE.\n");
                                    d -= 75;
                                    continue;
                                }
                                print("YOU DUBBED IT.\n");
                                d1 = 35;
                                second_routine = 1;
                                break;
                            } else if (c < 4 && la[0] == 2) {
                                print("YOU DUBBED IT.\n");
                                d1 = 35;
                                second_routine = 1;
                                break;
                            } else {
                                second_routine = 0;
                                break;
                            }
                        } else {
                            print("NOW GAUGE YOUR DISTANCE BY A PERCENTAGE (1 TO 100)\n");
                            print("OF A FULL SWING");
                            w = parseInt(await input());
                            w /= 100;
                            print("\n");
                            if (w <= 1) {
                                if (la[0] == 5) {
                                    if (t == 3) {
                                        if (Math.random() <= n) {
                                            n *= 0.2;
                                            print("SHOT DUBBED, STILL IN TRAP.\n");
                                            continue;
                                        }
                                        n = 0.8;
                                    }
                                    d2 = 1 + (3 * Math.floor((80 / (40 - h)) * Math.random()));
                                    second_routine = 2;
                                    break;
                                }
                                if (c != 14)
                                    c -= 10;
                                second_routine = 0;
                                break;
                            }
                            s1--;
                            // Fall through to THAT CLUB IS NOT IN THE BAG.
                        }
                    }
                }
                print("THAT CLUB IS NOT IN THE BAG.\n");
                print("\n");
            }
            if (second_routine == 0) {
                if (s1 > 7 && d < 200) {
                    d2 = 1 + (3 * Math.floor((80 / (40 - h)) * Math.random()));
                    second_routine = 2;
                } else {
                    d1 = Math.floor(((30 - h) * 2.5 + 187 - ((30 - h) * 0.25 + 15) * c / 2) + 25 * Math.random());
                    d1 = Math.floor(d1 * w);
                    if (t == 2)
                        d1 = Math.floor(d1 * 0.85);
                }
            }
            if (second_routine <= 1) {
                o = (Math.random() / 0.8) * (2 * h + 16) * Math.abs(Math.tan(d1 * 0.0035));
                d2 = Math.floor(Math.sqrt(Math.pow(o, 2) + Math.pow(Math.abs(d - d1), 2)));
                if (d - d1 < 0) {
                    if (d2 >= 20)
                        print("TOO MUCH CLUB, YOU'RE PAST THE HOLE.\n");
                }
                b = d;
                d = d2;
                if (d2 > 27) {
                    if (o < 30 || j > 0) {
                        la[0] = 1;
                    } else {
                        if (t <= 0) {
                            s9 = (s2 + 1) / 15;
                            if (Math.floor(s9) == s9) {
                                print("YOU SLICED- ");
                                la[0] = la[1];
                            } else {
                                print("YOU HOOKED- ");
                                la[0] = la[2];
                            }
                        } else {
                            s9 = (s2 + 1) / 15;
                            if (Math.floor(s9) == s9) {
                                print("YOU HOOKED- ");
                                la[0] = la[2];
                            } else {
                                print("YOU SLICED- ");
                                la[0] = la[1];
                            }
                        }
                        if (o > 45)
                            print("BADLY.\n");
                    }
                    first_routine = false;
                } else if (d2 > 20) {
                    la[0] = 5;
                    first_routine = false;
                } else if (d2 > 0.5) {
                    la[0] = 8;
                    d2 = Math.floor(d2 * 3);
                    second_routine = 2;
                } else {
                    la[0] = 9;
                    print("YOU HOLED IT.\n");
                    print("\n");
                    f++;
                    first_routine = true;
                }
            }
            if (second_routine == 2) {
                while (1) {
                    print("ON GREEN, " + d2 + " FEET FROM THE PIN.\n");
                    print("CHOOSE YOUR PUTT POTENCY (1 TO 13):");
                    i = parseInt(await input());
                    s1++;
                    if (s1 + 1 - p <= (h * 0.072) + 2 && k <= 2) {
                        k++;
                        if (t == 4)
                            d2 -= i * (4 + 1 * Math.random()) + 1;
                        else
                            d2 -= i * (4 + 2 * Math.random()) + 1.5;
                        if (d2 < -2) {
                            print("PASSED BY CUP.\n");
                            d2 = Math.floor(-d2);
                            continue;
                        }
                        if (d2 > 2) {
                            print("PUTT SHORT.\n");
                            d2 = Math.floor(d2);
                            continue;
                        }
                    }
                    print("YOU HOLED IT.\n");
                    print("\n");
                    f++;
                    break;
                }
                first_routine = true;
            }
        }
    }
    
    main();
    
    
    ================================================
    FILE: 39_Golf/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 39_Golf/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 39_Golf/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 39_Golf/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 39_Golf/python/golf.py
    ================================================
    '''
            8""""8 8"""88 8     8""""
            8    " 8    8 8     8
            8e     8    8 8e    8eeee
            88  ee 8    8 88    88
            88   8 8    8 88    88
            88eee8 8eeee8 88eee 88
    
    GOLF
    
    
    Despite being a text based game, the code uses simple geometry to simulate a course.
    Fairways are 40 yard wide rectangles, surrounded by 5 yards of rough around the perimeter.
    The green is a circle of 10 yards radius around the cup.
    The cup is always at point (0,0).
    
    Using basic trigonometry we can plot the ball's location using the distance of the stroke and
    and the angle of deviation (hook/slice).
    
    The stroke distances are based on real world averages of different club types.
    Lots of randomization, "business rules", and luck influence the game play.
    Probabilities are commented in the code.
    
    note: 'courseInfo', 'clubs', & 'scoreCard' arrays each include an empty object so indexing
    can begin at 1. Like all good programmers we count from zero, but in this context,
    it's more natural when hole number one is at index one
    
    
        |-----------------------------|
        |            rough            |
        |   ----------------------    |
        |   |                     |   |
        | r |        =  =         | r |
        | o |     =        =      | o |
        | u |    =    .     =     | u |
        | g |    =   green  =     | g |
        | h |     =        =      | h |
        |   |        =  =         |   |
        |   |                     |   |
        |   |                     |   |
        |   |      Fairway        |   |
        |   |                     |   |
        |   |               ------    |
        |   |            --        -- |
        |   |           --  hazard  --|
        |   |            --        -- |
        |   |               ------    |
        |   |                     |   |
        |   |                     |   |   out
        |   |                     |   |   of
        |   |                     |   |   bounds
        |   |                     |   |
        |   |                     |   |
        |            tee              |
    
    
    Typical green size: 20-30 yards
    Typical golf course fairways are 35 to 45 yards wide
    Our fairway extends 5 yards past green
    Our rough is a 5 yard perimeter around fairway
    
    We calculate the new position of the ball given the ball's point, the distance
    of the stroke, and degrees off line (hook or slice).
    
    Degrees off (for a right handed golfer):
    Slice: positive degrees = ball goes right
    Hook: negative degrees = left goes left
    
    The cup is always at point: 0,0.
    We use atan2 to compute the angle between the cup and the ball.
    Setting the cup's vector to 0,-1 on a 360 circle is equivalent to:
    0 deg = 12 o'clock;  90 deg = 3 o'clock;  180 deg = 6 o'clock;  270 = 9 o'clock
    The reverse angle between the cup and the ball is a difference of PI (using radians).
    
    Given the angle and stroke distance (hypotenuse), we use cosine to compute
    the opposite and adjacent sides of the triangle, which, is the ball's new position.
    
            0
            |
    270 - cup - 90
            |
            180
    
    
            cup
            |
            |
            | opp
            |-----* new position
            |    /
            |   /
        adj  |  /
            | /  hyp
            |/
            tee
    
    <- hook    slice ->
    
    
    Given the large number of combinations needed to describe a particular stroke / ball location,
    we use the technique of "bitwise masking" to describe stroke results.
    With bit masking, multiple flags (bits) are combined into a single binary number that can be
    tested by applying a mask. A mask is another binary number that isolates a particular bit that
    you are interested in. You can then apply your language's bitwise opeartors to test or
    set a flag.
    
    Game design by Jason Bonthron, 2021
    www.bonthron.com
    for my father, Raymond Bonthron, an avid golfer
    
    Inspired by the 1978 "Golf" from "Basic Computer Games"
    by Steve North, who modified an existing golf game by an unknown author
    
    Ported in 2022 to Python by Martin Thoma
    '''
    
    
    import enum
    import math
    import random
    import time
    from dataclasses import dataclass
    from functools import partial
    from typing import Any, Callable, List, NamedTuple, Tuple
    
    
    def clear_console() -> None:
        print("\033[H\033[J", end="")
    
    
    class Point(NamedTuple):
        x: int
        y: int
    
    
    class GameObjType(enum.Enum):
        BALL = enum.auto()
        CUP = enum.auto()
        GREEN = enum.auto()
        FAIRWAY = enum.auto()
        ROUGH = enum.auto()
        TREES = enum.auto()
        WATER = enum.auto()
        SAND = enum.auto()
    
    
    class CircleGameObj(NamedTuple):
        # center point
        X: int
        Y: int
        Radius: int
        Type: GameObjType
    
    
    class RectGameObj(NamedTuple):
        # Upper left corner
        X: int
        Y: int
        Width: int
        Length: int
        Type: GameObjType
    
    
    Ball = CircleGameObj
    Hazard = CircleGameObj
    
    
    class HoleInfo(NamedTuple):
        hole: int
        yards: int
        par: int
        hazards: List[Hazard]
        description: str
    
    
    class HoleGeometry(NamedTuple):
        cup: CircleGameObj
        green: CircleGameObj
        fairway: RectGameObj
        rough: RectGameObj
        hazards: List[Hazard]
    
    
    @dataclass
    class Plot:
        x: int
        y: int
        offline: int
    
    
    def get_distance(pt1: Point, pt2: Point) -> float:
        """distance between 2 points"""
        return math.sqrt(math.pow((pt2.x - pt1.x), 2) + math.pow((pt2.y - pt1.y), 2))
    
    
    def is_in_rectangle(pt: CircleGameObj, rect: RectGameObj) -> bool:
        # only true if its completely inside
        return (
            (pt.X > rect.X)
            and (pt.X < rect.X + rect.Width)
            and (pt.Y > rect.Y)
            and (pt.Y < rect.Y + rect.Length)
        )
    
    
    def to_radians(angle: float) -> float:
        return angle * (math.pi / 180.0)
    
    
    def to_degrees_360(angle: float) -> float:
        """radians to 360 degrees"""
        deg = angle * (180.0 / math.pi)
        if deg < 0.0:
            deg += 360.0
        return deg
    
    
    def odds(x: int) -> bool:
        # chance an integer is <= the given argument
        # between 1-100
        return random.randint(1, 101) <= x
    
    
    # THE COURSE
    CourseInfo = [
        HoleInfo(0, 0, 0, [], ""),  # include a blank so index 1 == hole 1
        # -------------------------------------------------------- front 9
        HoleInfo(
            1,
            361,
            4,
            [
                Hazard(20, 100, 10, GameObjType.TREES),
                Hazard(-20, 80, 10, GameObjType.TREES),
                Hazard(-20, 100, 10, GameObjType.TREES),
            ],
            "There are a couple of trees on the left and right.",
        ),
        HoleInfo(
            2,
            389,
            4,
            [Hazard(0, 160, 20, GameObjType.WATER)],
            "There is a large water hazard across the fairway about 150 yards.",
        ),
        HoleInfo(
            3,
            206,
            3,
            [
                Hazard(20, 20, 5, GameObjType.WATER),
                Hazard(-20, 160, 10, GameObjType.WATER),
                Hazard(10, 12, 5, GameObjType.SAND),
            ],
            "There is some sand and water near the green.",
        ),
        HoleInfo(
            4,
            500,
            5,
            [Hazard(-14, 12, 12, GameObjType.SAND)],
            "There's a bunker to the left of the green.",
        ),
        HoleInfo(
            5,
            408,
            4,
            [
                Hazard(20, 120, 20, GameObjType.TREES),
                Hazard(20, 160, 20, GameObjType.TREES),
                Hazard(10, 20, 5, GameObjType.SAND),
            ],
            "There are some trees to your right.",
        ),
        HoleInfo(
            6,
            359,
            4,
            [Hazard(14, 0, 4, GameObjType.SAND), Hazard(-14, 0, 4, GameObjType.SAND)],
            "",
        ),
        HoleInfo(
            7,
            424,
            5,
            [
                Hazard(20, 200, 10, GameObjType.SAND),
                Hazard(10, 180, 10, GameObjType.SAND),
                Hazard(20, 160, 10, GameObjType.SAND),
            ],
            "There are several sand traps along your right.",
        ),
        HoleInfo(8, 388, 4, [Hazard(-20, 340, 10, GameObjType.TREES)], ""),
        HoleInfo(
            9,
            196,
            3,
            [Hazard(-30, 180, 20, GameObjType.TREES), Hazard(14, -8, 5, GameObjType.SAND)],
            "",
        ),
        # -------------------------------------------------------- back 9
        HoleInfo(
            hole=10,
            yards=400,
            par=4,
            hazards=[
                Hazard(-14, -8, 5, GameObjType.SAND),
                Hazard(14, -8, 5, GameObjType.SAND),
            ],
            description="",
        ),
        HoleInfo(
            11,
            560,
            5,
            [
                Hazard(-20, 400, 10, GameObjType.TREES),
                Hazard(-10, 380, 10, GameObjType.TREES),
                Hazard(-20, 260, 10, GameObjType.TREES),
                Hazard(-20, 200, 10, GameObjType.TREES),
                Hazard(-10, 180, 10, GameObjType.TREES),
                Hazard(-20, 160, 10, GameObjType.TREES),
            ],
            "Lots of trees along the left of the fairway.",
        ),
        HoleInfo(
            12,
            132,
            3,
            [
                Hazard(-10, 120, 10, GameObjType.WATER),
                Hazard(-5, 100, 10, GameObjType.SAND),
            ],
            "There is water and sand directly in front of you. A good drive should clear both.",
        ),
        HoleInfo(
            13,
            357,
            4,
            [
                Hazard(-20, 200, 10, GameObjType.TREES),
                Hazard(-10, 180, 10, GameObjType.TREES),
                Hazard(-20, 160, 10, GameObjType.TREES),
                Hazard(14, 12, 8, GameObjType.SAND),
            ],
            "",
        ),
        HoleInfo(14, 294, 4, [Hazard(0, 20, 10, GameObjType.SAND)], ""),
        HoleInfo(
            15,
            475,
            5,
            [Hazard(-20, 20, 10, GameObjType.WATER), Hazard(10, 20, 10, GameObjType.SAND)],
            "Some sand and water near the green.",
        ),
        HoleInfo(16, 375, 4, [Hazard(-14, -8, 5, GameObjType.SAND)], ""),
        HoleInfo(
            17,
            180,
            3,
            [
                Hazard(20, 100, 10, GameObjType.TREES),
                Hazard(-20, 80, 10, GameObjType.TREES),
            ],
            "",
        ),
        HoleInfo(
            18,
            550,
            5,
            [Hazard(20, 30, 15, GameObjType.WATER)],
            "There is a water hazard near the green.",
        ),
    ]
    
    
    # -------------------------------------------------------- bitwise Flags
    dub = 0b00000000000001
    hook = 0b00000000000010
    slice_ = 0b00000000000100
    passed_cup = 0b00000000001000
    in_cup = 0b00000000010000
    on_fairway = 0b00000000100000
    on_green = 0b00000001000000
    in_rough = 0b00000010000000
    in_sand = 0b00000100000000
    in_trees = 0b00001000000000
    in_water = 0b00010000000000
    out_of_bounds = 0b00100000000000
    luck = 0b01000000000000
    ace = 0b10000000000000
    
    
    class Golf:
        ball: Ball
        hole_num: int = 0
        stroke_num: int = 0
        handicap: int = 0
        player_difficulty: int = 0
        hole_geometry: HoleGeometry
    
        # all fairways are 40 yards wide, extend 5 yards beyond the cup, and
        # have 5 yards of rough around the perimeter
        fairway_width: int = 40
        fairway_extension: int = 5
        rough_amt: int = 5
    
        # ScoreCard records the ball position after each stroke
        # a new list for each hole
        # include a blank list so index 1 == hole 1
        score_card: List[List[Ball]] = [[]]
    
        # YOUR BAG
        clubs: List[Tuple[str, int]] = [
            ("", 0),
            # name, average yardage
            ("Driver", 250),
            ("3 Wood", 225),
            ("5 Wood", 200),
            ("Hybrid", 190),
            ("4 Iron", 170),
            ("7 Iron", 150),
            ("9 Iron", 125),
            ("Pitching wedge", 110),
            ("Sand wedge", 75),
            ("Putter", 10),
        ]
    
        def __init__(self) -> None:
            print(" ")
            print('          8""""8 8"""88 8     8"""" ')
            print('          8    " 8    8 8     8     ')
            print("          8e     8    8 8e    8eeee ")
            print("          88  ee 8    8 88    88    ")
            print("          88   8 8    8 88    88    ")
            print("          88eee8 8eeee8 88eee 88    ")
            print(" ")
            print("Welcome to the Creative Computing Country Club,")
            print("an eighteen hole championship layout located a short")
            print("distance from scenic downtown Lambertville, New Jersey.")
            print("The game will be explained as you play.")
            print("Enjoy your game! See you at the 19th hole...")
            print(" ")
            print("Type QUIT at any time to leave the game.")
            print("Type BAG at any time to review the clubs in your bag.")
            print(" ")
    
            input("Press any key to continue.")
            clear_console()
            self.start_game()
    
        def start_game(self) -> None:
            print(" ")
            print("              YOUR BAG")
            self.review_bag()
            print("Type BAG at any time to review the clubs in your bag.")
            print(" ")
    
            input("Press any key to continue.")
            clear_console()
            self.ask_handicap()
    
        def ask_handicap(self) -> None:
            print(" ")
    
            self.ask(
                "PGA handicaps range from 0 to 30.\nWhat is your handicap?",
                0,
                30,
                self.set_handicap_ask_difficulty,
            )
    
        def set_handicap_ask_difficulty(self, i: int) -> None:
            self.handicap = i
            print(" ")
    
            self.ask(
                (
                    "Common difficulties at golf include:\n"
                    "1=Hook, 2=Slice, 3=Poor Distance, 4=Trap Shots, 5=Putting\n"
                    "Which one is your worst?"
                ),
                1,
                5,
                self.set_difficulty_and_hole,
            )
    
        def set_difficulty_and_hole(self, j: int) -> None:
            self.player_difficulty = j
            clear_console()
            self.new_hole()
    
        def new_hole(self) -> None:
            self.hole_num += 1
            self.stroke_num = 0
    
            info: HoleInfo = CourseInfo[self.hole_num]
    
            yards: int = info.yards
            # from tee to cup
            cup = CircleGameObj(0, 0, 0, GameObjType.CUP)
            green = CircleGameObj(0, 0, 10, GameObjType.GREEN)
    
            fairway = RectGameObj(
                0 - int(self.fairway_width / 2),
                0 - (green.Radius + self.fairway_extension),
                self.fairway_width,
                yards + (green.Radius + self.fairway_extension) + 1,
                GameObjType.FAIRWAY,
            )
    
            rough = RectGameObj(
                fairway.X - self.rough_amt,
                fairway.Y - self.rough_amt,
                fairway.Width + (2 * self.rough_amt),
                fairway.Length + (2 * self.rough_amt),
                GameObjType.ROUGH,
            )
    
            self.ball = Ball(0, yards, 0, GameObjType.BALL)
    
            self.score_card_start_new_hole()
    
            self.hole_geometry = HoleGeometry(cup, green, fairway, rough, info.hazards)
    
            print(f"                |> {self.hole_num}")
            print("                |        ")
            print("                |        ")
            print("          ^^^^^^^^^^^^^^^")
    
            print(
                f"Hole #{self.hole_num}. You are at the tee. Distance {info.yards} yards, par {info.par}."
            )
            print(info.description)
    
            self.tee_up()
    
        def set_putter_and_stroke(self, strength: float) -> None:
            putter = self.clubs[self.putt]
            self.stroke((putter[1] * (strength / 10.0)), self.putt)
    
        def ask_gauge(self, c: int) -> None:
            self.club = self.clubs[c]
    
            print(" ")
            print(f"[{self.club[0].upper()}: average {self.club[1]} yards]")
    
            foo = partial(self.make_stroke, c=c)
    
            self.ask(
                "Now gauge your distance by a percentage of a full swing. (1-10)",
                1,
                10,
                foo,
            )
    
        def make_stroke(self, strength: float, c: int) -> None:
            self.stroke((self.club[1] * (strength / 10.0)), c)
    
        def tee_up(self) -> None:
            # on the green? automatically select putter
            # otherwise Ask club and swing strength
            if self.is_on_green(self.ball) and not self.is_in_hazard(
                self.ball, GameObjType.SAND
            ):
                self.putt = 10
                print("[PUTTER: average 10 yards]")
                msg = "Keep your head down.\n" if odds(20) else ""
                self.ask(
                    f"{msg}Choose your putt potency. (1-10)",
                    1,
                    10,
                    self.set_putter_and_stroke,
                )
            else:
                self.ask("What club do you choose? (1-10)", 1, 10, self.ask_gauge)
    
        def stroke(self, club_amt: float, club_index: int) -> None:
            self.stroke_num += 1
    
            flags = 0b000000000000
    
            # fore! only when driving
            if (self.stroke_num == 1) and (club_amt > 210) and odds(30):
                print('"...Fore !"')
    
            # dub
            if odds(5):
                # there's always a 5% chance of dubbing it
                flags |= dub
    
            # if you're in the rough, or sand, you really should be using a wedge
            if (
                (
                    self.is_in_rough(self.ball)
                    or self.is_in_hazard(self.ball, GameObjType.SAND)
                )
                and club_index not in {8, 9}
                and odds(40)
            ):
                flags |= dub
    
            # trap difficulty
            if (
                self.is_in_hazard(self.ball, GameObjType.SAND)
                and self.player_difficulty == 4
            ) and odds(20):
                flags |= dub
    
            # hook/slice
            # There's 10% chance of a hook or slice
            # if it's a known player_difficulty then increase chance to 30%
            # if it's a putt & putting is a player_difficulty increase to 30%
    
            rand_hook_slice: bool
            if (
                self.player_difficulty == 1
                or self.player_difficulty == 2
                or (self.player_difficulty == 5 and self.is_on_green(self.ball))
            ):
                rand_hook_slice = odds(30)
            else:
                rand_hook_slice = odds(10)
    
            if rand_hook_slice:
                if self.player_difficulty == 1:
                    if odds(80):
                        flags |= hook
                    else:
                        flags |= slice_
                elif self.player_difficulty == 2:
                    if odds(80):
                        flags |= slice_
                    else:
                        flags |= hook
                elif odds(50):
                    flags |= hook
                else:
                    flags |= slice_
    
            # beginner's luck !
            # If handicap is greater than 15, there's a 10% chance of avoiding all errors
            if (self.handicap > 15) and (odds(10)):
                flags |= luck
    
            # ace
            # there's a 10% chance of an Ace on a par 3
            if CourseInfo[self.hole_num].par == 3 and odds(10) and self.stroke_num == 1:
                flags |= ace
    
            # distance:
            # If handicap is < 15, there a 50% chance of reaching club average,
            # a 25% of exceeding it, and a 25% of falling short
            # If handicap is > 15, there's a 25% chance of reaching club average,
            # and 75% chance of falling short
            # The greater the handicap, the more the ball falls short
            # If poor distance is a known player_difficulty, then reduce distance by 10%
    
            distance: float
            rnd = random.randint(1, 101)
    
            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:
                distance = club_amt + (club_amt * 0.10)
            if self.player_difficulty == 3 and odds(80):  # poor distance
                distance *= 0.80
    
            if (flags & luck) == luck:
                distance = club_amt
    
            # angle
            # For all strokes, there's a possible "drift" of 4 degrees
            # a hooks or slice increases the angle between 5-10 degrees,
            # hook uses negative degrees
            angle = random.randint(0, 5)
            if (flags & slice_) == slice_:
                angle = random.randint(5, 11)
            if (flags & hook) == hook:
                angle = 0 - random.randint(5, 11)
            if (flags & luck) == luck:
                angle = 0
    
            plot = self.plot_ball(self.ball, distance, angle)
            # calculate a new location
            if (flags & luck) == luck and plot.y > 0:
                plot.y = 2
    
            flags = self.find_ball(
                Ball(plot.x, plot.y, plot.offline, GameObjType.BALL), flags
            )
    
            self.interpret_results(plot, flags)
    
        def plot_ball(self, ball: Ball, stroke_distance: float, degrees_off: float) -> Plot:
            cup_vector = Point(0, -1)
            rad_from_cup = math.atan2(ball.Y, ball.X) - math.atan2(
                cup_vector.y, cup_vector.x
            )
            rad_from_ball = rad_from_cup - math.pi
    
            hypotenuse = stroke_distance
            adjacent = math.cos(rad_from_ball + to_radians(degrees_off)) * hypotenuse
            opposite = math.sqrt(math.pow(hypotenuse, 2) - math.pow(adjacent, 2))
    
            new_pos: Point
            if to_degrees_360(rad_from_ball + to_radians(degrees_off)) > 180:
                new_pos = Point(int(ball.X - opposite), int(ball.Y - adjacent))
            else:
                new_pos = Point(int(ball.X + opposite), int(ball.Y - adjacent))
    
            return Plot(new_pos.x, new_pos.y, int(opposite))
    
        def interpret_results(self, plot: Plot, flags: int) -> None:
            cup_distance: int = int(
                get_distance(
                    Point(plot.x, plot.y),
                    Point(self.hole_geometry.cup.X, self.hole_geometry.cup.Y),
                )
            )
            travel_distance: int = int(
                get_distance(Point(plot.x, plot.y), Point(self.ball.X, self.ball.Y))
            )
    
            print(" ")
    
            if (flags & ace) == ace:
                print("Hole in One! You aced it.")
                self.score_card_record_stroke(Ball(0, 0, 0, GameObjType.BALL))
                self.report_current_score()
                return
    
            if (flags & in_trees) == in_trees:
                print("Your ball is lost in the trees. Take a penalty stroke.")
                self.score_card_record_stroke(self.ball)
                self.tee_up()
                return
    
            if (flags & in_water) == in_water:
                if odds(50):
                    msg = "Your ball has gone to a watery grave."
                else:
                    msg = "Your ball is lost in the water."
                print(f"{msg} Take a penalty stroke.")
                self.score_card_record_stroke(self.ball)
                self.tee_up()
                return
    
            if (flags & out_of_bounds) == out_of_bounds:
                print("Out of bounds. Take a penalty stroke.")
                self.score_card_record_stroke(self.ball)
                self.tee_up()
                return
    
            if (flags & dub) == dub:
                print("You dubbed it.")
                self.score_card_record_stroke(self.ball)
                self.tee_up()
                return
    
            if (flags & in_cup) == in_cup:
                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 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 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:
                prev_ball = self.score_card_get_previous_stroke()
                d1 = get_distance(
                    Point(prev_ball.X, prev_ball.Y),
                    Point(self.hole_geometry.cup.X, self.hole_geometry.cup.Y),
                )
                d2 = cup_distance
                if d2 > d1:
                    print("Too much club.")
    
            if (flags & in_rough) == in_rough:
                print("You're in the rough.")
    
            if (flags & in_sand) == in_sand:
                print("You're in a sand trap.")
    
            if (flags & on_green) == on_green:
                if cup_distance < 4:
                    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.")
    
            if ((flags & on_fairway) == on_fairway) or ((flags & in_rough) == in_rough):
                print(
                    f"Shot went {travel_distance} yards. "
                    f"It's {cup_distance} yards from the cup."
                )
    
            self.score_card_record_stroke(Ball(plot.x, plot.y, 0, GameObjType.BALL))
    
            self.ball = Ball(plot.x, plot.y, 0, GameObjType.BALL)
    
            self.tee_up()
    
        def report_current_score(self) -> None:
            par = CourseInfo[self.hole_num].par
            if len(self.score_card[self.hole_num]) == par + 1:
                print("A bogey. One above par.")
            if len(self.score_card[self.hole_num]) == par:
                print("Par. Nice.")
            if len(self.score_card[self.hole_num]) == (par - 1):
                print("A birdie! One below par.")
            if len(self.score_card[self.hole_num]) == (par - 2):
                print("An Eagle! Two below par.")
            if len(self.score_card[self.hole_num]) == (par - 3):
                print("Double Eagle! Unbelievable.")
    
            total_par: int = sum(CourseInfo[i].par for i in range(1, self.hole_num + 1))
            print(" ")
            print("-----------------------------------------------------")
            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()}."
            )
            print("-----------------------------------------------------")
            print(" ")
    
            if self.hole_num == 18:
                self.game_over()
            else:
                time.sleep(2)
                self.new_hole()
    
        def find_ball(self, ball: Ball, flags: int) -> int:
            if self.is_on_fairway(ball) and not self.is_on_green(ball):
                flags |= on_fairway
            if self.is_on_green(ball):
                flags |= on_green
            if self.is_in_rough(ball):
                flags |= in_rough
            if self.is_out_of_bounds(ball):
                flags |= out_of_bounds
            if self.is_in_hazard(ball, GameObjType.WATER):
                flags |= in_water
            if self.is_in_hazard(ball, GameObjType.TREES):
                flags |= in_trees
            if self.is_in_hazard(ball, GameObjType.SAND):
                flags |= in_sand
    
            if ball.Y < 0:
                flags |= passed_cup
    
            # less than 2, it's in the cup
            d = get_distance(
                Point(ball.X, ball.Y),
                Point(self.hole_geometry.cup.X, self.hole_geometry.cup.Y),
            )
            if d < 2:
                flags |= in_cup
    
            return flags
    
        def is_on_fairway(self, ball: Ball) -> bool:
            return is_in_rectangle(ball, self.hole_geometry.fairway)
    
        def is_on_green(self, ball: Ball) -> bool:
            d = get_distance(
                Point(ball.X, ball.Y),
                Point(self.hole_geometry.cup.X, self.hole_geometry.cup.Y),
            )
            return d < self.hole_geometry.green.Radius
    
        def hazard_hit(self, h: Hazard, ball: Ball, hazard: GameObjType) -> bool:
            d = get_distance(Point(ball.X, ball.Y), Point(h.X, h.Y))
            return d < h.Radius and h.Type == hazard
    
        def is_in_hazard(self, ball: Ball, hazard: GameObjType) -> bool:
            result: bool = False
            for h in self.hole_geometry.hazards:
                result = result and self.hazard_hit(h, ball, hazard)
            return result
    
        def is_in_rough(self, ball: Ball) -> bool:
            return is_in_rectangle(ball, self.hole_geometry.rough) and (
                not is_in_rectangle(ball, self.hole_geometry.fairway)
            )
    
        def is_out_of_bounds(self, ball: Ball) -> bool:
            return (not self.is_on_fairway(ball)) and (not self.is_in_rough(ball))
    
        def score_card_start_new_hole(self) -> None:
            self.score_card.append([])
    
        def score_card_record_stroke(self, ball: Ball) -> None:
            clone = Ball(ball.X, ball.Y, 0, GameObjType.BALL)
            self.score_card[self.hole_num].append(clone)
    
        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 = sum(len(h) for h in self.score_card)
            return total
    
        def ask(
            self, question: str, min_: int, max_: int, callback: Callable[[int], Any]
        ) -> None:
            # input from console is always an integer passed to a callback
            # or "quit" to end game
            print(question)
            i = input().strip().lower()
            if i == "quit":
                self.quit_game()
                return
            if i == "bag":
                self.review_bag()
    
            try:
                n = int(i)
                success = True
            except Exception:
                success = False
                n = 0
    
            if success and n >= min_ and n <= max_:
                callback(n)
            else:
                self.ask(question, min_, max_, callback)
    
        def review_bag(self) -> None:
            print(" ")
            print("  #     Club      Average Yardage")
            print("-----------------------------------")
            print("  1    Driver           250")
            print("  2    3 Wood           225")
            print("  3    5 Wood           200")
            print("  4    Hybrid           190")
            print("  5    4 Iron           170")
            print("  6    7 Iron           150")
            print("  7    9 Iron           125")
            print("  8    Pitching wedge   110")
            print("  9    Sand wedge        75")
            print(" 10    Putter            10")
            print(" ")
    
        def quit_game(self) -> None:
            print("\nLooks like rain. Goodbye!\n")
            return
    
        def game_over(self) -> None:
            net = self.score_card_get_total() - self.handicap
            print("Good game!")
            print(f"Your net score is: {net}")
            print("Let's visit the pro shop...")
            print(" ")
            return
    
    
    if __name__ == "__main__":
        Golf()
    
    
    ================================================
    FILE: 39_Golf/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 39_Golf/vbnet/Golf.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Golf", "Golf.vbproj", "{65A2E065-6541-4E6E-B6F0-9881080B5FFF}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{65A2E065-6541-4E6E-B6F0-9881080B5FFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{65A2E065-6541-4E6E-B6F0-9881080B5FFF}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{65A2E065-6541-4E6E-B6F0-9881080B5FFF}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{65A2E065-6541-4E6E-B6F0-9881080B5FFF}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 39_Golf/vbnet/Golf.vbproj
    ================================================
    
      
        Exe
        Golf
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 39_Golf/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 40_Gomoko/README.md
    ================================================
    ### Gomoko
    
    GOMOKO or GOMOKU is a traditional game of the Orient. It is played by two people on a board of intersecting lines (19 left-to-right lines, 19 top-to-bottom lines, 361 intersections in all). Players take turns. During his turn, a player may cover one intersection with a marker; (one player uses white markers; the other player uses black markers). The object of the game is to get five adjacent markers in a row, horizontally, vertically or along either diagonal.
    
    Unfortunately, this program does not make the computer a very good player. It does not know when you are about to win or even who has won. But some of its moves may surprise you.
    
    The original author of this program is Peter Sessions of People’s Computer Company.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=74)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=89)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 40_Gomoko/csharp/Gomoko.csproj
    ================================================
    
      
        Exe
        net6.0
        10
        enable
        enable
      
    
    
    
    ================================================
    FILE: 40_Gomoko/csharp/Gomoko.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Gomoko", "Gomoko.csproj", "{558B84AB-2010-436E-B191-00980C6CBA61}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{558B84AB-2010-436E-B191-00980C6CBA61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{558B84AB-2010-436E-B191-00980C6CBA61}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{558B84AB-2010-436E-B191-00980C6CBA61}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{558B84AB-2010-436E-B191-00980C6CBA61}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 40_Gomoko/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 40_Gomoko/gomoko.bas
    ================================================
    2 PRINT TAB(33);"GOMOKO"
    4 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    6 PRINT:PRINT:PRINT
    8 DIM A(19,19)
    10 PRINT "WELCOME TO THE ORIENTAL GAME OF GOMOKO."
    20 PRINT: PRINT "THE GAME IS PLAYED ON AN N BY N GRID OF A SIZE"
    30 PRINT "THAT YOU SPECIFY.  DURING YOUR PLAY, YOU MAY COVER ONE GRID"
    40 PRINT "INTERSECTION WITH A MARKER. THE OBJECT OF THE GAME IS TO GET"
    50 PRINT "5 ADJACENT MARKERS IN A ROW -- HORIZONTALLY, VERTICALLY, OR"
    60 PRINT "DIAGONALLY.  ON THE BOARD DIAGRAM, YOUR MOVES ARE MARKED"
    70 PRINT "WITH A '1' AND THE COMPUTER MOVES WITH A '2'."
    80 PRINT: PRINT "THE COMPUTER DOES NOT KEEP TRACK OF WHO HAS WON."
    90 PRINT "TO END THE GAME, TYPE -1,-1 FOR YOUR MOVE.": PRINT
    110 PRINT "WHAT IS YOUR BOARD SIZE (MIN 7/ MAX 19)";: INPUT N
    115 IF N>6 THEN 117
    116 GOTO 120
    117 IF N<20 THEN 210
    120 PRINT "I SAID, THE MINIMUM IS 7, THE MAXIMUM IS 19.": GOTO 110
    210 FOR I=1 TO N:FOR J=1 TO N: A(I,J)=0: NEXT J: NEXT I
    300 PRINT: PRINT "WE ALTERNATE MOVES.  YOU GO FIRST...": PRINT
    310 PRINT "YOUR PLAY (I,J)";: INPUT I,J
    315 PRINT
    320 IF I=-1 THEN 980
    330 X=I: Y=J: GOSUB 910: IF L=1 THEN 410
    340 PRINT "ILLEGAL MOVE.  TRY AGAIN...": GOTO 310
    410 IF A(I,J)=0 THEN 440
    420 PRINT "SQUARE OCCUPIED.  TRY AGAIN...": GOTO 310
    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+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
    600 REM *** COMPUTER TRIES A RANDOM MOVE ***
    610 X=INT(N*RND(1)+1): Y=INT(N*RND(1)+1): GOSUB 910: IF L=0 THEN 610
    650 IF A(X,Y)<>0 THEN 610
    660 A(X,Y)=2: GOSUB 810: GOTO 310
    710 X=I-E: Y=J-F: GOSUB 910
    750 IF L=0 THEN 610
    760 GOTO 650
    800 REM *** PRINT THE BOARD ***
    810 FOR I=1 TO N: FOR J=1 TO N: PRINT A(I,J);
    840 NEXT J: PRINT: NEXT I: PRINT: RETURN
    910 L=1: IF X<1 THEN 970
    920 IF X>N THEN 970
    930 IF Y<1 THEN 970
    940 IF Y>N THEN 970
    950 RETURN
    970 L=0: RETURN
    980 PRINT: PRINT "THANKS FOR THE GAME!!"
    985 PRINT "PLAY AGAIN (1 FOR YES, 0 FOR NO)";: INPUT Q
    990 IF Q=1 THEN 110
    999 END
    
    
    ================================================
    FILE: 40_Gomoko/java/Gomoko.java
    ================================================
    import java.util.Arrays;
    import java.util.InputMismatchException;
    import java.util.Scanner;
    
    /**
     * GOMOKO
     * 

    * Converted from BASIC to Java by Aldrin Misquitta (@aldrinm) */ public class Gomoko { private static final int MIN_BOARD_SIZE = 7; private static final int MAX_BOARD_SIZE = 19; public static void main(String[] args) { printIntro(); Scanner scan = new Scanner(System.in); int boardSize = readBoardSize(scan); boolean continuePlay = true; while (continuePlay) { int[][] board = new int[boardSize][boardSize]; //initialize the board elements to 0 for (int[] ints : board) { Arrays.fill(ints, 0); } System.out.println("\n\nWE ALTERNATE MOVES. YOU GO FIRST..."); boolean doneRound = false; while (!doneRound) { Move playerMove = null; boolean validMove = false; while (!validMove) { playerMove = readMove(scan); if (playerMove.i == -1 || playerMove.j == -1) { doneRound = true; System.out.println("\nTHANKS FOR THE GAME!!"); System.out.print("PLAY AGAIN (1 FOR YES, 0 FOR NO)? "); final int playAgain = scan.nextInt(); scan.nextLine(); if (playAgain == 1) { continuePlay = true; break; } else { continuePlay = false; break; } } else if (!isLegalMove(playerMove, boardSize)) { System.out.println("ILLEGAL MOVE. TRY AGAIN..."); } else if (board[playerMove.i - 1][playerMove.j - 1] != 0) { System.out.println("SQUARE OCCUPIED. TRY AGAIN..."); } else { validMove = true; } } if (!doneRound) { board[playerMove.i - 1][playerMove.j - 1] = 1; Move computerMove = getComputerMove(playerMove, board, boardSize); if (computerMove == null) { computerMove = getRandomMove(board, boardSize); } board[computerMove.i - 1][computerMove.j - 1] = 2; printBoard(board); } } } } //*** COMPUTER TRIES AN INTELLIGENT MOVE *** private static Move getComputerMove(Move playerMove, int[][] board, int boardSize) { for (int e = -1; e <= 1; e++) { for (int f = -1; f <= 1; f++) { if ((e + f - e * f) != 0) { var x = playerMove.i + f; var y = playerMove.j + f; final Move newMove = new Move(x, y); if (isLegalMove(newMove, boardSize)) { if (board[newMove.i - 1][newMove.j - 1] != 0) { newMove.i = newMove.i - e; newMove.i = newMove.j - f; if (!isLegalMove(newMove, boardSize)) { return null; } else { if (board[newMove.i - 1][newMove.j - 1] == 0) { return newMove; } } } } } } } return null; } private static void printBoard(int[][] board) { for (int[] ints : board) { for (int cell : ints) { System.out.printf(" %s", cell); } System.out.println(); } } //*** COMPUTER TRIES A RANDOM MOVE *** private static Move getRandomMove(int[][] board, int boardSize) { boolean legalMove = false; Move randomMove = null; while (!legalMove) { randomMove = randomMove(boardSize); legalMove = isLegalMove(randomMove, boardSize) && board[randomMove.i - 1][randomMove.j - 1] == 0; } return randomMove; } private static Move randomMove(int boardSize) { int x = (int) (boardSize * Math.random() + 1); int y = (int) (boardSize * Math.random() + 1); return new Move(x, y); } private static boolean isLegalMove(Move move, int boardSize) { return (move.i >= 1) && (move.i <= boardSize) && (move.j >= 1) && (move.j <= boardSize); } private static void printIntro() { System.out.println(" GOMOKO"); System.out.println(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println("\n\n"); System.out.println("WELCOME TO THE ORIENTAL GAME OF GOMOKO."); System.out.println("\n"); System.out.println("THE GAME IS PLAYED ON AN N BY N GRID OF A SIZE"); System.out.println("THAT YOU SPECIFY. DURING YOUR PLAY, YOU MAY COVER ONE GRID"); System.out.println("INTERSECTION WITH A MARKER. THE OBJECT OF THE GAME IS TO GET"); System.out.println("5 ADJACENT MARKERS IN A ROW -- HORIZONTALLY, VERTICALLY, OR"); System.out.println("DIAGONALLY. ON THE BOARD DIAGRAM, YOUR MOVES ARE MARKED"); System.out.println("WITH A '1' AND THE COMPUTER MOVES WITH A '2'."); System.out.println("\nTHE COMPUTER DOES NOT KEEP TRACK OF WHO HAS WON."); System.out.println("TO END THE GAME, TYPE -1,-1 FOR YOUR MOVE.\n "); } private static int readBoardSize(Scanner scan) { System.out.print("WHAT IS YOUR BOARD SIZE (MIN 7/ MAX 19)? "); boolean validInput = false; int input = 0; while (!validInput) { try { input = scan.nextInt(); if (input < MIN_BOARD_SIZE || input > MAX_BOARD_SIZE) { System.out.printf("I SAID, THE MINIMUM IS %s, THE MAXIMUM IS %s.\n", MIN_BOARD_SIZE, MAX_BOARD_SIZE); } else { validInput = true; } } catch (InputMismatchException ex) { System.out.println("!NUMBER EXPECTED - RETRY INPUT LINE\n"); validInput = false; } finally { scan.nextLine(); } } return input; } private static Move readMove(Scanner scan) { System.out.print("YOUR PLAY (I,J)? "); boolean validInput = false; Move move = new Move(); while (!validInput) { String input = scan.nextLine(); final String[] split = input.split(","); try { move.i = Integer.parseInt(split[0]); move.j = Integer.parseInt(split[1]); validInput = true; } catch (NumberFormatException nfe) { System.out.println("!NUMBER EXPECTED - RETRY INPUT LINE\n? "); } } return move; } private static class Move { int i; int j; public Move() { } public Move(int i, int j) { this.i = i; this.j = j; } @Override public String toString() { return "Move{" + "i=" + i + ", j=" + j + '}'; } } } ================================================ FILE: 40_Gomoko/java/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Oracle Java](https://openjdk.java.net/) ================================================ FILE: 40_Gomoko/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 40_Gomoko/javascript/gomoko.html ================================================ GOMOKO

    
    
    
    
    
    
    ================================================
    FILE: 40_Gomoko/javascript/gomoko.js
    ================================================
    // GOMOKO
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    function reset_stats()
    {
        for (var j = 1; j <= 4; j++)
            f[j] = 0;
    }
    
    var a = [];
    var x;
    var y;
    var n;
    
    // *** PRINT THE BOARD ***
    function print_board()
    {
        for (i = 1; i <= n; i++) {
            for (j = 1; j <= n; j++) {
                print(" " + a[i][j] + " ");
            }
            print("\n");
        }
        print("\n");
    }
    
    // Is valid the movement
    function is_valid()
    {
        if (x < 1 || x > n || y < 1 || y > n)
            return false;
        return true;
    }
    
    // Main program
    async function main()
    {
        print(tab(33) + "GOMOKO\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        for (i = 0; i <= 19; i++) {
            a[i] = [];
            for (j = 0; j <= 19; j++)
                a[i][j] = 0;
        }
        print("WELCOME TO THE ORIENTAL GAME OF GOMOKO.\n");
        print("\n");
        print("THE GAME IS PLAYED ON AN N BY N GRID OF A SIZE\n");
        print("THAT YOU SPECIFY.  DURING YOUR PLAY, YOU MAY COVER ONE GRID\n");
        print("INTERSECTION WITH A MARKER. THE OBJECT OF THE GAME IS TO GET\n");
        print("5 ADJACENT MARKERS IN A ROW -- HORIZONTALLY, VERTICALLY, OR\n");
        print("DIAGONALLY.  ON THE BOARD DIAGRAM, YOUR MOVES ARE MARKED\n");
        print("WITH A '1' AND THE COMPUTER MOVES WITH A '2'.\n");
        print("\n");
        print("THE COMPUTER DOES NOT KEEP TRACK OF WHO HAS WON.\n");
        print("TO END THE GAME, TYPE -1,-1 FOR YOUR MOVE.\n");
        print("\n");
        while (1) {
            print("WHAT IS YOUR BOARD SIZE (MIN 7/ MAX 19)");
            while (1) {
                n = parseInt(await input());
                if (n >= 7 && n<= 19)
                    break;
                print("I SAID, THE MINIMUM IS 7, THE MAXIMUM IS 19.\n");
            }
            for (i = 1; i <= n; i++) {
                for (j = 1; j <= n; j++) {
                    a[i][j] = 0;
                }
            }
            print("\n");
            print("WE ALTERNATE MOVES.  YOU GO FIRST...\n");
            print("\n");
            while (1) {
                print("YOUR PLAY (I,J)");
                str = await input();
                i = parseInt(str);
                j = parseInt(str.substr(str.indexOf(",") + 1));
                print("\n");
                if (i == -1)
                    break;
                x = i;
                y = j;
                if (!is_valid()) {
                    print("ILLEGAL MOVE.  TRY AGAIN...\n");
                    continue;
                }
                if (a[i][j] != 0) {
                    print("SQUARE OCCUPIED.  TRY AGAIN...\n");
                    continue;
                }
                a[i][j] = 1;
                // *** Computer tries an intelligent move ***
                found = false;
                for (e = -1; e <= 1; e++) {
                    for (f = -1; f <= 1; f++) {
                        if (e + f - e * f == 0)
                            continue;
                        x = i + f;
                        y = j + f;
                        if (!is_valid())
                            continue;
                        if (a[x][y] == 1) {
                            x = i - e;
                            y = j - f;
                            if (is_valid() || a[x][y] == 0)
                                found = true;
                            break;
                        }
                    }
                }
                if (!found) {
                    // *** Computer tries a random move ***
                    do {
                        x = Math.floor(n * Math.random() + 1);
                        y = Math.floor(n * Math.random() + 1);
                    } while (!is_valid() || a[x][y] != 0) ;
                }
                a[x][y] = 2;
                print_board();
            }
            print("\n");
            print("THANKS FOR THE GAME!!\n");
            print("PLAY AGAIN (1 FOR YES, 0 FOR NO)");
            q = parseInt(await input());
            if (q != 1)
                break;
        }
    }
    
    main();
    
    
    ================================================
    FILE: 40_Gomoko/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 40_Gomoko/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 40_Gomoko/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 40_Gomoko/perl/gomoko.pl
    ================================================
    #!/usr/bin/perl
    use strict;
    
    
    print ' 'x 33 . "GOMOKO\n";
    print ' 'x 15 . "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n";
    print "\n"; print "\n"; print "\n";
    #my @A;
    print "WELCOME TO THE ORIENTAL GAME OF GOMOKO.\n";
    print "\n"; print "THE GAME IS PLAYED ON AN N BY N GRID OF A SIZE\n";
    print "THAT YOU SPECIFY. DURING YOUR PLAY, YOU MAY COVER ONE GRID\n";
    print "INTERSECTION WITH A MARKER. THE OBJECT OF THE GAME IS TO GET\n";
    print "5 ADJACENT MARKERS IN A ROW -- HORIZONTALLY, VERTICALLY, OR\n";
    print "DIAGONALLY. ON THE BOARD DIAGRAM, YOUR MOVES ARE MARKED\n";
    print "WITH A '1' AND THE COMPUTER MOVES WITH A '2'.\n";
    print "\n"; print "THE COMPUTER DOES NOT KEEP TRACK OF WHO HAS WON.\n";
    print "TO END THE GAME, TYPE -1,-1 FOR YOUR MOVE.\n"; print "\n";
    
    
    my $Ret;
    my $I;
    my $J;
    
    my @Board;
    my $Size= 0;
    
    
    while (1) {
    
    	do {
    		$Size= 0;
    		print "WHAT IS YOUR BOARD SIZE (MIN 7/ MAX 19)"; print "? "; chomp($Size = uc());
    		if ($Size<7 || $Size>19) {
    			$Size=0;
    			print "I SAID, THE MINIMUM IS 7, THE MAXIMUM IS 19.\n";
    			}
    		} until ($Size);
    
    	#==> Reset Board to zeroes...
    	for (my $I=1; $I<=$Size; $I++) {
    		for (my $J=1; $J<=$Size; $J++) {
    			$Board[$I][$J]= 0;
    			}
    		}
    
    	print "\n"; print "WE ALTERNATE MOVES. YOU GO FIRST...\n"; print "\n";
    
    	while (1) {
    		do {
    			print "YOUR PLAY (I,J)"; print "? "; chomp(my $Inp = uc());
    			($I, $J)= split(",", $Inp);
    			print "\n";
    			if ($I==-1) { last; }
    			$Ret= &ValidMove($I, $J, 1);
    			} until ($Ret==1);
    		if ($I==-1) { last; }
    		$Board[$I][$J]= 1;
    
    		my $X;
    		my $Y;
    		my $Found=0;
    		# REM *** COMPUTER TRIES AN INTELLIGENT MOVE ***
    		#==> Too complex, original basic code seems only move below user.
    		$Ret= &ValidMove($I+1, $J);
    		if ($Ret==1) {
    			$Found=1;
    			$X= $I+1;
    			$Y= $J;
    			}
    
    		while($Found==0) {
    		# REM *** COMPUTER TRIES A RANDOM MOVE ***
    			$X= int($Size*rand(1)+1);
    			$Y= int($Size*rand(1)+1);
    			$Ret= &ValidMove($X, $Y, 2);
    			if ($Ret==1) { $Found= 1; }
    			};
    		$Board[$X][$Y]=2;
    
    		&ShowBoard();
    		}
    
    	print "\n"; print "THANKS FOR THE GAME!!\n";
    	print "PLAY AGAIN (1 FOR YES, 0 FOR NO)"; print "? "; chomp(my $Q = uc());
    	if ($Q==0) { last; }
    	}
    
    
    
    exit;
    
    
    sub ShowBoard {
    	for (my $I=1; $I<=$Size; $I++) {
    		print " ";
    		for (my $J=1; $J<=$Size; $J++) {
    			print "$Board[$I][$J]  ";
    			}
    		print "\n";
    		}
    	print "\n";
    	return;
    	}
    
    
    sub ValidMove {
    	my ($X, $Y, $Val)= @_;
    	if ($X<1 || $X>$Size || $Y<1 || $Y>$Size) {
    		if ($Val==1) { print "ILLEGAL MOVE. TRY AGAIN...\n"; }
    		return 0;
    		}
    	if ($Board[$X][$Y]!=0) {
    		if ($Val==1) { print "SQUARE OCCUPIED. TRY AGAIN...\n"; }
    		return 0;
    		}
    
    	#$Board[$X][$Y]= $Val;
    	return 1;
    	}
    
    
    ================================================
    FILE: 40_Gomoko/python/Gomoko.py
    ================================================
    import random
    from typing import Any, List, Tuple
    
    
    def print_board(A: List[List[Any]], n: int) -> None:
        """PRINT THE BOARD"""
        for i in range(n):
            print(" ", end="")
            for j in range(n):
                print(A[i][j], end="")
                print(" ", end="")
            print()
    
    
    def check_move(_I, _J, _N) -> bool:  # 910
        return _I >= 1 and _I <= _N and _J >= 1 and _J <= _N
    
    
    def print_banner() -> None:
        print(" " * 33 + "GOMOKU")
        print(" " * 15 + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n\n\n")
        print("WELCOME TO THE ORIENTAL GAME OF GOMOKO.\n")
        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'.\n")
        print("THE COMPUTER DOES NOT KEEP TRACK OF WHO HAS WON.")
        print("TO END THE GAME, TYPE -1,-1 FOR YOUR MOVE.\n")
    
    
    def get_board_dimensions() -> int:
        n = 0
        while True:
            n = int(input("WHAT IS YOUR BOARD SIZE (MIN 7/ MAX 19)? "))
            if n >= 7 and n <= 19:
                break
            print("I SAID, THE MINIMUM IS 7, THE MAXIMUM IS 19.")
            print()
        return n
    
    
    def get_move() -> Tuple[int, int]:
        while True:
            xy = input("YOUR PLAY (I,J)? ")
            print()
            x_str, y_str = xy.split(",")
            try:
                x = int(x_str)
                y = int(y_str)
            except Exception:
                print("ILLEGAL MOVE.  TRY AGAIN...")
                continue
            return x, y
    
    
    def initialize_board(n: int) -> List[List[int]]:
        # Initialize the board
        board = []
        for _x in range(n):
            sub_a = [0 for _y in range(n)]
            board.append(sub_a)
        return board
    
    
    def main() -> None:
        print_banner()
    
        while True:
            n = get_board_dimensions()
            board = initialize_board(n)
    
            print()
            print()
            print("WE ALTERNATE MOVES. YOU GO FIRST...")
            print()
    
            while True:
                x, y = get_move()
                if x == -1:
                    break
                elif not check_move(x, y, n):
                    print("ILLEGAL MOVE.  TRY AGAIN...")
                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)? "))
            if repeat == 0:
                break
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 40_Gomoko/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 40_Gomoko/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 40_Gomoko/vbnet/Gomoko.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Gomoko", "Gomoko.vbproj", "{AEA608B9-083C-4EE7-9DE7-A0AB4FE313A8}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{AEA608B9-083C-4EE7-9DE7-A0AB4FE313A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{AEA608B9-083C-4EE7-9DE7-A0AB4FE313A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{AEA608B9-083C-4EE7-9DE7-A0AB4FE313A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{AEA608B9-083C-4EE7-9DE7-A0AB4FE313A8}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 40_Gomoko/vbnet/Gomoko.vbproj
    ================================================
    
      
        Exe
        Gomoko
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 40_Gomoko/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 41_Guess/README.md
    ================================================
    ### Guess
    
    In Program GUESS, the computer chooses a random integer between 0 and any limit you set. You must then try to guess the number the computer has chosen using the clues provided by the computer.
    
    You should be able to guess the number in one less than the number of digits needed to represent the number in binary notation — i.e., in base 2. This ought to give you a clue as to the optimum search technique.
    
    GUESS converted from the original program in FOCAL which appeared in the book “Computers in the Classroom” by Walt Koetke of Lexington High School, Lexington, Massachusetts.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=75)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=90)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 41_Guess/csharp/Game.cs
    ================================================
    namespace Guess;
    
    internal class Game
    {
        private readonly IReadWrite _io;
        private readonly IRandom _random;
    
        public Game(IReadWrite io, IRandom random)
        {
            _io = io;
            _random = random;
        }
    
        public void Play()
        {
            while (true)
            {
                _io.Write(Streams.Introduction);
    
                var limit = _io.ReadNumber(Prompts.Limit);
                _io.WriteLine();
    
                // There's a bug here that exists in the original code. 
                // If the limit entered is <= 0 then the program will crash.
                var targetGuessCount = checked((int)Math.Log2(limit) + 1);
    
                PlayGuessingRounds(limit, targetGuessCount);
    
                _io.Write(Streams.BlankLines);
            }
        }
    
        private void PlayGuessingRounds(float limit, int targetGuessCount)
        {
            while (true)
            {
                _io.WriteLine(Formats.Thinking, limit);
    
                // There's a bug here that exists in the original code. If a non-integer is entered as the limit
                // then it's possible for the secret number to be the next integer greater than the limit.
                var secretNumber = (int)_random.NextFloat(limit) + 1;
    
                var guessCount = 0;
    
                while (true)
                {
                    var guess = _io.ReadNumber("");
                    if (guess <= 0) { return; }
                    guessCount++;
                    if (IsGuessCorrect(guess, secretNumber)) { break; }
                }
    
                ReportResult(guessCount, targetGuessCount);
    
                _io.Write(Streams.BlankLines);
            }
        }
    
        private bool IsGuessCorrect(float guess, int secretNumber)
        {
            if (guess < secretNumber) { _io.Write(Streams.TooLow); }
            if (guess > secretNumber) { _io.Write(Streams.TooHigh); }
    
            return guess == secretNumber;
        }
    
        private void ReportResult(int guessCount, int targetGuessCount)
        {
            _io.WriteLine(Formats.ThatsIt, guessCount);
            _io.WriteLine(
                (guessCount - targetGuessCount) switch
                {
                    < 0 => Strings.VeryGood,
                    0 => Strings.Good,
                    > 0 => string.Format(Formats.ShouldHave, targetGuessCount)
                });
        }
    }
    
    ================================================
    FILE: 41_Guess/csharp/Guess.csproj
    ================================================
    
      
        Exe
        net6.0
        10
        enable
        enable
      
    
      
        
      
    
      
        
      
    
    
    
    ================================================
    FILE: 41_Guess/csharp/Guess.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Guess", "Guess.csproj", "{0D116201-33C0-4C86-A8D5-FF7C9CCC638F}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{0D116201-33C0-4C86-A8D5-FF7C9CCC638F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{0D116201-33C0-4C86-A8D5-FF7C9CCC638F}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{0D116201-33C0-4C86-A8D5-FF7C9CCC638F}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{0D116201-33C0-4C86-A8D5-FF7C9CCC638F}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 41_Guess/csharp/Program.cs
    ================================================
    global using Games.Common.IO;
    global using Games.Common.Randomness;
    global using static Guess.Resources.Resource;  
    
    using Guess;
    
    new Game(new ConsoleIO(), new RandomNumberGenerator()).Play();
    
    
    ================================================
    FILE: 41_Guess/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 41_Guess/csharp/Resources/BlankLines.txt
    ================================================
    
    
    
    
    
    
    
    ================================================
    FILE: 41_Guess/csharp/Resources/Good.txt
    ================================================
    Good.
    
    ================================================
    FILE: 41_Guess/csharp/Resources/Introduction.txt
    ================================================
                                     Guess
                   Creative Computing  Morristown, New Jersey
    
    
    
    This is a number guessing game. I'll think
    of a number between 1 and any limit you want.
    The you have to guess what it is.
    
    
    
    ================================================
    FILE: 41_Guess/csharp/Resources/Limit.txt
    ================================================
    What limit do you want
    
    ================================================
    FILE: 41_Guess/csharp/Resources/Resource.cs
    ================================================
    using System.Reflection;
    using System.Runtime.CompilerServices;
    
    namespace Guess.Resources;
    
    internal static class Resource
    {
        internal static class Streams
        {
            public static Stream Introduction => GetStream();
            public static Stream TooLow => GetStream();
            public static Stream TooHigh => GetStream();
            public static Stream BlankLines => GetStream();
        }
    
        internal static class Formats
        {
            public static string Thinking => GetString();
            public static string ThatsIt => GetString();
            public static string ShouldHave => GetString();
        }
    
        internal static class Prompts
        {
            public static string Limit => GetString();
        }
    
        internal static class Strings
        {
            public static string Good => GetString();
            public static string VeryGood => GetString();
        }
    
        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}'.");
    }
    
    ================================================
    FILE: 41_Guess/csharp/Resources/ShouldHave.txt
    ================================================
    You should have been able to get it in only {0}
    
    ================================================
    FILE: 41_Guess/csharp/Resources/ThatsIt.txt
    ================================================
    That's it! You got it in {0} tries.
    
    ================================================
    FILE: 41_Guess/csharp/Resources/Thinking.txt
    ================================================
    I'm thinking of a number between 1 and {0}
    Now you try to guess what it is.
    
    ================================================
    FILE: 41_Guess/csharp/Resources/TooHigh.txt
    ================================================
    Too high. Try a smaller answer.
    
    ================================================
    FILE: 41_Guess/csharp/Resources/TooLow.txt
    ================================================
    Too low. Try a bigger answer.
    
    ================================================
    FILE: 41_Guess/csharp/Resources/VeryGood.txt
    ================================================
    Very good.
    
    ================================================
    FILE: 41_Guess/guess.bas
    ================================================
    1 PRINT TAB(33);"GUESS"
    2 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    3 PRINT:PRINT:PRINT
    4 PRINT "THIS IS A NUMBER GUESSING GAME. I'LL THINK"
    5 PRINT "OF A NUMBER BETWEEN 1 AND ANY LIMIT YOU WANT."
    6 PRINT "THEN YOU HAVE TO GUESS WHAT IT IS."
    7 PRINT
    8 PRINT "WHAT LIMIT DO YOU WANT";
    9 INPUT L
    10 PRINT
    11 L1=INT(LOG(L)/LOG(2))+1
    12 PRINT "I'M THINKING OF A NUMBER BETWEEN 1 AND";L
    13 G=1
    14 PRINT "NOW YOU TRY TO GUESS WHAT IT IS."
    15 M=INT(L*RND(1)+1)
    20 INPUT N
    21 IF N>0 THEN 25
    22 GOSUB 70
    23 GOTO 1
    25 IF N=M THEN 50
    30 G=G+1
    31 IF N>M THEN 40
    32 PRINT "TOO LOW. TRY A BIGGER ANSWER."
    33 GOTO 20
    40 PRINT "TOO HIGH. TRY A SMALLER ANSWER."
    42 GOTO 20
    50 PRINT "THAT'S IT! YOU GOT IT IN";G;"TRIES."
    52 IF G
     * Based on the Basic game of Guess here
     * https://github.com/coding-horror/basic-computer-games/blob/main/41%20Guess/guess.bas
     * 

    * Note: The idea was to create a version of the 1970's Basic game in Java, without introducing * new features - no additional text, error checking, etc has been added. */ public class Guess { // Used for keyboard input private final Scanner kbScanner; private enum GAME_STATE { STARTUP, INPUT_RANGE, DEFINE_COMPUTERS_NUMBER, GUESS, GAME_OVER } // Current game state private GAME_STATE gameState; // User supplied maximum number to guess private int limit; // Computers calculated number for the player to guess private int computersNumber; // Number of turns the player has had guessing private int tries; // Optimal number of turns it should take to guess private int calculatedTurns; public Guess() { kbScanner = new Scanner(System.in); gameState = GAME_STATE.STARTUP; } /** * Main game loop */ public void play() { do { switch (gameState) { case STARTUP: intro(); gameState = GAME_STATE.INPUT_RANGE; break; case INPUT_RANGE: limit = displayTextAndGetNumber("WHAT LIMIT DO YOU WANT? "); calculatedTurns = (int) (Math.log(limit) / Math.log(2)) + 1; gameState = GAME_STATE.DEFINE_COMPUTERS_NUMBER; break; case DEFINE_COMPUTERS_NUMBER: tries = 1; System.out.println("I'M THINKING OF A NUMBER BETWEEN 1 AND " + limit); computersNumber = (int) (Math.random() * limit + 1); gameState = GAME_STATE.GUESS; break; case GUESS: int playersGuess = displayTextAndGetNumber("NOW YOU TRY TO GUESS WHAT IT IS "); // Allow player to restart game with entry of 0 if (playersGuess == 0) { linePadding(); gameState = GAME_STATE.STARTUP; break; } if (playersGuess == computersNumber) { System.out.println("THAT'S IT! YOU GOT IT IN " + tries + " TRIES."); if (tries < calculatedTurns) { System.out.println("VERY "); } System.out.println("GOOD."); System.out.println("YOU SHOULD HAVE BEEN ABLE TO GET IT IN ONLY " + calculatedTurns); linePadding(); gameState = GAME_STATE.DEFINE_COMPUTERS_NUMBER; break; } else if (playersGuess < computersNumber) { System.out.println("TOO LOW. TRY A BIGGER ANSWER."); } else { System.out.println("TOO HIGH. TRY A SMALLER ANSWER."); } tries++; break; } } while (gameState != GAME_STATE.GAME_OVER); } private void intro() { System.out.println(simulateTabs(33) + "GUESS"); System.out.println(simulateTabs(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println(); System.out.println("THIS IS A NUMBER GUESSING GAME. I'LL THINK"); System.out.println("OF A NUMBER BETWEEN 1 AND ANY LIMIT YOU WANT."); System.out.println("THEN YOU HAVE TO GUESS WHAT IT IS."); } /** * Print a predefined number of blank lines * */ private void linePadding() { for (int i = 1; i <= 5; i++) { System.out.println(); } } /* * Print a message on the screen, then accept input from Keyboard. * Converts input to an Integer * * @param text message to be displayed on screen. * @return what was typed by the player. */ private int displayTextAndGetNumber(String text) { return Integer.parseInt(displayTextAndGetInput(text)); } /* * Print a message on the screen, then accept input from Keyboard. * * @param text message to be displayed on screen. * @return what was typed by the player. */ private String displayTextAndGetInput(String text) { System.out.print(text); return kbScanner.next(); } /** * Simulate the old basic tab(xx) command which indented text by xx spaces. * * @param spaces number of spaces required * @return String with number of spaces */ private String simulateTabs(int spaces) { char[] spacesTemp = new char[spaces]; Arrays.fill(spacesTemp, ' '); return new String(spacesTemp); } } ================================================ FILE: 41_Guess/java/src/GuessGame.java ================================================ public class GuessGame { public static void main(String[] args) { Guess guess = new Guess(); guess.play(); } } ================================================ FILE: 41_Guess/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 41_Guess/javascript/guess.html ================================================ GUESS

    
    
    
    
    
    
    ================================================
    FILE: 41_Guess/javascript/guess.js
    ================================================
    // GUESS
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    function make_space()
    {
        for (h = 1; h <= 5; h++)
            print("\n");
    }
    
    // Main control section
    async function main()
    {
        while (1) {
            print(tab(33) + "GUESS\n");
            print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
            print("\n");
            print("\n");
            print("\n");
            print("THIS IS A NUMBER GUESSING GAME. I'LL THINK\n");
            print("OF A NUMBER BETWEEN 1 AND ANY LIMIT YOU WANT.\n");
            print("THEN YOU HAVE TO GUESS WHAT IT IS.\n");
            print("\n");
    
            print("WHAT LIMIT DO YOU WANT");
            l = parseInt(await input());
            print("\n");
            l1 = Math.floor(Math.log(l) / Math.log(2)) + 1;
            while (1) {
                print("I'M THINKING OF A NUMBER BETWEEN 1 AND " + l + "\n");
                g = 1;
                print("NOW YOU TRY TO GUESS WHAT IT IS.\n");
                m = Math.floor(l * Math.random() + 1);
                while (1) {
                    n = parseInt(await input());
                    if (n <= 0) {
                        make_space();
                        break;
                    }
                    if (n == m) {
                        print("THAT'S IT! YOU GOT IT IN " + g + " TRIES.\n");
                        if (g == l1) {
                            print("GOOD.\n");
                        } else if (g < l1) {
                            print("VERY GOOD.\n");
                        } else {
                            print("YOU SHOULD HAVE BEEN TO GET IT IN ONLY " + l1 + "\n");
                        }
                        make_space();
                        break;
                    }
                    g++;
                    if (n > m)
                        print("TOO HIGH. TRY A SMALLER ANSWER.\n");
                    else
                        print("TOO LOW. TRY A BIGGER ANSWER.\n");
                }
                if (n <= 0)
                    break;
            }
        }
    }
    
    main();
    
    
    ================================================
    FILE: 41_Guess/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 41_Guess/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 41_Guess/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 41_Guess/perl/guess.pl
    ================================================
    #!/usr/bin/perl
    use strict;
    
    my $L1;
    while (1) {
    	print ' 'x 33 . "GUESS\n";
    	print ' 'x 15 . "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n";
    	print "\n"; print "\n"; print "\n";
    	print "THIS IS A NUMBER GUESSING GAME. I'LL THINK\n";
    	print "OF A NUMBER BETWEEN 1 AND ANY LIMIT YOU WANT.\n";
    	print "THEN YOU HAVE TO GUESS WHAT IT IS.\n";
    	print "\n";
    	print "WHAT LIMIT DO YOU WANT";
    	print "? "; chomp(my $L = );
    	print "\n";
    	$L1= int(log($L)/log(2))+1;
    
    	while (1) {
    		print "I'M THINKING OF A NUMBER BETWEEN 1 AND $L\n";
    		my $G=0;
    		print "NOW YOU TRY TO GUESS WHAT IT IS.\n";
    		my $M=int($L*rand(1)+1);
    		my $N=0;
    		while (1) {
    			while (1) {
    				print "? "; chomp($N = );
    				if ($N>0) { last; }
    				}
    			$G=$G+1;
    			if ($N==$M) { last; }
    			if ($N>$M) { print "TOO HIGH. TRY A SMALLER ANSWER.\n"; }
    				else { print "TOO LOW. TRY A BIGGER ANSWER.\n"; }
    			}
    		print "THAT'S IT! YOU GOT IT IN $G TRIES.\n";
    		if ($G<$L1) { print "VERY "; }
    		if ($G<=$L1) { print "GOOD.\n"; }
    		if ($G>$L1) { print "YOU SHOULD HAVE BEEN ABLE TO GET IT IN ONLY $L1\n"; }
    		&ENTERS();
    		}
    	}
    
    exit;
    
    
    sub ENTERS { #GOSUB 70
    	for (my $H=1; $H<=5; $H++) {
    		print "\n";
    		}
    	return;
    	}
    
    
    ================================================
    FILE: 41_Guess/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 41_Guess/python/guess.py
    ================================================
    """
    Guess
    
    From: Basic Computer Games (1978)
    
     "In program Guess, the computer  chooses a random
      integer between 0 and any limit and any limit you
      set. You must then try to guess the number the
      computer has choosen using the clues provideed by
      the computer.
       You should be able to guess the number in one less
      than the number of digits needed to  represent the
      number in binary notation - i.e. in base 2. This ought
      to give you a clue as to the optimum search technique.
       Guess converted from the original program in FOCAL
      which appeared in the book "Computers in the Classroom"
      by Walt Koetke of Lexington High School, Lexington,
      Massaschusetts.
    """
    
    # Altough the introduction says that the computer chooses
    # a number between 0 and any limit, it actually chooses
    # a number between 1 and any limit. This due to the fact that
    # for computing the number of digits the limit has in binary
    # representation, it has to use log.
    
    from math import log
    from random import random
    from typing import Tuple
    
    
    def insert_whitespaces() -> None:
        print("\n\n\n\n\n")
    
    
    def limit_set() -> Tuple[int, int]:
        print("                   Guess")
        print("Creative Computing  Morristown, New Jersey")
        print("\n\n\n")
        print("This is a number guessing game. I'll think")
        print("of a number between 1 and any limit you want.\n")
        print("Then you have to guess what it is\n")
        print("What limit do you want?")
    
        limit = int(input())
    
        while limit <= 0:
            print("Please insert a number greater or equal to 1")
            limit = int(input())
    
        # limit_goal = Number of digits "limit" in binary has
        limit_goal = int((log(limit) / log(2)) + 1)
    
        return limit, limit_goal
    
    
    def main() -> None:
        limit, limit_goal = limit_set()
        while True:
            guess_count = 1
            still_guessing = True
            won = False
            my_guess = int(limit * random() + 1)
    
            print(f"I'm thinking of a number between 1 and {limit}")
            print("Now you try to guess what it is.")
    
            while still_guessing:
                n = int(input())
    
                if n < 0:
                    break
    
                insert_whitespaces()
                if n < my_guess:
                    print("Too low. Try a bigger answer")
                    guess_count += 1
                elif n > my_guess:
                    print("Too high. Try a smaller answer")
                    guess_count += 1
                else:
                    print(f"That's it! You got it in {guess_count} tries")
                    won = True
                    still_guessing = False
    
            if won:
                if guess_count < limit_goal:
                    print("Very good.")
                elif guess_count == limit_goal:
                    print("Good.")
                else:
                    print(f"You should have been able to get it in only {limit_goal}")
                insert_whitespaces()
            else:
                insert_whitespaces()
                limit, limit_goal = limit_set()
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 41_Guess/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 41_Guess/ruby/guess.rb
    ================================================
    def print_intro
      print " " * 31 + "GUESS\n"
      print " " * 15 + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n"
      print "THIS IS A NUMBER GUESSING GAME. I'LL THINK\nOF A NUMBER BETWEEN 1 AND ANY LIMIT YOU WANT.\nTHEN YOU HAVE TO GUESS WHAT IT IS.\n"
    end
    
    def game_play(limit,choice_limit)
        random = rand(limit.to_i)+1
        puts "I'M THINKING OF A NUMBER BETWEEN 1 and #{limit}"
        puts "NOW YOU TRY TO GUESS WHAT IT IS."
        print "? "
        ans=0
        guesses=0
        until ans.to_i == random.to_i
          ans = gets.chomp
          guesses += 1
          if ans.to_i > random.to_i
            puts "TOO HIGH. TRY A SMALLER ANSWER."
            print "? "
          elsif ans.to_i < random.to_i
            puts "TOO LOW. TRY A BIGGER ANSWER."
            print "? "
          elsif ans.to_i == random.to_i
            puts "THAT'S IT! YOU GOT IT IN #{guesses} TRIES."
            if guesses.to_i < choice_limit.to_i
              puts "VERY GOOD."
            elsif guesses.to_i == choice_limit.to_i
              puts "GOOD."
            else
              puts "YOU SHOULD HAVE BEEN ABLE TO GET IT IN ONLY #{choice_limit}"
            end
            print "\n\n\n\n\n"
          end
        end
    end
    
    
    def main
      print_intro
      puts "WHAT LIMIT DO YOU WANT"
      limit = gets.chomp
      choice_limit = (Math.log(limit.to_i)/Math.log(2)+1).to_i
      while 1
        game_play(limit,choice_limit)
      end
    end
    
    main
    
    
    ================================================
    FILE: 41_Guess/rust/Cargo.toml
    ================================================
    [package]
    name = "guess"
    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.4"
    
    
    ================================================
    FILE: 41_Guess/rust/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Rust](https://www.rust-lang.org/)
    
    
    ================================================
    FILE: 41_Guess/rust/src/main.rs
    ================================================
    //#######################################################
    //
    // Guess
    //
    // From: Basic Computer Games (1978)
    //
    // "In program Guess, the computer  chooses a random
    //  integer between 0 and any limit and any limit you
    //  set. You must then try to guess the number the
    //  computer has choosen using the clues provideed by
    //  the computer.
    //   You should be able to guess the number in one less
    //  than the number of digits needed to  represent the
    //  number in binary notation - i.e. in base 2. This ought
    //  to give you a clue as to the optimum search technique.
    //   Guess converted from the original program in FOCAL
    //  which appeared in the book "Computers in the Classroom"
    //  by Walt Koetke of Lexington High School, Lexington,
    //   Massaschusetts.
    //
    //#######################################################
    
    
    use rand::Rng;
    use std::io;
    use std::cmp::Ordering;
    // Rust haven't log2 in the standard library so I added fn log_2
    const fn num_bits() -> usize { std::mem::size_of::() * 8 }
    
    fn main() {
    
        let mut rng = rand::thread_rng();
        let mut still_guessing = true;
        let limit = set_limit();
        let limit_goal = 1+(log_2(limit.try_into().unwrap())/log_2(2)) ;
        loop{
    
            let mut won = false;
            let mut guess_count = 1;
            let my_guess = rng.gen_range(1..limit);
    
            println!("I'm thinking of a number between 1 and {}",limit);
            println!("Now you try to guess what it is.");
    
            while still_guessing {
                let inp = get_input()
                    .trim()
                    .parse::().unwrap();
                println!("\n\n\n");
                if inp < my_guess {
                    println!("Too low. Try a bigger answer");
                    guess_count+=1;
                }
                else if inp > my_guess {
                    println!("Too high. Try a smaller answer");
                    guess_count+=1;
                }
                else {
                    println!("That's it! You got it in {} tries", guess_count);
                    won = true;
                    still_guessing = false;
                }
            }
            if won {
                match guess_count.cmp(&limit_goal) {
                    Ordering::Less => println!("Very good."),
                    Ordering::Equal => println!("Good."),
                    Ordering::Greater => println!("You should have been able to get it in only {}", limit_goal),
                }
    
                println!("\n\n\n");
                still_guessing = true;
            } else {
                println!("\n\n\n");
            }
        }
    }
    
    fn log_2(x:i32) -> u32 {
        assert!(x > 0);
        num_bits::() as u32 - x.leading_zeros() - 1
    }
    
    fn set_limit() -> i64 {
    
        println!("                   Guess");
        println!("\n\n\n");
        println!("This is a number guessing game. I'll think");
        println!("of a number between 1 and any limit you want.\n");
        println!("Then you have to guess what it is\n");
        println!("What limit do you want?");
    
        let inp = get_input().trim().parse::().unwrap();
    
        if inp >= 2 {
            inp
        }
        else {
            set_limit()
        }
    }
    
    fn get_input() -> String {
        let mut input = String::new();
        io::stdin()
            .read_line(&mut input)
            .expect("Your input is not correct");
        input
    }
    
    
    ================================================
    FILE: 41_Guess/vbnet/Guess.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Guess", "Guess.vbproj", "{33AB1024-1609-4163-BEAF-BA02A5D42F8A}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{33AB1024-1609-4163-BEAF-BA02A5D42F8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{33AB1024-1609-4163-BEAF-BA02A5D42F8A}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{33AB1024-1609-4163-BEAF-BA02A5D42F8A}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{33AB1024-1609-4163-BEAF-BA02A5D42F8A}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 41_Guess/vbnet/Guess.vbproj
    ================================================
    
      
        Exe
        Guess
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 41_Guess/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 42_Gunner/README.md
    ================================================
    ### Gunner
    
    GUNNER allows you to adjust the fire of a field artillery weapon to hit a stationary target. You specify the number of degrees of elevation of your weapon; 45 degrees provides maximum range with values under or over 45 degrees providing less range.
    
    You get up to five shots to destroy the enemy before he destroys you. Gun range varies between 20,000 and 60,000 yards and burst radius is 100 yards. You must specify elevation within approximately 0.2 degrees to get a hit.
    
    Tom Kloos of the Oregon Museum of Science and Industry in Portland, Oregon originally wrote GUNNER. Extensive modifications were added by David Ahl.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=77)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=92)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 42_Gunner/csharp/Gunner.csproj
    ================================================
    
    
      
        Exe
        net6.0
        enable
        enable
      
    
    
    
    
    ================================================
    FILE: 42_Gunner/csharp/Gunner.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Gunner", "Gunner.csproj", "{0279F69D-A69A-49B6-867C-78AA4F4DB962}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{0279F69D-A69A-49B6-867C-78AA4F4DB962}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{0279F69D-A69A-49B6-867C-78AA4F4DB962}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{0279F69D-A69A-49B6-867C-78AA4F4DB962}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{0279F69D-A69A-49B6-867C-78AA4F4DB962}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {09C668DA-38D4-4EF4-9FCA-EB1FF9EF6067}
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 42_Gunner/csharp/Program.cs
    ================================================
    namespace Gunner
    {
        class Program
        {
            static void Main(string[] args)
            {
                PrintIntro();
    
                string keepPlaying = "Y";
    
                while (keepPlaying == "Y") {
                    PlayGame();
                    Console.WriteLine("TRY AGAIN (Y OR N)");
                    keepPlaying = Console.ReadLine();
                }
            }
    
            static void PlayGame()
            {
                int totalAttempts = 0;
                int amountOfGames = 0;
    
                while (amountOfGames < 4) {
    
                    int maximumRange = new Random().Next(0, 40000) + 20000;
                    Console.WriteLine($"MAXIMUM RANGE OF YOUR GUN IS {maximumRange} YARDS." + Environment.NewLine + Environment.NewLine + Environment.NewLine);
    
                    int distanceToTarget = (int) (maximumRange * (0.1 + 0.8 * new Random().NextDouble()));
                    Console.WriteLine($"DISTANCE TO THE TARGET IS {distanceToTarget} YARDS.");
    
                    (bool gameWon, int attempts) = HitTheTarget(maximumRange, distanceToTarget);
    
                    if(!gameWon) {
                        Console.WriteLine(Environment.NewLine + "BOOM !!!!   YOU HAVE JUST BEEN DESTROYED" + Environment.NewLine +
                            "BY THE ENEMY." + Environment.NewLine + Environment.NewLine + Environment.NewLine
                        );
                        PrintReturnToBase();
                        break;
                    } else {
                        amountOfGames += 1;
                        totalAttempts += attempts;
    
                        Console.WriteLine($"TOTAL ROUNDS EXPENDED WERE:{totalAttempts}");
    
                        if (amountOfGames < 4) {
                            Console.WriteLine("THE FORWARD OBSERVER HAS SIGHTED MORE ENEMY ACTIVITY...");
                        } else {
                            if (totalAttempts > 18) {
                                PrintReturnToBase();
                            } else {
                                Console.WriteLine($"NICE SHOOTING !!");
                            }
                        }
                    }
                }
            }
    
            static (bool, int) HitTheTarget(int maximumRange, int distanceToTarget)
            {
                int attempts = 0;
    
                while (attempts < 6)
                {
                    int elevation = GetElevation();
    
                    int differenceBetweenTargetAndImpact = CalculateDifferenceBetweenTargetAndImpact(maximumRange, distanceToTarget, elevation);
    
                    if (Math.Abs(differenceBetweenTargetAndImpact) < 100)
                    {
                        Console.WriteLine($"*** TARGET DESTROYED *** {attempts} ROUNDS OF AMMUNITION EXPENDED.");
                        return (true, attempts);
                    }
                    else if (differenceBetweenTargetAndImpact > 100)
                    {
                        Console.WriteLine($"OVER TARGET BY {Math.Abs(differenceBetweenTargetAndImpact)} YARDS.");
                    }
                    else
                    {
                        Console.WriteLine($"SHORT OF TARGET BY {Math.Abs(differenceBetweenTargetAndImpact)} YARDS.");
                    }
    
                    attempts += 1;
                }
                return (false, attempts);
            }
    
            static int CalculateDifferenceBetweenTargetAndImpact(int maximumRange, int distanceToTarget, int elevation)
            {
                double weirdNumber = 2 * elevation / 57.3;
                double distanceShot = maximumRange * Math.Sin(weirdNumber);
                return (int)distanceShot - distanceToTarget;
            }
    
            static void PrintReturnToBase()
            {
                Console.WriteLine("BETTER GO BACK TO FORT SILL FOR REFRESHER TRAINING!");
            }
    
            static int GetElevation()
            {
                Console.WriteLine("ELEVATION");
                int elevation = int.Parse(Console.ReadLine());
                if (elevation > 89) {
                    Console.WriteLine("MAXIMUM ELEVATION IS 89 DEGREES");
                    return GetElevation();
                }
                if (elevation < 1) {
                    Console.WriteLine("MINIMUM ELEVATION IS 1 DEGREE");
                    return GetElevation();
                }
                return elevation;
            }
    
            static void PrintIntro()
            {
                Console.WriteLine(new String(' ', 30) + "GUNNER");
                Console.WriteLine(new String(' ', 15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY" + Environment.NewLine + Environment.NewLine + Environment.NewLine);
                Console.WriteLine("YOU ARE THE OFFICER-IN-CHARGE, GIVING ORDERS TO A GUN");
                Console.WriteLine("CREW, TELLING THEM THE DEGREES OF ELEVATION YOU ESTIMATE");
                Console.WriteLine("WILL PLACE A PROJECTILE ON TARGET.  A HIT WITHIN 100 YARDS");
                Console.WriteLine("OF THE TARGET WILL DESTROY IT." + Environment.NewLine);
            }
        }
    }
    
    
    ================================================
    FILE: 42_Gunner/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 42_Gunner/gunner.bas
    ================================================
    10 PRINT TAB(30);"GUNNER"
    20 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    30 PRINT:PRINT:PRINT
    130 PRINT "YOU ARE THE OFFICER-IN-CHARGE, GIVING ORDERS TO A GUN"
    140 PRINT "CREW, TELLING THEM THE DEGREES OF ELEVATION YOU ESTIMATE"
    150 PRINT "WILL PLACE A PROJECTILE ON TARGET.  A HIT WITHIN 100 YARDS"
    160 PRINT "OF THE TARGET WILL DESTROY IT." : PRINT
    170 R=INT(40000*RND(1)+20000)
    180 PRINT "MAXIMUM RANGE OF YOUR GUN IS";R;" YARDS."
    185 Z=0
    190 PRINT
    195 S1=0
    200 T=INT(R*(.1+.8*RND(1)))
    210 S=0
    220 GOTO 370
    230 PRINT "MINIMUM ELEVATION IS ONE DEGREE."
    240 GOTO 390
    250 PRINT "MAXIMUM ELEVATION IS 89 DEGREES."
    260 GOTO 390
    270 PRINT "OVER TARGET BY ";ABS(E);"YARDS."
    280 GOTO 390
    290 PRINT "SHORT OF TARGET BY "ABS(E);"YARDS."
    300 GOTO 390
    320 PRINT "*** TARGET DESTROYED ***  ";S;"ROUNDS OF AMMUNITION EXPENDED."
    325 S1=S1+S
    330 IF Z=4 THEN 490
    340 Z=Z+1
    345 PRINT
    350 PRINT "THE FORWARD OBSERVER HAS SIGHTED MORE ENEMY ACTIVITY..."
    360 GOTO 200
    370 PRINT "DISTANCE TO THE TARGET IS "T;" YARDS."
    380 PRINT
    390 PRINT
    400 INPUT "ELEVATION";B
    420 IF B>89 THEN 250
    430 IF B<1 THEN 230
    440 S=S+1
    442 IF S<6 THEN 450
    444 PRINT:PRINT "BOOM !!!!   YOU HAVE JUST BEEN DESTROYED ";
    446 PRINT "BY THE ENEMY." : PRINT : PRINT : PRINT : GOTO 495
    450 B2=2*B/57.3 : I=R*SIN(B2) : X=T-I : E=INT(X)
    460 IF ABS(E)<100 THEN 320
    470 IF E>100 THEN 290
    480 GOTO 270
    490 PRINT : PRINT : PRINT "TOTAL ROUNDS EXPENDED WERE:";S1
    492 IF S1>18 THEN 495
    493 PRINT "NICE SHOOTING !!" : GOTO 500
    495 PRINT "BETTER GO BACK TO FORT SILL FOR REFRESHER TRAINING!"
    500 PRINT : INPUT "TRY AGAIN (Y OR N)";Z$
    510 IF Z$="Y" THEN 170
    520 PRINT:PRINT "OK.  RETURN TO BASE CAMP."
    999 END
    
    
    ================================================
    FILE: 42_Gunner/java/Gunner.java
    ================================================
    import java.util.Random;
    import java.util.Scanner;
    
    public class Gunner {
    
        public static final int MAX_ROUNDS = 6;
        public static final int MAX_ENEMIES = 4;
        public static final int ERROR_DISTANCE = 100;
    
        private static Scanner scanner = new Scanner(System.in);
        private static Random random = new Random();
    
        public static void main(String[] args) {
            println("                              GUNNER");
            println("               CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
            println();
            println();
            println();
            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 WITHIN " + ERROR_DISTANCE + " YARDS");
            println("OF THE TARGET WILL DESTROY IT.");
            println();
            while (true) {
                int maxRange = random.nextInt(40000) + 20000;
                int enemyCount = 0;
                int totalRounds = 0;
                println("MAXIMUM RANGE OF YOUR GUN IS " + maxRange + " YARDS.\n");
    
                while (true) {
                    int rounds = fightEnemy(maxRange);
                    totalRounds += rounds;
    
                    if (enemyCount == MAX_ENEMIES || rounds >= MAX_ROUNDS) {
                        if (rounds < MAX_ROUNDS) {
                            println("\n\n\nTOTAL ROUNDS EXPENDED WERE:" + totalRounds);
                        }
                        if (totalRounds > 18 || rounds >= MAX_ROUNDS) {
                            println("BETTER GO BACK TO FORT SILL FOR REFRESHER TRAINING!");
                        } else {
                            println("NICE SHOOTING !!");
                        }
                        println("\nTRY AGAIN (Y OR N)");
                        String tryAgainResponse = scanner.nextLine();
                        if ("Y".equals(tryAgainResponse) || "y".equals(tryAgainResponse)) {
                            break;
                        }
                        println("\nOK.  RETURN TO BASE CAMP.");
                        return;
                    }
                    enemyCount++;
                    println("\nTHE FORWARD OBSERVER HAS SIGHTED MORE ENEMY ACTIVITY...");
                }
            }
        }
    
        private static int fightEnemy(int maxRange) {
            int rounds = 0;
            long target = Math.round(maxRange * (random.nextDouble() * 0.8 + 0.1));
            println("      DISTANCE TO THE TARGET IS " + target + " YARDS.");
    
            while (true) {
                println("\nELEVATION?");
                double elevation = Double.parseDouble(scanner.nextLine());
                if (elevation > 89.0) {
                    println("MAXIMUM ELEVATION IS 89 DEGREES.");
                    continue;
                }
                if (elevation < 1.0) {
                    println("MINIMUM ELEVATION IS ONE DEGREE.");
                    continue;
                }
                rounds++;
                if (rounds >= MAX_ROUNDS) {
                    println("\nBOOM !!!!   YOU HAVE JUST BEEN DESTROYED ");
                    println("BY THE ENEMY.\n\n\n");
                    break;
                }
    
                long error = calculateError(maxRange, target, elevation);
                if (Math.abs(error) < ERROR_DISTANCE) {
                    println("*** TARGET DESTROYED ***  " + rounds + " ROUNDS OF AMMUNITION EXPENDED.");
                    break;
                } else if (error > ERROR_DISTANCE) {
                    println("SHORT OF TARGET BY " + Math.abs(error) + " YARDS.");
                } else {
                    println("OVER TARGET BY " + Math.abs(error) + " YARDS.");
                }
    
            }
            return rounds;
        }
    
        private static long calculateError(int maxRange, long target, double elevationInDegrees) {
            double elevationInRadians = Math.PI * elevationInDegrees / 90.0; //convert degrees to radians
            double impact = maxRange * Math.sin(elevationInRadians);
            double error = target - impact;
            return Math.round(error);
        }
    
        private static void println(String s) {
            System.out.println(s);
        }
    
        private static void println() {
            System.out.println();
        }
    }
    
    
    ================================================
    FILE: 42_Gunner/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 42_Gunner/javascript/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells)
    
    
    ================================================
    FILE: 42_Gunner/javascript/gunner.html
    ================================================
    
    
    GUNNER
    
    
    
    
    
    
    
    
    
    
    ================================================
    FILE: 42_Gunner/javascript/gunner.js
    ================================================
    // GUNNER
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    print(tab(30) + "GUNNER\n");
    print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
    print("\n");
    print("\n");
    print("\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");
    print("\n");
    
    // Main control section
    async function main()
    {
        while (1) {
            r = Math.floor(40000 * Math.random() + 20000);
            print("MAXIMUM RANGE OF YOUR GUN IS " + r + " YARDS.\n");
            z = 0;
            print("\n");
            s1 = 0;
            while (1) {
                t = Math.floor(r * (0.1 + 0.8 * Math.random()));
                s = 0;
                print("DISTANCE TO THE TARGET IS " + t + " YARDS.\n");
                print("\n");
    
                while (1) {
                    print("\n");
                    print("ELEVATION");
                    b = parseFloat(await input());
                    if (b > 89) {
                        print("MAXIMUM ELEVATION IS 89 DEGREES.\n");
                        continue;
                    }
                    if (b < 1) {
                        print("MINIMUM ELEVATION IS ONE DEGREE.\n");
                        continue;
                    }
                    if (++s >= 6) {
                        print("\n");
                        print("BOOM !!!!   YOU HAVE JUST BEEN DESTROYED BY THE ENEMY.\n");
                        print("\n");
                        print("\n");
                        print("\n");
                        e = 0;
                        break;
                    }
                    b2 = 2 * b / 57.3;
                    i = r * Math.sin(b2);
                    x = t - i;
                    e = Math.floor(x);
                    if (true) { //Math.abs(e) < 100) {
                        e = 1;
                        break;
                    }
                    if (e > 100) {
                        print("SHORT OF TARGET BY " + Math.abs(e) + " YARDS.\n");
                    } else {
                        print("OVER TARGET BY " + Math.abs(e) + " YARDS.\n");
                    }
                }
                if (e == 1) {
                    print("*** TARGET DESTROYED *** " + s + " ROUNDS OF AMMUNITION EXPENDED.\n");
                    s1 += s;
                    if (z == 4) {
                        print("\n");
                        print("\n");
                        print("TOTAL ROUND EXPENDED WERE: " + s1 + "\n");
                        break;
                    } else {
                        z++;
                        print("\n");
                        print("THE FORWARD OBSERVER HAS SIGHTED MORE ENEMY ACTIVITY...\n");
                    }
                } else {
                    s1 = 19;
                    break;
                }
            }
            if (s1 > 18) {
                print("BETTER GO BACK TO FORT SILL FOR REFRESHER TRAINING!\n");
            } else {
                print("NICE SHOOTING !!");
            }
            print("\n");
            print("TRY AGAIN (Y OR N)");
            str = await input();
            if (str.substr(0, 1) != "Y")
                break;
        }
        print("\n");
        print("OK.  RETURN TO BASE CAMP.\n");
    }
    
    main();
    
    
    ================================================
    FILE: 42_Gunner/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 42_Gunner/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 42_Gunner/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 42_Gunner/perl/gunner.pl
    ================================================
    #!/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; }
        }
    }
    
    
    ================================================
    FILE: 42_Gunner/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 42_Gunner/python/gunner.py
    ================================================
    #!/usr/bin/env python3
    #
    # Ported to Python by @iamtraction
    
    from math import sin
    from random import random
    
    
    def gunner() -> None:
        gun_range = int(40000 * random() + 20000)
    
        print("\nMAXIMUM RANGE OF YOUR GUN IS", gun_range, "YARDS.")
    
        killed_enemies = 0
        S1 = 0
    
        while True:
            target_distance = int(gun_range * (0.1 + 0.8 * random()))
            shots = 0
    
            print("\nDISTANCE TO THE TARGET IS", target_distance, "YARDS.")
    
            while True:
                elevation = float(input("\n\nELEVATION? "))
    
                if elevation > 89:
                    print("MAXIMUM ELEVATION IS 89 DEGREES.")
                    continue
    
                if elevation < 1:
                    print("MINIMUM ELEVATION IS ONE DEGREE.")
                    continue
    
                shots += 1
    
                if shots < 6:
                    B2 = 2 * elevation / 57.3
                    shot_impact = gun_range * sin(B2)
                    shot_proximity = target_distance - shot_impact
                    shot_proximity_int = int(shot_proximity)
    
                    if abs(shot_proximity_int) < 100:
                        print(
                            "*** TARGET DESTROYED *** ",
                            shots,
                            "ROUNDS OF AMMUNITION EXPENDED.",
                        )
                        S1 += shots
                        if killed_enemies == 4:
                            print("\n\nTOTAL ROUNDS EXPENDED WERE: ", S1)
                            if S1 > 18:
                                print("BETTER GO BACK TO FORT SILL FOR REFRESHER TRAINING!")
                            else:
                                print("NICE SHOOTING !!")
                            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:
                        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!")
                    return
    
    
    def main() -> None:
        print(" " * 33 + "GUNNER")
        print(" " * 15 + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY")
        print("\n\n\n")
        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.")
    
        while True:
            gunner()
    
            not_again = input("TRY AGAIN (Y OR N)? ").upper() != "Y"
            if not_again:
                print("\nOK.  RETURN TO BASE CAMP.")
                break
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 42_Gunner/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 42_Gunner/rust/Cargo.toml
    ================================================
    [package]
    name = "gunner"
    version = "0.1.0"
    edition = "2024"
    
    [dependencies]
    rand = "0.10.0"
    
    
    ================================================
    FILE: 42_Gunner/rust/src/main.rs
    ================================================
    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");
        }
    }
    
    
    ================================================
    FILE: 42_Gunner/vbnet/Gunner.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Gunner", "Gunner.vbproj", "{CC347B04-B99C-4F77-BFCF-2DDFBB4A135D}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{CC347B04-B99C-4F77-BFCF-2DDFBB4A135D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{CC347B04-B99C-4F77-BFCF-2DDFBB4A135D}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{CC347B04-B99C-4F77-BFCF-2DDFBB4A135D}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{CC347B04-B99C-4F77-BFCF-2DDFBB4A135D}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 42_Gunner/vbnet/Gunner.vbproj
    ================================================
    
      
        Exe
        Gunner
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 42_Gunner/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 43_Hammurabi/README.md
    ================================================
    ### Hammurabi
    
    In this game you direct the administrator of Sumeria, Hammurabi, how to manage the city. The city initially has 1,000 acres, 100 people and 3,000 bushels of grain in storage.
    
    You may buy and sell land with your neighboring city-states for bushels of grain — the price will vary between 17 and 26 bushels per acre. You also must use grain to feed your people and as seed to plant the next year’s crop.
    
    You will quickly find that a certain number of people can only tend a certain amount of land and that people starve if they are not fed enough. You also have the unexpected to contend with such as a plague, rats destroying stored grain, and variable harvests.
    
    You will also find that managing just the few resources in this game is not a trivial job over a period of say ten years. The crisis of population density rears its head very rapidly.
    
    This program was originally written in Focal at DEC; author unknown. David Ahl converted it to BASIC and added the 10-year performance assessment. If you wish to change any of the factors, the extensive remarks in the program should make modification fairly straightforward.
    
    Note for trivia buffs: somewhere along the line an m was dropped out of the spelling of Hammurabi in hte Ahl version of the computer program. This error has spread far and wide until a generation of students now think that Hammurabi is the incorrect spelling.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=78)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=93)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    - 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
     - Rust: https://github.com/beyonddream/hamurabi.rs
    
    
    ================================================
    FILE: 43_Hammurabi/csharp/ActionResult.cs
    ================================================
    namespace Hammurabi
    {
        /// 
        /// Enumerates the different possible outcomes of attempting the various
        /// actions in the game.
        /// 
        public enum ActionResult
        {
            /// 
            /// The action was a success.
            /// 
            Success,
    
            /// 
            /// The action could not be completed because the city does not have
            /// enough bushels of grain.
            /// 
            InsufficientStores,
    
            /// 
            /// The action could not be completed because the city does not have
            /// sufficient acreage.
            /// 
            InsufficientLand,
    
            /// 
            /// The action could not be completed because the city does not have
            /// sufficient population.
            /// 
            InsufficientPopulation,
    
            /// 
            /// The requested action offended the city steward.
            /// 
            Offense
        }
    }
    
    
    ================================================
    FILE: 43_Hammurabi/csharp/Controller.cs
    ================================================
    using System;
    
    namespace Hammurabi
    {
        /// 
        /// Provides methods for reading input from the user.
        /// 
        public static class Controller
        {
            /// 
            /// Continuously prompts the user to enter a number until he or she
            /// enters a valid number and updates the game state.
            /// 
            /// 
            /// The current game state.
            /// 
            /// 
            /// Action that will display the prompt to the user.
            /// 
            /// 
            /// The rule to invoke once input is retrieved.
            /// 
            /// 
            /// The updated game state.
            /// 
            public static GameState UpdateGameState(
                GameState state,
                Action prompt,
                Func rule)
            {
                while (true)
                {
                    prompt();
    
                    if (!Int32.TryParse(Console.ReadLine(), out var amount))
                    {
                        View.ShowInvalidNumber();
                        continue;
                    }
    
                    var (newState, result) = rule(state, amount);
    
                    switch (result)
                    {
                        case ActionResult.InsufficientLand:
                            View.ShowInsufficientLand(state);
                            break;
                        case ActionResult.InsufficientPopulation:
                            View.ShowInsufficientPopulation(state);
                            break;
                        case ActionResult.InsufficientStores:
                            View.ShowInsufficientStores(state);
                            break;
                        case ActionResult.Offense:
                            // Not sure why we have to blow up the game here...
                            // Maybe this made sense in the 70's.
                            throw new GreatOffence();
                        default:
                            return newState;
                    }
                }
            }
        }
    }
    
    
    ================================================
    FILE: 43_Hammurabi/csharp/GameResult.cs
    ================================================
    namespace Hammurabi
    {
        /// 
        /// Stores the final game result.
        /// 
        public record GameResult
        {
            /// 
            /// Gets the player's performance rating.
            /// 
            public PerformanceRating Rating { get; init; }
    
            /// 
            /// Gets the number of acres in the city per person.
            /// 
            public int AcresPerPerson { get; init; }
    
            /// 
            /// Gets the number of people who starved the final year in office.
            /// 
            public int FinalStarvation { get; init; }
    
            /// 
            /// Gets the total number of people who starved.
            /// 
            public int TotalStarvation { get; init; }
    
            /// 
            /// Gets the average starvation rate per year (as a percentage
            /// of population).
            /// 
            public int AverageStarvationRate { get; init; }
    
            /// 
            /// Gets the number of people who want to assassinate the player.
            /// 
            public int Assassins { get; init; }
    
            /// 
            /// Gets a flag indicating whether the player was impeached for
            /// starving too many people.
            /// 
            public bool WasPlayerImpeached { get; init; }
        }
    }
    
    
    ================================================
    FILE: 43_Hammurabi/csharp/GameState.cs
    ================================================
    namespace Hammurabi
    {
        /// 
        /// Stores the state of the game.
        /// 
        public record GameState
        {
            /// 
            /// Gets the current game year.
            /// 
            public int Year { get; init; }
    
            /// 
            /// Gets the city's population.
            /// 
            public int Population { get; init; }
    
            /// 
            /// Gets the population increase this year.
            /// 
            public int PopulationIncrease { get; init; }
    
            /// 
            /// Gets the number of people who starved.
            /// 
            public int Starvation { get; init; }
    
            /// 
            /// Gets the city's size in acres.
            /// 
            public int Acres { get; init; }
    
            /// 
            /// Gets the price for an acre of land (in bushels).
            /// 
            public int LandPrice { get; init; }
    
            /// 
            /// Gets the number of bushels of grain in the city stores.
            /// 
            public int Stores { get; init; }
    
            /// 
            /// Gets the amount of food distributed to the people.
            /// 
            public int FoodDistributed { get; init; }
    
            /// 
            /// Gets the number of acres that were planted.
            /// 
            public int AcresPlanted { get; init; }
    
            /// 
            /// Gets the number of bushels produced per acre.
            /// 
            public int Productivity { get; init; }
    
            /// 
            /// Gets the amount of food lost to rats.
            /// 
            public int Spoilage { get; init; }
    
            /// 
            /// Gets a flag indicating whether the current year is a plague year.
            /// 
            public bool IsPlagueYear { get; init; }
    
            /// 
            /// Gets a flag indicating whether the player has been impeached.
            /// 
            public bool IsPlayerImpeached { get; init; }
        }
    }
    
    
    ================================================
    FILE: 43_Hammurabi/csharp/GreatOffence.cs
    ================================================
    using System;
    
    namespace Hammurabi
    {
        /// 
        /// Indicates that the game cannot continue due to the player's extreme
        /// incompetance and/or unserious attitude!
        /// 
        public class GreatOffence : InvalidOperationException
        {
        }
    }
    
    
    ================================================
    FILE: 43_Hammurabi/csharp/Hammurabi.csproj
    ================================================
    
    
      
        Exe
        net5.0
      
    
    
    
    
    ================================================
    FILE: 43_Hammurabi/csharp/Hammurabi.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 17
    VisualStudioVersion = 17.0.32014.148
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hammurabi", "Hammurabi.csproj", "{2C4407AF-5ED6-4C9F-833E-35461DF7DBBB}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{2C4407AF-5ED6-4C9F-833E-35461DF7DBBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{2C4407AF-5ED6-4C9F-833E-35461DF7DBBB}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{2C4407AF-5ED6-4C9F-833E-35461DF7DBBB}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{2C4407AF-5ED6-4C9F-833E-35461DF7DBBB}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {89DBE213-C0F0-4ABA-BB2D-5D9AAED41FF6}
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 43_Hammurabi/csharp/PerformanceRating.cs
    ================================================
    namespace Hammurabi
    {
        /// 
        /// Enumerates the different performance ratings that the player can
        /// achieve.
        /// 
        public enum PerformanceRating
        {
            Disgraceful,
            Bad,
            Ok,
            Terrific
        }
    }
    
    
    ================================================
    FILE: 43_Hammurabi/csharp/Program.cs
    ================================================
    using System;
    using System.Collections.Immutable;
    
    namespace Hammurabi
    {
        public static class Program
        {
            public const int GameLength = 10;
    
            public static void Main(string[] args)
            {
                var random  = new Random() ;
                var state   = Rules.BeginGame();
                var history = ImmutableList.Empty;
    
                View.ShowBanner();
    
                try
                {
                    while (!state.IsPlayerImpeached)
                    {
                        state = Rules.BeginTurn(state, random);
                        View.ShowCitySummary(state);
    
                        if (state.Year > GameLength)
                            break;
    
                        View.ShowLandPrice(state);
                        var newState = Controller.UpdateGameState(state, View.PromptBuyLand, Rules.BuyLand);
                        state = newState.Acres != state.Acres ?
                            newState : Controller.UpdateGameState(state, View.PromptSellLand, Rules.SellLand);
    
                        View.ShowSeparator();
                        state = Controller.UpdateGameState(state, View.PromptFeedPeople, Rules.FeedPeople);
    
                        View.ShowSeparator();
                        state = Controller.UpdateGameState(state, View.PromptPlantCrops, Rules.PlantCrops);
    
                        state = Rules.EndTurn(state, random);
                        history = history.Add(state);
                    }
    
                    var result = Rules.GetGameResult(history, random);
                    View.ShowGameResult(result);
                }
                catch (GreatOffence)
                {
                    View.ShowGreatOffence();
                }
    
                View.ShowFarewell();
            }
        }
    }
    
    
    ================================================
    FILE: 43_Hammurabi/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 43_Hammurabi/csharp/Rules.cs
    ================================================
    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace Hammurabi
    {
        public static class Rules
        {
            /// 
            /// Creates the initial state for a new game.
            /// 
            public static GameState BeginGame() =>
                new GameState
                {
                    Year                = 0,
                    Population          = 95,
                    PopulationIncrease  = 5,
                    Starvation          = 0,
                    Acres               = 1000,
                    Stores              = 0,
                    AcresPlanted        = 1000,
                    Productivity        = 3,
                    Spoilage            = 200,
                    IsPlagueYear        = false,
                    IsPlayerImpeached   = false
                };
    
            /// 
            /// Updates the game state to start a new turn.
            /// 
            public static GameState BeginTurn(GameState state, Random random) =>
                state with
                {
                    Year            = state.Year + 1,
                    Population      = (state.Population + state.PopulationIncrease - state.Starvation) / (state.IsPlagueYear ? 2 : 1),
                    LandPrice       = random.Next(10) + 17,
                    Stores          = state.Stores + (state.AcresPlanted * state.Productivity) - state.Spoilage,
                    AcresPlanted    = 0,
                    FoodDistributed = 0
                };
    
            /// 
            /// Attempts to purchase the given number of acres.
            /// 
            /// 
            /// The updated game state and action result.
            /// 
            public static (GameState newState, ActionResult result) BuyLand(GameState state, int amount)
            {
                var price = state.LandPrice * amount;
    
                if (price < 0)
                    return (state, ActionResult.Offense);
                else
                if (price > state.Stores)
                    return (state, ActionResult.InsufficientStores);
                else
                    return (state with { Acres = state.Acres + amount, Stores = state.Stores - price }, ActionResult.Success);
            }
    
            /// 
            /// Attempts to sell the given number of acres.
            /// 
            /// 
            /// The updated game state and action result.
            /// 
            public static (GameState newState, ActionResult result) SellLand(GameState state, int amount)
            {
                var price = state.LandPrice * amount;
    
                if (price < 0)
                    return (state, ActionResult.Offense);
                else
                if (amount >= state.Acres)
                    return (state, ActionResult.InsufficientLand);
                else
                    return (state with { Acres = state.Acres - amount, Stores = state.Stores + price }, ActionResult.Success);
            }
    
            /// 
            /// Attempts to feed the people the given number of buschels.
            /// 
            /// 
            /// 
            /// The updated game state and action result.
            /// 
            public static (GameState newState, ActionResult result) FeedPeople(GameState state, int amount)
            {
                if (amount < 0)
                    return (state, ActionResult.Offense);
                else
                if (amount > state.Stores)
                    return (state, ActionResult.InsufficientStores);
                else
                    return (state with { Stores = state.Stores - amount, FoodDistributed = state.FoodDistributed + amount }, ActionResult.Success);
            }
    
            /// 
            /// Attempts to plant crops on the given number of acres.
            /// 
            /// 
            /// The updated game state and action result.
            /// 
            public static (GameState newState, ActionResult result) PlantCrops(GameState state, int amount)
            {
                var storesRequired = amount / 2;
                var maxAcres       = state.Population * 10;
    
                if (amount < 0)
                    return (state, ActionResult.Offense);
                else
                if (amount > state.Acres)
                    return (state, ActionResult.InsufficientLand);
                else
                if (storesRequired > state.Stores)
                    return (state, ActionResult.InsufficientStores);
                else
                if ((state.AcresPlanted + amount) > maxAcres)
                    return (state, ActionResult.InsufficientPopulation);
                else
                    return (state with
                    {
                        AcresPlanted = state.AcresPlanted + amount,
                        Stores       = state.Stores - storesRequired,
                    }, ActionResult.Success);
            }
    
            /// 
            /// Ends the current turn and returns the updated game state.
            /// 
            public static GameState EndTurn(GameState state, Random random)
            {
                var productivity = random.Next(1, 6);
                var harvest = productivity * state.AcresPlanted;
    
                var spoilage = random.Next(1, 6) switch
                {
                    2 => state.Stores / 2,
                    4 => state.Stores / 4,
                    _ => 0
                };
    
                var populationIncrease= (int)((double)random.Next(1, 6) * (20 * state.Acres + state.Stores + harvest - spoilage) / state.Population / 100 + 1);
    
                var plagueYear = random.Next(20) < 3;
    
                var peopleFed  = state.FoodDistributed / 20;
                var starvation = peopleFed < state.Population ? state.Population - peopleFed : 0;
                var impeached  = starvation > state.Population * 0.45;
    
                return state with
                {
                    Productivity       = productivity,
                    Spoilage           = spoilage,
                    PopulationIncrease = populationIncrease,
                    Starvation         = starvation,
                    IsPlagueYear       = plagueYear,
                    IsPlayerImpeached  = impeached
                };
            }
    
            /// 
            /// Examines the game's history to arrive at the final result.
            /// 
            public static GameResult GetGameResult(IEnumerable history, Random random)
            {
                var (_, averageStarvationRate, totalStarvation, finalState) = history.Aggregate(
                    (count: 0, starvationRate: 0, totalStarvation: 0, finalState: default(GameState)),
                    (stats, state) =>
                    (
                        stats.count + 1,
                        ((stats.starvationRate * stats.count) + (state.Starvation * 100 / state.Population)) / (stats.count + 1),
                        stats.totalStarvation + state.Starvation,
                        state
                    ));
    
                var acresPerPerson = finalState.Acres / finalState.Population;
    
                var rating = finalState.IsPlayerImpeached ?
                    PerformanceRating.Disgraceful :
                    (averageStarvationRate, acresPerPerson) switch
                    {
                        (> 33, _) => PerformanceRating.Disgraceful,
                        (_, < 7)  => PerformanceRating.Disgraceful,
                        (> 10, _) => PerformanceRating.Bad,
                        (_, < 9)  => PerformanceRating.Bad,
                        (> 3, _)  => PerformanceRating.Ok,
                        (_, < 10) => PerformanceRating.Ok,
                        _         => PerformanceRating.Terrific
                    };
    
                var assassins = rating == PerformanceRating.Ok ?
                    random.Next(0, (int)(finalState.Population * 0.8)) : 0;
    
                return new GameResult
                {
                    Rating                = rating,
                    AcresPerPerson        = acresPerPerson,
                    FinalStarvation       = finalState.Starvation,
                    TotalStarvation       = totalStarvation,
                    AverageStarvationRate = averageStarvationRate,
                    Assassins             = assassins,
                    WasPlayerImpeached    = finalState.IsPlayerImpeached
                };
            }
        }
    }
    
    
    ================================================
    FILE: 43_Hammurabi/csharp/View.cs
    ================================================
    using System;
    
    namespace Hammurabi
    {
        /// 
        /// Provides various methods for presenting information to the user.
        /// 
        public static class View
        {
            /// 
            /// Shows the introductory banner to the player.
            /// 
            public static void ShowBanner()
            {
                Console.WriteLine("                                HAMURABI");
                Console.WriteLine("               CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine("TRY YOUR HAND AT GOVERNING ANCIENT SUMERIA");
                Console.WriteLine("FOR A TEN-YEAR TERM OF OFFICE.");
            }
    
            /// 
            /// Shows a summary of the current state of the city.
            /// 
            public static void ShowCitySummary(GameState state)
            {
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine("HAMURABI:  I BEG TO REPORT TO YOU,");
                Console.WriteLine($"IN YEAR {state.Year}, {state.Starvation} PEOPLE STARVED, {state.PopulationIncrease} CAME TO THE CITY,");
    
                if (state.IsPlagueYear)
                {
                    Console.WriteLine("A HORRIBLE PLAGUE STRUCK!  HALF THE PEOPLE DIED.");
                }
    
                Console.WriteLine($"POPULATION IS NOW {state.Population}");
                Console.WriteLine($"THE CITY NOW OWNS {state.Acres} ACRES.");
                Console.WriteLine($"YOU HARVESTED {state.Productivity} BUSHELS PER ACRE.");
                Console.WriteLine($"THE RATS ATE {state.Spoilage} BUSHELS.");
                Console.WriteLine($"YOU NOW HAVE {state.Stores} BUSHELS IN STORE.");
                Console.WriteLine();
            }
    
            /// 
            /// Shows the current cost of land.
            /// 
            /// 
            public static void ShowLandPrice(GameState state)
            {
                Console.WriteLine ($"LAND IS TRADING AT {state.LandPrice} BUSHELS PER ACRE.");
            }
    
            /// 
            /// Displays a section separator.
            /// 
            public static void ShowSeparator()
            {
                Console.WriteLine();
            }
    
            /// 
            /// Inform the player that he or she has entered an invalid number.
            /// 
            public static void ShowInvalidNumber()
            {
                Console.WriteLine("PLEASE ENTER A VALID NUMBER");
            }
    
            /// 
            /// Inform the player that he or she has insufficient acreage.
            /// 
            public static void ShowInsufficientLand(GameState state)
            {
                Console.WriteLine($"HAMURABI:  THINK AGAIN.  YOU OWN ONLY {state.Acres} ACRES.  NOW THEN,");
            }
    
            /// 
            /// Inform the player that he or she has insufficient population.
            /// 
            public static void ShowInsufficientPopulation(GameState state)
            {
                Console.WriteLine($"BUT YOU HAVE ONLY {state.Population} PEOPLE TO TEND THE FIELDS!  NOW THEN,");
            }
    
            /// 
            /// Inform the player that he or she has insufficient grain stores.
            /// 
            public static void ShowInsufficientStores(GameState state)
            {
                Console.WriteLine("HAMURABI:  THINK AGAIN.  YOU HAVE ONLY");
                Console.WriteLine($"{state.Stores} BUSHELS OF GRAIN.  NOW THEN,");
            }
    
            /// 
            /// Show the player that he or she has caused great offence.
            /// 
            public static void ShowGreatOffence()
            {
                Console.WriteLine();
                Console.WriteLine("HAMURABI:  I CANNOT DO WHAT YOU WISH.");
                Console.WriteLine("GET YOURSELF ANOTHER STEWARD!!!!!");
            }
    
            /// 
            /// Shows the game's final result to the user.
            /// 
            public static void ShowGameResult(GameResult result)
            {
                if (!result.WasPlayerImpeached)
                {
                    Console.WriteLine($"IN YOUR 10-YEAR TERM OF OFFICE, {result.AverageStarvationRate} PERCENT OF THE");
                    Console.WriteLine("POPULATION STARVED PER YEAR ON THE AVERAGE, I.E. A TOTAL OF");
                    Console.WriteLine($"{result.TotalStarvation} PEOPLE DIED!!");
    
                    Console.WriteLine("YOU STARTED WITH 10 ACRES PER PERSON AND ENDED WITH");
                    Console.WriteLine($"{result.AcresPerPerson} ACRES PER PERSON.");
                    Console.WriteLine();
                }
    
                switch (result.Rating)
                {
                    case PerformanceRating.Disgraceful:
                        if (result.WasPlayerImpeached)
                            Console.WriteLine($"YOU STARVED {result.FinalStarvation} PEOPLE IN ONE YEAR!!!");
    
                        Console.WriteLine("DUE TO THIS EXTREME MISMANAGEMENT YOU HAVE NOT ONLY");
                        Console.WriteLine("BEEN IMPEACHED AND THROWN OUT OF OFFICE BUT YOU HAVE");
                        Console.WriteLine("ALSO BEEN DECLARED NATIONAL FINK!!!!");
                        break;
                    case PerformanceRating.Bad:
                        Console.WriteLine("YOUR HEAVY-HANDED PERFORMANCE SMACKS OF NERO AND IVAN IV.");
                        Console.WriteLine("THE PEOPLE (REMAINING) FIND YOU AN UNPLEASANT RULER, AND,");
                        Console.WriteLine("FRANKLY, HATE YOUR GUTS!!");
                        break;
                    case PerformanceRating.Ok:
                        Console.WriteLine("YOUR PERFORMANCE COULD HAVE BEEN SOMEWHAT BETTER, BUT");
                        Console.WriteLine($"REALLY WASN'T TOO BAD AT ALL. {result.Assassins} PEOPLE");
                        Console.WriteLine("WOULD DEARLY LIKE TO SEE YOU ASSASSINATED BUT WE ALL HAVE OUR");
                        Console.WriteLine("TRIVIAL PROBLEMS.");
                        break;
                    case PerformanceRating.Terrific:
                        Console.WriteLine("A FANTASTIC PERFORMANCE!!!  CHARLEMANGE, DISRAELI, AND");
                        Console.WriteLine("JEFFERSON COMBINED COULD NOT HAVE DONE BETTER!");
                        break;
                }
            }
    
            /// 
            /// Shows a farewell message to the user.
            /// 
            public static void ShowFarewell()
            {
                Console.WriteLine("SO LONG FOR NOW.");
                Console.WriteLine();
            }
    
            /// 
            /// Prompts the user to buy land.
            /// 
            public static void PromptBuyLand()
            {
                Console.Write("HOW MANY ACRES DO YOU WISH TO BUY? ");
            }
    
            /// 
            /// Prompts the user to sell land.
            /// 
            public static void PromptSellLand()
            {
                Console.Write("HOW MANY ACRES DO YOU WISH TO SELL? ");
            }
    
            /// 
            /// Prompts the user to feed the people.
            /// 
            public static void PromptFeedPeople()
            {
                Console.Write("HOW MANY BUSHELS DO YOU WISH TO FEED YOUR PEOPLE? ");
            }
    
            /// 
            /// Prompts the user to plant crops.
            /// 
            public static void PromptPlantCrops()
            {
                Console.Write("HOW MANY ACRES DO YOU WISH TO PLANT WITH SEED? ");
            }
        }
    }
    
    
    ================================================
    FILE: 43_Hammurabi/hammurabi.bas
    ================================================
    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)
    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."
    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
    323 GOSUB 710
    324 GOTO 320
    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 Q
    C/2 THEN 530 523 REM *** RATS ARE RUNNING WILD!! 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) 539 REM *** HOW MANY PEOPLE HAD FULL TUMMIES? 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!!!" 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 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." 855 PRINT "GET YOURSELF ANOTHER STEWARD!!!!!" 857 GOTO 990 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 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 900 PRINT "A FANTASTIC PERFORMANCE!!! CHARLEMANGE, DISRAELI, AND" 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 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" 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 ================================================ FILE: 43_Hammurabi/java/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Oracle Java](https://openjdk.java.net/) ================================================ FILE: 43_Hammurabi/java/src/Hamurabi.java ================================================ import java.util.Arrays; import java.util.Scanner; /** * Game of Hamurabi *

    * Based on the Basic game of Hamurabi here * https://github.com/coding-horror/basic-computer-games/blob/main/43%20Hammurabi/hammurabi.bas *

    * Note: The idea was to create a version of the 1970's Basic game in Java, without introducing * new features - no additional text, error checking, etc has been added. */ public class Hamurabi { public static final int INITIAL_POPULATION = 95; public static final int INITIAL_BUSHELS = 2800; public static final int INITIAL_HARVEST = 3000; public static final int INITIAL_LAND_TRADING_AT = 3; public static final int INITIAL_CAME_TO_CITY = 5; public static final int MAX_GAME_YEARS = 10; public static final double MAX_STARVATION_IN_A_YEAR = .45d; private int year; private int population; private int acres; private int bushels; private int harvest; private int landTradingAt; private int cameToCity; private int starvedInAYear; private int starvedOverall; private boolean chanceOfPlague; private int ratsAte; private double peopleFed; private double percentageStarved; private int bushelsToFeedPeople; // Used for keyboard input private final Scanner kbScanner; private enum GAME_STATE { STARTUP, INIT, YEAR_CYCLE, BUY_ACRES, SELL_ACRES, FEED_PEOPLE, PLANT_SEED, CALCULATE_HARVEST, CALCULATE_BABIES, RESULTS, FINISH_GAME, GAME_OVER } // Current game state private GAME_STATE gameState; public Hamurabi() { kbScanner = new Scanner(System.in); gameState = GAME_STATE.STARTUP; } /** * Main game loop */ public void play() { do { switch (gameState) { case STARTUP: intro(); gameState = GAME_STATE.INIT; break; case INIT: // These are hard coded startup figures from the basic program year = 0; population = INITIAL_POPULATION; bushels = INITIAL_BUSHELS; harvest = INITIAL_HARVEST; landTradingAt = INITIAL_LAND_TRADING_AT; acres = INITIAL_HARVEST / INITIAL_LAND_TRADING_AT; cameToCity = INITIAL_CAME_TO_CITY; starvedInAYear = 0; starvedOverall = 0; chanceOfPlague = false; ratsAte = INITIAL_HARVEST - INITIAL_BUSHELS; peopleFed = 0; percentageStarved = 0; bushelsToFeedPeople = 0; gameState = GAME_STATE.YEAR_CYCLE; break; case YEAR_CYCLE: System.out.println(); year += 1; // End of game? if (year > MAX_GAME_YEARS) { gameState = GAME_STATE.RESULTS; break; } System.out.println("HAMURABI: I BEG TO REPORT TO YOU,"); System.out.println("IN YEAR " + year + "," + starvedInAYear + " PEOPLE STARVED," + cameToCity + " CAME TO THE CITY,"); population += cameToCity; if (chanceOfPlague) { population /= 2; System.out.println("A HORRIBLE PLAGUE STRUCK! HALF THE PEOPLE DIED."); } System.out.println("POPULATION IS NOW " + population); System.out.println("THE CITY NOW OWNS " + acres + " ACRES."); System.out.println("YOU HARVESTED " + landTradingAt + " BUSHELS PER ACRE."); System.out.println("THE RATS ATE " + ratsAte + " BUSHELS."); System.out.println("YOU NOW HAVE " + bushels + " BUSHELS IN STORE."); System.out.println(); landTradingAt = (int) (Math.random() * 10) + 17; // Original formula unchanged System.out.println("LAND IS TRADING AT " + landTradingAt + " BUSHELS PER ACRE."); gameState = GAME_STATE.BUY_ACRES; break; case BUY_ACRES: int acresToBuy = displayTextAndGetNumber("HOW MANY ACRES DO YOU WISH TO BUY? "); if (acresToBuy < 0) { gameState = GAME_STATE.FINISH_GAME; } if (acresToBuy > 0) { if ((landTradingAt * acresToBuy) > bushels) { notEnoughBushelsMessage(); } else { acres += acresToBuy; bushels -= (landTradingAt * acresToBuy); peopleFed = 0; gameState = GAME_STATE.FEED_PEOPLE; } } else { // 0 entered as buy so try to sell gameState = GAME_STATE.SELL_ACRES; } break; case SELL_ACRES: int acresToSell = displayTextAndGetNumber("HOW MANY ACRES DO YOU WISH TO SELL? "); if (acresToSell < 0) { gameState = GAME_STATE.FINISH_GAME; } if (acresToSell < acres) { acres -= acresToSell; bushels += (landTradingAt * acresToSell); gameState = GAME_STATE.FEED_PEOPLE; } else { notEnoughLandMessage(); } break; case FEED_PEOPLE: bushelsToFeedPeople = displayTextAndGetNumber("HOW MANY BUSHELS DO YOU WISH TO FEED YOUR PEOPLE ? "); if (bushelsToFeedPeople < 0) { gameState = GAME_STATE.FINISH_GAME; } if (bushelsToFeedPeople <= bushels) { bushels -= bushelsToFeedPeople; peopleFed = 1; gameState = GAME_STATE.PLANT_SEED; } else { notEnoughBushelsMessage(); } break; case PLANT_SEED: int acresToPlant = displayTextAndGetNumber("HOW MANY ACRES DO YOU WISH TO PLANT WITH SEED ? "); if (acresToPlant < 0) { gameState = GAME_STATE.FINISH_GAME; } if (acresToPlant <= acres) { if (acresToPlant / 2 <= bushels) { if (acresToPlant < 10 * population) { bushels -= acresToPlant / 2; peopleFed = (int) (Math.random() * 5) + 1; landTradingAt = (int) peopleFed; harvest = acresToPlant * landTradingAt; ratsAte = 0; gameState = GAME_STATE.CALCULATE_HARVEST; } else { notEnoughPeopleMessage(); } } else { notEnoughBushelsMessage(); } } else { notEnoughLandMessage(); } break; case CALCULATE_HARVEST: if ((int) (peopleFed / 2) == peopleFed / 2) { // Rats are running wild ratsAte = (int) (bushels / peopleFed); } bushels = bushels - ratsAte; bushels += harvest; gameState = GAME_STATE.CALCULATE_BABIES; break; case CALCULATE_BABIES: cameToCity = (int) (peopleFed * (20 * acres + bushels) / population / 100 + 1); peopleFed = (bushelsToFeedPeople / 20.0d); // Simplify chance of plague to a true/false chanceOfPlague = (int) ((10 * (Math.random() * 2) - .3)) == 0; if (population < peopleFed) { gameState = GAME_STATE.YEAR_CYCLE; } double starved = population - peopleFed; if (starved < 0.0d) { starvedInAYear = 0; gameState = GAME_STATE.YEAR_CYCLE; } else { starvedInAYear = (int) starved; starvedOverall += starvedInAYear; if (starved > MAX_STARVATION_IN_A_YEAR * population) { starvedTooManyPeopleMessage((int) starved); gameState = GAME_STATE.FINISH_GAME; } else { percentageStarved = ((year - 1) * percentageStarved + starved * 100 / population) / year; population = (int) peopleFed; gameState = GAME_STATE.YEAR_CYCLE; } } break; case RESULTS: int acresPerPerson = acres / population; System.out.println("IN YOUR 10-YEAR TERM OF OFFICE," + String.format("%.2f", percentageStarved) + "% PERCENT OF THE"); System.out.println("POPULATION STARVED PER YEAR ON THE AVERAGE, I.E. A TOTAL OF"); System.out.println(starvedOverall + " PEOPLE DIED!!"); System.out.println("YOU STARTED WITH 10 ACRES PER PERSON AND ENDED WITH"); System.out.println(acresPerPerson + " ACRES PER PERSON."); System.out.println(); if (percentageStarved > 33.0d || acresPerPerson < 7) { starvedTooManyPeopleMessage(starvedOverall); } else if (percentageStarved > 10.0d || acresPerPerson < 9) { heavyHandedMessage(); } else if (percentageStarved > 3.0d || acresPerPerson < 10) { couldHaveBeenBetterMessage(); } else { fantasticPerformanceMessage(); } gameState = GAME_STATE.FINISH_GAME; case FINISH_GAME: System.out.println("SO LONG FOR NOW."); gameState = GAME_STATE.GAME_OVER; } } while (gameState != GAME_STATE.GAME_OVER); } private void starvedTooManyPeopleMessage(int starved) { System.out.println(); System.out.println("YOU STARVED " + starved + " PEOPLE IN ONE YEAR!!!"); System.out.println("DUE TO THIS EXTREME MISMANAGEMENT YOU HAVE NOT ONLY"); System.out.println("BEEN IMPEACHED AND THROWN OUT OF OFFICE BUT YOU HAVE"); System.out.println("ALSO BEEN DECLARED NATIONAL FINK!!!!"); } private void heavyHandedMessage() { System.out.println("DUE TO THIS EXTREME MISMANAGEMENT YOU HAVE NOT ONLY"); System.out.println("BEEN IMPEACHED AND THROWN OUT OF OFFICE BUT YOU HAVE"); System.out.println("ALSO BEEN DECLARED NATIONAL FINK!!!!"); } private void couldHaveBeenBetterMessage() { System.out.println("YOUR PERFORMANCE COULD HAVE BEEN SOMEWHAT BETTER, BUT"); System.out.println("REALLY WASN'T TOO BAD AT ALL. " + (int) (Math.random() * (population * .8)) + " PEOPLE"); System.out.println("WOULD DEARLY LIKE TO SEE YOU ASSASSINATED BUT WE ALL HAVE OUR"); System.out.println("TRIVIAL PROBLEMS."); } private void fantasticPerformanceMessage() { System.out.println("A FANTASTIC PERFORMANCE!!! CHARLEMANGE, DISRAELI, AND"); System.out.println("JEFFERSON COMBINED COULD NOT HAVE DONE BETTER!"); } private void notEnoughPeopleMessage() { System.out.println("BUT YOU HAVE ONLY " + population + " PEOPLE TO TEND THE FIELDS! NOW THEN,"); } private void notEnoughBushelsMessage() { System.out.println("HAMURABI: THINK AGAIN. YOU HAVE ONLY"); System.out.println(bushels + " BUSHELS OF GRAIN. NOW THEN,"); } private void notEnoughLandMessage() { System.out.println("HAMURABI: THINK AGAIN. YOU OWN ONLY " + acres + " ACRES. NOW THEN,"); } private void intro() { System.out.println(simulateTabs(32) + "HAMURABI"); System.out.println(simulateTabs(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println(); System.out.println("TRY YOUR HAND AT GOVERNING ANCIENT SUMERIA"); System.out.println("FOR A TEN-YEAR TERM OF OFFICE."); System.out.println(); } /* * Print a message on the screen, then accept input from Keyboard. * Converts input to an Integer * * @param text message to be displayed on screen. * @return what was typed by the player. */ private int displayTextAndGetNumber(String text) { return Integer.parseInt(displayTextAndGetInput(text)); } /* * Print a message on the screen, then accept input from Keyboard. * * @param text message to be displayed on screen. * @return what was typed by the player. */ private String displayTextAndGetInput(String text) { System.out.print(text); return kbScanner.next(); } /** * Simulate the old basic tab(xx) command which indented text by xx spaces. * * @param spaces number of spaces required * @return String with number of spaces */ private String simulateTabs(int spaces) { char[] spacesTemp = new char[spaces]; Arrays.fill(spacesTemp, ' '); return new String(spacesTemp); } } ================================================ FILE: 43_Hammurabi/java/src/HamurabiGame.java ================================================ public class HamurabiGame { public static void main(String[] args) { Hamurabi hamurabi = new Hamurabi(); hamurabi.play(); } } ================================================ FILE: 43_Hammurabi/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 43_Hammurabi/javascript/hammurabi.html ================================================ HAMMURABI

    
    
    
    
    
    
    ================================================
    FILE: 43_Hammurabi/javascript/hammurabi.js
    ================================================
    // HAMMURABI
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    var a;
    var s;
    
    function exceeded_grain()
    {
        print("HAMURABI: THINK AGAIN.  YOU HAVE ONLY\n");
        print(s + " BUSHELS OF GRAIN.  NOW THEN,\n");
    
    }
    
    function exceeded_acres()
    {
        print("HAMURABI: THINK AGAIN.  YOU OWN ONLY " + a + " ACRES.  NOW THEN,\n");
    }
    
    // Main control section
    async function main()
    {
        print(tab(32) + "HAMURABI\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        print("TRY YOUR HAND AT GOVERNING ANCIENT SUMERIA\n");
        print("FOR A TEN-YEAR TERM OF OFFICE.\n");
        print("\n");
    
        d1 = 0;
        p1 = 0;
        z = 0;
        p = 95;
        s = 2800;
        h = 3000;
        e = h - s;
        y = 3;
        a = h / y;
        i = 5;
        q = 1;
        d = 0;
        while (1) {
            print("\n");
            print("\n");
            print("\n");
            print("HAMURABI:  I BEG TO REPORT TO YOU,\n");
            z++;
            print("IN YEAR " + z + ", " + d + " PEOPLE STARVED, " + i + " CAME TO THE CITY,\n");
            p += i;
            if (q <= 0) {
                p = Math.floor(p / 2);
                print("A HORRIBLE PLAGUE STRUCK!  HALF THE PEOPLE DIED.\n");
            }
            print("POPULATION IS NOW " + p + "\n");
            print("THE CITY NOW OWNS " + a + " ACRES.\n");
            print("YOU HARVESTED " + y + " BUSHELS PER ACRE.\n");
            print("THE RATS ATE " + e + " BUSHELS.\n");
            print("YOU NOW HAVE " + s + " BUSHELS IN STORE.\n");
            print("\n");
            if (z == 11) {
                q = 0;
                break;
            }
            c = Math.floor(10 * Math.random());
            y = c + 17;
            print("LAND IS TRADING AT " + y + " BUSHELS PER ACRE.\n");
            while (1) {
                print("HOW MANY ACRES DO YOU WISH TO BUY");
                q = parseInt(await input());
                if (q < 0)
                    break;
                if (y * q > s) {
                    exceeded_grain();
                } else
                    break;
            }
            if (q < 0)
                break;
            if (q != 0) {
                a += q;
                s -= y * q;
                c = 0;
            } else {
                while (1) {
                    print("HOW MANY ACRES DO YOU WISH TO SELL");
                    q = parseInt(await input());
                    if (q < 0)
                        break;
                    if (q >= a) {
                        exceeded_acres();
                    } else {
                        break;
                    }
                }
                if (q < 0)
                    break;
                a -= q;
                s += y * q;
                c = 0;
            }
            print("\n");
            while (1) {
                print("HOW MANY BUSHELS DO YOU WISH TO FEED YOUR PEOPLE");
                q = parseInt(await input());
                if (q < 0)
                    break;
                if (q > s)  // Trying to use more grain than is in silos?
                    exceeded_grain();
                else
                    break;
            }
            if (q < 0)
                break;
            s -= q;
            c = 1;
            print("\n");
            while (1) {
                print("HOW MANY ACRES DO YOU WISH TO PLANT WITH SEED");
                d = parseInt(await input());
                if (d != 0) {
                    if (d < 0)
                        break;
                    if (d > a) {    // Trying to plant more acres than you own?
                        exceeded_acres();
                    } else {
                        if (Math.floor(d / 2) > s)  // Enough grain for seed?
                            exceeded_grain();
                        else {
                            if (d >= 10 * p) {
                                print("BUT YOU HAVE ONLY " + p + " PEOPLE TO TEND THE FIELDS!  NOW THEN,\n");
                            } else {
                                break;
                            }
                        }
                    }
                }
            }
            if (d < 0) {
                q = -1;
                break;
            }
            s -= Math.floor(d / 2);
            c = Math.floor(Math.random() * 5) + 1;
            // A bountiful harvest!
            if (c % 2 == 0) {
                // Rats are running wild!!
                e = Math.floor(s / c);
            }
            s = s - e + h;
            c = Math.floor(Math.random() * 5) + 1;
            // Let's have some babies
            i = Math.floor(c * (20 * a + s) / p / 100 + 1);
            // How many people had full tummies?
            c = Math.floor(q / 20);
            // Horros, a 15% chance of plague
            q = Math.floor(10 * (2 * Math.random() - 0.3));
            if (p < c) {
                d = 0;
                continue;
            }
            // Starve enough for impeachment?
            d = p - c;
            if (d <= 0.45 * p) {
                p1 = ((z - 1) * p1 + d * 100 / p) / z;
                p = c;
                d1 += d;
                continue;
            }
            print("\n");
            print("YOU STARVED " + d + " PEOPLE IN ONE YEAR!!!\n");
            q = 0;
            p1 = 34;
            p = 1;
            break;
        }
        if (q < 0) {
            print("\n");
            print("HAMURABI:  I CANNOT DO WHAT YOU WISH.\n");
            print("GET YOURSELF ANOTHER STEWARD!!!!!\n");
        } else {
            print("IN YOUR 10-YEAR TERM OF OFFICE, " + p1 + " PERCENT OF THE\n");
            print("POPULATION STARVED PER YEAR ON THE AVERAGE, I.E. A TOTAL OF\n");
            print(d1 + " PEOPLE DIED!!\n");
            l = a / p;
            print("YOU STARTED WITH 10 ACRES PER PERSON AND ENDED WITH\n");
            print(l + " ACRES PER PERSON.\n");
            print("\n");
            if (p1 > 33 || l < 7) {
                print("DUE TO THIS EXTREME MISMANAGEMENT YOU HAVE NOT ONLY\n");
                print("BEEN IMPEACHED AND THROWN OUT OF OFFICE BUT YOU HAVE\n");
                print("ALSO BEEN DECLARED NATIONAL FINK!!!!\n");
            } else if (p1 > 10 || l < 9) {
                print("YOUR HEAVY-HANDED PERFORMANCE SMACKS OF NERO AND IVAN IV.\n");
                print("THE PEOPLE (REMAINING) FIND YOU AN UNPLEASANT RULER, AND,\n");
                print("FRANKLY, HATE YOUR GUTS!!\n");
            } else if (p1 > 3 || l < 10) {
                print("YOUR PERFORMANCE COULD HAVE BEEN SOMEWHAT BETTER, BUT\n");
                print("REALLY WASN'T TOO BAD AT ALL. " + Math.floor(p * 0.8 * Math.random()) + " PEOPLE\n");
                print("WOULD DEARLY LIKE TO SEE YOU ASSASSINATED BUT WE ALL HAVE OUR\n");
                print("TRIVIAL PROBLEMS.\n");
            } else {
                print("A FANTASTIC PERFORMANCE!!!  CHARLEMANGE, DISRAELI, AND\n");
                print("JEFFERSON COMBINED COULD NOT HAVE DONE BETTER!\n");
            }
        }
        print("\n");
        print("SO LONG FOR NOW.\n");
        print("\n");
    }
    
    main();
    
    
    ================================================
    FILE: 43_Hammurabi/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 43_Hammurabi/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 43_Hammurabi/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 43_Hammurabi/perl/lib/BasicComputerGames/Hammurabi.pm
    ================================================
    #!/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
    
    
    ================================================
    FILE: 43_Hammurabi/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 43_Hammurabi/python/hamurabi.py
    ================================================
    from random import random, seed
    
    
    def gen_random() -> int:
        return int(random() * 5) + 1
    
    
    def bad_input_850() -> None:
        print("\nHAMURABI:  I CANNOT DO WHAT YOU WISH.")
        print("GET YOURSELF ANOTHER STEWARD!!!!!")
    
    
    def bad_input_710(grain_bushels: int) -> None:
        print("HAMURABI:  THINK AGAIN.  YOU HAVE ONLY")
        print(f"{grain_bushels} BUSHELS OF GRAIN.  NOW THEN,")
    
    
    def bad_input_720(acres: float) -> None:
        print(f"HAMURABI:  THINK AGAIN.  YOU OWN ONLY {acres} ACRES.  NOW THEN,")
    
    
    def national_fink() -> None:
        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!!!!")
    
    
    def b_input(promptstring: str) -> int:
        """emulate BASIC input. It rejects non-numeric values"""
        x = input(promptstring)
        while x.isalpha():
            x = input("?REDO FROM START\n? ")
        return int(x)
    
    
    def main() -> None:
        seed()
        title = "HAMURABI"
        title = title.rjust(32, " ")
        print(title)
        attribution = "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
        attribution = attribution.rjust(15, " ")
        print(attribution)
        print("\n\n\n")
        print("TRY YOUR HAND AT GOVERNING ANCIENT SUMERIA")
        print("FOR A TEN-YEAR TERM OF OFFICE.\n")
    
        D1 = 0
        P1: float = 0
        year = 0
        population = 95
        grain_stores = 2800
        H = 3000
        eaten_rats = H - grain_stores
        bushels_per_acre = (
            3  # yield (amount of production from land). Reused as price per acre
        )
        acres = H / bushels_per_acre  # acres of land
        immigrants = 5
        plague = 1  # boolean for plague, also input for buy/sell land
        people = 0
    
        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 += 1
            print(
                "IN YEAR",
                year,
                ",",
                people,
                "PEOPLE STARVED,",
                immigrants,
                "CAME TO THE CITY,",
            )
            population = population + immigrants
    
            if plague == 0:
                population = int(population / 2)
                print("A HORRIBLE PLAGUE STRUCK!  HALF THE PEOPLE DIED.")
    
            print("POPULATION IS NOW", population)
            print("THE CITY NOW OWNS", acres, "ACRES.")
            print("YOU HARVESTED", bushels_per_acre, "BUSHELS PER ACRE.")
            print("THE RATS ATE", eaten_rats, "BUSHELS.")
            print("YOU NOW HAVE ", grain_stores, "BUSHELS IN STORE.\n")
            C = int(10 * random())  # random number between 1 and 10
            bushels_per_acre = C + 17
            print("LAND IS TRADING AT", bushels_per_acre, "BUSHELS PER ACRE.")
    
            plague = -99  # dummy value to track status
            while plague == -99:  # always run the loop once
                plague = b_input("HOW MANY ACRES DO YOU WISH TO BUY? ")
                if plague < 0:
                    plague = -1  # to avoid the corner case of Q=-99
                    bad_input_850()
                    year = 99  # jump out of main loop and exit
                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
                else:
                    acres = acres + plague  # increase the number of acres by Q
                    grain_stores = (
                        grain_stores - bushels_per_acre * plague
                    )  # decrease the amount of grain in store to pay for it
                    C = 0  # WTF is C for?
    
            if plague == 0 and year != 99:  # maybe you want to sell some land?
                plague = -99
                while plague == -99:
                    plague = b_input("HOW MANY ACRES DO YOU WISH TO SELL? ")
                    if plague < 0:
                        bad_input_850()
                        year = 99  # jump out of main loop and exit
                    elif plague <= acres:  # normal case
                        acres = acres - plague  # reduce the acres
                        grain_stores = (
                            grain_stores + bushels_per_acre * plague
                        )  # add to grain stores
                        C = 0  # still don't know what C is for
                    else:  # Q>A error!
                        bad_input_720(acres)
                        plague = -99  # reloop
                print("\n")
    
            plague = -99
            while plague == -99 and year != 99:
                plague = b_input("HOW MANY BUSHELS DO YOU WISH TO FEED YOUR PEOPLE? ")
                if plague < 0:
                    bad_input_850()
                    year = 99  # jump out of main loop and exit
                # REM *** TRYING TO USE MORE GRAIN THAN IS IN SILOS?
                elif plague > grain_stores:
                    bad_input_710(grain_stores)
                    plague = -99  # try again!
                else:  # we're good. do the transaction
                    grain_stores = grain_stores - plague  # remove the grain from the stores
                    C = 1  # set the speed of light to 1. jk
    
            print("\n")
            people = -99  # dummy value to force at least one loop
            while people == -99 and year != 99:
                people = b_input("HOW MANY ACRES DO YOU WISH TO PLANT WITH SEED? ")
                if people < 0:
                    bad_input_850()
                    year = 99  # jump out of main loop and exit
                elif people > 0:
                    if people > acres:
                        # REM *** TRYING TO PLANT MORE ACRES THAN YOU OWN?
                        bad_input_720(acres)
                        people = -99
                    elif int(people / 2) > grain_stores:
                        # REM *** ENOUGH GRAIN FOR SEED?
                        bad_input_710(grain_stores)
                        people = -99
                    elif people > 10 * population:
                        # REM *** ENOUGH PEOPLE TO TEND THE CROPS?
                        print(
                            "BUT YOU HAVE ONLY",
                            population,
                            "PEOPLE TO TEND THE FIELDS!  NOW THEN,",
                        )
                        people = -99
                    else:  # we're good. decrement the grain store
                        grain_stores = grain_stores - int(people / 2)
    
            C = gen_random()
            # REM *** A BOUNTIFUL HARVEST!
            bushels_per_acre = C
            H = people * bushels_per_acre
            C = gen_random()
            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()
            # REM *** LET'S HAVE SOME BABIES
            immigrants = int(C * (20 * acres + grain_stores) / population / 100 + 1)
            # REM *** HOW MANY PEOPLE HAD FULL TUMMIES?
            C = int(plague / 20)
            # REM *** HORROS, A 15% CHANCE OF PLAGUE
            # yeah, should be HORRORS, but left it
            plague = int(10 * (2 * random() - 0.3))
            if (
                population >= C and year != 99
            ):  # if there are some people without full bellies...
                # REM *** STARVE ENOUGH FOR IMPEACHMENT?
                people = population - C
                if people > 0.45 * population:
                    print("\nYOU STARVED", people, "PEOPLE IN ONE YEAR!!!")
                    national_fink()
                    year = 99  # exit the loop
                P1 = ((year - 1) * P1 + people * 100 / population) / year
                population = C
                D1 = D1 + people
    
        if year != 99:
            print("IN YOUR 10-YEAR TERM OF OFFICE,", P1, "PERCENT OF THE")
            print("POPULATION STARVED PER YEAR ON THE AVERAGE, I.E. A TOTAL OF")
            print(D1, "PEOPLE DIED!!")
            L = acres / population
            print("YOU STARTED WITH 10 ACRES PER PERSON AND ENDED WITH")
            print(L, "ACRES PER PERSON.\n")
            if P1 > 33 or L < 7:
                national_fink()
            elif P1 > 10 or L < 9:
                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!!")
            elif P1 > 3 or L < 10:
                print("YOUR PERFORMANCE COULD HAVE BEEN SOMEWHAT BETTER, BUT")
                print(
                    "REALLY WASN'T TOO BAD AT ALL. ",
                    int(population * 0.8 * random()),
                    "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!\n")
            for _ in range(1, 10):
                print("\a")
    
        print("\nSO LONG FOR NOW.\n")
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 43_Hammurabi/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 43_Hammurabi/rust/Cargo.toml
    ================================================
    [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"
    
    
    ================================================
    FILE: 43_Hammurabi/rust/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Rust](https://www.rust-lang.org/)
    
    
    ================================================
    FILE: 43_Hammurabi/rust/src/main.rs
    ================================================
    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();
    }
    
    
    ================================================
    FILE: 43_Hammurabi/vbnet/Hammurabi.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Hammurabi", "Hammurabi.vbproj", "{52BC86C8-4AE8-4F0C-9566-5E0D97BB17BF}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{52BC86C8-4AE8-4F0C-9566-5E0D97BB17BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{52BC86C8-4AE8-4F0C-9566-5E0D97BB17BF}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{52BC86C8-4AE8-4F0C-9566-5E0D97BB17BF}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{52BC86C8-4AE8-4F0C-9566-5E0D97BB17BF}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 43_Hammurabi/vbnet/Hammurabi.vbproj
    ================================================
    
      
        Exe
        Hammurabi
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 43_Hammurabi/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 44_Hangman/README.md
    ================================================
    ### Hangman
    
    This is a simulation of the word guessing game, hangman. The computer picks a word, tells you how many letters in the word it has picked and then you guess a letter in the word. If you are right, the computer tells you where that letter belongs; if your letter is wrong, the computer starts to hang you. You get ten guesses before you are completely hanged:
    1. Head
    2. Body
    3. Right Arm
    4. Left Arm
    5. Right Leg
    6. Left Leg
    7. Right Hand
    8. Left Hand
    9. Right Foot
    10. Left Foot
    
    You may add words in Data statements; however if you do, you must also change the random word selector.
    
    David Ahl modified this program into its current form from the one created by Kenneth Aupperle of Melville, New York.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=80)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=95)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 44_Hangman/csharp/Graphic.cs
    ================================================
    using System;
    
    namespace Hangman
    {
        /// 
        /// Represents the main "Hangman" graphic.
        /// 
        public class Graphic
        {
            private readonly char[,] _graphic;
            private const int Width = 12;
            private const int Height = 12;
    
            public Graphic()
            {
                // 12 x 12 array to represent the graphics.
                _graphic = new char[Height, Width];
    
                // Fill it with empty spaces.
                for (var i = 0; i < Height; i++)
                {
                    for (var j = 0; j < Width; j++)
                    {
                        _graphic[i, j] = ' ';
                    }
                }
    
                // Draw the vertical line.
                for (var i = 0; i < Height; i++)
                {
                    _graphic[i, 0] = 'X';
                }
    
                // Draw the horizontal line.
                for (var i = 0; i < 7; i++)
                {
                    _graphic[0, i] = 'X';
                }
    
                // Draw the rope.
                _graphic[1, 6] = 'X';
            }
    
            public void Print()
            {
                for (var i = 0; i < Height; i++)
                {
                    for (var j = 0; j < Width; j++)
                    {
                        Console.Write(_graphic[i, j]);
                    }
    
                    Console.Write("\n"); // New line.
                }
            }
    
            public void AddHead()
            {
                _graphic[2, 5] = '-';
                _graphic[2, 6] = '-';
                _graphic[2, 7] = '-';
                _graphic[3, 4] = '(';
                _graphic[3, 5] = '.';
                _graphic[3, 7] = '.';
                _graphic[3, 8] = ')';
                _graphic[4, 5] = '-';
                _graphic[4, 6] = '-';
                _graphic[4, 7] = '-';
            }
    
            public void AddBody()
            {
                for (var i = 5; i < 9; i++)
                {
                    _graphic[i, 6] = 'X';
                }
            }
    
            public void AddRightArm()
            {
                for (var i = 3; i < 7; i++)
                {
                    _graphic[i, i - 1] = '\\'; // This is the escape character for the back slash.
                }
            }
    
            public void AddLeftArm()
            {
                _graphic[3, 10] = '/';
                _graphic[4, 9] = '/';
                _graphic[5, 8] = '/';
                _graphic[6, 7] = '/';
            }
    
            public void AddRightLeg()
            {
                _graphic[9, 5] = '/';
                _graphic[10, 4] = '/';
            }
    
            public void AddLeftLeg()
            {
                _graphic[9, 7] = '\\';
                _graphic[10, 8] = '\\';
            }
    
            public void AddRightHand()
            {
                _graphic[2, 2] = '/';
            }
    
            public void AddLeftHand()
            {
                _graphic[2, 10] = '\\';
            }
    
            public void AddRightFoot()
            {
                _graphic[11, 9] = '\\';
                _graphic[11, 10] = '-';
            }
    
            public void AddLeftFoot()
            {
                _graphic[11, 3] = '/';
                _graphic[11, 2] = '-';
            }
        }
    }
    
    
    ================================================
    FILE: 44_Hangman/csharp/Hangman.csproj
    ================================================
    
    
        
            Exe
            net5.0
        
    
    
    
    
    ================================================
    FILE: 44_Hangman/csharp/Hangman.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hangman", "Hangman.csproj", "{1C516A9E-F4F2-4C79-8C37-0162C403B1F7}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{1C516A9E-F4F2-4C79-8C37-0162C403B1F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{1C516A9E-F4F2-4C79-8C37-0162C403B1F7}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{1C516A9E-F4F2-4C79-8C37-0162C403B1F7}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{1C516A9E-F4F2-4C79-8C37-0162C403B1F7}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 44_Hangman/csharp/Program.cs
    ================================================
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text.Json.Serialization;
    
    namespace Hangman
    {
        /// 
        /// C# version of the game "Hangman" from the book BASIC Computer Games.
        /// 
        static class Program
        {
            static void Main()
            {
                Console.WriteLine(Tab(32) + "HANGMAN");
                Console.WriteLine(Tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine();
                MainLoop();
                Console.WriteLine();
                Console.WriteLine("IT'S BEEN FUN!  BYE FOR NOW.");
            }
    
            static void MainLoop()
            {
                var words = GetWords();
                var stillPlaying = true;
    
                while (stillPlaying)
                {
                    if (words.Count == 0)
                    {
                        Console.WriteLine("YOU DID ALL THE WORDS!!");
                        break;
                    }
    
                    // Get a random number from 0 to the number of words we have minus one (C# arrays are zero-based).
                    var rnd = new Random();
                    var randomNumber = rnd.Next(words.Count - 1);
    
                    // Pick a random word and remove it from the list.
                    var word = words[randomNumber];
                    words.Remove(word);
    
                    GameLoop(word);
    
                    // Game finished. Ask if player wants another one.
                    Console.WriteLine("WANT ANOTHER WORD? ");
                    var response = Console.ReadLine();
                    if (response == null || response.ToUpper() != "YES")
                    {
                        stillPlaying = false;   // Exit the loop if the player didn't answer "yes".
                    }
                }
            }
    
            static void GameLoop(string word)
            {
                var graphic = new Graphic();
                var wrongGuesses = 0;
                var numberOfGuesses = 0;
                var usedLetters = new List();
    
                // The word that the user sees. Since we just started, it's just dashes.
                var displayedWord = new char[word.Length];
                for (var i = 0; i < word.Length; i++)
                {
                    displayedWord[i] = '-';
                }
    
                var stillPlaying = true;
                while (stillPlaying)
                {
                    var guess = GetLetterFromPlayer(displayedWord, usedLetters);
                    usedLetters.Add(guess);
                    numberOfGuesses++;
                    var correctLetterCount = 0;
                    // Now we check every letter in the word to see if the player guessed any of them correctly.
                    for(var i = 0; i < word.Length; i++)
                    {
                        if (word[i] == guess)
                        {
                            correctLetterCount++;
                            displayedWord[i] = guess;
                        }
                    }
    
                    if (correctLetterCount == 0)
                    {
                        // Wrong guess.
                        Console.WriteLine("SORRY, THAT LETTER ISN'T IN THE WORD.");
                        wrongGuesses++;
                        DrawBody(graphic, wrongGuesses);
                        if (wrongGuesses == 10)
                        {
                            // Player exhausted all their guesses. Finish the game loop.
                            Console.WriteLine($"SORRY, YOU LOSE.  THE WORD WAS {word}");
                            Console.Write("YOU MISSED THAT ONE.  DO YOU ");
                            stillPlaying = false;
                        }
                    }
                    else
                    {
                        // Player guessed a correct letter. Let's see if there are any unguessed letters left in the word.
                        if (displayedWord.Contains('-'))
                        {
                            Console.WriteLine(displayedWord);
    
                            // Give the player a chance to guess the whole word.
                            var wordGuess = GetWordFromPlayer();
                            if (word == wordGuess)
                            {
                                // Player found the word. Mark it found.
                                Console.WriteLine("YOU FOUND THE WORD!");
                                stillPlaying = false;   // Exit game loop.
                            }
                            else
                            {
                                // Player didn't guess the word. Continue the game loop.
                                Console.WriteLine("WRONG.  TRY ANOTHER LETTER.");
                            }
                        }
                        else
                        {
                            // Player guessed all the letters.
                            Console.WriteLine("YOU FOUND THE WORD!");
                            stillPlaying = false;   // Exit game loop.
                        }
                    }
                } // End of game loop.
            }
    
            /// 
            /// Display the current state of the word and all the already guessed letters, and get a new guess from the player
            /// 
            /// A char array that represents the current state of the guessed word
            /// A list of chars that represents all the letters guessed so far
            /// The letter that the player has just entered as a guess
            private static char GetLetterFromPlayer(char[] displayedWord, List usedLetters)
            {
                while (true)    // Infinite loop, unless the player enters an unused letter.
                {
                    Console.WriteLine();
                    Console.WriteLine(displayedWord);
                    Console.WriteLine();
                    Console.WriteLine();
                    Console.WriteLine("HERE ARE THE LETTERS YOU USED:");
                    for (var i = 0; i < usedLetters.Count; i++)
                    {
                        Console.Write(usedLetters[i]);
    
                        // If it's not the last letter, print a comma.
                        if (i != usedLetters.Count - 1)
                        {
                            Console.Write(",");
                        }
                    }
    
                    Console.WriteLine();
                    Console.WriteLine("WHAT IS YOUR GUESS?");
                    var guess = char.ToUpper(Console.ReadKey().KeyChar);
                    Console.WriteLine();
    
                    if (usedLetters.Contains(guess))
                    {
                        // After this the loop will continue.
                        Console.WriteLine("YOU GUESSED THAT LETTER BEFORE!");
                    }
                    else
                    {
                        // Break out of the loop by returning guessed letter.
                        return guess;
                    }
                }
            }
    
            /// 
            /// Gets a word guess from the player.
            /// 
            /// The guessed word.
            private static string GetWordFromPlayer()
            {
                while (true)    // Infinite loop, unless the player enters something.
                {
                    Console.WriteLine("WHAT IS YOUR GUESS FOR THE WORD? ");
                    var guess = Console.ReadLine();
                    if (guess != null)
                    {
                        return guess.ToUpper();
                    }
                }
            }
    
            /// 
            /// Draw body after wrong guess.
            /// 
            /// The instance of the Graphic class being used.
            /// Number of wrong guesses.
            private static void DrawBody(Graphic graphic, int wrongGuesses)
            {
                switch (wrongGuesses)
                        {
                            case 1:
                                Console.WriteLine("FIRST, WE DRAW A HEAD.");
                                graphic.AddHead();
                                break;
                            case 2:
                                Console.WriteLine("NOW WE DRAW A BODY.");
                                graphic.AddBody();
                                break;
                            case 3:
                                Console.WriteLine("NEXT WE DRAW AN ARM.");
                                graphic.AddRightArm();
                                break;
                            case 4:
                                Console.WriteLine("THIS TIME IT'S THE OTHER ARM.");
                                graphic.AddLeftArm();
                                break;
                            case 5:
                                Console.WriteLine("NOW, LET'S DRAW THE RIGHT LEG.");
                                graphic.AddRightLeg();
                                break;
                            case 6:
                                Console.WriteLine("THIS TIME WE DRAW THE LEFT LEG.");
                                graphic.AddLeftLeg();
                                break;
                            case 7:
                                Console.WriteLine("NOW WE PUT UP A HAND.");
                                graphic.AddRightHand();
                                break;
                            case 8:
                                Console.WriteLine("NEXT THE OTHER HAND.");
                                graphic.AddLeftHand();
                                break;
                            case 9:
                                Console.WriteLine("NOW WE DRAW ONE FOOT.");
                                graphic.AddRightFoot();
                                break;
                            case 10:
                                Console.WriteLine("HERE'S THE OTHER FOOT -- YOU'RE HUNG!!");
                                graphic.AddLeftFoot();
                                break;
                        }
                        graphic.Print();
            }
    
            /// 
            /// Get a list of words to use in the game.
            /// 
            /// List of strings.
            private static List GetWords() => new()
            {
                "GUM",
                "SIN",
                "FOR",
                "CRY",
                "LUG",
                "BYE",
                "FLY",
                "UGLY",
                "EACH",
                "FROM",
                "WORK",
                "TALK",
                "WITH",
                "SELF",
                "PIZZA",
                "THING",
                "FEIGN",
                "FIEND",
                "ELBOW",
                "FAULT",
                "DIRTY",
                "BUDGET",
                "SPIRIT",
                "QUAINT",
                "MAIDEN",
                "ESCORT",
                "PICKAX",
                "EXAMPLE",
                "TENSION",
                "QUININE",
                "KIDNEY",
                "REPLICA",
                "SLEEPER",
                "TRIANGLE",
                "KANGAROO",
                "MAHOGANY",
                "SERGEANT",
                "SEQUENCE",
                "MOUSTACHE",
                "DANGEROUS",
                "SCIENTIST",
                "DIFFERENT",
                "QUIESCENT",
                "MAGISTRATE",
                "ERRONEOUSLY",
                "LOUDSPEAKER",
                "PHYTOTOXIC",
                "MATRIMONIAL",
                "PARASYMPATHOMIMETIC",
                "THIGMOTROPISM"
            };
    
            /// 
            /// Leave a number of spaces empty.
            /// 
            /// Number of spaces.
            /// The result string.
            private static string Tab(int length) => new string(' ', length);
        }
    }
    
    
    ================================================
    FILE: 44_Hangman/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 44_Hangman/hangman.bas
    ================================================
    10 PRINT TAB(32);"HANGMAN"
    20 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    25 PRINT:PRINT:PRINT
    30 DIM P$(12,12),L$(20),D$(20),N$(26),U(50)
    40 C=1: N=50
    50 FOR I=1 TO 20: D$(I)="-": NEXT I: M=0
    60 FOR I=1 TO 26: N$(I)="": NEXT I
    70 FOR I=1 TO 12: FOR J=1 TO 12: P$(I,J)=" ": NEXT J: NEXT I
    80 FOR I=1 TO 12: P$(I,1)="X": NEXT I
    90 FOR I=1 TO 7: P$(1,I)="X": NEXT: P$(2,7)="X"
    95 IF C10 THEN 170
    600 PRINT "SORRY, YOU LOSE.  THE WORD WAS ";A$
    610 PRINT "YOU MISSED THAT ONE.  DO YOU ";: GOTO 370
    620 INPUT "TYPE YES OR NO";Y$: IF LEFT$(Y$,1)="Y" THEN 50
    700 DATA "GUM","SIN","FOR","CRY","LUG","BYE","FLY"
    710 DATA "UGLY","EACH","FROM","WORK","TALK","WITH","SELF"
    720 DATA "PIZZA","THING","FEIGN","FIEND","ELBOW","FAULT","DIRTY"
    730 DATA "BUDGET","SPIRIT","QUAINT","MAIDEN","ESCORT","PICKAX"
    740 DATA "EXAMPLE","TENSION","QUININE","KIDNEY","REPLICA","SLEEPER"
    750 DATA "TRIANGLE","KANGAROO","MAHOGANY","SERGEANT","SEQUENCE"
    760 DATA "MOUSTACHE","DANGEROUS","SCIENTIST","DIFFERENT","QUIESCENT"
    770 DATA "MAGISTRATE","ERRONEOUSLY","LOUDSPEAKER","PHYTOTOXIC"
    780 DATA "MATRIMONIAL","PARASYMPATHOMIMETIC","THIGMOTROPISM"
    990 PRINT "BYE NOW"
    999 END
    
    
    ================================================
    FILE: 44_Hangman/java/Hangman.java
    ================================================
    import java.util.Arrays;
    import java.util.LinkedHashSet;
    import java.util.List;
    import java.util.Scanner;
    import java.util.Set;
    import java.util.stream.Collectors;
    
    /**
     * HANGMAN
     *
     * Converted from BASIC to Java by Aldrin Misquitta (@aldrinm)
     */
    
    public class Hangman {
    
    	//50 word list
    	private final static List words = List.of(
    			"GUM", "SIN", "FOR", "CRY", "LUG", "BYE", "FLY",
    			"UGLY", "EACH", "FROM", "WORK", "TALK", "WITH", "SELF",
    			"PIZZA", "THING", "FEIGN", "FIEND", "ELBOW", "FAULT", "DIRTY",
    			"BUDGET", "SPIRIT", "QUAINT", "MAIDEN", "ESCORT", "PICKAX",
    			"EXAMPLE", "TENSION", "QUININE", "KIDNEY", "REPLICA", "SLEEPER",
    			"TRIANGLE", "KANGAROO", "MAHOGANY", "SERGEANT", "SEQUENCE",
    			"MOUSTACHE", "DANGEROUS", "SCIENTIST", "DIFFERENT", "QUIESCENT",
    			"MAGISTRATE", "ERRONEOUSLY", "LOUDSPEAKER", "PHYTOTOXIC",
    			"MATRIMONIAL", "PARASYMPATHOMIMETIC", "THIGMOTROPISM");
    
    	public static void main(String[] args) {
    		Scanner scan = new Scanner(System.in);
    
    		printIntro();
    
    		int[] usedWords = new int[50];
    		int roundNumber = 1;
    		int totalWords = words.size();
    		boolean continueGame = false;
    
    		do {
    			if (roundNumber > totalWords) {
    				System.out.println("\nYOU DID ALL THE WORDS!!");
    				break;
    			}
    
    			int randomWordIndex;
    			do {
    				randomWordIndex = ((int) (totalWords * Math.random())) + 1;
    			} while (usedWords[randomWordIndex] == 1);
    			usedWords[randomWordIndex] = 1;
    
    			boolean youWon = playRound(scan, words.get(randomWordIndex - 1));
    			if (!youWon) {
    				System.out.print("\nYOU MISSED THAT ONE.  DO YOU WANT ANOTHER WORD? ");
    			} else {
    				System.out.print("\nWANT ANOTHER WORD? ");
    			}
    			final String anotherWordChoice = scan.next();
    
    			if (anotherWordChoice.toUpperCase().equals("YES") || anotherWordChoice.toUpperCase().equals("Y")) {
    				continueGame = true;
    			}
    			roundNumber++;
    		} while (continueGame);
    
    		System.out.println("\nIT'S BEEN FUN!  BYE FOR NOW.");
    	}
    
    	private static boolean playRound(Scanner scan, String word) {
    		char[] letters;
    		char[] discoveredLetters;
    		int misses = 0;
    		Set lettersUsed = new LinkedHashSet<>();//LinkedHashSet maintains the order of characters inserted
    
    		String[][] hangmanPicture = new String[12][12];
    		//initialize the hangman picture
    		for (int i = 0; i < hangmanPicture.length; i++) {
    			for (int j = 0; j < hangmanPicture[i].length; j++) {
    				hangmanPicture[i][j] = " ";
    			}
    		}
    		for (int i = 0; i < hangmanPicture.length; i++) {
    			hangmanPicture[i][0] = "X";
    		}
    		for (int i = 0; i < 7; i++) {
    			hangmanPicture[0][i] = "X";
    		}
    		hangmanPicture[1][6] = "X";
    
    		int totalWordGuesses = 0; //guesses
    
    		int len = word.length();
    		letters = word.toCharArray();
    
    		discoveredLetters = new char[len];
    		Arrays.fill(discoveredLetters, '-');
    
    		boolean validNextGuess = false;
    		char guessLetter = ' ';
    
    		while (misses < 10) {
    			while (!validNextGuess) {
    				printLettersUsed(lettersUsed);
    				printDiscoveredLetters(discoveredLetters);
    
    				System.out.print("WHAT IS YOUR GUESS? ");
    				var tmpRead = scan.next();
    				guessLetter = Character.toUpperCase(tmpRead.charAt(0));
    				if (lettersUsed.contains(guessLetter)) {
    					System.out.println("YOU GUESSED THAT LETTER BEFORE!");
    				} else {
    					lettersUsed.add(guessLetter);
    					totalWordGuesses++;
    					validNextGuess = true;
    				}
    			}
    
    			if (word.indexOf(guessLetter) >= 0) {
    				//replace all occurrences in D$ with G$
    				for (int i = 0; i < letters.length; i++) {
    					if (letters[i] == guessLetter) {
    						discoveredLetters[i] = guessLetter;
    					}
    				}
    				//check if the word is fully discovered
    				boolean isWordDiscovered = true;
    				for (char discoveredLetter : discoveredLetters) {
    					if (discoveredLetter == '-') {
    						isWordDiscovered = false;
    						break;
    					}
    				}
    				if (isWordDiscovered) {
    					System.out.println("YOU FOUND THE WORD!");
    					return true;
    				}
    
    				printDiscoveredLetters(discoveredLetters);
    				System.out.print("WHAT IS YOUR GUESS FOR THE WORD? ");
    				final String wordGuess = scan.next();
    				if (wordGuess.toUpperCase().equals(word)) {
    					System.out.printf("RIGHT!!  IT TOOK YOU %s GUESSES!", totalWordGuesses);
    					return true;
    				} else {
    					System.out.println("WRONG.  TRY ANOTHER LETTER.");
    				}
    			} else {
    				misses = misses + 1;
    				System.out.println("\n\nSORRY, THAT LETTER ISN'T IN THE WORD.");
    				drawHangman(misses, hangmanPicture);
    			}
    			validNextGuess = false;
    		}
    
    		System.out.printf("SORRY, YOU LOSE.  THE WORD WAS %s", word);
    		return false;
    	}
    
    	private static void drawHangman(int m, String[][] hangmanPicture) {
    		switch (m) {
    			case 1:
    				System.out.println("FIRST, WE DRAW A HEAD");
    				hangmanPicture[2][5] = "-";
    				hangmanPicture[2][6] = "-";
    				hangmanPicture[2][7] = "-";
    				hangmanPicture[3][4] = "(";
    				hangmanPicture[3][5] = ".";
    				hangmanPicture[3][7] = ".";
    				hangmanPicture[3][8] = ")";
    				hangmanPicture[4][5] = "-";
    				hangmanPicture[4][6] = "-";
    				hangmanPicture[4][7] = "-";
    				break;
    			case 2:
    				System.out.println("NOW WE DRAW A BODY.");
    				for (var i = 5; i <= 8; i++) {
    					hangmanPicture[i][6] = "X";
    				}
    				break;
    			case 3:
    				System.out.println("NEXT WE DRAW AN ARM.");
    				for (int i = 3; i <= 6; i++) {
    					hangmanPicture[i][i - 1] = "\\";
    				}
    				break;
    			case 4:
    				System.out.println("THIS TIME IT'S THE OTHER ARM.");
    				hangmanPicture[3][10] = "/";
    				hangmanPicture[4][9] = "/";
    				hangmanPicture[5][8] = "/";
    				hangmanPicture[6][7] = "/";
    				break;
    			case 5:
    				System.out.println("NOW, LET'S DRAW THE RIGHT LEG.");
    				hangmanPicture[9][5] = "/";
    				hangmanPicture[10][4] = "/";
    				break;
    			case 6:
    				System.out.println("THIS TIME WE DRAW THE LEFT LEG.");
    				hangmanPicture[9][7] = "\\";
    				hangmanPicture[10][8] = "\\";
    				break;
    			case 7:
    				System.out.println("NOW WE PUT UP A HAND.");
    				hangmanPicture[2][10] = "\\";
    				break;
    			case 8:
    				System.out.println("NEXT THE OTHER HAND.");
    				hangmanPicture[2][2] = "/";
    				break;
    			case 9:
    				System.out.println("NOW WE DRAW ONE FOOT");
    				hangmanPicture[11][9] = "\\";
    				hangmanPicture[11][10] = "-";
    				break;
    			case 10:
    				System.out.println("HERE'S THE OTHER FOOT -- YOU'RE HUNG!!");
    				hangmanPicture[11][2] = "-";
    				hangmanPicture[11][3] = "/";
    				break;
    		}
    		for (int i = 0; i <= 11; i++) {
    			for (int j = 0; j <= 11; j++) {
    				System.out.print(hangmanPicture[i][j]);
    			}
    			System.out.print("\n");
    		}
    
    	}
    
    	private static void printDiscoveredLetters(char[] D$) {
    		System.out.println(new String(D$));
    		System.out.println("\n");
    	}
    
    	private static void printLettersUsed(Set lettersUsed) {
    		System.out.println("\nHERE ARE THE LETTERS YOU USED:");
    		System.out.println(lettersUsed.stream()
    				.map(Object::toString).collect(Collectors.joining(",")));
    		System.out.println("\n");
    	}
    
    	private static void printIntro() {
    		System.out.println("                                HANGMAN");
    		System.out.println("              CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
    		System.out.println("\n\n\n");
    	}
    
    }
    
    
    ================================================
    FILE: 44_Hangman/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 44_Hangman/javascript/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells)
    
    
    ================================================
    FILE: 44_Hangman/javascript/hangman.html
    ================================================
    
    
    HANGMAN
    
    
    
    
    
    
    
    
    
    
    ================================================
    FILE: 44_Hangman/javascript/hangman.js
    ================================================
    // HANGMAN
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    print(tab(32) + "HANGMAN\n");
    print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
    print("\n");
    print("\n");
    print("\n");
    
    var pa = [];
    var la = [];
    var da = [];
    var na = [];
    var ua = [];
    
    var words = ["GUM","SIN","FOR","CRY","LUG","BYE","FLY",
                 "UGLY","EACH","FROM","WORK","TALK","WITH","SELF",
                 "PIZZA","THING","FEIGN","FIEND","ELBOW","FAULT","DIRTY",
                 "BUDGET","SPIRIT","QUAINT","MAIDEN","ESCORT","PICKAX",
                 "EXAMPLE","TENSION","QUININE","KIDNEY","REPLICA","SLEEPER",
                 "TRIANGLE","KANGAROO","MAHOGANY","SERGEANT","SEQUENCE",
                 "MOUSTACHE","DANGEROUS","SCIENTIST","DIFFERENT","QUIESCENT",
                 "MAGISTRATE","ERRONEOUSLY","LOUDSPEAKER","PHYTOTOXIC",
                 "MATRIMONIAL","PARASYMPATHOMIMETIC","THIGMOTROPISM"];
    
    // Main control section
    async function main()
    {
        c = 1;
        n = 50;
        while (1) {
            for (i = 1; i <= 20; i++)
                da[i] = "-";
            for (i = 1; i <= n; i++)
                ua[i] = 0;
            m = 0;
            ns = "";
            for (i = 1; i <= 12; i++) {
                pa[i] = [];
                for (j = 1; j <= 12; j++) {
                    pa[i][j] = " ";
                }
            }
            for (i = 1; i <= 12; i++) {
                pa[i][1] = "X";
            }
            for (i = 1; i <= 7; i++) {
                pa[1][i] = "X";
            }
            pa[2][7] = "X";
            if (c >= n) {
                print("YOU DID ALL THE WORDS!!\n");
                break;
            }
            do {
                q = Math.floor(n * Math.random()) + 1;
            } while (ua[q] == 1) ;
            ua[q] = 1;
            c++;
            t1 = 0;
            as = words[q - 1];
            l = as.length;
            for (i = 1; i <= as.length; i++)
                la[i] = as[i - 1];
            while (1) {
                while (1) {
                    print("HERE ARE THE LETTERS YOU USED:\n");
                    print(ns + "\n");
                    print("\n");
                    for (i = 1; i <= l; i++) {
                        print(da[i]);
                    }
                    print("\n");
                    print("\n");
                    print("WHAT IS YOUR GUESS");
                    str = await input();
                    if (ns.indexOf(str) != -1) {
                        print("YOU GUESSED THAT LETTER BEFORE!\n");
                    } else {
                        break;
                    }
                }
                ns += str;
                t1++;
                r = 0;
                for (i = 1; i <= l; i++) {
                    if (la[i] == str) {
                        da[i] = str;
                        r++;
                    }
                }
                if (r == 0) {
                    m++;
                    print("\n");
                    print("\n");
                    print("SORRY, THAT LETTER ISN'T IN THE WORD.\n");
                    switch (m) {
                        case 1:
                            print("FIRST, WE DRAW A HEAD\n");
                            break;
                        case 2:
                            print("NOW WE DRAW A BODY.\n");
                            break;
                        case 3:
                            print("NEXT WE DRAW AN ARM.\n");
                            break;
                        case 4:
                            print("THIS TIME IT'S THE OTHER ARM.\n");
                            break;
                        case 5:
                            print("NOW, LET'S DRAW THE RIGHT LEG.\n");
                            break;
                        case 6:
                            print("THIS TIME WE DRAW THE LEFT LEG.\n");
                            break;
                        case 7:
                            print("NOW WE PUT UP A HAND.\n");
                            break;
                        case 8:
                            print("NEXT THE OTHER HAND.\n");
                            break;
                        case 9:
                            print("NOW WE DRAW ONE FOOT.\n");
                            break;
                        case 10:
                            print("HERE'S THE OTHER FOOT -- YOU'RE HUNG!!\n");
                            break;
                    }
                    switch (m) {
                        case 1:
                            pa[3][6] = "-";
                            pa[3][7] = "-";
                            pa[3][8] = "-";
                            pa[4][5] = "(";
                            pa[4][6] = ".";
                            pa[4][8] = ".";
                            pa[4][9] = ")";
                            pa[5][6] = "-";
                            pa[5][7] = "-";
                            pa[5][8] = "-";
                            break;
                        case 2:
                            for (i = 6; i <= 9; i++)
                                pa[i][7] = "X";
                            break;
                        case 3:
                            for (i = 4; i <= 7; i++)
                                pa[i][i - 1] = "\\";
                            break;
                        case 4:
                            pa[4][11] = "/";
                            pa[5][10] = "/";
                            pa[6][9] = "/";
                            pa[7][8] = "/";
                            break;
                        case 5:
                            pa[10][6] = "/";
                            pa[11][5] = "/";
                            break;
                        case 6:
                            pa[10][8] = "\\";
                            pa[11][9] = "\\";
                            break;
                        case 7:
                            pa[3][11] = "\\";
                            break;
                        case 8:
                            pa[3][3] = "/";
                            break;
                        case 9:
                            pa[12][10] = "\\";
                            pa[12][11] = "-";
                            break;
                        case 10:
                            pa[12][3] = "-";
                            pa[12][4] = "/";
                            break;
                    }
                    for (i = 1; i <= 12; i++) {
                        str = "";
                        for (j = 1; j <= 12; j++)
                            str += pa[i][j];
                        print(str + "\n");
                    }
                    print("\n");
                    print("\n");
                    if (m == 10) {
                        print("SORRY, YOU LOSE.  THE WORD WAS " + as + "\n");
                        print("YOU MISSED THAT ONE.  DO YOU ");
                        break;
                    }
                } else {
                    for (i = 1; i <= l; i++)
                        if (da[i] == "-")
                            break;
                    if (i > l) {
                        print("YOU FOUND THE WORD!\n");
                        break;
                    }
                    print("\n");
                    for (i = 1; i <= l; i++)
                        print(da[i]);
                    print("\n");
                    print("\n");
                    print("WHAT IS YOUR GUESS FOR THE WORD");
                    bs = await input();
                    if (as == bs) {
                        print("RIGHT!!  IT TOOK YOU " + t1 + " GUESSES!\n");
                        break;
                    }
                    print("WRONG.  TRY ANOTHER LETTER.\n");
                    print("\n");
                }
            }
            print("WANT ANOTHER WORD");
            str = await input();
            if (str != "YES")
                break;
        }
        print("\n");
        print("IT'S BEEN FUN!  BYE FOR NOW.\n");
        // Lines 620 and 990 unused in original
    }
    
    main();
    
    
    ================================================
    FILE: 44_Hangman/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 44_Hangman/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 44_Hangman/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 44_Hangman/perl/hangman.pl
    ================================================
    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    
    # global variables defined here
    
    my(@WORDS) = qw(
        GUM SIN FOR CRY LUG BYE FLY
        UGLY EACH FROM WORK TALK WITH SELF
        PIZZA THING FEIGN FIEND ELBOW FAULT DIRTY
        BUDGET SPIRIT QUAINT MAIDEN ESCORT PICKAX
        EXAMPLE TENSION QUININE KIDNEY REPLICA SLEEPER
        TRIANGLE KANGAROO MAHOGANY SERGEANT SEQUENCE
        MOUSTACHE DANGEROUS SCIENTIST DIFFERENT QUIESCENT
        MAGISTRATE ERRONEOUSLY LOUDSPEAKER PHYTOTOXIC
        MATRIMONIAL PARASYMPATHOMIMETIC THIGMOTROPISM
    );
    my(@PIC,$board,@guessedLetters,$guessCount,$hangCount);
    my(%GUESSED);
    
    # Subroutines defined here.
    
    # init_variables: initialize all of the variables needed
    # (this covers lines 50-90 in the original BASIC program)
    
    sub init_variables {
        @guessedLetters = ();
        @PIC = (
            'XXXXXXX     ',
            'X     X     ',
            'X           ',
            'X           ',
            'X           ',
            'X           ',
            'X           ',
            'X           ',
            'X           ',
            'X           ',
            'X           ',
            'X           ',
        );
        $guessCount = 0; %GUESSED = ();
        $hangCount = 0;
    }
    
    # addchar: given a row & column, put the specified char in that place in @PIC
    sub addchar {
        my($row,$col, $c) = @_;
    
        substr($PIC[$row],$col,1) = $c;
    }
    
    
    # main code starts here
    
    print ' 'x31; print "Hangman\n";
    print ' 'x14; print "Creative Computing  Morristown, New Jersey\n\n\n\n";
    
    # an iteration of the PLAY block is one complete game.
    # There is a continue block that will ask if the user
    # wants to play another game.
    
    PLAY: while (1) {
    
        init_variables();
        # Any words left?
        if (@WORDS == 0) {
            print "You did all the words!\n";
            last PLAY;
        }
        # splice a random word out of the @WORDS array
        my($thisWord) = splice(@WORDS, int(rand(scalar @WORDS)),1);
        # $board is the "game board" of the filled-out word
        # that the user is working on
        $board = '.'x(length $thisWord);
    
        # GUESS loop is run for every time the user guesses a letter
        GUESS: while(1) {
            print "Here are the letters you used:\n";
            printf("%s\n", join(',',@guessedLetters));
            printf("\n\n%s\n", $board);
    
            print "What is your guess for a letter ? ";
            chomp(my $guess = );
            # The %GUESSED hash allows us to quickly identify
            # letters that have already been guessed
            if ($GUESSED{lc $guess}) {
                print "You guessed that letter before!\n\n";
                redo GUESS;
            }
    
            # save the guessed letter
            push @guessedLetters, $guess;
            $GUESSED{lc $guess} = 1;
            ++$guessCount;
    
            # now look for the letter in the $thisWord var
            # and put it into the $board var wherever it
            # shows up. $foundLetter is a flag that indicates
            # whether or not the letter is found.
            my $foundLetter = 0;
            for (my $i = 0; $i < length $thisWord; ++$i) {
                if (lc substr($thisWord,$i,1) eq lc $guess) {
                    $foundLetter = 1;
                    substr($board, $i, 1) = substr($thisWord, $i, 1);
                }
            }
    
            # The user found a letter in the solution!
            if ($foundLetter) {
    
                # Are there any '.' chars left in the board?
                if (index($board, '.') < 0) {
                    print "You found the word!\n\n";
                } else {
                    printf("%s\n\n", $board);
                    print "What is your guess for the word ? ";
                    chomp(my $guessword = );
                    if (lc $thisWord ne lc $guessword) {
                        print "Wrong.  Try another letter.\n";
                        # Go to the next iteration of the GUESS loop
                        next GUESS;
                    }
                    printf("Right! It took you %d %s!\n", $guessCount, ($guessCount == 1 ? 'guess' : 'guesses'));
                }
                # At this point the user has discovered the word and won.
                # This "next" statement takes execution down to the
                # continue block for the PLAY loop;
                next PLAY;
    
            } else {  # didn't find a letter
    
                ++$hangCount;
                print "\n\n\nSorry, that letter isn't in the word.\n";
    
                # The addchar() calls in the block below piece together the
                # hangman graphic, depending on how many wrong letters
                # the user has.
                if ($hangCount == 1) {
                    print "First, we draw a head\n";
                    addchar(2,5,"-");addchar(2,6,"-");addchar(2,7,"-");
                    addchar(3,4,"("); addchar(3,5,"."); addchar(3,7,"."); addchar(3,8,")");
                    addchar(4,5,"-");addchar(4,6,"-");addchar(4,7,"-");
                }
                if ($hangCount == 2) {
                    print "Now we draw a body.\n";
                    for (5 .. 8) {
                        addchar($_, 6, "X");
                    }
                }
                if ($hangCount == 3) {
                    print "Next we draw an arm.\n";
                    for (3 .. 6) {
                        addchar($_, $_-1, "\\");
                    }
                }
                if ($hangCount == 4) {
                    print "This time it's the other arm.\n";
                    addchar(3,10, "/");
                    addchar(4, 9, "/");
                    addchar(5, 8, "/");
                    addchar(6, 7, "/");
                }
                if ($hangCount == 5) {
                    print "Now, let's draw the right leg.\n";
                    addchar( 9,5, "/");
                    addchar(10,4, "/");
                }
                if ($hangCount == 6) {
                    print "This time we draw the left leg.\n";
                    addchar(9,7,"\\");
                    addchar(10,8,"\\");
                }
                if ($hangCount == 7) {
                    print "Now we put up a hand.\n";
                    addchar(2,10,"\\");
                }
                if ($hangCount == 8) {
                    print "Next the other hand.\n";
                    addchar(2,2,"/");
                }
                if ($hangCount == 9) {
                    print "Now we draw one foot\n";
                    addchar(11,9,"\\");
                    addchar(11,10, "-");
                }
                if ($hangCount == 10) {
                    print "Here's the other foot -- you're hung!!\n";
                    addchar(11,2,"-");
                    addchar(11,3, "/");
                }
    
                printf("$_\n") for @PIC;
                print "\n\n";
    
                # Next guess if the user has not lost
                if ($hangCount < 10) {
                    next GUESS;
                }
    
                printf("Sorry, you lose.  The word was %s\n", $thisWord);
                next PLAY;
    
            } # didn't find a letter block
        } # GUESS block
    } # PLAY block
    
    # This block is reached either by the player winning (see the "next PLAY")
    # statement) or by the user losing (as the PLAY block is complete and
    # execution naturally comes to this continue block).
    continue {
        print "Want another word ? ";
        chomp(my $in = );
        if ($in !~ m/^y/i) {
            # Exit the PLAY loop
            print "\nIt's been fun!  Bye for now.\n\n";
            last PLAY;
        }
        # At this point execution goes to the start of the PLAY block,
        # meaning a new game
    }
    
    
    ================================================
    FILE: 44_Hangman/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 44_Hangman/python/hangman.py
    ================================================
    #!/usr/bin/env python3
    
    """
    HANGMAN
    
    Converted from BASIC to Python by Trevor Hobson and Daniel Piron
    """
    
    import random
    from typing import List
    
    
    class Canvas:
        """For drawing text-based figures"""
    
        def __init__(self, width: int = 12, height: int = 12, fill: str = " ") -> None:
            self._buffer = []
            for _ in range(height):
                line = ["" for _ in range(width)]
                self._buffer.append(line)
    
            self.clear()
    
        def clear(self, fill: str = " ") -> None:
            for row in self._buffer:
                for x in range(len(row)):
                    row[x] = fill
    
        def render(self) -> str:
            lines = ["".join(line) for line in self._buffer]
            return "\n".join(lines)
    
        def put(self, s: str, x: int, y: int) -> None:
            # In an effort to avoid distorting the drawn image, only write the
            # first character of the given string to the buffer.
            self._buffer[y][x] = s[0]
    
    
    def init_gallows(canvas: Canvas) -> None:
        for i in range(12):
            canvas.put("X", 0, i)
        for i in range(7):
            canvas.put("X", i, 0)
        canvas.put("X", 6, 1)
    
    
    def draw_head(canvas: Canvas) -> None:
        canvas.put("-", 5, 2)
        canvas.put("-", 6, 2)
        canvas.put("-", 7, 2)
        canvas.put("(", 4, 3)
        canvas.put(".", 5, 3)
        canvas.put(".", 7, 3)
        canvas.put(")", 8, 3)
        canvas.put("-", 5, 4)
        canvas.put("-", 6, 4)
        canvas.put("-", 7, 4)
    
    
    def draw_body(canvas: Canvas) -> None:
        for i in range(5, 9, 1):
            canvas.put("X", 6, i)
    
    
    def draw_right_arm(canvas: Canvas) -> None:
        for i in range(3, 7):
            canvas.put("\\", i - 1, i)
    
    
    def draw_left_arm(canvas: Canvas) -> None:
        canvas.put("/", 10, 3)
        canvas.put("/", 9, 4)
        canvas.put("/", 8, 5)
        canvas.put("/", 7, 6)
    
    
    def draw_right_leg(canvas: Canvas) -> None:
        canvas.put("/", 5, 9)
        canvas.put("/", 4, 10)
    
    
    def draw_left_leg(canvas: Canvas) -> None:
        canvas.put("\\", 7, 9)
        canvas.put("\\", 8, 10)
    
    
    def draw_left_hand(canvas: Canvas) -> None:
        canvas.put("\\", 10, 2)
    
    
    def draw_right_hand(canvas: Canvas) -> None:
        canvas.put("/", 2, 2)
    
    
    def draw_left_foot(canvas: Canvas) -> None:
        canvas.put("\\", 9, 11)
        canvas.put("-", 10, 11)
    
    
    def draw_right_foot(canvas: Canvas) -> None:
        canvas.put("-", 2, 11)
        canvas.put("/", 3, 11)
    
    
    PHASES = (
        ("First, we draw a head", draw_head),
        ("Now we draw a body.", draw_body),
        ("Next we draw an arm.", draw_right_arm),
        ("this time it's the other arm.", draw_left_arm),
        ("Now, let's draw the right leg.", draw_right_leg),
        ("This time we draw the left leg.", draw_left_leg),
        ("Now we put up a hand.", draw_left_hand),
        ("Next the other hand.", draw_right_hand),
        ("Now we draw one foot", draw_left_foot),
        ("Here's the other foot -- you're hung!!", draw_right_foot),
    )
    
    
    words = [
        "GUM",
        "SIN",
        "FOR",
        "CRY",
        "LUG",
        "BYE",
        "FLY",
        "UGLY",
        "EACH",
        "FROM",
        "WORK",
        "TALK",
        "WITH",
        "SELF",
        "PIZZA",
        "THING",
        "FEIGN",
        "FIEND",
        "ELBOW",
        "FAULT",
        "DIRTY",
        "BUDGET",
        "SPIRIT",
        "QUAINT",
        "MAIDEN",
        "ESCORT",
        "PICKAX",
        "EXAMPLE",
        "TENSION",
        "QUININE",
        "KIDNEY",
        "REPLICA",
        "SLEEPER",
        "TRIANGLE",
        "KANGAROO",
        "MAHOGANY",
        "SERGEANT",
        "SEQUENCE",
        "MOUSTACHE",
        "DANGEROUS",
        "SCIENTIST",
        "DIFFERENT",
        "QUIESCENT",
        "MAGISTRATE",
        "ERRONEOUSLY",
        "LOUDSPEAKER",
        "PHYTOTOXIC",
        "MATRIMONIAL",
        "PARASYMPATHOMIMETIC",
        "THIGMOTROPISM",
    ]
    
    
    def play_game(guess_target: str) -> None:
        """Play one round of the game"""
        wrong_guesses = 0
        guess_progress = ["-"] * len(guess_target)
        guess_list: List[str] = []
    
        gallows = Canvas()
        init_gallows(gallows)
    
        guess_count = 0
        while True:
            print("Here are the letters you used:")
            print(",".join(guess_list) + "\n")
            print("".join(guess_progress) + "\n")
            guess_letter = ""
            guess_word = ""
            while not guess_letter:
    
                guess_letter = input("What is your guess? ").upper()[0]
                if not guess_letter.isalpha():
                    guess_letter = ""
                    print("Only letters are allowed!")
                elif guess_letter in guess_list:
                    guess_letter = ""
                    print("You guessed that letter before!")
    
            guess_list.append(guess_letter)
            guess_count += 1
            if guess_letter in guess_target:
                indices = [
                    i for i, letter in enumerate(guess_target) if letter == guess_letter
                ]
                for i in indices:
                    guess_progress[i] = guess_letter
                if "".join(guess_progress) == guess_target:
                    print("You found the word!")
                    break
                else:
                    print("\n" + "".join(guess_progress) + "\n")
                    while not guess_word:
                        guess_word = input("What is your guess for the word? ").upper()
                        if not guess_word.isalpha():
                            guess_word = ""
                            print("Only words are allowed!")
                    if guess_word == guess_target:
                        print("Right!! It took you", guess_count, "guesses!")
                        break
            else:
                comment, draw_bodypart = PHASES[wrong_guesses]
    
                print(comment)
                draw_bodypart(gallows)
                print(gallows.render())
    
                wrong_guesses += 1
                print("Sorry, that letter isn't in the word.")
    
                if wrong_guesses == 10:
                    print(f"Sorry, you lose. The word was {guess_target}")
                    break
    
    
    def main() -> None:
        print(" " * 32 + "HANGMAN")
        print(" " * 15 + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n")
    
        random.shuffle(words)
        current_word = 0
        word_count = len(words)
    
        keep_playing = True
        while keep_playing:
    
            play_game(words[current_word])
            current_word += 1
    
            if current_word == word_count:
                print("You did all the words!!")
                keep_playing = False
            else:
                keep_playing = (
                    input("Want another word? (yes or no) ").lower().startswith("y")
                )
    
        print("It's been fun! Bye for now.")
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 44_Hangman/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 44_Hangman/ruby/hangman.rb
    ================================================
    class Canvas
    	BUFFER = []
    	def initialize width = 12, height = 12, fill = " "
    		for i in (0...height) do
    			line = []
    			for i in (0...width) do
    				line << ""
    			end
    			BUFFER << line
    		end
    
    		clear
    	end
    
    	def render
    		lines = []
    		for line in BUFFER do
    			lines << line.join("")
    		end
    
    		return lines.join("\n")
    	end
    
    	def put s, x, y
    		BUFFER[y][x] = s[0]
    	end
    
    	private
    		def clear fill = " "
    			for row in BUFFER do
    				for x in (0...(row.length)) do
    					row[x] = fill
    				end
    			end
    		end
    end
    
    def init_gallows canvas
    	for i in (0...12) do
    		canvas.put("X", 0, i)
    	end
    
    	for i in (0...7) do
    		canvas.put("X", i, 0)
    	end
    
    	canvas.put("X", 6, 1)
    end
    
    def draw_head canvas
    	canvas.put("-", 5, 2)
    	canvas.put("-", 6, 2)
    	canvas.put("-", 7, 2)
    	canvas.put("(", 4, 3)
    	canvas.put(".", 5, 3)
    	canvas.put(".", 7, 3)
    	canvas.put(")", 8, 3)
    	canvas.put("-", 5, 4)
    	canvas.put("-", 6, 4)
    	canvas.put("-", 7, 4)
    end
    
    def draw_body canvas
    	for i in (5...9) do
    		canvas.put("X", 6, i)
    	end
    end
    
    def draw_right_arm canvas
    	for i in (3...8) do
    		canvas.put("\\", i - 1, i)
    	end
    end
    
    def draw_left_arm canvas
    	canvas.put("/", 10, 3)
    	canvas.put("/", 9, 4)
    	canvas.put("/", 8, 5)
    	canvas.put("/", 7, 6)
    end
    
    def draw_right_leg canvas
    	canvas.put("/", 5, 9)
    	canvas.put("/", 4, 10)
    end
    
    def draw_left_leg canvas
    	canvas.put("\\", 7, 9)
    	canvas.put("\\", 8, 10)
    end
    
    def draw_left_hand canvas
    	canvas.put("\\", 10, 2)
    end
    
    def draw_right_hand canvas
    	canvas.put("/", 2, 2)
    end
    
    def draw_left_foot canvas
    	canvas.put("\\", 9, 11)
    	canvas.put("-", 10, 11)
    end
    
    def draw_right_foot canvas
    	canvas.put("-", 2, 11)
    	canvas.put("/", 3, 11)
    end
    
    PHASES = [
    	["First, we draw a head", 'draw_head'],
    	["Now we draw a body.", 'draw_body'],
    	["Next we draw an arm.", 'draw_right_arm'],
    	["this time it's the other arm.", 'draw_left_arm'],
    	["Now, let's draw the right leg.", 'draw_right_leg'],
    	["This time we draw the left leg.", 'draw_left_leg'],
    	["Now we put up a hand.", 'draw_left_hand'],
    	["Next the other hand.", 'draw_right_hand'],
    	["Now we draw one foot", 'draw_left_foot'],
    	["Here's the other foot -- you're hung!!", 'draw_right_foot'],
    ]
    
    WORDS = [
    	"GUM",
    	"SIN",
    	"FOR",
    	"CRY",
    	"LUG",
    	"BYE",
    	"FLY",
    	"UGLY",
    	"EACH",
    	"FROM",
    	"WORK",
    	"TALK",
    	"WITH",
    	"SELF",
    	"PIZZA",
    	"THING",
    	"FEIGN",
    	"FIEND",
    	"ELBOW",
    	"FAULT",
    	"DIRTY",
    	"BUDGET",
    	"SPIRIT",
    	"QUAINT",
    	"MAIDEN",
    	"ESCORT",
    	"PICKAX",
    	"EXAMPLE",
    	"TENSION",
    	"QUININE",
    	"KIDNEY",
    	"REPLICA",
    	"SLEEPER",
    	"TRIANGLE",
    	"KANGAROO",
    	"MAHOGANY",
    	"SERGEANT",
    	"SEQUENCE",
    	"MOUSTACHE",
    	"DANGEROUS",
    	"SCIENTIST",
    	"DIFFERENT",
    	"QUIESCENT",
    	"MAGISTRATE",
    	"ERRONEOUSLY",
    	"LOUDSPEAKER",
    	"PHYTOTOXIC",
    	"MATRIMONIAL",
    	"PARASYMPATHOMIMETIC",
    	"THIGMOTROPISM",
    ]
    
    def play_game guess_target
    	wrong_guesses = 0
    	guess_progress = ["-"] * guess_target.length
    	guess_list = []
    
    	gallows = Canvas.new
    	init_gallows(gallows)
    
    	guess_count = 0
    	while true
    		puts "Here are the letters you used:"
    		puts "#{guess_list.join(",")}\n"
    		puts "#{guess_progress.join("")}\n"
    
    		guess_letter = ""
    		guess_word = ""
    		while guess_letter == ""
    			print "What is your guess? "
    			guess_letter = gets.chomp!.upcase[0]
    			if !guess_letter.match?(/[[:alpha:]]/)
    				guess_letter = ""
    				puts "Only letters are allowed!"
    			elsif guess_list.include?(guess_letter)
    				guess_letter = ""
    				puts "You guessed that letter before!"
    			end
    		end
    
    		guess_list << guess_letter
    		guess_count += 1
    
    		if guess_target.include?(guess_letter)
    			indices = (0...guess_target.length).find_all { |i| guess_target[i,1] == guess_letter }
    
    			for i in indices do
    				guess_progress[i] = guess_letter
    			end
    
    			if guess_progress.join("") == guess_target
    				puts "You found the word!"
    				break
    			else
    				puts "\n#{guess_progress.join("")}\n"
    
    				while guess_word == ""
    					print "What is your guess for the word? "
    					guess_word = gets.chomp!.upcase
    					if !guess_word.match?(/[[:alpha:]]/)
    						guess_word = ""
    						puts "Only words are allowed!"
    					end
    				end
    
    				if guess_word == guess_target
    					puts "Right!! It took you #{guess_count} guesses!"
    					break
    				end
    			end
    		else
    			comment, draw_bodypart = PHASES[wrong_guesses]
    
    			puts comment
    			method(draw_bodypart).call(gallows)
    			puts gallows.render()
    
    			wrong_guesses += 1
    			puts "Sorry, that letter isn't in the word."
    
    			if wrong_guesses == 10
    				puts "Sorry, you lose. The word was #{guess_target}"
    				break
    			end
    		end
    	end
    end
    
    
    def main
    	puts "#{(" " * 32)}HANGMAN"
    
    	shuffled = WORDS.shuffle(random: Random.new)
    	current_word = 0
    	word_count = shuffled.length
    
    	keep_playing = true
    	while keep_playing
    
    		play_game(shuffled[current_word])
    		current_word += 1
    
    		if current_word == word_count
    			puts "You did all the words!!"
    			keep_playing = false
    		else
    			print "Want another word? (yes or no) "
    			a = gets.chomp!.upcase
    			keep_playing = true if a == 'Y' || a == 'y' || a == 'Yes' || a == 'YES' || a == 'yes'
    		end
    	end
    	puts "It's been fun! Bye for now."
    end
    
    if __FILE__ == $0
    	main
    end
    
    ================================================
    FILE: 44_Hangman/vbnet/Hangman.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Hangman", "Hangman.vbproj", "{048C4117-70FC-4457-9B1D-BBD1FEDFEB76}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{048C4117-70FC-4457-9B1D-BBD1FEDFEB76}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{048C4117-70FC-4457-9B1D-BBD1FEDFEB76}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{048C4117-70FC-4457-9B1D-BBD1FEDFEB76}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{048C4117-70FC-4457-9B1D-BBD1FEDFEB76}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 44_Hangman/vbnet/Hangman.vbproj
    ================================================
    
      
        Exe
        Hangman
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 44_Hangman/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 45_Hello/README.md
    ================================================
    ### Hello
    
    This is a sample of one of the great number of conversational programs. In a sense, it is like a CAI program except that its responses are just good fun. Whenever a computer is exhibited at a convention or conference with people that have not used a computer before, the conversational programs seem to get the first activity.
    
    In this particular program, the computer dispenses advice on various problems such as sex, health, money, or job.
    
    David Ahl is the author of HELLO.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=82)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=97)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 45_Hello/csharp/Hello.csproj
    ================================================
    
      
        Exe
        net6.0
        10
        enable
        enable
      
    
    
    
    ================================================
    FILE: 45_Hello/csharp/Hello.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hello", "Hello.csproj", "{8ECD89E1-D9E1-4FA5-97B6-B1E68FE1CA16}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{8ECD89E1-D9E1-4FA5-97B6-B1E68FE1CA16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{8ECD89E1-D9E1-4FA5-97B6-B1E68FE1CA16}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{8ECD89E1-D9E1-4FA5-97B6-B1E68FE1CA16}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{8ECD89E1-D9E1-4FA5-97B6-B1E68FE1CA16}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 45_Hello/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 45_Hello/hello.bas
    ================================================
    2 PRINT TAB(33);"HELLO"
    4 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    6 PRINT: PRINT: PRINT
    10 PRINT "HELLO.  MY NAME IS CREATIVE COMPUTER."
    20 PRINT: PRINT: INPUT "WHAT'S YOUR NAME";N$: PRINT
    30 PRINT "HI THERE, ";N$;", ARE YOU ENJOYING YOURSELF HERE";
    40 INPUT B$: PRINT
    50 IF B$="YES" THEN 70
    55 IF B$="NO" THEN 80
    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
    70 PRINT "I'M GLAD TO HEAR THAT, ";N$;".": PRINT
    75 GOTO 100
    80 PRINT "OH, I'M SORRY TO HEAR THAT, ";N$;". MAYBE WE CAN"
    85 PRINT "BRIGHTEN UP YOUR VISIT A BIT."
    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
    130 IF C$="SEX" THEN 200
    132 IF C$="HEALTH" THEN 180
    134 IF C$="MONEY" THEN 160
    136 IF C$="JOB" THEN 145
    138 PRINT "OH, ";N$;", YOUR ANSWER OF ";C$;" IS GREEK TO ME."
    140 GOTO 250
    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
    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
    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
    200 INPUT "IS YOUR PROBLEM TOO MUCH OR TOO LITTLE";D$: PRINT
    210 IF D$="TOO MUCH" THEN 220
    212 IF D$="TOO LITTLE" THEN 230
    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
    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
    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
    255 PRINT "ANY MORE PROBLEMS YOU WANT SOLVED, ";N$;
    260 INPUT E$: PRINT
    270 IF E$="YES" THEN 280
    273 IF E$="NO" THEN 300
    275 PRINT "JUST A SIMPLE 'YES' OR 'NO' PLEASE, ";N$;"."
    277 GOTO 255
    280 PRINT "WHAT KIND (SEX, MONEY, HEALTH, JOB)";
    282 GOTO 125
    300 PRINT
    302 PRINT "THAT WILL BE $5.00 FOR THE ADVICE, ";N$;"."
    305 PRINT "PLEASE LEAVE THE MONEY ON THE TERMINAL."
    307 FOR I=1 TO 2000: NEXT I
    310 PRINT: PRINT: PRINT
    315 PRINT "DID YOU LEAVE THE MONEY";
    320 INPUT G$: PRINT
    325 IF G$="YES" THEN 350
    330 IF G$="NO" THEN 370
    335 PRINT "YOUR ANSWER OF '";G$;"' CONFUSES ME, ";N$;"."
    340 PRINT "PLEASE RESPOND WITH 'YES' OR 'NO'.": GOTO 315
    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
    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
    390 PRINT "NICE MEETING YOU, ";N$;", HAVE A NICE DAY."
    400 REM
    999 END
    
    
    ================================================
    FILE: 45_Hello/java/Hello.java
    ================================================
    import java.util.Scanner;
    
    /**
     * Game of Hello
     * 

    * Based on the BASIC game of Hello here * https://github.com/coding-horror/basic-computer-games/blob/main/45%20Hello/hello.bas *

    * Note: The idea was to create a version of the 1970's BASIC game in Java, without introducing * new features - no additional text, error checking, etc has been added. * * Converted from BASIC to Java by Darren Cardenas. */ public class Hello { private static final int MONEY_WAIT_MS = 3000; private final boolean goodEnding = false; private final Scanner scan; // For user input public Hello() { scan = new Scanner(System.in); } // End of constructor Hello public void play() { showIntro(); startGame(); } // End of method play private static void showIntro() { System.out.println(" ".repeat(32) + "HELLO"); System.out.println(" ".repeat(14) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println("\n\n"); } // End of method showIntro private void startGame() { boolean moreProblems = true; String userCategory = ""; String userName = ""; String userResponse = ""; // Name question System.out.println("HELLO. MY NAME IS CREATIVE COMPUTER.\n\n"); System.out.print("WHAT'S YOUR NAME? "); userName = scan.nextLine(); System.out.println(""); // Enjoyment question System.out.print("HI THERE, " + userName + ", ARE YOU ENJOYING YOURSELF HERE? "); while (true) { userResponse = scan.nextLine(); System.out.println(""); if (userResponse.toUpperCase().equals("YES")) { System.out.println("I'M GLAD TO HEAR THAT, " + userName + ".\n"); break; } else if (userResponse.toUpperCase().equals("NO")) { System.out.println("OH, I'M SORRY TO HEAR THAT, " + userName + ". MAYBE WE CAN"); System.out.println("BRIGHTEN UP YOUR VISIT A BIT."); break; } else { System.out.println(userName + ", I DON'T UNDERSTAND YOUR ANSWER OF '" + userResponse + "'."); System.out.print("PLEASE ANSWER 'YES' OR 'NO'. DO YOU LIKE IT HERE? "); } } // Category question System.out.println(""); System.out.println("SAY, " + userName + ", I CAN SOLVE ALL KINDS OF PROBLEMS EXCEPT"); System.out.println("THOSE DEALING WITH GREECE. WHAT KIND OF PROBLEMS DO"); System.out.print("YOU HAVE (ANSWER SEX, HEALTH, MONEY, OR JOB)? "); while (moreProblems) { userCategory = scan.nextLine(); System.out.println(""); // Sex advice if (userCategory.toUpperCase().equals("SEX")) { System.out.print("IS YOUR PROBLEM TOO MUCH OR TOO LITTLE? "); userResponse = scan.nextLine(); System.out.println(""); while (true) { if (userResponse.toUpperCase().equals("TOO MUCH")) { System.out.println("YOU CALL THAT A PROBLEM?!! I SHOULD HAVE SUCH PROBLEMS!"); System.out.println("IF IT BOTHERS YOU, " + userName + ", TAKE A COLD SHOWER."); break; } else if (userResponse.toUpperCase().equals("TOO LITTLE")) { System.out.println("WHY ARE YOU HERE IN SUFFERN, " + userName + "? YOU SHOULD BE"); System.out.println("IN TOKYO OR NEW YORK OR AMSTERDAM OR SOMEPLACE WITH SOME"); System.out.println("REAL ACTION."); break; } else { System.out.println("DON'T GET ALL SHOOK, " + userName + ", JUST ANSWER THE QUESTION"); System.out.print("WITH 'TOO MUCH' OR 'TOO LITTLE'. WHICH IS IT? "); userResponse = scan.nextLine(); } } } // Health advice else if (userCategory.toUpperCase().equals("HEALTH")) { System.out.println("MY ADVICE TO YOU " + userName + " IS:"); System.out.println(" 1. TAKE TWO ASPRIN"); System.out.println(" 2. DRINK PLENTY OF FLUIDS (ORANGE JUICE, NOT BEER!)"); System.out.println(" 3. GO TO BED (ALONE)"); } // Money advice else if (userCategory.toUpperCase().equals("MONEY")) { System.out.println("SORRY, " + userName + ", I'M BROKE TOO. WHY DON'T YOU SELL"); System.out.println("ENCYCLOPEADIAS OR MARRY SOMEONE RICH OR STOP EATING"); System.out.println("SO YOU WON'T NEED SO MUCH MONEY?"); } // Job advice else if (userCategory.toUpperCase().equals("JOB")) { System.out.println("I CAN SYMPATHIZE WITH YOU " + userName + ". I HAVE TO WORK"); System.out.println("VERY LONG HOURS FOR NO PAY -- AND SOME OF MY BOSSES"); System.out.println("REALLY BEAT ON MY KEYBOARD. MY ADVICE TO YOU, " + userName + ","); System.out.println("IS TO OPEN A RETAIL COMPUTER STORE. IT'S GREAT FUN."); } else { System.out.println("OH, " + userName + ", YOUR ANSWER OF " + userCategory + " IS GREEK TO ME."); } // More problems question while (true) { System.out.println(""); System.out.print("ANY MORE PROBLEMS YOU WANT SOLVED, " + userName + "? "); userResponse = scan.nextLine(); System.out.println(""); if (userResponse.toUpperCase().equals("YES")) { System.out.print("WHAT KIND (SEX, MONEY, HEALTH, JOB)? "); break; } else if (userResponse.toUpperCase().equals("NO")) { moreProblems = false; break; } else { System.out.println("JUST A SIMPLE 'YES' OR 'NO' PLEASE, " + userName + "."); } } } // Payment question System.out.println(""); System.out.println("THAT WILL BE $5.00 FOR THE ADVICE, " + userName + "."); System.out.println("PLEASE LEAVE THE MONEY ON THE TERMINAL."); // Pause try { Thread.sleep(MONEY_WAIT_MS); } catch (Exception e) { System.out.println("Caught Exception: " + e.getMessage()); } System.out.println("\n\n"); while (true) { System.out.print("DID YOU LEAVE THE MONEY? "); userResponse = scan.nextLine(); System.out.println(""); if (userResponse.toUpperCase().equals("YES")) { System.out.println("HEY, " + userName + "??? YOU LEFT NO MONEY AT ALL!"); System.out.println("YOU ARE CHEATING ME OUT OF MY HARD-EARNED LIVING."); System.out.println(""); System.out.println("WHAT A RIP OFF, " + userName + "!!!\n"); break; } else if (userResponse.toUpperCase().equals("NO")) { System.out.println("THAT'S HONEST, " + userName + ", BUT HOW DO YOU EXPECT"); System.out.println("ME TO GO ON WITH MY PSYCHOLOGY STUDIES IF MY PATIENTS"); System.out.println("DON'T PAY THEIR BILLS?"); break; } else { System.out.println("YOUR ANSWER OF '" + userResponse + "' CONFUSES ME, " + userName + "."); System.out.println("PLEASE RESPOND WITH 'YES' OR 'NO'."); } } // Legacy included unreachable code if (goodEnding) { System.out.println("NICE MEETING YOU, " + userName + ", HAVE A NICE DAY."); } else { System.out.println(""); System.out.println("TAKE A WALK, " + userName + ".\n"); } } // End of method startGame public static void main(String[] args) { Hello hello = new Hello(); hello.play(); } // End of method main } // End of class Hello ================================================ FILE: 45_Hello/java/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Oracle Java](https://openjdk.java.net/) ================================================ FILE: 45_Hello/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 45_Hello/javascript/hello.html ================================================ HELLO

    
    
    
    
    
    
    ================================================
    FILE: 45_Hello/javascript/hello.js
    ================================================
    // HELLO
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    // Main control section
    async function main()
    {
        print(tab(33) + "HELLO\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        print("HELLO.  MY NAME IS CREATIVE COMPUTER.\n");
        print("\n");
        print("\n");
        print("WHAT'S YOUR NAME");
        ns = await input();
        print("\n");
        print("HI THERE, " + ns + ", ARE YOU ENJOYING YOURSELF HERE");
        while (1) {
            bs = await input();
            print("\n");
            if (bs == "YES") {
                print("I'M GLAD TO HEAR THAT, " + ns + ".\n");
                print("\n");
                break;
            } else if (bs == "NO") {
                print("OH, I'M SORRY TO HEAR THAT, " + ns + ". MAYBE WE CAN\n");
                print("BRIGHTEN UP YOUR VISIT A BIT.\n");
                break;
            } else {
                print("PLEASE ANSWER 'YES' OR 'NO'.  DO YOU LIKE IT HERE");
            }
        }
        print("\n");
        print("SAY, " + ns + ", I CAN SOLVED ALL KINDS OF PROBLEMS EXCEPT\n");
        print("THOSE DEALING WITH GREECE.  WHAT KIND OF PROBLEMS DO\n");
        print("YOU HAVE (ANSWER SEX, HEALTH, MONEY, OR JOB)");
        while (1) {
            cs = await input();
            print("\n");
            if (cs != "SEX" && cs != "HEALTH" && cs != "MONEY" && cs != "JOB") {
                print("OH, " + ns + ", YOUR ANSWER OF " + cs + " IS GREEK TO ME.\n");
            } else if (cs == "JOB") {
                print("I CAN SYMPATHIZE WITH YOU " + ns + ".  I HAVE TO WORK\n");
                print("VERY LONG HOURS FOR NO PAY -- AND SOME OF MY BOSSES\n");
                print("REALLY BEAT ON MY KEYBOARD.  MY ADVICE TO YOU, " + ns + ",\n");
                print("IS TO OPEN A RETAIL COMPUTER STORE.  IT'S GREAT FUN.\n");
            } else if (cs == "MONEY") {
                print("SORRY, " + ns + ", I'M BROKE TOO.  WHY DON'T YOU SELL\n");
                print("ENCYCLOPEADIAS OR MARRY SOMEONE RICH OR STOP EATING\n");
                print("SO YOU WON'T NEED SO MUCH MONEY?\n");
            } else if (cs == "HEALTH") {
                print("MY ADVICE TO YOU " + ns + " IS:\n");
                print("     1.  TAKE TWO ASPRIN\n");
                print("     2.  DRINK PLENTY OF FLUIDS (ORANGE JUICE, NOT BEER!)\n");
                print("     3.  GO TO BED (ALONE)\n");
            } else {
                print("IS YOUR PROBLEM TOO MUCH OR TOO LITTLE");
                while (1) {
                    ds = await input();
                    print("\n");
                    if (ds == "TOO MUCH") {
                        print("YOU CALL THAT A PROBLEM?!!  I SHOULD HAVE SUCH PROBLEMS!\n");
                        print("IF IT BOTHERS YOU, " + ns + ", TAKE A COLD SHOWER.\n");
                        break;
                    } else if (ds == "TOO LITTLE") {
                        print("WHY ARE YOU HERE IN SUFFERN, " + ns + "?  YOU SHOULD BE\n");
                        print("IN TOKYO OR NEW YORK OR AMSTERDAM OR SOMEPLACE WITH SOME\n");
                        print("REAL ACTION.\n");
                        break;
                    } else {
                        print("DON'T GET ALL SHOOK, " + ns + ", JUST ANSWER THE QUESTION\n");
                        print("WITH 'TOO MUCH' OR 'TOO LITTLE'.  WHICH IS IT");
                    }
                }
            }
            print("\n");
            print("ANY MORE PROBLEMS YOU WANT SOLVED, " + ns);
            es = await input();
            print("\n");
            if (es == "YES") {
                print("WHAT KIND (SEX, MONEY, HEALTH, JOB)");
            } else if (es == "NO") {
                print("THAT WILL BE $5.00 FOR THE ADVICE, " + ns + ".\n");
                print("PLEASE LEAVE THE MONEY ON THE TERMINAL.\n");
                print("\n");
    //            d = new Date().valueOf();
    //            while (new Date().valueOf() - d < 2000) ;
                print("\n");
                print("\n");
                while (1) {
                    print("DID YOU LEAVE THE MONEY");
                    gs = await input();
                    print("\n");
                    if (gs == "YES") {
                        print("HEY, " + ns + "??? YOU LEFT NO MONEY AT ALL!\n");
                        print("YOU ARE CHEATING ME OUT OF MY HARD-EARNED LIVING.\n");
                        print("\n");
                        print("WHAT A RIP OFF, " + ns + "!!!\n");
                        print("\n");
                        break;
                    } else if (gs == "NO") {
                        print("THAT'S HONEST, " + ns + ", BUT HOW DO YOU EXPECT\n");
                        print("ME TO GO ON WITH MY PSYCHOLOGY STUDIES IF MY PATIENT\n");
                        print("DON'T PAY THEIR BILLS?\n");
                        break;
                    } else {
                        print("YOUR ANSWER OF '" + gs + "' CONFUSES ME, " + ns + ".\n");
                        print("PLEASE RESPOND WITH 'YES' OR 'NO'.\n");
                    }
                }
                break;
            }
        }
        print("\n");
        print("TAKE A WALK, " + ns + ".\n");
        print("\n");
        print("\n");
        // Line 390 not used in original
    }
    
    main();
    
    
    ================================================
    FILE: 45_Hello/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 45_Hello/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 45_Hello/lua/hello.lua
    ================================================
    -- HELLO
    --
    -- Converted from BASIC to Lua by Recanman
    
    local function tab(space)
        local str = ""
    
        for _ = space, 1, -1 do
            str = str .. " "
        end
    
        return str
    end
    
    -- reused from Bagels.lua
    function getInput(prompt)
        io.write(prompt)
        io.flush()
        local input = io.read("l")
        if not input then  --- test for EOF
            print("GOODBYE")
            os.exit(0)
        end
        return input
    end
    
    print(tab(33) .. "HELLO\n")
    print(tab(15) .. "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n")
    print("\n")
    print("\n")
    print("\n")
    
    print("HELLO.  MY NAME IS CREATIVE COMPUTER.\n")
    print("\n")
    print("\n")
    
    print("WHAT'S YOUR NAME")
    local ns = getInput("? ")
    
    print("\n")
    print("HI THERE, " .. ns .. ", ARE YOU ENJOYING YOURSELF HERE")
    
    while true do
        local bs = getInput("? ")
        print("\n")
        if bs == "YES" then
            print("I'M GLAD TO HEAR THAT, " .. ns .. ".\n")
            print("\n")
            break
        elseif bs == "NO" then
            print("OH, I'M SORRY TO HEAR THAT, " .. ns .. ". MAYBE WE CAN\n")
            print("BRIGHTEN UP YOUR VISIT A BIT.\n")
            break
        else
            print("PLEASE ANSWER 'YES' OR 'NO'.  DO YOU LIKE IT HERE")
        end
    end
    
    local function main()
        print("\n")
        print("SAY, " .. ns .. ", I CAN SOLVED ALL KINDS OF PROBLEMS EXCEPT\n")
        print("THOSE DEALING WITH GREECE.  WHAT KIND OF PROBLEMS DO\n")
        print("YOU HAVE (ANSWER SEX, HEALTH, MONEY, OR JOB)")
    
        while true do
            local cs = getInput("? ")
            print("\n")
    
            if cs ~= "SEX" and cs ~= "HEALTH" and cs ~= "MONEY" and cs ~= "JOB" then
                print("OH, " .. ns .. ", YOUR ANSWER OF " .. cs .. " IS GREEK TO ME.\n")
            elseif cs == "JOB" then
                print("I CAN SYMPATHIZE WITH YOU " .. ns .. ".  I HAVE TO WORK\n")
                print("VERY LONG HOURS FOR NO PAY -- AND SOME OF MY BOSSES\n")
                print("REALLY BEAT ON MY KEYBOARD.  MY ADVICE TO YOU, " .. ns .. ",\n")
                print("IS TO OPEN A RETAIL COMPUTER STORE.  IT'S GREAT FUN.\n")
            elseif cs == "MONEY" then
                print("SORRY, " .. ns .. ", I'M BROKE TOO.  WHY DON'T YOU SELL\n")
                print("ENCYCLOPEADIAS OR MARRY SOMEONE RICH OR STOP EATING\n")
                print("SO YOU WON'T NEED SO MUCH MONEY?\n")
            elseif cs == "HEALTH" then
                print("MY ADVICE TO YOU " .. ns .. " IS:\n")
                print(tab(5) .. "1.  TAKE TWO ASPRIN\n")
                print(tab(5) .. "2.  DRINK PLENTY OF FLUIDS (ORANGE JUICE, NOT BEER!)\n")
                print(tab(5) .. "3.  GO TO BED (ALONE)\n")
            elseif cs == "SEX" then
                print("IS YOUR PROBLEM TOO MUCH OR TOO LITTLE")
    
                while true do
                    local ds = getInput("? ")
                    print("\n")
    
                    if ds == "TOO MUCH" then
                        print("YOU CALL THAT A PROBLEM?!!  I SHOULD HAVE SUCH PROBLEMS!\n")
                        print("IF IT BOTHERS YOU, " .. ns .. ", TAKE A COLD SHOWER.\n")
                        break
                    elseif ds == "TOO LITTLE" then
                        print("WHY ARE YOU HERE IN SUFFERN, " .. ns .. "?  YOU SHOULD BE\n")
                        print("IN TOKYO OR NEW YORK OR AMSTERDAM OR SOMEPLACE WITH SOME\n")
                        print("REAL ACTION.\n")
                        break
                    else
                        print("DON'T GET ALL SHOOK, " .. ns .. ", JUST ANSWER THE QUESTION\n")
                        print("WITH 'TOO MUCH' OR 'TOO LITTLE'.  WHICH IS IT")
                    end
                end
            end
    
            print("\n")
            print("ANY MORE PROBLEMS YOU WANT SOLVED, " .. ns)
    
            local es = getInput("? ")
    
            if es == "YES" then
                print("WHAT KIND (SEX, MONEY, HEALTH, JOB)")
            elseif es == "NO" then
                print("THAT WILL BE $5.00 FOR THE ADVICE, " .. ns .. ".\n")
                print("PLEASE LEAVE THE MONEY ON THE TERMINAL.\n")
                print("\n")
                print("\n")
                print("\n")
    
                while true do
                    print("DID YOU LEAVE THE MONEY")
    
                    local gs = getInput("? ")
                    print("\n")
    
                    if gs == "YES" then
                        print("HEY, " .. ns .. "??? YOU LEFT NO MONEY AT ALL!\n")
                        print("YOU ARE CHEATING ME OUT OF MY HARD-EARNED LIVING.\n")
                        print("\n")
                        print("WHAT A RIP OFF, " .. ns .. "!!!\n")
                        print("\n")
                        break
                    elseif gs == "NO" then
                        print("THAT'S HONEST, " .. ns .. ", BUT HOW DO YOU EXPECT\n")
                        print("ME TO GO ON WITH MY PSYCHOLOGY STUDIES IF MY PATIENT\n")
                        print("DON'T PAY THEIR BILLS?\n")
                        break
                    else
                        print("YOUR ANSWER OF '" .. gs .. "' CONFUSES ME, " .. ns .. ".\n")
                        print("PLEASE RESPOND WITH 'YES' OR 'NO'.\n")
                    end
                end
    
                break
            end
        end
    
        print("\n")
        print("TAKE A WALK, " .. ns .. ".\n")
        print("\n")
        print("\n")
    end
    
    main()
    
    ================================================
    FILE: 45_Hello/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 45_Hello/perl/hello.pl
    ================================================
    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    print ' ' x 33 . "HELLO\n";
    print ' ' x 15 . "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n";
    print "\n\n\n";
    
    print "HELLO.  MY NAME IS CREATIVE COMPUTER.\n\n\n";
    print "WHAT'S YOUR NAME?\n";
    chomp( my $N = uc  );
    
    print "\nHI THERE, $N, ARE YOU ENJOYING YOURSELF HERE?\n";
    
    GREET:
    {
        chomp( my $B = uc  );
        print "\n";
    
        if ( $B eq 'YES' ) {
            print "I'M GLAD TO HEAR THAT, $N.\n\n";
        }
        elsif ( $B eq 'NO' ) {
            print "OH, I'M SORRY TO HEAR THAT, $N. MAYBE WE CAN\n";
            print "BRIGHTEN UP YOUR VISIT A BIT.\n";
        }
        else {
            print "$N, I DON'T UNDERSTAND YOUR ANSWER OF '$B'.\n";
            print "PLEASE ANSWER 'YES' OR 'NO'.  DO YOU LIKE IT HERE?\n";
            redo GREET;
        }
    }
    
    print "\nSAY, $N, I CAN SOLVE ALL KINDS OF PROBLEMS EXCEPT\n";
    print "THOSE DEALING WITH GREECE.  WHAT KIND OF PROBLEMS DO\n";
    print "YOU HAVE (ANSWER SEX, HEALTH, MONEY, OR JOB)?\n";
    
    ADVICE:
    {
        chomp( my $C = uc  );
        print "\n";
    
        if ( $C eq 'SEX' ) {
            print "IS YOUR PROBLEM TOO MUCH OR TOO LITTLE?\n";
    
            SEX:
            {
                chomp( my $D = uc  );
                print "\n";
    
                if ( $D eq 'TOO MUCH' ) {
                    print "YOU CALL THAT A PROBLEM?!!  I SHOULD HAVE SUCH PROBLEMS!\n";
                    print "IF IT BOTHERS YOU, $N, TAKE A COLD SHOWER.\n";
                }
                elsif ( $D eq 'TOO LITTLE' ) {
                    print "WHY ARE YOU HERE IN SUFFERN, $N?  YOU SHOULD BE\n";
                    print "IN TOKYO OR NEW YORK OR AMSTERDAM OR SOMEPLACE WITH SOME\n";
                    print "REAL ACTION.\n";
                }
                else {
                    print "DON'T GET ALL SHOOK, $N, JUST ANSWER THE QUESTION\n";
                    print "WITH 'TOO MUCH' OR 'TOO LITTLE'.  WHICH IS IT?\n";
                    redo SEX;
                }
            }
        }
        elsif ( $C eq 'HEALTH' ) {
            print "MY ADVICE TO YOU $N IS:\n";
            print "     1.  TAKE TWO ASPRIN\n";
            print "     2.  DRINK PLENTY OF FLUIDS (ORANGE JUICE, NOT BEER!)\n";
            print "     3.  GO TO BED (ALONE)\n";
        }
        elsif ( $C eq 'MONEY' ) {
            print "SORRY, $N, I'M BROKE TOO.  WHY DON'T YOU SELL\n";
            print "ENCYCLOPEADIAS OR MARRY SOMEONE RICH OR STOP EATING\n";
            print "SO YOU WON'T NEED SO MUCH MONEY?\n";
        }
        elsif ( $C eq 'JOB' ) {
            print "I CAN SYMPATHIZE WITH YOU $N.  I HAVE TO WORK\n";
            print "VERY LONG HOURS FOR NO PAY -- AND SOME OF MY BOSSES\n";
            print "REALLY BEAT ON MY KEYBOARD.  MY ADVICE TO YOU, $N,\n";
            print "IS TO OPEN A RETAIL COMPUTER STORE.  IT'S GREAT FUN.\n";
        }
        else {
            print "OH, $N, YOUR ANSWER OF '$C' IS GREEK TO ME.\n";
        }
    
        MORE:
        {
            print "\nANY MORE PROBLEMS YOU WANT SOLVED, $N?\n";
            chomp( my $E = uc  );
            print "\n";
    
            if ( $E eq 'YES' ) {
                print "WHAT KIND (SEX, MONEY, HEALTH, JOB)?\n";
                redo ADVICE;
            }
            elsif ( $E eq 'NO' ) {
                print "\nTHAT WILL BE \$5.00 FOR THE ADVICE, $N.\n";
                print "PLEASE LEAVE THE MONEY ON THE TERMINAL.\n";
            }
            else {
                print "JUST A SIMPLE 'YES' OR 'NO' PLEASE, $N.\n";
                redo MORE;
            }
        }
    
        sleep 2;
        print "\n\n\n";
    
        MONEY:
        {
            print "DID YOU LEAVE THE MONEY?\n";
            chomp( my $G = uc  );
            print "\n";
    
            if ( $G eq 'YES' ) {
                print "HEY, $N??? YOU LEFT NO MONEY AT ALL!\n";
                print "YOU ARE CHEATING ME OUT OF MY HARD-EARNED LIVING.\n";
                print "\nWHAT A RIP OFF, $N!!!\n\n";
            }
            elsif ( $G eq 'NO' ) {
                print "THAT'S HONEST, $N, BUT HOW DO YOU EXPECT\n";
                print "ME TO GO ON WITH MY PSYCHOLOGY STUDIES IF MY PATIENTS\n";
                print "DON'T PAY THEIR BILLS?\n";
            }
            else {
                print "YOUR ANSWER OF '$G' CONFUSES ME, $N.\n";
                print "PLEASE RESPOND WITH 'YES' OR 'NO'.\n";
                redo MONEY;
            }
    
            print "\nTAKE A WALK, $N.\n\n\n";
        }
    }
    
    
    ================================================
    FILE: 45_Hello/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 45_Hello/python/hello.py
    ================================================
    """
    HELLO
    
    A very simple "chat" bot.
    
    Warning, the advice given here is bad.
    
    Ported by Dave LeCompte
    """
    
    import time
    from typing import Optional, Tuple
    
    
    def get_yes_or_no() -> Tuple[bool, Optional[bool], str]:
        msg = input()
        if msg.upper() == "YES":
            return True, True, msg
        elif msg.upper() == "NO":
            return True, False, msg
        else:
            return False, None, msg
    
    
    def ask_enjoy_question(user_name: str) -> None:
        print(f"HI THERE, {user_name}, ARE YOU ENJOYING YOURSELF HERE?")
    
        while True:
            valid, value, msg = get_yes_or_no()
    
            if valid:
                if value:
                    print(f"I'M GLAD TO HEAR THAT, {user_name}.")
                    print()
                else:
                    print(f"OH, I'M SORRY TO HEAR THAT, {user_name}. MAYBE WE CAN")
                    print("BRIGHTEN UP YOUR VISIT A BIT.")
                break
            else:
                print(f"{user_name}, I DON'T UNDERSTAND YOUR ANSWER OF '{msg}'.")
                print("PLEASE ANSWER 'YES' OR 'NO'.  DO YOU LIKE IT HERE?")
    
    
    def prompt_for_problems(user_name: str) -> str:
        print()
        print(f"SAY, {user_name}, 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)")
    
        return input().upper()
    
    
    def prompt_too_much_or_too_little() -> Tuple[bool, Optional[bool]]:
        answer = input().upper()
        if answer == "TOO MUCH":
            return True, True
        elif answer == "TOO LITTLE":
            return True, False
        return False, None
    
    
    def solve_sex_problem(user_name: str) -> None:
        print("IS YOUR PROBLEM TOO MUCH OR TOO LITTLE?")
        while True:
            valid, too_much = prompt_too_much_or_too_little()
            if valid:
                if too_much:
                    print("YOU CALL THAT A PROBLEM?!!  I SHOULD HAVE SUCH PROBLEMS!")
                    print(f"IF IT BOTHERS YOU, {user_name}, TAKE A COLD SHOWER.")
                else:
                    print(f"WHY ARE YOU HERE IN SUFFERN, {user_name}?  YOU SHOULD BE")
                    print("IN TOKYO OR NEW YORK OR AMSTERDAM OR SOMEPLACE WITH SOME")
                    print("REAL ACTION.")
                return
            else:
                print(f"DON'T GET ALL SHOOK, {user_name}, JUST ANSWER THE QUESTION")
                print("WITH 'TOO MUCH' OR 'TOO LITTLE'.  WHICH IS IT?")
    
    
    def solve_money_problem(user_name: str) -> None:
        print(f"SORRY, {user_name}, 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?")
    
    
    def solve_health_problem(user_name: str) -> None:
        print(f"MY ADVICE TO YOU {user_name} IS:")
        print("     1.  TAKE TWO ASPRIN")
        print("     2.  DRINK PLENTY OF FLUIDS (ORANGE JUICE, NOT BEER!)")
        print("     3.  GO TO BED (ALONE)")
    
    
    def solve_job_problem(user_name: str) -> None:
        print(f"I CAN SYMPATHIZE WITH YOU {user_name}.  I HAVE TO WORK")
        print("VERY LONG HOURS FOR NO PAY -- AND SOME OF MY BOSSES")
        print(f"REALLY BEAT ON MY KEYBOARD.  MY ADVICE TO YOU, {user_name},")
        print("IS TO OPEN A RETAIL COMPUTER STORE.  IT'S GREAT FUN.")
    
    
    def alert_unknown_problem_type(user_name: str, problem_type: str) -> None:
        print(f"OH, {user_name}, YOUR ANSWER OF {problem_type} IS GREEK TO ME.")
    
    
    def ask_question_loop(user_name: str) -> None:
        while True:
            problem_type = prompt_for_problems(user_name)
            if problem_type == "SEX":
                solve_sex_problem(user_name)
            elif problem_type == "HEALTH":
                solve_health_problem(user_name)
            elif problem_type == "MONEY":
                solve_money_problem(user_name)
            elif problem_type == "JOB":
                solve_job_problem(user_name)
            else:
                alert_unknown_problem_type(user_name, problem_type)
    
            while True:
                print()
                print(f"ANY MORE PROBLEMS YOU WANT SOLVED, {user_name}?")
    
                valid, value, msg = get_yes_or_no()
                if valid:
                    if not value:
                        return
                    print("WHAT KIND (SEX, MONEY, HEALTH, JOB)")
                    break
                print(f"JUST A SIMPLE 'YES' OR 'NO' PLEASE, {user_name}.")
    
    
    def ask_for_fee(user_name: str) -> None:
        print()
        print(f"THAT WILL BE $5.00 FOR THE ADVICE, {user_name}.")
        print("PLEASE LEAVE THE MONEY ON THE TERMINAL.")
        time.sleep(4)
        print()
        print()
        print()
        print("DID YOU LEAVE THE MONEY?")
    
        while True:
            valid, value, msg = get_yes_or_no()
            if valid:
                if value:
                    print(f"HEY, {user_name}, YOU LEFT NO MONEY AT ALL!")
                    print("YOU ARE CHEATING ME OUT OF MY HARD-EARNED LIVING.")
                    print()
                    print(f"WHAT A RIP OFF, {user_name}!!!")
                    print()
                else:
                    print(f"THAT'S HONEST, {user_name}, BUT HOW DO YOU EXPECT")
                    print("ME TO GO ON WITH MY PSYCHOLOGY STUDIES IF MY PATIENTS")
                    print("DON'T PAY THEIR BILLS?")
                return
            else:
                print(f"YOUR ANSWER OF '{msg}' CONFUSES ME, {user_name}.")
                print("PLEASE RESPOND WITH 'YES' or 'NO'.")
    
    
    def unhappy_goodbye(user_name: str) -> None:
        print()
        print(f"TAKE A WALK, {user_name}.")
        print()
        print()
    
    
    def happy_goodbye(user_name: str) -> None:
        print(f"NICE MEETING YOU, {user_name}, HAVE A NICE DAY.")
    
    
    def main() -> None:
        print(" " * 33 + "HELLO")
        print(" " * 15 + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n\n\n")
        print("HELLO.  MY NAME IS CREATIVE COMPUTER.\n\n")
        print("WHAT'S YOUR NAME?")
        user_name = input()
        print()
    
        ask_enjoy_question(user_name)
    
        ask_question_loop(user_name)
    
        ask_for_fee(user_name)
    
        unhappy_goodbye(user_name)
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 45_Hello/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 45_Hello/ruby/hello.rb
    ================================================
    class Hello
      def start
        puts  "HELLO.  MY NAME IS CREATIVE COMPUTER.\n\n"
        print "WHAT'S YOUR NAME? "
        user_name = gets.chomp!
    
        ask_enjoy_question(user_name)
    
        ask_question_loop(user_name)
    
        isHonest = ask_for_fee(user_name)
        
        if isHonest
          happy_goodbye(user_name)
        else
          unhappy_goodbye(user_name)
        end
      end
      private
        def get_yes_or_no
          msg = gets.chomp!
          if msg.upcase() == "YES"
              return true, true, msg
          elsif msg.upcase() == "NO"
              return true, false, msg
          else
              return false, false, msg
          end
        end
    
        def ask_enjoy_question user_name
          print "\nHI THERE, #{user_name}, ARE YOU ENJOYING YOURSELF HERE? "
    
          while true
            valid, value, msg = get_yes_or_no()
    
            if valid
              if value
                puts "\nI'M GLAD TO HEAR THAT, #{user_name}."
                break
              else
                puts "\nOH, I'M SORRY TO HEAR THAT, #{user_name}. MAYBE WE CAN"
                puts "BRIGHTEN UP YOUR VISIT A BIT."
                break
              end
            else
              puts "\n#{user_name}, I DON'T UNDERSTAND YOUR ANSWER OF '#{msg}'."
              print "PLEASE ANSWER 'YES' OR 'NO'.  DO YOU LIKE IT HERE? "
            end
          end
        end
    
        def prompt_for_problems user_name
          puts "\nSAY, #{user_name}, I CAN SOLVE ALL KINDS OF PROBLEMS EXCEPT" 
          puts "THOSE DEALING WITH GREECE.  WHAT KIND OF PROBLEMS DO"
          print "YOU HAVE? (ANSWER SEX, HEALTH, MONEY, OR JOB) "
      
          problem_type = gets.chomp!
          return problem_type
        end
    
        def prompt_too_much_or_too_little
          answer = gets.chomp!
          if answer.upcase() == "TOO MUCH"
            return true, true
          elsif answer.upcase() == "TOO LITTLE"
            return true, false
          else
            return false, false
          end
        end
    
        def solve_sex_problem user_name
          print "\nIS YOUR PROBLEM TOO MUCH OR TOO LITTLE? "
          while true
            valid, too_much = prompt_too_much_or_too_little()
            if valid
              if too_much
                puts "\nYOU CALL THAT A PROBLEM?!!  I SHOULD HAVE SUCH PROBLEMS!"
                puts "IF IT BOTHERS YOU, #{user_name}, TAKE A COLD SHOWER."
                break
              else
                puts "\nWHY ARE YOU HERE IN SUFFERN, #{user_name}?  YOU SHOULD BE"
                puts "IN TOKYO OR NEW YORK OR AMSTERDAM OR SOMEPLACE WITH SOME"
                puts "REAL ACTION."
                break
              end
            else
              puts "\nDON'T GET ALL SHOOK, #{user_name}, JUST ANSWER THE QUESTION"
              print "WITH 'TOO MUCH' OR 'TOO LITTLE'.  WHICH IS IT? "
            end
          end
        end
    
        def solve_health_problem user_name
          puts "\nMY ADVICE TO YOU #{user_name} IS:"
          puts "     1.  TAKE TWO ASPRIN"
          puts "     2.  DRINK PLENTY OF FLUIDS (ORANGE JUICE, NOT BEER!)"
          puts "     3.  GO TO BED (ALONE)"
        end
    
        def solve_money_problem user_name
          puts "\nSORRY, #{user_name}, I'M BROKE TOO.  WHY DON'T YOU SELL"
          puts "ENCYCLOPEADIAS OR MARRY SOMEONE RICH OR STOP EATING"
          puts "SO YOU WON'T NEED SO MUCH MONEY?"
        end
    
        def solve_job_problem user_name
          puts "\nI CAN SYMPATHIZE WITH YOU #{user_name}.  I HAVE TO WORK"
          puts "VERY LONG HOURS FOR NO PAY -- AND SOME OF MY BOSSES"
          puts "REALLY BEAT ON MY KEYBOARD.  MY ADVICE TO YOU, #{user_name},"
          puts "IS TO OPEN A RETAIL COMPUTER STORE.  IT'S GREAT FUN."
        end
    
        def alert_unknown_problem_type user_name, problem_type
          puts "\nOH, #{user_name}, YOUR ANSWER OF #{problem_type} IS GREEK TO ME."
        end
    
        def ask_question_loop user_name
          while true
            problem_type = prompt_for_problems(user_name)
            if problem_type == "SEX"
              solve_sex_problem(user_name)
            elsif problem_type == "HEALTH"
              solve_health_problem(user_name)
            elsif problem_type == "MONEY"
              solve_money_problem(user_name)
            elsif problem_type == "JOB"
              solve_job_problem(user_name)
            else
              alert_unknown_problem_type(user_name, problem_type)
            end
    
            while true
              print "\nANY MORE PROBLEMS YOU WANT SOLVED, #{user_name}? "
    
              valid, value, msg = get_yes_or_no()
              if valid
                if value
                  puts "\nWHAT KIND (SEX, MONEY, HEALTH, JOB)"
                  break
                else
                  return
                end
              else
                puts "\nJUST A SIMPLE 'YES' OR 'NO' PLEASE, #{user_name}."
              end
            end
          end
        end
    
        def ask_for_fee user_name
          puts "\nTHAT WILL BE $5.00 FOR THE ADVICE, #{user_name}."
          puts "PLEASE LEAVE THE MONEY ON THE TERMINAL."
          sleep(3)
          print "\n\nDID YOU LEAVE THE MONEY? "
      
          while true
            valid, value, msg = get_yes_or_no()
            if valid
                if value
                  puts "\nHEY, #{user_name}, YOU LEFT NO MONEY AT ALL!"
                  puts "YOU ARE CHEATING ME OUT OF MY HARD-EARNED LIVING."
                  puts "\nWHAT A RIP OFF, #{user_name}!!!"
                  return false
                else
                  puts "\nTHAT'S HONEST, #{user_name}, BUT HOW DO YOU EXPECT"
                  puts "ME TO GO ON WITH MY PSYCHOLOGY STUDIES IF MY PATIENTS"
                  puts "DON'T PAY THEIR BILLS?"
                  return true
                end
            else
              puts "\nYOUR ANSWER OF '#{msg}' CONFUSES ME, #{user_name}."
              print "PLEASE RESPOND WITH 'YES' or 'NO'. "
            end
          end
        end
    
        def unhappy_goodbye user_name
          puts "\nTAKE A WALK, #{user_name}.\n\n"
        end
      
        def happy_goodbye user_name
          puts "\nNICE MEETING YOU, #{user_name}, HAVE A NICE DAY."
        end
    end
    
    if __FILE__ == $0
      hello = Hello.new
      hello.start()
    end
    
    ================================================
    FILE: 45_Hello/rust/Cargo.toml
    ================================================
    [package]
    name = "rust"
    version = "0.1.0"
    edition = "2021"
    
    [dependencies]
    
    
    ================================================
    FILE: 45_Hello/rust/src/main.rs
    ================================================
    /** 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
    }
    
    
    ================================================
    FILE: 45_Hello/vbnet/Hello.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Hello", "Hello.vbproj", "{7E2BDA03-A9F7-4DE5-AB8F-222DD87C833D}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{7E2BDA03-A9F7-4DE5-AB8F-222DD87C833D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{7E2BDA03-A9F7-4DE5-AB8F-222DD87C833D}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{7E2BDA03-A9F7-4DE5-AB8F-222DD87C833D}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{7E2BDA03-A9F7-4DE5-AB8F-222DD87C833D}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 45_Hello/vbnet/Hello.vbproj
    ================================================
    
      
        Exe
        Hello
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 45_Hello/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 46_Hexapawn/README.md
    ================================================
    ### Hexapawn
    
    The game of Hexapawn and a method to learn a strategy for playing the game was described in Martin Gardner’s “Mathematical Games” column in the March 1962 issue of _Scientific American_. The method described in the article was for a hypothetical learning machine composed of match boxes and colored beads. This has been generalized in the program HEX.
    
    The program learns by elimination of bad moves. All positions encountered by the program and acceptable moves from them are stored in an array. When the program encounters an unfamiliar position, the position and all legal moves from it are added to the list. If the program loses a game, it erases the move that led to defeat. If it hits a position from which all moves have been deleted (they all led to defeat), it erases the move that got it there and resigns. Eventually, the program learns to play extremely well and, indeed, is unbeatable. The learning strategy could be adopted to other simple games with a finite number of moves (tic-tac-toe, small board checkers, or other chess-based games).
    
    The original version of this program was written by R.A. Kaapke. It was subsequently modified by Jeff Dalton and finally by Steve North of Creative Computing.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=83)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=98)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### 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)
    
    
    ================================================
    FILE: 46_Hexapawn/csharp/Board.cs
    ================================================
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    using static Hexapawn.Pawn;
    
    namespace Hexapawn;
    
    internal class Board : IEnumerable, IEquatable
    {
        private readonly Pawn[] _cells;
    
        public Board()
        {
            _cells = new[]
            {
                Black, Black, Black,
                None,  None,  None,
                White, White, White
            };
        }
    
        public Board(params Pawn[] cells)
        {
            _cells = cells;
        }
    
        public Pawn this[int index]
        {
            get => _cells[index - 1];
            set => _cells[index - 1] = value;
        }
    
        public Board Reflected => new(Cell.AllCells.Select(c => this[c.Reflected]).ToArray());
    
        public IEnumerator GetEnumerator() => _cells.OfType().GetEnumerator();
    
        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
    
        public override string ToString()
        {
            var builder = new StringBuilder().AppendLine();
            for (int row = 0; row < 3; row++)
            {
                builder.Append("          ");
                for (int col = 0; col < 3; col++)
                {
                    builder.Append(_cells[row * 3 + col]);
                }
                builder.AppendLine();
            }
            return builder.ToString();
        }
    
        public bool Equals(Board other) => other?.Zip(this).All(x => x.First == x.Second) ?? false;
    
        public override bool Equals(object obj) => Equals(obj as Board);
    
        public override int GetHashCode()
        {
            var hash = 19;
    
            for (int i = 0; i < 9; i++)
            {
                hash = hash * 53 + _cells[i].GetHashCode();
            }
    
            return hash;
        }
    }
    
    
    ================================================
    FILE: 46_Hexapawn/csharp/Cell.cs
    ================================================
    using System;
    using System.Collections.Generic;
    
    namespace Hexapawn;
    
    // Represents a cell on the board, numbered 1 to 9, with support for finding the reflection of the reference around
    // the middle column of the board.
    internal class Cell
    {
        private static readonly Cell[] _cells = new Cell[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
        private static readonly Cell[] _reflected = new Cell[] { 3, 2, 1, 6, 5, 4, 9, 8, 7 };
        private readonly int _number;
        private Cell(int number)
        {
            if (number < 1 || number > 9)
            {
                throw new ArgumentOutOfRangeException(nameof(number), number, "Must be from 1 to 9");
            }
            _number = number;
        }
        // Facilitates enumerating all the cells.
        public static IEnumerable AllCells => _cells;
        // Takes a value input by the user and attempts to create a Cell reference
        public static bool TryCreate(float input, out Cell cell)
        {
            if (IsInteger(input) && input >= 1 && input <= 9)
            {
                cell = (int)input;
                return true;
            }
            cell = default;
            return false;
            static bool IsInteger(float value) => value - (int)value == 0;
        }
        // Returns the reflection of the cell reference about the middle column of the board.
        public Cell Reflected => _reflected[_number - 1];
        // Allows the cell reference to be used where an int is expected, such as the indexer in Board.
        public static implicit operator int(Cell c) => c._number;
        public static implicit operator Cell(int number) => new(number);
        public override string ToString() => _number.ToString();
    }
    
    
    ================================================
    FILE: 46_Hexapawn/csharp/Computer.cs
    ================================================
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Games.Common.IO;
    using Games.Common.Randomness;
    using static Hexapawn.Pawn;
    
    namespace Hexapawn;
    
    /// 
    /// Encapsulates the logic of the computer player.
    /// 
    internal class Computer
    {
        private readonly TextIO _io;
        private readonly IRandom _random;
        private readonly Dictionary> _potentialMoves;
        private (List, Move) _lastMove;
        public Computer(TextIO io, IRandom random)
        {
            _io = io;
            _random = random;
    
            // This dictionary implements the data in the original code, which encodes board positions for which the
            // computer has a legal move, and the list of possible moves for each position:
            //   900 DATA -1,-1,-1,1,0,0,0,1,1,-1,-1,-1,0,1,0,1,0,1
            //   905 DATA -1,0,-1,-1,1,0,0,0,1,0,-1,-1,1,-1,0,0,0,1
            //   910 DATA -1,0,-1,1,1,0,0,1,0,-1,-1,0,1,0,1,0,0,1
            //   915 DATA 0,-1,-1,0,-1,1,1,0,0,0,-1,-1,-1,1,1,1,0,0
            //   920 DATA -1,0,-1,-1,0,1,0,1,0,0,-1,-1,0,1,0,0,0,1
            //   925 DATA 0,-1,-1,0,1,0,1,0,0,-1,0,-1,1,0,0,0,0,1
            //   930 DATA 0,0,-1,-1,-1,1,0,0,0,-1,0,0,1,1,1,0,0,0
            //   935 DATA 0,-1,0,-1,1,1,0,0,0,-1,0,0,-1,-1,1,0,0,0
            //   940 DATA 0,0,-1,-1,1,0,0,0,0,0,-1,0,1,-1,0,0,0,0
            //   945 DATA -1,0,0,-1,1,0,0,0,0
            //   950 DATA 24,25,36,0,14,15,36,0,15,35,36,47,36,58,59,0
            //   955 DATA 15,35,36,0,24,25,26,0,26,57,58,0
            //   960 DATA 26,35,0,0,47,48,0,0,35,36,0,0,35,36,0,0
            //   965 DATA 36,0,0,0,47,58,0,0,15,0,0,0
            //   970 DATA 26,47,0,0,47,58,0,0,35,36,47,0,28,58,0,0,15,47,0,0
            //
            // The original code loaded this data into two arrays.
            //   40 FOR I=1 TO 19: FOR J=1 TO 9: READ B(I,J): NEXT J: NEXT I
            //   45 FOR I=1 TO 19: FOR J=1 TO 4: READ M(I,J): NEXT J: NEXT I
            //
            // When finding moves for the computer the first array was searched for the current board position, or the
            // reflection of it, and the resulting index was used in the second array to get the possible moves.
            // With this dictionary we can just use the current board as the index, and retrieve a list of moves for
            // consideration by the computer.
            _potentialMoves = new()
            {
                [new(Black, Black, Black, White, None,  None,  None,  White, White)] = Moves((2, 4), (2, 5), (3, 6)),
                [new(Black, Black, Black, None,  White, None,  White, None,  White)] = Moves((1, 4), (1, 5), (3, 6)),
                [new(Black, None,  Black, Black, White, None,  None,  None,  White)] = Moves((1, 5), (3, 5), (3, 6), (4, 7)),
                [new(None,  Black, Black, White, Black, None,  None,  None,  White)] = Moves((3, 6), (5, 8), (5, 9)),
                [new(Black, None,  Black, White, White, None,  None,  White, None)]  = Moves((1, 5), (3, 5), (3, 6)),
                [new(Black, Black, None,  White, None,  White, None,  None,  White)] = Moves((2, 4), (2, 5), (2, 6)),
                [new(None,  Black, Black, None,  Black, White, White, None,  None)]  = Moves((2, 6), (5, 7), (5, 8)),
                [new(None,  Black, Black, Black, White, White, White, None,  None)]  = Moves((2, 6), (3, 5)),
                [new(Black, None,  Black, Black, None,  White, None,  White, None)]  = Moves((4, 7), (4, 8)),
                [new(None,  Black, Black, None,  White, None,  None,  None,  White)] = Moves((3, 5), (3, 6)),
                [new(None,  Black, Black, None,  White, None,  White, None,  None)]  = Moves((3, 5), (3, 6)),
                [new(Black, None,  Black, White, None,  None,  None,  None,  White)] = Moves((3, 6)),
                [new(None,  None,  Black, Black, Black, White, None,  None,  None)]  = Moves((4, 7), (5, 8)),
                [new(Black, None,  None,  White, White, White, None,  None,  None)]  = Moves((1, 5)),
                [new(None,  Black, None,  Black, White, White, None,  None,  None)]  = Moves((2, 6), (4, 7)),
                [new(Black, None,  None,  Black, Black, White, None,  None,  None)]  = Moves((4, 7), (5, 8)),
                [new(None,  None,  Black, Black, White, None,  None,  None,  None)]  = Moves((3, 5), (3, 6), (4, 7)),
                [new(None,  Black, None,  White, Black, None,  None,  None,  None)]  = Moves((2, 8), (5, 8)),
                [new(Black, None,  None,  Black, White, None,  None,  None,  None)]  = Moves((1, 5), (4, 7))
            };
        }
    
        // Try to make a move. We first try to find a legal move for the current board position.
        public bool TryMove(Board board)
        {
            if (TryGetMoves(board, out var moves, out var reflected) &&
                TrySelectMove(moves, out var move))
            {
                // We've found a move, so we record it as the last move made, and then announce and make the move.
                _lastMove = (moves, move);
                // If we found the move from a reflacted match of the board we need to make the reflected move.
                if (reflected) { move = move.Reflected; }
                _io.WriteLine($"I move {move}");
                move.Execute(board);
                return true;
            }
            // We haven't found a move for this board position, so remove the previous move that led to this board
            // position from future consideration. We don't want to make that move again, because we now know it's a
            // non-winning move.
            ExcludeLastMoveFromFuturePlay();
            return false;
        }
    
        // Looks up the given board and its reflection in the potential moves dictionary. If it's found then we have a
        // list of potential moves. If the board is not found in the dictionary then the computer has no legal moves,
        // and the human player wins.
        private bool TryGetMoves(Board board, out List moves, out bool reflected)
        {
            if (_potentialMoves.TryGetValue(board, out moves))
            {
                reflected = false;
                return true;
            }
            if (_potentialMoves.TryGetValue(board.Reflected, out moves))
            {
                reflected = true;
                return true;
            }
            reflected = default;
            return false;
        }
    
        // Get a random move from the list. If the list is empty, then we've previously eliminated all the moves for
        // this board position as being non-winning moves. We therefore resign the game.
        private bool TrySelectMove(List moves, out Move move)
        {
            if (moves.Any())
            {
                move = moves[_random.Next(moves.Count)];
                return true;
            }
            _io.WriteLine("I resign.");
            move = null;
            return false;
        }
    
        private void ExcludeLastMoveFromFuturePlay()
        {
            var (moves, move) = _lastMove;
            moves.Remove(move);
        }
    
        private static List Moves(params Move[] moves) => moves.ToList();
    
        public bool IsFullyAdvanced(Board board) =>
            board[9] == Black || board[8] == Black || board[7] == Black;
    }
    
    
    ================================================
    FILE: 46_Hexapawn/csharp/Game.cs
    ================================================
    using System;
    using Games.Common.IO;
    
    namespace Hexapawn;
    
    // A single game of Hexapawn
    internal class Game
    {
        private readonly TextIO _io;
        private readonly Board _board;
    
        public Game(TextIO io)
        {
            _board = new Board();
            _io = io;
        }
    
        public object Play(Human human, Computer computer)
        {
            _io.WriteLine(_board);
            while(true)
            {
                human.Move(_board);
                _io.WriteLine(_board);
                if (!computer.TryMove(_board))
                {
                    return human;
                }
                _io.WriteLine(_board);
                if (computer.IsFullyAdvanced(_board) || human.HasNoPawns(_board))
                {
                    return computer;
                }
                if (!human.HasLegalMove(_board))
                {
                    _io.Write("You can't move, so ");
                    return computer;
                }
            }
        }
    }
    
    
    ================================================
    FILE: 46_Hexapawn/csharp/GameSeries.cs
    ================================================
    using System.Collections.Generic;
    using System.Linq;
    using Games.Common.IO;
    using Games.Common.Randomness;
    using Hexapawn.Resources;
    
    namespace Hexapawn;
    
    // Runs series of games between the computer and the human player
    internal class GameSeries
    {
        private readonly TextIO _io;
        private readonly Computer _computer;
        private readonly Human _human;
        private readonly Dictionary _wins;
    
        public GameSeries(TextIO io, IRandom random)
        {
            _io = io;
            _computer = new(io, random);
            _human = new(io);
            _wins = new() { [_computer] = 0, [_human] = 0 };
        }
    
        public void Play()
        {
            _io.Write(Resource.Streams.Title);
    
            if (_io.GetYesNo("Instructions") == 'Y')
            {
                _io.Write(Resource.Streams.Instructions);
            }
    
            while (true)
            {
                var game = new Game(_io);
    
                var winner = game.Play(_human, _computer);
                _wins[winner]++;
                _io.WriteLine(winner == _computer ? "I win." : "You win.");
    
                _io.Write($"I have won {_wins[_computer]} and you {_wins[_human]}");
                _io.WriteLine($" out of {_wins.Values.Sum()} games.");
                _io.WriteLine();
            }
        }
    }
    
    
    ================================================
    FILE: 46_Hexapawn/csharp/Hexapawn.csproj
    ================================================
    
    
      
        Exe
        net6.0
      
    
      
        
      
    
      
        
      
    
    
    
    
    ================================================
    FILE: 46_Hexapawn/csharp/Hexapawn.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 17
    VisualStudioVersion = 17.0.32014.148
    MinimumVisualStudioVersion = 15.0.26124.0
    Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hexapawn", "Hexapawn.csproj", "{785DA416-2609-4DB1-9F18-63CF6AC9927E}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{785DA416-2609-4DB1-9F18-63CF6AC9927E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{785DA416-2609-4DB1-9F18-63CF6AC9927E}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{785DA416-2609-4DB1-9F18-63CF6AC9927E}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{785DA416-2609-4DB1-9F18-63CF6AC9927E}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {D662AD9F-88B1-4AC9-83AD-DBFC08F384A8}
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 46_Hexapawn/csharp/Human.cs
    ================================================
    using System;
    using System.Linq;
    using Games.Common.IO;
    using static Hexapawn.Cell;
    using static Hexapawn.Move;
    using static Hexapawn.Pawn;
    
    namespace Hexapawn;
    
    internal class Human
    {
        private readonly TextIO _io;
    
        public Human(TextIO io)
        {
            _io = io;
        }
    
        public void Move(Board board)
        {
            while (true)
            {
                var move = _io.ReadMove("Your move");
    
                if (TryExecute(board, move)) { return; }
    
                _io.WriteLine("Illegal move.");
            }
        }
    
        public bool HasLegalMove(Board board)
        {
            foreach (var from in AllCells.Where(c => c > 3))
            {
                if (board[from] != White) { continue; }
    
                if (HasLegalMove(board, from))
                {
                    return true;
                }
            }
    
            return false;
        }
    
        private bool HasLegalMove(Board board, Cell from) =>
            Right(from).IsRightDiagonalToCapture(board) ||
            Straight(from).IsStraightMoveToEmptySpace(board) ||
            from > 4 && Left(from).IsLeftDiagonalToCapture(board);
    
        public bool HasNoPawns(Board board) => board.All(c => c != White);
    
        public bool TryExecute(Board board, Move move)
        {
            if (board[move.From] != White) { return false; }
    
            if (move.IsStraightMoveToEmptySpace(board) ||
                move.IsLeftDiagonalToCapture(board) ||
                move.IsRightDiagonalToCapture(board))
            {
                move.Execute(board);
                return true;
            }
    
            return false;
        }
    }
    
    
    ================================================
    FILE: 46_Hexapawn/csharp/IReadWriteExtensions.cs
    ================================================
    using System;
    using System.Linq;
    using Games.Common.IO;
    
    namespace Hexapawn;
    
    // Provides input methods which emulate the BASIC interpreter's keyboard input routines
    internal static class IReadWriteExtensions
    {
        internal static char GetYesNo(this IReadWrite io, string prompt)
        {
            while (true)
            {
                var response = io.ReadString($"{prompt} (Y-N)").FirstOrDefault();
                if ("YyNn".Contains(response))
                {
                    return char.ToUpperInvariant(response);
                }
            }
        }
    
        // Implements original code:
        //   120 PRINT "YOUR MOVE";
        //   121 INPUT M1,M2
        //   122 IF M1=INT(M1)AND M2=INT(M2)AND M1>0 AND M1<10 AND M2>0 AND M2<10 THEN 130
        //   123 PRINT "ILLEGAL CO-ORDINATES."
        //   124 GOTO 120
        internal static Move ReadMove(this IReadWrite io, string prompt)
        {
            while(true)
            {
                var (from, to) = io.Read2Numbers(prompt);
    
                if (Move.TryCreate(from, to, out var move))
                {
                    return move;
                }
    
                io.WriteLine("Illegal Coordinates.");
            }
        }
    }
    
    
    ================================================
    FILE: 46_Hexapawn/csharp/Move.cs
    ================================================
    using static Hexapawn.Pawn;
    
    namespace Hexapawn;
    
    /// 
    /// Represents a move which may, or may not, be legal.
    /// 
    internal class Move
    {
        private readonly Cell _from;
        private readonly Cell _to;
        private readonly int _metric;
    
        public Move(Cell from, Cell to)
        {
            _from = from;
            _to = to;
            _metric = _from - _to;
        }
    
        public void Deconstruct(out Cell from, out Cell to)
        {
            from = _from;
            to = _to;
        }
    
        public Cell From => _from;
    
        // Produces the mirror image of the current moved, reflected around the central column of the board.
        public Move Reflected => (_from.Reflected, _to.Reflected);
    
        // Allows a tuple of two ints to be implicitly converted to a Move.
        public static implicit operator Move((int From, int To) value) => new(value.From, value.To);
    
        // Takes floating point coordinates, presumably from keyboard input, and attempts to create a Move object.
        public static bool TryCreate(float input1, float input2, out Move move)
        {
            if (Cell.TryCreate(input1, out var from) &&
                Cell.TryCreate(input2, out var to))
            {
                move = (from, to);
                return true;
            }
    
            move = default;
            return false;
        }
    
        public static Move Right(Cell from) => (from, from - 2);
        public static Move Straight(Cell from) => (from, from - 3);
        public static Move Left(Cell from) => (from, from - 4);
    
        public bool IsStraightMoveToEmptySpace(Board board) => _metric == 3 && board[_to] == None;
    
        public bool IsLeftDiagonalToCapture(Board board) => _metric == 4 && _from != 7 && board[_to] == Black;
    
        public bool IsRightDiagonalToCapture(Board board) =>
            _metric == 2 && _from != 9 && _from != 6 && board[_to] == Black;
    
        public void Execute(Board board)
        {
            board[_to] = board[_from];
            board[_from] = None;
        }
    
        public override string ToString() => $"from {_from} to {_to}";
    }
    
    
    ================================================
    FILE: 46_Hexapawn/csharp/Pawn.cs
    ================================================
    namespace Hexapawn;
    
    // Represents the contents of a cell on the board
    internal class Pawn
    {
        public static readonly Pawn Black = new('X');
        public static readonly Pawn White = new('O');
        public static readonly Pawn None = new('.');
    
        private readonly char _symbol;
    
        private Pawn(char symbol)
        {
            _symbol = symbol;
        }
    
        public override string ToString() => _symbol.ToString();
    }
    
    
    
    ================================================
    FILE: 46_Hexapawn/csharp/Program.cs
    ================================================
    using Games.Common.IO;
    using Games.Common.Randomness;
    using Hexapawn;
    
    new GameSeries(new ConsoleIO(), new RandomNumberGenerator()).Play();
    
    
    
    ================================================
    FILE: 46_Hexapawn/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 46_Hexapawn/csharp/Resources/Instructions.txt
    ================================================
    
    This program plays the game of Hexapawn.
    Hexapawn is played with Chess pawns on a 3 by 3 board.
    The pawns are move as in Chess - one space forward to
    an empty space, or one space forward and diagonally to
    capture an opposing man.  On the board, your pawns
    are 'O', the computer's pawns are 'X', and empty
    squares are '.'.  To enter a move, type the number of
    the square you are moving from, followed by the number
    of the square you will move to.  The numbers must be
    separated by a comma.
    
    The computer starts a series of games knowing only when
    the game is won (a draw is impossible) and how to move.
    It has no strategy at first and just moves randomly.
    However, it learns from each game.  Thus winning becomes
    more and more difficult.  Also, to help offset your
    initial advantage, you will not be told how to win the
    game but must learn this by playing.
    
    The numbering of the board is as follows:
              123
              456
              789
    
    For example, to move your rightmost pawn forward,
    you would type 9,6 in response to the question
    'Your move ?'.  Since I'm a good sport, you'll always
    go first.
    
    
    
    ================================================
    FILE: 46_Hexapawn/csharp/Resources/Resource.cs
    ================================================
    using System.IO;
    using System.Reflection;
    using System.Runtime.CompilerServices;
    
    namespace Hexapawn.Resources;
    
    internal static class Resource
    {
        internal static class Streams
        {
            public static Stream Instructions => GetStream();
            public static Stream Title => GetStream();
        }
    
        private static Stream GetStream([CallerMemberName] string name = null)
            => Assembly.GetExecutingAssembly().GetManifestResourceStream($"Hexapawn.Resources.{name}.txt");
    }
    
    ================================================
    FILE: 46_Hexapawn/csharp/Resources/Title.txt
    ================================================
                                    Hexapawn
                   Creative Computing  Morristown, New Jersey
    
    
    
    
    
    ================================================
    FILE: 46_Hexapawn/hexapawn.bas
    ================================================
    1 PRINT TAB(32);"HEXAPAWN"
    2 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    3 PRINT:PRINT:PRINT
    4 REM  HEXAPAWN:  INTERPRETATION OF HEXAPAWN GAME AS PRESENTED IN
    5 REM  MARTIN GARDNER'S "THE UNEXPECTED HANGING AND OTHER MATHEMATIC-
    6 REM  AL DIVERSIONS", CHAPTER EIGHT:  A MATCHBOX GAME-LEARNING MACHINE
    7 REM  ORIGINAL VERSION FOR H-P TIMESHARE SYSTEM BY R.A. KAAPKE 5/5/76
    8 REM  INSTRUCTIONS BY JEFF DALTON
    9 REM  CONVERSION TO MITS BASIC BY STEVE NORTH
    10 DIM B(19,9),M(19,4),S(9),P$(3)
    15 W=0: L=0
    20 DEF FNR(X)=-3*(X=1)-(X=3)-4*(X=6)-6*(X=4)-7*(X=9)-9*(X=7)+FNS(X)
    25 DEF FNS(X)=-X*(X=2 OR X=5 OR X=8)
    30 DEF FNM(Y)=Y-INT(Y/10)*10
    35 P$="X.O"
    40 FOR I=1 TO 19: FOR J=1 TO 9: READ B(I,J): NEXT J: NEXT I
    45 FOR I=1 TO 19: FOR J=1 TO 4: READ M(I,J): NEXT J: NEXT I
    50 PRINT "INSTRUCTIONS (Y-N)";
    60 INPUT A$
    70 A$=LEFT$(A$,1)
    80 IF A$="Y" THEN 2000
    90 IF A$<>"N" THEN 50
    100 X=0: Y=0
    111 S(4)=0: S(5)=0: S(6)=0
    112 S(1)=-1: S(2)=-1: S(3)=-1
    113 S(7)=1: S(8)=1: S(9)=1
    115 GOSUB 1000
    120 PRINT "YOUR MOVE";
    121 INPUT M1,M2
    122 IF M1=INT(M1)AND M2=INT(M2)AND M1>0 AND M1<10 AND M2>0 AND M2<10 THEN 130
    123 PRINT "ILLEGAL CO-ORDINATES."
    124 GOTO 120
    130 IF S(M1)=1 THEN 150
    140 PRINT "ILLEGAL MOVE.": GOTO 120
    150 IF S(M2)=1 THEN 140
    160 IF M2-M1<>-3 AND S(M2)<>-1 THEN 140
    170 IF M2>M1 THEN 140
    180 IF M2-M1=-3 AND (S(M2)<>0) THEN 140
    185 IF M2-M1<-4 THEN 140
    186 IF M1=7 AND M2=3 THEN 140
    190 S(M1)=0
    200 S(M2)=1
    205 GOSUB 1000
    210 IF S(1)=1 OR S(2)=1 OR S(3)=1 THEN 820
    220 FOR I=1 TO 9
    221 IF S(I)=-1 THEN 230
    222 NEXT I
    223 GOTO 820
    230 FOR I=1 TO 9
    240 IF S(I)<>-1 THEN 330
    250 IF S(I+3)=0 THEN 350
    260 IF FNR(I)=I THEN 320
    270 IF I>3 THEN 300
    280 IF S(5)=1 THEN 350
    290 GOTO 330
    300 IF S(8)=1 THEN 350
    310 GOTO 330
    320 IF S(I+2)=1 OR S(I+4)=1 THEN 350
    330 NEXT I
    340 GOTO 820
    350 FOR I=1 TO 19
    360 FOR J=1 TO 3
    370 FOR K=3 TO 1 STEP -1
    380 T((J-1)*3+K)=B(I,(J-1)*3+4-K)
    390 NEXT K
    400 NEXT J
    410 FOR J=1 TO 9
    420 IF S(J)<>B(I,J) THEN 460
    430 NEXT J
    440 R=0
    450 GOTO 540
    460 FOR J=1 TO 9
    470 IF S(J)<>T(J) THEN 510
    480 NEXT J
    490 R=1
    500 GOTO 540
    510 NEXT I
    511 REMEMBER THE TERMINATION OF THIS LOOP IS IMPOSSIBLE
    512 PRINT "ILLEGAL BOARD PATTERN."
    530 STOP
    540 X=I
    550 FOR I=1 TO 4
    560 IF M(X,I)<>0 THEN 600
    570 NEXT I
    580 PRINT "I RESIGN."
    590 GOTO 820
    600 Y=INT(RND(1)*4+1)
    601 IF M(X,Y)=0 THEN 600
    610 IF R<>0 THEN 630
    620 PRINT "I MOVE FROM ";STR$(INT(M(X,Y)/10));" TO ";STR$(FNM(M(X,Y)))
    622 S(INT(M(X,Y)/10))=0
    623 S(FNM(M(X,Y)))=-1
    624 GOTO 640
    630 PRINT "I MOVE FROM ";STR$(FNR(INT(M(X,Y)/10)));" TO ";
    631 PRINT STR$(FNR(FNM(M(X,Y))))
    632 S(FNR(INT(M(X,Y)/10)))=0
    633 S(FNR(FNM(M(X,Y))))=-1
    640 GOSUB 1000
    641 IF S(7)=-1 OR S(8)=-1 OR S(9)=-1 THEN 870
    650 FOR I=1 TO 9
    660 IF S(I)=1 THEN 690
    670 NEXT I
    680 GOTO 870
    690 FOR I=1 TO 9
    700 IF S(I)<>1 THEN 790
    710 IF S(I-3)=0 THEN 120
    720 IF FNR(I)=I THEN 780
    730 IF I<7 THEN 760
    740 IF S(5)=-1 THEN 120
    750 GOTO 790
    760 IF S(2)=-1 THEN 120
    770 GOTO 790
    780 IF S(I-2)=-1 OR S(I-4)=-1 THEN 120
    790 NEXT I
    800 PRINT "YOU CAN'T MOVE, SO ";
    810 GOTO 870
    820 PRINT "YOU WIN."
    830 M(X,Y)=0
    840 L=L+1
    850 PRINT "I HAVE WON";W;"AND YOU";L;"OUT OF";L+W;"GAMES."
    851 PRINT
    860 GOTO 100
    870 PRINT "I WIN."
    880 W=W+1
    890 GOTO 850
    900 DATA -1,-1,-1,1,0,0,0,1,1,-1,-1,-1,0,1,0,1,0,1
    905 DATA -1,0,-1,-1,1,0,0,0,1,0,-1,-1,1,-1,0,0,0,1
    910 DATA -1,0,-1,1,1,0,0,1,0,-1,-1,0,1,0,1,0,0,1
    915 DATA 0,-1,-1,0,-1,1,1,0,0,0,-1,-1,-1,1,1,1,0,0
    920 DATA -1,0,-1,-1,0,1,0,1,0,0,-1,-1,0,1,0,0,0,1
    925 DATA 0,-1,-1,0,1,0,1,0,0,-1,0,-1,1,0,0,0,0,1
    930 DATA 0,0,-1,-1,-1,1,0,0,0,-1,0,0,1,1,1,0,0,0
    935 DATA 0,-1,0,-1,1,1,0,0,0,-1,0,0,-1,-1,1,0,0,0
    940 DATA 0,0,-1,-1,1,0,0,0,0,0,-1,0,1,-1,0,0,0,0
    945 DATA -1,0,0,-1,1,0,0,0,0
    950 DATA 24,25,36,0,14,15,36,0,15,35,36,47,36,58,59,0
    955 DATA 15,35,36,0,24,25,26,0,26,57,58,0
    960 DATA 26,35,0,0,47,48,0,0,35,36,0,0,35,36,0,0
    965 DATA 36,0,0,0,47,58,0,0,15,0,0,0
    970 DATA 26,47,0,0,47,58,0,0,35,36,47,0,28,58,0,0,15,47,0,0
    1000 PRINT
    1010 FOR I=1 TO 3
    1020 FOR J=1 TO 3
    1030 PRINT TAB(10);MID$(P$,S((I-1)*3+J)+2,1);
    1040 NEXT J
    1050 PRINT
    1060 NEXT I
    1070 PRINT
    1080 RETURN
    2000 PRINT: PRINT "THIS PROGRAM PLAYS THE GAME OF HEXAPAWN."
    2010 PRINT "HEXAPAWN IS PLAYED WITH CHESS PAWNS ON A 3 BY 3 BOARD."
    2020 PRINT "THE PAWNS ARE MOVED AS IN CHESS - ONE SPACE FORWARD TO"
    2030 PRINT "AN EMPTY SPACE OR ONE SPACE FORWARD AND DIAGONALLY TO"
    2040 PRINT "CAPTURE AN OPPOSING MAN.  ON THE BOARD, YOUR PAWNS"
    2050 PRINT "ARE 'O', THE COMPUTER'S PAWNS ARE 'X', AND EMPTY "
    2060 PRINT "SQUARES ARE '.'.  TO ENTER A MOVE, TYPE THE NUMBER OF"
    2070 PRINT "THE SQUARE YOU ARE MOVING FROM, FOLLOWED BY THE NUMBER"
    2080 PRINT "OF THE SQUARE YOU WILL MOVE TO.  THE NUMBERS MUST BE"
    2090 PRINT "SEPERATED BY A COMMA.": PRINT
    2100 PRINT "THE COMPUTER STARTS A SERIES OF GAMES KNOWING ONLY WHEN"
    2105 PRINT "THE GAME IS WON (A DRAW IS IMPOSSIBLE) AND HOW TO MOVE."
    2110 PRINT "IT HAS NO STRATEGY AT FIRST AND JUST MOVES RANDOMLY."
    2120 PRINT "HOWEVER, IT LEARNS FROM EACH GAME.  THUS, WINNING BECOMES"
    2130 PRINT "MORE AND MORE DIFFICULT.  ALSO, TO HELP OFFSET YOUR"
    2140 PRINT "INITIAL ADVANTAGE, YOU WILL NOT BE TOLD HOW TO WIN THE"
    2150 PRINT "GAME BUT MUST LEARN THIS BY PLAYING."
    2160 PRINT: PRINT "THE NUMBERING OF THE BOARD IS AS FOLLOWS:"
    2170 PRINT TAB(10);"123": PRINT TAB(10);"456": PRINT TAB(10);"789"
    2180 PRINT: PRINT "FOR EXAMPLE, TO MOVE YOUR RIGHTMOST PAWN FORWARD,"
    2190 PRINT "YOU WOULD TYPE 9,6 IN RESPONSE TO THE QUESTION"
    2200 PRINT "'YOUR MOVE ?'.  SINCE I'M A GOOD SPORT, YOU'LL ALWAYS"
    2210 PRINT "GO FIRST.": PRINT
    2220 GOTO 100
    9999 END
    
    
    ================================================
    FILE: 46_Hexapawn/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 46_Hexapawn/javascript/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells)
    
    
    ================================================
    FILE: 46_Hexapawn/javascript/hexapawn.html
    ================================================
    
    
    HEXAPAWN
    
    
    
    
    
    
    
    
    
    
    ================================================
    FILE: 46_Hexapawn/javascript/hexapawn.js
    ================================================
    // HEXAPAWN
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    var ba = [,
              [,-1,-1,-1,1,0,0,0,1,1],
              [,-1,-1,-1,0,1,0,1,0,1],
              [,-1,0,-1,-1,1,0,0,0,1],
              [,0,-1,-1,1,-1,0,0,0,1],
              [,-1,0,-1,1,1,0,0,1,0],
              [,-1,-1,0,1,0,1,0,0,1],
              [,0,-1,-1,0,-1,1,1,0,0],
              [,0,-1,-1,-1,1,1,1,0,0],
              [,-1,0,-1,-1,0,1,0,1,0],
              [,0,-1,-1,0,1,0,0,0,1],
              [,0,-1,-1,0,1,0,1,0,0],
              [,-1,0,-1,1,0,0,0,0,1],
              [,0,0,-1,-1,-1,1,0,0,0],
              [,-1,0,0,1,1,1,0,0,0],
              [,0,-1,0,-1,1,1,0,0,0],
              [,-1,0,0,-1,-1,1,0,0,0],
              [,0,0,-1,-1,1,0,0,0,0],
              [,0,-1,0,1,-1,0,0,0,0],
              [,-1,0,0,-1,1,0,0,0,0]];
    var ma = [,
              [,24,25,36,0],
              [,14,15,36,0],
              [,15,35,36,47],
              [,36,58,59,0],
              [,15,35,36,0],
              [,24,25,26,0],
              [,26,57,58,0],
              [,26,35,0,0],
              [,47,48,0,0],
              [,35,36,0,0],
              [,35,36,0,0],
              [,36,0,0,0],
              [,47,58,0,0],
              [,15,0,0,0],
              [,26,47,0,0],
              [,47,58,0,0],
              [,35,36,47,0],
              [,28,58,0,0],
              [,15,47,0,0]];
    var s = [];
    var t = [];
    var ps = "X.O";
    
    function show_board()
    {
        print("\n");
        for (var i = 1; i <= 3; i++) {
            print(tab(10));
            for (var j = 1; j <= 3; j++) {
                print(ps[s[(i - 1) * 3 + j] + 1]);
            }
            print("\n");
        }
    }
    
    function mirror(x)
    {
        if (x == 1)
            return 3;
        if (x == 3)
            return 1;
        if (x == 6)
            return 4;
        if (x == 4)
            return 6;
        if (x == 9)
            return 7;
        if (x == 7)
            return 9;
        return x;
    }
    
    // Main program
    async function main()
    {
        print(tab(32) + "HEXAPAWN\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        // 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 R.A. KAAPKE 5/5/76
        // INSTRUCTIONS BY JEFF DALTON
        // CONVERSION TO MITS BASIC BY STEVE NORTH
        for (i = 0; i <= 9; i++) {
            s[i] = 0;
        }
        w = 0;
        l = 0;
        do {
            print("INSTRUCTIONS (Y-N)");
            str = await input();
            str = str.substr(0, 1);
        } while (str != "Y" && str != "N") ;
        if (str == "Y") {
            print("\n");
            print("THIS PROGRAM PLAYS THE GAME OF HEXAPAWN.\n");
            print("HEXAPAWN IS PLAYED WITH CHESS PAWNS ON A 3 BY 3 BOARD.\n");
            print("THE PAWNS ARE MOVED AS IN CHESS - ONE SPACE FORWARD TO\n");
            print("AN EMPTY SPACE OR ONE SPACE FORWARD AND DIAGONALLY TO\n");
            print("CAPTURE AN OPPOSING MAN.  ON THE BOARD, YOUR PAWNS\n");
            print("ARE 'O', THE COMPUTER'S PAWNS ARE 'X', AND EMPTY \n");
            print("SQUARES ARE '.'.  TO ENTER A MOVE, TYPE THE NUMBER OF\n");
            print("THE SQUARE YOU ARE MOVING FROM, FOLLOWED BY THE NUMBER\n");
            print("OF THE SQUARE YOU WILL MOVE TO.  THE NUMBERS MUST BE\n");
            print("SEPERATED BY A COMMA.\n");
            print("\n");
            print("THE COMPUTER STARTS A SERIES OF GAMES KNOWING ONLY WHEN\n");
            print("THE GAME IS WON (A DRAW IS IMPOSSIBLE) AND HOW TO MOVE.\n");
            print("IT HAS NO STRATEGY AT FIRST AND JUST MOVES RANDOMLY.\n");
            print("HOWEVER, IT LEARNS FROM EACH GAME.  THUS, WINNING BECOMES\n");
            print("MORE AND MORE DIFFICULT.  ALSO, TO HELP OFFSET YOUR\n");
            print("INITIAL ADVANTAGE, YOU WILL NOT BE TOLD HOW TO WIN THE\n");
            print("GAME BUT MUST LEARN THIS BY PLAYING.\n");
            print("\n");
            print("THE NUMBERING OF THE BOARD IS AS FOLLOWS:\n");
            print(tab(10) + "123\n");
            print(tab(10) + "456\n");
            print(tab(10) + "789\n");
            print("\n");
            print("FOR EXAMPLE, TO MOVE YOUR RIGHTMOST PAWN FORWARD,\n");
            print("YOU WOULD TYPE 9,6 IN RESPONSE TO THE QUESTION\n");
            print("'YOUR MOVE ?'.  SINCE I'M A GOOD SPORT, YOU'LL ALWAYS\n");
            print("GO FIRST.\n");
            print("\n");
        }
        while (1) {
            x = 0;
            y = 0;
            s[4] = 0;
            s[5] = 0;
            s[6] = 0;
            s[1] = -1;
            s[2] = -1;
            s[3] = -1;
            s[7] = 1;
            s[8] = 1;
            s[9] = 1;
            show_board();
            while (1) {
                while (1) {
                    print("YOUR MOVE");
                    str = await input();
                    m1 = parseInt(str);
                    m2 = parseInt(str.substr(str.indexOf(",") + 1));
                    if (m1 > 0 && m1 < 10 && m2 > 0 && m2 < 10) {
                        if (s[m1] != 1 || s[m2] == 1 || (m2 - m1 != -3 && s[m2] != -1) || (m2 > m1) || (m2 - m1 == -3 && s[m2] != 0) || (m2 - m1 < -4) || (m1 == 7 && m2 == 3))
                            print("ILLEGAL MOVE.\n");
                        else
                            break;
                    } else {
                        print("ILLEGAL CO-ORDINATES.\n");
                    }
                }
    
                // Move player's pawn
                s[m1] = 0;
                s[m2] = 1;
                show_board();
    
                // Find computer pawns
                for (i = 1; i <= 9; i++) {
                    if (s[i] == -1)
                        break;
                }
                // If none or player reached top then finish
                if (i > 9 || s[1] == 1 || s[2] == 1 || s[3] == 1) {
                    computer = false;
                    break;
                }
                // Find computer pawns with valid move
                for (i = 1; i <= 9; i++) {
                    if (s[i] != -1)
                        continue;
                    if (s[i + 3] == 0
                     || (mirror(i) == i && (s[i + 2] == 1 || s[i + 4] == 1))
                     || (i <= 3 && s[5] == 1)
                     || s[8] == 1)
                        break;
                }
                if (i > 9) {  // Finish if none possible
                    computer = false;
                    break;
                }
                for (i = 1; i <= 19; i++) {
                    for (j = 1; j <= 3; j++) {
                        for (k = 3; k >= 1; k--) {
                            t[(j - 1) * 3 + k] = ba[i][(j - 1) * 3 + 4 - k];
                        }
                    }
                    for (j = 1; j <= 9; j++) {
                        if (s[j] != ba[i][j])
                            break;
                    }
                    if (j > 9) {
                        r = 0;
                        break;
                    }
                    for (j = 1; j <= 9; j++) {
                        if (s[j] != t[j])
                            break;
                    }
                    if (j > 9) {
                        r = 1;
                        break;
                    }
                }
                if (i > 19) {
                    print("ILLEGAL BOARD PATTERN\n");
                    break;
                }
                x = i;
                for (i = 1; i <= 4; i++) {
                    if (ma[x][i] != 0)
                        break;
                }
                if (i > 4) {
                    print("I RESIGN.\n");
                    computer = false;
                    break;
                }
                // Select random move from possibilities
                do {
                    y = Math.floor(Math.random() * 4 + 1);
                } while (ma[x][y] == 0) ;
                // Announce move
                if (r == 0) {
                    print("I MOVE FROM " + Math.floor(ma[x][y] / 10) + " TO " + ma[x][y] % 10 + "\n");
                    s[Math.floor(ma[x][y] / 10)] = 0;
                    s[ma[x][y] % 10] = -1;
                } else {
                    print("I MOVE FROM " + mirror(Math.floor(ma[x][y] / 10)) + " TO " + mirror(ma[x][y]) % 10 + "\n");
                    s[mirror(Math.floor(ma[x][y] / 10))] = 0;
                    s[mirror(ma[x][y] % 10)] = -1;
                }
                show_board();
                // Finish if computer reaches bottom
                if (s[7] == -1 || s[8] == -1 || s[9] == -1) {
                    computer = true;
                    break;
                }
                // Finish if no player pawns
                for (i = 1; i <= 9; i++) {
                    if (s[i] == 1)
                        break;
                }
                if (i > 9) {
                    computer = true;
                    break;
                }
                // Finish if player cannot move
                for (i = 1; i <= 9; i++) {
                    if (s[i] != 1)
                        continue;
                    if (s[i - 3] == 0)
                        break;
                    if (mirror(i) != i) {
                        if (i >= 7) {
                            if (s[5] == -1)
                                break;
                        } else {
                            if (s[2] == -1)
                                break;
                        }
                    } else {
                        if (s[i - 2] == -1 || s[i - 4] == -1)
                            break;
                    }
    
                }
                if (i > 9) {
                    print("YOU CAN'T MOVE, SO ");
                    computer = true;
                    break;
                }
            }
            if (computer) {
                print("I WIN.\n");
                w++;
            } else {
                print("YOU WIN\n");
                ma[x][y] = 0;
                l++;
            }
            print("I HAVE WON " + w + " AND YOU " + l + " OUT OF " + (l + w) + " GAMES.\n");
            print("\n");
        }
    }
    
    main();
    
    
    ================================================
    FILE: 46_Hexapawn/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 46_Hexapawn/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 46_Hexapawn/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 46_Hexapawn/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 46_Hexapawn/python/hexapawn.py
    ================================================
    """
    HEXAPAWN
    
    A machine learning game, an interpretation of HEXAPAWN game as
    presented in Martin Gardner's "The Unexpected Hanging and Other
    Mathematical Diversions", Chapter Eight: A Matchbox Game-Learning
    Machine.
    
    Original version for H-P timeshare system by R.A. Kaapke 5/5/76
    Instructions by Jeff Dalton
    Conversion to MITS BASIC by Steve North
    
    
    Port to Python by Dave LeCompte
    """
    
    # PORTING NOTES:
    #
    # I printed out the BASIC code and hand-annotated what each little block
    # of code did, which feels amazingly retro.
    #
    # I encourage other porters that have a complex knot of GOTOs and
    # semi-nested subroutines to do hard-copy hacking, it might be a
    # different perspective that helps.
    #
    # A spoiler - the objective of the game is not documented, ostensibly to
    # give the human player a challenge. If a player (human or computer)
    # advances a pawn across the board to the far row, that player wins. If
    # a player has no legal moves (either by being blocked, or all their
    # pieces having been captured), that player loses.
    #
    # The original BASIC had 2 2-dimensional tables stored in DATA at the
    # end of the program. This encoded all 19 different board configurations
    # (Hexapawn is a small game), with reflections in one table, and then in
    # a parallel table, for each of the 19 rows, a list of legal moves was
    # encoded by turning them into 2-digit decimal numbers. As gameplay
    # continued, the AI would overwrite losing moves with 0 in the second
    # array.
    #
    # My port takes this "parallel array" structure and turns that
    # information into a small Python class, BoardLayout. BoardLayout stores
    # the board description and legal moves, but stores the moves as (row,
    # column) 2-tuples, which is easier to read. The logic for checking if a
    # BoardLayout matches the current board, as well as removing losing move
    # have been moved into methods of this class.
    
    import random
    from typing import Iterator, List, NamedTuple, Optional, Tuple
    
    PAGE_WIDTH = 64
    
    HUMAN_PIECE = 1
    EMPTY_SPACE = 0
    COMPUTER_PIECE = -1
    
    
    class ComputerMove(NamedTuple):
        board_index: int
        move_index: int
        m1: int
        m2: int
    
    
    wins = 0
    losses = 0
    
    
    def print_centered(msg: str) -> None:
        spaces = " " * ((PAGE_WIDTH - len(msg)) // 2)
        print(spaces + msg)
    
    
    def print_header(title: str) -> None:
        print_centered(title)
        print_centered("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n\n\n")
    
    
    def print_instructions() -> None:
        print(
            """
    THIS PROGRAM PLAYS THE GAME OF HEXAPAWN.
    HEXAPAWN IS PLAYED WITH CHESS PAWNS ON A 3 BY 3 BOARD.
    THE PAWNS ARE MOVED AS IN CHESS - ONE SPACE FORWARD TO
    AN EMPTY SPACE OR ONE SPACE FORWARD AND DIAGONALLY TO
    CAPTURE AN OPPOSING MAN.  ON THE BOARD, YOUR PAWNS
    ARE 'O', THE COMPUTER'S PAWNS ARE 'X', AND EMPTY
    SQUARES ARE '.'.  TO ENTER A MOVE, TYPE THE NUMBER OF
    THE SQUARE YOU ARE MOVING FROM, FOLLOWED BY THE NUMBER
    OF THE SQUARE YOU WILL MOVE TO.  THE NUMBERS MUST BE
    SEPERATED BY A COMMA.
    
    THE COMPUTER STARTS A SERIES OF GAMES KNOWING ONLY WHEN
    THE GAME IS WON (A DRAW IS IMPOSSIBLE) AND HOW TO MOVE.
    IT HAS NO STRATEGY AT FIRST AND JUST MOVES RANDOMLY.
    HOWEVER, IT LEARNS FROM EACH GAME.  THUS, WINNING BECOMES
    MORE AND MORE DIFFICULT.  ALSO, TO HELP OFFSET YOUR
    INITIAL ADVANTAGE, YOU WILL NOT BE TOLD HOW TO WIN THE
    GAME BUT MUST LEARN THIS BY PLAYING.
    
    THE NUMBERING OF THE BOARD IS AS FOLLOWS:
              123
              456
              789
    
    FOR EXAMPLE, TO MOVE YOUR RIGHTMOST PAWN FORWARD,
    YOU WOULD TYPE 9,6 IN RESPONSE TO THE QUESTION
    'YOUR MOVE ?'.  SINCE I'M A GOOD SPORT, YOU'LL ALWAYS
    GO FIRST.
    
    """
        )
    
    
    def prompt_yes_no(msg: str) -> bool:
        while True:
            print(msg)
            response = input().upper()
            if response[0] == "Y":
                return True
            elif response[0] == "N":
                return False
    
    
    def reverse_space_name(space_name: int) -> int:
        # reverse a space name in the range 1-9 left to right
        assert 1 <= space_name <= 9
    
        reflections = {1: 3, 2: 2, 3: 1, 4: 6, 5: 5, 6: 4, 7: 9, 8: 8, 9: 7}
        return reflections[space_name]
    
    
    def is_space_in_center_column(space_name: int) -> bool:
        return reverse_space_name(space_name) == space_name
    
    
    class BoardLayout:
        def __init__(self, cells: List[int], move_list: List[Tuple[int, int]]) -> None:
            self.cells = cells
            self.moves = move_list
    
        def _check_match_no_mirror(self, cell_list: List[int]) -> bool:
            return all(
                board_contents == cell_list[space_index]
                for space_index, board_contents in enumerate(self.cells)
            )
    
        def _check_match_with_mirror(self, cell_list: List[int]) -> bool:
            for space_index, board_contents in enumerate(self.cells):
                reversed_space_index = reverse_space_name(space_index + 1) - 1
                if board_contents != cell_list[reversed_space_index]:
                    return False
            return True
    
        def check_match(self, cell_list: List[int]) -> Tuple[bool, Optional[bool]]:
            if self._check_match_with_mirror(cell_list):
                return True, True
            elif self._check_match_no_mirror(cell_list):
                return True, False
            return False, None
    
        def get_random_move(
            self, reverse_board: Optional[bool]
        ) -> Optional[Tuple[int, int, int]]:
            if not self.moves:
                return None
            move_index = random.randrange(len(self.moves))
    
            m1, m2 = self.moves[move_index]
            if reverse_board:
                m1 = reverse_space_name(m1)
                m2 = reverse_space_name(m2)
    
            return move_index, m1, m2
    
    
    boards = [
        BoardLayout([-1, -1, -1, 1, 0, 0, 0, 1, 1], [(2, 4), (2, 5), (3, 6)]),
        BoardLayout([-1, -1, -1, 0, 1, 0, 1, 0, 1], [(1, 4), (1, 5), (3, 6)]),
        BoardLayout([-1, 0, -1, -1, 1, 0, 0, 0, 1], [(1, 5), (3, 5), (3, 6), (4, 7)]),
        BoardLayout([0, -1, -1, 1, -1, 0, 0, 0, 1], [(3, 6), (5, 8), (5, 9)]),
        BoardLayout([-1, 0, -1, 1, 1, 0, 0, 1, 0], [(1, 5), (3, 5), (3, 6)]),
        BoardLayout([-1, -1, 0, 1, 0, 1, 0, 0, 1], [(2, 4), (2, 5), (2, 6)]),
        BoardLayout([0, -1, -1, 0, -1, 1, 1, 0, 0], [(2, 6), (5, 7), (5, 8)]),
        BoardLayout([0, -1, -1, -1, 1, 1, 1, 0, 0], [(2, 6), (3, 5)]),
        BoardLayout([-1, 0, -1, -1, 0, 1, 0, 1, 0], [(4, 7), (4, 8)]),
        BoardLayout([0, -1, -1, 0, 1, 0, 0, 0, 1], [(3, 5), (3, 6)]),
        BoardLayout([0, -1, -1, 0, 1, 0, 1, 0, 0], [(3, 5), (3, 6)]),
        BoardLayout([-1, 0, -1, 1, 0, 0, 0, 0, 1], [(3, 6)]),
        BoardLayout([0, 0, -1, -1, -1, 1, 0, 0, 0], [(4, 7), (5, 8)]),
        BoardLayout([-1, 0, 0, 1, 1, 1, 0, 0, 0], [(1, 5)]),
        BoardLayout([0, -1, 0, -1, 1, 1, 0, 0, 0], [(2, 6), (4, 7)]),
        BoardLayout([-1, 0, 0, -1, -1, 1, 0, 0, 0], [(4, 7), (5, 8)]),
        BoardLayout([0, 0, -1, -1, 1, 0, 0, 0, 0], [(3, 5), (3, 6), (4, 7)]),
        BoardLayout([0, -1, 0, 1, -1, 0, 0, 0, 0], [(2, 8), (5, 8)]),
        BoardLayout([-1, 0, 0, -1, 1, 0, 0, 0, 0], [(1, 5), (4, 7)]),
    ]
    
    
    def get_move(board_index: int, move_index: int) -> Tuple[int, int]:
        assert board_index >= 0 and board_index < len(boards)
        board = boards[board_index]
    
        assert move_index >= 0 and move_index < len(board.moves)
    
        return board.moves[move_index]
    
    
    def remove_move(board_index: int, move_index: int) -> None:
        assert board_index >= 0 and board_index < len(boards)
        board = boards[board_index]
    
        assert move_index >= 0 and move_index < len(board.moves)
    
        del board.moves[move_index]
    
    
    def init_board() -> List[int]:
        return [COMPUTER_PIECE] * 3 + [EMPTY_SPACE] * 3 + [HUMAN_PIECE] * 3
    
    
    def print_board(board: List[int]) -> None:
        piece_dict = {COMPUTER_PIECE: "X", EMPTY_SPACE: ".", HUMAN_PIECE: "O"}
    
        space = " " * 10
        print()
        for row in range(3):
            line = ""
            for column in range(3):
                line += space
                space_number = row * 3 + column
                space_contents = board[space_number]
                line += piece_dict[space_contents]
            print(line)
        print()
    
    
    def get_coordinates() -> Tuple[int, int]:
        while True:
            try:
                print("YOUR MOVE?")
                response = input()
                m1, m2 = (int(c) for c in response.split(","))
                return m1, m2
            except ValueError:
                print_illegal()
    
    
    def print_illegal() -> None:
        print("ILLEGAL MOVE.")
    
    
    def board_contents(board: List[int], space_number: int) -> int:
        return board[space_number - 1]
    
    
    def set_board(board: List[int], space_number: int, new_value: int) -> None:
        board[space_number - 1] = new_value
    
    
    def is_legal_human_move(board: List[int], m1: int, m2: int) -> bool:
        if board_contents(board, m1) != HUMAN_PIECE:
            # Start space doesn't contain player's piece
            return False
        if board_contents(board, m2) == HUMAN_PIECE:
            # Destination space contains player's piece (can't capture your own piece)
            return False
    
        is_capture = m2 - m1 != -3
        if is_capture and board_contents(board, m2) != COMPUTER_PIECE:
            # Destination does not contain computer piece
            return False
    
        if m2 > m1:
            # can't move backwards
            return False
    
        if (not is_capture) and board_contents(board, m2) != EMPTY_SPACE:
            # Destination is not open
            return False
    
        return False if m2 - m1 < -4 else m1 != 7 or m2 != 3
    
    
    def player_piece_on_back_row(board: List[int]) -> bool:
        return any(board_contents(board, space) == HUMAN_PIECE for space in range(1, 4))
    
    
    def computer_piece_on_front_row(board: List[int]) -> bool:
        return any(board_contents(board, space) == COMPUTER_PIECE for space in range(7, 10))
    
    
    def all_human_pieces_captured(board: List[int]) -> bool:
        return not list(get_human_spaces(board))
    
    
    def all_computer_pieces_captured(board: List[int]) -> bool:
        return not list(get_computer_spaces(board))
    
    
    def human_win(last_computer_move: ComputerMove) -> None:
        print("YOU WIN")
        remove_move(last_computer_move.board_index, last_computer_move.move_index)
        global losses
        losses += 1
    
    
    def computer_win(has_moves: bool) -> None:
        msg = ("YOU CAN'T MOVE, SO " if not has_moves else "") + "I WIN"
        print(msg)
        global wins
        wins += 1
    
    
    def show_scores() -> None:
        print(f"I HAVE WON {wins} AND YOU {losses} OUT OF {wins + losses} GAMES.\n")
    
    
    def human_has_move(board: List[int]) -> bool:
        for i in get_human_spaces(board):
            if board_contents(board, i - 3) == EMPTY_SPACE:
                # can move piece forward
                return True
            elif is_space_in_center_column(i):
                if (board_contents(board, i - 2) == COMPUTER_PIECE) or (
                    board_contents(board, i - 4) == COMPUTER_PIECE
                ):
                    # can capture from center
                    return True
                else:
                    continue
            elif i < 7:
                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 in [7, 9]
                # can capture computer piece at 5
                return True
            else:
                continue
        return False
    
    
    def get_board_spaces() -> Iterator[int]:
        """generates the space names (1-9)"""
        yield from range(1, 10)
    
    
    def get_board_spaces_with(board: List[int], val: int) -> Iterator[int]:
        """generates spaces containing pieces of type val"""
        for i in get_board_spaces():
            if board_contents(board, i) == val:
                yield i
    
    
    def get_human_spaces(board: List[int]) -> Iterator[int]:
        yield from get_board_spaces_with(board, HUMAN_PIECE)
    
    
    def get_empty_spaces(board: List[int]) -> Iterator[int]:
        yield from get_board_spaces_with(board, EMPTY_SPACE)
    
    
    def get_computer_spaces(board: List[int]) -> Iterator[int]:
        yield from get_board_spaces_with(board, COMPUTER_PIECE)
    
    
    def has_computer_move(board: List[int]) -> bool:
        for i in get_computer_spaces(board):
            if board_contents(board, i + 3) == EMPTY_SPACE:
                # can move forward (down)
                return True
    
            if is_space_in_center_column(i):
                # i is in the middle column
                if (board_contents(board, i + 2) == HUMAN_PIECE) or (
                    board_contents(board, i + 4) == HUMAN_PIECE
                ):
                    return True
            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
    
    
    def find_board_index_that_matches_board(board: List[int]) -> Tuple[int, Optional[bool]]:
        for board_index, board_layout in enumerate(boards):
            matches, is_reversed = board_layout.check_match(board)
            if matches:
                return board_index, is_reversed
    
        # This point should never be reached
        # In future, mypy might be able to check exhaustiveness via assert_never
        raise RuntimeError("ILLEGAL BOARD PATTERN.")
    
    
    def pick_computer_move(board: List[int]) -> Optional[ComputerMove]:
        if not has_computer_move(board):
            return None
    
        board_index, reverse_board = find_board_index_that_matches_board(board)
    
        m = boards[board_index].get_random_move(reverse_board)
    
        if m is None:
            print("I RESIGN")
            return None
    
        move_index, m1, m2 = m
    
        return ComputerMove(board_index, move_index, m1, m2)
    
    
    def get_human_move(board: List[int]) -> Tuple[int, int]:
        while True:
            m1, m2 = get_coordinates()
    
            if not is_legal_human_move(board, m1, m2):
                print_illegal()
            else:
                return m1, m2
    
    
    def apply_move(board: List[int], m1: int, m2: int, piece_value: int) -> None:
        set_board(board, m1, EMPTY_SPACE)
        set_board(board, m2, piece_value)
    
    
    def play_game() -> None:
        last_computer_move = None
    
        board = init_board()
    
        while True:
            print_board(board)
    
            m1, m2 = get_human_move(board)
    
            apply_move(board, m1, m2, HUMAN_PIECE)
    
            print_board(board)
    
            if player_piece_on_back_row(board) or all_computer_pieces_captured(board):
                assert last_computer_move is not None
                human_win(last_computer_move)
                return
    
            computer_move = pick_computer_move(board)
            if computer_move is None:
                assert last_computer_move is not None
                human_win(last_computer_move)
                return
    
            last_computer_move = computer_move
    
            m1, m2 = last_computer_move.m1, last_computer_move.m2
    
            print(f"I MOVE FROM {m1} TO {m2}")
            apply_move(board, m1, m2, COMPUTER_PIECE)
    
            print_board(board)
    
            if computer_piece_on_front_row(board):
                computer_win(True)
                return
            elif (not human_has_move(board)) or (all_human_pieces_captured(board)):
                computer_win(False)
                return
    
    
    def main() -> None:
        print_header("HEXAPAWN")
        if prompt_yes_no("INSTRUCTIONS (Y-N)?"):
            print_instructions()
    
        global wins, losses
        wins = 0
        losses = 0
    
        while True:
            play_game()
            show_scores()
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 46_Hexapawn/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 46_Hexapawn/vbnet/Hexapawn.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Hexapawn", "Hexapawn.vbproj", "{83C2A51C-6014-4CC7-A4AF-81004B5F721F}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{83C2A51C-6014-4CC7-A4AF-81004B5F721F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{83C2A51C-6014-4CC7-A4AF-81004B5F721F}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{83C2A51C-6014-4CC7-A4AF-81004B5F721F}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{83C2A51C-6014-4CC7-A4AF-81004B5F721F}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 46_Hexapawn/vbnet/Hexapawn.vbproj
    ================================================
    
      
        Exe
        Hexapawn
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 46_Hexapawn/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 47_Hi-Lo/README.md
    ================================================
    ### Hi-Lo
    
    This game is an adaptation of the game GUESS; however, instead of just guessing a number between 1 and 100, in this game you win dollars when you guess the number. The directions, in the words of the author, are as follows:
    1. There is an amount of money, between one and one hundred dollars, in the “HI-LO” jackpot.
    2. You will have six chances in which to guess the amount of money in the jackpot.
    3. After each guess, the computer will tell whether the guess was too high or too low.
    4. If the correct amount of money is not guessed after six chances, the computer will print the amount in the jackpot.
    5. If the correct amount of money is guessed within the six chance limit, the computer will register this amount.
    6. After each sequence of guesses, you have the choice of playing again or ending the program. If a new game is played, a new amount of money will constitute the jackpot.
    7. If youwin more than once, then your earnings are totalled.
    
    The author is Dean ALtman of Fort Worth, Texas.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=85)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=100)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 47_Hi-Lo/csharp/HiLo.csproj
    ================================================
    
    
      
        Exe
        net5.0
      
    
    
    
    
    ================================================
    FILE: 47_Hi-Lo/csharp/HiLo.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HiLo", "HiLo.csproj", "{C4D0FAB6-056D-4DD2-827D-3383BE0AA382}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{C4D0FAB6-056D-4DD2-827D-3383BE0AA382}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{C4D0FAB6-056D-4DD2-827D-3383BE0AA382}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{C4D0FAB6-056D-4DD2-827D-3383BE0AA382}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{C4D0FAB6-056D-4DD2-827D-3383BE0AA382}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {BF77DB59-426B-4A01-A8AC-09AAF42DA633}
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 47_Hi-Lo/csharp/Program.cs
    ================================================
    using System;
    
    Console.WriteLine(Tab(34) +                 "HI LO");
    Console.WriteLine(Tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
    Console.WriteLine();
    Console.WriteLine();
    Console.WriteLine();
    Console.WriteLine("THIS IS THE GAME OF HI LO.");
    Console.WriteLine();
    Console.WriteLine("YOU WILL HAVE 6 TRIES TO GUESS THE AMOUNT OF MONEY IN THE");
    Console.WriteLine("HI LO JACKPOT, WHICH IS BETWEEN 1 AND 100 DOLLARS.  IF YOU");
    Console.WriteLine("GUESS THE AMOUNT, YOU WIN ALL THE MONEY IN THE JACKPOT!");
    Console.WriteLine("THEN YOU GET ANOTHER CHANCE TO WIN MORE MONEY.  HOWEVER,");
    Console.WriteLine("IF YOU DO NOT GUESS THE AMOUNT, THE GAME ENDS.");
    Console.WriteLine();
    
    // rnd is our random number generator
    Random rnd = new();
    
    bool playAgain = false;
    int totalWinnings = 0;
    
    do // Our game loop
    {
        int jackpot = rnd.Next(100) + 1; // [0..99] + 1 -> [1..100]
        int guess = 1;
    
        while (true) // Our guessing loop
        {
            Console.WriteLine();
            int amount = ReadInt("YOUR GUESS ");
    
            if (amount == jackpot)
            {
                Console.WriteLine($"GOT IT!!!!!!!!!!   YOU WIN {jackpot} DOLLARS.");
                totalWinnings += jackpot;
                Console.WriteLine($"YOUR TOTAL WINNINGS ARE NOW {totalWinnings} DOLLARS.");
                break;
            }
            else if (amount > jackpot)
            {
                Console.WriteLine("YOUR GUESS IS TOO HIGH.");
            }
            else
            {
                Console.WriteLine("YOUR GUESS IS TOO LOW.");
            }
    
            guess++;
            if (guess > 6)
            {
                Console.WriteLine($"YOU BLEW IT...TOO BAD...THE NUMBER WAS {jackpot}");
                break;
            }
        }
    
        Console.WriteLine();
        Console.Write("PLAY AGAIN (YES OR NO) ");
        playAgain = Console.ReadLine().ToUpper().StartsWith("Y");
    
    } while (playAgain);
    
    Console.WriteLine();
    Console.WriteLine("SO LONG.  HOPE YOU ENJOYED YOURSELF!!!");
    
    // Tab(n) returns n spaces
    static string Tab(int n) => new String(' ', n);
    
    // ReadInt asks the user to enter a number
    static int ReadInt(string question)
    {
        while (true)
        {
            Console.Write(question);
            var input = Console.ReadLine().Trim();
            if (int.TryParse(input, out int value))
            {
                return value;
            }
            Console.WriteLine("!Invalid Number Entered.");
        }
    }
    
    
    ================================================
    FILE: 47_Hi-Lo/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 47_Hi-Lo/hi-lo.bas
    ================================================
    10 PRINT TAB(34);"HI LO"
    20 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    30 PRINT:PRINT:PRINT
    100 PRINT "THIS IS THE GAME OF HI LO.":PRINT
    110 PRINT "YOU WILL HAVE 6 TRIES TO GUESS THE AMOUNT OF MONEY IN THE"
    120 PRINT "HI LO JACKPOT, WHICH IS BETWEEN 1 AND 100 DOLLARS.  IF YOU"
    130 PRINT "GUESS THE AMOUNT, YOU WIN ALL THE MONEY IN THE JACKPOT!"
    140 PRINT "THEN YOU GET ANOTHER CHANCE TO WIN MORE MONEY.  HOWEVER,"
    150 PRINT "IF YOU DO NOT GUESS THE AMOUNT, THE GAME ENDS.":PRINT
    160 R=0
    170 B=0:PRINT
    180 Y=INT(100*RND(1))
    200 PRINT "YOUR GUESS";
    210 INPUT A
    220 B=B+1
    230 IF A=Y THEN 300
    240 IF A>Y THEN 270
    250 PRINT "YOUR GUESS IS TOO LOW.":GOTO 280
    270 PRINT "YOUR GUESS IS TOO HIGH."
    280 PRINT:IF B<6 THEN 200
    290 PRINT "YOU BLEW IT...TOO BAD...THE NUMBER WAS";Y
    295 R=0:GOTO 350
    300 PRINT "GOT IT!!!!!!!!!!   YOU WIN";Y;"DOLLARS."
    310 R=R+Y
    320 PRINT "YOUR TOTAL WINNINGS ARE NOW";R;"DOLLARS."
    350 PRINT:PRINT "PLAY AGAIN (YES OR NO)";
    360 INPUT A$:IF A$="YES" THEN 170
    380 PRINT:PRINT "SO LONG.  HOPE YOU ENJOYED YOURSELF!!!"
    390 END
    
    
    ================================================
    FILE: 47_Hi-Lo/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 47_Hi-Lo/java/src/HiLo.java
    ================================================
    import java.util.Scanner;
    
    /**
     * Game of HiLo
     *
     * Based on the Basic game of Hi-Lo here
     * https://github.com/coding-horror/basic-computer-games/blob/main/47%20Hi-Lo/hi-lo.bas
     *
     * Note:  The idea was to create a version of the 1970's Basic game in Java, without introducing
     *        new features - no additional text, error checking, etc has been added.
     */
    public class HiLo {
    
        public static final int LOW_NUMBER_RANGE = 1;
        public static final int HIGH_NUMBER_RANGE = 100;
        public static final int MAX_GUESSES = 6;
    
        private enum GAME_STATE {
            STARTING,
            START_GAME,
            GUESSING,
            PLAY_AGAIN,
            GAME_OVER
        }
    
        // Used for keyboard input
        private final Scanner kbScanner;
    
        // Current game state
        private GAME_STATE gameState;
    
        // Players Winnings
        private int playerAmountWon;
    
        // Players guess count;
        private int playersGuesses;
    
        // Computers random number
        private int computersNumber;
    
        public HiLo() {
    
            gameState = GAME_STATE.STARTING;
            playerAmountWon = 0;
    
            // Initialise kb scanner
            kbScanner = new Scanner(System.in);
        }
    
        /**
         * Main game loop
         *
         */
        public void play() {
    
            do {
                switch (gameState) {
    
                    // Show an introduction the first time the game is played.
                    case STARTING:
                        intro();
                        gameState = GAME_STATE.START_GAME;
                        break;
    
                    // Generate computers number for player to guess, etc.
                    case START_GAME:
                        init();
                        System.out.println("O.K.  I HAVE A NUMBER IN MIND.");
                        gameState = GAME_STATE.GUESSING;
                        break;
    
                    // Player guesses the number until they get it or run out of guesses
                    case GUESSING:
                        int guess = playerGuess();
    
                        // Check if the player guessed the number
                        if(validateGuess(guess)) {
                            System.out.println("GOT IT!!!!!!!!!!   YOU WIN " + computersNumber
                                    + " DOLLARS.");
                            playerAmountWon += computersNumber;
                            System.out.println("YOUR TOTAL WINNINGS ARE NOW "
                                    + playerAmountWon + " DOLLARS.");
                            gameState = GAME_STATE.PLAY_AGAIN;
                        } else {
                            // incorrect guess
                            playersGuesses++;
                            // Ran out of guesses?
                            if (playersGuesses == MAX_GUESSES) {
                                System.out.println("YOU BLEW IT...TOO BAD...THE NUMBER WAS "
                                        + computersNumber);
                                playerAmountWon = 0;
                                gameState = GAME_STATE.PLAY_AGAIN;
                            }
                        }
                        break;
    
                    // Play again, or exit game?
                    case PLAY_AGAIN:
                        System.out.println();
                        if(yesEntered(displayTextAndGetInput("PLAY AGAIN (YES OR NO) "))) {
                            gameState = GAME_STATE.START_GAME;
                        } else {
                            // Chose not to play again
                            System.out.println("SO LONG.  HOPE YOU ENJOYED YOURSELF!!!");
                            gameState = GAME_STATE.GAME_OVER;
                        }
                }
            } while (gameState != GAME_STATE.GAME_OVER);
        }
    
        /**
         * Checks the players guess against the computers randomly generated number
         *
         * @param theGuess the players guess
         * @return true if the player guessed correctly, false otherwise
         */
        private boolean validateGuess(int theGuess) {
    
            // Correct guess?
            if(theGuess == computersNumber) {
                return true;
            }
    
            if(theGuess > computersNumber) {
                System.out.println("YOUR GUESS IS TOO HIGH.");
            } else {
                System.out.println("YOUR GUESS IS TOO LOW.");
            }
    
            return false;
        }
    
        private void init() {
            playersGuesses = 0;
            computersNumber = randomNumber();
        }
    
        public void intro() {
            System.out.println("HI LO");
            System.out.println("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
            System.out.println();
            System.out.println();
            System.out.println("IS THE GAME OF HI LO.");
            System.out.println();
            System.out.println("YOU WILL HAVE 6 TRIES TO GUESS THE AMOUNT OF MONEY IN THE");
            System.out.println("HI LO JACKPOT, WHICH IS BETWEEN 1 AND 100 DOLLARS.  IF YOU");
            System.out.println("GUESS THE AMOUNT, YOU WIN ALL THE MONEY IN THE JACKPOT!");
            System.out.println("THEN YOU GET ANOTHER CHANCE TO WIN MORE MONEY.  HOWEVER,");
            System.out.println("IF YOU DO NOT GUESS THE AMOUNT, THE GAME ENDS.");
        }
    
        /**
         * Get players guess from kb
         *
         * @return players guess as an int
         */
        private int playerGuess() {
            return Integer.parseInt((displayTextAndGetInput("YOUR GUESS? ")));
        }
    
        /**
         * Checks whether player entered Y or YES to a question.
         *
         * @param text  player string from kb
         * @return true of Y or YES was entered, otherwise false
         */
        private boolean yesEntered(String text) {
            return stringIsAnyValue(text, "Y", "YES");
        }
    
        /**
         * Check whether a string equals one of a variable number of values
         * Useful to check for Y or YES for example
         * Comparison is case insensitive.
         *
         * @param text source string
         * @param values a range of values to compare against the source string
         * @return true if a comparison was found in one of the variable number of strings passed
         */
        private boolean stringIsAnyValue(String text, String... values) {
    
            // Cycle through the variable number of values and test each
            for(String val:values) {
                if(text.equalsIgnoreCase(val)) {
                    return true;
                }
            }
    
            // no matches
            return false;
        }
    
        /*
         * Print a message on the screen, then accept input from Keyboard.
         *
         * @param text message to be displayed on screen.
         * @return what was typed by the player.
         */
        private String displayTextAndGetInput(String text) {
            System.out.print(text);
            return kbScanner.next();
        }
    
        /**
         * Generate random number
         * Used as a single digit of the computer player
         *
         * @return random number
         */
        private int randomNumber() {
            return (int) (Math.random()
                    * (HIGH_NUMBER_RANGE - LOW_NUMBER_RANGE + 1) + LOW_NUMBER_RANGE);
        }
    }
    
    
    ================================================
    FILE: 47_Hi-Lo/java/src/HiLoGame.java
    ================================================
    public class HiLoGame {
    
        public static void main(String[] args) {
    
            HiLo hiLo = new HiLo();
            hiLo.play();
        }
    }
    
    
    ================================================
    FILE: 47_Hi-Lo/javascript/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells)
    
    
    ================================================
    FILE: 47_Hi-Lo/javascript/hi-lo.html
    ================================================
    
    
    HI-LO
    
    
    
    
    
    
    
    
    
    
    ================================================
    FILE: 47_Hi-Lo/javascript/hi-lo.js
    ================================================
    // HI-LO
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    // Main program
    async function main()
    {
        print(tab(34) + "HI LO\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        print("THIS IS THE GAME OF HI LO.\n");
        print("\n");
        print("YOU WILL HAVE 6 TRIES TO GUESS THE AMOUNT OF MONEY IN THE\n");
        print("HI LO JACKPOT, WHICH IS BETWEEN 1 AND 100 DOLLARS.  IF YOU\n");
        print("GUESS THE AMOUNT, YOU WIN ALL THE MONEY IN THE JACKPOT!\n");
        print("THEN YOU GET ANOTHER CHANCE TO WIN MORE MONEY.  HOWEVER,\n");
        print("IF YOU DO NOT GUESS THE AMOUNT, THE GAME ENDS.\n");
        print("\n");
        r = 0;
        while (1) {
            b = 0;
            print("\n");
            y = Math.floor(100 * Math.random());
            for (b = 1; b <= 6; b++) {
                print("YOUR GUESS");
                a = parseInt(await input());
                if (a < y) {
                    print("YOUR GUESS IS TOO LOW.\n");
                } else if (a > y) {
                    print("YOUR GUESS IS TOO HIGH.\n");
                } else {
                    break;
                }
                print("\n");
            }
            if (b > 6) {
                print("YOU BLEW IT...TOO BAD...THE NUMBER WAS " + y + "\n");
                r = 0;
            } else {
                print("GOT IT!!!!!!!!!!   YOU WIN " + y + " DOLLARS.\n");
                r += y;
                print("YOUR TOTAL WINNINGS ARE NOW " + r + " DOLLARS.\n");
            }
            print("\n");
            print("PLAY AGAIN (YES OR NO)");
            str = await input();
            str = str.toUpperCase();
            if (str != "YES")
                break;
        }
        print("\n");
        print("SO LONG.  HOPE YOU ENJOYED YOURSELF!!!\n");
    }
    
    main();
    
    
    ================================================
    FILE: 47_Hi-Lo/kotlin/HiLo.kt
    ================================================
    
    fun main() {
        println(introText)
        var winnings = 0
        do {
            winnings += playGame()
            println("YOUR TOTAL WINNINGS ARE NOW $winnings DOLLARS")
        } while(playAgain())
    
        println("SO LONG.  HOPE YOU ENJOYED YOURSELF!!!")
    }
    
    fun playGame():Int {
        val amount = (1..100).random()
        repeat(6) {
            println("YOUR GUESS")
            val guess = readln().toInt()
            when {
                guess == amount -> {
                    println("GOT IT!!!!!!!! YOU WIN $amount DOLLARS.")
                    return amount
                }
                guess > amount -> println("YOUR GUESS IS TOO HIGH")
                else -> println("YOUR GUESS IS TOO LOW")
            }
        }
        println("YOU BLEW IT...TOO BAD...THE NUMBER WAS $amount")
        return 0
    }
    
    fun playAgain():Boolean {
        println("PLAY AGAIN (YES OR NO)")
        return readLine()?.uppercase() == "YES"
    }
    
    
    val introText = """
        HI LO
        CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY
    
    
        THIS IS THE GAME OF HI LO.
    
        YOU WILL HAVE 6 TRIES TO GUESS THE AMOUNT OF MONEY IN THE
        HI LO JACKPOT, WHICH IS BETWEEN 1 AND 100 DOLLARS.  IF YOU
        GUESS THE AMOUNT, YOU WIN ALL THE MONEY IN THE JACKPOT!
        THEN YOU GET ANOTHER CHANCE TO WIN MORE MONEY.  HOWEVER,
        IF YOU DO NOT GUESS THE AMOUNT, THE GAME ENDS
    
    """.trimIndent()
    
    
    ================================================
    FILE: 47_Hi-Lo/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 47_Hi-Lo/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 47_Hi-Lo/lua/hilo.lua
    ================================================
    local function hilo (randomNum)
       local numTries = 0
       math.randomseed(os.time())
    
       local randomNum = math.random(1, 100)
       print(randomNum)
    
       while numTries < 6 do
          print("")
    
          io.write("YOUR GUESS? ")
       
          local guess = io.read("*n")
    
          numTries = numTries + 1
    
          if guess < randomNum then
             print("YOUR GUESS IS TOO LOW")
          end
    
          if guess > randomNum then
             print("YOUR GUESS IS TOO HIGH")
          end
    
          if guess == randomNum then
             print("GOT IT!!!!!!!!!!   YOU WIN " .. randomNum .. " DOLLARS.")
             break
          end
        end
     
        if numTries == 6 then
           print("")
           print("YOU BLEW IT...TOO BAD...THE NUMBER WAS " .. randomNum)
           return 0
        else
           return randomNum
        end
    end
    
    local THIRTY_FOUR_TABS=string.rep("\t",34)
    print(THIRTY_FOUR_TABS, "HI LO")
    
    local FIFTEEN_TABS=string.rep("\t",15)
    print(FIFTEEN_TABS, "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY")
    
    local THREE_NEWLINES=string.rep("\n", 3)
    print(THREE_NEWLINES)
    
    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.")
    
    local wonSoFar = 0
    
    ::continue::
    local won = 0
    local won = hilo(randomNum)
    wonSoFar = won + wonSoFar
    print("YOUR TOTAL WINNINGS ARE NOW " .. wonSoFar .. " DOLLARS.")
    
    --- This flush is here because if not then it will keep the newline in the
    --- input buffer and cause the program to inadvertantly go to the 
    --- Invalid Answer!
    --- part of the code which we don't want the program to do. Appears to be a
    --- Lua-ism.
    
    io.stdin:flush()
    io.write("PLAY AGAIN (YES OR NO)? ")
    answer = io.read()
    
    while(not(answer == "YES" or answer == "NO")) do
       io.write("Invalid Answer! Try again (YES/NO): ")
       answer = io.read()
    end
    
    if answer == "YES" then
       goto continue
    else
       print("")
       print("SO LONG.  HOPE YOU ENJOYED YOURSELF!!!") 
       os.exit()
    end
    
    ================================================
    FILE: 47_Hi-Lo/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 47_Hi-Lo/perl/hi-lo.pl
    ================================================
    #!/usr/bin/perl
    use strict;
    
    
    print ' 'x 34 . "HI LO\n";
    print ' 'x 15 . "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n";
    print "\n"; print "\n"; print "\n";
    print "THIS IS THE GAME OF HI LO.\n"; print "\n";
    print "YOU WILL HAVE 6 TRIES TO GUESS THE AMOUNT OF MONEY IN THE\n";
    print "HI LO JACKPOT, WHICH IS BETWEEN 1 AND 100 DOLLARS. IF YOU\n";
    print "GUESS THE AMOUNT, YOU WIN ALL THE MONEY IN THE JACKPOT!\n";
    print "THEN YOU GET ANOTHER CHANCE TO WIN MORE MONEY. HOWEVER,\n";
    print "IF YOU DO NOT GUESS THE AMOUNT, THE GAME ENDS.\n"; print "\n";
    my $R=0;
    my $A;
    do {
    	print "\n";
    	my $Y=int(100*rand(1));
    	foreach (1..6) {
    		print "YOUR GUESS $Y";
    		print "? "; chomp($A = );
    		if ($A eq $Y) { last; }
    		if ($A>$Y) {
    			print "YOUR GUESS IS TOO HIGH.\n";
    			} else {
    			print "YOUR GUESS IS TOO LOW.\n";
    			}
    		print "\n";
    		}
    
    	if ($A==$Y) {
    		$R=$R+$Y;
    		print "GOT IT!!!!!!!!!! YOU WIN $Y DOLLARS.\n";
    		print "YOUR TOTAL WINNINGS ARE NOW $R DOLLARS.\n";
    		} else {
    		$R=0;
    		print "YOU BLEW IT...TOO BAD...THE NUMBER WAS $Y"
    		}
    	print "\n"; print "PLAY AGAIN (YES OR NO)";
    	print "? "; chomp($A = );
    	} until (uc($A) ne "YES");
    print "\n"; print "SO LONG. HOPE YOU ENJOYED YOURSELF!!!\n";
    exit;
    
    
    ================================================
    FILE: 47_Hi-Lo/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 47_Hi-Lo/python/hilo.py
    ================================================
    #!/usr/bin/env python3
    import random
    
    MAX_ATTEMPTS = 6
    QUESTION_PROMPT = "? "
    
    
    def main() -> None:
        print("HI LO")
        print("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n\n\n")
        print("THIS IS THE GAME OF HI LO.\n")
        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.\n\n")
    
        total_winnings = 0
        while True:
            print()
            secret = random.randint(1, 100)
            guessed_correctly = False
    
            for _attempt in range(MAX_ATTEMPTS):
                print("YOUR GUESS", end=QUESTION_PROMPT)
                guess = int(input())
    
                if guess == secret:
                    print(f"GOT IT!!!!!!!!!!   YOU WIN {secret} DOLLARS.")
                    guessed_correctly = True
                    break
                elif guess > secret:
                    print("YOUR GUESS IS TOO HIGH.")
                else:
                    print("YOUR GUESS IS TOO LOW.")
    
            if guessed_correctly:
                total_winnings += secret
                print(f"YOUR TOTAL WINNINGS ARE NOW {total_winnings} DOLLARS.")
            else:
                print(f"YOU BLEW IT...TOO BAD...THE NUMBER WAS {secret}")
    
            print("\n")
            print("PLAY AGAIN (YES OR NO)", end=QUESTION_PROMPT)
            answer = input().upper()
            if answer != "YES":
                break
    
        print("\nSO LONG.  HOPE YOU ENJOYED YOURSELF!!!")
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 47_Hi-Lo/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/) by [R.T. Lechow](https://github.com/rtlechow)
    
    
    ================================================
    FILE: 47_Hi-Lo/ruby/hi_lo.rb
    ================================================
    #!/usr/bin/env ruby
    MAX_TRIES = 6
    RANGE = (1..100)
    
    def intro
      puts <<~END_OF_INTRO
        #{'HI LO'.center(74)}
        #{"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n\n\n".center(76)}
        THIS IS THE GAME OF HI LO.\n
        YOU WILL HAVE #{MAX_TRIES} TRIES TO GUESS THE AMOUNT OF MONEY IN THE
        HI LO JACKPOT, WHICH IS BETWEEN #{RANGE.min} AND #{RANGE.max} DOLLARS.  IF YOU
        GUESS THE AMOUNT, YOU WIN ALL THE MONEY IN THE JACKPOT!
        THEN YOU GET ANOTHER CHANCE TO WIN MORE MONEY.  HOWEVER,
        IF YOU DO NOT GUESS THE AMOUNT, THE GAME ENDS.\n\n
      END_OF_INTRO
    end
    
    def make_guess
      puts 'YOUR GUESS?'
      @guess = gets.to_i
    end
    
    def check_guess
      if @guess == @number
        @guessed_correctly = true
        @total_winnings += @number
        puts <<~END_OF_WIN_TEXT
          GOT IT!!!!!!!!!!   YOU WIN #{@number} DOLLARS.
          YOUR TOTAL WINNINGS ARE NOW #{@total_winnings} DOLLARS.
        END_OF_WIN_TEXT
      else
        puts "YOUR GUESS IS TOO #{@guess > @number ? 'HIGH' : 'LOW'}.\n\n"
      end
    end
    
    def blew_it
      @total_winnings = 0
      puts "YOU BLEW IT...TOO BAD...THE NUMBER WAS #{@number}"
    end
    
    def outro
      puts "\nSO LONG.  HOPE YOU ENJOYED YOURSELF!!!"
    end
    
    intro
    @total_winnings = 0
    loop do
      @guessed_correctly = false
      @number = rand(RANGE)
      MAX_TRIES.times do
        make_guess
        check_guess
        break if @guessed_correctly
      end
      blew_it unless @guessed_correctly
      puts "\nPLAY AGAIN (YES OR NO)?"
      break if gets.start_with?(/n/i)
    end
    outro
    
    
    ================================================
    FILE: 47_Hi-Lo/rust/Cargo.toml
    ================================================
    [package]
    name = "guessing_game"
    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.3"
    
    ================================================
    FILE: 47_Hi-Lo/rust/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Rust](https://www.rust-lang.org/)
    
    
    ================================================
    FILE: 47_Hi-Lo/rust/src/main.rs
    ================================================
    use rand::Rng;
    use std::io;
    
    fn main() {
        println!(
            "{: >39}\n{: >57}\n\n\n",
            "HI LO", "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
        );
        println!("THIS IS THE GAME OF HI LO.\n");
        println!("YOU WILL HAVE 6 TRIES TO GUESS THE AMOUNT OF MONEY IN THE");
        println!("HI LO JACKPOT, WHICH IS BETWEEN 1 AND 100 DOLLARS.  IF YOU");
        println!("GUESS THE AMOUNT, YOU WIN ALL THE MONEY IN THE JACKPOT!");
        println!("THEN YOU GET ANOTHER CHANCE TO WIN MORE MONEY.  HOWEVER,");
        println!("IF YOU DO NOT GUESS THE AMOUNT, THE GAME ENDS.\n");
    
        let mut total: u32 = 0;
        loop {
            let jackpot_amount = rand::thread_rng().gen_range(1..101); // generates a random number between 1 and 100
            for i in 0..6 {
                println!("YOUR GUESS?");
    
                let mut guess = String::new();
    
                io::stdin()
                    .read_line(&mut guess)
                    .expect("Failed to read the line");
    
                // this converts the input string into unsigned 32bit number and if the input entered is not a number
                // it will again prompt the user to enter the guess number
                let guess: u32 = match guess.trim().parse() {
                    Ok(num) => num,
                    Err(_) => {
                        println!("PLEASE ENTER A NUMBER VALUE.\n");
                        continue;
                    }
                };
    
                // compare it with the jackpot amount
                if guess == jackpot_amount {
                    println!("\nGOT IT!!!!!!!!!!   YOU WIN {} DOLLARS.", jackpot_amount);
                    total += jackpot_amount;
                    println!("YOUR TOTAL WINNINGS ARE NOW {} DOLLARS.\n", total);
                    break;
                } else if guess < jackpot_amount {
                    println!("YOUR GUESS IS TOO LOW.\n");
                } else {
                    println!("YOUR GUESS IS TOO HIGH.\n");
                }
    
                // if 6 tries are over make total jackpot amount to zero
                if i == 5 {
                    total = 0;
                    println!(
                        "YOU BLEW IT...TOO BAD...THE NUMBER WAS {}\n",
                        jackpot_amount
                    );
                }
            }
            println!("PLAY AGAIN (YES OR NO)?");
            let mut tocontinue = String::new();
            io::stdin()
                .read_line(&mut tocontinue)
                .expect("Error Getting your input");
            let tocontinue = tocontinue.trim().to_ascii_uppercase();
            if tocontinue.eq("YES") {
                println!("\n");
                continue;
            } else {
                println!("\nSO LONG.  HOPE YOU ENJOYED YOURSELF!!!\n");
                break;
            }
        }
    }
    
    
    ================================================
    FILE: 47_Hi-Lo/vbnet/HiLo.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "HiLo", "HiLo.vbproj", "{B93E0994-AEC4-4FC9-93C2-FC708368B32F}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{B93E0994-AEC4-4FC9-93C2-FC708368B32F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{B93E0994-AEC4-4FC9-93C2-FC708368B32F}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{B93E0994-AEC4-4FC9-93C2-FC708368B32F}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{B93E0994-AEC4-4FC9-93C2-FC708368B32F}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 47_Hi-Lo/vbnet/HiLo.vbproj
    ================================================
    
      
        Exe
        HiLo
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 47_Hi-Lo/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 48_High_IQ/README.md
    ================================================
    ### High IQ
    
    This is a computerized version of an old European solitaire game of logic. The game starts with a pegboard shaped like a cross having pegs in every hole but the center. The object is to remove all 32 pegs, or as many as possible, by jumping into an empty hole, then removing the jumped peg.
    
    There are several different winning strategies for playing, and of course, each strategy can be played eight different ways on the board. Can you find a consistent winner?
    
    Charles Lund wrote this game while at The American School in The Hague, Netherlands.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=86)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=101)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 48_High_IQ/csharp/HighIQ.csproj
    ================================================
    
      
        Exe
        net6.0
        10
        enable
        enable
      
    
    
    
    ================================================
    FILE: 48_High_IQ/csharp/HighIQ.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HighIQ", "HighIQ.csproj", "{B334E0AD-4A84-4F93-85FB-E6370BC8B71A}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{B334E0AD-4A84-4F93-85FB-E6370BC8B71A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{B334E0AD-4A84-4F93-85FB-E6370BC8B71A}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{B334E0AD-4A84-4F93-85FB-E6370BC8B71A}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{B334E0AD-4A84-4F93-85FB-E6370BC8B71A}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 48_High_IQ/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 48_High_IQ/d/.gitignore
    ================================================
    *.exe
    *.obj
    
    
    ================================================
    FILE: 48_High_IQ/d/README.md
    ================================================
    Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Converted to [D](https://dlang.org/) by [Bastiaan Veelo](https://github.com/veelo).
    
    ## Running the code
    
    Assuming the reference [dmd](https://dlang.org/download.html#dmd) compiler:
    ```shell
    dmd -dip1000 -run highiq.d
    ```
    
    [Other compilers](https://dlang.org/download.html) also exist.
    
    
    ## Discussion
    
    The original BASIC game code made use of calculus and clever choises of field IDs to determine the validity of moves.
    This is the original layout of IDs over the board:
    
    ```
              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
    ```
    
    This seems not very logical, because, wouldn't it make much more sense to let columns increase with 1 and rows increase
    with 10, so you'd get a consistent coordinate system? It seems that the original author's first step in validating
    moves was to check that moves jumped from one field over another one onto the next. He did this by making sure that
    adjacent IDs alter between even and odd horizontally *and* vertically. So a valid move was always from an even ID to an
    even ID *or* from an odd ID to an odd ID. So one of the checks that the BASIC code made was that the sum of both IDs
    was even. This is of course not a sufficient test, because moves that jump over three fields are illegal. Therefore the
    IDs seem to have been carefully laid oud so that the IDs increase with 1 horizontally, and 9 vertically, everywhere. So
    the only valid difference between IDs for a horizontal move was always 2, and the only valid difference for a vertical
    move was always 18.
    
    Fact of the matter is, however, that checking for difference is sufficient and the even sum rule is superfluous, so
    there is no need for the peculiar distribution of field IDs. Therefore I have chosen the following more logical
    distribution:
    
    ```
              13   14   15
    
              23   24   25
    
    31   32   33   34   35   36   37
    
    41   42   43   44   45   46   47
    
    51   52   53   54   55   56   57
    
              63   64   65
    
              73   74   75
    ```
    
    As a consequence, the implementation of the game code has become much simpler; Not alone due to one less check, but due
    to the fact that conversions between IDs and board coordinates have become unnecessary and thus we can work with a single
    representation of the board state.
    
    This version makes a prettier print of the board than the BASIC original, with coordinates for every move, and explains
    illegal moves.
    
    
    ## Demo
    
    ```
                          H-I-Q
    (After Creative Computing  Morristown, New Jersey)
    
    
    Fields are identified by 2-digit numbers, each
    between 1 and 7. Example: the middle field is 44,
    the bottom middle is 74.
    
          _1  _2  _3  _4  _5  _6  _7
                ┌───┬───┬───┐
     1_         │ ■ │ ■ │ ■ │
                ├───┼───┼───┤
     2_         │ ■ │ ■ │ ■ │
        ┌───┬───┼───┼───┼───┼───┬───┐
     3_ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │
        ├───┼───┼───┼───┼───┼───┼───┤
     4_ │ ■ │ ■ │ ■ │   │ ■ │ ■ │ ■ │
        ├───┼───┼───┼───┼───┼───┼───┤
     5_ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │
        └───┴───┼───┼───┼───┼───┴───┘
     6_         │ ■ │ ■ │ ■ │
                ├───┼───┼───┤
     7_         │ ■ │ ■ │ ■ │
                └───┴───┴───┘
    
    Move which peg? 23
    The peg at 23 has nowhere to go. Try again.
    
    Move which peg? 24
    To where? 34
    Field 34 is occupied. Try again.
    To where? 54
    Field 54 is occupied. Try again.
    To where? 44
    
          _1  _2  _3  _4  _5  _6  _7
                ┌───┬───┬───┐
     1_         │ ■ │ ■ │ ■ │
                ├───┼───┼───┤
     2_         │ ■ │   │ ■ │
        ┌───┬───┼───┼───┼───┼───┬───┐
     3_ │ ■ │ ■ │ ■ │   │ ■ │ ■ │ ■ │
        ├───┼───┼───┼───┼───┼───┼───┤
     4_ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │
        ├───┼───┼───┼───┼───┼───┼───┤
     5_ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │
        └───┴───┼───┼───┼───┼───┴───┘
     6_         │ ■ │ ■ │ ■ │
                ├───┼───┼───┤
     7_         │ ■ │ ■ │ ■ │
                └───┴───┴───┘
    
    Move which peg? 14
    The peg at 14 has nowhere to go. Try again.
    
    Move which peg? 24
    There is no peg at 24. Try again.
    
    Move which peg? 44
    The peg at 44 has nowhere to go. Try again.
    
    Move which peg? 32
    To where? 22
    Field 22 is ouside the board. Try again.
    To where? 33
    Field 33 is occupied. Try again.
    To where? 34
    
          _1  _2  _3  _4  _5  _6  _7
                ┌───┬───┬───┐
     1_         │ ■ │ ■ │ ■ │
                ├───┼───┼───┤
     2_         │ ■ │   │ ■ │
        ┌───┬───┼───┼───┼───┼───┬───┐
     3_ │ ■ │   │   │ ■ │ ■ │ ■ │ ■ │
        ├───┼───┼───┼───┼───┼───┼───┤
     4_ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │
        ├───┼───┼───┼───┼───┼───┼───┤
     5_ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │
        └───┴───┼───┼───┼───┼───┴───┘
     6_         │ ■ │ ■ │ ■ │
                ├───┼───┼───┤
     7_         │ ■ │ ■ │ ■ │
                └───┴───┴───┘
    
    Move which peg? 44
    To where? 33
    You cannot move diagonally. Try again.
    To where? 24
    
          _1  _2  _3  _4  _5  _6  _7
                ┌───┬───┬───┐
     1_         │ ■ │ ■ │ ■ │
                ├───┼───┼───┤
     2_         │ ■ │ ■ │ ■ │
        ┌───┬───┼───┼───┼───┼───┬───┐
     3_ │ ■ │   │   │   │ ■ │ ■ │ ■ │
        ├───┼───┼───┼───┼───┼───┼───┤
     4_ │ ■ │ ■ │ ■ │   │ ■ │ ■ │ ■ │
        ├───┼───┼───┼───┼───┼───┼───┤
     5_ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │
        └───┴───┼───┼───┼───┼───┴───┘
     6_         │ ■ │ ■ │ ■ │
                ├───┼───┼───┤
     7_         │ ■ │ ■ │ ■ │
                └───┴───┴───┘
    
    Move which peg? 36
    To where? 33
    You can't jump that far. Try again.
    To where? 35
    Field 35 is occupied. Try again.
    To where? 34
    
          _1  _2  _3  _4  _5  _6  _7
                ┌───┬───┬───┐
     1_         │ ■ │ ■ │ ■ │
                ├───┼───┼───┤
     2_         │ ■ │ ■ │ ■ │
        ┌───┬───┼───┼───┼───┼───┬───┐
     3_ │ ■ │   │   │ ■ │   │   │ ■ │
        ├───┼───┼───┼───┼───┼───┼───┤
     4_ │ ■ │ ■ │ ■ │   │ ■ │ ■ │ ■ │
        ├───┼───┼───┼───┼───┼───┼───┤
     5_ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │ ■ │
        └───┴───┼───┼───┼───┼───┴───┘
     6_         │ ■ │ ■ │ ■ │
                ├───┼───┼───┤
     7_         │ ■ │ ■ │ ■ │
                └───┴───┴───┘
    
    Move which peg? 46
    To where? 36
    You need to jump over another peg. Try again.
    To where? down
    Field 00 is ouside the board. Try again.
    To where?
    ```
    
    
    ================================================
    FILE: 48_High_IQ/d/highiq.d
    ================================================
    @safe: // Make @safe the default for this file, enforcing memory-safety.
    import std;
    
    void main()
    {
        enum width = 50;
        writeln(center("H-I-Q", width));
        writeln(center("(After Creative Computing  Morristown, New Jersey)\n\n", width));
        writeln(wrap("Fields are identified by 2-digit numbers, each between 1 and 7. " ~
                     "Example: the middle field is 44, the bottom middle is 74.", width));
    
        Board board;
    
        while (true)
        {
            while (!board.isGameOver)
            {
                writeln(board); // Calls board.toString().
                board.makeMove;
            }
            writeln(board, "\nThe game is over.\nYou had ", board.numPegs, " pieces remaining.");
            if (board.numPegs == 1)
                writeln("Bravo!  You made a perfect score!\n",
                        "Make a screen dump as a record of your accomplishment!\n");
            write("Play again? (Yes or No) ");
            if (readString.toLower != "yes")
                break;
            writeln; writeln;
            board = Board.init;
        }
        writeln("\nSo long for now.\n");
    }
    
    /// Representation of the game board with pegs.
    struct Board
    {
        enum {outside, taken, empty};
        int[8][8] state = [
            1: [                    3: taken, 4: taken, 5: taken],
            2: [                    3: taken, 4: taken, 5: taken],
            3: [1: taken, 2: taken, 3: taken, 4: taken, 5: taken, 6: taken, 7: taken],
            4: [1: taken, 2: taken, 3: taken, 4: empty, 5: taken, 6: taken, 7: taken],
            5: [1: taken, 2: taken, 3: taken, 4: taken, 5: taken, 6: taken, 7: taken],
            6: [                    3: taken, 4: taken, 5: taken],
            7: [                    3: taken, 4: taken, 5: taken]
        ]; // Row 0 and column 0 are unused. Default is 0 (outside).
    
        /// Returns a string representing the board and its current state.
        string toString() const
        {
            dchar[][] lines = [("      _1  _2  _3  _4  _5  _6  _7 ").to!(dchar[]),
                               ("            ┌───┬───┬───┐        ").to!(dchar[]),
                               (" 1_         │   │   │   │        ").to!(dchar[]),
                               ("            ├───┼───┼───┤        ").to!(dchar[]),
                               (" 2_         │   │   │   │        ").to!(dchar[]),
                               ("    ┌───┬───┼───┼───┼───┼───┬───┐").to!(dchar[]),
                               (" 3_ │   │   │   │   │   │   │   │").to!(dchar[]),
                               ("    ├───┼───┼───┼───┼───┼───┼───┤").to!(dchar[]),
                               (" 4_ │   │   │   │   │   │   │   │").to!(dchar[]),
                               ("    ├───┼───┼───┼───┼───┼───┼───┤").to!(dchar[]),
                               (" 5_ │   │   │   │   │   │   │   │").to!(dchar[]),
                               ("    └───┴───┼───┼───┼───┼───┴───┘").to!(dchar[]),
                               (" 6_         │   │   │   │        ").to!(dchar[]),
                               ("            ├───┼───┼───┤        ").to!(dchar[]),
                               (" 7_         │   │   │   │        ").to!(dchar[]),
                               ("            └───┴───┴───┘        ").to!(dchar[])];
            foreach (y, row; state)
                foreach (x, field; row)
                    if (field == taken)
                        lines[y * 2][x * 4 + 2] = '■';
            return lines.join("\n").to!string;
        }
    
        /// Tests for possible moves.
        bool isGameOver() const
        {
            foreach (r, row; state)
                foreach (c, field; row)
                    if (field == taken && canMoveFrom(r, c))
                        return false;
            return true;
        }
    
        bool canMoveFrom(int row, int col) const
        {
            if (row >= 3 && state[row - 2][col] == empty)   // Up
                return state[row - 1][col] == taken;
            if (row <= 5 && state[row + 2][col] == empty)   // Down
                return state[row + 1][col] == taken;
            if (col >= 3 && state[row][col - 2] == empty)   // Left
                return state[row][col - 1] == taken;
            if (col <= 5 && state[row][col + 2] == empty)   // Right
                return state[row][col + 1] == taken;
            return false;
        }
    
        /// Asks for input, validates the move and updates the board.
        void makeMove()
        {
            bool isOutside(int row, int col)
            {
                if (row < 1 || row > 7 ||
                    col < 1 || col > 7 ||
                    state[row][col] == outside)
                {
                    writeln("Field ", row, col, " is ouside the board. Try again.");
                    return true;
                }
                return false;
            }
    
            while (true)
            {
                auto from = (){
                    while (true)
                    {
                        write("\nMove which peg? ");
                        int field = readInt;
                        int row = field / 10, col = field % 10;
                        if (isOutside(row, col))
                            continue;
                        if (state[row][col] != taken)
                        {
                            writeln("There is no peg at ", field, ". Try again.");
                            continue;
                        }
                        if (!canMoveFrom(row, col))
                        {
                            writeln("The peg at ", field, " has nowhere to go. Try again.");
                            continue;
                        }
                        return tuple!("row", "col")(row, col);
                    }
                }();
                auto to = (){
                    while (true)
                    {
                        write("To where? ");
                        int field = readInt;
                        int row = field / 10, col = field % 10;
                        if (isOutside(row, col))
                            continue;
                        if (state[row][col] == taken)
                        {
                            writeln("Field ", field, " is occupied. Try again.");
                            continue;
                        }
                        if (row != from.row && col != from.col)
                        {
                            writeln("You cannot move diagonally. Try again.");
                            continue;
                        }
                        if (row == from.row && col == from.col)
                        {
                            writeln("You aren't going anywhere. Try again.");
                            continue;
                        }
                        if (abs(row - from.row) + abs(col - from.col) > 2)
                        {
                            writeln("You can't jump that far. Try again.");
                            continue;
                        }
                        if (abs(row - from.row) + abs(col - from.col) < 2 ||
                            state[(row + from.row) / 2][(col + from.col) / 2] != taken)
                        {
                            writeln("You need to jump over another peg. Try again.");
                            continue;
                        }
                        return tuple!("row", "col")(row, col);
                    }
                }();
                // The move is legal. Update the board state.
                state[from.row][from.col] = empty;
                state[  to.row][  to.col] = taken;
                state[(from.row + to.row) / 2][(from.col + to.col) / 2] = empty;
                writeln;
                break;
            }
        }
    
        /// Returns the number of remaining pegs on the board.
        int numPegs() const
        {
            int num = 0;
            foreach (row; state)
                foreach (field; row)
                    if (field == taken)
                        num++;
            return num;
        }
    }
    
    /// Reads an integer from standard input.
    int readInt() nothrow
    {
        try
            return readString.to!int;
        catch (Exception)   // Not an integer.
            return 0;
    }
    
    /// Reads a string from standard input.
    string readString() nothrow
    {
        try
            return trustedReadln.strip;
        catch (Exception)   // readln throws on I/O and Unicode errors, which we handle here.
            return "";
    }
    
    /** An @trusted wrapper around readln.
     *
     * This is the only function that formally requires manual review for memory-safety.
     * [Arguably readln should be safe already](https://forum.dlang.org/post/rab398$1up$1@digitalmars.com)
     * which would remove the need to have any @trusted code in this program.
     */
    string trustedReadln() @trusted
    {
        return readln;
    }
    
    version (Windows)
    {
        // Make the Windows console do a better job at printing UTF-8 strings,
        // and restore the default upon termination.
    
        import core.sys.windows.windows;
    
        shared static this() @trusted
        {
            SetConsoleOutputCP(CP_UTF8);
        }
    
        shared static ~this() @trusted
        {
            SetConsoleOutputCP(GetACP);
        }
    }
    
    
    ================================================
    FILE: 48_High_IQ/highiq.bas
    ================================================
    1 PRINT TAB(33);"H-I-Q"
    2 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    3 PRINT:PRINT:PRINT
    4 DIM B(70),T(9,9)
    5 PRINT "HERE IS THE BOARD:": PRINT
    6 PRINT "          !    !    !"
    7 PRINT "         13   14   15": PRINT
    8 PRINT "          !    !    !"
    9 PRINT "         22   23   24": PRINT
    10 PRINT "!    !    !    !    !    !    !"
    11 PRINT "29   30   31   32   33   34   35": PRINT
    12 PRINT "!    !    !    !    !    !    !"
    13 PRINT "38   39   40   41   42   43   44": PRINT
    14 PRINT "!    !    !    !    !    !    !"
    15 PRINT "47   48   49   50   51   52   53": PRINT
    16 PRINT "          !    !    !"
    17 PRINT "         58   59   60": PRINT
    18 PRINT "          !    !    !"
    19 PRINT "         67   68   69": PRINT
    20 PRINT "TO SAVE TYPING TIME, A COMPRESSED VERSION OF THE GAME BOARD"
    22 PRINT "WILL BE USED DURING PLAY.  REFER TO THE ABOVE ONE FOR PEG"
    24 PRINT "NUMBERS.  OK, LET'S BEGIN."
    28 REM *** SET UP BOARD
    29 FOR R=1 TO 9
    30 FOR C=1 TO 9
    31 IF (R-4)*(R-5)*(R-6)=0 THEN 40
    32 IF (C-4)*(C-5)*(C-6)=0 THEN 40
    35 T(R,C)=-5
    36 GOTO 50
    40 IF (R-1)*(C-1)*(R-9)*(C-9)=0 THEN 35
    42 T(R,C)=5
    50 NEXT C
    60 NEXT R
    65 T(5,5)=0: GOSUB 500
    70 REM *** INPUT MOVE AND CHECK ON LEGALITY
    75 FOR W=1 TO 33
    77 READ M
    79 DATA 13,14,15,22,23,24,29,30,31,32,33,34,35,38,39,40,41
    81 DATA 42,43,44,47,48,49,50,51,52,53,58,59,60,67,68,69
    83 B(M)=-7: NEXT W
    86 B(41)=-3
    100 INPUT "MOVE WHICH PIECE";Z
    110 IF B(Z)=-7 THEN 140
    120 PRINT "ILLEGAL MOVE, TRY AGAIN...": GOTO 100
    140 INPUT "TO WHERE";P
    150 IF B(P)=0 THEN 120
    153 IF B(P)=-7 THEN 120
    156 IF Z=P THEN 100
    160 IF ((Z+P)/2)=INT((Z+P)/2) THEN 180
    170 GOTO 120
    180 IF (ABS(Z-P)-2)*(ABS(Z-P)-18)<>0 THEN 120
    190 GOSUB 1000
    200 GOSUB 500
    210 GOSUB 1500
    220 GOTO 100
    500 REM *** PRINT BOARD
    510 FOR X=1 TO 9
    520 FOR Y=1 TO 9
    525 IF (X-1)*(X-9)*(Y-1)*(Y-9)=0 THEN 550
    530 IF (X-4)*(X-5)*(X-6)=0 THEN 570
    540 IF (Y-4)*(Y-5)*(Y-6)=0 THEN 570
    550 REM
    560 GOTO 610
    570 IF T(X,Y)<>5 THEN 600
    580 PRINT TAB(Y*2);"!";
    590 GOTO 610
    600 PRINT TAB(Y*2);"O";
    610 REM
    615 NEXT Y
    620 PRINT
    630 NEXT X
    640 RETURN
    1000 REM *** UPDATE BOARD
    1005 C=1: FOR X=1 TO 9
    1020 FOR Y=1 TO 9
    1030 IF C<>Z THEN 1220
    1040 IF C+2<>P THEN 1080
    1045 IF T(X,Y+1)=0 THEN 120
    1050 T(X,Y+2)=5
    1060 T(X,Y+1)=0: B(C+1)=-3
    1070 GOTO 1200
    1080 IF C+18<>P THEN 1130
    1085 IF T(X+1,Y)=0 THEN 120
    1090 T(X+2,Y)=5: T(X+1,Y)=0: B(C+9)=-3
    1120 GOTO 1200
    1130 IF C-2<>P THEN 1170
    1135 IF T(X,Y-1)=0 THEN 120
    1140 T(X,Y-2)=5: T(X,Y-1)=0: B(C-1)=-3
    1160 GOTO 1200
    1170 IF C-18<>P THEN 1220
    1175 IF T(X-1,Y)=0 THEN 120
    1180 T(X-2,Y)=5: T(X-1,Y)=0: B(C-9)=-3
    1200 B(Z)=-3: B(P)=-7
    1210 T(X,Y)=0: GOTO 1240
    1220 C=C+1
    1225 NEXT Y
    1230 NEXT X
    1240 RETURN
    1500 REM*** CHECK IF GAME IS OVER
    1505 F=0
    1510 FOR R=2 TO 8
    1520 FOR C=2 TO 8
    1530 IF T(R,C)<>5 THEN 1580
    1535 F=F+1
    1540 FOR A=R-1 TO R+1
    1545 T=0
    1550 FOR B=C-1 TO C+1
    1560 T=T+T(A,B)
    1561 NEXT B
    1564 IF T<>10 THEN 1567
    1565 IF T(A,C)<>0 THEN 1630
    1567 NEXT A
    1568 FOR X=C-1 TO C+1
    1569 T=0
    1570 FOR Y=R-1 TO R+1
    1571 T=T+T(Y,X)
    1572 NEXT Y
    1573 IF T<>10 THEN 1575
    1574 IF T(R,X)<>0 THEN 1630
    1575 NEXT X
    1580 NEXT C
    1590 NEXT R
    1600 REM *** GAME IS OVER
    1605 PRINT "THE GAME IS OVER."
    1610 PRINT "YOU HAD";F;"PIECES REMAINING."
    1611 IF F<>1 THEN 1615
    1612 PRINT "BRAVO!  YOU MADE A PERFECT SCORE!"
    1613 PRINT "SAVE THIS PAPER AS A RECORD OF YOUR ACCOMPLISHMENT!"
    1615 PRINT: INPUT "PLAY AGAIN (YES OR NO)";A$
    1617 IF A$="NO" THEN 2000
    1618 RESTORE: GOTO 28
    1620 STOP
    1630 RETURN
    2000 PRINT: PRINT "SO LONG FOR NOW.": PRINT
    2010 END
    
    
    ================================================
    FILE: 48_High_IQ/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 48_High_IQ/java/src/HighIQ.java
    ================================================
    import java.io.PrintStream;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Scanner;
    
    /**
     * Game of HighIQ
     * 

    * Based on the Basic Game of HighIQ Here: * https://github.com/coding-horror/basic-computer-games/blob/main/48_High_IQ/highiq.bas * * No additional functionality has been added */ public class HighIQ { //Game board, as a map of position numbers to their values private final Map board; //Output stream private final PrintStream out; //Input scanner to use private final Scanner scanner; public HighIQ(Scanner scanner) { out = System.out; this.scanner = scanner; board = new HashMap<>(); //Set of all locations to put initial pegs on int[] locations = new int[]{ 13, 14, 15, 22, 23, 24, 29, 30, 31, 32, 33, 34, 35, 38, 39, 40, 42, 43, 44, 47, 48, 49, 50, 51, 52, 53, 58, 59, 60, 67, 68, 69 }; for (int i : locations) { board.put(i, true); } board.put(41, false); } /** * Plays the actual game, from start to finish. */ public void play() { do { printBoard(); while (!move()) { out.println("ILLEGAL MOVE, TRY AGAIN..."); } } while (!isGameFinished()); int pegCount = 0; for (Integer key : board.keySet()) { if (board.getOrDefault(key, false)) { pegCount++; } } out.println("YOU HAD " + pegCount + " PEGS REMAINING"); if (pegCount == 1) { out.println("BRAVO! YOU MADE A PERFECT SCORE!"); out.println("SAVE THIS PAPER AS A RECORD OF YOUR ACCOMPLISHMENT!"); } } /** * Makes an individual move * @return True if the move was valid, false if the user made an error and the move is invalid */ public boolean move() { out.println("MOVE WHICH PIECE"); int from = scanner.nextInt(); //using the getOrDefault, which will make the statement false if it is an invalid position if (!board.getOrDefault(from, false)) { return false; } out.println("TO WHERE"); int to = scanner.nextInt(); if (board.getOrDefault(to, true)) { return false; } //Do nothing if they are the same if (from == to) { return true; } //using the difference to check if the relative locations are valid int difference = Math.abs(to - from); if (difference != 2 && difference != 18) { return false; } //check if there is a peg between from and to if (!board.getOrDefault((to + from) / 2, false)) { return false; } //Actually move board.put(from,false); board.put(to,true); board.put((from + to) / 2, false); return true; } /** * Checks if the game is finished * @return True if there are no more moves, False otherwise */ public boolean isGameFinished() { for (Integer key : board.keySet()) { if (board.get(key)) { //Spacing is either 1 or 9 //Looking to the right and down from every point, checking for both directions of movement for (int space : new int[]{1, 9}) { Boolean nextToPeg = board.getOrDefault(key + space, false); Boolean hasMovableSpace = !board.getOrDefault(key - space, true) || !board.getOrDefault(key + space * 2, true); if (nextToPeg && hasMovableSpace) { return false; } } } } return true; } public void printBoard() { for (int i = 0; i < 7; i++) { for (int j = 11; j < 18; j++) { out.print(getChar(j + 9 * i)); } out.println(); } } private char getChar(int position) { Boolean value = board.get(position); if (value == null) { return ' '; } else if (value) { return '!'; } else { return 'O'; } } } ================================================ FILE: 48_High_IQ/java/src/HighIQGame.java ================================================ import java.util.Scanner; public class HighIQGame { public static void main(String[] args) { printInstructions(); Scanner scanner = new Scanner(System.in); do { new HighIQ(scanner).play(); System.out.println("PLAY AGAIN (YES OR NO)"); } while(scanner.nextLine().equalsIgnoreCase("yes")); } public static void printInstructions() { System.out.println("\t\t\t H-I-Q"); System.out.println("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println("HERE IS THE BOARD:"); System.out.println(" ! ! !"); System.out.println(" 13 14 15\n"); System.out.println(" ! ! !"); System.out.println(" 22 23 24\n"); System.out.println("! ! ! ! ! ! !"); System.out.println("29 30 31 32 33 34 35\n"); System.out.println("! ! ! ! ! ! !"); System.out.println("38 39 40 41 42 43 44\n"); System.out.println("! ! ! ! ! ! !"); System.out.println("47 48 49 50 51 52 53\n"); System.out.println(" ! ! !"); System.out.println(" 58 59 60\n"); System.out.println(" ! ! !"); System.out.println(" 67 68 69"); System.out.println("TO SAVE TYPING TIME, A COMPRESSED VERSION OF THE GAME BOARD"); System.out.println("WILL BE USED DURING PLAY. REFER TO THE ABOVE ONE FOR PEG"); System.out.println("NUMBERS. OK, LET'S BEGIN."); } } ================================================ FILE: 48_High_IQ/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 48_High_IQ/javascript/highiq.html ================================================ H-I-Q

    
    
    
    
    
    
    ================================================
    FILE: 48_High_IQ/javascript/highiq.js
    ================================================
    // H-I-Q
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    var b = [];
    var t = [];
    var m = [,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];
    var z;
    var p;
    
    //
    // Print board
    //
    function print_board()
    {
        for (x = 1; x <= 9; x++) {
            str = "";
            for (y = 1; y <= 9; y++) {
                if (x == 1 || x == 9 || y == 1 || y == 9)
                    continue;
                if (x == 4 || x == 5 || x == 6 || y == 4 || y == 5 || y == 6) {
                    while (str.length < y * 2)
                        str += " ";
                    if (t[x][y] == 5)
                        str += "!";
                    else
                        str += "O";
                }
            }
            print(str + "\n");
        }
    }
    
    //
    // Update board
    //
    function update_board()
    {
        c = 1;
        for (var x = 1; x <= 9; x++) {
            for (var y = 1; y <= 9; y++, c++) {
                if (c != z)
                    continue;
                if (c + 2 == p) {
                    if (t[x][y + 1] == 0)
                        return false;
                    t[x][y + 2] = 5;
                    t[x][y + 1] = 0;
                    b[c + 1] = -3;
                } else if (c + 18 == p) {
                    if (t[x + 1][y] == 0)
                        return false;
                    t[x + 2][y] = 5;
                    t[x + 1][y] = 0;
                    b[c + 9] = -3;
                } else if (c - 2 == p) {
                    if (t[x][y - 1] == 0)
                        return false;
                    t[x][y - 2] = 5;
                    t[x][y - 1] = 0;
                    b[c - 1] = -3;
                } else if (c - 18 == p) {
                    if (t[x - 1][y] == 0)
                        return false;
                    t[x - 2][y] = 5;
                    t[x - 1][y] = 0;
                    b[c - 9] = -3;
                } else {
                    continue;
                }
                b[z] = -3;
                b[p] = -7;
                t[x][y] = 0;
                return true;
            }
        }
    }
    
    //
    // Check for game over
    //
    // Rewritten because original subroutine was buggy
    //
    function check_game_over()
    {
        f = 0;
        for (r = 2; r <= 8; r++) {
            for (c = 2; c <= 8; c++) {
                if (t[r][c] != 5)
                    continue;
                f++;
                if (r > 3 && t[r - 1][c] == 5 && t[r - 2][c] == 0)
                    return false;
                if (c > 3 && t[r][c - 1] == 5 && t[r][c - 2] == 0)
                    return false;
                if (r < 7 && t[r + 1][c] == 5 && t[r + 2][c] == 0)
                    return false;
                if (c < 7 && t[r][c + 1] == 5 && t[r][c + 2] == 0)
                    return false;
            }
        }
        return true;
    }
    
    // Main program
    async function main()
    {
        print(tab(33) + "H-I-Q\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        for (r = 0; r <= 70; r++)
            b[r] = 0;
        print("HERE IS THE BOARD:\n");
        print("\n");
        print("          !    !    !\n");
        print("         13   14   15\n");
        print("\n");
        print("          !    !    !\n");
        print("         22   23   24\n");
        print("\n");
        print("!    !    !    !    !    !    !\n");
        print("29   30   31   32   33   34   35\n");
        print("\n");
        print("!    !    !    !    !    !    !\n");
        print("38   39   40   41   42   43   44\n");
        print("\n");
        print("!    !    !    !    !    !    !\n");
        print("47   48   49   50   51   52   53\n");
        print("\n");
        print("          !    !    !\n");
        print("         58   59   60\n");
        print("\n");
        print("          !    !    !\n");
        print("         67   68   69\n");
        print("\n");
        print("TO SAVE TYPING TIME, A COMPRESSED VERSION OF THE GAME BOARD\n");
        print("WILL BE USED DURING PLAY.  REFER TO THE ABOVE ONE FOR PEG\n");
        print("NUMBERS.  OK, LET'S BEGIN.\n");
        while (1) {
            // Set up board
            for (r = 1; r <= 9; r++) {
                t[r] = [];
                for (c = 1; c <= 9; c++) {
                    if (r == 4 || r == 5 || r == 6 || c == 4 || c == 5 || c == 6 && (r != 1 && c != 1 && r != 9 && c != 9)) {
                        t[r][c] = 5;
                    } else {
                        t[r][c] = -5;
                    }
                }
            }
            t[5][5] = 0;
            print_board();
            // Init secondary board
            for (w = 1; w <= 33; w++) {
                b[m[w]] = -7;
            }
            b[41] = -3;
            // Input move and check on legality
            do {
                while (1) {
                    print("MOVE WHICH PIECE");
                    z = parseInt(await input());
                    if (b[z] == -7) {
                        print("TO WHERE");
                        p = parseInt(await input());
                        if (p != z
                            && b[p] != 0
                            && b[p] != -7
                            && (z + p) % 2 == 0
                            && (Math.abs(z - p) - 2) * (Math.abs(z - p) - 18) == 0
                            && update_board())
                            break;
                    }
                    print("ILLEGAL MOVE, TRY AGAIN...\n");
                }
                print_board();
            } while (!check_game_over()) ;
            // Game is over
            print("THE GAME IS OVER.\n");
            print("YOU HAD " + f + " PIECES REMAINING.\n");
            if (f == 1) {
                print("BRAVO!  YOU MADE A PERFECT SCORE!\n");
                print("SAVE THIS PAPER AS A RECORD OF YOUR ACCOMPLISHMENT!\n");
            }
            print("\n");
            print("PLAY AGAIN (YES OR NO)");
            str = await input();
            if (str == "NO")
                break;
        }
        print("\n");
        print("SO LONG FOR NOW.\n");
        print("\n");
    }
    
    main();
    
    
    ================================================
    FILE: 48_High_IQ/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 48_High_IQ/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 48_High_IQ/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 48_High_IQ/python/High_IQ.py
    ================================================
    from typing import Dict
    
    
    def new_board() -> Dict[int, str]:
        """
        Using a dictionary in python to store the board,
        since we are not including all numbers within a given range.
        """
        return {
            13: "!",
            14: "!",
            15: "!",
            22: "!",
            23: "!",
            24: "!",
            29: "!",
            30: "!",
            31: "!",
            32: "!",
            33: "!",
            34: "!",
            35: "!",
            38: "!",
            39: "!",
            40: "!",
            42: "!",
            43: "!",
            44: "!",
            47: "!",
            48: "!",
            49: "!",
            50: "!",
            51: "!",
            52: "!",
            53: "!",
            58: "!",
            59: "!",
            60: "!",
            67: "!",
            68: "!",
            69: "!",
            41: "O",
        }
    
    
    def print_instructions() -> None:
        print(
            """
    HERE IS THE BOARD:
    
              !    !    !
             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
    
    TO SAVE TYPING TIME, A COMPRESSED VERSION OF THE GAME BOARD
    WILL BE USED DURING PLAY.  REFER TO THE ABOVE ONE FOR PEG
    NUMBERS.  OK, LET'S BEGIN.
        """
        )
    
    
    def print_board(board: Dict[int, str]) -> None:
        """Prints the boards using indexes in the passed parameter"""
        print(" " * 2 + board[13] + board[14] + board[15])
        print(" " * 2 + board[22] + board[23] + board[24])
        print(
            board[29]
            + board[30]
            + board[31]
            + board[32]
            + board[33]
            + board[34]
            + board[35]
        )
        print(
            board[38]
            + board[39]
            + board[40]
            + board[41]
            + board[42]
            + board[43]
            + board[44]
        )
        print(
            board[47]
            + board[48]
            + board[49]
            + board[50]
            + board[51]
            + board[52]
            + board[53]
        )
        print(" " * 2 + board[58] + board[59] + board[60])
        print(" " * 2 + board[67] + board[68] + board[69])
    
    
    def play_game() -> None:
        # Create new board
        board = new_board()
    
        # Main game loop
        while not is_game_finished(board):
            print_board(board)
            while not move(board):
                print("ILLEGAL MOVE! TRY AGAIN")
    
        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!")
            print("SAVE THIS PAPER AS A RECORD OF YOUR ACCOMPLISHMENT!")
    
    
    def move(board: Dict[int, str]) -> bool:
        """Queries the user to move. Returns false if the user puts in an invalid input or move, returns true if the move was successful"""
        start_input = input("MOVE WHICH PIECE? ")
    
        if not start_input.isdigit():
            return False
    
        start = int(start_input)
    
        if start not in board or board[start] != "!":
            return False
    
        end_input = input("TO WHERE? ")
    
        if not end_input.isdigit():
            return False
    
        end = int(end_input)
    
        if end not in board or board[end] != "O":
            return False
    
        difference = abs(start - end)
        center = int((end + start) / 2)
        if difference in [2, 18] and board[center] == "!":
            board[start] = "O"
            board[center] = "O"
            board[end] = "!"
            return True
        else:
            return False
    
    
    def main() -> None:
        print(" " * 33 + "H-I-Q")
        print(" " * 15 + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY")
        print_instructions()
        play_game()
    
    
    def is_game_finished(board) -> bool:
        """Check all locations and whether or not a move is possible at that location."""
        for pos in board.keys():
            if board[pos] == "!":
                for space in [1, 9]:
                    # Checks if the next location has a peg
                    next_to_peg = ((pos + space) in board) and board[pos + space] == "!"
                    # Checks both going forward (+ location) or backwards (-location)
                    has_movable_space = (
                        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
        return True
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 48_High_IQ/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    [Implementation](./High_IQ.py) by [Thomas Kwashnak](https://github.com/LittleTealeaf)
    
    
    ================================================
    FILE: 48_High_IQ/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 48_High_IQ/vbnet/HighIQ.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "HighIQ", "HighIQ.vbproj", "{18AA4FCA-2733-4BBC-B65F-68C37B8CFDAF}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{18AA4FCA-2733-4BBC-B65F-68C37B8CFDAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{18AA4FCA-2733-4BBC-B65F-68C37B8CFDAF}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{18AA4FCA-2733-4BBC-B65F-68C37B8CFDAF}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{18AA4FCA-2733-4BBC-B65F-68C37B8CFDAF}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 48_High_IQ/vbnet/HighIQ.vbproj
    ================================================
    
      
        Exe
        HighIQ
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 48_High_IQ/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 49_Hockey/README.md
    ================================================
    ### Hockey
    
    This is a simulation of a ice hockey game. The computer, in this case, moderates and referees the pay between two human opponents. Of course, one person could play both sides.
    
    The program asks for team names, player names, and even the name of the referee. Four types of shot are permitted and a shot may be aimed at one of four areas. You are also asked about passing. The game is very comprehensive with lots of action, face offs, blocks, passes, 4 on 2 situations, and so on. Unfortunately there are no penalties.
    
    The original author is Robert Puopolo; modifications by Steve North of Creative Computing.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=88)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=103)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### 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)
    
    
    ================================================
    FILE: 49_Hockey/csharp/Hockey.csproj
    ================================================
    
      
        Exe
        net6.0
        10
        enable
        enable
      
    
    
    
    ================================================
    FILE: 49_Hockey/csharp/Hockey.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Hockey", "Hockey.csproj", "{98E2914A-41D4-4931-B17F-4EAD3C98CFE1}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{98E2914A-41D4-4931-B17F-4EAD3C98CFE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{98E2914A-41D4-4931-B17F-4EAD3C98CFE1}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{98E2914A-41D4-4931-B17F-4EAD3C98CFE1}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{98E2914A-41D4-4931-B17F-4EAD3C98CFE1}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 49_Hockey/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 49_Hockey/hockey.bas
    ================================================
    2 PRINT TAB(33);"HOCKEY"
    4 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    6 PRINT:PRINT:PRINT
    10 REM ROBERT PUOPOLO ALG. 1 140 MCCOWAN 6/7/73 HOCKEY
    30 LET X=1
    40 PRINT:PRINT:PRINT
    50 PRINT "WOULD YOU LIKE THE INSTRUCTIONS";:INPUT C$
    55 PRINT
    60 IF C$="NO" THEN 90
    65 IF C$="YES" THEN 80
    70 PRINT "ANSWER YES OR NO!!":GOTO 50
    80 GOTO 1720
    90 DIM A$(7),B$(7),H(20),T(5),T1(5),T2(5),T3(5)
    100 PRINT "ENTER THE TWO TEAMS";:INPUT A$(7),B$(7)
    105 PRINT
    110 PRINT "ENTER THE NUMBER OF MINUTES IN A GAME";:INPUT T6
    115 PRINT
    120 IF T6<1 THEN 110:PRINT
    130 PRINT "WOULD THE " A$(7) " COACH ENTER HIS TEAM"
    135 PRINT
    140 FOR I=1 TO 6:PRINT "PLAYER"I;:INPUT A$(I):NEXT I:PRINT
    150 PRINT "WOULD THE " B$(7) " COACH DO THE SAME"
    155 PRINT
    160 FOR T=1 TO 6:PRINT "PLAYER"T;:INPUT B$(T):NEXT T:PRINT
    170 PRINT "INPUT THE REFEREE FOR THIS GAME";:INPUT R$
    180 PRINT:PRINT TAB(10);A$(7) " STARTING LINEUP"
    190 FOR T=1 TO 6:PRINT A$(T):NEXT T
    200 PRINT:PRINT TAB(10);B$(7)" STARTING LINEUP"
    210 FOR T=1 TO 6:PRINT B$(T):NEXT T:PRINT
    220 PRINT "WE'RE READY FOR TONIGHTS OPENING FACE-OFF."
    230 PRINT R$ " WILL DROP THE PUCK BETWEEN " A$(2) " AND " B$(2)
    240 FOR L=1 TO T6:IF L=1 THEN 260
    250 PRINT "AND WE'RE READY FOR THE FACE-OFF"
    260 C=INT(2*RND(X))+1:ON C GOTO 270,280
    270 PRINT A$(7) " HAS CONTROL OF THE PUCK":GOTO 290
    280 PRINT B$(7) " HAS CONTROL."
    290 PRINT "PASS";:INPUT P:FOR N=1 TO 3:H(N)=0:NEXT N
    300 IF P<0 THEN 290
    305 IF P>3 THEN 290
    310 FOR J=1 TO (P+2)
    320 H(J)=INT(5*RND(X))+1
    330 NEXT J:IF H(J-1)=H(J-2) THEN 310
    331 IF P+2<3 THEN 350
    335 IF H(J-1)=H(J-3) THEN 310
    340 IF H(J-2)=H(J-3) THEN 310
    350 IF P=0 THEN 360
    355 GOTO 490
    360 INPUT "SHOT";S:IF S<1 THEN 360
    365 IF S>4 THEN 360
    370 ON C GOTO 380,480
    380 PRINT A$(H(J-1));:G=H(J-1):G1=0:G2=0
    390 ON S GOTO 400,420,440,460
    400 PRINT " LET'S A BOOMER GO FROM THE RED LINE!!"
    410 Z=10:GOTO 890
    420 PRINT " FLIPS A WRISTSHOT DOWN THE ICE"
    440 PRINT " BACKHANDS ONE IN ON THE GOALTENDER"
    450 Z=25:GOTO 890
    460 PRINT " SNAPS A LONG FLIP SHOT"
    470 Z=17:GOTO 890
    480 PRINT B$(H(J-1));:G1=0:G2=0:G=H(J-1):GOTO 390
    490 ON C GOTO 500,640
    500 ON P GOTO 510,540,570
    510 PRINT A$(H(J-2)) " LEADS " A$(H(J-1)) " WITH A PERFECT PASS."
    520 PRINT A$(H(J-1)) " CUTTING IN!!!"
    530 G=H(J-1):G1=H(J-2):G2=0:Z1=3:GOTO 770
    540 PRINT A$(H(J-2)) " GIVES TO A STREAKING " A$(H(J-1))
    550 PRINT A$(H(J-3)) " COMES DOWN ON " B$(5) " AND " B$(4)
    560 G=H(J-3):G1=H(J-1):G2=H(J-2):Z1=2:GOTO 770
    570 PRINT "OH MY GOD!! A ' 4 ON 2 ' SITUATION"
    580 PRINT A$(H(J-3)) " LEADS " A$(H(J-2))
    590 PRINT A$(H(J-2)) " IS WHEEELING THROUGH CENTER."
    600 PRINT A$(H(J-2)) " GIVES AND GOES WITH " A$(H(J-1))
    610 PRINT "PRETTY PASSING!"
    620 PRINT A$(H(J-1)) " DROPS IT TO " A$(H(J-4))
    630 G=H(J-4):G1=H(J-1):G2=H(J-2):Z1=1:GOTO 770
    640 ON P GOTO 650,670,720
    650 PRINT B$(H(J-1)) " HITS " B$(H(J-2)) " FLYING DOWN THE LEFT SIDE"
    660 G=H(J-2):G1=H(J-1):G2=0:Z1=3:GOTO 770
    670 PRINT "IT'S A ' 3 ON 2 '!"
    680 PRINT "ONLY " A$(4) " AND " A$(5) " ARE BACK."
    690 PRINT B$(H(J-2)) " GIVES OFF TO " B$(H(J-1))
    700 PRINT B$(H(J-1)) " DROPS TO " B$(H(J-3))
    710 G=H(J-3):G1=H(J-1):G2=H(J-2):Z1=2:GOTO 770
    720 PRINT " A ' 3 ON 2 ' WITH A ' TRAILER '!"
    730 PRINT B$(H(J-4)) " GIVES TO " B$(H(J-2)) " WHO SHUFFLES IT OFF TO"
    740 PRINT B$(H(J-1)) " WHO FIRES A WING TO WING PASS TO "
    750 PRINT B$(H(J-3)) " AS HE CUTS IN ALONE!!"
    760 G=H(J-3):G1=H(J-1):G2=H(J-2):Z1=1:GOTO 770
    770 PRINT "SHOT";:INPUT S:IF S>4 THEN 770:IF S<1 THEN 770
    780 ON C GOTO 790,880
    790 PRINT A$(G);:ON S GOTO 800,820,840,860
    800 PRINT " LET'S A BIG SLAP SHOT GO!!"
    810 Z=4:Z=Z+Z1:GOTO 890
    820 PRINT " RIPS A WRIST SHOT OFF"
    830 Z=2:Z=Z+Z1:GOTO 890
    840 PRINT " GETS A BACKHAND OFF"
    850 Z=3:Z=Z+Z1:GOTO 890
    860 PRINT " SNAPS OFF A SNAP SHOT"
    870 Z=2:Z=Z+Z1:GOTO 890
    880 PRINT B$(G);:ON S GOTO 800,820,840,860
    890 PRINT "AREA";:INPUT A:IF A<1 THEN 890
    895 IF A>4 THEN 890
    900 ON C GOTO 910,920
    910 S2=S2+1:GOTO 930
    920 S3=S3+1
    930 A1=INT(4*RND(X))+1:IF A<>A1 THEN 1200
    940 H(20)=INT(100*RND(X))+1
    950 IF INT(H(20)/Z)=H(20)/Z THEN 1160
    960 ON C GOTO 970,980
    970 PRINT "GOAL " A$(7):H(9)=H(9)+1:GOTO 990
    980 PRINT "SCORE " B$(7):H(8)=H(8)+1
    990 FOR B1=1 TO 25:PRINT CHR$(7);:NEXT B1:PRINT
    1000 PRINT "SCORE: ";:IF H(8)>H(9) THEN 1020
    1010 PRINT A$(7)":";H(9),B$(7)":";H(8):GOTO 1030
    1020 PRINT B$(7)":";H(8),A$(7)":";H(9)
    1030 ON C GOTO 1040,1100
    1040 PRINT "GOAL SCORED BY: " A$(G):IF G1=0 THEN 1070
    1050 IF G2=0 THEN 1080
    1060 PRINT " ASSISTED BY: " A$(G1) " AND " A$(G2):GOTO 1090
    1070 PRINT " UNASSISTED.":GOTO 1090
    1080 PRINT " ASSISTED BY: " A$(G1)
    1090 T(G)=T(G)+1:T1(G1)=T1(G1)+1:T1(G2)=T1(G2)+1:GOTO 1540
    1100 PRINT "GOAL SCORED BY: " B$(G);
    1110 IF G1=0 THEN 1130
    1115 IF G2=0 THEN 1140
    1120 PRINT " ASSISTED BY: " B$(G1) " AND " B$(G2):GOTO 1150
    1130 PRINT " UNASSISTED":GOTO 1150
    1140 PRINT " ASSISTED BY: " B$(G1):GOTO 1150
    1150 T2(G)=T2(G)+1:T3(G1)=T3(G1)+1:T3(G2)=T3(G2)+1:GOTO 1540
    1160 A2=INT(100*RND(X))+1:IF INT(A2/4)=A2/4 THEN 1170
    1165 GOTO 1200
    1170 ON C GOTO 1180,1190
    1180 PRINT "SAVE " B$(6) " --  REBOUND":GOTO 940
    1190 PRINT "SAVE " A$(6) " --  FOLLOW UP":GOTO 940
    1200 S1=INT(6*RND(X))+1
    1210 ON C GOTO 1220,1380
    1220 ON S1 GOTO 1230,1260,1290,1300,1330,1350
    1230 PRINT "KICK SAVE AND A BEAUTY BY " B$(6)
    1240 PRINT "CLEARED OUT BY " B$(3)
    1250 GOTO 260
    1260 PRINT "WHAT A SPECTACULAR GLOVE SAVE BY " B$(6)
    1270 PRINT "AND " B$(6) " GOLFS IT INTO THE CROWD"
    1280 GOTO 1540
    1290 PRINT "SKATE SAVE ON A LOW STEAMER BY " B$(6):GOTO 260
    1300 PRINT "PAD SAVE BY " B$(6) " OFF THE STICK"
    1310 PRINT "OF "A$(G) " AND " B$(6) " COVERS UP"
    1320 GOTO 1540
    1330 PRINT "WHISTLES ONE OVER THE HEAD OF " B$(6)
    1340 GOTO 260
    1350 PRINT B$(6) " MAKES A FACE SAVE!! AND HE IS HURT"
    1360 PRINT "THE DEFENSEMAN " B$(5) " COVERS UP FOR HIM"
    1370 GOTO 1540
    1380 ON S1 GOTO 1390,1410,1440,1470,1490,1520
    1390 PRINT "STICK SAVE BY " A$(6)
    1400 PRINT "AND CLEARED OUT BY " A$(4):GOTO 260
    1410 PRINT "OH MY GOD!! " B$(G) " RATTLES ONE OFF THE POST"
    1420 PRINT "TO THE RIGHT OF " A$(6) " AND " A$(6) " COVERS ";
    1430 PRINT "ON THE LOOSE PUCK!":GOTO 1540
    1440 PRINT "SKATE SAVE BY " A$(6)
    1450 PRINT A$(6) " WHACKS THE LOOSE PUCK INTO THE STANDS"
    1460 GOTO 1540
    1470 PRINT "STICK SAVE BY " A$(6) " AND HE CLEARS IT OUT HIMSELF"
    1480 GOTO 260
    1490 PRINT "KICKED OUT BY " A$(6)
    1500 PRINT "AND IT REBOUNDS ALL THE WAY TO CENTER ICE"
    1510 GOTO 260
    1520 PRINT "GLOVE SAVE " A$(6) " AND HE HANGS ON"
    1530 GOTO 1540
    1540 NEXT L:FOR N=1 TO 30:PRINT CHR$(7);:NEXT N:PRINT "THAT'S THE SIREN"
    1550 PRINT:PRINT TAB(15);"FINAL SCORE:"
    1560 IF H(8)>H(9) THEN 1580
    1570 PRINT A$(7)":";H(9),B$(7)":";H(8):GOTO 1590
    1580 PRINT B$(7)":";H(8),A$(7)":";H(9)
    1590 PRINT: PRINT TAB(10);"SCORING SUMMARY":PRINT
    1600 PRINT TAB(25);A$(7)
    1610 PRINT TAB(5);"NAME";TAB(20);"GOALS";TAB(35);"ASSISTS"
    1620 PRINT TAB(5);"----";TAB(20);"-----";TAB(35);"-------"
    1630 FOR I=1 TO 5:PRINT TAB(5);A$(I);TAB(21);T(I);TAB(36);T1(I)
    1640 NEXT I:PRINT
    1650 PRINT TAB(25);B$(7)
    1660 PRINT TAB(5);"NAME";TAB(20);"GOALS";TAB(35);"ASSISTS"
    1670 PRINT TAB(5);"----";TAB(20);"-----";TAB(35);"-------"
    1680 FOR T=1 TO 5:PRINT TAB(5);B$(T);TAB(21);T2(T);TAB(36);T3(T)
    1690 NEXT T:PRINT
    1700 PRINT "SHOTS ON NET":PRINT A$(7)":";S2:PRINT B$(7)":";S3
    1710 END
    1720 PRINT: PRINT "THIS IS A SIMULATED HOCKEY GAME."
    1730 PRINT "QUESTION     RESPONSE"
    1740 PRINT "PASS         TYPE IN THE NUMBER OF PASSES YOU WOULD"
    1750 PRINT "             LIKE TO MAKE, FROM 0 TO 3."
    1760 PRINT "SHOT         TYPE THE NUMBER CORRESPONDING TO THE SHOT"
    1770 PRINT "             YOU WANT TO MAKE.  ENTER:"
    1780 PRINT "             1 FOR A SLAPSHOT"
    1790 PRINT "             2 FOR A WRISTSHOT"
    1800 PRINT "             3 FOR A BACKHAND"
    1810 PRINT "             4 FOR A SNAP SHOT"
    1820 PRINT "AREA         TYPE IN THE NUMBER CORRESPONDING TO"
    1830 PRINT "             THE AREA YOU ARE AIMING AT.  ENTER:"
    1840 PRINT "             1 FOR UPPER LEFT HAND CORNER"
    1850 PRINT "             2 FOR UPPER RIGHT HAND CORNER"
    1860 PRINT "             3 FOR LOWER LEFT HAND CORNER"
    1870 PRINT "             4 FOR LOWER RIGHT HAND CORNER"
    1880 PRINT
    1890 PRINT "AT THE START OF THE GAME, YOU WILL BE ASKED FOR THE NAMES"
    1900 PRINT "OF YOUR PLAYERS.  THEY ARE ENTERED IN THE ORDER: "
    1910 PRINT "LEFT WING, CENTER, RIGHT WING, LEFT DEFENSE,"
    1920 PRINT "RIGHT DEFENSE, GOALKEEPER.  ANY OTHER INPUT REQUIRED WILL"
    1930 PRINT "HAVE EXPLANATORY INSTRUCTIONS."
    1940 GOTO 90
    1950 END
    
    
    ================================================
    FILE: 49_Hockey/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 49_Hockey/javascript/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells)
    
    
    ================================================
    FILE: 49_Hockey/javascript/hockey.html
    ================================================
    
    
    HOCKEY
    
    
    
    
    
    
    
    
    
    
    ================================================
    FILE: 49_Hockey/javascript/hockey.js
    ================================================
    // HOCKEY
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    var as = [];
    var bs = [];
    var ha = [];
    var ta = [];
    var t1 = [];
    var t2 = [];
    var t3 = [];
    
    // Main program
    async function main()
    {
        print(tab(33) + "HOCKEY\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        // Robert Puopolo Alg. 1 140 McCowan 6/7/73 Hockey
        for (c = 0; c <= 20; c++)
            ha[c] = 0;
        for (c = 1; c <= 5; c++) {
            ta[c] = 0;
            t1[c] = 0;
            t2[c] = 0;
            t3[c] = 0;
        }
        x = 1;
        print("\n");
        print("\n");
        print("\n");
        while (1) {
            print("WOULD YOU LIKE THE INSTRUCTIONS");
            str = await input();
            print("\n");
            if (str == "YES" || str == "NO")
                break;
            print("ANSWER YES OR NO!!\n");
        }
        if (str == "YES") {
            print("\n");
            print("THIS IS A SIMULATED HOCKEY GAME.\n");
            print("QUESTION     RESPONSE\n");
            print("PASS         TYPE IN THE NUMBER OF PASSES YOU WOULD\n");
            print("             LIKE TO MAKE, FROM 0 TO 3.\n");
            print("SHOT         TYPE THE NUMBER CORRESPONDING TO THE SHOT\n");
            print("             YOU WANT TO MAKE.  ENTER:\n");
            print("             1 FOR A SLAPSHOT\n");
            print("             2 FOR A WRISTSHOT\n");
            print("             3 FOR A BACKHAND\n");
            print("             4 FOR A SNAP SHOT\n");
            print("AREA         TYPE IN THE NUMBER CORRESPONDING TO\n");
            print("             THE AREA YOU ARE AIMING AT.  ENTER:\n");
            print("             1 FOR UPPER LEFT HAND CORNER\n");
            print("             2 FOR UPPER RIGHT HAND CORNER\n");
            print("             3 FOR LOWER LEFT HAND CORNER\n");
            print("             4 FOR LOWER RIGHT HAND CORNER\n");
            print("\n");
            print("AT THE START OF THE GAME, YOU WILL BE ASKED FOR THE NAMES\n");
            print("OF YOUR PLAYERS.  THEY ARE ENTERED IN THE ORDER: \n");
            print("LEFT WING, CENTER, RIGHT WING, LEFT DEFENSE,\n");
            print("RIGHT DEFENSE, GOALKEEPER.  ANY OTHER INPUT REQUIRED WILL\n");
            print("HAVE EXPLANATORY INSTRUCTIONS.\n");
        }
        print("ENTER THE TWO TEAMS");
        str = await input();
        c = str.indexOf(",");
        as[7] = str.substr(0, c);
        bs[7] = str.substr(c + 1);
        print("\n");
        do {
            print("ENTER THE NUMBER OF MINUTES IN A GAME");
            t6 = parseInt(await input());
            print("\n");
        } while (t6 < 1) ;
        print("\n");
        print("WOULD THE " + as[7] + " COACH ENTER HIS TEAM\n");
        print("\n");
        for (i = 1; i <= 6; i++) {
            print("PLAYER " + i + " ");
            as[i] = await input();
        }
        print("\n");
        print("WOULD THE " + bs[7] + " COACH DO THE SAME\n");
        print("\n");
        for (t = 1; t <= 6; t++) {
            print("PLAYER " + t + " ");
            bs[t] = await input();
        }
        print("\n");
        print("INPUT THE REFEREE FOR THIS GAME");
        rs = await input();
        print("\n");
        print(tab(10) + as[7] + " STARTING LINEUP\n");
        for (t = 1; t <= 6; t++) {
            print(as[t] + "\n");
        }
        print("\n");
        print(tab(10) + bs[7] + " STARTING LINEUP\n");
        for (t = 1; t <= 6; t++) {
            print(bs[t] + "\n");
        }
        print("\n");
        print("WE'RE READY FOR TONIGHTS OPENING FACE-OFF.\n");
        print(rs + " WILL DROP THE PUCK BETWEEN " + as[2] + " AND " + bs[2] + "\n");
        s2 = 0;
        s3 = 0;
        for (l = 1; l <= t6; l++) {
            c = Math.floor(2 * Math.random()) + 1;
            if (c == 1)
                print(as[7] + " HAS CONTROL OF THE PUCK\n");
            else
                print(bs[7] + " HAS CONTROL.\n");
            do {
    
                print("PASS");
                p = parseInt(await input());
                for (n = 1; n <= 3; n++)
                    ha[n] = 0;
            } while (p < 0 || p > 3) ;
            do {
                for (j = 1; j <= p + 2; j++)
                    ha[j] = Math.floor(5 * Math.random()) + 1;
            } while (ha[j - 1] == ha[j - 2] || (p + 2 >= 3 && (ha[j - 1] == ha[j - 3] || ha[j - 2] == ha[j - 3]))) ;
            if (p == 0) {
                while (1) {
                    print("SHOT");
                    s = parseInt(await input());
                    if (s >= 1 && s <= 4)
                        break;
                }
                if (c == 1) {
                    print(as[ha[j - 1]]);
                    g = ha[j - 1];
                    g1 = 0;
                    g2 = 0;
                } else {
                    print(bs[ha[j - 1]]);
                    g2 = 0;
                    g2 = 0;
                    g = ha[j - 1];
                }
                switch (s) {
                    case 1:
                        print(" LET'S A BOOMER GO FROM THE RED LINE!!\n");
                        z = 10;
                        break;
                    case 2:
                        print(" FLIPS A WRISTSHOT DOWN THE ICE\n");
                        // Probable missing line 430 in original
                    case 3:
                        print(" BACKHANDS ONE IN ON THE GOALTENDER\n");
                        z = 25;
                        break;
                    case 4:
                        print(" SNAPS A LONG FLIP SHOT\n");
                        z = 17;
                        break;
                }
            } else {
                if (c == 1) {
                    switch (p) {
                        case 1:
                            print(as[ha[j - 2]] + " LEADS " + as[ha[j - 1]] + " WITH A PERFECT PASS.\n");
                            print(as[ha[j - 1]] + " CUTTING IN!!!\n");
                            g = ha[j - 1];
                            g1 = ha[j - 2];
                            g2 = 0;
                            z1 = 3;
                            break;
                        case 2:
                            print(as[ha[j - 2]] + " GIVES TO A STREAKING " + as[ha[j - 1]] + "\n");
                            print(as[ha[j - 3]] + " COMES DOWN ON " + bs[5] + " AND " + bs[4] + "\n");
                            g = ha[j - 3];
                            g1 = ha[j - 1];
                            g2 = ha[j - 2];
                            z1 = 2;
                            break;
                        case 3:
                            print("OH MY GOD!! A ' 4 ON 2 ' SITUATION\n");
                            print(as[ha[j - 3]] + " LEADS " + as[ha[j - 2]] + "\n");
                            print(as[ha[j - 2]] + " IS WHEELING THROUGH CENTER.\n");
                            print(as[ha[j - 2]] + " GIVES AND GOEST WITH " + as[ha[j - 1]] + "\n");
                            print("PRETTY PASSING!\n");
                            print(as[ha[j - 1]] + " DROPS IT TO " + as[ha[j - 4]] + "\n");
                            g = ha[j - 4];
                            g1 = ha[j - 1];
                            g2 = ha[j - 2];
                            z1 = 1;
                            break;
                    }
                } else {
                    switch (p) {
                        case 1:
                            print(bs[ha[j - 1]] + " HITS " + bs[ha[j - 2]] + " FLYING DOWN THE LEFT SIDE\n");
                            g = ha[j - 2];
                            g1 = ha[j - 1];
                            g2 = 0;
                            z1 = 3;
                            break;
                        case 2:
                            print("IT'S A ' 3 ON 2 '!\n");
                            print("ONLY " + as[4] + " AND " + as[5] + " ARE BACK.\n");
                            print(bs[ha[j - 2]] + " GIVES OFF TO " + bs[ha[j - 1]] + "\n");
                            print(bs[ha[j - 1]] + " DROPS TO " + bs[ha[j - 3]] + "\n");
                            g = ha[j - 3];
                            g1 = ha[j - 1];
                            g2 = ha[j - 2];
                            z1 = 2;
                            break;
                        case 3:
                            print(" A '3 ON 2 ' WITH A ' TRAILER '!\n");
                            print(bs[ha[j - 4]] + " GIVES TO " + bs[ha[j - 2]] + " WHO SHUFFLES IT OFF TO\n");
                            print(bs[ha[j - 1]] + " WHO FIRES A WING TO WING PASS TO \n");
                            print(bs[ha[j - 3]] + " AS HE CUTS IN ALONE!!\n");
                            g = ha[j - 3];
                            g1 = ha[j - 1];
                            g2 = ha[j - 2];
                            z1 = 1;
                            break;
                    }
                }
                do {
                    print("SHOT");
                    s = parseInt(await input());
                } while (s < 1 || s > 4) ;
                if (c == 1)
                    print(as[g]);
                else
                    print(bs[g]);
                switch (s) {
                    case 1:
                        print(" LET'S A BIG SLAP SHOT GO!!\n");
                        z = 4;
                        z += z1;
                        break;
                    case 2:
                        print(" RIPS A WRIST SHOT OFF\n");
                        z = 2;
                        z += z1;
                        break;
                    case 3:
                        print(" GETS A BACKHAND OFF\n");
                        z = 3;
                        z += z1;
                        break;
                    case 4:
                        print(" SNAPS OFF A SNAP SHOT\n");
                        z = 2;
                        z += z1;
                        break;
                }
            }
            do {
                print("AREA");
                a = parseInt(await input());
            } while (a < 1 || a > 4) ;
            if (c == 1)
                s2++;
            else
                s3++;
            a1 = Math.floor(4 * Math.random()) + 1;
            if (a == a1) {
                while (1) {
                    ha[20] = Math.floor(100 * Math.random()) + 1;
                    if (ha[20] % z != 0)
                        break;
                    a2 = Math.floor(100 * Math.random()) + 1;
                    if (a2 % 4 == 0) {
                        if (c == 1)
                            print("SAVE " + bs[6] + " --  REBOUND\n");
                        else
                            print("SAVE " + as[6] + " --  FOLLOW up\n");
                        continue;
                    } else {
                        a1 = a + 1;  // So a != a1
                    }
                }
                if (ha[20] % z != 0) {
                    if (c == 1) {
                        print("GOAL " + as[7] + "\n");
                        ha[9]++;
                    } else {
                        print("SCORE " + bs[7] + "\n");
                        ha[8]++;
                    }
                    // Bells in origninal
                    print("\n");
                    print("SCORE: ");
                    if (ha[8] <= ha[9]) {
                        print(as[7] + ": " + ha[9] + "\t" + bs[7] + ": " + ha[8] + "\n");
                    } else {
                        print(bs[7] + ": " + ha[8] + "\t" + as[7] + ": " + ha[9] + "\n");
                    }
                    if (c == 1) {
                        print("GOAL SCORED BY: " + as[g] + "\n");
                        if (g1 != 0) {
                            if (g2 != 0) {
                                print(" ASSISTED BY: " + as[g1] + " AND " + as[g2] + "\n");
                            } else {
                                print(" ASSISTED BY: " + as[g1] + "\n");
                            }
                        } else {
                            print(" UNASSISTED.\n");
                        }
                        ta[g]++;
                        t1[g1]++;
                        t1[g2]++;
                        // 1540
                    } else {
                        print("GOAL SCORED BY: " + bs[g] + "\n");
                        if (g1 != 0) {
                            if (g2 != 0) {
                                print(" ASSISTED BY: " + bs[g1] + " AND " + bs[g2] + "\n");
                            } else {
                                print(" ASSISTED BY: " + bs[g1] + "\n");
                            }
                        } else {
                            print(" UNASSISTED.\n");
                        }
                        t2[g]++;
                        t3[g1]++;
                        t3[g2]++;
                        // 1540
                    }
                }
            }
            if (a != a1) {
                s1 = Math.floor(6 * Math.random()) + 1;
                if (c == 1) {
                    switch (s1) {
                        case 1:
                            print("KICK SAVE AND A BEAUTY BY " + bs[6] + "\n");
                            print("CLEARED OUT BY " + bs[3] + "\n");
                            l--;
                            continue;
                        case 2:
                            print("WHAT A SPECTACULAR GLOVE SAVE BY " + bs[6] + "\n");
                            print("AND " + bs[6] + " GOLFS IT INTO THE CROWD\n");
                            break;
                        case 3:
                            print("SKATE SAVE ON A LOW STEAMER BY " + bs[6] + "\n");
                            l--;
                            continue;
                        case 4:
                            print("PAD SAVE BY " + bs[6] + " OFF THE STICK\n");
                            print("OF " + as[g] + " AND " + bs[6] + " COVERS UP\n");
                            break;
                        case 5:
                            print("WHISTLES ONE OVER THE HEAD OF " + bs[6] + "\n");
                            l--;
                            continue;
                        case 6:
                            print(bs[6] + " MAKES A FACE SAVE!! AND HE IS HURT\n");
                            print("THE DEFENSEMAN " + bs[5] + " COVERS UP FOR HIM\n");
                            break;
                    }
                } else {
                    switch (s1) {
                        case 1:
                            print("STICK SAVE BY " + as[6] +"\n");
                            print("AND CLEARED OUT BY " + as[4] + "\n");
                            l--;
                            continue;
                        case 2:
                            print("OH MY GOD!! " + bs[g] + " RATTLES ONE OFF THE POST\n");
                            print("TO THE RIGHT OF " + as[6] + " AND " + as[6] + " COVERS ");
                            print("ON THE LOOSE PUCK!\n");
                            break;
                        case 3:
                            print("SKATE SAVE BY " + as[6] + "\n");
                            print(as[6] + " WHACKS THE LOOSE PUCK INTO THE STANDS\n");
                            break;
                        case 4:
                            print("STICK SAVE BY " + as[6] + " AND HE CLEARS IT OUT HIMSELF\n");
                            l--;
                            continue;
                        case 5:
                            print("KICKED OUT BY " + as[6] + "\n");
                            print("AND IT REBOUNDS ALL THE WAY TO CENTER ICE\n");
                            l--;
                            continue;
                        case 6:
                            print("GLOVE SAVE " + as[6] + " AND HE HANGS ON\n");
                            break;
                    }
                }
            }
            print("AND WE'RE READY FOR THE FACE-OFF\n");
        }
        // Bells chime
        print("THAT'S THE SIREN\n");
        print("\n");
        print(tab(15) + "FINAL SCORE:\n");
        if (ha[8] <= ha[9]) {
            print(as[7] + ": " + ha[9] + "\t" + bs[7] + ": " + ha[8] + "\n");
        } else {
            print(bs[7] + ": " + ha[8] + "\t" + as[7] + ": " + ha[9] + "\n");
        }
        print("\n");
        print(tab(10) + "SCORING SUMMARY\n");
        print("\n");
        print(tab(25) + as[7] + "\n");
        print("\tNAME\tGOALS\tASSISTS\n");
        print("\t----\t-----\t-------\n");
        for (i = 1; i <= 5; i++) {
            print("\t" + as[i] + "\t" + ta[i] + "\t" + t1[i] + "\n");
        }
        print("\n");
        print(tab(25) + bs[7] + "\n");
        print("\tNAME\tGOALS\tASSISTS\n");
        print("\t----\t-----\t-------\n");
        for (t = 1; t <= 5; t++) {
            print("\t" + bs[t] + "\t" + t2[t] + "\t" + t3[t] + "\n");
        }
        print("\n");
        print("SHOTS ON NET\n");
        print(as[7] + ": " + s2 + "\n");
        print(bs[7] + ": " + s3 + "\n");
    }
    
    main();
    
    
    ================================================
    FILE: 49_Hockey/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 49_Hockey/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 49_Hockey/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 49_Hockey/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ## Porting Notes
    
    Variables:
    
    * C: Do you want instructions?
    * A$(7): Team name + player names (6 players)
    * B$(7): Team name + player names (6 players)
    * T6: Minutes per game
    * R: REFEREE
    
    Functions:
    
    * REM: A line comment
    * `INT(2*RND(X))+1`: X is constantly 1. That means that this expression is simpler expressed as `randint(1,2)`
    
    ---
    
    Looking at the JS implementation:
    
    * as[7] / bs[7]: The team name
    * ha[8] : Score of team B
    * ha[9] : Score of team A
    
    
    ================================================
    FILE: 49_Hockey/python/hockey.py
    ================================================
    """
    HOCKEY
    
    A simulation of an ice hockey game.
    
    The original author is Robert Puopolo;
    modifications by Steve North of Creative Computing.
    
    Ported to Python by Martin Thoma in 2022
    """
    
    from dataclasses import dataclass, field
    from random import randint
    from typing import List, Tuple
    
    NB_PLAYERS = 6
    
    
    @dataclass
    class Team:
        # TODO: It would be better to use a Player-class (name, goals, assits)
        #       and have the attributes directly at each player. This would avoid
        #       dealing with indices that much
        #
        #       I'm also rather certain that I messed up somewhere with the indices
        #       - instead of using those, one could use actual player positions:
        #       LEFT WING,    CENTER,        RIGHT WING
        #       LEFT DEFENSE, RIGHT DEFENSE, GOALKEEPER
        name: str
        players: List[str]  # 6 players
        shots_on_net: int = 0
        goals: List[int] = field(default_factory=lambda: [0 for _ in range(NB_PLAYERS)])
        assists: List[int] = field(default_factory=lambda: [0 for _ in range(NB_PLAYERS)])
        score: int = 0
    
        def show_lineup(self) -> None:
            print(" " * 10 + f"{self.name} STARTING LINEUP")
            for player in self.players:
                print(player)
    
    
    def ask_binary(prompt: str, error_msg: str) -> bool:
        while True:
            answer = input(prompt).lower()
            if answer in ["y", "yes"]:
                return True
            if answer in ["n", "no"]:
                return False
            print(error_msg)
    
    
    def get_team_names() -> Tuple[str, str]:
        while True:
            answer = input("ENTER THE TWO TEAMS: ")
            if answer.count(",") == 1:
                return answer.split(",")  # type: ignore
            print("separated by a single comma")
    
    
    def get_pass() -> int:
        while True:
            answer = input("PASS? ")
            try:
                passes = int(answer)
                if passes >= 0 and passes <= 3:
                    return passes
            except ValueError:
                print("ENTER A NUMBER BETWEEN 0 AND 3")
    
    
    def get_minutes_per_game() -> int:
        while True:
            answer = input("ENTER THE NUMBER OF MINUTES IN A GAME ")
            try:
                minutes = int(answer)
                if minutes >= 1:
                    return minutes
            except ValueError:
                print("ENTER A NUMBER")
    
    
    def get_player_names(prompt: str) -> List[str]:
        players = []
        print(prompt)
        for i in range(1, 7):
            player = input(f"PLAYER {i}: ")
            players.append(player)
        return players
    
    
    def make_shot(
        controlling_team: int, team_a: Team, team_b: Team, player_index: List[int], j: int
    ) -> Tuple[int, int, int, int]:
        while True:
            try:
                s = int(input("SHOT? "))
            except ValueError:
                continue
            if s >= 1 and s <= 4:
                break
        if controlling_team == 1:
            print(team_a.players[player_index[j - 1]])
        else:
            print(team_b.players[player_index[j - 1]])
        g = player_index[j - 1]
        g1 = 0
        g2 = 0
        if s == 1:
            print(" LET'S A BOOMER GO FROM THE RED LINE!!\n")  # line 400
            z = 10
        elif s == 2:
            print(" FLIPS A WRISTSHOT DOWN THE ICE\n")  # line 420
            # Probable missing line 430 in original
        elif s == 3:
            print(" BACKHANDS ONE IN ON THE GOALTENDER\n")
            z = 25
        elif s == 4:
            print(" SNAPS A LONG FLIP SHOT\n")
            # line 460
            z = 17
        return z, g, g1, g2
    
    
    def print_header() -> None:
        print(" " * 33 + "HOCKEY")
        print(" " * 15 + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n\n\n")
    
    
    def instructions() -> None:
        if ask_binary(
            "WOULD YOU LIKE THE INSTRUCTIONS? ", "ANSWER YES OR NO!!"
        ):
            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("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.")
    
    
    def team1_action(
        pass_value: int, player_index: List[int], team_a: Team, team_b: Team, j: int
    ) -> Tuple[int, int, int, int]:
        if pass_value == 1:
            print(
                team_a.players[player_index[j - 2]]
                + " LEADS "
                + team_a.players[player_index[j - 1]]
                + " WITH A PERFECT PASS.\n"
            )
            print(team_a.players[player_index[j - 1]] + " CUTTING IN!!!\n")
            scoring_player = player_index[j - 1]
            goal_assistant1 = player_index[j - 2]
            goal_assistant2 = 0
            z1 = 3
        elif pass_value == 2:
            print(
                team_a.players[player_index[j - 2]]
                + " GIVES TO A STREAKING "
                + team_a.players[player_index[j - 1]]
            )
            print(
                team_a.players[player_index[j - 3]]
                + " COMES DOWN ON "
                + team_b.players[4]
                + " AND "
                + team_b.players[3]
            )
            scoring_player = player_index[j - 3]
            goal_assistant1 = player_index[j - 1]
            goal_assistant2 = player_index[j - 2]
            z1 = 2
        elif pass_value == 3:
            print("OH MY GOD!! A ' 4 ON 2 ' SITUATION\n")
            print(
                team_a.players[player_index[j - 3]]
                + " LEADS "
                + team_a.players[player_index[j - 2]]
                + "\n"
            )
            print(team_a.players[player_index[j - 2]] + " IS WHEELING THROUGH CENTER.\n")
            print(
                team_a.players[player_index[j - 2]]
                + " GIVES AND GOEST WITH "
                + team_a.players[player_index[j - 1]]
            )
            print("PRETTY PASSING!\n")
            print(
                team_a.players[player_index[j - 1]]
                + " DROPS IT TO "
                + team_a.players[player_index[j - 4]]
            )
            scoring_player = player_index[j - 4]
            goal_assistant1 = player_index[j - 1]
            goal_assistant2 = player_index[j - 2]
            z1 = 1
        return scoring_player, goal_assistant1, goal_assistant2, z1
    
    
    def team2_action(
        pass_value: int, player_index: List[int], team_a: Team, team_b: Team, j: int
    ) -> Tuple[int, int, int, int]:
        if pass_value == 1:
            print(
                team_b.players[player_index[j - 1]]
                + " HITS "
                + team_b.players[player_index[j - 2]]
                + " FLYING DOWN THE LEFT SIDE\n"
            )
            scoring_player = player_index[j - 2]
            goal_assistant1 = player_index[j - 1]
            goal_assistant2 = 0
            z1 = 3
        elif pass_value == 2:
            print("IT'S A ' 3 ON 2 '!\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 "
                + team_b.players[player_index[j - 1]]
            )
            print(
                team_b.players[player_index[j - 1]]
                + " DROPS TO "
                + team_b.players[player_index[j - 3]]
            )
            scoring_player = player_index[j - 3]
            goal_assistant1 = player_index[j - 1]
            goal_assistant2 = player_index[j - 2]
            z1 = 2
        elif pass_value == 3:
            print(" A '3 ON 2 ' WITH A ' TRAILER '!\n")
            print(
                team_b.players[player_index[j - 4]]
                + " GIVES TO "
                + team_b.players[player_index[j - 2]]
                + " WHO SHUFFLES IT OFF TO\n"
            )
            print(
                team_b.players[player_index[j - 1]] + " WHO FIRES A WING TO WING PASS TO \n"
            )
            print(team_b.players[player_index[j - 3]] + " AS HE CUTS IN ALONE!!\n")
            scoring_player = player_index[j - 3]
            goal_assistant1 = player_index[j - 1]
            goal_assistant2 = player_index[j - 2]
            z1 = 1
        return scoring_player, goal_assistant1, goal_assistant2, z1
    
    
    def final_message(team_a: Team, team_b: Team, player_index: List[int]) -> None:
        # Bells chime
        print("THAT'S THE SIREN\n")
        print("\n")
        print(" " * 15 + "FINAL SCORE:\n")
        if team_b.score <= team_a.score:
            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}\t:{team_a.score}\n")
        print("\n")
        print(" " * 10 + "SCORING SUMMARY\n")
        print("\n")
        print(" " * 25 + team_a.name + "\n")
        print("\tNAME\tGOALS\tASSISTS\n")
        print("\t----\t-----\t-------\n")
        for i in range(1, 6):
            print(f"\t{team_a.players[i]}\t{team_a.goals[i]}\t{team_a.assists[i]}\n")
        print("\n")
        print(" " * 25 + team_b.name + "\n")
        print("\tNAME\tGOALS\tASSISTS\n")
        print("\t----\t-----\t-------\n")
        for t in range(1, 6):
            print(f"\t{team_b.players[t]}\t{team_b.goals[t]}\t{team_b.assists[t]}\n")
        print("\n")
        print("SHOTS ON NET\n")
        print(f"{team_a.name}: {team_a.shots_on_net}\n")
        print(f"{team_b.name}: {team_b.shots_on_net}\n")
    
    
    def main() -> None:
        # Intro
        print_header()
        player_index: List[int] = [0 for _ in range(21)]
        print("\n" * 3)
        instructions()
    
        # Gather input
        team_name_a, team_name_b = get_team_names()
        print()
        minutes_per_game = get_minutes_per_game()
        print()
        players_a = get_player_names(f"WOULD THE {team_name_a} COACH ENTER HIS TEAM")
        print()
        players_b = get_player_names(f"WOULD THE {team_name_b} COACH DO THE SAME")
        team_a = Team(team_name_a, players_a)
        team_b = Team(team_name_b, players_b)
        print()
        referee = input("INPUT THE REFEREE FOR THIS GAME: ")
        print()
        team_a.show_lineup()
        print()
        team_b.show_lineup()
        print("WE'RE READY FOR TONIGHTS OPENING FACE-OFF.")
        print(
            f"{referee} WILL DROP THE PUCK BETWEEN "
            f"{team_a.players[0]} AND {team_b.players[0]}"
        )
        remaining_time = minutes_per_game
    
        # Play the game
        while remaining_time > 0:
            cont, remaining_time = simulate_game_round(
                team_a, team_b, player_index, remaining_time
            )
            remaining_time -= 1
            if cont == "break":
                break
    
        # Outro
        final_message(team_a, team_b, player_index)
    
    
    def handle_hit(
        controlling_team: int,
        team_a: Team,
        team_b: Team,
        player_index: List[int],
        goal_player: int,
        goal_assistant1: int,
        goal_assistant2: int,
        hit_area: int,
        z: int,
    ) -> int:
        while True:
            player_index[20] = randint(1, 100)
            if player_index[20] % z != 0:
                break
            a2 = randint(1, 100)
            if a2 % 4 == 0:
                if controlling_team == 1:
                    print(f"SAVE {team_b.players[5]} --  REBOUND\n")
                else:
                    print(f"SAVE {team_a.players[5]} --  FOLLOW up\n")
                continue
            else:
                hit_area += 1
        if player_index[20] % z != 0:
            if controlling_team == 1:
                print(f"GOAL {team_a.name}\n")
                team_a.score += 1
            else:
                print(f"SCORE {team_b.name}\n")
                team_b.score += 1
            # Bells in origninal
            print("\n")
            print("SCORE: ")
            if team_b.score <= team_a.score:
                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")
            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(
                        f" ASSISTED BY: {team.players[goal_assistant1]}"
                        f" AND {team.players[goal_assistant2]}"
                    )
                else:
                    print(f" ASSISTED BY: {team.players[goal_assistant1]}")
                team.assists[goal_assistant1] += 1
                team.assists[goal_assistant2] += 1
            else:
                print(" UNASSISTED.\n")
            team.goals[goal_player] += 1
    
        return hit_area
    
    
    def handle_miss(
        controlling_team: int,
        team_a: Team,
        team_b: Team,
        remaining_time: int,
        goal_player: int,
    ) -> Tuple[str, int]:
        saving_player = randint(1, 7)
        if controlling_team == 1:
            if saving_player == 1:
                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(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(f"SKATE SAVE ON A LOW STEAMER BY {team_b.players[5]}\n")
                remaining_time -= 1
                return ("continue", remaining_time)
            if saving_player == 4:
                print(f"PAD SAVE BY {team_b.players[5]} OFF THE STICK\n")
                print(
                    f"OF {team_a.players[goal_player]} AND "
                    f"{team_b.players[5]} COVERS UP\n"
                )
                return ("break", remaining_time)
            if saving_player == 5:
                print(f"WHISTLES ONE OVER THE HEAD OF {team_b.players[5]}\n")
                remaining_time -= 1
                return ("continue", remaining_time)
            if saving_player == 6:
                print(f"{team_b.players[5]} MAKES A FACE SAVE!! AND HE IS HURT\n")
                print(f"THE DEFENSEMAN {team_b.players[5]} COVERS UP FOR HIM\n")
                return ("break", remaining_time)
        else:
            if saving_player == 1:
                print(f"STICK SAVE BY {team_a.players[5]}\n")
                print(f"AND CLEARED OUT BY {team_a.players[3]}\n")
                remaining_time -= 1
                return ("continue", remaining_time)
            if saving_player == 2:
                print(
                    "OH MY GOD!! "
                    f"{team_b.players[goal_player]} RATTLES ONE OFF THE POST\n"
                )
                print(
                    f"TO THE RIGHT OF {team_a.players[5]} AND "
                    f"{team_a.players[5]} COVERS "
                )
                print("ON THE LOOSE PUCK!\n")
                return ("break", remaining_time)
            if saving_player == 3:
                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(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(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(f"GLOVE SAVE {team_a.players[5]} AND HE HANGS ON\n")
                return ("break", remaining_time)
        return ("continue", remaining_time)
    
    
    def simulate_game_round(
        team_a: Team, team_b: Team, player_index: List[int], remaining_time: int
    ) -> Tuple[str, int]:
        controlling_team = randint(1, 2)
        if controlling_team == 1:
            print(f"{team_a.name} HAS CONTROL OF THE PUCK.")
        else:
            print(f"{team_b.name} HAS CONTROL.")
        pass_value = get_pass()
        for i in range(1, 4):
            player_index[i] = 0
    
        # Line 310:
        while True:
            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 >= 1
                and (
                    player_index[j - 1] == player_index[j - 3]
                    or player_index[j - 2] == player_index[j - 3]
                )
            ):
                break
        if pass_value == 0:  # line 350
            z, goal_player, goal_assistant1, goal_assistant2 = make_shot(
                controlling_team, team_a, team_b, player_index, j
            )
        else:
            if controlling_team == 1:
                goal_player, goal_assistant1, goal_assistant2, z1 = team1_action(
                    pass_value, player_index, team_a, team_b, j
                )
            else:
                goal_player, goal_assistant1, goal_assistant2, z1 = team2_action(
                    pass_value, player_index, team_a, team_b, j
                )
            while True:
                shot_type = int(input("SHOT? "))
                if shot_type >= 1 and shot_type <= 4:
                    break
            if controlling_team == 1:
                print(team_a.players[goal_player], end="")
            else:
                print(team_b.players[goal_player], end="")
            if shot_type == 1:
                print(" LET'S A BIG SLAP SHOT GO!!\n")
                z = 4
                z += z1
            elif shot_type == 2:
                print(" RIPS A WRIST SHOT OFF\n")
                z = 2
                z += z1
            elif shot_type == 3:
                print(" GETS A BACKHAND OFF\n")
                z = 3
                z += z1
            elif shot_type == 4:
                print(" SNAPS OFF A SNAP SHOT\n")
                z = 2
                z += z1
        while True:
            goal_area = int(input("AREA? "))
            if goal_area >= 1 and goal_area <= 4:
                break
        if controlling_team == 1:
            team_a.shots_on_net += 1
        else:
            team_b.shots_on_net += 1
        hit_area = randint(1, 5)
        if goal_area == hit_area:
            hit_area = handle_hit(
                controlling_team,
                team_a,
                team_b,
                player_index,
                goal_player,
                goal_assistant1,
                goal_assistant2,
                hit_area,
                z,
            )
        if goal_area != hit_area:
            return handle_miss(
                controlling_team, team_a, team_b, remaining_time, goal_player
            )
        print("AND WE'RE READY FOR THE FACE-OFF\n")
        return ("continue", remaining_time)
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 49_Hockey/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 49_Hockey/vbnet/Hockey.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Hockey", "Hockey.vbproj", "{9ED23BC7-7C12-4B48-8E85-F21E310813D5}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{9ED23BC7-7C12-4B48-8E85-F21E310813D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{9ED23BC7-7C12-4B48-8E85-F21E310813D5}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{9ED23BC7-7C12-4B48-8E85-F21E310813D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{9ED23BC7-7C12-4B48-8E85-F21E310813D5}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 49_Hockey/vbnet/Hockey.vbproj
    ================================================
    
      
        Exe
        Hockey
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 49_Hockey/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 50_Horserace/README.md
    ================================================
    ### Horserace
    
    This program simulates a one-mile horse race for three-year old throughbreds. Up to ten people may place bets on the race up to $10,000 each. However, you may only bet to win. You place your bet by inputting the number of the horse, a comma, and the amount of your bet. The computer then shows the position of the horses at seven points around the track and at the finish. Payoffs and winnings are shown at the end.
    
    The program was written by Laurie Chevalier while a student at South Portland High School.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=92)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=107)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 50_Horserace/csharp/Horserace.csproj
    ================================================
    
      
        Exe
        net6.0
        10
        enable
        enable
      
    
    
    
    ================================================
    FILE: 50_Horserace/csharp/Horserace.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Horserace", "Horserace.csproj", "{B1CD8505-43BA-4C2C-A458-54E14539DB35}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{B1CD8505-43BA-4C2C-A458-54E14539DB35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{B1CD8505-43BA-4C2C-A458-54E14539DB35}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{B1CD8505-43BA-4C2C-A458-54E14539DB35}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{B1CD8505-43BA-4C2C-A458-54E14539DB35}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 50_Horserace/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 50_Horserace/horserace.bas
    ================================================
    100 PRINT TAB(31);"HORSERACE"
    110 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    120 PRINT:PRINT:PRINT
    210 DIM S(8)
    220 PRINT "WELCOME TO SOUTH PORTLAND HIGH RACETRACK"
    230 PRINT "                      ...OWNED BY LAURIE CHEVALIER"
    240 PRINT "DO YOU WANT DIRECTIONS";
    250 INPUT X$
    260 IF X$="NO" THEN 320
    270 PRINT"UP TO 10 MAY PLAY.  A TABLE OF ODDS WILL BE PRINTED.  YOU"
    280 PRINT"MAY BET ANY + AMOUNT UNDER 100000 ON ONE HORSE."
    290 PRINT "DURING THE RACE, A HORSE WILL BE SHOWN BY ITS"
    300 PRINT"NUMBER.  THE HORSES RACE DOWN THE PAPER!"
    310 PRINT
    320 PRINT "HOW MANY WANT TO BET";
    330 INPUT C
    340 PRINT "WHEN ? APPEARS,TYPE NAME"
    350 FOR A=1 TO C
    360 INPUT W$(A)
    370 NEXT A
    380 PRINT
    390 PRINT"HORSE",,"NUMBER","ODDS"
    400 PRINT
    410 FOR I=1 TO 8: S(I)=0: NEXT I
    420 LET R=0
    430 FOR A=1 TO 8
    440 LET D(A)=INT(10*RND(1)+1)
    450 NEXT A
    460 FOR A=1 TO 8
    470 LET R=R+D(A)
    480 NEXT A
    490 LET V$(1)="JOE MAW"
    500 LET V$(2)="L.B.J."
    510 LET V$(3)="MR.WASHBURN"
    520 LET V$(4)="MISS KAREN"
    530 LET V$(5)="JOLLY"
    540 LET V$(6)="HORSE"
    550 LET V$(7)="JELLY DO NOT"
    560 LET V$(8)="MIDNIGHT"
    570 FOR N=1 TO 8
    580 PRINT V$(N),,N,R/D(N);":1"
    590 NEXT N
    600 PRINT"--------------------------------------------------"
    610 PRINT "PLACE YOUR BETS...HORSE # THEN AMOUNT"
    620 FOR J=1 TO C
    630 PRINT W$(J);
    640 INPUT Q(J),P(J)
    650 IF P(J)<1 THEN 670
    660 IF P(J)<100000 THEN 690
    670 PRINT"  YOU CAN'T DO THAT!"
    680 GOTO 630
    690 NEXT J
    700 PRINT
    710 PRINT"1 2 3 4 5 6 7 8"
    720 PRINT"XXXXSTARTXXXX"
    730 FOR I=1 TO 8
    740 LET M=I
    750 LET M(I)=M
    760 LET Y(M(I))=INT(100*RND(1)+1)
    770 IF Y(M(I))<10 THEN 860
    780 LET S=INT(R/D(I)+.5)
    790 IF Y(M(I))27 THEN 1240
    1180 NEXT A
    1190 PRINT M(I);
    1200 NEXT I
    1210 FOR A=1 TO 28-T
    1220 PRINT
    1230 NEXT A
    1240 PRINT "XXXXFINISHXXXX";
    1242 PRINT
    1243 PRINT
    1244 PRINT "---------------------------------------------"
    1245 PRINT
    1250 IF T<28 THEN 720
    1270 PRINT "THE RACE RESULTS ARE:"
    1272 LET Z9=1
    1280 FOR I=8 TO 1 STEP-1
    1290 LET F=M(I)
    1300 PRINT
    1310 PRINT Z9;"PLACE HORSE NO.";F,"AT ";R/D(F);":1"
    1312 LET Z9=Z9+1
    1320  NEXT I
    1330 FOR J=1 TO C
    1340 IF Q(J)<>M(8) THEN 1370
    1350 LET N=Q(J)
    1355 PRINT
    1360 PRINT W$(J);" WINS $";(R/D(N))*P(J)
    1370 NEXT J
    1372 PRINT "DO YOU WANT TO BET ON THE NEXT RACE ?"
    1374 INPUT "YES OR NO"; O$
    1376 IF O$="YES" THEN 380
    1380 END
    
    
    ================================================
    FILE: 50_Horserace/java/Horserace.java
    ================================================
    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
     * 

    * 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 + '}'; } } } ================================================ FILE: 50_Horserace/java/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Oracle Java](https://openjdk.java.net/) ================================================ FILE: 50_Horserace/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 50_Horserace/javascript/horserace.html ================================================ HORSERACE

    
    
    
    
    
    
    ================================================
    FILE: 50_Horserace/javascript/horserace.js
    ================================================
    // HORSERACE
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    var sa = [];
    var ws = [];
    var da = [];
    var qa = [];
    var pa = [];
    var ma = [];
    var ya = [];
    var vs = [];
    
    // Main program
    async function main()
    {
        print(tab(31) + "HORSERACE\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        print("WELCOME TO SOUTH PORTLAND HIGH RACETRACK\n");
        print("                      ...OWNED BY LAURIE CHEVALIER\n");
        print("DO YOU WANT DIRECTIONS");
        str = await input();
        if (str == "YES") {
            print("UP TO 10 MAY PLAY.  A TABLE OF ODDS WILL BE PRINTED.  YOU\n");
            print("MAY BET ANY + AMOUNT UNDER 100000 ON ONE HORSE.\n");
            print("DURING THE RACE, A HORSE WILL BE SHOWN BY ITS\n");
            print("NUMBER.  THE HORSES RACE DOWN THE PAPER!\n");
            print("\n");
        }
        print("HOW MANY WANT TO BET");
        c = parseInt(await input());
        print("WHEN ? APPEARS,TYPE NAME\n");
        for (a = 1; a <= c; a++) {
            ws[a] = await input();
        }
        do {
            print("\n");
            print("HORSE\t\tNUMBERS\tODDS\n");
            print("\n");
            for (i = 1; i <= 8; i++) {
                sa[i] = 0;
            }
            r = 0;
            for (a = 1; a <= 8; a++) {
                da[a] = Math.floor(10 * Math.random() + 1);
            }
            for (a = 1; a <= 8; a++) {
                r = r + da[a];
            }
            vs[1] = "JOE MAN";
            vs[2] = "L.B.J.";
            vs[3] = "MR.WASHBURN";
            vs[4] = "MISS KAREN";
            vs[5] = "JOLLY";
            vs[6] = "HORSE";
            vs[7] = "JELLY DO NOT";
            vs[8] = "MIDNIGHT";
            for (n = 1; n <= 8; n++) {
                print(vs[n] + "\t\t" + n + "\t" + (r / da[n]) + ":1\n");
            }
            print("--------------------------------------------------\n");
            print("PLACE YOUR BETS...HORSE # THEN AMOUNT\n");
            for (j = 1; j <= c; j++) {
                while (1) {
                    print(ws[j]);
                    str = await input();
                    qa[j] = parseInt(str);
                    pa[j] = parseInt(str.substr(str.indexOf(",") + 1));
                    if (pa[j] < 1 || pa[j] >= 100000) {
                        print("  YOU CAN'T DO THAT!\N");
                    } else {
                        break;
                    }
                }
            }
            print("\n");
            print("1 2 3 4 5 6 7 8\n");
            t = 0;
            do {
                print("XXXXSTARTXXXX\n");
                for (i = 1; i <= 8; i++) {
                    m = i;
                    ma[i] = m;
                    ya[ma[i]] = Math.floor(100 * Math.random() + 1);
                    if (ya[ma[i]] < 10) {
                        ya[ma[i]] = 1;
                        continue;
                    }
                    s = Math.floor(r / da[i] + 0.5);
                    if (ya[ma[i]] < s + 17) {
                        ya[ma[i]] = 2;
                        continue;
                    }
                    if (ya[ma[i]] < s + 37) {
                        ya[ma[i]] = 3;
                        continue;
                    }
                    if (ya[ma[i]] < s + 57) {
                        ya[ma[i]] = 4;
                        continue;
                    }
                    if (ya[ma[i]] < s + 77) {
                        ya[ma[i]] = 5;
                        continue;
                    }
                    if (ya[ma[i]] < s + 92) {
                        ya[ma[i]] = 6;
                        continue;
                    }
                    ya[ma[i]] = 7;
                }
                m = i;
                for (i = 1; i <= 8; i++) {
                    sa[ma[i]] = sa[ma[i]] + ya[ma[i]];
                }
                i = 1;
                for (l = 1; l <= 8; l++) {
                    for (i = 1; i <= 8 - l; i++) {
                        if (sa[ma[i]] < sa[ma[i + 1]])
                            continue;
                        h = ma[i];
                        ma[i] = ma[i + 1];
                        ma[i + 1] = h;
                    }
                }
                t = sa[ma[8]];
                for (i = 1; i <= 8; i++) {
                    b = sa[ma[i]] - sa[ma[i - 1]];
                    if (b != 0) {
                        for (a = 1; a <= b; a++) {
                            print("\n");
                            if (sa[ma[i]] > 27)
                                break;
                        }
                        if (a <= b)
                            break;
                    }
                    print(" " + ma[i] + " ");
                }
                for (a = 1; a < 28 - t; a++) {
                    print("\n");
                }
                print("XXXXFINISHXXXX\n");
                print("\n");
                print("\n");
                print("---------------------------------------------\n");
                print("\n");
            } while (t < 28) ;
            print("THE RACE RESULTS ARE:\n");
            z9 = 1;
            for (i = 8; i >= 1; i--) {
                f = ma[i];
                print("\n");
                print("" + z9 + " PLACE HORSE NO. " + f + " AT " + (r / da[f]) + ":1\n");
                z9++;
            }
            for (j = 1; j <= c; j++) {
                if (qa[j] != ma[8])
                    continue;
                n = qa[j];
                print("\n");
                print(ws[j] + " WINS $" + (r / da[n]) * pa[j] + "\n");
            }
            print("DO YOU WANT TO BET ON THE NEXT RACE ?\n");
            print("YES OR NO");
            str = await input();
        } while (str == "YES") ;
    }
    
    main();
    
    
    ================================================
    FILE: 50_Horserace/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 50_Horserace/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 50_Horserace/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 50_Horserace/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 50_Horserace/python/horserace.py
    ================================================
    import math
    import random
    import time
    from typing import List, Tuple
    
    
    def basic_print(*zones, **kwargs) -> None:
        """Simulates the PRINT command from BASIC to some degree.
        Supports `printing zones` if given multiple arguments."""
    
        line = ""
        if len(zones) == 1:
            line = str(zones[0])
        else:
            line = "".join([f"{str(zone):<14}" for zone in zones])
        identation = kwargs.get("indent", 0)
        end = kwargs.get("end", "\n")
        print(" " * identation + line, end=end)
    
    
    def basic_input(prompt: str, type_conversion=None):
        """BASIC INPUT command with optional type conversion"""
    
        while True:
            try:
                inp = input(f"{prompt}? ")
                if type_conversion is not None:
                    inp = type_conversion(inp)
                break
            except ValueError:
                basic_print("INVALID INPUT!")
        return inp
    
    
    # horse names do not change over the program, therefore making it a global.
    # throught the game, the ordering of the horses is used to indentify them
    HORSE_NAMES = [
        "JOE MAW",
        "L.B.J.",
        "MR.WASHBURN",
        "MISS KAREN",
        "JOLLY",
        "HORSE",
        "JELLY DO NOT",
        "MIDNIGHT",
    ]
    
    
    def introduction() -> None:
        """Print the introduction, and optional the instructions"""
    
        basic_print("HORSERACE", indent=31)
        basic_print("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY", indent=15)
        basic_print("\n\n")
        basic_print("WELCOME TO SOUTH PORTLAND HIGH RACETRACK")
        basic_print("                      ...OWNED BY LAURIE CHEVALIER")
        y_n = basic_input("DO YOU WANT DIRECTIONS")
    
        # if no instructions needed, return
        if y_n.upper() == "NO":
            return
    
        basic_print("UP TO 10 MAY PLAY.  A TABLE OF ODDS WILL BE PRINTED.  YOU")
        basic_print("MAY BET ANY + AMOUNT UNDER 100000 ON ONE HORSE.")
        basic_print("DURING THE RACE, A HORSE WILL BE SHOWN BY ITS")
        basic_print("NUMBER.  THE HORSES RACE DOWN THE PAPER!")
        basic_print("")
    
    
    def setup_players() -> List[str]:
        """Gather the number of players and their names"""
    
        # ensure we get an integer value from the user
        number_of_players = basic_input("HOW MANY WANT TO BET", int)
    
        basic_print("WHEN ? APPEARS,TYPE NAME")
        return [basic_input("") for _ in range(number_of_players)]
    
    
    def setup_horses() -> List[float]:
        """Generates random odds for each horse. Returns a list of
        odds, indexed by the order of the global HORSE_NAMES."""
    
        odds = [random.randrange(1, 10) for _ in HORSE_NAMES]
        total = sum(odds)
    
        # rounding odds to two decimals for nicer output,
        # this is not in the origin implementation
        return [round(total / odd, 2) for odd in odds]
    
    
    def print_horse_odds(odds) -> None:
        """Print the odds for each horse"""
    
        basic_print("")
        for i in range(len(HORSE_NAMES)):
            basic_print(HORSE_NAMES[i], i, f"{odds[i]}:1")
        basic_print("")
    
    
    def get_bets(player_names: List[str]) -> List[Tuple[int, float]]:
        """For each player, get the number of the horse to bet on,
        as well as the amount of money to bet"""
    
        basic_print("--------------------------------------------------")
        basic_print("PLACE YOUR BETS...HORSE # THEN AMOUNT")
    
        bets: List[Tuple[int, float]] = []
        for name in player_names:
            horse = basic_input(name, int)
            amount = None
            while amount is None:
                amount = basic_input("", float)
                if amount < 1 or amount >= 100000:
                    basic_print("  YOU CAN'T DO THAT!")
                    amount = None
            bets.append((horse, amount))
    
        basic_print("")
    
        return bets
    
    
    def get_distance(odd: float) -> int:
        """Advances a horse during one step of the racing simulation.
        The amount travelled is random, but scaled by the odds of the horse"""
    
        d = random.randrange(1, 100)
        s = math.ceil(odd)
        if d < 10:
            return 1
        elif d < s + 17:
            return 2
        elif d < s + 37:
            return 3
        elif d < s + 57:
            return 4
        elif d < s + 77:
            return 5
        elif d < s + 92:
            return 6
        else:
            return 7
    
    
    def print_race_state(total_distance, race_pos) -> None:
        """Outputs the current state/stop of the race.
        Each horse is placed according to the distance they have travelled. In
        case some horses travelled the same distance, their numbers are printed
        on the same name"""
    
        # we dont want to modify the `race_pos` list, since we need
        # it later. Therefore we generating an interator from the list
        race_pos_iter = iter(race_pos)
    
        # race_pos is stored by last to first horse in the race.
        # we get the next horse we need to print out
        next_pos = next(race_pos_iter)
    
        # start line
        basic_print("XXXXSTARTXXXX")
    
        # print all 28 lines/unit of the race course
        for line in range(28):
    
            # ensure we still have a horse to print and if so, check if the
            # next horse to print is not the current line
            # needs iteration, since multiple horses can share the same line
            while next_pos is not None and line == total_distance[next_pos]:
                basic_print(f"{next_pos} ", end="")
                next_pos = next(race_pos_iter, None)
            else:
                # if no horses are left to print for this line, print a new line
                basic_print("")
    
        # finish line
        basic_print("XXXXFINISHXXXX")
    
    
    def simulate_race(odds) -> List[int]:
        num_horses = len(HORSE_NAMES)
    
        # in spirit of the original implementation, using two arrays to
        # track the total distance travelled, and create an index from
        # race position -> horse index
        total_distance = [0] * num_horses
    
        # race_pos maps from the position in the race, to the index of the horse
        # it will later be sorted from last to first horse, based on the
        # distance travelled by each horse.
        # e.g. race_pos[0] => last horse
        #      race_pos[-1] => winning horse
        race_pos = list(range(num_horses))
    
        basic_print("\n1 2 3 4 5 6 7 8")
    
        while True:
    
            # advance each horse by a random amount
            for i in range(num_horses):
                total_distance[i] += get_distance(odds[i])
    
            # bubble sort race_pos based on total distance travelled
            # in the original implementation, race_pos is reset for each
            # simulation step, so we keep this behaviour here
            race_pos = list(range(num_horses))
            for line in range(num_horses):
                for i in range(num_horses - 1 - line):
                    if total_distance[race_pos[i]] < total_distance[race_pos[i + 1]]:
                        continue
                    race_pos[i], race_pos[i + 1] = race_pos[i + 1], race_pos[i]
    
            # print current state of the race
            print_race_state(total_distance, race_pos)
    
            # goal line is defined as 28 units from start
            # check if the winning horse is already over the finish line
            if total_distance[race_pos[-1]] >= 28:
                return race_pos
    
            # this was not in the original BASIC implementation, but it makes the
            # race visualization a nice animation (if the terminal size is set to 31 rows)
            time.sleep(1)
    
    
    def print_race_results(race_positions, odds, bets, player_names) -> None:
        """Print the race results, as well as the winnings of each player"""
    
        # print the race positions first
        basic_print("THE RACE RESULTS ARE:")
        for position, horse_idx in enumerate(reversed(race_positions), start=1):
            line = f"{position} PLACE HORSE NO. {horse_idx} AT {odds[horse_idx]}:1"
            basic_print("")
            basic_print(line)
    
        # followed by the amount the players won
        winning_horse_idx = race_positions[-1]
        for idx, name in enumerate(player_names):
            (horse, amount) = bets[idx]
            if horse == winning_horse_idx:
                basic_print("")
                basic_print(f"{name} WINS ${amount * odds[winning_horse_idx]}")
    
    
    def main_loop(player_names, horse_odds) -> None:
        """Main game loop"""
    
        while True:
            print_horse_odds(horse_odds)
            bets = get_bets(player_names)
            final_race_positions = simulate_race(horse_odds)
            print_race_results(final_race_positions, horse_odds, bets, player_names)
    
            basic_print("DO YOU WANT TO BET ON THE NEXT RACE ?")
            one_more = basic_input("YES OR NO")
            if one_more.upper() != "YES":
                break
    
    
    def main() -> None:
        # introduction, player names and horse odds are only generated once
        introduction()
        player_names = setup_players()
        horse_odds = setup_horses()
    
        # main loop of the game, the player can play multiple races, with the
        # same odds
        main_loop(player_names, horse_odds)
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 50_Horserace/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 50_Horserace/rust/Cargo.toml
    ================================================
    [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"
    
    
    ================================================
    FILE: 50_Horserace/rust/src/game.rs
    ================================================
    use std::{thread, time::Duration};
    
    use crate::{horses::Horses, players::Players};
    
    pub struct Game {
        horses: Horses,
        players: Players,
    }
    
    impl Game {
        pub fn new() -> Self {
            Game {
                horses: Horses::new(),
                players: Players::new(),
            }
        }
    
        pub fn play(&mut self) -> bool {
            self.horses.randomize_odds();
            self.horses.print_table();
    
            self.players.make_bets();
    
            println!("\n1 2 3 4 5 6 7 8");
    
            for _ in 1..=7 {
                self.horses.advance();
                self.draw();
                thread::sleep(Duration::from_secs(1));
            }
    
            let winner = self.horses.print_placements();
            self.players.process_winner(winner);
    
            return self.players.prompt_next_round();
        }
    
        pub fn draw(&self) {
            println!("=============");
            println!("XXXXSTARTXXXX");
            for row in 1..=28 {
                let neighbors = self.horses.get_at(row);
    
                match neighbors.len() {
                    0 => println!(),
                    1 => println!("{}", neighbors[0].no),
                    _ => {
                        for h in neighbors {
                            print!("{} ", h.no);
                        }
                        println!();
                    }
                }
            }
            println!("XXXXFINISHXXXX");
        }
    }
    
    
    ================================================
    FILE: 50_Horserace/rust/src/horses.rs
    ================================================
    use rand::Rng;
    
    pub struct Horse {
        pub name: String,
        pub no: u8,
        pub odd: f32,
        pub position: u8,
    }
    
    impl Horse {
        fn new(name: &str, no: u8) -> Self {
            Horse {
                name: name.to_string(),
                no,
                odd: 0.,
                position: 0,
            }
        }
    }
    
    pub struct Horses {
        horses: [Horse; 8],
    }
    
    impl Horses {
        pub fn new() -> Self {
            Horses {
                horses: [
                    Horse::new("JOE MAW", 1),
                    Horse::new("L.B.J.", 2),
                    Horse::new("MR.WASHBURN", 3),
                    Horse::new("MISS KAREN", 4),
                    Horse::new("JOLLY", 5),
                    Horse::new("HORSE", 6),
                    Horse::new("JELLY DO NOT", 7),
                    Horse::new("MIDNIGHT", 8),
                ],
            }
        }
    
        pub fn randomize_odds(&mut self) {
            let mut odds = Vec::new();
    
            for _ in 1..=8 {
                odds.push(rand::thread_rng().gen_range(1.0..=10.));
            }
    
            let total: f32 = odds.iter().sum();
    
            for (i, o) in odds.iter().enumerate() {
                let o = total / o;
                self.horses[i].odd = o;
            }
        }
    
        pub fn advance(&mut self) {
            for h in self.horses.iter_mut() {
                let distance = rand::thread_rng().gen_range(1..=100);
                let scale = h.odd.ceil() as i32;
    
                let dt = if distance < 10 {
                    1
                } else if distance < scale + 17 {
                    2
                } else if distance < scale + 37 {
                    3
                } else if distance < scale + 57 {
                    4
                } else if distance < scale + 77 {
                    5
                } else if distance < scale + 92 {
                    6
                } else {
                    7
                };
    
                h.position += dt as u8;
            }
        }
    
        pub fn get_at(&self, row: usize) -> Vec<&Horse> {
            self.horses
                .iter()
                .filter(|h| h.position == row as u8)
                .collect()
        }
    
        pub fn print_table(&self) {
            println!("HORSE\t\tNUMBER\t\tODDS\t\t\n");
            for horse in self.horses.iter() {
                let (h, n, o) = (horse.name.clone(), horse.no, horse.odd);
    
                if h.len() > 7 {
                    println!("{}\t{}\t\t{:.2} :1", h, n, o);
                } else {
                    println!("{}\t\t{}\t\t{:.2} :1", h, n, o);
                }
            }
            println!("-----------------------------------------\n")
        }
    
        pub fn print_placements(&mut self) -> u8 {
            self.horses.sort_by(|a, b| b.position.cmp(&a.position));
    
            println!("\nTHE RACE RESULTS ARE:\n");
    
            for (i, h) in self.horses.iter_mut().enumerate() {
                println!("{} PLACE HORSE NO. {}\t\tAT {:.2} :1", i + 1, h.no, h.odd);
                h.position = 0;
            }
    
            self.horses[0].no
        }
    }
    
    
    ================================================
    FILE: 50_Horserace/rust/src/main.rs
    ================================================
    use crate::{game::Game, util::PromptResult};
    
    mod game;
    mod horses;
    mod players;
    mod util;
    
    fn main() {
        println!("\n\n\t\tHORSERACE");
        println!("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n");
        println!("WELCOME TO SOUTH PORTLAND HIGH RACETRACK\n\t\t...OWNED BY LAURIE CHEVALIER");
    
        if let PromptResult::YesNo(yes) = util::prompt(Some(false), "DO YOU WANT DIRECTIONS?") {
            if yes {
                println!("UP TO 10 MAY PLAY.  A TABLE OF ODDS WILL BE PRINTED.  YOU");
                println!("MAY BET ANY AMOUNT UNDER $100,000 ON ONE HORSE.");
                println!("DURING THE RACE, A HORSE WILL BE SHOWN BY ITS");
                println!("NUMBER.  THE HORSES RACE DOWN THE PAPER!\n");
            }
        }
    
        let mut game = Game::new();
        let mut again = true;
    
        while again {
            again = game.play();
        }
    }
    
    
    ================================================
    FILE: 50_Horserace/rust/src/players.rs
    ================================================
    use crate::util::{self, PromptResult};
    
    #[derive(Debug)]
    pub struct Player {
        pub name: String,
        pub money: u32,
        pub playing: bool,
        pub horse_no: u8,
        pub bet: u32,
    }
    
    impl Player {
        fn new(name: String) -> Self {
            Player {
                name,
                money: 100000,
                playing: true,
                horse_no: 0,
                bet: 0,
            }
        }
    }
    
    #[derive(Debug)]
    pub struct Players {
        players: Vec,
    }
    
    impl Players {
        pub fn new() -> Self {
            let players;
    
            loop {
                if let PromptResult::Numeric(n) = util::prompt(Some(true), "HOW MANY WANT TO BET?") {
                    if n <= 0 {
                        println!("THERE CAN'T BE (LESS THAN) ZERO PLAYERS!");
                    } else if n > 10 {
                        println!("THERE CAN'T BE MORE THAN TEN PLAYERS!");
                    } else {
                        println!("WHEN ? APPEARS, TYPE NAME");
                        players = Players::generate_players(n);
                        break;
                    }
                }
            }
    
            Players { players }
        }
    
        pub fn make_bets(&mut self) {
            println!("PLACE YOUR BETS...HORSE # THEN AMOUNT");
    
            for p in self.players.iter_mut() {
                if !p.playing {
                    continue;
                }
    
                let name = format!("{}?", p.name);
    
                'prompt: loop {
                    if let PromptResult::Normal(response) = util::prompt(None, name.as_str()) {
                        let response: Vec<&str> = response.trim().split(",").collect();
    
                        for (i, n) in response.iter().enumerate() {
                            if let Ok(n) = n.parse::() {
                                if n.is_negative() {
                                    println!("YOU CAN'T ENTER A NEGATIVE NUMBER!")
                                } else {
                                    match i {
                                        0 => {
                                            if n > 8 {
                                                println!("INVALID HORSE #")
                                            } else {
                                                p.horse_no = n as u8;
                                            }
                                        }
                                        1 => {
                                            if n == 0 {
                                                println!("YOU CAN'T BET NOTHING!");
                                            } else if n > p.money as i32 {
                                                println!("YOU DON'T HAVE ENOUGH MONEY!")
                                            } else {
                                                p.bet = n as u32;
                                                break 'prompt;
                                            }
                                        }
                                        _ => println!("YOU CAN'T ENTER MORE THAN 2 NUMBERS!"),
                                    }
                                }
                            } else {
                                println!("ONLY ENTER NUMBERS PLEASE!");
                            }
                        }
                    }
                }
            }
        }
    
        fn generate_players(n: i32) -> Vec {
            let mut players: Vec = Vec::new();
    
            for _ in 0..n {
                loop {
                    if let PromptResult::Normal(name) = util::prompt(None, "?") {
                        let name = name.trim().to_uppercase();
    
                        if name.is_empty() {
                            println!("NAME CAN'T BE EMPTY!");
                        } else if let Some(_) = players.iter().find(|p| p.name == name) {
                            println!("THERE IS ALREADY A PLAYER WITH THAT NAME!");
                        } else {
                            players.push(Player::new(name));
                            break;
                        }
                    }
                }
            }
    
            players
        }
    
        pub fn process_winner(&mut self, no: u8) {
            println!();
            for p in self.players.iter_mut() {
                if !p.playing {
                    continue;
                }
    
                if p.horse_no == no {
                    p.money += p.bet;
                    println!("{} WON ${}! THEY HAVE ${}.", p.name, p.bet, p.money);
                } else {
                    p.money -= p.bet;
                    println!("{} LOST ${}. THEY HAVE ${} LEFT.", p.name, p.bet, p.money);
                }
                p.bet = 0;
                p.horse_no = 0;
            }
            println!();
        }
    
        pub fn prompt_next_round(&mut self) -> bool {
            for p in self.players.iter_mut() {
                let msg = format!("{}, DO YOU WANT TO BET ON THE NEXT RACE?", p.name);
                if let PromptResult::YesNo(yes) = util::prompt(Some(false), msg.as_str()) {
                    p.playing = yes;
                }
            }
    
            if let None = self.players.iter().find(|p| p.playing) {
                return false;
            }
    
            true
        }
    }
    
    
    ================================================
    FILE: 50_Horserace/rust/src/util.rs
    ================================================
    use std::io;
    
    pub enum PromptResult {
        Normal(String),
        YesNo(bool),
        Numeric(i32),
    }
    
    pub fn prompt(is_numeric: Option, msg: &str) -> PromptResult {
        use PromptResult::*;
    
        println!("{msg}");
    
        loop {
            let mut input = String::new();
    
            io::stdin()
                .read_line(&mut input)
                .expect("Failed to read input.");
    
            if let Some(is_numeric) = is_numeric {
                let input = input.trim();
    
                if is_numeric {
                    if let Ok(n) = input.parse::() {
                        return Numeric(n);
                    }
                    println!("PLEASE ENTER A VALID NUMBER!");
                } else {
                    match input.to_uppercase().as_str() {
                        "YES" | "Y" => return YesNo(true),
                        "NO" | "N" => return YesNo(false),
                        _ => println!("PLEASE ENTER (Y)ES OR (N)O."),
                    }
                }
            } else {
                return Normal(input);
            }
        }
    }
    
    
    ================================================
    FILE: 50_Horserace/vbnet/Horserace.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Horserace", "Horserace.vbproj", "{B44BF36F-DF93-4374-A3A0-C6D447B4D0A7}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{B44BF36F-DF93-4374-A3A0-C6D447B4D0A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{B44BF36F-DF93-4374-A3A0-C6D447B4D0A7}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{B44BF36F-DF93-4374-A3A0-C6D447B4D0A7}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{B44BF36F-DF93-4374-A3A0-C6D447B4D0A7}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 50_Horserace/vbnet/Horserace.vbproj
    ================================================
    
      
        Exe
        Horserace
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 50_Horserace/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 51_Hurkle/README.md
    ================================================
    ### Hurkle
    
    Hurkle? A Hurkle is a happy beast and lives in another galaxy on a planet named Lirht that has three moons. Hurkle are favorite pets of the Gwik, the dominant race of Lihrt and … well, to find out more, read “The Hurkle is a Happy Beast,” a story in the book _A Way Home_ by Theodore Sturgeon.
    
    In this program a shy hurkle is hiding on a 10 by 10 grid. Homebase is point 0,0 in the _Southwest_ corner. Your guess as to the gridpoint where the hurkle is hiding should be a pair of whole numbers, separated by a comma. After each try, the computer will tell you the approximate direction to go look for the Hurkle. You get five guesses to find him; you may change this number, although four guesses is actually enough.
    
    This program was written by Bob Albrecht of People’s Computer Company.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=94)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=109)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 51_Hurkle/csharp/.gitignore
    ================================================
    
    # Created by https://www.toptal.com/developers/gitignore/api/csharp
    # Edit at https://www.toptal.com/developers/gitignore?templates=csharp
    
    ### Csharp ###
    ## Ignore Visual Studio temporary files, build results, and
    ## files generated by popular Visual Studio add-ons.
    ##
    ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
    
    # User-specific files
    *.rsuser
    *.suo
    *.user
    *.userosscache
    *.sln.docstates
    
    # User-specific files (MonoDevelop/Xamarin Studio)
    *.userprefs
    
    # Mono auto generated files
    mono_crash.*
    
    # Build results
    [Dd]ebug/
    [Dd]ebugPublic/
    [Rr]elease/
    [Rr]eleases/
    x64/
    x86/
    [Aa][Rr][Mm]/
    [Aa][Rr][Mm]64/
    bld/
    [Bb]in/
    [Oo]bj/
    [Ll]og/
    [Ll]ogs/
    
    # Visual Studio 2015/2017 cache/options directory
    .vs/
    # Uncomment if you have tasks that create the project's static files in wwwroot
    #wwwroot/
    
    # Visual Studio 2017 auto generated files
    Generated\ Files/
    
    # MSTest test Results
    [Tt]est[Rr]esult*/
    [Bb]uild[Ll]og.*
    
    # NUnit
    *.VisualState.xml
    TestResult.xml
    nunit-*.xml
    
    # Build Results of an ATL Project
    [Dd]ebugPS/
    [Rr]eleasePS/
    dlldata.c
    
    # Benchmark Results
    BenchmarkDotNet.Artifacts/
    
    # .NET Core
    project.lock.json
    project.fragment.lock.json
    artifacts/
    
    # StyleCop
    StyleCopReport.xml
    
    # Files built by Visual Studio
    *_i.c
    *_p.c
    *_h.h
    *.ilk
    *.meta
    *.obj
    *.iobj
    *.pch
    *.pdb
    *.ipdb
    *.pgc
    *.pgd
    *.rsp
    *.sbr
    *.tlb
    *.tli
    *.tlh
    *.tmp
    *.tmp_proj
    *_wpftmp.csproj
    *.log
    *.vspscc
    *.vssscc
    .builds
    *.pidb
    *.svclog
    *.scc
    
    # Chutzpah Test files
    _Chutzpah*
    
    # Visual C++ cache files
    ipch/
    *.aps
    *.ncb
    *.opendb
    *.opensdf
    *.sdf
    *.cachefile
    *.VC.db
    *.VC.VC.opendb
    
    # Visual Studio profiler
    *.psess
    *.vsp
    *.vspx
    *.sap
    
    # Visual Studio Trace Files
    *.e2e
    
    # TFS 2012 Local Workspace
    $tf/
    
    # Guidance Automation Toolkit
    *.gpState
    
    # ReSharper is a .NET coding add-in
    _ReSharper*/
    *.[Rr]e[Ss]harper
    *.DotSettings.user
    
    # TeamCity is a build add-in
    _TeamCity*
    
    # DotCover is a Code Coverage Tool
    *.dotCover
    
    # AxoCover is a Code Coverage Tool
    .axoCover/*
    !.axoCover/settings.json
    
    # Coverlet is a free, cross platform Code Coverage Tool
    coverage*[.json, .xml, .info]
    
    # Visual Studio code coverage results
    *.coverage
    *.coveragexml
    
    # NCrunch
    _NCrunch_*
    .*crunch*.local.xml
    nCrunchTemp_*
    
    # MightyMoose
    *.mm.*
    AutoTest.Net/
    
    # Web workbench (sass)
    .sass-cache/
    
    # Installshield output folder
    [Ee]xpress/
    
    # DocProject is a documentation generator add-in
    DocProject/buildhelp/
    DocProject/Help/*.HxT
    DocProject/Help/*.HxC
    DocProject/Help/*.hhc
    DocProject/Help/*.hhk
    DocProject/Help/*.hhp
    DocProject/Help/Html2
    DocProject/Help/html
    
    # Click-Once directory
    publish/
    
    # Publish Web Output
    *.[Pp]ublish.xml
    *.azurePubxml
    # Note: Comment the next line if you want to checkin your web deploy settings,
    # but database connection strings (with potential passwords) will be unencrypted
    *.pubxml
    *.publishproj
    
    # Microsoft Azure Web App publish settings. Comment the next line if you want to
    # checkin your Azure Web App publish settings, but sensitive information contained
    # in these scripts will be unencrypted
    PublishScripts/
    
    # NuGet Packages
    *.nupkg
    # NuGet Symbol Packages
    *.snupkg
    # The packages folder can be ignored because of Package Restore
    **/[Pp]ackages/*
    # except build/, which is used as an MSBuild target.
    !**/[Pp]ackages/build/
    # Uncomment if necessary however generally it will be regenerated when needed
    #!**/[Pp]ackages/repositories.config
    # NuGet v3's project.json files produces more ignorable files
    *.nuget.props
    *.nuget.targets
    
    # Microsoft Azure Build Output
    csx/
    *.build.csdef
    
    # Microsoft Azure Emulator
    ecf/
    rcf/
    
    # Windows Store app package directories and files
    AppPackages/
    BundleArtifacts/
    Package.StoreAssociation.xml
    _pkginfo.txt
    *.appx
    *.appxbundle
    *.appxupload
    
    # Visual Studio cache files
    # files ending in .cache can be ignored
    *.[Cc]ache
    # but keep track of directories ending in .cache
    !?*.[Cc]ache/
    
    # Others
    ClientBin/
    ~$*
    *~
    *.dbmdl
    *.dbproj.schemaview
    *.jfm
    *.pfx
    *.publishsettings
    orleans.codegen.cs
    
    # Including strong name files can present a security risk
    # (https://github.com/github/gitignore/pull/2483#issue-259490424)
    #*.snk
    
    # Since there are multiple workflows, uncomment next line to ignore bower_components
    # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
    #bower_components/
    
    # RIA/Silverlight projects
    Generated_Code/
    
    # Backup & report files from converting an old project file
    # to a newer Visual Studio version. Backup files are not needed,
    # because we have git ;-)
    _UpgradeReport_Files/
    Backup*/
    UpgradeLog*.XML
    UpgradeLog*.htm
    ServiceFabricBackup/
    *.rptproj.bak
    
    # SQL Server files
    *.mdf
    *.ldf
    *.ndf
    
    # Business Intelligence projects
    *.rdl.data
    *.bim.layout
    *.bim_*.settings
    *.rptproj.rsuser
    *- [Bb]ackup.rdl
    *- [Bb]ackup ([0-9]).rdl
    *- [Bb]ackup ([0-9][0-9]).rdl
    
    # Microsoft Fakes
    FakesAssemblies/
    
    # GhostDoc plugin setting file
    *.GhostDoc.xml
    
    # Node.js Tools for Visual Studio
    .ntvs_analysis.dat
    node_modules/
    
    # Visual Studio 6 build log
    *.plg
    
    # Visual Studio 6 workspace options file
    *.opt
    
    # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
    *.vbw
    
    # Visual Studio LightSwitch build output
    **/*.HTMLClient/GeneratedArtifacts
    **/*.DesktopClient/GeneratedArtifacts
    **/*.DesktopClient/ModelManifest.xml
    **/*.Server/GeneratedArtifacts
    **/*.Server/ModelManifest.xml
    _Pvt_Extensions
    
    # Paket dependency manager
    .paket/paket.exe
    paket-files/
    
    # FAKE - F# Make
    .fake/
    
    # CodeRush personal settings
    .cr/personal
    
    # Python Tools for Visual Studio (PTVS)
    __pycache__/
    *.pyc
    
    # Cake - Uncomment if you are using it
    # tools/**
    # !tools/packages.config
    
    # Tabs Studio
    *.tss
    
    # Telerik's JustMock configuration file
    *.jmconfig
    
    # BizTalk build output
    *.btp.cs
    *.btm.cs
    *.odx.cs
    *.xsd.cs
    
    # OpenCover UI analysis results
    OpenCover/
    
    # Azure Stream Analytics local run output
    ASALocalRun/
    
    # MSBuild Binary and Structured Log
    *.binlog
    
    # NVidia Nsight GPU debugger configuration file
    *.nvuser
    
    # MFractors (Xamarin productivity tool) working folder
    .mfractor/
    
    # Local History for Visual Studio
    .localhistory/
    
    # BeatPulse healthcheck temp database
    healthchecksdb
    
    # Backup folder for Package Reference Convert tool in Visual Studio 2017
    MigrationBackup/
    
    # Ionide (cross platform F# VS Code tools) working folder
    .ionide/
    
    # End of https://www.toptal.com/developers/gitignore/api/csharp
    
    
    ================================================
    FILE: 51_Hurkle/csharp/CardinalDirection.cs
    ================================================
    namespace hurkle
    {
        internal enum CardinalDirection
        {
            None,
            North,
            NorthEast,
            East,
            SouthEast,
            South,
            SouthWest,
            West,
            NorthWest
        }
    }
    
    
    ================================================
    FILE: 51_Hurkle/csharp/ConsoleHurkleView.cs
    ================================================
    using System;
    
    namespace hurkle
    {
        internal class ConsoleHurkleView : IHurkleView
        {
            public GamePoint GetGuess(GuessViewModel guessViewModel)
            {
                Console.WriteLine($"GUESS #{guessViewModel.CurrentGuessNumber}");
                var inputLine = Console.ReadLine();
                var seperateStrings = inputLine.Split(',', 2, StringSplitOptions.TrimEntries);
                var guessPoint = new GamePoint{
                    X = int.Parse(seperateStrings[0]),
                    Y = int.Parse(seperateStrings[1])
                };
    
                return guessPoint;
            }
    
            public void ShowDirection(FailedGuessViewModel failedGuessViewModel)
            {
                Console.Write("GO ");
                switch(failedGuessViewModel.Direction)
                {
                    case CardinalDirection.East:
                        Console.WriteLine("EAST");
                        break;
                    case CardinalDirection.North:
                        Console.WriteLine("NORTH");
                        break;
                    case CardinalDirection.South:
                        Console.WriteLine("SOUTH");
                        break;
                    case CardinalDirection.West:
                        Console.WriteLine("WEST");
                        break;
                    case CardinalDirection.NorthEast:
                        Console.WriteLine("NORTHEAST");
                        break;
                    case CardinalDirection.NorthWest:
                        Console.WriteLine("NORTHWEST");
                        break;
                    case CardinalDirection.SouthEast:
                        Console.WriteLine("SOUTHEAST");
                        break;
                    case CardinalDirection.SouthWest:
                        Console.WriteLine("SOUTHWEST");
                        break;
                }
    
                Console.WriteLine();
            }
    
            public void ShowLoss(LossViewModel lossViewModel)
            {
                Console.WriteLine();
                Console.WriteLine($"SORRY, THAT'S {lossViewModel.MaxGuesses} GUESSES");
                Console.WriteLine($"THE HURKLE IS AT {lossViewModel.HurkleLocation.X},{lossViewModel.HurkleLocation.Y}");
            }
    
            public void ShowVictory(VictoryViewModel victoryViewModel)
            {
                Console.WriteLine();
                Console.WriteLine($"YOU FOUND HIM IN {victoryViewModel.CurrentGuessNumber} GUESSES!");
            }
        }
    }
    
    
    ================================================
    FILE: 51_Hurkle/csharp/FailedGuessViewModel.cs
    ================================================
    namespace hurkle
    {
        internal class FailedGuessViewModel
        {
            public CardinalDirection Direction { get; init; }
        }
    }
    
    
    ================================================
    FILE: 51_Hurkle/csharp/GamePoint.cs
    ================================================
    namespace hurkle
    {
        internal class GamePoint
        {
            public int X {get;init;}
            public int Y {get;init;}
    
            public CardinalDirection GetDirectionTo(GamePoint target)
            {
                if(X == target.X)
                {
                    if(Y > target.Y)
                    {
                        return CardinalDirection.South;
                    }
                    else if(Y < target.Y)
                    {
                        return CardinalDirection.North;
                    }
                    else
                    {
                        return CardinalDirection.None;
                    }
                }
                else if(X > target.X)
                {
                    if(Y == target.Y)
                    {
                        return CardinalDirection.West;
                    }
                    else if(Y > target.Y)
                    {
                        return CardinalDirection.SouthWest;
                    }
                    else
                    {
                        return CardinalDirection.NorthWest;
                    }
                }
                else
                {
                    if(Y == target.Y)
                    {
                        return CardinalDirection.East;
                    }
                    else if(Y > target.Y)
                    {
                        return CardinalDirection.SouthEast;
                    }
                    else{
                        return CardinalDirection.NorthEast;
                    }
                }
            }
        }
    }
    
    
    ================================================
    FILE: 51_Hurkle/csharp/GuessViewModel.cs
    ================================================
    namespace hurkle
    {
        internal class GuessViewModel
        {
            public int CurrentGuessNumber {get;init;}
        }
    }
    
    
    ================================================
    FILE: 51_Hurkle/csharp/Hurkle.csproj
    ================================================
    
    
      
        Exe
        net5.0
      
    
    
    
    
    ================================================
    FILE: 51_Hurkle/csharp/Hurkle.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Hurkle", "Hurkle.csproj", "{BE321D5B-93BD-4F91-A875-564DC9D4094F}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{BE321D5B-93BD-4F91-A875-564DC9D4094F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{BE321D5B-93BD-4F91-A875-564DC9D4094F}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{BE321D5B-93BD-4F91-A875-564DC9D4094F}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{BE321D5B-93BD-4F91-A875-564DC9D4094F}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {42DC6AE5-5127-4B1B-BD5E-F3B1CCDC3822}
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 51_Hurkle/csharp/HurkleGame.cs
    ================================================
    using System;
    
    namespace hurkle
    {
        internal class HurkleGame
        {
            private readonly Random _random = new Random();
            private readonly IHurkleView _view;
            private readonly int guesses;
            private readonly int gridSize;
    
            public HurkleGame(int guesses, int gridSize, IHurkleView view)
            {
                _view = view;
                this.guesses = guesses;
                this.gridSize = gridSize;
            }
    
            public void PlayGame()
            {
                // BASIC program was generating a float between 0 and 1
                // then multiplying by the size of the grid to to a number
                // between 1 and 10. C# allows you to do that directly.
                var hurklePoint = new GamePoint{
                    X = _random.Next(0, gridSize),
                    Y = _random.Next(0, gridSize)
                };
    
                for(var K=1;K<=guesses;K++)
                {
                    var guessPoint = _view.GetGuess(new GuessViewModel{CurrentGuessNumber = K});
    
                    var direction = guessPoint.GetDirectionTo(hurklePoint);
                    switch(direction)
                    {
                        case CardinalDirection.None:
                            _view.ShowVictory(new VictoryViewModel{CurrentGuessNumber = K});
                            return;
                        default:
                            _view.ShowDirection(new FailedGuessViewModel{Direction = direction});
                            continue;
                    }
                }
    
                _view.ShowLoss(new LossViewModel{MaxGuesses = guesses, HurkleLocation = hurklePoint } );
            }
        }
    }
    
    
    ================================================
    FILE: 51_Hurkle/csharp/IHurkleView.cs
    ================================================
    namespace hurkle
    {
        internal interface IHurkleView
        {
            GamePoint GetGuess(GuessViewModel guessViewModel);
            void ShowVictory(VictoryViewModel victoryViewModel);
            void ShowDirection(FailedGuessViewModel failedGuessViewModel);
            void ShowLoss(LossViewModel lossViewModel);
        }
    }
    
    
    ================================================
    FILE: 51_Hurkle/csharp/LossViewModel.cs
    ================================================
    namespace hurkle
    {
        internal class LossViewModel
        {
            public int MaxGuesses { get; init; }
            public GamePoint HurkleLocation { get; init; }
        }
    }
    
    
    ================================================
    FILE: 51_Hurkle/csharp/Program.cs
    ================================================
    using System;
    
    namespace hurkle
    {
        class Program
        {
            static void Main(string[] args)
            {
                /*
                Original source transscription
                10 PRINT TAB(33);"HURKLE"
                20 PRINT TAB(15);"CREATIVE COMPUTING NORRISTOWN, NEW JERSEY"
                30 PRINT;PRINT;PRINT
                */
                Console.WriteLine(new string(' ', 33) + @"HURKLE");
                Console.WriteLine(new string(' ', 15) + @"CREATIVE COMPUTING NORRISTOWN, NEW JERSEY");
                /*
                110 N=5
                120 G=10
                */
                var N=5;
                var G=10;
                /*
                210 PRINT
                220 PRINT "A HURKLE IS HIDING ON A";G;"BY";G;"GRID. HOMEBASE"
                230 PRINT "ON THE GRID IS POINT 0,0 AND ANY GRIDPOINT IS A"
                240 PRINT "PAIR OF WHOLE NUMBERS SEPERATED BY A COMMA. TRY TO"
                250 PRINT "GUESS THE HURKLE'S GRIDPOINT. YOU GET";N;"TRIES."
                260 PRINT "AFTER EACH TRY, I WILL TELL YOU THE APPROXIMATE"
                270 PRINT "DIRECTION TO GO TO LOOK FOR THE HURKLE."
                280 PRINT
                */
                // Using string formatting via the '$' string
                Console.WriteLine();
                Console.WriteLine($"A HURKLE IS HIDING ON A {G} BY {G} GRID. HOMEBASE");
                Console.WriteLine(@"ON THE GRID IS POINT 0,0 AND ANY GRIDPOINT IS A");
                Console.WriteLine(@"PAIR OF WHOLE NUMBERS SEPERATED BY A COMMA. TRY TO");
                Console.WriteLine($"GUESS THE HURKLE'S GRIDPOINT. YOU GET {N} TRIES.");
                Console.WriteLine(@"AFTER EACH TRY, I WILL TELL YOU THE APPROXIMATE");
                Console.WriteLine(@"DIRECTION TO GO TO LOOK FOR THE HURKLE.");
                Console.WriteLine();
    
                var view = new ConsoleHurkleView();
                var hurkle = new HurkleGame(N,G, view);
                while(true)
                {
                    hurkle.PlayGame();
    
                    Console.WriteLine("PLAY AGAIN? (Y)ES/(N)O");
                    var playAgainResponse = Console.ReadLine();
                    if(playAgainResponse.Trim().StartsWith("y", StringComparison.InvariantCultureIgnoreCase))
                    {
                        Console.WriteLine();
                        Console.WriteLine("LET'S PLAY AGAIN. HURKLE IS HIDING");
                        Console.WriteLine();
                    }else{
                        Console.WriteLine("THANKS FOR PLAYING!");
                        break;
                    }
    
                }
            }
        }
    }
    
    
    ================================================
    FILE: 51_Hurkle/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    This is demonstrating seperating the user interface from the application logic through the
    use of the View/ViewModel/Controller pattern.
    
    It also makes an effort to be relatively immutable.
    
    
    ================================================
    FILE: 51_Hurkle/csharp/VictoryViewModel.cs
    ================================================
    namespace hurkle
    {
        internal class VictoryViewModel
        {
            public int CurrentGuessNumber {get; init;}
        }
    }
    
    
    ================================================
    FILE: 51_Hurkle/hurkle.bas
    ================================================
    10 PRINT TAB(33);"HURKLE"
    20 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    30 PRINT:PRINT:PRINT
    110 N=5
    120 G=10
    210 PRINT
    220 PRINT "A HURKLE IS HIDING ON A";G;"BY";G;"GRID. HOMEBASE"
    230 PRINT "ON THE GRID IS POINT 0,0 IN THE SOUTHWEST CORNER,"
    235 PRINT "AND ANY POINT ON THE GRID IS DESIGNATED BY A"
    240 PRINT "PAIR OF WHOLE NUMBERS SEPERATED BY A COMMA. THE FIRST"
    245 PRINT "NUMBER IS THE HORIZONTAL POSITION AND THE SECOND NUMBER"
    246 PRINT "IS THE VERTICAL POSITION. YOU MUST TRY TO"
    250 PRINT "GUESS THE HURKLE'S GRIDPOINT. YOU GET";N;"TRIES."
    260 PRINT "AFTER EACH TRY, I WILL TELL YOU THE APPROXIMATE"
    270 PRINT "DIRECTION TO GO TO LOOK FOR THE HURKLE."
    280 PRINT
    285 A=INT(G*RND(1))
    286 B=INT(G*RND(1))
    310 FOR K=1 TO N
    320 PRINT "GUESS #";K;
    330 INPUT X,Y
    340 IF ABS(X-A)+ABS(Y-B)=0 THEN 500
    350 REM PRINT INFO
    360 GOSUB 610
    370 PRINT
    380 NEXT K
    410 PRINT
    420 PRINT "SORRY, THAT'S";N;"GUESSES."
    430 PRINT "THE HURKLE IS AT ";A;",";B
    440 PRINT
    450 PRINT "LET'S PLAY AGAIN, HURKLE IS HIDING."
    460 PRINT
    470 GOTO 285
    500 REM
    510 PRINT
    520 PRINT "YOU FOUND HIM IN";K;"GUESSES!"
    540 GOTO 440
    610 PRINT "GO ";
    620 IF Y=B THEN 670
    630 IF Y
     * Based on the Basic game of Hurkle here
     * https://github.com/coding-horror/basic-computer-games/blob/main/51%20Hurkle/hurkle.bas
     * 

    * Note: The idea was to create a version of the 1970's Basic game in Java, without introducing * new features - no additional text, error checking, etc has been added. */ public class Hurkle { public static final int GRID_SIZE = 10; public static final int MAX_GUESSES = 5; private enum GAME_STATE { STARTING, START_GAME, GUESSING, PLAY_AGAIN, GAME_OVER } private GAME_STATE gameState; // Used for keyboard input private final Scanner kbScanner; private int guesses; // hurkle position private int hurkleXPos; private int hurkleYPos; // player guess private int playerGuessXPos; private int playerGuessYPos; public Hurkle() { gameState = GAME_STATE.STARTING; // Initialise kb scanner kbScanner = new Scanner(System.in); } /** * Main game loop */ public void play() { do { switch (gameState) { // Show an introduction the first time the game is played. case STARTING: intro(); gameState = GAME_STATE.START_GAME; break; // Start the game, set the number of players, names and round case START_GAME: hurkleXPos = randomNumber(); hurkleYPos = randomNumber(); guesses = 1; gameState = GAME_STATE.GUESSING; break; // Guess an x,y position of the hurkle case GUESSING: String guess = displayTextAndGetInput("GUESS #" + guesses + "? "); playerGuessXPos = getDelimitedValue(guess, 0); playerGuessYPos = getDelimitedValue(guess, 1); if (foundHurkle()) { gameState = GAME_STATE.PLAY_AGAIN; } else { showDirectionOfHurkle(); guesses++; if (guesses > MAX_GUESSES) { System.out.println("SORRY, THAT'S " + MAX_GUESSES + " GUESSES."); System.out.println("THE HURKLE IS AT " + hurkleXPos + "," + hurkleYPos); System.out.println(); gameState = GAME_STATE.PLAY_AGAIN; } } break; case PLAY_AGAIN: System.out.println("LET'S PLAY AGAIN, HURKLE IS HIDING."); System.out.println(); gameState = GAME_STATE.START_GAME; break; } // Effectively an endless loop because the game never quits as per // the original basic code. } while (gameState != GAME_STATE.GAME_OVER); } private void showDirectionOfHurkle() { System.out.print("GO "); if (playerGuessYPos == hurkleYPos) { // don't print North or South because the player has chosen the // same y grid pos as the hurkle } else if (playerGuessYPos < hurkleYPos) { System.out.print("NORTH"); } else if (playerGuessYPos > hurkleYPos) { System.out.print("SOUTH"); } if (playerGuessXPos == hurkleXPos) { // don't print East or West because the player has chosen the // same x grid pos as the hurkle } else if (playerGuessXPos < hurkleXPos) { System.out.print("EAST"); } else if (playerGuessXPos > hurkleXPos) { System.out.print("WEST"); } System.out.println(); } private boolean foundHurkle() { if ((playerGuessXPos - hurkleXPos) - (playerGuessYPos - hurkleYPos) == 0) { System.out.println("YOU FOUND HIM IN " + guesses + " GUESSES."); return true; } return false; } /** * Display info about the game */ private void intro() { System.out.println("HURKLE"); System.out.println("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println(); System.out.println("A HURKLE IS HIDING ON A " + GRID_SIZE + " BY " + GRID_SIZE + " GRID. HOMEBASE"); System.out.println("ON THE GRID IS POINT 0,0 IN THE SOUTHWEST CORNER,"); System.out.println("AND ANY POINT ON THE GRID IS DESIGNATED BY A"); System.out.println("PAIR OF WHOLE NUMBERS SEPERATED BY A COMMA. THE FIRST"); System.out.println("NUMBER IS THE HORIZONTAL POSITION AND THE SECOND NUMBER"); System.out.println("IS THE VERTICAL POSITION. YOU MUST TRY TO"); System.out.println("GUESS THE HURKLE'S GRIDPOINT. YOU GET " + MAX_GUESSES + " TRIES."); System.out.println("AFTER EACH TRY, I WILL TELL YOU THE APPROXIMATE"); System.out.println("DIRECTION TO GO TO LOOK FOR THE HURKLE."); } /** * Generate random number * Used to create one part of an x,y grid position * * @return random number */ private int randomNumber() { return (int) (Math.random() * (GRID_SIZE) + 1); } /* * Print a message on the screen, then accept input from Keyboard. * * @param text message to be displayed on screen. * @return what was typed by the player. */ private String displayTextAndGetInput(String text) { System.out.print(text); return kbScanner.next(); } /** * Accepts a string delimited by comma's and returns the pos'th delimited * value (starting at count 0). * * @param text - text with values separated by comma's * @param pos - which position to return a value for * @return the int representation of the value */ private int getDelimitedValue(String text, int pos) { String[] tokens = text.split(","); return Integer.parseInt(tokens[pos]); } } ================================================ FILE: 51_Hurkle/java/src/HurkleGame.java ================================================ public class HurkleGame { public static void main(String[] args) { Hurkle hurkle = new Hurkle(); hurkle.play(); } } ================================================ FILE: 51_Hurkle/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 51_Hurkle/javascript/hurkle.html ================================================ HURKLE

    
    
    
    
    
    
    ================================================
    FILE: 51_Hurkle/javascript/hurkle.js
    ================================================
    // BATNUM
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    // Main program
    async function main()
    {
        print(tab(33) + "HURKLE\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        n = 5;
        g = 10;
        print("\n");
        print("A HURKLE IS HIDING ON A " + g + " BY " + g + " GRID. HOMEBASE\n");
        print("ON THE GRID IS POINT 0,0 IN THE SOUTHWEST CORNER,\n");
        print("AND ANY POINT ON THE GRID IS DESIGNATED BY A\n");
        print("PAIR OF WHOLE NUMBERS SEPERATED BY A COMMA. THE FIRST\n");
        print("NUMBER IS THE HORIZONTAL POSITION AND THE SECOND NUMBER\n");
        print("IS THE VERTICAL POSITION. YOU MUST TRY TO\n");
        print("GUESS THE HURKLE'S GRIDPOINT. YOU GET " + n + " TRIES.\n");
        print("AFTER EACH TRY, I WILL TELL YOU THE APPROXIMATE\n");
        print("DIRECTION TO GO TO LOOK FOR THE HURKLE.\n");
        print("\n");
        while (1) {
            a = Math.floor(g * Math.random());
            b = Math.floor(g * Math.random());
            for (k = 1; k <= n; k++) {
                print("GUESS #" + k + " ");
                str = await input();
                x = parseInt(str);
                y = parseInt(str.substr(str.indexOf(",") + 1));
                if (x == a && y == b) {
                    print("\n");
                    print("YOU FOUND HIM IN " + k + " GUESSES!\n");
                    break;
                }
                print("GO ");
                if (y < b) {
                    print("NORTH");
                } else if (y > b) {
                    print("SOUTH");
                }
                if (x < a) {
                    print("EAST\n");
                } else {
                    print("WEST\n");
                }
            }
            if (k > n) {
                print("\n");
                print("SORRY, THAT'S " + n + " GUESSES.\n");
                print("THE HURKLE IS AT " + a + "," + b + "\n");
            }
            print("\n");
            print("LET'S PLAY AGAIN, HURKLE IS HIDING.\n");
            print("\n");
        }
    }
    
    main();
    
    
    ================================================
    FILE: 51_Hurkle/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 51_Hurkle/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 51_Hurkle/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 51_Hurkle/perl/hurkle.pl
    ================================================
    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    # global variables
    
    my($GRID)  = 10;
    my($TRIES) = 5;
    
    
    # main program starts here
    
    # print instructions
    print <);
                # Use a regex to attempt to parse out
                # two integers separated by a comma.
                if ($in =~ m{(\d+)\s*,\s*(\d+)}) {
                    $G1 = $1; $G2 = $2;
                    last CHECK;
                }
                # Input not accepted, please try again
                print "Please enter two numbers separated by a comma ? ";
            }
    
            if (abs($H1 - $G1) + abs($H2 - $G2) != 0) {
    
                # print directional info
                printf("Go %s%s\n\n",
                    ($G2 == $H2 ? '' : $G2 < $H2 ? 'north' : 'south'),
                    ($G1 == $H1 ? '' : $G1 < $H1 ? 'east'  : 'west' ),
                );
            } else {
                # win!
                printf("\nYou found him in %d tries!\n", $i);
                # move to the continue block
                next PLAY;
            }
        } # tries loop
    
        # No more guesses
        printf("Sorry, that's %d guesses.\n", $TRIES);
        printf("The Hurkle is at %d, %d\n", $H1, $H2);
    }
    
    # Execution comes here either from the "next PLAY"
    # statement, or by the PLAY block naturally ending
    # after the player has lost.
    continue {
        print "\nLet's play again. Hurkle is hiding.\n\n";
    }
    
    
    ================================================
    FILE: 51_Hurkle/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 51_Hurkle/python/hurkle.py
    ================================================
    #!/usr/bin/env python3
    
    """Ported to Python by @iamtraction"""
    
    from random import random
    
    
    def direction(A, B, X, Y) -> None:
        """Print the direction hint for finding the hurkle."""
    
        print("GO ", end="")
        if Y < B:
            print("NORTH", end="")
        elif Y > B:
            print("SOUTH", end="")
    
        if X < A:
            print("EAST", end="")
        elif X > A:
            print("WEST", end="")
    
        print()
    
    
    def main() -> None:
        print(" " * 33 + "HURKLE")
        print(" " * 15 + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY")
    
        print("\n\n\n")
    
        N = 5
        G = 10
    
        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()
    
        while True:
            A = int(G * random())
            B = int(G * random())
    
            for k in range(0, N):
                print("\nGUESS #" + str(k))
    
                # read coordinates in `X, Y` format, split the string
                # at `,`, and then parse the coordinates to `int` and
                # store them in `X` and `Y` respectively.
                [X, Y] = [int(c) for c in input("X,Y? ").split(",")]
    
                if abs(X - A) + abs(Y - B) == 0:
                    print("\nYOU FOUND HIM IN", k + 1, "GUESSES!")
                    break
                else:
                    direction(A, B, X, Y)
                    continue
    
            print("\n\nLET'S PLAY AGAIN, HURKLE IS HIDING.\n")
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 51_Hurkle/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 51_Hurkle/ruby/hurkle.rb
    ================================================
    MAX_GUESSES = 5
    GRID_SIZE = 10
    
    class Point < Object
      attr_accessor :x
      attr_accessor :y
    
      def initialize(text="")
        x, y = text.split(",").map(&:strip)
        @x = (x || rand(GRID_SIZE).floor).to_i
        @y = (y || rand(GRID_SIZE).floor).to_i
      end
    
      def to_s
        "#{@x}, #{@y}"
      end
    
      def ==(other_point)
        @x == other_point.x && @y == other_point.y
      end
    
      def direction_to(other_point)
        (  @y < other_point.y ? "NORTH" : "SOUTH" unless @y == other_point.y ).to_s +
          (@x < other_point.x ? "EAST"  : "WEST"  unless @x == other_point.x ).to_s
      end
    end
    
    def main
      say_introduction
    
      loop do
        hurkle_point = Point.new
        found = false
        (1..MAX_GUESSES).each do |guess_num|
          found = guess(hurkle_point, guess_num)
          break if found
        end
        say_failure(hurkle_point) if not found
        say_play_again
      end
    
    end
    
    def say_introduction
      puts " " * 33 + "HURKLE"
      puts " " * 15 + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
      3.times { puts }
      puts
      puts "A HURKLE IS HIDING ON A #{GRID_SIZE} BY #{GRID_SIZE} GRID. HOMEBASE"
      puts "ON THE GRID IS POINT 0,0 IN THE SOUTHWEST CORNER,"
      puts "AND ANY POINT ON THE GRID IS DESIGNATED BY A"
      puts "PAIR OF WHOLE NUMBERS SEPERATED BY A COMMA. THE FIRST"
      puts "NUMBER IS THE HORIZONTAL POSITION AND THE SECOND NUMBER"
      puts "IS THE VERTICAL POSITION. YOU MUST TRY TO"
      puts "GUESS THE HURKLE'S GRIDPOINT. YOU GET #{MAX_GUESSES} TRIES."
      puts "AFTER EACH TRY, I WILL TELL YOU THE APPROXIMATE"
      puts "DIRECTION TO GO TO LOOK FOR THE HURKLE."
      puts
    end
    
    def guess(hurkle_point, guess_num)
      print "GUESS # #{guess_num} ? "
      guess_point = Point.new(gets.chomp)
      if guess_point == hurkle_point
        say_success(guess_num)
        true
      else
        say_where_to_go(hurkle_point, guess_point)
        false
      end
    end
    
    def say_success(guess_num)
      puts
      puts "YOU FOUND IT IN #{guess_num} GUESSES!"
    end
    
    def say_where_to_go(hurkle_point, guess_point)
      puts "GO #{guess_point.direction_to(hurkle_point)}"
      puts
    end
    
    def say_failure(hurkle_point)
      puts
      puts "SORRY, THAT'S " + MAX_GUESSES.to_s + " GUESSES."
      puts "THE HURKLE IS AT #{hurkle_point}"
    end
    
    def say_play_again
      puts
      puts "LET'S PLAY AGAIN, HURKLE IS HIDING."
      puts
    end
    
    main
    
    
    ================================================
    FILE: 51_Hurkle/rust/Cargo.toml
    ================================================
    [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"
    
    
    ================================================
    FILE: 51_Hurkle/rust/src/game.rs
    ================================================
    use std::io;
    
    use rand::Rng;
    
    type Position = (u8, u8);
    
    pub struct Game {
        hurkle: Position,
        tries: u8,
    }
    
    impl Game {
        pub fn new() -> Self {
            let x: u8 = rand::thread_rng().gen_range(1..=10);
            let y: u8 = rand::thread_rng().gen_range(1..=10);
            let hurkle = (x, y);
    
            Game { hurkle, tries: 0 }
        }
    
        pub fn update(&mut self) -> bool {
            if self.tries >= 5 {
                println!("SORRY, THAT'S {} GUESSES.", self.tries);
                println!("THE HURKLE IS AT {}, {}", self.hurkle.0, self.hurkle.1);
                return true;
            }
            self.tries += 1;
            self.process_guess(self.get_guess())
        }
    
        fn get_guess(&self) -> Position {
            let mut pos = (0, 0);
    
            'guess: loop {
                println!("GUESS # {}?", self.tries);
    
                let mut input = String::new();
    
                io::stdin()
                    .read_line(&mut input)
                    .expect("**Failed to read line**");
    
                let input: Vec<&str> = input.trim().split(",").collect();
    
                let mut is_y = false;
                for a in input {
                    match a.parse::() {
                        Ok(a) => {
                            if a > 10 || a == 0 {
                                println!("GUESS AXIS CANNOT BE ZERO OR LARGER THAN TEN!");
                                break;
                            }
                            if is_y {
                                pos.1 = a;
                                break 'guess;
                            } else {
                                pos.0 = a;
                                is_y = true;
                            }
                        }
                        Err(e) => println!("{} - TRY AGAIN!", e.to_string().to_uppercase()),
                    }
                }
            }
    
            pos
        }
    
        fn process_guess(&self, p: Position) -> bool {
            if p == self.hurkle {
                println!("\nYOU FOUND HIM IN {} GUESSES!", self.tries);
                return true;
            }
    
            let (x, y) = (p.0, p.1);
            let (hx, hy) = (self.hurkle.0, self.hurkle.1);
    
            let mut dir_x = "WEST";
            let mut dir_y = "SOUTH";
    
            let mut set_y_dir = || {
                if y < hy {
                    dir_y = "NORTH";
                } else {
                    dir_y = "";
                }
            };
    
            if x > hx {
                set_y_dir();
            } else if x < hx {
                dir_x = "EAST";
                set_y_dir();
            } else {
                dir_x = "";
                set_y_dir();
            }
    
            println!("GO {}{}\n", dir_y, dir_x);
    
            false
        }
    }
    
    
    ================================================
    FILE: 51_Hurkle/rust/src/main.rs
    ================================================
    use game::Game;
    
    mod game;
    
    fn main() {
        println!("\n\n\t\tHURKLE");
        println!("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n");
    
        println!("A HURKLE IS HIDING ON A 10 BY 10 GRID. HOMEBASE");
        println!("ON THE GRID IS POINT 0,0 IN THE SOUTHWEST CORNER,");
        println!("AND ANY POINT ON THE GRID IS DESIGNATED BY A");
        println!("PAIR OF WHOLE NUMBERS SEPERATED BY A COMMA. THE FIRST");
        println!("NUMBER IS THE HORIZONTAL POSITION AND THE SECOND NUMBER");
        println!("IS THE VERTICAL POSITION. YOU MUST TRY TO");
        println!("GUESS THE HURKLE'S GRIDPOINT. YOU GET 5 TRIES.");
        println!("AFTER EACH TRY, I WILL TELL YOU THE APPROXIMATE");
        println!("DIRECTION TO GO TO LOOK FOR THE HURKLE.\n");
    
        loop {
            let mut game = Game::new();
    
            loop {
                if game.update() {
                    println!("\nLET'S PLAY AGAIN. HURKLE IS HIDING.");
                    break;
                }
            }
        }
    }
    
    
    ================================================
    FILE: 51_Hurkle/vbnet/Hurkle.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Hurkle", "Hurkle.vbproj", "{63674AC0-0FE6-467F-B2D0-016105155ADE}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{63674AC0-0FE6-467F-B2D0-016105155ADE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{63674AC0-0FE6-467F-B2D0-016105155ADE}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{63674AC0-0FE6-467F-B2D0-016105155ADE}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{63674AC0-0FE6-467F-B2D0-016105155ADE}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 51_Hurkle/vbnet/Hurkle.vbproj
    ================================================
    
      
        Exe
        Hurkle
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 51_Hurkle/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 52_Kinema/README.md
    ================================================
    ### Kinema
    
    This program tests your fundamental knowledge of kinematics. It presents a simple problem: a ball is thrown straight up into the air at some random velocity. You then must answer three questions about the flight of the ball:
    1. How high will it go?
    2. How long until it returns to earth?
    3. What will be its velocity after a random number of seconds?
    
    The computer evaluates your performance; within 15% of the correct answer is considered close enough. After each run, the computer gives you another problem until you interrupt it.
    
    KINEMA was shorted from the original Huntington Computer Project Program, KINERV, by Richard Pav of Patchogue High School, Patchogue, New York.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=95)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=110)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 52_Kinema/csharp/Kinema.csproj
    ================================================
    
      
        Exe
        net6.0
        10
        enable
        enable
      
    
    
    
    ================================================
    FILE: 52_Kinema/csharp/Kinema.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kinema", "Kinema.csproj", "{FD7FF20E-F7A8-4372-BF7C-6898BDEF53D7}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{FD7FF20E-F7A8-4372-BF7C-6898BDEF53D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{FD7FF20E-F7A8-4372-BF7C-6898BDEF53D7}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{FD7FF20E-F7A8-4372-BF7C-6898BDEF53D7}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{FD7FF20E-F7A8-4372-BF7C-6898BDEF53D7}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 52_Kinema/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 52_Kinema/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 52_Kinema/java/src/Kinema.java
    ================================================
    import java.util.Arrays;
    import java.util.Scanner;
    
    /**
     * Game of Kinema
     * 

    * Based on the Basic game of Kinema here * https://github.com/coding-horror/basic-computer-games/blob/main/52%20Kinema/kinema.bas *

    * Note: The idea was to create a version of the 1970's Basic game in Java, without introducing * new features - no additional text, error checking, etc has been added. */ public class Kinema { // Used for keyboard input private final Scanner kbScanner; private enum GAME_STATE { STARTUP, INIT, HOW_HIGH, SECONDS_TILL_IT_RETURNS, ITS_VELOCITY, RESULTS, GAME_OVER } // Current game state private GAME_STATE gameState; private int numberAnswersCorrect; // How many meters per second a ball is thrown private int velocity; public Kinema() { kbScanner = new Scanner(System.in); gameState = GAME_STATE.STARTUP; } /** * Main game loop */ public void play() { double playerAnswer; double correctAnswer; do { switch (gameState) { case STARTUP: intro(); gameState = GAME_STATE.INIT; break; case INIT: numberAnswersCorrect = 0; // calculate a random velocity for the player to use in the calculations velocity = 5 + (int) (35 * Math.random()); System.out.println("A BALL IS THROWN UPWARDS AT " + velocity + " METERS PER SECOND."); gameState = GAME_STATE.HOW_HIGH; break; case HOW_HIGH: playerAnswer = displayTextAndGetNumber("HOW HIGH WILL IT GO (IN METERS)? "); // Calculate the correct answer to how high it will go correctAnswer = 0.05 * Math.pow(velocity, 2); if (calculate(playerAnswer, correctAnswer)) { numberAnswersCorrect++; } gameState = GAME_STATE.ITS_VELOCITY; break; case ITS_VELOCITY: playerAnswer = displayTextAndGetNumber("HOW LONG UNTIL IT RETURNS (IN SECONDS)? "); // Calculate current Answer for how long until it returns to the ground in seconds correctAnswer = (double) velocity / 5; if (calculate(playerAnswer, correctAnswer)) { numberAnswersCorrect++; } gameState = GAME_STATE.SECONDS_TILL_IT_RETURNS; break; case SECONDS_TILL_IT_RETURNS: // Calculate random number of seconds for 3rd question double seconds = 1 + (Math.random() * (2 * velocity)) / 10; // Round to one decimal place. double scale = Math.pow(10, 1); seconds = Math.round(seconds * scale) / scale; playerAnswer = displayTextAndGetNumber("WHAT WILL ITS VELOCITY BE AFTER " + seconds + " SECONDS? "); // Calculate the velocity after the given number of seconds correctAnswer = velocity - (10 * seconds); if (calculate(playerAnswer, correctAnswer)) { numberAnswersCorrect++; } gameState = GAME_STATE.RESULTS; break; case RESULTS: System.out.println(numberAnswersCorrect + " RIGHT OUT OF 3"); if (numberAnswersCorrect > 1) { System.out.println(" NOT BAD."); } gameState = GAME_STATE.STARTUP; break; } } while (gameState != GAME_STATE.GAME_OVER); } private void intro() { System.out.println(simulateTabs(33) + "KINEMA"); System.out.println(simulateTabs(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println(); } private boolean calculate(double playerAnswer, double correctAnswer) { boolean gotItRight = false; if (Math.abs((playerAnswer - correctAnswer) / correctAnswer) < 0.15) { System.out.println("CLOSE ENOUGH"); gotItRight = true; } else { System.out.println("NOT EVEN CLOSE"); } System.out.println("CORRECT ANSWER IS " + correctAnswer); System.out.println(); return gotItRight; } /* * Print a message on the screen, then accept input from Keyboard. * Converts input to a Double * * @param text message to be displayed on screen. * @return what was typed by the player. */ private double displayTextAndGetNumber(String text) { return Double.parseDouble(displayTextAndGetInput(text)); } /* * Print a message on the screen, then accept input from Keyboard. * * @param text message to be displayed on screen. * @return what was typed by the player. */ private String displayTextAndGetInput(String text) { System.out.print(text); return kbScanner.next(); } /** * Simulate the old basic tab(xx) command which indented text by xx spaces. * * @param spaces number of spaces required * @return String with number of spaces */ private String simulateTabs(int spaces) { char[] spacesTemp = new char[spaces]; Arrays.fill(spacesTemp, ' '); return new String(spacesTemp); } } ================================================ FILE: 52_Kinema/java/src/KinemaGame.java ================================================ public class KinemaGame { public static void main(String[] args) { Kinema kinema = new Kinema(); kinema.play(); } } ================================================ FILE: 52_Kinema/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 52_Kinema/javascript/kinema.html ================================================ KINEMA

    
    
    
    
    
    
    ================================================
    FILE: 52_Kinema/javascript/kinema.js
    ================================================
    // KINEMA
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    var q;
    
    function evaluate_answer(str, a)
    {
        g = parseFloat(str);
        if (Math.abs((g - a) / a) < 0.15) {
            print("CLOSE ENOUGH.\n");
            q++;
        } else {
            print("NOT EVEN CLOSE....\n");
        }
        print("CORRECT ANSWER IS " + a + "\n\n");
    }
    
    // Main program
    async function main()
    {
        print(tab(33) + "KINEMA\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        while (1) {
            print("\n");
            print("\n");
            q = 0;
            v = 5 + Math.floor(35 * Math.random());
            print("A BALL IS THROWN UPWARDS AT " + v + " METERS PER SECOND.\n");
            print("\n");
            a = 0.5 * Math.pow(v, 2);
            print("HOW HIGH WILL IT GO (IN METERS)");
            str = await input();
            evaluate_answer(str, a);
            a = v / 5;
            print("HOW LONG UNTIL IT RETURNS (IN SECONDS)");
            str = await input();
            evaluate_answer(str, a);
            t = 1 + Math.floor(2 * v * Math.random()) / 10;
            a = v - 10 * t;
            print("WHAT WILL ITS VELOCITY BE AFTER " + t + " SECONDS");
            str = await input();
            evaluate_answer(str, a);
            print("\n");
            print(q + " RIGHT OUT OF 3.");
            if (q < 2)
                continue;
            print("  NOT BAD.\n");
        }
    }
    
    main();
    
    
    ================================================
    FILE: 52_Kinema/kinema.bas
    ================================================
    10 PRINT TAB(33);"KINEMA"
    20 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    30 PRINT: PRINT: PRINT
    100 PRINT
    105 PRINT
    106 Q=0
    110 V=5+INT(35*RND(1))
    111 PRINT "A BALL IS THROWN UPWARDS AT";V;"METERS PER SECOND."
    112 PRINT
    115 A=.05*V^2
    116 PRINT "HOW HIGH WILL IT GO (IN METERS)";
    117 GOSUB 500
    120 A=V/5
    122 PRINT "HOW LONG UNTIL IT RETURNS (IN SECONDS)";
    124 GOSUB 500
    130 T=1+INT(2*V*RND(1))/10
    132 A=V-10*T
    134 PRINT "WHAT WILL ITS VELOCITY BE AFTER";T;"SECONDS";
    136 GOSUB 500
    140 PRINT
    150 PRINT Q;"RIGHT OUT OF 3.";
    160 IF Q<2 THEN 100
    170 PRINT "  NOT BAD."
    180 GOTO 100
    500 INPUT G
    502 IF ABS((G-A)/A)<.15 THEN 510
    504 PRINT "NOT EVEN CLOSE...."
    506 GOTO 512
    510 PRINT "CLOSE ENOUGH."
    511 Q=Q+1
    512 PRINT "CORRECT ANSWER IS ";A
    520 PRINT
    530 RETURN
    999 END
    
    
    ================================================
    FILE: 52_Kinema/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 52_Kinema/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 52_Kinema/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 52_Kinema/perl/kinema.pl
    ================================================
    #!/usr/bin/perl
    use strict;
    
    
    print ' 'x 33 . "KINEMA\n";
    print ' 'x 15 . "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n";
    print "\n\n\n";
    
    while (1) {
    	print "\n";
    	print "\n";
    	my $Q=0;
    	my $V=5+int(35*rand(1));
    	print "A BALL IS THROWN UPWARDS AT $V METERS PER SECOND.\n";
    	print "\n";
    
    	my $A=.05*$V^2;
    	print "HOW HIGH WILL IT GO (IN METERS)";
    	$Q+= &Input($A);
    
    	$A=$V/5;
    	print "HOW LONG UNTIL IT RETURNS (IN SECONDS)";
    	$Q+= &Input($A);
    
    	my $T=1+int(2*$V*rand(1))/10;
    	$A=$V-10*$T;
    	print "WHAT WILL ITS VELOCITY BE AFTER $T SECONDS";
    	$Q+= &Input($A);
    
    	print "\n";
    	print "$Q RIGHT OUT OF 3.";
    	if ($Q<2) { next; }
    	print " NOT BAD.\n";
    	}
    
    exit;
    
    
    #Line500:
    sub Input {
    	my ($A)= @_;
    	my $Point=0;
    	print "? "; chomp(my $G = );
    	if (abs(($G-$A)/$A)<.15) {
    		print "CLOSE ENOUGH.\n";
    		$Point=1;
    		} else {
    		print "NOT EVEN CLOSE....\n";
    		}
    	print "CORRECT ANSWER IS $A\n";
    	print "\n";
    	return $Point;
    	}
    
    
    ================================================
    FILE: 52_Kinema/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 52_Kinema/python/kinema.py
    ================================================
    """
    KINEMA
    
    A kinematics physics quiz.
    
    Ported by Dave LeCompte
    """
    
    import random
    
    # We approximate gravity from 9.8 meters/second squared to 10, which
    # is only off by about 2%. 10 is also a lot easier for people to use
    # for mental math.
    
    g = 10
    
    # We only expect the student to get within this percentage of the
    # correct answer. This isn't rocket science.
    
    EXPECTED_ACCURACY_PERCENT = 15
    
    
    def do_quiz() -> None:
        print()
        print()
        num_questions_correct = 0
    
        # pick random initial velocity
        v0 = random.randint(5, 40)
        print(f"A BALL IS THROWN UPWARDS AT {v0} METERS PER SECOND.")
        print()
    
        answer = v0**2 / (2 * g)
        num_questions_correct += ask_player("HOW HIGH WILL IT GO (IN METERS)?", answer)
    
        answer = 2 * v0 / g
        num_questions_correct += ask_player(
            "HOW LONG UNTIL IT RETURNS (IN SECONDS)?", answer
        )
    
        t = 1 + random.randint(0, 2 * v0) // g
        answer = v0 - g * t
        num_questions_correct += ask_player(
            f"WHAT WILL ITS VELOCITY BE AFTER {t} SECONDS?", answer
        )
    
        print()
        print(f"{num_questions_correct} right out of 3.")
        if num_questions_correct >= 2:
            print("  NOT BAD.")
    
    
    def ask_player(question: str, answer) -> int:
        print(question)
        player_answer = float(input())
    
        accuracy_frac = EXPECTED_ACCURACY_PERCENT / 100.0
        if abs((player_answer - answer) / answer) < accuracy_frac:
            print("CLOSE ENOUGH.")
            score = 1
        else:
            print("NOT EVEN CLOSE....")
            score = 0
        print(f"CORRECT ANSWER IS {answer}")
        print()
        return score
    
    
    def main() -> None:
        print(" " * 33 + "KINEMA")
        print(" " * 15 + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n\n\n")
    
        while True:
            do_quiz()
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 52_Kinema/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 52_Kinema/ruby/kinema.rb
    ================================================
    #!/usr/bin/env ruby
    
    # Kinema
    # reinterpreted from BASIC by stephan.com
    
    EPSILON = 0.15
    
    def close?(guess, answer)
      (guess-answer).abs < answer * EPSILON
    end
    
    def ask(text, answer)
      puts text
      guess = gets.strip.to_f
      if close?(guess, answer)
        puts 'Close enough'
        @score += 1
      else
        puts 'Not even close....'
      end
    
      puts "Correct answer is #{answer}"
    end
    
    puts 'Kinema'.center(80)
    puts 'Adapted by stephan.com'.center(80)
    puts; puts; puts;
    
    loop do
      puts; puts
      @score = 0
      v = 5 + rand(35)
    
      puts "A ball is thrown upwards at #{v} meters per second"
    
      ask 'How high will it go? (in meters)', 0.05 * v * v
      ask 'How long until it returns? (in seconds)', v/5.0
    
      t = 1 + rand(2*v)/10.0
      ask "What will its velocity be after #{t} seconds?", v - 10 * t
      puts
      print "#{@score} right out of 3."
      print " not bad" if @score > 1
      puts
    end
    
    
    ================================================
    FILE: 52_Kinema/rust/Cargo.toml
    ================================================
    [package]
    name = "rust"
    version = "0.1.0"
    edition = "2021"
    
    [dependencies]
    rand = "0.9.0"
    
    ================================================
    FILE: 52_Kinema/rust/src/main.rs
    ================================================
    /** 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
    }
    
    
    ================================================
    FILE: 52_Kinema/vbnet/Kinema.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Kinema", "Kinema.vbproj", "{C929831F-0B1C-4EE4-9BAA-001BE5CCC2E2}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{C929831F-0B1C-4EE4-9BAA-001BE5CCC2E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{C929831F-0B1C-4EE4-9BAA-001BE5CCC2E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{C929831F-0B1C-4EE4-9BAA-001BE5CCC2E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{C929831F-0B1C-4EE4-9BAA-001BE5CCC2E2}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 52_Kinema/vbnet/Kinema.vbproj
    ================================================
    
      
        Exe
        Kinema
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 52_Kinema/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 53_King/README.md
    ================================================
    ## King
    
    This is one of the most comprehensive, difficult, and interesting games. (If you've never played one of these games, start with HAMMURABI.)
    
    In this game, you are Premier of Setats Detinu, a small communist island 30 by 70 miles long. Your job is to decide upon the budget of your country and distribute money to your country from the communal treasury.
    
    The money system is Rollods; each person needs 100 Rallods per year to survive. Your country's income comes from farm produce and tourists visiting your magnificent forests, hunting, fishing, etc. Part of your land is farm land but it also has an excellent mineral content and may be sold to foreign industry for strip mining. Industry import and support their own workers. Crops cost between 10 and 15 Rallods per square mile to plant, cultivate, and harvest. Your goal is to complete an eight-year term of office without major mishap. A word of warning: it isn't easy!
    
    The author of this program is James A. Storer who wrote it while a student at Lexington High School.
    
    ⚠️ This game includes references to suicide or self-harm.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=96)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=111)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    Implementers should be aware that this game contains bugs.
    
    ### Bug 1
    
    On basic line 1450
    
        1450 V3=INT(A+V3)
        1451 A=INT(A+V3)
    
    ...where A is the current treasury, and V3 is initially zero.
    This would mean that the treasury doubles at the end of the first year, and all calculations for an increase in the treasury due to tourism are discarded.
    Possibly, this made the game more playable, although impossible for the player to understand why the treasury was increasing?
    
    A quick fix for this bug in the original code would be
    
        1450 V3=ABS(INT(V1-V2))
        1451 A=INT(A+V3)
    
    ...judging from the description of tourist income on basic line 1410
    
        1410 PRINT " YOU MADE";ABS(INT(V1-V2));"RALLODS FROM TOURIST TRADE."
    
    ### Bug 2
    
    On basic line 1330 following was the variable T1 never assigned:
    
        1330 PRINT " YOU HARVESTED ";INT(J-U2);"SQ. MILES OF CROPS."
        1340 IF U2=0 THEN 1370
        1344 IF T1>=2 THEN 1370
        1350 PRINT "   (DUE TO ";
        1355 IF T1=0 THEN 1365
        1360 PRINT "INCREASED ";
    
    Likely it should be the difference of the current years crop loss compared to the
    last years crop loss.
    
    ### Bug 3
    
    On basic line 1997 it is:
    
        1997 PRINT "   AND 10,000 SQ. MILES OF FOREST LAND."
    
    but it should be:
    
        1997 PRINT "   AND 1,000 SQ. MILES OF FOREST LAND."
    
    ### Bug 4
    
    On basic line 1310 we see this:
    
        1310 IF C=0 THEN 1324
        1320 PRINT "OF ";INT(J);"SQ. MILES PLANTED,";
        1324 ...
    
    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.
    
    
    ================================================
    FILE: 53_King/csharp/Country.cs
    ================================================
    namespace King;
    
    internal class Country
    {
        private const int InitialLand = 1000;
    
        private readonly IReadWrite _io;
        private readonly IRandom _random;
        private float _rallods;
        private float _countrymen;
        private float _foreigners;
        private float _arableLand;
        private float _industryLand;
    
        public Country(IReadWrite io, IRandom random)
            : this(
                io,
                random,
                (int)(60000 + random.NextFloat(1000) - random.NextFloat(1000)),
                (int)(500 + random.NextFloat(10) - random.NextFloat(10)),
                0,
                InitialLand)
        {
        }
    
        public Country(IReadWrite io, IRandom random, float rallods, float countrymen, float foreigners, float land)
        {
            _io = io;
            _random = random;
            _rallods = rallods;
            _countrymen = countrymen;
            _foreigners = foreigners;
            _arableLand = land;
        }
    
        public string GetStatus(int landValue, int plantingCost) 
            => Resource.Status(_rallods, _countrymen, _foreigners, _arableLand, landValue, plantingCost);
        
        public float Countrymen => _countrymen;
        public float Workers => _foreigners;
        public bool HasWorkers => _foreigners > 0;
        private float FarmLand => _arableLand;
        public bool HasRallods => _rallods > 0;
        public float Rallods => _rallods;
        public float IndustryLand => InitialLand - _arableLand;
        public int PreviousTourismIncome { get; private set; }
    
        public bool SellLand(int landValue, out float landSold)
        {
            if (_io.TryReadValue(
                    SellLandPrompt, 
                    out landSold, 
                    new ValidityTest(v => v <= FarmLand, () => SellLandError(FarmLand))))
            {
                _arableLand = (int)(_arableLand - landSold);
                _rallods = (int)(_rallods + landSold * landValue);
                return true;
            }
    
            return false;
        }
    
        public bool DistributeRallods(out float rallodsGiven)
        {
            if (_io.TryReadValue(
                    GiveRallodsPrompt,
                    out rallodsGiven, 
                    new ValidityTest(v => v <= _rallods, () => GiveRallodsError(_rallods))))
            {
                _rallods = (int)(_rallods - rallodsGiven);
                return true;
            }
    
            return false;
        }
    
        public bool PlantLand(int plantingCost, out float landPlanted)
        {
            if (_io.TryReadValue(
                    PlantLandPrompt, 
                    out landPlanted, 
                    new ValidityTest(v => v <= _countrymen * 2, PlantLandError1),
                    new ValidityTest(v => v <= FarmLand, PlantLandError2(FarmLand)),
                    new ValidityTest(v => v * plantingCost <= _rallods, PlantLandError3(_rallods))))
            {
                _rallods -= (int)(landPlanted * plantingCost);
                return true;
            }
    
            return false;
        }
    
        public bool ControlPollution(out float rallodsSpent)
        {
            if (_io.TryReadValue(
                    PollutionPrompt,
                    out rallodsSpent, 
                    new ValidityTest(v => v <= _rallods, () => PollutionError(_rallods))))
            {
                _rallods = (int)(_rallods - rallodsSpent);
                return true;
            }
    
            return false;
        }
    
        public bool TrySpend(float amount, float landValue)
        {
            if (_rallods >= amount)
            {
                _rallods -= amount;
                return true;
            }
            
            _arableLand = (int)(_arableLand - (int)(amount - _rallods) / landValue);
            _rallods = 0;
            return false;
        }
    
        public void RemoveTheDead(int deaths) => _countrymen = (int)(_countrymen - deaths);
    
        public void Migration(int migration) => _countrymen = (int)(_countrymen + migration);
    
        public void AddWorkers(int newWorkers) => _foreigners = (int)(_foreigners + newWorkers);
    
        public void SellCrops(int income) => _rallods = (int)(_rallods + income);
    
        public void EntertainTourists(int income)
        {
            PreviousTourismIncome = income;
            _rallods = (int)(_rallods + income);
        }
    }
    
    
    ================================================
    FILE: 53_King/csharp/Game.cs
    ================================================
    namespace King;
    
    internal class Game
    {
        const int TermOfOffice = 8;
    
        private readonly IReadWrite _io;
        private readonly IRandom _random;
    
        public Game(IReadWrite io, IRandom random)
        {
            _io = io;
            _random = random;
        }
    
        public void Play()
        {
            _io.Write(Title);
    
            var reign = SetUpReign();
            if (reign != null)
            {
                while (reign.PlayYear());
            }
    
            _io.WriteLine();
            _io.WriteLine();
        }
    
        private Reign? SetUpReign()
        {
            var response = _io.ReadString(InstructionsPrompt).ToUpper();
    
            if (response.Equals("Again", StringComparison.InvariantCultureIgnoreCase))
            {
                return _io.TryReadGameData(_random, out var reign) ? reign : null;
            }
            
            if (!response.StartsWith("N", StringComparison.InvariantCultureIgnoreCase))
            {
                _io.Write(InstructionsText(TermOfOffice));
            }
    
            _io.WriteLine();
            return new Reign(_io, _random);
        }
    }
    
    
    ================================================
    FILE: 53_King/csharp/IOExtensions.cs
    ================================================
    using System.Diagnostics.CodeAnalysis;
    using static King.Resources.Resource;
    
    namespace King;
    
    internal static class IOExtensions
    {
        internal static bool TryReadGameData(this IReadWrite io, IRandom random, [NotNullWhen(true)] out Reign? reign)
        {
            if (io.TryReadValue(SavedYearsPrompt, v => v < Reign.MaxTerm, SavedYearsError(Reign.MaxTerm), out var years) &&
                io.TryReadValue(SavedTreasuryPrompt, out var rallods) &&
                io.TryReadValue(SavedCountrymenPrompt, out var countrymen) &&
                io.TryReadValue(SavedWorkersPrompt, out var workers) &&
                io.TryReadValue(SavedLandPrompt, v => v is > 1000 and <= 2000, SavedLandError, out var land))
            {
                reign = new Reign(io, random, new Country(io, random, rallods, countrymen, workers, land), years + 1);
                return true;
            }
    
            reign = default;
            return false;
        }
    
        internal static bool TryReadValue(this IReadWrite io, string prompt, out float value, params ValidityTest[] tests)
        {
            while (true)
            {
                var response = value = io.ReadNumber(prompt);
                if (response == 0) { return false; }
                if (tests.All(test => test.IsValid(response, io))) { return true; }
            } 
        }
    
        internal static bool TryReadValue(this IReadWrite io, string prompt, out float value)
            => io.TryReadValue(prompt, _ => true, "", out value);
        
        internal static bool TryReadValue(
            this IReadWrite io,
            string prompt,
            Predicate isValid,
            string error,
            out float value)
            => io.TryReadValue(prompt, isValid, () => error, out value);
    
        internal static bool TryReadValue(
            this IReadWrite io,
            string prompt,
            Predicate isValid,
            Func getError,
            out float value)
        {
            while (true)
            {
                value = io.ReadNumber(prompt);
                if (value < 0) { return false; }
                if (isValid(value)) { return true; }
                
                io.Write(getError());
            }
        }
    }
    
    
    ================================================
    FILE: 53_King/csharp/King.csproj
    ================================================
    
      
        Exe
        net6.0
        10
        enable
        enable
      
    
      
        
      
    
      
        
      
    
    
    
    ================================================
    FILE: 53_King/csharp/King.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "King", "King.csproj", "{6CB6A32F-FFC7-4839-AAE2-4091D349BD34}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{6CB6A32F-FFC7-4839-AAE2-4091D349BD34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{6CB6A32F-FFC7-4839-AAE2-4091D349BD34}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{6CB6A32F-FFC7-4839-AAE2-4091D349BD34}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{6CB6A32F-FFC7-4839-AAE2-4091D349BD34}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 53_King/csharp/Program.cs
    ================================================
    global using Games.Common.IO;
    global using Games.Common.Randomness;
    global using King.Resources;
    global using static King.Resources.Resource;
    using King;
    
    new Game(new ConsoleIO(), new RandomNumberGenerator()).Play();
    
    
    ================================================
    FILE: 53_King/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 53_King/csharp/Reign.cs
    ================================================
    namespace King;
    
    internal class Reign
    {
        public const int MaxTerm = 8;
    
        private readonly IReadWrite _io;
        private readonly IRandom _random;
        private readonly Country _country;
        private float _yearNumber;
    
        public Reign(IReadWrite io, IRandom random)
            : this(io, random, new Country(io, random), 1)
        {
        }
    
        public Reign(IReadWrite io, IRandom random, Country country, float year)
        {
            _io = io;
            _random = random;
            _country = country;
            _yearNumber = year;
        }
    
        public bool PlayYear()
        {
            var year = new Year(_country, _random, _io);
    
            _io.Write(year.Status);
    
            var result = year.GetPlayerActions() ?? year.EvaluateResults() ?? IsAtEndOfTerm();
            if (result.IsGameOver)
            {
                _io.WriteLine(result.Message);
                return false;
            }
    
            return true;
        }
    
        private Result IsAtEndOfTerm() 
            => _yearNumber == MaxTerm 
                ? Result.GameOver(EndCongratulations(MaxTerm)) 
                : Result.Continue;
    }
    
    
    ================================================
    FILE: 53_King/csharp/Resources/DeathsPollution.txt
    ================================================
     {0} countrymen died of carbon-monoxide and dust inhalation
    
    ================================================
    FILE: 53_King/csharp/Resources/DeathsStarvation.txt
    ================================================
     {0} countrymen died of starvation
    
    ================================================
    FILE: 53_King/csharp/Resources/Emigration.txt
    ================================================
     {0} countrymen left the island.
    
    ================================================
    FILE: 53_King/csharp/Resources/EndAlso.txt
    ================================================
    also had your left eye gouged out!
    ;have also gained a very bad reputation.
    ;have also been declared national fink.
    
    
    ================================================
    FILE: 53_King/csharp/Resources/EndCongratulations.txt
    ================================================
    
    
    Congratulations!!!!!!!!!!!!!!!!!!
    You have successfully completed your {0} 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.
    
    
    
    
    ================================================
    FILE: 53_King/csharp/Resources/EndConsequences.txt
    ================================================
    You have been thrown out of office and are now
    residing in prison.;
    You have been assassinated.
    
    
    ================================================
    FILE: 53_King/csharp/Resources/EndForeignWorkers.txt
    ================================================
    
    
    The number of foreign workers has exceeded the number
    of countrymen. As a minority, they have revolted and
    taken over the country.
    {0}
    
    
    
    ================================================
    FILE: 53_King/csharp/Resources/EndManyDead.txt
    ================================================
    {0} countrymen died in one year!!!!!
    due to this extreme mismanagement, you have not only
    been impeached and thrown out of office, but you
    {1}
    
    
    
    ================================================
    FILE: 53_King/csharp/Resources/EndMoneyLeftOver.txt
    ================================================
    
    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 resign.
    
    
    
    
    ================================================
    FILE: 53_King/csharp/Resources/EndOneThirdDead.txt
    ================================================
    
    
    Over one third of the population has died since you
    were elected to office. The people (remaining)
    hate your guts.
    {0}
    
    
    
    ================================================
    FILE: 53_King/csharp/Resources/FuneralExpenses.txt
    ================================================
       You were forced to spend {0} rallods on funeral expenses
    
    ================================================
    FILE: 53_King/csharp/Resources/GiveRallodsError.txt
    ================================================
       Think again. You've only got {0}  rallods in the treasury.
    
    
    ================================================
    FILE: 53_King/csharp/Resources/GiveRallodsPrompt.txt
    ================================================
    How many rallods will you distribute among your countrymen
    
    ================================================
    FILE: 53_King/csharp/Resources/Goodbye.txt
    ================================================
    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).
    
    
    ================================================
    FILE: 53_King/csharp/Resources/Harvest.txt
    ================================================
     you harvested  {0} sq. miles of crops.
    {1}making {2} rallods.
    
    
    ================================================
    FILE: 53_King/csharp/Resources/HarvestReason.txt
    ================================================
       (Due to increased air and water pollution from foreign industry.)
    
    
    ================================================
    FILE: 53_King/csharp/Resources/Immigration.txt
    ================================================
     {0} countrymen came to the island.
    
    ================================================
    FILE: 53_King/csharp/Resources/InstructionsPrompt.txt
    ================================================
    Do you want instructions
    
    ================================================
    FILE: 53_King/csharp/Resources/InstructionsText.txt
    ================================================
    
    
    
    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 country'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 if 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 {0} year term of office.
    Good luck!
    
    
    ================================================
    FILE: 53_King/csharp/Resources/InsufficientReserves.txt
    ================================================
    
    
    ================================================
    FILE: 53_King/csharp/Resources/LandPlanted.txt
    ================================================
    Of  {0} sq. miles planted,
    
    ================================================
    FILE: 53_King/csharp/Resources/PlantLandError1.txt
    ================================================
       Sorry, but each countryman can only plant 2 sq. miles.
    
    ================================================
    FILE: 53_King/csharp/Resources/PlantLandError2.txt
    ================================================
       Sorry, but you've only {0} sq. miles of farm land.
    
    ================================================
    FILE: 53_King/csharp/Resources/PlantLandError3.txt
    ================================================
       Think again, You've only {0}  rallods left in the treasury.
    
    
    ================================================
    FILE: 53_King/csharp/Resources/PlantLandPrompt.txt
    ================================================
    How many square miles do you wish to plant
    
    ================================================
    FILE: 53_King/csharp/Resources/PollutionError.txt
    ================================================
       Think again. You only have  {0}  rallods remaining.
    
    
    ================================================
    FILE: 53_King/csharp/Resources/PollutionPrompt.txt
    ================================================
    How many rallods do you wish to spend on pollution control
    
    ================================================
    FILE: 53_King/csharp/Resources/Resource.cs
    ================================================
    using System.Reflection;
    using System.Runtime.CompilerServices;
    
    namespace King.Resources;
    
    internal static class Resource
    {
        private static bool _sellLandErrorShown;
    
        public static Stream Title => GetStream();
        
        public static string InstructionsPrompt => GetString();
        public static string InstructionsText(int years) => string.Format(GetString(), years);
    
        public static string Status(
            float rallods,
            float countrymen,
            float workers,
            float land,
            float landValue,
            float plantingCost)
            => string.Format(
                workers == 0 ? StatusWithWorkers : StatusSansWorkers,
                rallods,
                (int)countrymen,
                (int)workers,
                (int)land,
                landValue,
                plantingCost);
    
        private static string StatusWithWorkers => GetString();
        private static string StatusSansWorkers => GetString();
    
        public static string SellLandPrompt => GetString();
        public static string SellLandError(float farmLand)
        {
            var error = string.Format(GetString(), farmLand, _sellLandErrorShown ? "" : SellLandErrorReason);
            _sellLandErrorShown = true;
            return error;
        }
        private static string SellLandErrorReason => GetString();
    
        public static string GiveRallodsPrompt => GetString();
        public static string GiveRallodsError(float rallods) => string.Format(GetString(), rallods);
    
        public static string PlantLandPrompt => GetString();
        public static string PlantLandError1 => GetString();
        public static string PlantLandError2(float farmLand) => string.Format(GetString(), farmLand);
        public static string PlantLandError3(float rallods) => string.Format(GetString(), rallods);
    
        public static string PollutionPrompt => GetString();
        public static string PollutionError(float rallods) => string.Format(GetString(), rallods);
    
        public static string DeathsStarvation(float deaths) => string.Format(GetString(), (int)deaths);
        public static string DeathsPollution(int deaths) => string.Format(GetString(), deaths);
        public static string FuneralExpenses(int expenses) => string.Format(GetString(), expenses);
        public static string InsufficientReserves => GetString();
    
        public static string WorkerMigration(int newWorkers) => string.Format(GetString(), newWorkers);
        public static string Migration(int migration) 
            => string.Format(migration < 0 ? Emigration : Immigration, Math.Abs(migration));
        public static string Emigration => GetString();
        public static string Immigration => GetString();
    
        public static string LandPlanted(float landPlanted) 
            => landPlanted > 0 ? string.Format(GetString(), (int)landPlanted) : "";
        public static string Harvest(int yield, int income, bool hasIndustry) 
            => string.Format(GetString(), yield, HarvestReason(hasIndustry), income);
        private static string HarvestReason(bool hasIndustry) => hasIndustry ? GetString() : "";
    
        public static string TourismEarnings(int income) => string.Format(GetString(), income);
        public static string TourismDecrease(IRandom random) => string.Format(GetString(), TourismReason(random));
        private static string TourismReason(IRandom random) => GetStrings()[random.Next(5)];
    
        private static string EndAlso(IRandom random)
            => random.Next(10) switch
            {
                <= 3 => GetStrings()[0],
                <= 6 => GetStrings()[1],
                _ => GetStrings()[2]
            };
    
        public static string EndCongratulations(int termLength) => string.Format(GetString(), termLength);
        private static string EndConsequences(IRandom random) => GetStrings()[random.Next(2)];
        public static string EndForeignWorkers(IRandom random) => string.Format(GetString(), EndConsequences(random));
        public static string EndManyDead(int deaths, IRandom random) => string.Format(GetString(), deaths, EndAlso(random));
        public static string EndMoneyLeftOver() => GetString();
        public static string EndOneThirdDead(IRandom random) => string.Format(GetString(), EndConsequences(random));
        
        public static string SavedYearsPrompt => GetString();
        public static string SavedYearsError(int years) => string.Format(GetString(), years);
        public static string SavedTreasuryPrompt => GetString();
        public static string SavedCountrymenPrompt => GetString();
        public static string SavedWorkersPrompt => GetString();
        public static string SavedLandPrompt => GetString();
        public static string SavedLandError => GetString();
    
        public static string Goodbye => GetString();
    
        private static string[] GetStrings([CallerMemberName] string? name = null) => GetString(name).Split(';');
    
        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}'.");
    }
    
    ================================================
    FILE: 53_King/csharp/Resources/SavedCountrymenPrompt.txt
    ================================================
    How many countrymen
    
    ================================================
    FILE: 53_King/csharp/Resources/SavedLandError.txt
    ================================================
       Come on, you started with 1000 sq. miles of farm land
       and 10,000 sq. miles of forest land
    
    
    ================================================
    FILE: 53_King/csharp/Resources/SavedLandPrompt.txt
    ================================================
    How many square miles of land
    
    ================================================
    FILE: 53_King/csharp/Resources/SavedTreasuryPrompt.txt
    ================================================
    How much did you have in the treasury
    
    ================================================
    FILE: 53_King/csharp/Resources/SavedWorkersPrompt.txt
    ================================================
    How many workers
    
    ================================================
    FILE: 53_King/csharp/Resources/SavedYearsError.txt
    ================================================
       Come on, your term in office is only {0} years.
    
    
    ================================================
    FILE: 53_King/csharp/Resources/SavedYearsPrompt.txt
    ================================================
    How many years had you been in office when interrupted
    
    ================================================
    FILE: 53_King/csharp/Resources/SellLandError.txt
    ================================================
    *** Think again. You only have {0} square miles of farm land.
    {1}
    
    ================================================
    FILE: 53_King/csharp/Resources/SellLandErrorReason.txt
    ================================================
    
    (Foreign industry will only buy farm land because
    forest land is uneconomical to strip mine due to trees,
    thicker top soil, etc.)
    
    
    ================================================
    FILE: 53_King/csharp/Resources/SellLandPrompt.txt
    ================================================
    How many square miles do you wish to sell to industry
    
    ================================================
    FILE: 53_King/csharp/Resources/StatusSansWorkers.txt
    ================================================
    
    You now have  {0}  rallods in the treasury.
     {1} countrymen, and {3} sq. miles of land.
    This year industry will buy land for {4} rallods per square mile.
    Land currently costs {5} rallods per square mile to plant.
    
    
    
    ================================================
    FILE: 53_King/csharp/Resources/StatusWithWorkers.txt
    ================================================
    
    You now have  {0}  rallods in the treasury.
     {1} countrymen,  {2} foreign workers and {3} sq. miles of land.
    This year industry will buy land for {4} rallods per square mile.
    Land currently costs {5} rallods per square mile to plant.
    
    
    
    ================================================
    FILE: 53_King/csharp/Resources/Title.txt
    ================================================
                                      King
                   Creative Computing  Morristown, New Jersey
    
    
    
    
    
    ================================================
    FILE: 53_King/csharp/Resources/TourismDecrease.txt
    ================================================
       Decrease because {0}
    
    ================================================
    FILE: 53_King/csharp/Resources/TourismEarnings.txt
    ================================================
     You made {0} rallods from tourist trade.
    
    ================================================
    FILE: 53_King/csharp/Resources/TourismReason.txt
    ================================================
    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.
    
    
    ================================================
    FILE: 53_King/csharp/Resources/WorkerMigration.txt
    ================================================
     {0} workers came to the country and
    
    ================================================
    FILE: 53_King/csharp/Result.cs
    ================================================
    namespace King;
    
    internal record struct Result (bool IsGameOver, string Message)
    {
        internal static Result GameOver(string message) => new(true, message);
        internal static Result Continue => new(false, "");
    }
    
    
    ================================================
    FILE: 53_King/csharp/ValidityTest.cs
    ================================================
    namespace King;
    
    internal class ValidityTest
    {
        private readonly Predicate _isValid;
        private readonly Func _getError;
    
        public ValidityTest(Predicate isValid, string error)
            : this(isValid, () => error)
        {
        }
    
        public ValidityTest(Predicate isValid, Func getError)
        {
            _isValid = isValid;
            _getError = getError;
        }
    
        public bool IsValid(float value, IReadWrite io)
        {
            if (_isValid(value)) { return true; }
            
            io.Write(_getError());
            return false;
        }
    }
    
    ================================================
    FILE: 53_King/csharp/Year.cs
    ================================================
    using System.Text;
    
    namespace King;
    
    internal class Year
    {
        private readonly Country _country;
        private readonly IRandom _random;
        private readonly IReadWrite _io;
        private readonly int _plantingCost;
        private readonly int _landValue;
    
        private float _landSold;
        private float _rallodsDistributed;
        private float _landPlanted;
        private float _pollutionControlCost;
    
        private float _citizenSupport;
        private int _deaths;
        private float _starvationDeaths;
        private int _pollutionDeaths;
        private int _migration;
    
        public Year(Country country, IRandom random, IReadWrite io)
        {
            _country = country;
            _random = random;
            _io = io;
            
            _plantingCost = random.Next(10, 15);
            _landValue = random.Next(95, 105);
        }
    
        public string Status => _country.GetStatus(_landValue, _plantingCost);
    
        public Result? GetPlayerActions()
        {
            var playerSoldLand = _country.SellLand(_landValue, out _landSold);
            var playerDistributedRallods = _country.DistributeRallods(out _rallodsDistributed);
            var playerPlantedLand = _country.HasRallods && _country.PlantLand(_plantingCost, out _landPlanted);
            var playerControlledPollution = _country.HasRallods && _country.ControlPollution(out _pollutionControlCost);
    
            return playerSoldLand || playerDistributedRallods || playerPlantedLand || playerControlledPollution
                ? null
                : Result.GameOver(Goodbye);
        }
    
        public Result? EvaluateResults()
        {
            var rallodsUnspent = _country.Rallods;
    
            _io.WriteLine();
            _io.WriteLine();
    
            return EvaluateDeaths() 
                ?? EvaluateMigration() 
                ?? EvaluateAgriculture()
                ?? EvaluateTourism()
                ?? DetermineResult(rallodsUnspent);
        }
    
        public Result? EvaluateDeaths()
        {
            var supportedCountrymen = _rallodsDistributed / 100;
            _citizenSupport = supportedCountrymen - _country.Countrymen;
            _starvationDeaths = -_citizenSupport;
            if (_starvationDeaths > 0)
            {
                if (supportedCountrymen < 50) { return Result.GameOver(EndOneThirdDead(_random)); }
                _io.WriteLine(DeathsStarvation(_starvationDeaths));
            }
    
            var pollutionControl = _pollutionControlCost >= 25 ? _pollutionControlCost / 25 : 1;
            _pollutionDeaths = (int)(_random.Next((int)_country.IndustryLand) / pollutionControl);
            if (_pollutionDeaths > 0)
            {
                _io.WriteLine(DeathsPollution(_pollutionDeaths));
            }
    
            _deaths = (int)(_starvationDeaths + _pollutionDeaths);
            if (_deaths > 0)
            {
                var funeralCosts = _deaths * 9;
                _io.WriteLine(FuneralExpenses(funeralCosts));
    
                if (!_country.TrySpend(funeralCosts, _landValue))
                {
                    _io.WriteLine(InsufficientReserves);
                }
    
                _country.RemoveTheDead(_deaths);
            }
    
            return null;
        }
    
        private Result? EvaluateMigration()
        {
            if (_landSold > 0)
            {
                var newWorkers = (int)(_landSold + _random.NextFloat(10) - _random.NextFloat(20));
                if (!_country.HasWorkers) { newWorkers += 20; }
                _io.Write(WorkerMigration(newWorkers));
                _country.AddWorkers(newWorkers);
            }
    
            _migration = 
                (int)(_citizenSupport / 10 + _pollutionControlCost / 25 - _country.IndustryLand / 50 - _pollutionDeaths / 2);
            _io.WriteLine(Migration(_migration));
            _country.Migration(_migration);
    
            return null;
        }
    
        private Result? EvaluateAgriculture()
        {
            var ruinedCrops = (int)Math.Min(_country.IndustryLand * (_random.NextFloat() + 1.5f) / 2, _landPlanted);
            var yield = (int)(_landPlanted - ruinedCrops);
            var income = (int)(yield * _landValue / 2f);
    
            _io.Write(LandPlanted(_landPlanted));
            _io.Write(Harvest(yield, income, _country.IndustryLand > 0));
    
            _country.SellCrops(income);
    
            return null;
        }
    
        private Result? EvaluateTourism()
        {
            var reputationValue = (int)((_country.Countrymen - _migration) * 22 + _random.NextFloat(500));
            var industryAdjustment = (int)(_country.IndustryLand * 15);
            var tourismIncome = Math.Abs(reputationValue - industryAdjustment);
    
            _io.WriteLine(TourismEarnings(tourismIncome));
            if (industryAdjustment > 0 && tourismIncome < _country.PreviousTourismIncome)
            {
                _io.Write(TourismDecrease(_random));
            }
    
            _country.EntertainTourists(tourismIncome);
    
            return null;
        }
    
        private Result? DetermineResult(float rallodsUnspent)
        {
            if (_deaths > 200) { return Result.GameOver(EndManyDead(_deaths, _random)); }
            if (_country.Countrymen < 343) { return Result.GameOver(EndOneThirdDead(_random)); }
            if (rallodsUnspent / 100 > 5 && _starvationDeaths >= 2) { return Result.GameOver(EndMoneyLeftOver()); }
            if (_country.Workers > _country.Countrymen) { return Result.GameOver(EndForeignWorkers(_random)); }
            return null;
        }
    }
    
    
    ================================================
    FILE: 53_King/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 53_King/javascript/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells)
    
    
    ================================================
    FILE: 53_King/javascript/king.html
    ================================================
    
    
    KING
    
    
    
    
    
    
    
    
    
    
    ================================================
    FILE: 53_King/javascript/king.js
    ================================================
    // KING
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    function hate_your_guts()
    {
        print("\n");
        print("\n");
        print("OVER ONE THIRD OF THE POPULATION HAS DIED SINCE YOU\n");
        print("WERE ELECTED TO OFFICE. THE PEOPLE (REMAINING)\n");
        print("HATE YOUR GUTS.\n");
    }
    
    // Main program
    async function main()
    {
        print(tab(34) + "KING\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        print("DO YOU WANT INSTRUCTIONS");
        str = await input();
        n5 = 8;
        if (str == "AGAIN") {
            while (1) {
                print("HOW MANY YEARS HAD YOU BEEN IN OFFICE WHEN INTERRUPTED");
                x5 = parseInt(await input());
                if (x5 == 0)
                    return;
                if (x5 < 8)
                    break;
                print("   COME ON, YOUR TERM IN OFFICE IS ONLY " + n5 + " YEARS.\n");
            }
            print("HOW MUCH DID YOU HAVE IN THE TREASURY");
            a = parseInt(await input());
            if (a < 0)
                return;
            print("HOW MANY COUNTRYMEN");
            b = parseInt(await input());
            if (b < 0)
                return;
            print("HOW MANY WORKERS");
            c = parseInt(await input());
            if (c < 0)
                return;
            while (1) {
                print("HOW MANY SQUARE MILES OF LAND");
                d = parseInt(await input());
                if (d < 0)
                    return;
                if (d > 1000 && d <= 2000)
                    break;
                print("   COME ON, YOU STARTED WITH 1000 SQ. MILES OF FARM LAND\n");
                print("   AND 10,000 SQ. MILES OF FOREST LAND.\n");
            }
        } else {
            if (str.substr(0, 1) != "N") {
                print("\n");
                print("\n");
                print("\n");
                print("CONGRATULATIONS! YOU'VE JUST BEEN ELECTED PREMIER OF SETATS\n");
                print("DETINU, A SMALL COMMUNIST ISLAND 30 BY 70 MILES LONG. YOUR\n");
                print("JOB IS TO DECIDE UPON THE CONTRY'S BUDGET AND DISTRIBUTE\n");
                print("MONEY TO YOUR COUNTRYMEN FROM THE COMMUNAL TREASURY.\n");
                print("THE MONEY SYSTEM IS RALLODS, AND EACH PERSON NEEDS 100\n");
                print("RALLODS PER YEAR TO SURVIVE. YOUR COUNTRY'S INCOME COMES\n");
                print("FROM FARM PRODUCE AND TOURISTS VISITING YOUR MAGNIFICENT\n");
                print("FORESTS, HUNTING, FISHING, ETC. HALF YOUR LAND IS FARM LAND\n");
                print("WHICH ALSO HAS AN EXCELLENT MINERAL CONTENT AND MAY BE SOLD\n");
                print("TO FOREIGN INDUSTRY (STRIP MINING) WHO IMPORT AND SUPPORT\n");
                print("THEIR OWN WORKERS. CROPS COST BETWEEN 10 AND 15 RALLODS PER\n");
                print("SQUARE MILE TO PLANT.\n");
                print("YOUR GOAL IS TO COMPLETE YOUR " + n5 + " YEAR TERM OF OFFICE.\n");
                print("GOOD LUCK!\n");
            }
            print("\n");
            a = Math.floor(60000 + (1000 * Math.random()) - (1000 * Math.random()));
            b = Math.floor(500 + (10 * Math.random()) - (10 * Math.random()));
            c = 0;
            d = 2000;
            x5 = 0;
        }
        v3 = 0;
        b5 = 0;
        x = false;
        while (1) {
            w = Math.floor(10 * Math.random() + 95);
            print("\n");
            print("YOU NOW HAVE " + a + " RALLODS IN THE TREASURY.\n");
            print(b + " COUNTRYMEN, ");
            v9 = Math.floor(((Math.random() / 2) * 10 + 10));
            if (c != 0)
                print(c + " FOREIGN WORKERS, ");
            print("AND " + Math.floor(d) + " SQ. MILES OF LAND.\n");
            print("THIS YEAR INDUSTRY WILL BUY LAND FOR " + w + " ");
            print("RALLODS PER SQUARE MILE.\n");
            print("LAND CURRENTLY COSTS " + v9 + " RALLODS PER SQUARE MILE TO PLANT.\n");
            print("\n");
            while (1) {
                print("HOW MANY SQUARE MILES DO YOU WISH TO SELL TO INDUSTRY");
                h = parseInt(await input());
                if (h < 0)
                    continue;
                if (h <= d - 1000)
                    break;
                print("***  THINK AGAIN. YOU ONLY HAVE " + (d - 1000) + " SQUARE MILES OF FARM LAND.\n");
                if (x == false) {
                    print("\n");
                    print("(FOREIGN INDUSTRY WILL ONLY BUY FARM LAND BECAUSE\n");
                    print("FOREST LAND IS UNECONOMICAL TO STRIP MINE DUE TO TREES,\n");
                    print("THICKER TOP SOIL, ETC.)\n");
                    x = true;
                }
            }
            d = Math.floor(d - h);
            a = Math.floor(a + (h * w));
            while (1) {
                print("HOW MANY RALLODS WILL YOU DISTRIBUTE AMONG YOUR COUNTRYMEN");
                i = parseInt(await input());
                if (i < 0)
                    continue;
                if (i < a)
                    break;
                if (i == a) {
                    j = 0;
                    k = 0;
                    a = 0;
                    break;
                }
                print("   THINK AGAIN. YOU'VE ONLY " + a + " RALLODS IN THE TREASURY\n");
            }
            if (a) {
                a = Math.floor(a - i);
                while (1) {
                    print("HOW MANY SQUARE MILES DO YOU WISH TO PLANT");
                    j = parseInt(await input());
                    if (j < 0)
                        continue;
                    if (j <= b * 2) {
                        if (j <= d - 1000) {
                            u1 = Math.floor(j * v9);
                            if (u1 > a) {
                                print("   THINK AGAIN. YOU'VE ONLY " + a + " RALLODS LEFT IN THE TREASURY.\n");
                                continue;
                            } else if (u1 == a) {
                                k = 0;
                                a = 0;
                            }
                            break;
                        }
                        print("   SORRY, BUT YOU'VE ONLY " + (d - 1000) + " SQ. MILES OF FARM LAND.\n");
                        continue;
                    }
                    print("   SORRY, BUT EACH COUNTRYMAN CAN ONLY PLANT 2 SQ. MILES.\n");
                }
            }
            if (a) {
                a -= u1;
                while (1) {
                    print("HOW MANY RALLODS DO YOU WISH TO SPEND ON POLLUTION CONTROL");
                    k = parseInt(await input());
                    if (k < 0)
                        continue;
                    if (k <= a)
                        break;
                    print("   THINK AGAIN. YOU ONLY HAVE " + a + " RALLODS REMAINING.\n");
                }
            }
            if (h == 0 && i == 0 && j == 0 && k == 0) {
                print("GOODBYE.\n");
                print("(IF YOU WISH TO CONTINUE THIS GAME AT A LATER DATE, ANSWER\n");
                print("'AGAIN' WHEN ASKED IF YOU WANT INSTRUCTIONS AT THE START\n");
                print("OF THE GAME).\n");
                return;
            }
            print("\n");
            print("\n");
            a = Math.floor(a - k);
            a4 = a;
            if (Math.floor(i / 100 - b) < 0) {
                if (i / 100 < 50) {
                    hate_your_guts();
                    break;
                }
                print(Math.floor(b - (i / 100)) + " COUNTRYMEN DIED OF STARVATION\n");
            }
            f1 = Math.floor(Math.random() * (2000 - d));
            if (k >= 25)
                f1 = Math.floor(f1 / (k / 25));
            if (f1 > 0)
                print(f1 + " COUNTRYMEN DIED OF CARBON-MONOXIDE AND DUST INHALATION\n");
            funeral = false;
            if (Math.floor((i / 100) - b) >= 0) {
                if (f1 > 0) {
                    print("   YOU WERE FORCED TO SPEND " + Math.floor(f1 * 9) + " RALLODS ON ");
                    print("FUNERAL EXPENSES.\n");
                    b5 = f1;
                    a = Math.floor(a - (f1 * 9));
                    funeral = true;
                }
            } else {
                print("   YOU WERE FORCED TO SPEND " + Math.floor((f1 + (b - (i / 100))) * 9));
                print(" RALLODS ON FUNERAL EXPENSES.\n");
                b5 = Math.floor(f1 + (b - (i / 100)));
                a = Math.floor(a - ((f1 + (b - (i / 100))) * 9));
                funeral = true;
            }
            if (funeral) {
                if (a < 0) {
                    print("   INSUFFICIENT RESERVES TO COVER COST - LAND WAS SOLD\n");
                    d = Math.floor(d + (a / w));
                    a = 0;
                }
                b = Math.floor(b - b5);
            }
            c1 = 0;
            if (h != 0) {
                c1 = Math.floor(h + (Math.random() * 10) - (Math.random() * 20));
                if (c <= 0)
                    c1 += 20;
                print(c1 + " WORKERS CAME TO THE COUNTRY AND ");
            }
            p1 = Math.floor(((i / 100 - b) / 10) + (k / 25) - ((2000 - d) / 50) - (f1 / 2));
            print(Math.abs(p1) + " COUNTRYMEN ");
            if (p1 >= 0)
                print("CAME TO");
            else
                print("LEFT");
            print(" THE ISLAND.\n");
            b = Math.floor(b + p1);
            c = Math.floor(c + c1);
            u2 = Math.floor(((2000 - d) * ((Math.random() + 1.5) / 2)));
            if (c != 0) {
                print("OF " + Math.floor(j) + " SQ. MILES PLANTED,");
            }
            if (j <= u2)
                u2 = j;
            print(" YOU HARVESTED " + Math.floor(j - u2) + " SQ. MILES OF CROPS.\n");
            if (u2 != 0 && t1 < 2) {
                print("   (DUE TO ");
                if (t1 != 0)
                    print("INCREASED ");
                print("AIR AND WATER POLLUTION FROM FOREIGN INDUSTRY.)\n");
            }
            q = Math.floor((j - u2) * (w / 2));
            print("MAKING " + q + " RALLODS.\n");
            a = Math.floor(a + q);
            v1 = Math.floor(((b - p1) * 22) + (Math.random() * 500));
            v2 = Math.floor((2000 - d) * 15);
            print(" YOU MADE " + Math.abs(Math.floor(v1 - v2)) + " RALLODS FROM TOURIST TRADE.\n");
            if (v2 != 0 && v1 - v2 < v3) {
                print("   DECREASE BECAUSE ");
                g1 = 10 * Math.random();
                if (g1 <= 2)
                    print("FISH POPULATION HAS DWINDLED DUE TO WATER POLLUTION.\n");
                else if (g1 <= 4)
                    print("AIR POLLUTION IS KILLING GAME BIRD POPULATION.\n");
                else if (g1 <= 6)
                    print("MINERAL BATHS ARE BEING RUINED BY WATER POLLUTION.\n");
                else if (g1 <= 8)
                    print("UNPLEASANT SMOG IS DISCOURAGING SUN BATHERS.\n");
                else if (g1 <= 10)
                    print("HOTELS ARE LOOKING SHABBY DUE TO SMOG GRIT.\n");
            }
            v3 = Math.floor(a + v3);    // Probable bug from original game
            a = Math.floor(a + v3);
            if (b5 > 200) {
                print("\n");
                print("\n");
                print(b5 + " COUNTRYMEN DIED IN ONE YEAR!!!!!\n");
                print("DUE TO THIS EXTREME MISMANAGEMENT, YOU HAVE NOT ONLY\n");
                print("BEEN IMPEACHED AND THROWN OUT OF OFFICE, BUT YOU\n");
                m6 = Math.floor(Math.random() * 10);
                if (m6 <= 3)
                    print("ALSO HAD YOUR LEFT EYE GOUGED OUT!\n");
                else if (m6 <= 6)
                    print("HAVE ALSO GAINED A VERY BAD REPUTATION.\n");
                else
                    print("HAVE ALSO BEEN DECLARED NATIONAL FINK.\n");
                print("\n");
                print("\n");
                return;
            }
            if (b < 343) {
                hate_your_guts();
                break;
            }
            if (a4 / 100 > 5 && b5 - f1 >= 2) {
                print("\n");
                print("MONEY WAS LEFT OVER IN THE TREASURY WHICH YOU DID\n");
                print("NOT SPEND. AS A RESULT, SOME OF YOUR COUNTRYMEN DIED\n");
                print("OF STARVATION. THE PUBLIC IS ENRAGED AND YOU HAVE\n");
                print("BEEN FORCED TO EITHER RESIGN OR COMMIT SUICIDE.\n");
                print("THE CHOICE IS YOURS.\n");
                print("IF YOU CHOOSE THE LATTER, PLEASE TURN OFF YOUR COMPUTER\n");
                print("BEFORE PROCEEDING.\n");
                print("\n");
                print("\n");
                return;
            }
            if (c > b) {
                print("\n");
                print("\n");
                print("THE NUMBER OF FOREIGN WORKERS HAS EXCEEDED THE NUMBER\n");
                print("OF COUNTRYMEN. AS A MINORITY, THEY HAVE REVOLTED AND\n");
                print("TAKEN OVER THE COUNTRY.\n");
                break;
            }
            if (n5 - 1 == x5) {
                print("\n");
                print("\n");
                print("CONGRATULATIONS!!!!!!!!!!!!!!!!!!\n");
                print("YOU HAVE SUCCESFULLY COMPLETED YOUR " + n5 + " YEAR TERM\n");
                print("OF OFFICE. YOU WERE, OF COURSE, EXTREMELY LUCKY, BUT\n");
                print("NEVERTHELESS, IT'S QUITE AN ACHIEVEMENT. GOODBYE AND GOOD\n");
                print("LUCK - YOU'LL PROBABLY NEED IT IF YOU'RE THE TYPE THAT\n");
                print("PLAYS THIS GAME.\n");
                print("\n");
                print("\n");
                return;
            }
            x5++;
            b5 = 0;
        }
        if (Math.random() <= 0.5) {
            print("YOU HAVE BEEN ASSASSINATED.\n");
        } else {
            print("YOU HAVE BEEN THROWN OUT OF OFFICE AND ARE NOW\n");
            print("RESIDING IN PRISON.\n");
        }
        print("\n");
        print("\n");
    }
    
    main();
    
    
    ================================================
    FILE: 53_King/king.bas
    ================================================
    1 PRINT TAB(34);"KING"
    2 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    3 PRINT:PRINT:PRINT
    4 PRINT "DO YOU WANT INSTRUCTIONS";
    5 INPUT Z$
    6 N5=8
    10 IF LEFT$(Z$,1)="N" THEN 47
    11 IF Z$="AGAIN" THEN 1960
    12 PRINT:PRINT:PRINT
    20 PRINT "CONGRATULATIONS! YOU'VE JUST BEEN ELECTED PREMIER OF SETATS"
    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"
    30 PRINT "RALLODS PER YEAR TO SURVIVE. YOUR COUNTRY'S INCOME COMES"
    32 PRINT "FROM FARM PRODUCE AND TOURISTS VISITING YOUR MAGNIFICENT"
    34 PRINT "FORESTS, HUNTING, FISHING, ETC. HALF YOUR LAND IS FARM LAND"
    36 PRINT "WHICH ALSO HAS AN EXCELLENT MINERAL CONTENT AND MAY BE SOLD"
    38 PRINT "TO FOREIGN INDUSTRY (STRIP MINING) WHO IMPORT AND SUPPORT"
    40 PRINT "THEIR OWN WORKERS. CROPS COST BETWEEN 10 AND 15 RALLODS PER"
    42 PRINT "SQUARE MILE TO PLANT."
    44 PRINT "YOUR GOAL IS TO COMPLETE YOUR";N5;"YEAR TERM OF OFFICE."
    46 PRINT "GOOD LUCK!"
    47 PRINT
    50 A=INT(60000+(1000*RND(1))-(1000*RND(1)))
    55 B=INT(500+(10*RND(1))-(10*RND(1)))
    65 D=2000
    100 W=INT(10*RND(1)+95)
    102 PRINT
    105 PRINT "YOU NOW HAVE ";A;" RALLODS IN THE TREASURY."
    110 PRINT INT(B);:PRINT "COUNTRYMEN, ";
    115 V9=INT(((RND(1)/2)*10+10))
    120 IF C=0 THEN 140
    130 PRINT INT(C);"FOREIGN WORKERS, ";
    140 PRINT "AND";INT(D);"SQ. MILES OF LAND."
    150 PRINT "THIS YEAR INDUSTRY WILL BUY LAND FOR";W;
    152 PRINT "RALLODS PER SQUARE MILE."
    155 PRINT "LAND CURRENTLY COSTS";V9;"RALLODS PER SQUARE MILE TO PLANT."
    162 PRINT
    200 PRINT "HOW MANY SQUARE MILES DO YOU WISH TO SELL TO INDUSTRY";
    210 INPUT H
    215 IF H<0 THEN 200
    220 IF H<=D-1000 THEN 300
    230 PRINT "***  THINK AGAIN. YOU ONLY HAVE";D-1000;"SQUARE MILES OF FARM LAND."
    240 IF X<>0 THEN 200
    250 PRINT:PRINT "(FOREIGN INDUSTRY WILL ONLY BUY FARM LAND BECAUSE"
    260 PRINT "FOREST LAND IS UNECONOMICAL TO STRIP MINE DUE TO TREES,"
    270 PRINT "THICKER TOP SOIL, ETC.)"
    280 X=1
    299 GOTO 200
    300 D=INT(D-H)
    310 A=INT(A+(H*W))
    320 PRINT "HOW MANY RALLODS WILL YOU DISTRIBUTE AMONG YOUR COUNTRYMEN";
    340 INPUT I
    342 IF I<0 THEN 320
    350 IF I
    0 THEN 1002 602 IF I<>0 THEN 1002 604 IF J<>0 THEN 1002 606 IF K<>0 THEN 1002 609 PRINT 612 PRINT "GOODBYE." 614 PRINT "(IF YOU WISH TO CONTINUE THIS GAME AT A LATER DATE, ANSWER" 616 PRINT "'AGAIN' WHEN ASKED IF YOU WANT INSTRUCTIONS AT THE START" 617 PRINT "OF THE GAME)." 618 STOP 1000 GOTO 600 1002 PRINT 1003 PRINT 1010 A=INT(A-K) 1020 A4=A 1100 IF INT(I/100-B)>=0 THEN 1120 1105 IF I/100<50 THEN 1700 1110 PRINT INT(B-(I/100));"COUNTRYMEN DIED OF STARVATION" 1120 F1=INT(RND(1)*(2000-D)) 1122 IF K<25 THEN 1130 1125 F1=INT(F1/(K/25)) 1130 IF F1<=0 THEN 1150 1140 PRINT F1;"COUNTRYMEN DIED OF CARBON-MONOXIDE AND DUST INHALATION" 1150 IF INT((I/100)-B)<0 THEN 1170 1160 IF F1>0 THEN 1180 1165 GOTO 1200 1170 PRINT " YOU WERE FORCED TO SPEND";INT((F1+(B-(I/100)))*9); 1172 PRINT "RALLODS ON FUNERAL EXPENSES" 1174 B5=INT(F1+(B-(I/100))) 1175 A=INT(A-((F1+(B-(I/100)))*9)) 1176 GOTO 1185 1180 PRINT " YOU WERE FORCED TO SPEND ";INT(F1*9);"RALLODS ON "; 1181 PRINT "FUNERAL EXPENSES." 1182 B5=F1 1183 A=INT(A-(F1*9)) 1185 IF A>=0 THEN 1194 1187 PRINT " INSUFFICIENT RESERVES TO COVER COST - LAND WAS SOLD" 1189 D=INT(D+(A/W)) 1190 A=0 1194 B=INT(B-B5) 1200 IF H=0 THEN 1250 1220 C1=INT(H+(RND(1)*10)-(RND(1)*20)) 1224 IF C>0 THEN 1230 1226 C1=C1+20 1230 PRINT C1;"WORKERS CAME TO THE COUNTRY AND"; 1250 P1=INT(((I/100-B)/10)+(K/25)-((2000-D)/50)-(F1/2)) 1255 PRINT ABS(P1);"COUNTRYMEN "; 1260 IF P1<0 THEN 1275 1265 PRINT "CAME TO"; 1270 GOTO 1280 1275 PRINT "LEFT"; 1280 PRINT " THE ISLAND." 1290 B=INT(B+P1) 1292 C=INT(C+C1) 1305 U2=INT(((2000-D)*((RND(1)+1.5)/2))) 1310 IF C=0 THEN 1324 1320 PRINT "OF ";INT(J);"SQ. MILES PLANTED,"; 1324 IF J>U2 THEN 1330 1326 U2=J 1330 PRINT " YOU HARVESTED ";INT(J-U2);"SQ. MILES OF CROPS." 1340 IF U2=0 THEN 1370 1344 IF T1>=2 THEN 1370 1350 PRINT " (DUE TO "; 1355 IF T1=0 THEN 1365 1360 PRINT "INCREASED "; 1365 PRINT "AIR AND WATER POLLUTION FROM FOREIGN INDUSTRY.)" 1370 Q=INT((J-U2)*(W/2)) 1380 PRINT "MAKING";INT(Q);"RALLODS." 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." 1420 IF V2=0 THEN 1450 1425 IF V1-V2>=V3 THEN 1450 1430 PRINT " DECREASE BECAUSE "; 1435 G1=10*RND(1) 1440 IF G1<=2 THEN 1460 1442 IF G1<=4 THEN 1465 1444 IF G1<=6 THEN 1470 1446 IF G1<=8 THEN 1475 1448 IF G1<=10 THEN 1480 1450 V3=INT(A+V3) 1451 A=INT(A+V3) 1452 GOTO 1500 1460 PRINT "FISH POPULATION HAS DWINDLED DUE TO WATER POLLUTION." 1462 GOTO 1450 1465 PRINT "AIR POLLUTION IS KILLING GAME BIRD POPULATION." 1467 GOTO 1450 1470 PRINT "MINERAL BATHS ARE BEING RUINED BY WATER POLLUTION." 1472 GOTO 1450 1475 PRINT "UNPLEASANT SMOG IS DISCOURAGING SUN BATHERS." 1477 GOTO 1450 1480 PRINT "HOTELS ARE LOOKING SHABBY DUE TO SMOG GRIT." 1482 GOTO 1450 1500 IF B5>200 THEN 1600 1505 IF B<343 THEN 1700 1510 IF (A4/100)>5 THEN 1800 1515 IF C>B THEN 1550 1520 IF N5-1=X5 THEN 1900 1545 GOTO 2000 1550 PRINT 1552 PRINT 1560 PRINT "THE NUMBER OF FOREIGN WORKERS HAS EXCEEDED THE NUMBER" 1562 PRINT "OF COUNTRYMEN. AS A MINORITY, THEY HAVE REVOLTED AND" 1564 PRINT "TAKEN OVER THE COUNTRY." 1570 IF RND(1)<=.5 THEN 1580 1574 PRINT "YOU HAVE BEEN THROWN OUT OF OFFICE AND ARE NOW" 1576 PRINT "RESIDING IN PRISON." 1578 GOTO 1590 1580 PRINT "YOU HAVE BEEN ASSASSINATED." 1590 PRINT 1592 PRINT 1596 STOP 1600 PRINT 1602 PRINT 1610 PRINT B5;"COUNTRYMEN DIED IN ONE YEAR!!!!!" 1615 PRINT "DUE TO THIS EXTREME MISMANAGEMENT, YOU HAVE NOT ONLY" 1620 PRINT "BEEN IMPEACHED AND THROWN OUT OF OFFICE, BUT YOU" 1622 M6=INT(RND(1)*10) 1625 IF M6<=3 THEN 1670 1630 IF M6<=6 THEN 1680 1635 IF M6<=10 THEN 1690 1670 PRINT "ALSO HAD YOUR LEFT EYE GOUGED OUT!" 1672 GOTO 1590 1680 PRINT "HAVE ALSO GAINED A VERY BAD REPUTATION." 1682 GOTO 1590 1690 PRINT "HAVE ALSO BEEN DECLARED NATIONAL FINK." 1692 GOTO 1590 1700 PRINT 1702 PRINT 1710 PRINT "OVER ONE THIRD OF THE POPULTATION HAS DIED SINCE YOU" 1715 PRINT "WERE ELECTED TO OFFICE. THE PEOPLE (REMAINING)" 1720 PRINT "HATE YOUR GUTS." 1730 GOTO 1570 1800 IF B5-F1<2 THEN 1515 1807 PRINT 1815 PRINT "MONEY WAS LEFT OVER IN THE TREASURY WHICH YOU DID" 1820 PRINT "NOT SPEND. AS A RESULT, SOME OF YOUR COUNTRYMEN DIED" 1825 PRINT "OF STARVATION. THE PUBLIC IS ENRAGED AND YOU HAVE" 1830 PRINT "BEEN FORCED TO EITHER RESIGN OR COMMIT SUICIDE." 1835 PRINT "THE CHOICE IS YOURS." 1840 PRINT "IF YOU CHOOSE THE LATTER, PLEASE TURN OFF YOUR COMPUTER" 1845 PRINT "BEFORE PROCEEDING." 1850 GOTO 1590 1900 PRINT 1902 PRINT 1920 PRINT "CONGRATULATIONS!!!!!!!!!!!!!!!!!!" 1925 PRINT "YOU HAVE SUCCESFULLY COMPLETED YOUR";N5;"YEAR TERM" 1930 PRINT "OF OFFICE. YOU WERE, OF COURSE, EXTREMELY LUCKY, BUT" 1935 PRINT "NEVERTHELESS, IT'S QUITE AN ACHIEVEMENT. GOODBYE AND GOOD" 1940 PRINT "LUCK - YOU'LL PROBABLY NEED IT IF YOU'RE THE TYPE THAT" 1945 PRINT "PLAYS THIS GAME." 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 1965 PRINT " COME ON, YOUR TERM IN OFFICE IS ONLY";N5;"YEARS." 1967 GOTO 1960 1969 PRINT "HOW MUCH DID YOU HAVE IN THE TREASURY"; 1970 INPUT A 1971 IF A<0 THEN 1590 1975 PRINT "HOW MANY COUNTRYMEN"; 1976 INPUT B 1977 IF B<0 THEN 1590 1980 PRINT "HOW MANY WORKERS"; 1981 INPUT C 1982 IF C<0 THEN 1590 1990 PRINT "HOW MANY SQUARE MILES OF LAND"; 1991 INPUT D 1992 IF D<0 THEN 1590 1993 IF D>2000 THEN 1996 1994 IF D>1000 THEN 100 1996 PRINT " COME ON, YOU STARTED WITH 1000 SQ. MILES OF FARM LAND" 1997 PRINT " AND 10,000 SQ. MILES OF FOREST LAND." 1998 GOTO 1990 2000 X5=X5+1 2020 B5=0 2040 GOTO 100 2046 END ================================================ FILE: 53_King/king_variable_update.bas ================================================ 1 PRINT TAB(34);"KING" 2 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 3 PRINT:PRINT:PRINT 4 PRINT "DO YOU WANT INSTRUCTIONS"; 5 INPUT Z$ 6 YEARS_REQUIRED=8 10 IF LEFT$(Z$,1)="N" THEN 47 11 IF Z$="AGAIN" THEN 1960 12 PRINT:PRINT:PRINT 20 PRINT "CONGRATULATIONS! YOU'VE JUST BEEN ELECTED PREMIER OF SETATS" 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" 30 PRINT "RALLODS PER YEAR TO SURVIVE. YOUR COUNTRY'S INCOME COMES" 32 PRINT "FROM FARM PRODUCE AND TOURISTS VISITING YOUR MAGNIFICENT" 34 PRINT "FORESTS, HUNTING, FISHING, ETC. HALF YOUR LAND IS FARM LAND" 36 PRINT "WHICH ALSO HAS AN EXCELLENT MINERAL CONTENT AND MAY BE SOLD" 38 PRINT "TO FOREIGN INDUSTRY (STRIP MINING) WHO IMPORT AND SUPPORT" 40 PRINT "THEIR OWN WORKERS. CROPS COST BETWEEN 10 AND 15 RALLODS PER" 42 PRINT "SQUARE MILE TO PLANT." 44 PRINT "YOUR GOAL IS TO COMPLETE YOUR";YEARS_REQUIRED;"YEAR TERM OF OFFICE." 46 PRINT "GOOD LUCK!" 47 PRINT 50 RALLODS=INT(60000+(1000*RND(1))-(1000*RND(1))) 55 COUNTRYMEN=INT(500+(10*RND(1))-(10*RND(1))) 65 LANDAREA=2000 100 LANDPRICE=INT(10*RND(1)+95) 102 PRINT 105 PRINT "YOU NOW HAVE ";RALLODS;" RALLODS IN THE TREASURY." 110 PRINT INT(COUNTRYMEN);:PRINT "COUNTRYMEN, "; 115 COST_TO_PLANT=INT(((RND(1)/2)*10+10)) 120 IF FOREIGN_WORKERS=0 THEN 140 130 PRINT INT(FOREIGN_WORKERS);"FOREIGN WORKERS, "; 140 PRINT "AND";INT(LANDAREA);"SQ. MILES OF LAND." 150 PRINT "THIS YEAR INDUSTRY WILL BUY LAND FOR";LANDPRICE; 152 PRINT "RALLODS PER SQUARE MILE." 155 PRINT "LAND CURRENTLY COSTS";COST_TO_PLANT;"RALLODS PER SQUARE MILE TO PLANT." 162 PRINT 200 PRINT "HOW MANY SQUARE MILES DO YOU WISH TO SELL TO INDUSTRY"; 210 INPUT SELL_TO_INDUSTRY 215 IF SELL_TO_INDUSTRY<0 THEN 200 220 IF SELL_TO_INDUSTRY<=LANDAREA-1000 THEN 300 230 PRINT "*** THINK AGAIN. YOU ONLY HAVE";LANDAREA-1000;"SQUARE MILES OF FARM LAND." 240 IF EXPLANATION_GIVEN THEN 200 250 PRINT:PRINT "(FOREIGN INDUSTRY WILL ONLY BUY FARM LAND BECAUSE" 260 PRINT "FOREST LAND IS UNECONOMICAL TO STRIP MINE DUE TO TREES," 270 PRINT "THICKER TOP SOIL, ETC.)" 280 EXPLANATION_GIVEN=TRUE 299 GOTO 200 300 LANDAREA=INT(LANDAREA-SELL_TO_INDUSTRY) 310 RALLODS=INT(RALLODS+(SELL_TO_INDUSTRY*LANDPRICE)) 320 PRINT "HOW MANY RALLODS WILL YOU DISTRIBUTE AMONG YOUR COUNTRYMEN"; 340 INPUT WELFARE 342 IF WELFARE<0 THEN 320 350 IF WELFARE0 THEN 1002 602 IF WELFARE<>0 THEN 1002 604 IF PLANTING_AREA<>0 THEN 1002 606 IF MONEY_SPENT_ON_POLLUTION_CONTROL<>0 THEN 1002 609 PRINT 612 PRINT "GOODBYE." 614 PRINT "(IF YOU WISH TO CONTINUE THIS GAME AT A LATER DATE, ANSWER" 616 PRINT "'AGAIN' WHEN ASKED IF YOU WANT INSTRUCTIONS AT THE START" 617 PRINT "OF THE GAME)." 618 STOP 1000 GOTO 600 1002 PRINT 1003 PRINT 1010 RALLODS=INT(RALLODS-MONEY_SPENT_ON_POLLUTION_CONTROL) 1020 ORIGINAL_RALLODS=RALLODS 1100 IF INT(WELFARE/100-COUNTRYMEN)>=0 THEN 1120 1105 IF WELFARE/100<50 THEN 1700 1110 PRINT INT(COUNTRYMEN-(WELFARE/100));"COUNTRYMEN DIED OF STARVATION" 1120 POLLUTION_DEATHS=INT(RND(1)*(2000-LANDAREA)) 1122 IF MONEY_SPENT_ON_POLLUTION_CONTROL<25 THEN 1130 1125 POLLUTION_DEATHS=INT(POLLUTION_DEATHS/(MONEY_SPENT_ON_POLLUTION_CONTROL/25)) 1130 IF POLLUTION_DEATHS<=0 THEN 1150 1140 PRINT POLLUTION_DEATHS;"COUNTRYMEN DIED OF CARBON-MONOXIDE AND DUST INHALATION" 1150 IF INT((WELFARE/100)-COUNTRYMEN)<0 THEN 1170 1160 IF POLLUTION_DEATHS>0 THEN 1180 1165 GOTO 1200 1170 PRINT " YOU WERE FORCED TO SPEND";INT((POLLUTION_DEATHS+(COUNTRYMEN-(WELFARE/100)))*9); 1172 PRINT "RALLODS ON FUNERAL EXPENSES" 1174 DEATHS=INT(POLLUTION_DEATHS+(COUNTRYMEN-(WELFARE/100))) 1175 RALLODS=INT(RALLODS-((POLLUTION_DEATHS+(COUNTRYMEN-(WELFARE/100)))*9)) 1176 GOTO 1185 1180 PRINT " YOU WERE FORCED TO SPEND ";INT(POLLUTION_DEATHS*9);"RALLODS ON "; 1181 PRINT "FUNERAL EXPENSES." 1182 DEATHS=POLLUTION_DEATHS 1183 RALLODS=INT(RALLODS-(POLLUTION_DEATHS*9)) 1185 IF RALLODS>=0 THEN 1194 1187 PRINT " INSUFFICIENT RESERVES TO COVER COST - LAND WAS SOLD" 1189 LANDAREA=INT(LANDAREA+(RALLODS/LANDPRICE)) 1190 RALLODS=0 1194 COUNTRYMEN=INT(COUNTRYMEN-DEATHS) 1200 IF SELL_TO_INDUSTRY=0 THEN 1250 1220 NEW_FOREIGNERS=INT(SELL_TO_INDUSTRY+(RND(1)*10)-(RND(1)*20)) 1224 IF FOREIGN_WORKERS>0 THEN 1230 1226 NEW_FOREIGNERS=NEW_FOREIGNERS+20 1230 PRINT NEW_FOREIGNERS;"WORKERS CAME TO THE COUNTRY AND"; 1250 IMMIGRATION=INT(((WELFARE/100-COUNTRYMEN)/10)+(MONEY_SPENT_ON_POLLUTION_CONTROL/25)-((2000-LANDAREA)/50)-(POLLUTION_DEATHS/2)) 1255 PRINT ABS(IMMIGRATION);"COUNTRYMEN "; 1260 IF IMMIGRATION<0 THEN 1275 1265 PRINT "CAME TO"; 1270 GOTO 1280 1275 PRINT "LEFT"; 1280 PRINT " THE ISLAND." 1290 COUNTRYMEN=INT(COUNTRYMEN+IMMIGRATION) 1292 FOREIGN_WORKERS=INT(FOREIGN_WORKERS+NEW_FOREIGNERS) 1305 CROP_LOSS=INT(((2000-LANDAREA)*((RND(1)+1.5)/2))) 1310 IF FOREIGN_WORKERS=0 THEN 1324 1320 PRINT "OF ";INT(PLANTING_AREA);"SQ. MILES PLANTED,"; 1324 IF PLANTING_AREA>CROP_LOSS THEN 1330 1326 CROP_LOSS=PLANTING_AREA 1330 PRINT " YOU HARVESTED ";INT(PLANTING_AREA-CROP_LOSS);"SQ. MILES OF CROPS." 1340 IF CROP_LOSS=0 THEN 1370 1344 IF T1>=2 THEN 1370 1350 PRINT " (DUE TO "; 1355 IF T1=0 THEN 1365 1360 PRINT "INCREASED "; 1365 PRINT "AIR AND WATER POLLUTION FROM FOREIGN INDUSTRY.)" 1370 AGRICULTURAL_INCOME=INT((PLANTING_AREA-CROP_LOSS)*(LANDPRICE/2)) 1380 PRINT "MAKING";INT(AGRICULTURAL_INCOME);"RALLODS." 1390 RALLODS=INT(RALLODS+AGRICULTURAL_INCOME) REM I think tourism calculations are actually wrong in the original code! 1400 V1=INT(((COUNTRYMEN-IMMIGRATION)*22)+(RND(1)*500)) 1405 V2=INT((2000-LANDAREA)*15) 1410 PRINT " YOU MADE";ABS(INT(V1-V2));"RALLODS FROM TOURIST TRADE." 1420 IF V2=0 THEN 1450 1425 IF V1-V2>=V3 THEN 1450 1430 PRINT " DECREASE BECAUSE "; 1435 G1=10*RND(1) 1440 IF G1<=2 THEN 1460 1442 IF G1<=4 THEN 1465 1444 IF G1<=6 THEN 1470 1446 IF G1<=8 THEN 1475 1448 IF G1<=10 THEN 1480 1450 V3=INT(RALLODS+V3) 1451 RALLODS=INT(RALLODS+V3) 1452 GOTO 1500 1460 PRINT "FISH POPULATION HAS DWINDLED DUE TO WATER POLLUTION." 1462 GOTO 1450 1465 PRINT "AIR POLLUTION IS KILLING GAME BIRD POPULATION." 1467 GOTO 1450 1470 PRINT "MINERAL BATHS ARE BEING RUINED BY WATER POLLUTION." 1472 GOTO 1450 1475 PRINT "UNPLEASANT SMOG IS DISCOURAGING SUN BATHERS." 1477 GOTO 1450 1480 PRINT "HOTELS ARE LOOKING SHABBY DUE TO SMOG GRIT." 1482 GOTO 1450 1500 IF DEATHS>200 THEN 1600 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 1545 GOTO 2000 1550 PRINT 1552 PRINT 1560 PRINT "THE NUMBER OF FOREIGN WORKERS HAS EXCEEDED THE NUMBER" 1562 PRINT "OF COUNTRYMEN. AS A MINORITY, THEY HAVE REVOLTED AND" 1564 PRINT "TAKEN OVER THE COUNTRY." 1570 IF RND(1)<=.5 THEN 1580 1574 PRINT "YOU HAVE BEEN THROWN OUT OF OFFICE AND ARE NOW" 1576 PRINT "RESIDING IN PRISON." 1578 GOTO 1590 1580 PRINT "YOU HAVE BEEN ASSASSINATED." 1590 PRINT 1592 PRINT 1596 STOP 1600 PRINT 1602 PRINT 1610 PRINT DEATHS;"COUNTRYMEN DIED IN ONE YEAR!!!!!" 1615 PRINT "DUE TO THIS EXTREME MISMANAGEMENT, YOU HAVE NOT ONLY" 1620 PRINT "BEEN IMPEACHED AND THROWN OUT OF OFFICE, BUT YOU" 1622 M6=INT(RND(1)*10) 1625 IF M6<=3 THEN 1670 1630 IF M6<=6 THEN 1680 1635 IF M6<=10 THEN 1690 1670 PRINT "ALSO HAD YOUR LEFT EYE GOUGED OUT!" 1672 GOTO 1590 1680 PRINT "HAVE ALSO GAINED A VERY BAD REPUTATION." 1682 GOTO 1590 1690 PRINT "HAVE ALSO BEEN DECLARED NATIONAL FINK." 1692 GOTO 1590 1700 PRINT 1702 PRINT 1710 PRINT "OVER ONE THIRD OF THE POPULTATION HAS DIED SINCE YOU" 1715 PRINT "WERE ELECTED TO OFFICE. THE PEOPLE (REMAINING)" 1720 PRINT "HATE YOUR GUTS." 1730 GOTO 1570 1800 IF DEATHS-POLLUTION_DEATHS<2 THEN 1515 1807 PRINT 1815 PRINT "MONEY WAS LEFT OVER IN THE TREASURY WHICH YOU DID" 1820 PRINT "NOT SPEND. AS A RESULT, SOME OF YOUR COUNTRYMEN DIED" 1825 PRINT "OF STARVATION. THE PUBLIC IS ENRAGED AND YOU HAVE" 1830 PRINT "BEEN FORCED TO EITHER RESIGN OR COMMIT SUICIDE." 1835 PRINT "THE CHOICE IS YOURS." 1840 PRINT "IF YOU CHOOSE THE LATTER, PLEASE TURN OFF YOUR COMPUTER" 1845 PRINT "BEFORE PROCEEDING." 1850 GOTO 1590 1900 PRINT 1902 PRINT 1920 PRINT "CONGRATULATIONS!!!!!!!!!!!!!!!!!!" 1925 PRINT "YOU HAVE SUCCESFULLY COMPLETED YOUR";YEARS_REQUIRED;"YEAR TERM" 1930 PRINT "OF OFFICE. YOU WERE, OF COURSE, EXTREMELY LUCKY, BUT" 1935 PRINT "NEVERTHELESS, IT'S QUITE AN ACHIEVEMENT. GOODBYE AND GOOD" 1940 PRINT "LUCK - YOU'LL PROBABLY NEED IT IF YOU'RE THE TYPE THAT" 1945 PRINT "PLAYS THIS GAME." 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 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"; 1970 INPUT RALLODS 1971 IF RALLODS<0 THEN 1590 1975 PRINT "HOW MANY COUNTRYMEN"; 1976 INPUT COUNTRYMEN 1977 IF COUNTRYMEN<0 THEN 1590 1980 PRINT "HOW MANY WORKERS"; 1981 INPUT FOREIGN_WORKERS 1982 IF FOREIGN_WORKERS<0 THEN 1590 1990 PRINT "HOW MANY SQUARE MILES OF LAND"; 1991 INPUT LANDAREA 1992 IF LANDAREA<0 THEN 1590 1993 IF LANDAREA>2000 THEN 1996 1994 IF LANDAREA>1000 THEN 100 1996 PRINT " COME ON, YOU STARTED WITH 1000 SQ. MILES OF FARM LAND" 1997 PRINT " AND 10,000 SQ. MILES OF FOREST LAND." 1998 GOTO 1990 2000 X5=X5+1 2020 DEATHS=0 2040 GOTO 100 2046 END ================================================ FILE: 53_King/kotlin/King.kt ================================================ package king53 import kotlin.math.abs import kotlin.random.Random import kotlin.system.exitProcess lateinit var gameState: GameState const val KEEP_ORIGINAL_BUGS = false const val KEEP_ORIGINAL_SUICIDE_REFERENCE = false val rnd: Double get() = Random.nextDouble() fun tab(i: Int) = " ".repeat(i) class EndOfInputException : Throwable() fun main() { header() print("DO YOU WANT INSTRUCTIONS? ") readLine()?.apply { gameState = if (startsWith("AGAIN")) loadOldGame() else GameState() if (startsWith("Y")) instructions(gameState.yearsRequired) } ?: throw EndOfInputException() with(gameState) { do { recalculateLandCost() displayStatus() inputLandSale() performLandSale() inputWelfare() performWelfare() inputPlantingArea() performPlanting() inputPollutionControl() if (zeroInput()) { displayExitMessage() exitProcess(0) } val yearResult = simulateOneYear().also { it.displayConsequences() } } while (yearResult == YearOutcome.ContinueNextYear) } } private fun header() { println("${tab(34)}KING") println("${tab(14)}CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") println() println() println() } fun instructions(yearsRequired: Int) { println( """ 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 $yearsRequired YEAR TERM OF OFFICE. GOOD LUCK! """.trimIndent() ) } fun loadOldGame(): GameState = GameState().apply { do { var retry = false print("HOW MANY YEARS HAD YOU BEEN IN OFFICE WHEN INTERRUPTED? ") currentYear = validatedInput { it > 0 } if (currentYear >= yearsRequired) { println(" COME ON, YOUR TERM IN OFFICE IS ONLY $yearsRequired YEARS.") retry = true } } while (retry) print("HOW MUCH DID YOU HAVE IN THE TREASURY? ") rallods = validatedInput { it >= 0 } print("HOW MANY COUNTRYMEN? ") countrymen = validatedInput { it >= 0 } print("HOW MANY WORKERS? ") foreignWorkers = validatedInput { it >= 0 } do { var retry = false print("HOW MANY SQUARE MILES OF LAND? ") landArea = validatedInput { it >= 0 } if (landArea > 2000 || landArea <= 1000) { println(" COME ON, YOU STARTED WITH 1000 SQ. MILES OF FARM LAND") println(" AND 10,000 SQ. MILES OF FOREST LAND.") retry = true } } while (retry) } /** * Possible outcomes for a year. */ sealed class YearOutcome { /** * Display output for the end of the year, for each different possible * year outcome. */ open fun displayConsequences() { // Default display nothing } fun finalFate() { if (rnd < .5) { println("YOU HAVE BEEN THROWN OUT OF OFFICE AND ARE NOW") println("RESIDING IN PRISON.") } else { println("YOU HAVE BEEN ASSASSINATED.") } println() println() } object ContinueNextYear : YearOutcome() class Win(private val yearsRequired: Int) : YearOutcome() { override fun displayConsequences() { // The misspelling of "successfully" is in the original code. println( """ CONGRATULATIONS!!!!!!!!!!!!!!!!!! YOU HAVE SUCCESFULLY COMPLETED YOUR $yearsRequired 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. """.trimIndent() ) } } class ExtremeMismanagement(private val death: Int) : YearOutcome() { override fun displayConsequences() { println() println("$death COUNTRYMEN DIED IN ONE YEAR!!!!!") println("DUE TO THIS EXTREME MISMANAGEMENT, YOU HAVE NOT ONLY") println("BEEN IMPEACHED AND THROWN OUT OF OFFICE, BUT YOU") println( when ((rnd * 10.0).toInt()) { in 0..3 -> "ALSO HAD YOUR LEFT EYE GOUGED OUT!" in 4..6 -> "HAVE ALSO GAINED A VERY BAD REPUTATION." else -> "HAVE ALSO BEEN DECLARED NATIONAL FINK." } ) } } object TooManyPeopleDead : YearOutcome() { // The mistyping of "population" is in the original game. override fun displayConsequences() { println( """ OVER ONE THIRD OF THE POPULTATION HAS DIED SINCE YOU WERE ELECTED TO OFFICE. THE PEOPLE (REMAINING) HATE YOUR GUTS. """.trimIndent() ) finalFate() } } object AntiImmigrationRevolution : YearOutcome() { override fun displayConsequences() { println( """ THE NUMBER OF FOREIGN WORKERS HAS EXCEEDED THE NUMBER OF COUNTRYMEN. AS A MINORITY, THEY HAVE REVOLTED AND TAKEN OVER THE COUNTRY. """.trimIndent() ) finalFate() } } object StarvationWithFullTreasury : YearOutcome() { override fun displayConsequences() { println( if (KEEP_ORIGINAL_SUICIDE_REFERENCE) { """ 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. """.trimIndent() } else { """ 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 RESIGN. PLEASE TURN OFF YOUR COMPUTER AND SURRENDER IT TO THE NEAREST POLICE STATION. """.trimIndent() } ) } } } /** * Record data, allow data input, and process the simulation for the game. */ class GameState(val yearsRequired: Int = 8) { /** * The current year. Years start with zero, but we never * output the current year. */ var currentYear = 0 /** * Keep track of each year's crop loss, so we can report increases. */ private var lastYearsCropLoss: Int = 0 /** * Number of countrymen who have died of either pollution * or starvation this year. * It costs 9 rallods to bury a body. * If you lose 200 people in one year, you will throw an {@see ExtremeMismanagementException} */ private var death = 0 /** * Last year's tourist numbers. Use this to check whether the number * of tourists has gone up or down each year. */ private var tourists = 0 private var moneySpentOnPollutionControl = 0 private var moneySpentOnPlanting = 0 /** * Current stock of rallods. * Player starts with between 59000 and 61000 rallods, but * mostly distributed close to 60000. 75% of the time it's * between 59500 and 60500. */ var rallods = (60000.0 + (1000.0 * rnd) - (1000.0 * rnd)).toInt() /** * Population. * Initial population is about 500. * 75% of the time it's between 495 and 505. */ var countrymen = (500 + (10 * rnd) - (10 * rnd)).toInt() /** * Land sale price is evenly between 95 and 104 rallods per * square mile. * Price doesn't change over the course of the game. */ private var landPrice = (10 * rnd + 95).toInt() private var plantingArea = 0 private var welfareThisYear = 0 /** * Land area in square miles. Arable land is 1000 square miles less. * Almost all calculations use landArea-1000 because only arable * land is of any use. */ var landArea = 2000 /** * Number of foreigners brought in by companies to whom you * have sold land. If this gets higher than your population, there will * be a revolution. */ var foreignWorkers = 0 /** * Planting cost is recalculated every year. */ private var costToPlant: Int = 1 /** * There is a brief explanation of land selling only * on the first turn. */ private var explanationOfSellingGiven = false private var sellThisYear: Int = 0 /** * Planting cost is recalculated every year * at between 10 and 14 rallods. */ fun recalculateLandCost() { costToPlant = ((rnd / 2.0) * 10.0 + 10.0).toInt() } /** * Show the current status of the world. */ fun displayStatus() { println() println("YOU NOW HAVE $rallods RALLODS IN THE TREASURY.") print("$countrymen COUNTRYMEN, ") if (foreignWorkers != 0) { println("$foreignWorkers FOREIGN WORKERS, ") } println("AND $landArea SQ. MILES OF LAND.") println("THIS YEAR INDUSTRY WILL BUY LAND FOR $landPrice") println("RALLODS PER SQUARE MILE.") println("LAND CURRENTLY COSTS $costToPlant RALLODS PER SQUARE MILE TO PLANT.") } fun displayExitMessage() { println() println("GOODBYE.") println("(IF YOU WISH TO CONTINUE THIS GAME AT A LATER DATE, ANSWER") println("'AGAIN' WHEN ASKED IF YOU WANT INSTRUCTIONS AT THE START") println("OF THE GAME).") } fun performLandSale() { landArea -= sellThisYear rallods += sellThisYear * landPrice } fun performPlanting() { rallods -= moneySpentOnPlanting } fun performWelfare() { rallods -= welfareThisYear } /** * Ask how much land we want to sell. Immediately get the money. * The player has to do the calculations to work out how much * money that makes. */ fun inputLandSale() { do { print("HOW MANY SQUARE MILES DO YOU WISH TO SELL TO INDUSTRY? ") sellThisYear = numberInput() if (sellThisYear > landArea - 1000) { println("*** THINK AGAIN. YOU ONLY HAVE ${landArea - 1000} SQUARE MILES OF FARM LAND.") if (!explanationOfSellingGiven) { println() println("(FOREIGN INDUSTRY WILL ONLY BUY FARM LAND BECAUSE") println("FOREST LAND IS UNECONOMICAL TO STRIP MINE DUE TO TREES,") println("THICKER TOP SOIL, ETC.)") explanationOfSellingGiven = true } } } while (sellThisYear < 0 || sellThisYear > landArea - 1000) } /** * Input the value of `welfareThisYear` */ fun inputWelfare() { do { var retry = false print("HOW MANY RALLODS WILL YOU DISTRIBUTE AMONG YOUR COUNTRYMEN? ") welfareThisYear = numberInput() if (welfareThisYear > rallods) { println(" THINK AGAIN. YOU'VE ONLY $rallods RALLODS IN THE TREASURY") retry = true } if (welfareThisYear < 0) { retry = true } } while (retry) } /** * Get the number of square miles to plant this year. * Validate the response: * Each countryman can only plant 2 square miles. * You can only plant on arable land. * You may not spend more on planting than your treasury. */ fun inputPlantingArea() { if (welfareThisYear == rallods) { plantingArea = 0 } else { do { var retry = false print("HOW MANY SQUARE MILES DO YOU WISH TO PLANT? ") plantingArea = numberInput() val moneySpentOnPlanting = plantingArea * costToPlant if (plantingArea < 0) { retry = true } else if (plantingArea >= 0 && plantingArea > countrymen * 2) { println(" SORRY, BUT EACH COUNTRYMAN CAN ONLY PLANT 2 SQ. MILES.") retry = true } else if (plantingArea > landArea - 1000) { println(" SORRY, BUT YOU'VE ONLY ${landArea - 1000} SQ. MILES OF FARM LAND.") retry = true } else if (moneySpentOnPlanting > rallods) { println(" THINK AGAIN. YOU'VE ONLY $rallods RALLODS LEFT IN THE TREASURY.") retry = true } } while (retry) } } /** * Enter amount for pollution control. * Validate that this does not exceed treasury. */ fun inputPollutionControl() { do { var retry = false print("HOW MANY RALLODS DO YOU WISH TO SPEND ON POLLUTION CONTROL? ") moneySpentOnPollutionControl = numberInput() if (rallods < 0) { retry = true } else if (moneySpentOnPollutionControl > rallods) { println(" THINK AGAIN. YOU ONLY HAVE $rallods RALLODS REMAINING.") retry = true } } while (retry) } /** * @return true if all data entered so far has been zero. */ fun zeroInput() = sellThisYear == 0 && welfareThisYear == 0 && plantingArea == 0 && moneySpentOnPollutionControl == 0 fun simulateOneYear(): YearOutcome { rallods -= moneySpentOnPollutionControl val rallodsAfterPollutionControl = rallods var starvationDeaths = 0 if (welfareThisYear / 100.0 - countrymen < 0) { /* Wait, WHAT? If you spend less than 5000 rallods on welfare, no matter the current size of the population, then you will end the game, with the game claiming that too many people have died, without showing exactly how many have died? https://github.com/coding-horror/basic-computer-games/blob/main/53_King/king.bas#:~:text=1105%20IF%20I/100%3C50%20THEN%201700 */ if (welfareThisYear / 100.0 < 50) return YearOutcome.TooManyPeopleDead starvationDeaths = (countrymen - (welfareThisYear / 100.0)).toInt() println("$starvationDeaths COUNTRYMEN DIED OF STARVATION") } var pollutionDeaths = (rnd * (2000 - landArea)).toInt() if (moneySpentOnPollutionControl >= 25) { pollutionDeaths = (pollutionDeaths / (moneySpentOnPollutionControl / 25.0)).toInt() } if (pollutionDeaths > 0) { println("$pollutionDeaths COUNTRYMEN DIED OF CARBON-MONOXIDE AND DUST INHALATION") } death = pollutionDeaths + starvationDeaths if (death > 0) { println(" YOU WERE FORCED TO SPEND ${death * 9}") println("RALLODS ON FUNERAL EXPENSES") rallods -= death * 9 } if (rallods < 0) { println(" INSUFFICIENT RESERVES TO COVER COST - LAND WAS SOLD") landArea += rallods / landPrice rallods = 1 } countrymen -= death val newForeigners = if (sellThisYear > 0) { (sellThisYear + rnd * 10.0 + rnd * 20.0).toInt() + (if (foreignWorkers <= 0) 20 else 0) } else 0 /* Immigration is calculated as One for every thousand rallods more welfare than strictly required minus one for every 10 starvation deaths plus One for every 25 rallods spent on pollution control plus one for every 50 square miles of arable land minus one for every 2 pollution deaths */ val immigration = ( (welfareThisYear / 100.0 - countrymen) / 10.0 + moneySpentOnPollutionControl / 25.0 - (2000 - landArea) / 50.0 - pollutionDeaths / 2.0 ).toInt() println( "$newForeigners WORKERS CAME TO THE COUNTRY AND" + " ${abs(immigration)} COUNTRYMEN ${if (immigration < 0) "LEFT" else "CAME TO"}" + " THE ISLAND." ) countrymen += immigration foreignWorkers += newForeigners /* Crop loss is between 75% and 125% of the land sold to industry, due to the pollution that industry causes. Money spent on pollution control reduces pollution deaths among the population, but does not affect crop losses. */ var cropLoss = ((2000 - landArea) * (rnd + 1.5) / 2.0).toInt() if (foreignWorkers > 0) print("OF $plantingArea SQ. MILES PLANTED,") if (plantingArea <= cropLoss) cropLoss = plantingArea val cropLossWorse = cropLoss > lastYearsCropLoss lastYearsCropLoss = cropLoss println(" YOU HARVESTED ${plantingArea - cropLoss} SQ. MILES OF CROPS.") if (cropLoss > 0) { println(" (DUE TO ${if (cropLossWorse) "INCREASED " else ""}AIR AND WATER POLLUTION FROM FOREIGN INDUSTRY)") } val agriculturalIncome = ((plantingArea - cropLoss) * landPrice / 2.0).toInt() println("MAKING $agriculturalIncome RALLODS.") rallods += agriculturalIncome val v1 = (((countrymen - immigration) * 22.0) + rnd * 500).toInt() val v2 = ((2000.0 - landArea) * 15.0).toInt() println(" YOU MADE ${abs(v1 - v2)} RALLODS FROM TOURIST TRADE.") if (v2 != 0 && v1 - v2 < tourists) { print(" DECREASE BECAUSE ") println( when ((10 * rnd).toInt()) { in 0..2 -> "FISH POPULATION HAS DWINDLED DUE TO WATER POLLUTION." in 3..4 -> "AIR POLLUTION IS KILLING GAME BIRD POPULATION." in 5..6 -> "MINERAL BATHS ARE BEING RUINED BY WATER POLLUTION." in 7..8 -> "UNPLEASANT SMOG IS DISCOURAGING SUN BATHERS." else -> "HOTELS ARE LOOKING SHABBY DUE TO SMOG GRIT." } ) } /* The original code was incorrect. If v3 starts at 0, for example, our money doubles, when we have already been told that "YOU MADE ${abs(v1 - v2)} RALLODS FROM TOURIST TRADE" See the original code 1450 V3=INT(A+V3) 1451 A=INT(A+V3) https://github.com/coding-horror/basic-computer-games/blob/main/53_King/king.bas#:~:text=1450%20V3%3DINT,INT(A%2BV3) */ if (KEEP_ORIGINAL_BUGS) { tourists += rallods } else { tourists = abs(v1 - v2) } rallods += tourists return if (death > 200) YearOutcome.ExtremeMismanagement(death) else if (countrymen < 343) YearOutcome.TooManyPeopleDead else if (rallodsAfterPollutionControl / 100 > 5 && death - pollutionDeaths >= 2) YearOutcome.StarvationWithFullTreasury else if (foreignWorkers > countrymen) YearOutcome.AntiImmigrationRevolution else { if (currentYear++ > yearsRequired) YearOutcome.Win(yearsRequired) else YearOutcome.ContinueNextYear } } } private fun numberInput() = try { readLine()?.toInt() ?: throw EndOfInputException() } catch (r: NumberFormatException) { 0 } class DataEntryValidationException : Throwable() private fun validatedInput(predicate : (Int)->Boolean) = numberInput().apply { if (!predicate(this)) throw DataEntryValidationException() } ================================================ FILE: 53_King/kotlin/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Kotlin](https://kotlinlang.org/) ================================================ FILE: 53_King/lua/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Lua](https://www.lua.org/) ================================================ FILE: 53_King/perl/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Perl](https://www.perl.org/) ================================================ FILE: 53_King/python/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Python](https://www.python.org/about/) ## Porting notes Variables: * A: Available rallods (money) * B: Current countrymen * C: foreign_workers * C1: foreign_workers_influx * D: Available land (farmland=D-1000) * F1: polution_deaths (last round) * B5: died_contrymen (starvation + pollution) * H: sm_sell_to_industry * I: distributed_rallods * J: planted_sq in a round * K: pollution_control_spendings in a round * X5: years in office * N5: YEARS_IN_TERM - how many years one term in office has * P1: population_change (positive means people come, negative means people leave) * W: land_buy_price * V9: planting_cost * U2: crop_loss * V1-V2: Earnings from tourist trade * V3: tourism_earnings * T1: crop_loss_last_year * W: land_buy_price * X: only show an error message once Functions: * `RND(1)`: `random.random()` * `INT(...)`: `int(...)` * `ABS(...)`: `abs(...)` Bugs: See [53 King README](../README.md) Implicit knowledge: * `COST_OF_LIVING`: One countryman needs 100 for food. Otherwise they will die of starvation * `COST_OF_FUNERAL`: A funeral costs 9 ================================================ FILE: 53_King/python/king.py ================================================ """ KING A strategy game where the player is the king. Ported to Python by Martin Thoma in 2022 """ import sys from dataclasses import dataclass from random import randint, random FOREST_LAND = 1000 INITIAL_LAND = FOREST_LAND + 1000 COST_OF_LIVING = 100 COST_OF_FUNERAL = 9 YEARS_IN_TERM = 8 POLLUTION_CONTROL_FACTOR = 25 def ask_int(prompt) -> int: while True: try: return int(input(prompt)) except ValueError: continue @dataclass class GameState: rallods: int = -1 countrymen: int = -1 land: int = INITIAL_LAND foreign_workers: int = 0 years_in_office: int = 0 # previous year stats crop_loss_last_year: int = 0 # current year stats died_contrymen: int = 0 pollution_deaths: int = 0 population_change: int = 0 # current year - market situation (in rallods per square mile) planting_cost: int = -1 land_buy_price: int = -1 tourism_earnings: int = 0 def set_market_conditions(self) -> None: self.land_buy_price = randint(95, 105) self.planting_cost = randint(10, 15) @property def farmland(self) -> int: return self.land - FOREST_LAND @property def settled_people(self) -> int: return self.countrymen - self.population_change def sell_land(self, amount: int) -> None: assert amount <= self.farmland self.land -= amount self.rallods += self.land_buy_price * amount def distribute_rallods(self, distribute: int) -> None: self.rallods -= distribute def spend_pollution_control(self, spend: int) -> None: self.rallods -= spend def plant(self, sq_to_plant: int) -> None: self.rallods -= sq_to_plant * self.planting_cost def print_status(self) -> None: print(f"\n\nYOU NOW HAVE {self.rallods} RALLODS IN THE TREASURY.") print(f"{int(self.countrymen)} COUNTRYMEN, ", end="") if self.foreign_workers > 0: print(f"{int(self.foreign_workers)} FOREIGN WORKERS, ", end="") print(f"AND {self.land} SQ. MILES OF LAND.") print( f"THIS YEAR INDUSTRY WILL BUY LAND FOR {self.land_buy_price} " "RALLODS PER SQUARE MILE." ) print( f"LAND CURRENTLY COSTS {self.planting_cost} RALLODS " "PER SQUARE MILE TO PLANT.\n" ) def handle_deaths( self, distributed_rallods: int, pollution_control_spendings: int ) -> None: starved_countrymen = max( 0, int(self.countrymen - distributed_rallods / COST_OF_LIVING) ) if starved_countrymen > 0: print(f"{starved_countrymen} COUNTRYMEN DIED OF STARVATION") self.pollution_deaths = int(random() * (INITIAL_LAND - self.land)) if pollution_control_spendings >= POLLUTION_CONTROL_FACTOR: self.pollution_deaths = int( self.pollution_deaths / (pollution_control_spendings / POLLUTION_CONTROL_FACTOR) ) if self.pollution_deaths > 0: print( f"{self.pollution_deaths} COUNTRYMEN DIED OF CARBON-MONOXIDE " f"AND DUST INHALATION" ) self.died_contrymen = starved_countrymen + self.pollution_deaths if self.died_contrymen > 0: funeral_cost = self.died_contrymen * COST_OF_FUNERAL print(f" YOU WERE FORCED TO SPEND {funeral_cost} RALLODS ON ") print("FUNERAL EXPENSES.") self.rallods -= funeral_cost if self.rallods < 0: print(" INSUFFICIENT RESERVES TO COVER COST - LAND WAS SOLD") self.land += int(self.rallods / self.land_buy_price) self.rallods = 0 self.countrymen -= self.died_contrymen def handle_tourist_trade(self) -> None: V1 = int(self.settled_people * 22 + random() * 500) V2 = int((INITIAL_LAND - self.land) * 15) 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 ", end="") reason = randint(0, 10) if reason <= 2: print("FISH POPULATION HAS DWINDLED DUE TO WATER POLLUTION.") elif reason <= 4: print("AIR POLLUTION IS KILLING GAME BIRD POPULATION.") elif reason <= 6: print("MINERAL BATHS ARE BEING RUINED BY WATER POLLUTION.") elif reason <= 8: print("UNPLEASANT SMOG IS DISCOURAGING SUN BATHERS.") else: print("HOTELS ARE LOOKING SHABBY DUE TO SMOG GRIT.") # NOTE: The following two lines had a bug in the original game: self.tourism_earnings = abs(int(V1 - V2)) self.rallods += self.tourism_earnings def handle_harvest(self, planted_sq: int) -> None: crop_loss = int((INITIAL_LAND - self.land) * ((random() + 1.5) / 2)) if self.foreign_workers != 0: print(f"OF {planted_sq} SQ. MILES PLANTED,") if planted_sq <= crop_loss: crop_loss = planted_sq harvested = int(planted_sq - crop_loss) print(f" YOU HARVESTED {harvested} SQ. MILES OF CROPS.") unlucky_harvesting_worse = crop_loss - self.crop_loss_last_year if crop_loss != 0: print(" (DUE TO ", end="") if unlucky_harvesting_worse > 2: print("INCREASED ", end="") print("AIR AND WATER POLLUTION FROM FOREIGN INDUSTRY.)") revenue = int((planted_sq - crop_loss) * (self.land_buy_price / 2)) print(f"MAKING {revenue} RALLODS.") self.crop_loss_last_year = crop_loss self.rallods += revenue def handle_foreign_workers( self, sm_sell_to_industry: int, distributed_rallods: int, polltion_control_spendings: int, ) -> None: foreign_workers_influx = 0 if sm_sell_to_industry != 0: foreign_workers_influx = int( sm_sell_to_industry + (random() * 10) - (random() * 20) ) if self.foreign_workers <= 0: foreign_workers_influx += 20 print(f"{foreign_workers_influx} WORKERS CAME TO THE COUNTRY AND") surplus_distributed = distributed_rallods / COST_OF_LIVING - self.countrymen population_change = int( (surplus_distributed / 10) + (polltion_control_spendings / POLLUTION_CONTROL_FACTOR) - ((INITIAL_LAND - self.land) / 50) - (self.died_contrymen / 2) ) print(f"{abs(population_change)} COUNTRYMEN ", end="") if population_change < 0: print("LEFT ", end="") else: print("CAME TO ", end="") print("THE ISLAND") self.countrymen += population_change 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!!!!!") print("\n\n\nDUE TO THIS EXTREME MISMANAGEMENT, YOU HAVE NOT ONLY") print("BEEN IMPEACHED AND THROWN OUT OF OFFICE, BUT YOU") message = randint(0, 10) if message <= 3: print("ALSO HAD YOUR LEFT EYE GOUGED OUT!") if message <= 6: print("HAVE ALSO GAINED A VERY BAD REPUTATION.") if message <= 10: print("HAVE ALSO BEEN DECLARED NATIONAL FINK.") sys.exit() def handle_third_died(self) -> None: 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.") self.end_game() def handle_money_mismanagement(self) -> None: 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.") sys.exit() def handle_too_many_foreigners(self) -> None: print("\n\nTHE NUMBER OF FOREIGN WORKERS HAS EXCEEDED THE NUMBER") print("OF COUNTRYMEN. AS A MINORITY, THEY HAVE REVOLTED AND") print("TAKEN OVER THE COUNTRY.") self.end_game() def end_game(self) -> None: if random() <= 0.5: print("YOU HAVE BEEN ASSASSINATED.") else: print("YOU HAVE BEEN THROWN OUT OF OFFICE AND ARE NOW") print("RESIDING IN PRISON.") sys.exit() def handle_congratulations(self) -> None: print("\n\nCONGRATULATIONS!!!!!!!!!!!!!!!!!!") print(f"YOU HAVE SUCCESFULLY COMPLETED YOUR {YEARS_IN_TERM} 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.") sys.exit() def print_header() -> None: print(" " * 34 + "KING") print(" " * 15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n") def print_instructions() -> None: print( f"""\n\n\nCONGRATULATIONS! 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 {COST_OF_LIVING} 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 {YEARS_IN_TERM} YEAR TERM OF OFFICE. GOOD LUCK!""" ) def ask_how_many_sq_to_plant(state: GameState) -> int: while True: sq = ask_int("HOW MANY SQUARE MILES DO YOU WISH TO PLANT? ") if sq < 0: continue elif sq > 2 * state.countrymen: print(" SORRY, BUT EACH COUNTRYMAN CAN ONLY PLANT 2 SQ. MILES.") elif sq > state.farmland: print( f" SORRY, BUT YOU ONLY HAVE {state.farmland} " "SQ. MILES OF FARM LAND." ) elif sq * state.planting_cost > state.rallods: print( f" THINK AGAIN. YOU'VE ONLY {state.rallods} RALLODS " "LEFT IN THE TREASURY." ) else: return sq def ask_pollution_control(state: GameState) -> int: while True: rallods = ask_int( "HOW MANY RALLODS DO YOU WISH TO SPEND ON POLLUTION CONTROL? " ) if rallods > state.rallods: print(f" THINK AGAIN. YOU ONLY HAVE {state.rallods} RALLODS REMAINING.") elif rallods < 0: continue else: return rallods def ask_sell_to_industry(state: GameState) -> int: had_first_err = False first = """(FOREIGN INDUSTRY WILL ONLY BUY FARM LAND BECAUSE FOREST LAND IS UNECONOMICAL TO STRIP MINE DUE TO TREES, THICKER TOP SOIL, ETC.)""" err = f"""*** THINK AGAIN. YOU ONLY HAVE {state.farmland} SQUARE MILES OF FARM LAND.""" while True: sm = input("HOW MANY SQUARE MILES DO YOU WISH TO SELL TO INDUSTRY? ") try: sm_sell = int(sm) except ValueError: if not had_first_err: print(first) had_first_err = True print(err) continue if sm_sell > state.farmland: print(err) elif sm_sell < 0: continue else: return sm_sell def ask_distribute_rallods(state: GameState) -> int: while True: rallods = ask_int( "HOW MANY RALLODS WILL YOU DISTRIBUTE AMONG YOUR COUNTRYMEN? " ) if rallods < 0: continue elif rallods > state.rallods: print( f" THINK AGAIN. YOU'VE ONLY {state.rallods} RALLODS IN THE TREASURY" ) else: return rallods def resume() -> GameState: while True: years = ask_int("HOW MANY YEARS HAD YOU BEEN IN OFFICE WHEN INTERRUPTED? ") if years < 0: sys.exit() if years >= YEARS_IN_TERM: print(f" COME ON, YOUR TERM IN OFFICE IS ONLY {YEARS_IN_TERM} YEARS.") else: break treasury = ask_int("HOW MUCH DID YOU HAVE IN THE TREASURY? ") if treasury < 0: sys.exit() countrymen = ask_int("HOW MANY COUNTRYMEN? ") if countrymen < 0: sys.exit() workers = ask_int("HOW MANY WORKERS? ") if workers < 0: sys.exit() while True: land = ask_int("HOW MANY SQUARE MILES OF LAND? ") if land < 0: sys.exit() if land > INITIAL_LAND: farm_land = INITIAL_LAND - FOREST_LAND print(f" COME ON, YOU STARTED WITH {farm_land:,} SQ. MILES OF FARM LAND") print(f" AND {FOREST_LAND:,} SQ. MILES OF FOREST LAND.") if land > FOREST_LAND: break return GameState( rallods=treasury, countrymen=countrymen, foreign_workers=workers, years_in_office=years, ) def main() -> None: print_header() want_instructions = input("DO YOU WANT INSTRUCTIONS? ").upper() if want_instructions == "AGAIN": state = resume() else: state = GameState( rallods=randint(59000, 61000), countrymen=randint(490, 510), planting_cost=randint(10, 15), ) if want_instructions != "NO": print_instructions() while True: state.set_market_conditions() state.print_status() # Users actions sm_sell_to_industry = ask_sell_to_industry(state) state.sell_land(sm_sell_to_industry) distributed_rallods = ask_distribute_rallods(state) state.distribute_rallods(distributed_rallods) planted_sq = ask_how_many_sq_to_plant(state) state.plant(planted_sq) polltion_control_spendings = ask_pollution_control(state) state.spend_pollution_control(polltion_control_spendings) # Run the year state.handle_deaths(distributed_rallods, polltion_control_spendings) state.handle_foreign_workers( sm_sell_to_industry, distributed_rallods, polltion_control_spendings ) state.handle_harvest(planted_sq) state.handle_tourist_trade() if state.died_contrymen > 200: state.handle_too_many_deaths() if state.countrymen < 343: state.handle_third_died() elif ( 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() elif YEARS_IN_TERM - 1 == state.years_in_office: state.handle_congratulations() else: state.years_in_office += 1 state.died_contrymen = 0 if __name__ == "__main__": main() ================================================ FILE: 53_King/python/king_variable_update.py ================================================ ================================================ FILE: 53_King/ruby/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Ruby](https://www.ruby-lang.org/en/) ================================================ FILE: 53_King/rust/Cargo.toml ================================================ [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" ================================================ FILE: 53_King/rust/README.md ================================================ 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. ================================================ FILE: 53_King/rust/src/main.rs ================================================ #![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); } } } } ================================================ FILE: 53_King/vbnet/King.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30114.105 MinimumVisualStudioVersion = 10.0.40219.1 Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "King", "King.vbproj", "{043D7CC7-1FFC-438E-BB00-31CB467BEB73}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {043D7CC7-1FFC-438E-BB00-31CB467BEB73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {043D7CC7-1FFC-438E-BB00-31CB467BEB73}.Debug|Any CPU.Build.0 = Debug|Any CPU {043D7CC7-1FFC-438E-BB00-31CB467BEB73}.Release|Any CPU.ActiveCfg = Release|Any CPU {043D7CC7-1FFC-438E-BB00-31CB467BEB73}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal ================================================ FILE: 53_King/vbnet/King.vbproj ================================================ Exe King net6.0 16.9 ================================================ FILE: 53_King/vbnet/README.md ================================================ Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET) ================================================ FILE: 54_Letter/README.md ================================================ ### Letter LETTER is similar to the game GUESS in which you guess a number chosen by the computer; in this program, the computer picks a random letter of the alphabet and you must guess which one it is using the clues provided as you go along. It should not take you more than five guesses to get the mystery letter. The program which appears here is loosely based on the original written by Bob Albrect of People’s Computer Company. --- As published in Basic Computer Games (1978): - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=99) - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=114) Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html #### Porting Notes (please note any difficulties or challenges in porting here) ================================================ FILE: 54_Letter/csharp/Game.cs ================================================ namespace Letter { internal static class Game { /// /// Maximum number of guesses. /// Note the program doesn't enforce this - it just displays a message if this is exceeded. /// private const int MaximumGuesses = 5; /// /// Main game loop. /// public static void Play() { DisplayIntroductionText(); // Keep playing forever, or until the user quits. while (true) { PlayRound(); } } /// /// Play a single round. /// internal static void PlayRound() { var gameState = new GameState(); DisplayRoundIntroduction(); char letterInput = '\0'; // Set the initial character to something that's not A-Z. while (letterInput != gameState.Letter) { letterInput = GetCharacterFromKeyboard(); gameState.GuessesSoFar++; DisplayGuessResult(gameState.Letter, letterInput); } DisplaySuccessMessage(gameState); } /// /// Display an introduction when the game loads. /// internal static void DisplayIntroductionText() { Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine("LETTER"); Console.WriteLine("Creative Computing, Morristown, New Jersey."); Console.WriteLine(""); Console.ForegroundColor = ConsoleColor.DarkGreen; Console.WriteLine("Letter Guessing Game"); Console.WriteLine("I'll think of a letter of the alphabet, A to Z."); Console.WriteLine("Try to guess my letter and I'll give you clues"); Console.WriteLine("as to how close you're getting to my letter."); Console.WriteLine(""); Console.ResetColor(); } /// /// Display introductionary text for each round. /// internal static void DisplayRoundIntroduction() { Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine("O.K., I have a letter. Start guessing."); Console.ResetColor(); } /// /// Display text depending whether the guess is lower or higher. /// internal static void DisplayGuessResult(char letterToGuess, char letterInput) { Console.BackgroundColor = ConsoleColor.White; Console.ForegroundColor = ConsoleColor.Black; Console.Write(" " + letterInput + " "); Console.ResetColor(); Console.ForegroundColor = ConsoleColor.Gray; Console.Write(" "); if (letterInput != letterToGuess) { if (letterInput > letterToGuess) { Console.WriteLine("Too high. Try a lower letter"); } else { Console.WriteLine("Too low. Try a higher letter"); } } Console.ResetColor(); } /// /// Display success, and the number of guesses. /// internal static void DisplaySuccessMessage(GameState gameState) { Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine($"You got it in {gameState.GuessesSoFar} guesses!!"); if (gameState.GuessesSoFar > MaximumGuesses) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine($"But it shouldn't take more than {MaximumGuesses} guesses!"); } else { Console.WriteLine("Good job !!!!!"); } Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine(""); Console.WriteLine("Let's play again....."); Console.ResetColor(); } /// /// Get valid input from the keyboard: must be an alpha character. Converts to upper case if necessary. /// internal static char GetCharacterFromKeyboard() { char letterInput; do { var keyPressed = Console.ReadKey(true); letterInput = Char.ToUpper(keyPressed.KeyChar); // Convert to upper case immediately. } while (!Char.IsLetter(letterInput)); // If the input is not a letter, wait for another letter to be pressed. return letterInput; } } } ================================================ FILE: 54_Letter/csharp/GameState.cs ================================================ namespace Letter { /// /// Holds the current state. /// internal class GameState { /// /// Initialise the game state with a random letter. /// public GameState() { Letter = GetRandomLetter(); GuessesSoFar = 0; } /// /// The letter that the user is guessing. /// public char Letter { get; set; } /// /// The number of guesses the user has had so far. /// public int GuessesSoFar { get; set; } /// /// Get a random character (A-Z) for the user to guess. /// internal static char GetRandomLetter() { var random = new Random(); var randomNumber = random.Next(0, 26); return (char)('A' + randomNumber); } } } ================================================ FILE: 54_Letter/csharp/Letter.csproj ================================================ Exe net6.0 10 enable enable ================================================ FILE: 54_Letter/csharp/Letter.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30114.105 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Letter", "Letter.csproj", "{8BE20DCF-A729-46ED-92EA-55866844EB93}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {8BE20DCF-A729-46ED-92EA-55866844EB93}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8BE20DCF-A729-46ED-92EA-55866844EB93}.Debug|Any CPU.Build.0 = Debug|Any CPU {8BE20DCF-A729-46ED-92EA-55866844EB93}.Release|Any CPU.ActiveCfg = Release|Any CPU {8BE20DCF-A729-46ED-92EA-55866844EB93}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal ================================================ FILE: 54_Letter/csharp/Program.cs ================================================ using Letter; Game.Play(); ================================================ FILE: 54_Letter/csharp/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/) ================================================ FILE: 54_Letter/java/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Oracle Java](https://openjdk.java.net/) ================================================ FILE: 54_Letter/java/src/Letter.java ================================================ import java.awt.*; import java.util.Arrays; import java.util.Scanner; /** * Game of Letter *

    * Based on the Basic game of Letter here * https://github.com/coding-horror/basic-computer-games/blob/main/54%20Letter/letter.bas *

    * Note: The idea was to create a version of the 1970's Basic game in Java, without introducing * new features - no additional text, error checking, etc has been added. */ public class Letter { public static final int OPTIMAL_GUESSES = 5; public static final int ASCII_A = 65; public static final int ALL_LETTERS = 26; private enum GAME_STATE { STARTUP, INIT, GUESSING, RESULTS, GAME_OVER } // Used for keyboard input private final Scanner kbScanner; // Current game state private GAME_STATE gameState; // Players guess count; private int playerGuesses; // Computers ascii code for a random letter between A..Z private int computersLetter; public Letter() { gameState = GAME_STATE.STARTUP; // Initialise kb scanner kbScanner = new Scanner(System.in); } /** * Main game loop */ public void play() { do { switch (gameState) { // Show an introduction the first time the game is played. case STARTUP: intro(); gameState = GAME_STATE.INIT; break; case INIT: playerGuesses = 0; computersLetter = ASCII_A + (int) (Math.random() * ALL_LETTERS); System.out.println("O.K., I HAVE A LETTER. START GUESSING."); gameState = GAME_STATE.GUESSING; break; // Player guesses the number until they get it or run out of guesses case GUESSING: String playerGuess = displayTextAndGetInput("WHAT IS YOUR GUESS? ").toUpperCase(); // Convert first character of input string to ascii int toAscii = playerGuess.charAt(0); playerGuesses++; if (toAscii == computersLetter) { gameState = GAME_STATE.RESULTS; break; } if (toAscii > computersLetter) { System.out.println("TOO HIGH. TRY A LOWER LETTER."); } else { System.out.println("TOO LOW. TRY A HIGHER LETTER."); } break; // Play again, or exit game? case RESULTS: System.out.println(); System.out.println("YOU GOT IT IN " + playerGuesses + " GUESSES!!"); if (playerGuesses <= OPTIMAL_GUESSES) { System.out.println("GOOD JOB !!!!!"); // Original game beeped 15 tims if you guessed in the optimal guesses or less // Changed this to do a single beep only Toolkit.getDefaultToolkit().beep(); } else { // Took more than optimal number of guesses System.out.println("BUT IT SHOULDN'T TAKE MORE THAN " + OPTIMAL_GUESSES + " GUESSES!"); } System.out.println(); System.out.println("LET'S PLAN AGAIN....."); gameState = GAME_STATE.INIT; break; } } while (gameState != GAME_STATE.GAME_OVER); } public void intro() { System.out.println(simulateTabs(33) + "LETTER"); System.out.println(simulateTabs(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println(); System.out.println("LETTER GUESSING GAME"); System.out.println(); System.out.println("I'LL THINK OF A LETTER OF THE ALPHABET, A TO Z."); System.out.println("TRY TO GUESS MY LETTER AND I'LL GIVE YOU CLUES"); System.out.println("AS TO HOW CLOSE YOU'RE GETTING TO MY LETTER."); } /** * Simulate the old basic tab(xx) command which indented text by xx spaces. * * @param spaces number of spaces required * @return String with number of spaces */ private String simulateTabs(int spaces) { char[] spacesTemp = new char[spaces]; Arrays.fill(spacesTemp, ' '); return new String(spacesTemp); } /* * Print a message on the screen, then accept input from Keyboard. * * @param text message to be displayed on screen. * @return what was typed by the player. */ private String displayTextAndGetInput(String text) { System.out.print(text); return kbScanner.next(); } } ================================================ FILE: 54_Letter/java/src/LetterGame.java ================================================ public class LetterGame { public static void main(String[] args) { Letter letter = new Letter(); letter.play(); } } ================================================ FILE: 54_Letter/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 54_Letter/javascript/letter.html ================================================ LETTER

    
    
    
    
    
    
    ================================================
    FILE: 54_Letter/javascript/letter.js
    ================================================
    // LETTER
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    // Main program
    async function main()
    {
        print(tab(33) + "LETTER\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        print("LETTER GUESSING GAME\n");
        print("\n");
        print("I'LL THINK OF A LETTER OF THE ALPHABET, A TO Z.\n");
        print("TRY TO GUESS MY LETTER AND I'LL GIVE YOU CLUES\n");
        print("AS TO HOW CLOSE YOU'RE GETTING TO MY LETTER.\n");
        while (1) {
            l = 65 + Math.floor(26 * Math.random());
            g = 0;
            print("\n");
            print("O.K., I HAVE A LETTER.  START GUESSING.\n");
            while (1) {
    
                print("\n");
                print("WHAT IS YOUR GUESS");
                g++;
                str = await input();
                a = str.charCodeAt(0);
                print("\n");
                if (a == l)
                    break;
                if (a < l) {
                    print("TOO LOW.  TRY A HIGHER LETTER.\n");
                } else {
                    print("TOO HIGH.  TRY A LOWER LETTER.\n");
                }
            }
            print("\n");
            print("YOU GOT IT IN " + g + " GUESSES!!\n");
            if (g > 5) {
                print("BUT IT SHOULDN'T TAKE MORE THAN 5 GUESSES!\n");
            } else {
                print("GOOD JOB !!!!!\n");
            }
            print("\n");
            print("LET'S PLAY AGAIN.....");
        }
    }
    
    main();
    
    
    ================================================
    FILE: 54_Letter/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 54_Letter/letter.bas
    ================================================
    10 PRINT TAB(33);"LETTER"
    20 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    30 PRINT:PRINT:PRINT
    100 PRINT "LETTER GUESSING GAME": PRINT
    210 PRINT "I'LL THINK OF A LETTER OF THE ALPHABET, A TO Z."
    220 PRINT "TRY TO GUESS MY LETTER AND I'LL GIVE YOU CLUES"
    230 PRINT "AS TO HOW CLOSE YOU'RE GETTING TO MY LETTER."
    310 L=65+INT(RND(1)*26)
    320 G=0
    340 PRINT: PRINT "O.K., I HAVE A LETTER.  START GUESSING."
    410 PRINT: PRINT "WHAT IS YOUR GUESS";
    420 G=G+1
    430 INPUT A$: A=ASC(A$): PRINT
    440 IF A=L THEN 500
    450 IF A>L THEN 480
    460 PRINT "TOO LOW.  TRY A HIGHER LETTER.": GOTO 410
    480 PRINT "TOO HIGH.  TRY A LOWER LETTER.": GOTO 410
    500 PRINT: PRINT "YOU GOT IT IN";G;"GUESSES!!"
    504 IF G<=5 THEN 508
    506 PRINT "BUT IT SHOULDN'T TAKE MORE THAN 5 GUESSES!": GOTO 515
    508 PRINT "GOOD JOB !!!!!"
    510 FOR N=1 TO 15: PRINT CHR$(7);: NEXT N
    515 PRINT
    520 PRINT "LET'S PLAY AGAIN....."
    530 GOTO 310
    999 END
    
    
    ================================================
    FILE: 54_Letter/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 54_Letter/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 54_Letter/perl/letter.pl
    ================================================
    #!/usr/bin/perl
    use strict;
    
    print ' 'x33 . "LETTER\n";
    print ' 'x15 . "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n";
    print "\n\n\n";
    
    print "LETTER GUESSING GAME\n\n";
    print "I'LL THINK OF A LETTER OF THE ALPHABET, A TO Z.\n";
    print "TRY TO GUESS MY LETTER AND I'LL GIVE YOU CLUES\n";
    print "AS TO HOW CLOSE YOU'RE GETTING TO MY LETTER.\n";
    
    while (1) {
    	my $letter = 65 + int(rand(26));
    	my $guesses = 0;
    	print "\nO.K., I HAVE A LETTER. START GUESSING.\n";
    
    	my $answer;
    	do {
    		print "\nWHAT IS YOUR GUESS? ";
    		$guesses++;
    		chomp($answer = );
    		$answer = ord($answer);
    		print "\n";
    		print "TOO LOW. TRY A HIGHER LETTER.\n" if $answer < $letter;
    		print "TOO HIGH. TRY A LOWER LETTER.\n" if $answer > $letter;
    	} until($answer eq $letter);
    
    	print "\nYOU GOT IT IN $guesses GUESSES!!\n";
    
    	if ($guesses <= 5) {
    		print "GOOD JOB !!!!!\n";
    		print chr(7) x 15; # ASCII Bell
    	} else {
    		print "BUT IT SHOULDN'T TAKE MORE THAN 5 GUESSES!\n";
    	}
    
    	print "\nLET'S PLAY AGAIN.....\n";
    }
    
    exit;
    
    
    ================================================
    FILE: 54_Letter/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 54_Letter/python/letter.py
    ================================================
    """
    LETTER
    
    A letter guessing game.
    
    Ported by Dave LeCompte
    """
    
    import random
    
    # The original code printed character 7, the "BELL" character 15 times
    # when the player won. Many modern systems do not support this, and in
    # any case, it can quickly become annoying, so it is disabled here.
    
    BELLS_ON_SUCCESS = False
    
    
    def print_instructions() -> None:
        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.")
    
    
    def play_game() -> None:
        target_value = random.randint(ord("A"), ord("Z"))
        num_guesses = 0
        print()
        print("O.K., I HAVE A LETTER.  START GUESSING.")
        print()
        while True:
            print("WHAT IS YOUR GUESS?")
            num_guesses += 1
            guess = ord(input())
            print()
            if guess == target_value:
                print()
                print(f"YOU GOT IT IN {num_guesses} GUESSES!!")
                if num_guesses > 5:
                    print("BUT IT SHOULDN'T TAKE MORE THAN 5 GUESSES!")
                    # goto 515
                print("GOOD JOB !!!!!")
    
                if BELLS_ON_SUCCESS:
                    bell_str = chr(7) * 15
                    print(bell_str)
    
                print()
                print("LET'S PLAY AGAIN.....")
                return
            elif guess > target_value:
                print("TOO HIGH. TRY A LOWER LETTER.")
                continue
            else:
                print("TOO LOW. TRY A HIGHER LETTER.")
                continue
    
    
    def main() -> None:
        print(" " * 33 + "LETTER")
        print(" " * 15 + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n\n\n")
    
        print_instructions()
    
        while True:
            play_game()
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 54_Letter/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 54_Letter/ruby/letter.rb
    ================================================
    #!/usr/bin/env ruby
    
    # Kinema
    # reinterpreted from BASIC by stephan.com
    
    puts 'Letter'.center(80)
    puts 'Adapted by stephan.com'.center(80)
    puts "\n\n\n"
    
    puts "Letter guessing game\n\n"
    
    puts "I'll think of a letter of the alphabet, A to Z."
    puts "Try to guess my letter and I'll give you clues"
    puts "as to how close you're getting to my letter."
    
    def win(turns)
      puts "\nyou got it in #{turns} guesses!!"
      return puts "but it shouldn't take more than 5 guesses!" if turns > 5
    
      puts "good job !!!!!\a\a\a"
    end
    
    def play
      letter = ('A'..'Z').to_a.sample
      guess = nil
      turn = 0
    
      puts "\nO.K., I have a letter.  Start guessing."
    
      until guess == letter
        puts "\nWhat is your guess?"
    
        guess = gets.strip.chars.first.upcase
        turn += 1
    
        puts 'Too low.  Try a higher letter.' if guess < letter
        puts 'Too high.  Try a lower letter.' if guess > letter
      end
      win(turn)
    end
    
    loop do
      play
      puts "\nlet's play again....."
    end
    
    
    ================================================
    FILE: 54_Letter/rust/Cargo.toml
    ================================================
    [package]
    name = "letter"
    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.4"
    
    
    
    ================================================
    FILE: 54_Letter/rust/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Rust](https://www.rust-lang.org/)
    
    
    ================================================
    FILE: 54_Letter/rust/src/main.rs
    ================================================
    use rand::Rng;
    use std::cmp::Ordering;
    use std::io;
    
    fn main() {
        println!(
            "{: >40}\n{: >57}\n\n\n",
            "LETTER", "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
        );
        println!("LETTER GUESSING GAME\n");
        println!("I'LL THINK OF A LETTER OF THE ALPHABET, A TO Z.");
        println!("TRY TO GUESS MY LETTER AND I'LL GIVE YOU CLUES");
        println!("AS TO HOW CLOSE YOU'RE GETTING TO MY LETTER.");
    
        loop {
            let gen_character = rand::thread_rng().gen_range('A'..='Z'); // generates a random character between A and Z
            let gen_character = String::from(gen_character);
            println!("\nO.K., I HAVE A LETTER.  START GUESSING.");
            for i in 0..999999 {
                println!("\nWHAT IS YOUR GUESS?");
    
                let mut guess = String::new();
    
                io::stdin()
                    .read_line(&mut guess)
                    .expect("Failed to read the line");
                println!("{}", gen_character);
                let guess = guess.trim().to_ascii_uppercase();
                match guess.cmp(&gen_character) {
                    Ordering::Less => println!("\nTOO LOW.  TRY A HIGHER LETTER."),
                    Ordering::Greater => println!("\nTOO HIGH.  TRY A LOWER LETTER."),
                    Ordering::Equal => {
                        println!("\nYOU GOT IT IN {} GUESSES!!", i + 1);
                        if i >= 4 {
                            println!("BUT IT SHOULDN'T TAKE MORE THAN 5 GUESSES!\n");
                        } else {
                            println!("{}", std::iter::repeat("💖").take(15).collect::());
                            println!("GOOD JOB !!!!!");
                        }
                        break;
                    }
                }
            }
            println!("\nLET'S PLAY AGAIN.....");
        }
    }
    
    
    ================================================
    FILE: 54_Letter/vbnet/Letter.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Letter", "Letter.vbproj", "{15392FFC-0EBB-4CA3-970F-8AC32DE84724}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{15392FFC-0EBB-4CA3-970F-8AC32DE84724}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{15392FFC-0EBB-4CA3-970F-8AC32DE84724}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{15392FFC-0EBB-4CA3-970F-8AC32DE84724}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{15392FFC-0EBB-4CA3-970F-8AC32DE84724}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 54_Letter/vbnet/Letter.vbproj
    ================================================
    
      
        Exe
        Letter
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 54_Letter/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 55_Life/README.md
    ================================================
    ### Life
    
    The Game of Life was originally described in _Scientific American_, October 1970, in an article by Martin Gardner. The game itself was originated by John Conway of Gonville and Caius College, University of Cambridge England.
    
    In the “manual” game, organisms exist in the form of counters (chips or checkers) on a large checkerboard and die or reproduce according to some simple genetic rules. Conway’s criteria for choosing his genetic laws were carefully delineated as follows:
    1. There should be no initial pattern for which there is a simple proof that the population can grow without limit.
    2. There should be simple initial patterns that apparently do grow without limit.
    3. There should be simple initial patterns that grow and change for a considerable period of time before coming to an end in three possible ways:
        1. Fading away completely (from overcrowding or from becoming too sparse)
        2. Settling into a stable configuration that remains unchanged thereafter
        3. Entering an oscillating phase in which they repeat an endless cycle of two or more periods
    
    In brief, the rules should be such as to make the behavior of the population relatively unpredictable. Conway’s genetic laws are delightfully simple. First note that each cell of the checkerboard (assumed to be an infinite plane) has eight neighboring cells, four adjacent orthogonally, four adjacent diagonally. The rules are:
    1. Survivals. Every counter with two or three neighboring counters survives for the next generation.
    2. Deaths. Each counter with four or more neighbors dies (is removed) from overpopulation. Every counter with one neighbor or none dies from isolation.
    3. Births. Each empty cell adjacent to exactly three neighbors — no more — is a birth cell. A counter is placed on it at the next move.
    
    It is important to understand that all births and deaths occur simultaneously. Together they constitute a single generation or, as we shall call it, a “move” in the complete “life history” of the initial configuration.
    
    You will find the population constantly undergoing unusual, sometimes beautiful and always unexpected change. In a few cases the society eventually dies out (all counters vanishing), although this may not happen until after a great many generations. Most starting patterns either reach stable figures — Conway calls them “still lifes” — that cannot change or patterns that oscillate forever. Patterns with no initial symmetry tend to become symmetrical. Once this happens the symmetry cannot be lost, although it may increase in richness.
    
    Conway used a DEC PDP-7 with a graphic display to observe long-lived populations. You’ll probably find this more enjoyable to watch on a CRT than a hard-copy terminal.
    
    Since MITS 8K BASIC does not have LINE INPUT, to enter leading blanks in the patter, type a “.” at the start of the line. This will be converted to a space by BASIC, but it permits you to type leading spaces. Typing DONE indicates that you are finished entering the pattern. See sample run.
    
    Clark Baker of Project DELTA originally wrote this version of LIFE which was further modified by Steve North of Creative Computing.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=100)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=115)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    
    #### 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)
    
    
    ================================================
    FILE: 55_Life/csharp/.gitignore
    ================================================
    
    # Created by https://www.toptal.com/developers/gitignore/api/dotnetcore,rider,visualstudio,visualstudiocode
    # Edit at https://www.toptal.com/developers/gitignore?templates=dotnetcore,rider,visualstudio,visualstudiocode
    
    ### DotnetCore ###
    # .NET Core build folders
    bin/
    obj/
    
    # Common node modules locations
    /node_modules
    /wwwroot/node_modules
    
    ### Rider ###
    # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
    # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
    
    # User-specific stuff
    .idea/**/workspace.xml
    .idea/**/tasks.xml
    .idea/**/usage.statistics.xml
    .idea/**/dictionaries
    .idea/**/shelf
    
    # AWS User-specific
    .idea/**/aws.xml
    
    # Generated files
    .idea/**/contentModel.xml
    
    # Sensitive or high-churn files
    .idea/**/dataSources/
    .idea/**/dataSources.ids
    .idea/**/dataSources.local.xml
    .idea/**/sqlDataSources.xml
    .idea/**/dynamic.xml
    .idea/**/uiDesigner.xml
    .idea/**/dbnavigator.xml
    
    # Gradle
    .idea/**/gradle.xml
    .idea/**/libraries
    
    # Gradle and Maven with auto-import
    # When using Gradle or Maven with auto-import, you should exclude module files,
    # since they will be recreated, and may cause churn.  Uncomment if using
    # auto-import.
    # .idea/artifacts
    # .idea/compiler.xml
    # .idea/jarRepositories.xml
    # .idea/modules.xml
    # .idea/*.iml
    # .idea/modules
    # *.iml
    # *.ipr
    
    # CMake
    cmake-build-*/
    
    # Mongo Explorer plugin
    .idea/**/mongoSettings.xml
    
    # File-based project format
    *.iws
    
    # IntelliJ
    out/
    
    # mpeltonen/sbt-idea plugin
    .idea_modules/
    
    # JIRA plugin
    atlassian-ide-plugin.xml
    
    # Cursive Clojure plugin
    .idea/replstate.xml
    
    # SonarLint plugin
    .idea/sonarlint/
    
    # Crashlytics plugin (for Android Studio and IntelliJ)
    com_crashlytics_export_strings.xml
    crashlytics.properties
    crashlytics-build.properties
    fabric.properties
    
    # Editor-based Rest Client
    .idea/httpRequests
    
    # Android studio 3.1+ serialized cache file
    .idea/caches/build_file_checksums.ser
    
    ### VisualStudioCode ###
    .vscode/*
    !.vscode/settings.json
    !.vscode/tasks.json
    !.vscode/launch.json
    !.vscode/extensions.json
    !.vscode/*.code-snippets
    
    # Local History for Visual Studio Code
    .history/
    
    # Built Visual Studio Code Extensions
    *.vsix
    
    ### VisualStudioCode Patch ###
    # Ignore all local history of files
    .history
    .ionide
    
    # Support for Project snippet scope
    
    ### VisualStudio ###
    ## Ignore Visual Studio temporary files, build results, and
    ## files generated by popular Visual Studio add-ons.
    ##
    ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
    
    # User-specific files
    *.rsuser
    *.suo
    *.user
    *.userosscache
    *.sln.docstates
    
    # User-specific files (MonoDevelop/Xamarin Studio)
    *.userprefs
    
    # Mono auto generated files
    mono_crash.*
    
    # Build results
    [Dd]ebug/
    [Dd]ebugPublic/
    [Rr]elease/
    [Rr]eleases/
    x64/
    x86/
    [Ww][Ii][Nn]32/
    [Aa][Rr][Mm]/
    [Aa][Rr][Mm]64/
    bld/
    [Bb]in/
    [Oo]bj/
    [Ll]og/
    [Ll]ogs/
    
    # Visual Studio 2015/2017 cache/options directory
    .vs/
    # Uncomment if you have tasks that create the project's static files in wwwroot
    #wwwroot/
    
    # Visual Studio 2017 auto generated files
    Generated\ Files/
    
    # MSTest test Results
    [Tt]est[Rr]esult*/
    [Bb]uild[Ll]og.*
    
    # NUnit
    *.VisualState.xml
    TestResult.xml
    nunit-*.xml
    
    # Build Results of an ATL Project
    [Dd]ebugPS/
    [Rr]eleasePS/
    dlldata.c
    
    # Benchmark Results
    BenchmarkDotNet.Artifacts/
    
    # .NET Core
    project.lock.json
    project.fragment.lock.json
    artifacts/
    
    # ASP.NET Scaffolding
    ScaffoldingReadMe.txt
    
    # StyleCop
    StyleCopReport.xml
    
    # Files built by Visual Studio
    *_i.c
    *_p.c
    *_h.h
    *.ilk
    *.meta
    *.obj
    *.iobj
    *.pch
    *.pdb
    *.ipdb
    *.pgc
    *.pgd
    *.rsp
    *.sbr
    *.tlb
    *.tli
    *.tlh
    *.tmp
    *.tmp_proj
    *_wpftmp.csproj
    *.log
    *.tlog
    *.vspscc
    *.vssscc
    .builds
    *.pidb
    *.svclog
    *.scc
    
    # Chutzpah Test files
    _Chutzpah*
    
    # Visual C++ cache files
    ipch/
    *.aps
    *.ncb
    *.opendb
    *.opensdf
    *.sdf
    *.cachefile
    *.VC.db
    *.VC.VC.opendb
    
    # Visual Studio profiler
    *.psess
    *.vsp
    *.vspx
    *.sap
    
    # Visual Studio Trace Files
    *.e2e
    
    # TFS 2012 Local Workspace
    $tf/
    
    # Guidance Automation Toolkit
    *.gpState
    
    # ReSharper is a .NET coding add-in
    _ReSharper*/
    *.[Rr]e[Ss]harper
    *.DotSettings.user
    
    # TeamCity is a build add-in
    _TeamCity*
    
    # DotCover is a Code Coverage Tool
    *.dotCover
    
    # AxoCover is a Code Coverage Tool
    .axoCover/*
    !.axoCover/settings.json
    
    # Coverlet is a free, cross platform Code Coverage Tool
    coverage*.json
    coverage*.xml
    coverage*.info
    
    # Visual Studio code coverage results
    *.coverage
    *.coveragexml
    
    # NCrunch
    _NCrunch_*
    .*crunch*.local.xml
    nCrunchTemp_*
    
    # MightyMoose
    *.mm.*
    AutoTest.Net/
    
    # Web workbench (sass)
    .sass-cache/
    
    # Installshield output folder
    [Ee]xpress/
    
    # DocProject is a documentation generator add-in
    DocProject/buildhelp/
    DocProject/Help/*.HxT
    DocProject/Help/*.HxC
    DocProject/Help/*.hhc
    DocProject/Help/*.hhk
    DocProject/Help/*.hhp
    DocProject/Help/Html2
    DocProject/Help/html
    
    # Click-Once directory
    publish/
    
    # Publish Web Output
    *.[Pp]ublish.xml
    *.azurePubxml
    # Note: Comment the next line if you want to checkin your web deploy settings,
    # but database connection strings (with potential passwords) will be unencrypted
    *.pubxml
    *.publishproj
    
    # Microsoft Azure Web App publish settings. Comment the next line if you want to
    # checkin your Azure Web App publish settings, but sensitive information contained
    # in these scripts will be unencrypted
    PublishScripts/
    
    # NuGet Packages
    *.nupkg
    # NuGet Symbol Packages
    *.snupkg
    # The packages folder can be ignored because of Package Restore
    **/[Pp]ackages/*
    # except build/, which is used as an MSBuild target.
    !**/[Pp]ackages/build/
    # Uncomment if necessary however generally it will be regenerated when needed
    #!**/[Pp]ackages/repositories.config
    # NuGet v3's project.json files produces more ignorable files
    *.nuget.props
    *.nuget.targets
    
    # Microsoft Azure Build Output
    csx/
    *.build.csdef
    
    # Microsoft Azure Emulator
    ecf/
    rcf/
    
    # Windows Store app package directories and files
    AppPackages/
    BundleArtifacts/
    Package.StoreAssociation.xml
    _pkginfo.txt
    *.appx
    *.appxbundle
    *.appxupload
    
    # Visual Studio cache files
    # files ending in .cache can be ignored
    *.[Cc]ache
    # but keep track of directories ending in .cache
    !?*.[Cc]ache/
    
    # Others
    ClientBin/
    ~$*
    *~
    *.dbmdl
    *.dbproj.schemaview
    *.jfm
    *.pfx
    *.publishsettings
    orleans.codegen.cs
    
    # Including strong name files can present a security risk
    # (https://github.com/github/gitignore/pull/2483#issue-259490424)
    #*.snk
    
    # Since there are multiple workflows, uncomment next line to ignore bower_components
    # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
    #bower_components/
    
    # RIA/Silverlight projects
    Generated_Code/
    
    # Backup & report files from converting an old project file
    # to a newer Visual Studio version. Backup files are not needed,
    # because we have git ;-)
    _UpgradeReport_Files/
    Backup*/
    UpgradeLog*.XML
    UpgradeLog*.htm
    ServiceFabricBackup/
    *.rptproj.bak
    
    # SQL Server files
    *.mdf
    *.ldf
    *.ndf
    
    # Business Intelligence projects
    *.rdl.data
    *.bim.layout
    *.bim_*.settings
    *.rptproj.rsuser
    *- [Bb]ackup.rdl
    *- [Bb]ackup ([0-9]).rdl
    *- [Bb]ackup ([0-9][0-9]).rdl
    
    # Microsoft Fakes
    FakesAssemblies/
    
    # GhostDoc plugin setting file
    *.GhostDoc.xml
    
    # Node.js Tools for Visual Studio
    .ntvs_analysis.dat
    node_modules/
    
    # Visual Studio 6 build log
    *.plg
    
    # Visual Studio 6 workspace options file
    *.opt
    
    # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
    *.vbw
    
    # Visual Studio 6 auto-generated project file (contains which files were open etc.)
    *.vbp
    
    # Visual Studio 6 workspace and project file (working project files containing files to include in project)
    *.dsw
    *.dsp
    
    # Visual Studio 6 technical files
    
    # Visual Studio LightSwitch build output
    **/*.HTMLClient/GeneratedArtifacts
    **/*.DesktopClient/GeneratedArtifacts
    **/*.DesktopClient/ModelManifest.xml
    **/*.Server/GeneratedArtifacts
    **/*.Server/ModelManifest.xml
    _Pvt_Extensions
    
    # Paket dependency manager
    .paket/paket.exe
    paket-files/
    
    # FAKE - F# Make
    .fake/
    
    # CodeRush personal settings
    .cr/personal
    
    # Python Tools for Visual Studio (PTVS)
    __pycache__/
    *.pyc
    
    # Cake - Uncomment if you are using it
    # tools/**
    # !tools/packages.config
    
    # Tabs Studio
    *.tss
    
    # Telerik's JustMock configuration file
    *.jmconfig
    
    # BizTalk build output
    *.btp.cs
    *.btm.cs
    *.odx.cs
    *.xsd.cs
    
    # OpenCover UI analysis results
    OpenCover/
    
    # Azure Stream Analytics local run output
    ASALocalRun/
    
    # MSBuild Binary and Structured Log
    *.binlog
    
    # NVidia Nsight GPU debugger configuration file
    *.nvuser
    
    # MFractors (Xamarin productivity tool) working folder
    .mfractor/
    
    # Local History for Visual Studio
    .localhistory/
    
    # Visual Studio History (VSHistory) files
    .vshistory/
    
    # BeatPulse healthcheck temp database
    healthchecksdb
    
    # Backup folder for Package Reference Convert tool in Visual Studio 2017
    MigrationBackup/
    
    # Ionide (cross platform F# VS Code tools) working folder
    .ionide/
    
    # Fody - auto-generated XML schema
    FodyWeavers.xsd
    
    # VS Code files for those working on multiple tools
    *.code-workspace
    
    # Local History for Visual Studio Code
    
    # Windows Installer files from build outputs
    *.cab
    *.msi
    *.msix
    *.msm
    *.msp
    
    # JetBrains Rider
    *.sln.iml
    
    ### VisualStudio Patch ###
    # Additional files built by Visual Studio
    
    # End of https://www.toptal.com/developers/gitignore/api/dotnetcore,rider,visualstudio,visualstudiocode
    
    
    ================================================
    FILE: 55_Life/csharp/Life.csproj
    ================================================
    
    
        
            Exe
            net6.0
            enable
        
    
    
    
    
    ================================================
    FILE: 55_Life/csharp/Life.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Life", "Life.csproj", "{28B02688-78D1-4B3E-B998-BCC78C292D03}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{28B02688-78D1-4B3E-B998-BCC78C292D03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{28B02688-78D1-4B3E-B998-BCC78C292D03}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{28B02688-78D1-4B3E-B998-BCC78C292D03}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{28B02688-78D1-4B3E-B998-BCC78C292D03}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 55_Life/csharp/Program.cs
    ================================================
    using System.Text;
    
    const int maxWidth = 70;
    const int maxHeight = 24;
    
    Console.WriteLine("ENTER YOUR PATTERN:");
    var pattern = new Pattern(ReadPattern(limitHeight: maxHeight).ToArray());
    
    var minX = 10 - pattern.Height / 2;
    var minY = 34 - pattern.Width / 2;
    var maxX = maxHeight - 1;
    var maxY = maxWidth - 1;
    
    var matrix = new Matrix(height: maxHeight, width: maxWidth);
    var simulation = InitializeSimulation(pattern, matrix);
    
    PrintHeader();
    ProcessSimulation();
    
    IEnumerable ReadPattern(int limitHeight)
    {
        for (var i = 0; i < limitHeight; i++)
        {
            var input = Console.ReadLine();
            if (input.ToUpper() == "DONE")
            {
                break;
            }
    
            // In the original version, BASIC would trim the spaces in the beginning of an input, so the original
            // game allowed you to input an '.' before the spaces to circumvent this limitation. This behavior was
            // kept for compatibility.
            if (input.StartsWith('.'))
                yield return input.Substring(1, input.Length - 1);
    
            yield return input;
        }
    }
    
    void PrintHeader()
    {
        void PrintCentered(string text)
        {
            const int pageWidth = 64;
    
            var spaceCount = (pageWidth - text.Length) / 2;
            Console.Write(new string(' ', spaceCount));
            Console.WriteLine(text);
        }
    
        PrintCentered("LIFE");
        PrintCentered("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
        Console.WriteLine();
        Console.WriteLine();
        Console.WriteLine();
    }
    
    Simulation InitializeSimulation(Pattern pattern, Matrix matrixToInitialize) {
        var newSimulation = new Simulation();
    
        // transcribes the pattern to the middle of the simulation and counts initial population
        for (var x = 0; x < pattern.Height; x++)
        {
            for (var y = 0; y < pattern.Width; y++)
            {
                if (pattern.Content[x][y] == ' ')
                    continue;
    
                matrixToInitialize[minX + x, minY + y] = CellState.Stable;
                newSimulation.IncreasePopulation();
            }
        }
    
        return newSimulation;
    }
    
    TimeSpan GetPauseBetweenIterations()
    {
        if (args.Length != 2) return TimeSpan.Zero;
    
        var parameter = args[0].ToLower();
        if (parameter.Contains("wait"))
        {
            var value = args[1];
            if (int.TryParse(value, out var sleepMilliseconds))
                return TimeSpan.FromMilliseconds(sleepMilliseconds);
        }
    
        return TimeSpan.Zero;
    }
    
    void ProcessSimulation()
    {
        var pauseBetweenIterations = GetPauseBetweenIterations();
        var isInvalid = false;
    
        while (true)
        {
            var invalidText = isInvalid ? "INVALID!" : "";
            Console.WriteLine($"GENERATION: {simulation.Generation}\tPOPULATION: {simulation.Population} {invalidText}");
    
            simulation.StartNewGeneration();
    
            var nextMinX = maxHeight - 1;
            var nextMinY = maxWidth - 1;
            var nextMaxX = 0;
            var nextMaxY = 0;
    
            var matrixOutput = new StringBuilder();
    
            // prints the empty lines before search area
            for (var x = 0; x < minX; x++)
            {
                matrixOutput.AppendLine();
            }
    
            // refreshes the matrix and updates search area
            for (var x = minX; x <= maxX; x++)
            {
                var printedLine = Enumerable.Repeat(' ', maxWidth).ToList();
                for (var y = minY; y <= maxY; y++)
                {
                    if (matrix[x, y] == CellState.Dying)
                    {
                        matrix[x, y] = CellState.Empty;
                        continue;
                    }
                    if (matrix[x, y] == CellState.New)
                    {
                        matrix[x, y] = CellState.Stable;
                    }
                    else if (matrix[x, y] != CellState.Stable)
                    {
                        continue;
                    }
    
                    printedLine[y] = '*';
    
                    nextMinX = Math.Min(x, nextMinX);
                    nextMaxX = Math.Max(x, nextMaxX);
                    nextMinY = Math.Min(y, nextMinY);
                    nextMaxY = Math.Max(y, nextMaxY);
                }
    
                matrixOutput.AppendLine(string.Join(separator: null, values: printedLine));
            }
    
            // prints empty lines after search area
            for (var x = maxX + 1; x < maxHeight; x++)
            {
                matrixOutput.AppendLine();
            }
            Console.Write(matrixOutput);
    
            void UpdateSearchArea()
            {
                minX = nextMinX;
                maxX = nextMaxX;
                minY = nextMinY;
                maxY = nextMaxY;
    
                const int limitX = 21;
                const int limitY = 67;
    
                if (minX < 2)
                {
                    minX = 2;
                    isInvalid = true;
                }
    
                if (maxX > limitX)
                {
                    maxX = limitX;
                    isInvalid = true;
                }
    
                if (minY < 2)
                {
                    minY = 2;
                    isInvalid = true;
                }
    
                if (maxY > limitY)
                {
                    maxY = limitY;
                    isInvalid = true;
                }
            }
            UpdateSearchArea();
    
            for (var x = minX - 1; x <= maxX + 1; x++)
            {
                for (var y = minY - 1; y <= maxY + 1; y++)
                {
                    int CountNeighbors()
                    {
                        var neighbors = 0;
                        for (var i = x - 1; i <= x + 1; i++)
                        {
                            for (var j = y - 1; j <= y + 1; j++)
                            {
                                if (matrix[i, j] == CellState.Stable || matrix[i, j] == CellState.Dying)
                                    neighbors++;
                            }
                        }
    
                        return neighbors;
                    }
    
                    var neighbors = CountNeighbors();
                    if (matrix[x, y] == CellState.Empty)
                    {
                        if (neighbors == 3)
                        {
                            matrix[x, y] = CellState.New;
                            simulation.IncreasePopulation();
                        }
                    }
                    else if (neighbors is < 3 or > 4)
                    {
                        matrix[x, y] = CellState.Dying;
                    }
                    else
                    {
                        simulation.IncreasePopulation();
                    }
                }
            }
    
            // expands search area to accommodate new cells
            minX--;
            minY--;
            maxX++;
            maxY++;
    
            if (pauseBetweenIterations > TimeSpan.Zero)
                Thread.Sleep(pauseBetweenIterations);
        }
    }
    
    public class Pattern
    {
        public string[] Content { get; }
        public int Height { get; }
        public int Width { get; }
    
        public Pattern(IReadOnlyCollection patternLines)
        {
            Height = patternLines.Count;
            Width = patternLines.Max(x => x.Length);
            Content = NormalizeWidth(patternLines);
        }
    
        private string[] NormalizeWidth(IReadOnlyCollection patternLines)
        {
            return patternLines
                .Select(x => x.PadRight(Width, ' '))
                .ToArray();
        }
    }
    
    /// 
    /// Indicates the state of a given cell in the simulation.
    /// 
    internal enum CellState
    {
        Empty = 0,
        Stable = 1,
        Dying = 2,
        New = 3
    }
    
    public class Simulation
    {
        public int Generation { get; private set; }
    
        public int Population { get; private set; }
    
        public void StartNewGeneration()
        {
            Generation++;
            Population = 0;
        }
    
        public void IncreasePopulation()
        {
            Population++;
        }
    }
    
    /// 
    /// This class was created to aid debugging, through the implementation of the ToString() method.
    /// 
    class Matrix
    {
        private readonly CellState[,] _matrix;
    
        public Matrix(int height, int width)
        {
            _matrix = new CellState[height, width];
        }
    
        public CellState this[int x, int y]
        {
            get => _matrix[x, y];
            set => _matrix[x, y] = value;
        }
    
        public override string ToString()
        {
            var stringBuilder = new StringBuilder();
            for (var x = 0; x < _matrix.GetLength(0); x++)
            {
                for (var y = 0; y < _matrix.GetLength(1); y++)
                {
                    var character = _matrix[x, y] == 0 ? " ": ((int)_matrix[x, y]).ToString();
                    stringBuilder.Append(character);
                }
    
                stringBuilder.AppendLine();
            }
            return stringBuilder.ToString();
        }
    }
    
    
    ================================================
    FILE: 55_Life/csharp/README.md
    ================================================
    # Life
    
    An implementation of John Conway's popular cellular automaton, also know as **Conway's Game of Life**. The original source was downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html).
    
    Ported by Dyego Alekssander Maas.
    
    ## How to run
    
    This program requires you to install [.NET 6 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/6.0). After installed, you just need to run `dotnet run` from this directory in the terminal.
    
    ## Know more about Conway's Game of Life
    
    You can find more about Conway's Game of Life on this page of the [Cornell Math Explorers' Club](http://pi.math.cornell.edu/~lipa/mec/lesson6.html), alongside many examples of patterns you can try.
    
    ### Optional parameters
    
    Optionally, you can run this program with the `--wait 1000` argument, the number being the time in milliseconds
    that the application will pause between each iteration. This is enables you to watch the simulation unfolding. By default, there is no pause between iterations.
    
    The complete command would be `dotnet run --wait 1000`.
    
    ## Entering patterns
    
    Once running the game, you are expected to enter a pattern. This pattern consists of multiple lines of text with either **spaces** or **some character**, usually an asterisk (`*`).
    
    Spaces represent empty cells. Asterisks represent alive cells.
    
    After entering the pattern, you need to enter the word "DONE". It is not case sensitive. An example of pattern would be:
    
    ```
     *
    ***
    DONE
    ```
    
    ### Some patterns you could try
    
    ```
     *
    ***
    ```
    
    ```
    *
    ***
    ```
    
    ```
    **
    **
    ```
    
    ```
      *
     *
    *
    ```
    
    This one is known as **glider**:
    
    ```
    ***
    *
     *
    ```
    
    ## Instructions to the port
    
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 55_Life/java/README.md
    ================================================
    # Game of Life - Java version
    
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    ## Requirements
    
    * Requires Java 17 (or later)
    
    ## Notes
    
    The Java version of Game of Life tries to mimics the behaviour of the BASIC version.
    However, the Java code does not have much in common with the original.
    
    **Differences in behaviour:**
    * Input supports the ```.``` character, but it's optional.
    * Evaluation of ```DONE``` input string is case insensitive.
    * Run with the ```-s``` command line argument to halt the program after each generation, and continue when ```ENTER``` is pressed.
    
    
    ================================================
    FILE: 55_Life/java/src/java/Life.java
    ================================================
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Scanner;
    
    /**
     * The Game of Life class.
    *
    * Mimics the behaviour of the BASIC version, however the Java code does not have much in common with the original. *
    * Differences in behaviour: *
      *
    • Input supports the "." character, but it's optional.
    • *
    • Input regarding the "DONE" string is case insensitive.
    • *
    */ public class Life { private static final byte DEAD = 0; private static final byte ALIVE = 1; private static final String NEWLINE = "\n"; private final Scanner consoleReader = new Scanner(System.in); private final byte[][] matrix = new byte[21][67]; private int generation = 0; private int population = 0; boolean stopAfterGen = false; boolean invalid = false; /** * Constructor. * * @param args the command line arguments */ public Life(String[] args) { parse(args); } private void parse(String[] args) { for (String arg : args) { if ("-s".equals(arg)) { stopAfterGen = true; break; } } } /** * Starts the game. */ public void start() { printGameHeader(); readPattern(); while (true) { printGeneration(); advanceToNextGeneration(); if (stopAfterGen) { System.out.print("PRESS ENTER TO CONTINUE"); consoleReader.nextLine(); } } } private void advanceToNextGeneration() { // store all cell transitions in a list, i.e. if a dead cell becomes alive, or a living cell dies List transitions = new ArrayList<>(); // there's still room for optimization: instead of iterating over all cells in the matrix, // we could consider only the section containing the pattern(s), as in the BASIC version for (int y = 0; y < matrix.length; y++) { for (int x = 0; x < matrix[y].length; x++) { int neighbours = countNeighbours(y, x); if (matrix[y][x] == ALIVE) { if (neighbours < 2 || neighbours > 3) { transitions.add(new Transition(y, x, DEAD)); population--; } } else { // cell is dead if (neighbours == 3) { if (x < 2 || x > 67 || y < 2 || y > 21) { invalid = true; } transitions.add(new Transition(y, x, ALIVE)); population++; } } } } // apply all transitions to the matrix transitions.forEach(t -> matrix[t.y()][t.x()] = t.newState()); generation++; } private int countNeighbours(int y, int x) { int neighbours = 0; for (int row = Math.max(y - 1, 0); row <= Math.min(y + 1, matrix.length - 1); row++) { for (int col = Math.max(x - 1, 0); col <= Math.min(x + 1, matrix[row].length - 1); col++) { if (row == y && col == x) { continue; } if (matrix[row][col] == ALIVE) { neighbours++; } } } return neighbours; } private void readPattern() { System.out.println("ENTER YOUR PATTERN:"); List lines = new ArrayList<>(); String line; int maxLineLength = 0; boolean reading = true; while (reading) { System.out.print("? "); line = consoleReader.nextLine(); if (line.equalsIgnoreCase("done")) { reading = false; } else { // optional support for the '.' that is needed in the BASIC version lines.add(line.replace('.', ' ')); maxLineLength = Math.max(maxLineLength, line.length()); } } fillMatrix(lines, maxLineLength); } private void fillMatrix(List lines, int maxLineLength) { float xMin = 33 - maxLineLength / 2f; float yMin = 11 - lines.size() / 2f; for (int y = 0; y < lines.size(); y++) { String line = lines.get(y); for (int x = 1; x <= line.length(); x++) { if (line.charAt(x-1) == '*') { matrix[floor(yMin + y)][floor(xMin + x)] = ALIVE; population++; } } } } private int floor(float f) { return (int) Math.floor(f); } private void printGameHeader() { printIndented(34, "LIFE"); printIndented(15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println(NEWLINE.repeat(3)); } private void printIndented(int spaces, String str) { System.out.println(" ".repeat(spaces) + str); } private void printGeneration() { printGenerationHeader(); for (int y = 0; y < matrix.length; y++) { for (int x = 0; x < matrix[y].length; x++) { System.out.print(matrix[y][x] == 1 ? "*" : " "); } System.out.println(); } } private void printGenerationHeader() { String invalidText = invalid ? "INVALID!" : ""; System.out.printf("GENERATION: %-13d POPULATION: %d %s\n", generation, population, invalidText); } /** * Main method that starts the program. * * @param args the command line arguments: *
    -s: Stop after each generation (press enter to continue)
    * @throws Exception if something goes wrong. */ public static void main(String[] args) throws Exception { new Life(args).start(); } } /** * Represents a state change for a single cell within the matrix. * * @param y the y coordinate (row) of the cell * @param x the x coordinate (column) of the cell * @param newState the new state of the cell (either DEAD or ALIVE) */ record Transition(int y, int x, byte newState) { } ================================================ FILE: 55_Life/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 55_Life/javascript/life.html ================================================ LIFE
    
    
    
    
    
    
    ================================================
    FILE: 55_Life/javascript/life.js
    ================================================
    // LIFE
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    var bs = [];
    var a = [];
    
    // Main program
    async function main()
    {
        print(tab(34) + "LIFE\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        print("ENTER YOUR PATTERN:\n");
        x1 = 1;
        y1 = 1;
        x2 = 24;
        y2 = 70;
        for (c = 1; c <= 24; c++) {
            bs[c] = "";
            a[c] = [];
            for (d = 1; d <= 70; d++)
                a[c][d] = 0;
        }
        c = 1;
        while (1) {
            bs[c] = await input();
            if (bs[c] == "DONE") {
                bs[c] = "";
                break;
            }
            if (bs[c].substr(0, 1) == ".")
                bs[c] = " " + bs[c].substr(1);
            c++;
        }
        c--;
        l = 0;
        for (x = 1; x <= c - 1; x++) {
            if (bs[x].length > l)
                l = bs[x].length;
        }
        x1 = 11 - (c >> 1);
        y1 = 33 - (l >> 1);
        p = 0;
        for (x = 1; x <= c; x++) {
            for (y = 1; y <= bs[x].length; y++) {
                if (bs[x][y - 1] != " ") {
                    a[x1 + x][y1 + y] = 1;
                    p++;
                }
            }
        }
        print("\n");
        print("\n");
        print("\n");
        i9 = false;
        g = 0;
        while (g < 100) {
            print("GENERATION: " + g + " POPULATION: " + p + " ");
            if (i9)
                print("INVALID!");
            x3 = 24;
            y3 = 70;
            x4 = 1;
            y4 = 1;
            p = 0;
            g++;
            for (x = 1; x <= x1 - 1; x++)
                print("\n");
            for (x = x1; x <= x2; x++) {
                print("\n");
                str = "";
                for (y = y1; y <= y2; y++) {
                    if (a[x][y] == 2) {
                        a[x][y] = 0;
                        continue;
                    } else if (a[x][y] == 3) {
                        a[x][y] = 1;
                    } else if (a[x][y] != 1) {
                        continue;
                    }
                    while (str.length < y)
                        str += " ";
                    str += "*";
                    if (x < x3)
                        x3 = x;
                    if (x > x4)
                        x4 = x;
                    if (y < y3)
                        y3 = y;
                    if (y > y4)
                        y4 = y;
                }
                print(str);
            }
            for (x = x2 + 1; x <= 24; x++)
                print("\n");
            x1 = x3;
            x2 = x4;
            y1 = y3;
            y2 = y4;
            if (x1 < 3) {
                x1 = 3;
                i9 = true;
            }
            if (x2 > 22) {
                x2 = 22;
                i9 = true;
            }
            if (y1 < 3) {
                y1 = 3;
                i9 = true;
            }
            if (y2 > 68) {
                y2 = 68;
                i9 = true;
            }
            p = 0;
            for (x = x1 - 1; x <= x2 + 1; x++) {
                for (y = y1 - 1; y <= y2 + 1; y++) {
                    c = 0;
                    for (i = x - 1; i <= x + 1; i++) {
                        for (j = y - 1; j <= y + 1; j++) {
                            if (a[i][j] == 1 || a[i][j] == 2)
                                c++;
                        }
                    }
                    if (a[x][y] == 0) {
                        if (c == 3) {
                            a[x][y] = 3;
                            p++;
                        }
                    } else {
                        if (c < 3 || c > 4) {
                            a[x][y] = 2;
                        } else {
                            p++;
                        }
                    }
                }
            }
            x1--;
            y1--;
            x2++;
            y2++;
        }
    }
    
    main();
    
    
    ================================================
    FILE: 55_Life/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 55_Life/life.bas
    ================================================
    2 PRINT TAB(34);"LIFE"
    4 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    6 PRINT: PRINT: PRINT
    8 PRINT "ENTER YOUR PATTERN:"
    9 X1=1: Y1=1: X2=24: Y2=70
    10 DIM A(24,70),B$(24)
    20 C=1
    30 INPUT B$(C)
    40 IF B$(C)="DONE" THEN B$(C)="": GOTO 80
    50 IF LEFT$(B$(C),1)="." THEN B$(C)=" "+RIGHT$(B$(C),LEN(B$(C))-1)
    60 C=C+1
    70 GOTO 30
    80 C=C-1: L=0
    90 FOR X=1 TO C-1
    100 IF LEN(B$(X))>L THEN L=LEN(B$(X))
    110 NEXT X
    120 X1=11-C/2
    130 Y1=33-L/2
    140 FOR X=1 TO C
    150 FOR Y=1 TO LEN(B$(X))
    160 IF MID$(B$(X),Y,1)<>" " THEN A(X1+X,Y1+Y)=1:P=P+1
    170 NEXT Y
    180 NEXT X
    200 PRINT:PRINT:PRINT
    210 PRINT "GENERATION:";G,"POPULATION:";P;: IF I9 THEN PRINT "INVALID!";
    215 X3=24:Y3=70:X4=1: Y4=1: P=0
    220 G=G+1
    225 FOR X=1 TO X1-1: PRINT: NEXT X
    230 FOR X=X1 TO X2
    240 PRINT
    250 FOR Y=Y1 TO Y2
    253 IF A(X,Y)=2 THEN A(X,Y)=0:GOTO 270
    256 IF A(X,Y)=3 THEN A(X,Y)=1:GOTO 261
    260 IF A(X,Y)<>1 THEN 270
    261 PRINT TAB(Y);"*";
    262 IF XX4 THEN X4=X
    266 IF YY4 THEN Y4=Y
    270 NEXT Y
    290 NEXT X
    295 FOR X=X2+1 TO 24: PRINT: NEXT X
    299 X1=X3: X2=X4: Y1=Y3: Y2=Y4
    301 IF X1<3 THEN X1=3:I9=-1
    303 IF X2>22 THEN X2=22:I9=-1
    305 IF Y1<3 THEN Y1=3:I9=-1
    307 IF Y2>68 THEN Y2=68:I9=-1
    309 P=0
    500 FOR X=X1-1 TO X2+1
    510 FOR Y=Y1-1 TO Y2+1
    520 C=0
    530 FOR I=X-1 TO X+1
    540 FOR J=Y-1 TO Y+1
    550 IF A(I,J)=1 OR A(I,J)=2 THEN C=C+1
    560 NEXT J
    570 NEXT I
    580 IF A(X,Y)=0 THEN 610
    590 IF C<3 OR C>4 THEN A(X,Y)=2: GOTO 600
    595 P=P+1
    600 GOTO 620
    610 IF C=3 THEN A(X,Y)=3:P=P+1
    620 NEXT Y
    630 NEXT X
    635 X1=X1-1:Y1=Y1-1:X2=X2+1:Y2=Y2+1
    640 GOTO 210
    650 END
    
    
    ================================================
    FILE: 55_Life/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 55_Life/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 55_Life/perl/life.pl
    ================================================
    #!/usr/bin/perl
    #use strict;
    
    print ' 'x 34 . "LIFE\n";
    print ' 'x 15 . "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n";
    print "\n"; print "\n"; print "\n";
    print "ENTER YOUR PATTERN; \n";
    $X1=1; $Y1=1; $X2=24; $Y2=70;
    @A;
    $C=1;
    
    @B;
    Line30:
    print "? "; chomp($B[$C] = uc());
    if ($B[$C] eq "DONE") { $B[$C]=""; goto Line80; }
    $B[$C]=~ s/\./ /g;
    $C=$C+1;
    goto Line30;
    
    
    Line80:
    
    $C=$C-1; $L=0; $G=0;
    for ($X=1; $X<=$C-1; $X++) {
    	if (length($B[$X])>$L) { $L=length($B[$X]); }
    	}
    
    $X1=11-$C/2;
    $Y1=33-$L/2;
    for ($X=1; $X<=$C; $X++) {
    	for ($Y=1; $Y<=length($B[$X]); $Y++) {
    		if (substr($B[$X],$Y-1,1) ne " ") { $A[$X1+$X][$Y1+$Y]=1; $P=$P+1; }
    		}
    	}
    print "\n"; print "\n"; print "\n";
    
    Line210:
    print "GENERATION: ".$G."\t\tPOPULATION: ".$P; if ($I9) { print "\tINVALID!"; }
    print "\n";
    $X3=24; $Y3=70; $X4=1; $Y4=1; $P=0;
    $G=$G+1;
    for ($X=1; $X<=$X1-1; $X++) { print "\n"; }
    for ($X=$X1; $X<=$X2; $X++) {
    	$Row= " "x 80;
    	for ($Y=$Y1; $Y<=$Y2; $Y++) {
    		if ($A[$X][$Y]==2) { $A[$X][$Y]=0; goto Line270; }
    		if ($A[$X][$Y]==3) { $A[$X][$Y]=1; goto Line261; }
    		if ($A[$X][$Y]!=1) { goto Line270; }
    
    		Line261:
    		substr($Row, $Y, 1, "*");
    		if ($X<$X3) { $X3=$X; }
    		if ($X>$X4) { $X4=$X; }
    		if ($Y<$Y3) { $Y3=$Y; }
    		if ($Y>$Y4) { $Y4=$Y; }
    
    		Line270:
    		}
    	print "$Row\n";
    	}
    
    for ($X=$X2+1; $X<=24; $X++) { print "\n"; }
    $X1=$X3; $X2=$X4; $Y1=$Y3; $Y2=$Y4;
    if ($X1<3) { $X1=3; $I9=-1; }
    if ($X2>22) { $X2=22; $I9=-1; }
    if ($Y1<3) { $Y1=3; $I9=-1; }
    if ($Y2>68) { $Y2=68; $I9=-1; }
    $P=0;
    
    for ($X=$X1-1; $X<=$X2+1; $X++) {
    	for ($Y=$Y1-1; $Y<=$Y2+1; $Y++) {
    		$C=0;
    		for ($I=$X-1; $I<=$X+1; $I++) {
    			for ($J=$Y-1; $J<=$Y+1; $J++) {
    				if ($A[$I][$J]==1 || $A[$I][$J]==2) { $C=$C+1; }
    				}
    			}
    		if ($A[$X][$Y]==0) { goto Line610; }
    		if ($C<3 || $C>4) { $A[$X][$Y]=2; goto Line600; }
    		$P=$P+1;
    
    		Line600:
    		goto Line620;
    
    		Line610:
    		if ($C==3) { $A[$X][$Y]=3; $P=$P+1; }
    
    		Line620:
    		}
    	}
    $X1=$X1-1; $Y1=$Y1-1; $X2=$X2+1; $Y2=$Y2+1;
    goto Line210;
    exit;
    
    
    ================================================
    FILE: 55_Life/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 55_Life/python/life.py
    ================================================
    """
    LIFE
    
    An implementation of John Conway's popular cellular automaton
    
    Ported by Dave LeCompte
    """
    
    from typing import Dict
    
    PAGE_WIDTH = 64
    
    MAX_WIDTH = 70
    MAX_HEIGHT = 24
    
    
    def print_centered(msg) -> None:
        spaces = " " * ((PAGE_WIDTH - len(msg)) // 2)
        print(spaces + msg)
    
    
    def print_header(title) -> None:
        print_centered(title)
        print_centered("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY")
        print()
        print()
        print()
    
    
    def get_pattern() -> Dict[int, str]:
        print("ENTER YOUR PATTERN:")
        c = 0
    
        pattern: Dict[int, str] = {}
        while True:
            line = input()
            if line == "DONE":
                return pattern
    
            # BASIC input would strip of leading whitespace.
            # Python input does not. The following allows you to start a
            # line with a dot to disable the whitespace stripping. This is
            # unnecessary for Python, but for historical accuracy, it's
            # staying in.
    
            if line[0] == ".":
                line = f" {line[1:]}"
            pattern[c] = line
            c += 1
    
    
    def main() -> None:
        print_header("LIFE")
    
        pattern = get_pattern()
    
        pattern_height = len(pattern)
        pattern_width = 0
        for _line_num, line in pattern.items():
            pattern_width = max(pattern_width, len(line))
    
        min_x = 11 - pattern_height // 2
        min_y = 33 - pattern_width // 2
        max_x = MAX_HEIGHT - 1
        max_y = MAX_WIDTH - 1
    
        a = [[0 for _ in range(MAX_WIDTH)] for _ in range(MAX_HEIGHT)]
        p = 0
        g = 0
        invalid = False
    
        # line 140
        # transcribe the input pattern into the active array
        for x in range(0, pattern_height):
            for y in range(0, len(pattern[x])):
                if pattern[x][y] != " ":
                    a[min_x + x][min_y + y] = 1
                    p += 1
    
        print()
        print()
        print()
        while True:
            inv_str = "INVALID!" if invalid else ""
            print(f"GENERATION: {g}\tPOPULATION: {p} {inv_str}")
    
            next_min_x = MAX_HEIGHT - 1
            next_min_y = MAX_WIDTH - 1
            next_max_x = 0
            next_max_y = 0
    
            p = 0
            g += 1
            for _ in range(min_x):
                print()
    
            for x in range(min_x, max_x + 1):
                print()
                line_list = [" "] * MAX_WIDTH
                for y in range(min_y, max_y + 1):
                    if a[x][y] == 2:
                        a[x][y] = 0
                        continue
                    elif a[x][y] == 3:
                        a[x][y] = 1
                    elif a[x][y] != 1:
                        continue
    
                    line_list[y] = "*"
    
                    next_min_x = min(x, next_min_x)
                    next_max_x = max(x, next_max_x)
                    next_min_y = min(y, next_min_y)
                    next_max_y = max(y, next_max_y)
    
                print("".join(line_list))
    
            # line 295
            for _ in range(max_x + 1, MAX_HEIGHT):
                print()
    
            print()
    
            min_x = next_min_x
            max_x = next_max_x
            min_y = next_min_y
            max_y = next_max_y
    
            if min_x < 3:
                min_x = 3
                invalid = True
            if max_x > 22:
                max_x = 22
                invalid = True
            if min_y < 3:
                min_y = 3
                invalid = True
            if max_y > 68:
                max_y = 68
                invalid = True
    
            # line 309
            p = 0
    
            for x in range(min_x - 1, max_x + 2):
                for y in range(min_y - 1, max_y + 2):
                    count = 0
                    for i in range(x - 1, x + 2):
                        for j in range(y - 1, y + 2):
                            if a[i][j] in [1, 2]:
                                count += 1
                    if a[x][y] == 0:
                        if count == 3:
                            a[x][y] = 3
                            p += 1
                    elif (count < 3) or (count > 4):
                        a[x][y] = 2
                    else:
                        p += 1
    
            # line 635
            min_x = min_x - 1
            min_y = min_y - 1
            max_x = max_x + 1
            max_y = max_y + 1
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 55_Life/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 55_Life/ruby/life.rb
    ================================================
    #!ruby
    
    # The Pattern class encapsulates everything we would want to know about a
    # pattern in our Game of Life: its size, its current pattern of alive and
    # dead cells, which generation it's on and how long it can run, and how
    # to accept input for itself, print itself, and most importantly iterate
    # from one generation to the next.
    
    PATTERN_WIDTH = 80
    PATTERN_HEIGHT = 24
    
    class Pattern
    
      # Begin with a totally empty/dead pattern of fixed size at generation 0.
    
      def initialize(max_generations: 10)
        @max_generations = max_generations
        @generation = 0
        @population = nil
        @counter = Array.new(PATTERN_HEIGHT) { Array.new(PATTERN_WIDTH, 0) }
        @invalid = false
      end
    
      # Take input from the console and enter it into the pattern.
    
      def get_input
        input = []
        done = false
    
        # Accept the input from the user on the console
    
        loop do
          print "? "
          line = gets.chomp
          break if line == 'DONE'
          line.gsub!(/^\./, ' ')
          input << line
        end
    
        # Emit some blank space
        (1..10).each { puts }
    
        # Center the input in the two-dimensional array
        input_width = input.map { |line| line.length }.max
        width_offset = ((PATTERN_WIDTH - input_width) / 2).floor
        height_offset = ((PATTERN_HEIGHT - input.count) / 2).floor
        # TODO emit error if width > PATTERN_WIDTH or line count > PATTERN_HEIGHT
    
        # Start by setting each element to 0 if dead or a 1 if alive
    
        input.each_index do |y|
          line = input[y].ljust(input_width)
          y_offset = y + height_offset
          @counter[y_offset] = [
            [0] * width_offset,
            line.split("").map { |char| char == ' ' ? 0 : 1 },
            [0] * PATTERN_WIDTH
          ].flatten.take(PATTERN_WIDTH)
        end
    
        @population = @counter.flatten.sum
      end
    
      # Emit the pattern to the console.
    
      def display
        puts "GENERATION:#{@generation}\tPOPULATION:#{@population}"
        puts "INVALID!"if @invalid
    
        @counter.each do |row|
          puts row.map { |cell| ((cell == 0 || cell == 2) ? ' ' : 'X') }.join('')
        end
      end
    
      # Iterate from one generation to the next, returning true if the
      # game of life should continue, and false if it should terminate.
    
      def iterate
        @generation = @generation + 1
        return false if @generation > @max_generations
    
        # Update the counter array with new values.
        # First, change each 2 (dying) to a 0 (dead)
        # and each 3 (born) to a 1 (alive)
        @counter.map! { |row| row.map! { |cell| cell >= 2 ? cell-2 : cell } }
    
        # Now for each cell, count its neighbors and update it
    
        @population = 0
        @counter.each_index do |rownum|
          @counter[rownum].each_index do |colnum|
            cell_value = @counter[rownum][colnum]
    
            # If any cell on the border is set, our small algorithm is not
            # smart enough to correctly check its neighbors, so sadly our
            # pattern becomes invalid. We keep going though
    
            @invalid = true if cell_value > 0 && (
              rownum == 0 || rownum == PATTERN_HEIGHT-1 || colnum == 0 || colnum == PATTERN_WIDTH-1
            )
    
            # Count the cell's neighbors (not including itself)
    
            neighbors = @counter[rownum-1..rownum+1].map { |row| row[colnum-1..colnum+1] }.flatten
            neighbor_count = neighbors.inject(0) do |sum, value|
              sum += (value == 1 || value == 2) ? 1 : 0
            end
            neighbor_count = neighbor_count - cell_value
    
            # Update this cell based on its neighbor count, either leaving it
            # as a 0 or 1, or setting it to 2 (dying) or 3 (being born)
    
            if cell_value == 0
              if neighbor_count == 3
                cell_value = 3
                @population = @population + 1
              end
            elsif neighbor_count < 2 || neighbor_count > 3
              cell_value = 2
            else
              @population = @population + 1
            end
            @counter[rownum][colnum] = cell_value
    
          end
        end
    
        # If every cell is dead, we are done. Otherwise, keep going up to the
        # maximum number of generations
    
        @population > 0
      end
    
    end
    
    # The following program code makes use of the Pattern class to create,
    # iterate on, and display the Game of Life.
    
    def display_banner
      puts " " * 34 + "LIFE"
      puts " " * 15 + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
      puts
      puts "PLEASE ENTER YOUR STARTING PATTERN, USING SPACE FOR AN EMPTY CELL"
      puts "AND AN 'X' FOR A FILLED CELL. YOUR PATTERN MAY BE UP TO #{PATTERN_HEIGHT} ROWS"
      puts "OF UP TO #{PATTERN_WIDTH} COLUMNS EACH. TYPE 'DONE' WHEN DONE."
      puts "ENTER YOUR PATTERN:"
    end
    
    def main
    
      display_banner
    
      pattern = Pattern.new
      pattern.get_input
      pattern.display
      pattern.display while pattern.iterate
    
    end
    
    main
    
    
    ================================================
    FILE: 55_Life/rust/Cargo.toml
    ================================================
    [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]
    
    
    ================================================
    FILE: 55_Life/rust/README.md
    ================================================
    # Conway's Life
    
    Original from David Ahl's _Basic Computer Games_, downloaded from http://www.vintage-basic.net/games.html.
    
    Ported to Rust by Jon Fetter-Degges
    
    Developed and tested on Rust 1.64.0
    
    ## How to Run
    
    Install Rust using the instructions at [rust-lang.org](https://www.rust-lang.org/tools/install).
    
    At a command or shell prompt in the `rust` subdirectory, enter `cargo run`.
    
    ## Differences from Original Behavior
    
    * The simulation stops if all cells die.
    * `.` at the beginning of an input line is supported but optional.
    * Input of more than 66 columns is rejected. Input will automatically terminate after 20 rows. Beyond these bounds, the original
    implementation would have marked the board as invalid, and beyond 68 cols/24 rows it would have had an out of bounds array access.
    * The check for the string "DONE" at the end of input is case-independent.
    * The program pauses for half a second between each generation.
    
    
    ================================================
    FILE: 55_Life/rust/src/main.rs
    ================================================
    // Rust implementation of the "Basic Computer Games" version of Conway's Life
    //
    // Jon Fetter-Degges
    // October 2022
    
    // I am a Rust newbie. Corrections and suggestions are welcome.
    
    use std::{cmp, fmt, io, thread, time};
    
    // The BASIC implementation uses integers to represent the state of each cell: 1 is
    // alive, 2 is about to die, 3 is about to be born, 0 is dead. Here, we'll use an enum
    // instead.
    // Deriving Copy (which requires Clone) allows us to use this enum value in assignments,
    // and deriving Eq (or PartialEq) allows us to use the == operator. These need to be
    // explicitly specified because some enums may have associated data that makes copies and
    // comparisons more complicated or expensive.
    #[derive(Clone, Copy, PartialEq, Eq)]
    enum CellState {
        Empty,
        Alive,
        AboutToDie,
        AboutToBeBorn,
    }
    
    // Support direct printing of the cell. In this program cells will only be Alive or Empty
    // when they are printed.
    impl fmt::Display for CellState {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            let rep = match *self {
                CellState::Empty => ' ',
                CellState::Alive => '*',
                CellState::AboutToDie => 'o',
                CellState::AboutToBeBorn => '.',
            };
            write!(f, "{}", rep)
        }
    }
    
    // Following the BASIC implementation, we will bound the board at 24 rows x 70 columns.
    // The board is an array of CellState. Using an array of arrays gives us bounds checking
    // in both dimensions.
    const HEIGHT: usize = 24;
    const WIDTH: usize = 70;
    
    struct Board {
        cells: [[CellState; WIDTH]; HEIGHT],
        min_row: usize,
        max_row: usize,
        min_col: usize,
        max_col: usize,
        population: usize,
        generation: usize,
        invalid: bool,
    }
    
    impl Board {
        fn new() -> Board {
            Board {
                cells: [[CellState::Empty; WIDTH]; HEIGHT],
                min_row: 0,
                max_row: 0,
                min_col: 0,
                max_col: 0,
                population: 0,
                generation: 0,
                invalid: false,
            }
        }
    }
    
    fn main() {
        println!(); println!(); println!();
        println!("{:33}{}", " ", "Life");
        println!("{:14}{}", " ", "Creative Computing  Morristown, New Jersey");
        println!("Enter your pattern: ");
        let mut board = parse_pattern(get_pattern());
        loop {
            finish_cell_transitions(&mut board);
            print_board(&board);
            mark_cell_transitions(&mut board);
            if board.population == 0 {
                break; // this isn't in the original implementation but it seemed better than
                       // spewing blank screens
            }
            delay();
        }
    }
    
    fn get_pattern() -> Vec> {
        let max_line_len = WIDTH - 4;
        let max_line_count = HEIGHT - 4;
        let mut lines = Vec::new();
        loop {
            let mut line = String::new();
            // read_line reads into the buffer (appending if it's not empty). It returns the
            // number of characters read, including the newline. This will be 0 on EOF.
            // unwrap() will panic and terminate the program if there is an error reading
            // from stdin. That's reasonable behavior in this case.
            let nread = io::stdin().read_line(&mut line).unwrap();
            let line = line.trim_end();
            if nread == 0 || line.eq_ignore_ascii_case("DONE") {
                return lines;
            }
            // Handle Unicode by converting the string to a vector of characters up front. We
            // do this here because we check the number of characters several times, so we
            // might as well just do the Unicode parsing once.
            let line = Vec::from_iter(line.chars());
            if line.len() > max_line_len {
                println!("Line too long - the maximum is {max_line_len} characters.");
                continue;
            }
            lines.push(line);
            if lines.len() == max_line_count {
                println!("Maximum line count reached. Starting simulation.");
                return lines;
            }
        }
    }
    
    fn parse_pattern(rows: Vec>) -> Board {
        // This function assumes that the input pattern in rows is in-bounds. If the pattern
        // is too large, this function will panic. get_pattern checks the size of the input,
        // so it is safe to call this function with its results.
    
        let mut board = Board::new();
    
        // The BASIC implementation puts the pattern roughly in the center of the board,
        // assuming that there are no blank rows at the beginning or end, or blanks entered
        // at the beginning or end of every row. It wouldn't be hard to check for that, but
        // for now we'll preserve the original behavior.
        let nrows = rows.len();
        // If rows is empty, the call to max will return None. The unwrap_or then provides a
        // default value
        let ncols = rows.iter().map(|l| l.len()).max().unwrap_or(0);
    
        // The min and max values here are unsigned. If nrows >= 24 or ncols >= 68, these
        // assignments will panic - they do not wrap around unless we use a function with
        // that specific behavior. Again, we expect bounds checking on the input before this
        // function is called.
        board.min_row = 11 - nrows / 2;
        board.min_col = 33 - ncols / 2;
        board.max_row = board.min_row + nrows - 1;
        board.max_col = board.min_col + ncols - 1;
    
        // Loop over the rows provided. enumerate() augments the iterator with an index.
        for (row_index, pattern) in rows.iter().enumerate() {
            let row = board.min_row + row_index;
            // Now loop over the non-empty cells in the current row. filter_map takes a
            // closure that returns an Option. If the Option is None, filter_map filters out
            // that entry from the for loop. If it's Some(x), filter_map executes the loop
            // body with the value x.
            for col in pattern.iter().enumerate().filter_map(|(col_index, chr)| {
                if *chr == ' ' || (*chr == '.' && col_index == 0) {
                    None
                } else {
                    Some(board.min_col + col_index)
                }
            }) {
                board.cells[row][col] = CellState::Alive;
                board.population += 1;
            }
        }
    
        board
    }
    
    fn finish_cell_transitions(board: &mut Board) {
        // In the BASIC implementation, this happens in the same loop that prints the board.
        // We're breaking it out to improve separation of concerns.
        let mut min_row = HEIGHT - 1;
        let mut max_row = 0usize;
        let mut min_col = WIDTH - 1;
        let mut max_col = 0usize;
        for row_index in board.min_row-1..=board.max_row+1 {
            let mut any_alive_this_row = false;
            for col_index in board.min_col-1..=board.max_col+1 {
                let cell = &mut board.cells[row_index][col_index];
                if *cell == CellState::AboutToBeBorn {
                    *cell = CellState::Alive;
                    board.population += 1;
                } else if *cell == CellState::AboutToDie {
                    *cell = CellState::Empty;
                    board.population -= 1;
                }
                if *cell == CellState::Alive {
                    any_alive_this_row = true;
                    min_col = cmp::min(min_col, col_index);
                    max_col = cmp::max(max_col, col_index);
                }
            }
            if any_alive_this_row {
                min_row = cmp::min(min_row, row_index);
                max_row = cmp::max(max_row, row_index);
        }
        }
        // If anything is alive within two cells of the boundary, mark the board invalid and
        // clamp the bounds. We need a two-cell margin because we'll count neighbors on cells
        // one space outside the min/max, and when we count neighbors we go out by an
        // additional space.
        if min_row < 2 {
            min_row = 2;
            board.invalid = true;
        }
        if max_row > HEIGHT - 3 {
            max_row = HEIGHT - 3;
            board.invalid = true;
        }
        if min_col < 2 {
            min_col = 2;
            board.invalid = true;
        }
        if max_col > WIDTH - 3 {
            max_col = WIDTH - 3;
            board.invalid = true;
        }
    
        board.min_row = min_row;
        board.max_row = max_row;
        board.min_col = min_col;
        board.max_col = max_col;
    }
    
    fn print_board(board: &Board) {
        println!(); println!(); println!();
        print!("Generation: {}  Population: {}", board.generation, board.population);
        if board.invalid {
            print!("  Invalid!");
        }
        println!();
        for row_index in 0..HEIGHT {
            for col_index in 0..WIDTH {
                // This print uses the Display implementation for cell_state, above.
                print!("{}", board.cells[row_index][col_index]);
            }
            println!();
        }
    }
    
    fn count_neighbors(board: &Board, row_index: usize, col_index: usize) -> i32 {
        // Simply loop over all the immediate neighbors of a cell. We assume that the row and
        // column indices are not on (or outside) the boundary of the arrays; if they are,
        // the function will panic instead of going out of bounds.
        let mut count = 0;
        for i in row_index-1..=row_index+1 {
            for j in col_index-1..=col_index+1 {
                if i == row_index && j == col_index {
                    continue;
                }
                if board.cells[i][j] == CellState::Alive || board.cells[i][j] == CellState::AboutToDie {
                    count += 1;
                }
            }
        }
        count
    }
    
    fn mark_cell_transitions(board: &mut Board) {
        for row_index in board.min_row-1..=board.max_row+1 {
            for col_index in board.min_col-1..=board.max_col+1 {
                let neighbors = count_neighbors(board, row_index, col_index);
                // Borrow a mutable reference to the array cell
                let this_cell_state = &mut board.cells[row_index][col_index];
                *this_cell_state = match *this_cell_state {
                    CellState::Empty if neighbors == 3 => CellState::AboutToBeBorn,
                    CellState::Alive if !(2..=3).contains(&neighbors) => CellState::AboutToDie,
                    _ => *this_cell_state,
                }
            }
        }
        board.generation += 1;
    }
    
    fn delay() {
        thread::sleep(time::Duration::from_millis(500));
    }
    
    
    ================================================
    FILE: 55_Life/vbnet/Life.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Life", "Life.vbproj", "{05455AEE-3BE0-4DDD-A59E-89F862EF68AE}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{05455AEE-3BE0-4DDD-A59E-89F862EF68AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{05455AEE-3BE0-4DDD-A59E-89F862EF68AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{05455AEE-3BE0-4DDD-A59E-89F862EF68AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{05455AEE-3BE0-4DDD-A59E-89F862EF68AE}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 55_Life/vbnet/Life.vbproj
    ================================================
    
      
        Exe
        Life
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 55_Life/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 56_Life_for_Two/README.md
    ================================================
    ### Life for Two
    
    LIFE-2 is based on Conway’s game of Life. You must be familiar with the rules of LIFE before attempting to play LIFE-2.
    
    There are two players; the game is played on a 5x5 board and each player has a symbol to represent his own pieces of ‘life.’ Live cells belonging to player 1 are represented by `*` and live cells belonging to player 2 are represented by the symbol `#`.
    
    The # and * are regarded as the same except when deciding whether to generate a live cell. An empty cell having two `#` and one `*` for neighbors will generate a `#`, i.e. the live cell generated belongs to the player who has the majority of the 3 live cells surrounding the empty cell where life is to be generated, for example:
    
    ```
    |   | 1 | 2 | 3 | 4 | 5 |
    |:-:|:-:|:-:|:-:|:-:|:-:|
    | 1 |   |   |   |   |   |
    | 2 |   |   | * |   |   |
    | 3 |   |   |   | # |   |
    | 4 |   |   | # |   |   |
    | 5 |   |   |   |   |   |
    ```
    
    A new cell will be generated at (3,3) which will be a `#` since there are two `#` and one `*` surrounding. The board will then become:
    ```
    |   | 1 | 2 | 3 | 4 | 5 |
    |:-:|:-:|:-:|:-:|:-:|:-:|
    | 1 |   |   |   |   |   |
    | 2 |   |   |   |   |   |
    | 3 |   |   | # | # |   |
    | 4 |   |   |   |   |   |
    | 5 |   |   |   |   |   |
    ```
    On the first 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.
    
    On each subsequent turn each player places one piece on the board, the object being to annihilate his opponent’s pieces. The board is adjusted for the next generation and printed out after both players have entered their new piece.
    
    The game continues until one player has no more live pieces. The computer will then print out the board and declare the winner.
    
    The idea for this game, the game itself, and the above write-up were written by Brian Wyvill of Bradford University in Yorkshire, England.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=102)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=117)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    
    #### 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.
    
    
    ================================================
    FILE: 56_Life_for_Two/csharp/Board.cs
    ================================================
    using System.Collections;
    using System.Text;
    
    namespace LifeforTwo;
    
    internal class Board : IEnumerable
    {
        private readonly Piece[,] _cells = new Piece[7, 7];
        private readonly Dictionary _cellCounts = 
            new() { [Piece.None] = 0, [Piece.Player1] = 0, [Piece.Player2] = 0 };
    
        public Piece this[Coordinates coordinates]
        {
            get => this[coordinates.X, coordinates.Y];
            set => this[coordinates.X, coordinates.Y] = value;
        }
    
        private Piece this[int x, int y]
        {
            get => _cells[x, y];
            set
            {
                if (!_cells[x, y].IsEmpty) { _cellCounts[_cells[x, y]] -= 1; }
                _cells[x, y] = value;
                _cellCounts[value] += 1;
            }
        }
    
        public int Player1Count => _cellCounts[Piece.Player1];
        public int Player2Count => _cellCounts[Piece.Player2];
    
        internal bool IsEmptyAt(Coordinates coordinates) => this[coordinates].IsEmpty;
    
        internal void ClearCell(Coordinates coordinates) => this[coordinates] = Piece.NewNone();
        internal void AddPlayer1Piece(Coordinates coordinates) => this[coordinates] = Piece.NewPlayer1();
        internal void AddPlayer2Piece(Coordinates coordinates) => this[coordinates] = Piece.NewPlayer2();
    
        public override string ToString()
        {
            var builder = new StringBuilder();
    
            for (var y = 0; y <= 6; y++)
            {
                builder.AppendLine();
                for (var x = 0; x <= 6; x++)
                {
                    builder.Append(GetCellDisplay(x, y));
                }
            }
    
            return builder.ToString();
        }
    
        private string GetCellDisplay(int x, int y) =>
            (x, y) switch
            {
                (0 or 6, _) => $" {y % 6} ",
                (_, 0 or 6) => $" {x % 6} ",
                _ => $" {this[x, y]} "
            };
    
        public IEnumerator GetEnumerator()
        {
            for (var x = 1; x <= 5; x++)
            {
                for (var y = 1; y <= 5; y++)
                {
                    yield return new(x, y);
                }
            }
        }
    
        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
    }
    
    
    ================================================
    FILE: 56_Life_for_Two/csharp/Coordinates.cs
    ================================================
    namespace LifeforTwo;
    
    internal record Coordinates (int X, int Y)
    {
        public static bool TryCreate((float X, float Y) values, out Coordinates coordinates)
        {
            if (values.X <= 0 || values.X > 5 || values.Y <= 0 || values.Y > 5)
            {
                coordinates = new(0, 0);
                return false;
            }
    
            coordinates = new((int)values.X, (int)values.Y);
            return true;
        }
    
        public static Coordinates operator +(Coordinates coordinates, int value) =>
            new (coordinates.X + value, coordinates.Y + value);
    
        public IEnumerable GetNeighbors()
        {
            yield return new(X - 1, Y);
            yield return new(X + 1, Y);
            yield return new(X, Y - 1);
            yield return new(X, Y + 1);
            yield return new(X - 1, Y - 1);
            yield return new(X + 1, Y - 1);
            yield return new(X - 1, Y + 1);
            yield return new(X + 1, Y + 1);
        }
    }
    
    
    ================================================
    FILE: 56_Life_for_Two/csharp/Game.cs
    ================================================
    internal class Game
    {
        private readonly IReadWrite _io;
    
        public Game(IReadWrite io)
        {
            _io = io;
        }
    
        public void Play()
        {
            _io.Write(Streams.Title);
    
            var life = new Life(_io);
    
            _io.Write(life.FirstGeneration);
    
            foreach (var generation in life)
            {
                _io.WriteLine();
                _io.Write(generation);
            }
    
            _io.WriteLine(life.Result ?? "No result");
        }
    }
    
    
    ================================================
    FILE: 56_Life_for_Two/csharp/Generation.cs
    ================================================
    internal class Generation
    {
        private readonly Board _board;
    
        public Generation(Board board)
        {
            _board = board;
            CountNeighbours();
        }
    
        public Board Board => _board;
    
        public int Player1Count => _board.Player1Count;
        public int Player2Count => _board.Player2Count;
    
        public string? Result => 
            (Player1Count, Player2Count) switch
            {
                (0, 0) => Strings.Draw,
                (_, 0) => string.Format(Formats.Winner, 1),
                (0, _) => string.Format(Formats.Winner, 2),
                _ => null
            };
    
        public static Generation Create(IReadWrite io)
        {
            var board = new Board();
    
            SetInitialPieces(1, coord => board.AddPlayer1Piece(coord));
            SetInitialPieces(2, coord => board.AddPlayer2Piece(coord));
    
            return new Generation(board);
    
            void SetInitialPieces(int player, Action setPiece)
            {
                io.WriteLine(Formats.InitialPieces, player);
                for (var i = 1; i <= 3; i++)
                {
                    setPiece(io.ReadCoordinates(board));
                }
            }
        }
    
        public Generation CalculateNextGeneration()
        {
            var board = new Board();
    
            foreach (var coordinates in _board)
            {
                board[coordinates] = _board[coordinates].GetNext();
            }
    
            return new(board);
        }
        
        public void AddPieces(IReadWrite io)
        {
            var player1Coordinate = io.ReadCoordinates(1, _board);
            var player2Coordinate = io.ReadCoordinates(2, _board);
    
            if (player1Coordinate == player2Coordinate)
            {
                io.Write(Streams.SameCoords);
                // This is a bug existing in the original code. The line should be _board[_coordinates[_player]] = 0;
                _board.ClearCell(player1Coordinate + 1);
            }
            else
            {
                _board.AddPlayer1Piece(player1Coordinate);
                _board.AddPlayer2Piece(player2Coordinate);
            }
        }
    
        private void CountNeighbours()
        {
            foreach (var coordinates in _board)
            {
                var piece = _board[coordinates];
                if (piece.IsEmpty) { continue; }
    
                foreach (var neighbour in coordinates.GetNeighbors())
                {
                    _board[neighbour] = _board[neighbour].AddNeighbour(piece);
                }
            }
        }
    
        public override string ToString() => _board.ToString();
    }
    
    ================================================
    FILE: 56_Life_for_Two/csharp/IOExtensions.cs
    ================================================
    internal static class IOExtensions
    {
        internal static Coordinates ReadCoordinates(this IReadWrite io, int player, Board board)
        {
            io.Write(Formats.Player, player);
            return io.ReadCoordinates(board);
        }
    
        internal static Coordinates ReadCoordinates(this IReadWrite io, Board board)
        {
            while (true)
            {
                io.WriteLine("X,Y");
                var values = io.Read2Numbers("&&&&&&\r");
                if (Coordinates.TryCreate(values, out var coordinates) && board.IsEmptyAt(coordinates))
                {
                    return coordinates;
                }
                io.Write(Streams.IllegalCoords);
            }
        }
    }
    
    ================================================
    FILE: 56_Life_for_Two/csharp/Life.cs
    ================================================
    using System.Collections;
    
    internal class Life : IEnumerable
    {
        private readonly IReadWrite _io;
    
        public Life(IReadWrite io)
        {
            _io = io;
            FirstGeneration = Generation.Create(io);
        }
    
        public Generation FirstGeneration { get; }
        public string? Result { get; private set; }
        
        public IEnumerator GetEnumerator()
        {
            var current = FirstGeneration;
            while (current.Result is null)
            {
                current = current.CalculateNextGeneration();
                yield return current;
    
                if (current.Result is null) { current.AddPieces(_io); }
            }
    
            Result = current.Result;
        }
    
        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 
    }
    
    ================================================
    FILE: 56_Life_for_Two/csharp/LifeforTwo.csproj
    ================================================
    
      
        Exe
        net6.0
        10
        enable
        enable
      
    
      
        
      
    
      
        
      
    
    
    
    ================================================
    FILE: 56_Life_for_Two/csharp/LifeforTwo.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LifeforTwo", "LifeforTwo.csproj", "{B2BFE429-A4BC-4CEA-881E-32382182EA32}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{B2BFE429-A4BC-4CEA-881E-32382182EA32}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{B2BFE429-A4BC-4CEA-881E-32382182EA32}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{B2BFE429-A4BC-4CEA-881E-32382182EA32}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{B2BFE429-A4BC-4CEA-881E-32382182EA32}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 56_Life_for_Two/csharp/Piece.cs
    ================================================
    using System.Collections.Immutable;
    using System.Diagnostics.CodeAnalysis;
    
    namespace LifeforTwo;
    
    public struct Piece
    {
        public const int None = 0x0000;
        public const int Player1 = 0x0100;
        public const int Player2 = 0x1000;
        private const int PieceMask = Player1 | Player2;
        private const int NeighbourValueOffset = 8;
    
        private static readonly ImmutableHashSet _willBePlayer1 = 
            new[] { 0x0003, 0x0102, 0x0103, 0x0120, 0x0130, 0x0121, 0x0112, 0x0111, 0x0012 }.ToImmutableHashSet();
        private static readonly ImmutableHashSet _willBePlayer2 = 
            new[] { 0x0021, 0x0030, 0x1020, 0x1030, 0x1011, 0x1021, 0x1003, 0x1002, 0x1012 }.ToImmutableHashSet();
    
        private int _value;
    
        private Piece(int value) => _value = value;
    
        public int Value => _value & PieceMask;
        public bool IsEmpty => (_value & PieceMask) == None;
    
        public static Piece NewNone() => new(None);
        public static Piece NewPlayer1() => new(Player1);
        public static Piece NewPlayer2() => new(Player2);
    
        public Piece AddNeighbour(Piece neighbour)
        {
            _value += neighbour.Value >> NeighbourValueOffset;
            return this;
        }
    
        public Piece GetNext() => new(
            _value switch
            {
                _ when _willBePlayer1.Contains(_value) => Player1,
                _ when _willBePlayer2.Contains(_value) => Player2,
                _ => None
            });
    
        public override string ToString() =>
            (_value & PieceMask) switch
            {
                Player1 => "*",
                Player2 => "#",
                _ => " "
            };
    
        public static implicit operator Piece(int value) => new(value);
        public static implicit operator int(Piece piece) => piece.Value;
    }
    
    ================================================
    FILE: 56_Life_for_Two/csharp/Program.cs
    ================================================
    global using Games.Common.IO;
    global using static LifeforTwo.Resources.Resource;
    global using LifeforTwo;
    
    new Game(new ConsoleIO()).Play();
    
    
    ================================================
    FILE: 56_Life_for_Two/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 56_Life_for_Two/csharp/Resources/Draw.txt
    ================================================
    
    A draw
    
    ================================================
    FILE: 56_Life_for_Two/csharp/Resources/IllegalCoords.txt
    ================================================
    Illegal coords. Retype
    
    
    ================================================
    FILE: 56_Life_for_Two/csharp/Resources/InitialPieces.txt
    ================================================
    
    Player {0}  - 3 live pieces
    
    ================================================
    FILE: 56_Life_for_Two/csharp/Resources/Player.txt
    ================================================
    
    
    Player {0} 
    
    ================================================
    FILE: 56_Life_for_Two/csharp/Resources/Resource.cs
    ================================================
    using System.Reflection;
    using System.Runtime.CompilerServices;
    
    namespace LifeforTwo.Resources;
    
    internal static class Resource
    {
        internal static class Streams
        {
            public static Stream Title => GetStream();
            public static Stream IllegalCoords => GetStream();
            public static Stream SameCoords => GetStream();
        }
    
        internal static class Formats
        {
            public static string InitialPieces => GetString();
            public static string Player => GetString();
            public static string Winner => GetString();
        }
    
        internal static class Strings
        {
            public static string Draw => GetString();
        }
    
        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}'.");
    }
    
    ================================================
    FILE: 56_Life_for_Two/csharp/Resources/SameCoords.txt
    ================================================
    Same coord.  Set to 0
    
    
    ================================================
    FILE: 56_Life_for_Two/csharp/Resources/Title.txt
    ================================================
                                     Life2
                   Creative Computing  Morristown, New Jersey
    
    
    
              U.B. Life Game
    
    
    ================================================
    FILE: 56_Life_for_Two/csharp/Resources/Winner.txt
    ================================================
    
    Player {0} is the winner
    
    ================================================
    FILE: 56_Life_for_Two/java/LifeForTwo.java
    ================================================
    import java.util.*;
    import java.util.stream.IntStream;
    
    /**
     * Life for Two
     * 

    * The original BASIC program uses a grid with an extras border of cells all around, * probably to simplify calculations and manipulations. This java program has the exact * grid size and instead uses boundary check conditions in the logic. *

    * Converted from BASIC to Java by Aldrin Misquitta (@aldrinm) */ public class LifeForTwo { final static int GRID_SIZE = 5; //Pair of offset which when added to the current cell's coordinates, // give the coordinates of the neighbours final static int[] neighbourCellOffsets = { -1, 0, 1, 0, 0, -1, 0, 1, -1, -1, 1, -1, -1, 1, 1, 1 }; //The best term that I could come with to describe these numbers was 'masks' //They act like indicators to decide which player won the cell. The value is the score of the cell after all the // generation calculations. final static List maskPlayer1 = List.of(3, 102, 103, 120, 130, 121, 112, 111, 12); final static List maskPlayer2 = List.of(21, 30, 1020, 1030, 1011, 1021, 1003, 1002, 1012); public static void main(String[] args) { printIntro(); Scanner scan = new Scanner(System.in); scan.useDelimiter("\\D"); int[][] grid = new int[GRID_SIZE][GRID_SIZE]; initializeGrid(grid); //Read the initial 3 moves for each player for (int b = 1; b <= 2; b++) { System.out.printf("\nPLAYER %d - 3 LIVE PIECES.%n", b); for (int k1 = 1; k1 <= 3; k1++) { var player1Coordinates = readUntilValidCoordinates(scan, grid); grid[player1Coordinates.x - 1][player1Coordinates.y - 1] = (b == 1 ? 3 : 30); } } printGrid(grid); calculatePlayersScore(grid); //Convert 3, 30 to 100, 1000 resetGridForNextGen(grid); computeCellScoresForOneGen(grid); var playerScores = calculatePlayersScore(grid); resetGridForNextGen(grid); boolean gameOver = false; while (!gameOver) { printGrid(grid); if (playerScores.getPlayer1Score() == 0 && playerScores.getPlayer2Score() == 0) { System.out.println("\nA DRAW"); gameOver = true; } else if (playerScores.getPlayer2Score() == 0) { System.out.println("\nPLAYER 1 IS THE WINNER"); gameOver = true; } else if (playerScores.getPlayer1Score() == 0) { System.out.println("\nPLAYER 2 IS THE WINNER"); gameOver = true; } else { System.out.print("PLAYER 1 "); Coordinate player1Move = readCoordinate(scan); System.out.print("PLAYER 2 "); Coordinate player2Move = readCoordinate(scan); if (!player1Move.equals(player2Move)) { grid[player1Move.x - 1][player1Move.y - 1] = 100; grid[player2Move.x - 1][player2Move.y - 1] = 1000; } //In the original, B is assigned 99 when both players choose the same cell //and that is used to control the flow computeCellScoresForOneGen(grid); playerScores = calculatePlayersScore(grid); resetGridForNextGen(grid); } } } private static void initializeGrid(int[][] grid) { for (int[] row : grid) { Arrays.fill(row, 0); } } private static void computeCellScoresForOneGen(int[][] grid) { for (int i = 0; i < GRID_SIZE; i++) { for (int j = 0; j < GRID_SIZE; j++) { if (grid[i][j] >= 100) { calculateScoreForOccupiedCell(grid, i, j); } } } } private static Scores calculatePlayersScore(int[][] grid) { int m2 = 0; int m3 = 0; for (int i = 0; i < GRID_SIZE; i++) { for (int j = 0; j < GRID_SIZE; j++) { if (grid[i][j] < 3) { grid[i][j] = 0; } else { if (maskPlayer1.contains(grid[i][j])) { m2++; } else if (maskPlayer2.contains(grid[i][j])) { m3++; } } } } return new Scores(m2, m3); } private static void resetGridForNextGen(int[][] grid) { for (int i = 0; i < GRID_SIZE; i++) { for (int j = 0; j < GRID_SIZE; j++) { if (grid[i][j] < 3) { grid[i][j] = 0; } else { if (maskPlayer1.contains(grid[i][j])) { grid[i][j] = 100; } else if (maskPlayer2.contains(grid[i][j])) { grid[i][j] = 1000; } else { grid[i][j] = 0; } } } } } private static void calculateScoreForOccupiedCell(int[][] grid, int i, int j) { var b = 1; if (grid[i][j] > 999) { b = 10; } for (int k = 0; k < 15; k += 2) { //check bounds var neighbourX = i + neighbourCellOffsets[k]; var neighbourY = j + neighbourCellOffsets[k + 1]; if (neighbourX >= 0 && neighbourX < GRID_SIZE && neighbourY >= 0 && neighbourY < GRID_SIZE) { grid[neighbourX][neighbourY] = grid[neighbourX][neighbourY] + b; } } } private static void printGrid(int[][] grid) { System.out.println(); printRowEdge(); System.out.println(); for (int i = 0; i < grid.length; i++) { System.out.printf("%d ", i + 1); for (int j = 0; j < grid[i].length; j++) { System.out.printf(" %c ", mapChar(grid[i][j])); } System.out.printf(" %d", i + 1); System.out.println(); } printRowEdge(); System.out.println(); } private static void printRowEdge() { System.out.print("0 "); IntStream.range(1, GRID_SIZE + 1).forEach(i -> System.out.printf(" %s ", i)); System.out.print(" 0"); } private static char mapChar(int i) { if (i == 3 || i == 100) { return '*'; } if (i == 30 || i == 1000) { return '#'; } return ' '; } private static Coordinate readUntilValidCoordinates(Scanner scanner, int[][] grid) { boolean coordinateInRange = false; Coordinate coordinate = null; while (!coordinateInRange) { coordinate = readCoordinate(scanner); if (coordinate.x <= 0 || coordinate.x > GRID_SIZE || coordinate.y <= 0 || coordinate.y > GRID_SIZE || grid[coordinate.x - 1][coordinate.y - 1] != 0) { System.out.println("ILLEGAL COORDS. RETYPE"); } else { coordinateInRange = true; } } return coordinate; } private static Coordinate readCoordinate(Scanner scanner) { Coordinate coordinate = null; int x, y; boolean valid = false; System.out.println("X,Y"); System.out.print("XXXXXX\r"); System.out.print("$$$$$$\r"); System.out.print("&&&&&&\r"); while (!valid) { try { System.out.print("? "); y = scanner.nextInt(); x = scanner.nextInt(); valid = true; coordinate = new Coordinate(x, y); } catch (InputMismatchException e) { System.out.println("!NUMBER EXPECTED - RETRY INPUT LINE"); valid = false; } finally { scanner.nextLine(); } } return coordinate; } private static void printIntro() { System.out.println(" LIFE2"); System.out.println(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println("\n\n"); System.out.println("\tU.B. LIFE GAME"); } private static class Coordinate { private final int x, y; public Coordinate(int x, int y) { this.x = x; this.y = y; } public int getX() { return x; } public int getY() { return y; } @Override public String toString() { return "Coordinate{" + "x=" + x + ", y=" + y + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Coordinate that = (Coordinate) o; return x == that.x && y == that.y; } @Override public int hashCode() { return Objects.hash(x, y); } } private static class Scores { private final int player1Score; private final int player2Score; public Scores(int player1Score, int player2Score) { this.player1Score = player1Score; this.player2Score = player2Score; } public int getPlayer1Score() { return player1Score; } public int getPlayer2Score() { return player2Score; } } } ================================================ FILE: 56_Life_for_Two/java/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Oracle Java](https://openjdk.java.net/) ================================================ FILE: 56_Life_for_Two/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 56_Life_for_Two/javascript/lifefortwo.html ================================================ LIFE FOR TWO

    
    
    
    
    
    
    ================================================
    FILE: 56_Life_for_Two/javascript/lifefortwo.js
    ================================================
    // LIFE FOR TWO
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    var na = [];
    var ka = [, 3,102,103,120,130,121,112,111,12,
              21,30,1020,1030,1011,1021,1003,1002,1012];
    var aa = [,-1,0,1,0,0,-1,0,1,-1,-1,1,-1,-1,1,1,1];
    var xa = [];
    var ya = [];
    var j;
    var k;
    var m2;
    var m3;
    
    function show_data()
    {
        k = 0;
        m2 = 0;
        m3 = 0;
        for (j = 0; j <= 6; j++) {
            print("\n");
            for (k = 0; k <= 6; k++) {
                if (j == 0 || j == 6) {
                    if (k == 6)
                        print(" 0 ");
                    else
                        print(" " + k + " ");
                } else if (k == 0 || k == 6) {
                    if (j == 6)
                        print(" 0\n");
                    else
                        print(" " + j + " ");
                } else {
                    if (na[j][k] >= 3) {
                        for (o1 = 1; o1 <= 18; o1++) {
                            if (na[j][k] == ka[o1])
                                break;
                        }
                        if (o1 <= 18) {
                            if (o1 <= 9) {
                                na[j][k] = 100;
                                m2++;
                                print(" * ");
                            } else {
                                na[j][k] = 1000;
                                m3++;
                                print(" # ");
                            }
                        } else {
                            na[j][k] = 0;
                            print("   ");
                        }
                    } else {
                        na[j][k] = 0;
                        print("   ");
                    }
                }
            }
        }
    }
    
    function process_board()
    {
        for (j = 1; j <= 5; j++) {
            for (k = 1; k <= 5; k++) {
                if (na[j][k] > 99) {
                    b = 1;
                    if (na[j][k] > 999)
                        b = 10;
                    for (o1 = 1; o1 <= 15; o1 += 2) {
                        na[j + aa[o1]][k + aa[o1 + 1]] = na[j + aa[o1]][k + aa[o1 + 1]] + b;
                    }
                }
            }
        }
        show_data();
    }
    
    // Main program
    async function main()
    {
        print(tab(33) + "LIFE2\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        print(tab(10) + "U.B. LIFE GAME\n");
        m2 = 0;
        m3 = 0;
        for (j = 0; j <= 6; j++) {
            na[j] = [];
            for (k = 0; k <= 6; k++)
                na[j][k] = 0;
        }
        for (b = 1; b <= 2; b++) {
            p1 = (b == 2) ? 30 : 3;
            print("\n");
            print("PLAYER " + b + " - 3 LIVE PIECES.\n");
            for (k1 = 1; k1 <= 3; k1++) {
                while (1) {
                    print("X,Y\n");
                    str = await input();
                    ya[b] = parseInt(str);
                    xa[b] = parseInt(str.substr(str.indexOf(",") + 1));
                    if (xa[b] > 0 && xa[b] < 6 && ya[b] > 0 && ya[b] < 5 && na[xa[b]][ya[b]] == 0)
                        break;
                    print("ILLEGAL COORDS. RETYPE\n");
                }
                if (b != 1) {
                    if (xa[1] == xa[2] && ya[1] == ya[2]) {
                        print("SAME COORD.  SET TO 0\n");
                        na[xa[b] + 1][ya[b] + 1] = 0;
                        b = 99;
                    }
                }
                na[xa[b]][ya[b]] = p1;
            }
        }
        show_data();
        while (1) {
            print("\n");
            process_board();
            if (m2 == 0 && m3 == 0) {
                print("\n");
                print("A DRAW\n");
                break;
            }
            if (m3 == 0) {
                print("\n");
                print("PLAYER 1 IS THE WINNER\n");
                break;
            }
            if (m2 == 0) {
                print("\n");
                print("PLAYER 2 IS THE WINNER\n");
                break;
            }
            for (b = 1; b <= 2; b++) {
                print("\n");
                print("\n");
                print("PLAYER " + b + " ");
                while (1) {
                    print("X,Y\n");
                    str = await input();
                    ya[b] = parseInt(str);
                    xa[b] = parseInt(str.substr(str.indexOf(",") + 1));
                    if (xa[b] > 0 && xa[b] < 6 && ya[b] > 0 && ya[b] < 5 && na[xa[b]][ya[b]] == 0)
                        break;
                    print("ILLEGAL COORDS. RETYPE\n");
                }
                if (b != 1) {
                    if (xa[1] == xa[2] && ya[1] == ya[2]) {
                        print("SAME COORD.  SET TO 0\n");
                        na[xa[b] + 1][ya[b] + 1] = 0;
                        b = 99;
                    }
                }
                if (b == 99)
                    break;
            }
            if (b <= 2) {
                na[x[1]][y[1]] = 100;
                na[x[2]][y[2]] = 1000;
            }
        }
    }
    
    main();
    
    
    ================================================
    FILE: 56_Life_for_Two/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 56_Life_for_Two/lifefortwo.bas
    ================================================
    2 PRINT TAB(33);"LIFE2"
    4 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    6 PRINT: PRINT: PRINT
    7 DIM N(6,6),K(18),A(16),X(2),Y(2)
    8 DATA 3,102,103,120,130,121,112,111,12
    9 DATA 21,30,1020,1030,1011,1021,1003,1002,1012
    10 FOR M=1 TO 18: READ K(M): NEXT M
    13 DATA -1,0,1,0,0,-1,0,1,-1,-1,1,-1,-1,1,1,1
    14 FOR O1= 1 TO 16: READ A(O1): NEXT O1
    20 GOTO 500
    50 FOR J=1 TO 5
    51 FOR K=1 TO 5
    55 IF N(J,K)>99 THEN GOSUB 200
    60 NEXT K
    65 NEXT J
    90 K=0: M2=0: M3=0
    99 FOR J=0 TO 6: PRINT
    100 FOR K=0 TO 6
    101 IF J<>0 THEN IF J<>6 THEN 105
    102 IF K=6 THEN PRINT 0;: GOTO 125
    103 PRINT K;: GOTO 120
    105 IF K<>0 THEN IF K<>6 THEN 110
    106 IF J=6 THEN PRINT 0: GOTO 126
    107 PRINT J;: GOTO 120
    110 GOSUB 300
    120 NEXT K
    125 NEXT J
    126 RETURN
    200 B=1: IF N(J,K)>999 THEN B=10
    220 FOR O1= 1 TO 15 STEP 2
    230 N(J+A(O1),K+A(O1+1))=N(J+A(O1),K+A(O1+1))+B
    231 NEXT O1
    239 RETURN
    300 IF N(J,K)<3 THEN 399
    305 FOR O1=1 TO 18
    310 IF N(J,K)=K(O1) THEN 350
    315 NEXT O1
    320 GOTO 399
    350 IF O1>9 THEN 360
    351 N(J,K)=100: M2=M2+1: PRINT " * ";
    355 RETURN
    360 N(J,K)=1000: M3=M3+1: PRINT " # ";
    365 RETURN
    399 N(J,K)=0: PRINT "   ";: RETURN
    500 PRINT TAB(10);"U.B. LIFE GAME"
    505 M2=0: M3=0
    510 FOR J=1 TO 5
    511 FOR K=1 TO 5
    515 N(J,K)=0
    516 NEXT K
    517 NEXT J
    519 FOR B=1 TO 2: P1=3: IF B=2 THEN P1=30
    520 PRINT:PRINT "PLAYER";B;" - 3 LIVE PIECES."
    535 FOR K1=1 TO 3: GOSUB 700
    540 N(X(B),Y(B))=P1: NEXT K1
    542 NEXT B
    559 GOSUB 90
    560 PRINT: GOSUB 50
    570 IF M2=0 THEN IF M3=0 THEN 574
    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 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
    586 N(X(1),Y(1))=100: N(X(2),Y(2))=1000
    596 GOTO 560
    700 PRINT "X,Y":PRINT"XXXXXX";CHR$(13);"$$$$$$";CHR$(13);"&&&&&&";
    701 PRINT CHR$(13);: INPUT Y(B),X(B)
    705 IF X(B)<=5 THEN IF X(B)>0 THEN 708
    706 GOTO 750
    708 IF Y(B)<=5 THEN IF Y(B)>0 THEN 715
    710 GOTO 750
    715 IF N(X(B),Y(B))<>0 THEN 750
    720 IF B=1 THEN RETURN
    725 IF X(1)=X(2) THEN IF Y(1)=Y(2) THEN 740
    730 RETURN
    740 PRINT "SAME COORD.  SET TO 0"
    741 N(X(B)+1,Y(B)+1)=0: B=99: RETURN
    750 PRINT "ILLEGAL COORDS. RETYPE": GOTO 700
    999 END
    
    
    ================================================
    FILE: 56_Life_for_Two/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 56_Life_for_Two/perl/README.md
    ================================================
    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.
    
    
    ================================================
    FILE: 56_Life_for_Two/perl/lifefortwo.pl
    ================================================
    #!/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;
    }
    
    
    ================================================
    FILE: 56_Life_for_Two/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 56_Life_for_Two/python/life_for_two.py
    ================================================
    '''
    LIFE FOR TWO
    
    Competitive Game of Life (two or more players).
    
    Ported by Sajid Sarker (2022).
    '''
    
    # Global Variable Initialisation
    # Initialise the board
    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]
    ga = [0, -1, 0, 1, 0, 0, -1, 0, 1, -1, -1, 1, -1, -1, 1, 1, 1]
    m2 = 0
    m3 = 0
    
    
    # Helper Functions
    def tab(number) -> str:
        t = ""
        while len(t) < number:
            t += " "
        return t
    
    
    def display_header() -> None:
        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
    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(f"\nPLAYER {b} - 3 LIVE PIECES.")
            for _ in range(1, 4):
                query_player(b)
                gn[gx[b]][gy[b]] = p1
    
    
    def modify_board() -> None:
        # Players take turns to add symbols and modify the board
        for b in range(1, 3):
            print(f"PLAYER {b} ")
            query_player(b)
            if b == 99:
                break
        if b <= 2:
            gn[gx[1]][gy[1]] = 100
            gn[gx[2]][gy[2]] = 1000
    
    
    def simulate_board() -> None:
        # Simulate the board for one step
        for j in range(1, 6):
            for k in range(1, 6):
                if gn[j][k] > 99:
                    b = 1 if gn[j][k] <= 999 else 10
                    for o1 in range(1, 16, 2):
                        gn[j + ga[o1] - 1][k + ga[o1 + 1] - 1] += b
                        # gn[j+ga[o1]][k+ga[o1+1]-1] = gn[j+ga[o1]][k+ga[o1+1]]+b
    
    
    def display_board() -> None:
        # Draws the board with all symbols
        m2, m3 = 0, 0
        for j in range(7):
            print("")
            for k in range(7):
                if j in [0, 6]:
                    if k != 6:
                        print(f" {str(k)} ", end="")
                    else:
                        print(" 0 ", end="")
                elif k in [0, 6]:
                    print(f" {str(j)} ", end="")
                elif gn[j][k] < 3:
                    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
    def query_player(b) -> None:
        # Query player for symbol placement coordinates
        while True:
            print("X,Y\nXXXXXX\n$$$$$$\n&&&&&&")
            a_ = input("??")
            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 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)\
                    and gy[b] in range(1, 6)\
                    and gn[gx[b]][gy[b]] == 0:
                break
            print("ILLEGAL COORDS. RETYPE")
        if b != 1:
            if gx[1] == gx[2] and gy[1] == gy[2]:
                print("SAME COORD. SET TO 0")
                gn[gx[b] + 1][gy[b] + 1] = 0
                b = 99
    
    
    # Game Functions
    def check_winner(m2, m3) -> None:
        # Check if the game has been won
        if m2 == 0 and m3 == 0:
            print("\nA DRAW\n")
            return
        if m3 == 0:
            print("\nPLAYER 1 IS THE WINNER\n")
            return
        if m2 == 0:
            print("\nPLAYER 2 IS THE WINNER\n")
            return
    
    
    # Program Flow
    def main() -> None:
        display_header()
        setup_board()
        display_board()
        while True:
            print("\n")
            simulate_board()
            display_board()
            check_winner(m2, m3)
            modify_board()
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 56_Life_for_Two/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 56_Life_for_Two/vbnet/LifeforTwo.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "LifeforTwo", "LifeforTwo.vbproj", "{571A55BD-86BA-4DD2-9769-B258E7654586}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{571A55BD-86BA-4DD2-9769-B258E7654586}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{571A55BD-86BA-4DD2-9769-B258E7654586}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{571A55BD-86BA-4DD2-9769-B258E7654586}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{571A55BD-86BA-4DD2-9769-B258E7654586}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 56_Life_for_Two/vbnet/LifeforTwo.vbproj
    ================================================
    
      
        Exe
        LifeforTwo
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 56_Life_for_Two/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 57_Literature_Quiz/README.md
    ================================================
    ### Literature Quiz
    
    This is a simple CAI-type program which presents four multiple-choice questions from children’s literature. Running the program is self-explanatory.
    
    The program was written by Pamela McGinley while at DEC.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=104)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=117)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 57_Literature_Quiz/csharp/LiteratureQuiz.csproj
    ================================================
    
      
        Exe
        net6.0
        10
        enable
        enable
      
    
    
    
    ================================================
    FILE: 57_Literature_Quiz/csharp/LiteratureQuiz.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LiteratureQuiz", "LiteratureQuiz.csproj", "{1B77EECB-5ECC-41E5-BD12-519CA4C745AE}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{1B77EECB-5ECC-41E5-BD12-519CA4C745AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{1B77EECB-5ECC-41E5-BD12-519CA4C745AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{1B77EECB-5ECC-41E5-BD12-519CA4C745AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{1B77EECB-5ECC-41E5-BD12-519CA4C745AE}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 57_Literature_Quiz/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 57_Literature_Quiz/csharp/litquiz.cs
    ================================================
    using System;
    
    namespace litquiz
    {
        class litquiz
        {
            public static int Score = 0;
    
    
            public static void Main(string[] args)
            {
    
                //Print the title and intro
    
                Console.WriteLine("                         LITERATURE QUIZ");
                Console.WriteLine("               CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine("TEST YOUR KNOWLEDGE OF CHILDREN'S LITERATURE");
                Console.WriteLine();
                Console.WriteLine("THIS IS A MULTIPLE-CHOICE QUIZ");
                Console.WriteLine("TYPE A 1, 2, 3, OR 4 AFTER THE QUESTION MARK.");
                Console.WriteLine();
                Console.WriteLine("GOOD LUCK!");
                Console.WriteLine();
                Console.WriteLine();
                One();
    
    
    
            }
    
            public static void One() {
                Console.WriteLine("IN PINOCCHIO, WHAT WAS THE NAME OF THE CAT");
                Console.WriteLine("1)TIGGER, 2)CICERO, 3)FIGARO, 4)GUIPETTO");
    
                string answerOne;
                answerOne = Console.ReadLine();
    
                if(answerOne == "4")
                {
                    Console.WriteLine("VERY GOOD! HERE'S ANOTHER.");
                    Score = Score + 1;
                    Two();
                }
                else
                {
                    Console.WriteLine("SORRY...FIGARO WAS HIS NAME.");
                    Two();
                }
    
            }
    
            public static void Two()
            {
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine("FROM WHOSE GARDEN DID BUGS BUNNY STEAL THE CARROTS?");
                Console.WriteLine("1)MR. NIXON'S, 2)ELMER FUDD'S, 3)CLEM JUDD'S, 4)STROMBOLI'S");
    
                string answerTwo;
                answerTwo = Console.ReadLine();
    
                if(answerTwo == "2")
                {
                    Console.WriteLine("PRETTY GOOD!");
                    Score = Score + 1;
                    Three();
                }
                else
                {
                    Console.WriteLine("TOO BAD...IT WAS ELMER FUDD'S GARDEN.");
                    Three();
                }
            }
    
            public static void Three()
            {
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine("IN THE WIZARD OF OS, DOROTHY'S DOG WAS NAMED");
                Console.WriteLine("1)CICERO, 2)TRIXIA, 3)KING, 4)TOTO");
    
                string answerThree;
                answerThree = Console.ReadLine();
    
                if(answerThree == "4")
                {
                    Console.WriteLine("YEA!  YOU'RE A REAL LITERATURE GIANT.");
                    Score = Score + 1;
                    Four();
                }
                else
                {
                    Console.WriteLine("BACK TO THE BOOKS,...TOTO WAS HIS NAME.");
                    Four();
                }
    
    
    
    
            }
    
            public static void Four()
            {
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine("WHO WAS THE FAIR MAIDEN WHO ATE THE POISON APPLE");
                Console.WriteLine("1)SLEEPING BEAUTY, 2)CINDERELLA, 3)SNOW WHITE, 4)WENDY");
    
                string answerFour;
                answerFour = Console.ReadLine();
    
                if(answerFour == "3")
                {
                    Console.WriteLine("GOOD MEMORY!");
                    Score = Score + 1;
                    End();
                }
                else
                {
                    Console.WriteLine("OH, COME ON NOW...IT WAS SNOW WHITE.");
                    End();
                }
    
            }
    
            public static void End()
            {
                Console.WriteLine();
                Console.WriteLine();
                if(Score == 4)
                {
                    Console.WriteLine("WOW!  THAT'S SUPER!  YOU REALLY KNOW YOUR NURSERY");
                    Console.WriteLine("YOUR NEXT QUIZ WILL BE ON 2ND CENTURY CHINESE");
                    Console.WriteLine("LITERATURE (HA, HA, HA)");
                    return;
                }
                else if(Score < 2)
                {
                    Console.WriteLine("UGH.  THAT WAS DEFINITELY NOT TOO SWIFT.  BACK TO");
                    Console.WriteLine("NURSERY SCHOOL FOR YOU, MY FRIEND.");
                    return;
                }
                else
                {
                    Console.WriteLine("NOT BAD, BUT YOU MIGHT SPEND A LITTLE MORE TIME");
                    Console.WriteLine("READING THE NURSERY GREATS.");
                    return;
                }
            }
    
    	}
    }
    
    
    ================================================
    FILE: 57_Literature_Quiz/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 57_Literature_Quiz/java/src/LiteratureQuiz.java
    ================================================
    import java.util.Arrays;
    import java.util.Scanner;
    
    /**
     * Game of Literature Quiz
     * 

    * Based on the Basic game of Literature Quiz here * https://github.com/coding-horror/basic-computer-games/blob/main/57%20Literature%20Quiz/litquiz.bas *

    * Note: The idea was to create a version of the 1970's Basic game in Java, without introducing * new features - no additional text, error checking, etc has been added. */ public class LiteratureQuiz { // Used for keyboard input private final Scanner kbScanner; private enum GAME_STATE { STARTUP, QUESTIONS, RESULTS, GAME_OVER } // Current game state private GAME_STATE gameState; // Players correct answers private int correctAnswers; public LiteratureQuiz() { gameState = GAME_STATE.STARTUP; // Initialise kb scanner kbScanner = new Scanner(System.in); } /** * Main game loop */ public void play() { do { switch (gameState) { // Show an introduction the first time the game is played. case STARTUP: intro(); correctAnswers = 0; gameState = GAME_STATE.QUESTIONS; break; // Ask the player four questions case QUESTIONS: // Question 1 System.out.println("IN PINOCCHIO, WHAT WAS THE NAME OF THE CAT"); int question1Answer = displayTextAndGetNumber("1)TIGGER, 2)CICERO, 3)FIGARO, 4)GUIPETTO ? "); if (question1Answer == 3) { System.out.println("VERY GOOD! HERE'S ANOTHER."); correctAnswers++; } else { System.out.println("SORRY...FIGARO WAS HIS NAME."); } System.out.println(); // Question 2 System.out.println("FROM WHOSE GARDEN DID BUGS BUNNY STEAL THE CARROTS?"); int question2Answer = displayTextAndGetNumber("1)MR. NIXON'S, 2)ELMER FUDD'S, 3)CLEM JUDD'S, 4)STROMBOLI'S ? "); if (question2Answer == 2) { System.out.println("PRETTY GOOD!"); correctAnswers++; } else { System.out.println("TOO BAD...IT WAS ELMER FUDD'S GARDEN."); } System.out.println(); // Question 3 System.out.println("IN THE WIZARD OF OS, DOROTHY'S DOG WAS NAMED"); int question3Answer = displayTextAndGetNumber("1)CICERO, 2)TRIXIA, 3)KING, 4)TOTO ? "); if (question3Answer == 4) { System.out.println("YEA! YOU'RE A REAL LITERATURE GIANT."); correctAnswers++; } else { System.out.println("BACK TO THE BOOKS,...TOTO WAS HIS NAME."); } System.out.println(); // Question 4 System.out.println("WHO WAS THE FAIR MAIDEN WHO ATE THE POISON APPLE"); int question4Answer = displayTextAndGetNumber("1)SLEEPING BEAUTY, 2)CINDERELLA, 3)SNOW WHITE, 4)WENDY ? "); if (question4Answer == 3) { System.out.println("GOOD MEMORY!"); correctAnswers++; } else { System.out.println("OH, COME ON NOW...IT WAS SNOW WHITE."); } System.out.println(); gameState = GAME_STATE.RESULTS; break; // How did the player do? case RESULTS: if (correctAnswers == 4) { // All correct System.out.println("WOW! THAT'S SUPER! YOU REALLY KNOW YOUR NURSERY"); System.out.println("YOUR NEXT QUIZ WILL BE ON 2ND CENTURY CHINESE"); System.out.println("LITERATURE (HA, HA, HA)"); // one or none correct } else if (correctAnswers < 2) { System.out.println("UGH. THAT WAS DEFINITELY NOT TOO SWIFT. BACK TO"); System.out.println("NURSERY SCHOOL FOR YOU, MY FRIEND."); // two or three correct } else { System.out.println("NOT BAD, BUT YOU MIGHT SPEND A LITTLE MORE TIME"); System.out.println("READING THE NURSERY GREATS."); } gameState = GAME_STATE.GAME_OVER; break; } } while (gameState != GAME_STATE.GAME_OVER); } public void intro() { System.out.println(simulateTabs(25) + "LITERATURE QUIZ"); System.out.println(simulateTabs(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println(); System.out.println("LITERATURE QUIZ"); System.out.println("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println(); System.out.println("TEST YOUR KNOWLEDGE OF CHILDREN'S LITERATURE."); System.out.println("THIS IS A MULTIPLE-CHOICE QUIZ."); System.out.println("TYPE A 1, 2, 3, OR 4 AFTER THE QUESTION MARK."); System.out.println(); System.out.println("GOOD LUCK!"); System.out.println(); } /** * Simulate the old basic tab(xx) command which indented text by xx spaces. * * @param spaces number of spaces required * @return String with number of spaces */ private String simulateTabs(int spaces) { char[] spacesTemp = new char[spaces]; Arrays.fill(spacesTemp, ' '); return new String(spacesTemp); } /* * Print a message on the screen, then accept input from Keyboard. * Converts input to an Integer * * @param text message to be displayed on screen. * @return what was typed by the player. */ private int displayTextAndGetNumber(String text) { return Integer.parseInt(displayTextAndGetInput(text)); } /* * Print a message on the screen, then accept input from Keyboard. * * @param text message to be displayed on screen. * @return what was typed by the player. */ private String displayTextAndGetInput(String text) { System.out.print(text); return kbScanner.next(); } } ================================================ FILE: 57_Literature_Quiz/java/src/LiteratureQuizGame.java ================================================ public class LiteratureQuizGame { public static void main(String[] args) { LiteratureQuiz literatureQuiz = new LiteratureQuiz(); literatureQuiz.play(); } } ================================================ FILE: 57_Literature_Quiz/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 57_Literature_Quiz/javascript/litquiz.html ================================================ LITERATURE QUIZ

    
    
    
    
    
    
    ================================================
    FILE: 57_Literature_Quiz/javascript/litquiz.js
    ================================================
    // LITQUIZ
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    // Main program
    async function main()
    {
        print(tab(25) + "LITERATURE QUIZ\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        r = 0;
        print("TEST YOUR KNOWLEDGE OF CHILDREN'S LITERATURE.\n");
        print("\n");
        print("THIS IS A MULTIPLE-CHOICE QUIZ.\n");
        print("TYPE A 1, 2, 3, OR 4 AFTER THE QUESTION MARK.\n");
        print("\n");
        print("GOOD LUCK!\n");
        print("\n");
        print("\n");
        print("IN PINOCCHIO, WHAT WAS THE NAME OF THE CAT\n");
        print("1)TIGGER, 2)CICERO, 3)FIGARO, 4)GUIPETTO\n");
        a = parseInt(await input());
        if (a == 3) {
            print("VERY GOOD!  HERE'S ANOTHER.\n");
            r++;
        } else {
            print("SORRY...FIGARO WAS HIS NAME.\n");
        }
        print("\n");
        print("\n");
        print("FROM WHOSE GARDEN DID BUGS BUNNY STEAL THE CARROTS?\n");
        print("1)MR. NIXON'S, 2)ELMER FUDD'S, 3)CLEM JUDD'S, 4)STROMBOLI'S\n");
        a = parseInt(await input());
        if (a == 2) {
            print("PRETTY GOOD!\n");
            r++;
        } else {
            print("TOO BAD...IT WAS ELMER FUDD'S GARDEN.\n");
        }
        print("\n");
        print("\n");
        print("IN THE WIZARD OF OS, DOROTHY'S DOG WAS NAMED\n");
        print("1)CICERO, 2)TRIXIA, 3)KING, 4)TOTO\n");
        a = parseInt(await input());
        if (a == 4) {
            print("YEA!  YOU'RE A REAL LITERATURE GIANT.\n");
            r++;
        } else {
            print("BACK TO THE BOOKS,...TOTO WAS HIS NAME.\n");
        }
        print("\n");
        print("\n");
        print("WHO WAS THE FAIR MAIDEN WHO ATE THE POISON APPLE\n");
        print("1)SLEEPING BEAUTY, 2)CINDERELLA, 3)SNOW WHITE, 4)WENDY\n");
        a = parseInt(await input());
        if (a == 3) {
            print("GOOD MEMORY!\n");
            r++;
        } else {
            print("OH, COME ON NOW...IT WAS SNOW WHITE.\n");
        }
        print("\n");
        print("\n");
        if (r == 4) {
            print("WOW!  THAT'S SUPER!  YOU REALLY KNOW YOUR NURSERY\n");
            print("YOUR NEXT QUIZ WILL BE ON 2ND CENTURY CHINESE\n");
            print("LITERATURE (HA, HA, HA)\n");
        } else if (r < 2) {
            print("UGH.  THAT WAS DEFINITELY NOT TOO SWIFT.  BACK TO\n");
            print("NURSERY SCHOOL FOR YOU, MY FRIEND.\n");
        } else {
            print("NOT BAD, BUT YOU MIGHT SPEND A LITTLE MORE TIME\n");
            print("READING THE NURSERY GREATS.\n");
        }
    }
    
    main();
    
    
    ================================================
    FILE: 57_Literature_Quiz/javascript/litquiz.mjs
    ================================================
    #!/usr/bin/env node
    
    import { println, input } from '../../00_Common/javascript/common.mjs';
    
    function printAlign(message = "", align = "left") {
        // process.stdout.columns is the number of spaces per line in the terminal
        const maxWidth = process.stdout.columns
        if (align === "center") {
            // calculate the amount of spaces required to center the message
            const padColCount = Math.round((process.stdout.columns-message.length)/2);
            const padding = padColCount <= 0 ? '' : ' '.repeat(padColCount);
            println(padding, message);
        } else if (align === "right") {
            const padColCount = Math.round(process.stdout.columns-message.length);
            const padding = padColCount <= 0 ? '' : ' '.repeat(padColCount);
            println(padding, message);
        } else {
            println(message);
        }
    }
    
    function equalIgnoreCase(correct, provided){
        return correct.toString().toLowerCase() === provided.toString().toLowerCase()
    }
    
    async function evaluateQuestion(question, answerOptions, correctAnswer, correctMessage, wrongMessage){
        // ask the user to answer the given question
        println(question);
        println(answerOptions.map((answer, index) => `${index+1})${answer}`).join(', '));
        // this is a blocking wait
        const answer = await input('?')
        const isCorrect = equalIgnoreCase(correctAnswer, answer)
        println(isCorrect ? correctMessage : wrongMessage)
        return isCorrect ? 1 : 0
    }
    
    async function main(){
        let score = 0
    
        printAlign("LITERATURE QUIZ", "center")
        printAlign("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY", "center")
        println("\n\n")
    
        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("\n\n");
    
        score += await evaluateQuestion("IN PINOCCHIO, WHAT WAS THE NAME OF THE CAT?",
            [ "TIGGER", "CICERO", "FIGARO", "GUIPETTO"], 3,
            "VERY GOOD!  HERE'S ANOTHER.", "SORRY...FIGARO WAS HIS NAME.")
        println()
    
        score += await evaluateQuestion("FROM WHOSE GARDEN DID BUGS BUNNY STEAL THE CARROTS?",
            [ "MR. NIXON'S", "ELMER FUDD'S", "CLEM JUDD'S", "STROMBOLI'S" ], 2,
            "PRETTY GOOD!", "TOO BAD...IT WAS ELMER FUDD'S GARDEN.")
        println()
    
        score += await evaluateQuestion("IN THE WIZARD OF OS, DOROTHY'S DOG WAS NAMED",
            [ "CICERO", "TRIXIA", "KING", "TOTO" ], 4,
            "YEA!  YOU'RE A REAL LITERATURE GIANT.",
            "BACK TO THE BOOKS,...TOTO WAS HIS NAME.")
        println()
    
        score += await evaluateQuestion("WHO WAS THE FAIR MAIDEN WHO ATE THE POISON APPLE",
            [ "SLEEPING BEAUTY", "CINDERELLA", "SNOW WHITE", "WENDY" ], 3,
            "GOOD MEMORY!", "OH, COME ON NOW...IT WAS SNOW WHITE.")
    
        println("\n")
    
        if(score === 4) {
            println("WOW!  THAT'S SUPER!  YOU REALLY KNOW YOUR NURSERY\n"+
            "YOUR NEXT QUIZ WILL BE ON 2ND CENTURY CHINESE\n"+
            "LITERATURE (HA, HA, HA)")
        } else if(score <= 2){
            println("UGH.  THAT WAS DEFINITELY NOT TOO SWIFT.  BACK TO\n" +
            "NURSERY SCHOOL FOR YOU, MY FRIEND.")
        } else {
            println("NOT BAD, BUT YOU MIGHT SPEND A LITTLE MORE TIME\n"+
            "READING THE NURSERY GREATS.")
        }
    }
    
    main()
    
    
    ================================================
    FILE: 57_Literature_Quiz/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 57_Literature_Quiz/litquiz.bas
    ================================================
    1 PRINT TAB(25);"LITERATURE QUIZ"
    2 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    3 PRINT:PRINT:PRINT
    5 R=0
    10 PRINT "TEST YOUR KNOWLEDGE OF CHILDREN'S LITERATURE."
    12 PRINT: PRINT "THIS IS A MULTIPLE-CHOICE QUIZ."
    13 PRINT "TYPE A 1, 2, 3, OR 4 AFTER THE QUESTION MARK."
    15 PRINT: PRINT "GOOD LUCK!": PRINT: PRINT
    40 PRINT "IN PINOCCHIO, WHAT WAS THE NAME OF THE CAT"
    42 PRINT "1)TIGGER, 2)CICERO, 3)FIGARO, 4)GUIPETTO";
    43 INPUT A: IF A=3 THEN 46
    44 PRINT "SORRY...FIGARO WAS HIS NAME.": GOTO 50
    46 PRINT "VERY GOOD!  HERE'S ANOTHER."
    47 R=R+1
    50 PRINT: PRINT
    51 PRINT "FROM WHOSE GARDEN DID BUGS BUNNY STEAL THE CARROTS?"
    52 PRINT "1)MR. NIXON'S, 2)ELMER FUDD'S, 3)CLEM JUDD'S, 4)STROMBOLI'S";
    53 INPUT A: IF A=2 THEN 56
    54 PRINT "TOO BAD...IT WAS ELMER FUDD'S GARDEN.": GOTO 60
    56 PRINT "PRETTY GOOD!"
    57 R=R+1
    60 PRINT: PRINT
    61 PRINT "IN THE WIZARD OF OS, DOROTHY'S DOG WAS NAMED"
    62 PRINT "1)CICERO, 2)TRIXIA, 3)KING, 4)TOTO";
    63 INPUT A: IF A=4 THEN 66
    64 PRINT "BACK TO THE BOOKS,...TOTO WAS HIS NAME.": GOTO 70
    66 PRINT "YEA!  YOU'RE A REAL LITERATURE GIANT."
    67 R=R+1
    70 PRINT:PRINT
    71 PRINT "WHO WAS THE FAIR MAIDEN WHO ATE THE POISON APPLE"
    72 PRINT "1)SLEEPING BEAUTY, 2)CINDERELLA, 3)SNOW WHITE, 4)WENDY";
    73 INPUT A: IF A=3 THEN 76
    74 PRINT "OH, COME ON NOW...IT WAS SNOW WHITE."
    75 GOTO 80
    76 PRINT "GOOD MEMORY!"
    77 R=R+1
    80 PRINT:PRINT
    85 IF R=4 THEN 100
    90 IF R<2 THEN 200
    92 PRINT "NOT BAD, BUT YOU MIGHT SPEND A LITTLE MORE TIME"
    94 PRINT "READING THE NURSERY GREATS."
    96 STOP
    100 PRINT "WOW!  THAT'S SUPER!  YOU REALLY KNOW YOUR NURSERY"
    110 PRINT "YOUR NEXT QUIZ WILL BE ON 2ND CENTURY CHINESE"
    120 PRINT "LITERATURE (HA, HA, HA)"
    130 STOP
    200 PRINT "UGH.  THAT WAS DEFINITELY NOT TOO SWIFT.  BACK TO"
    205 PRINT "NURSERY SCHOOL FOR YOU, MY FRIEND."
    999 END
    
    
    ================================================
    FILE: 57_Literature_Quiz/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 57_Literature_Quiz/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 57_Literature_Quiz/perl/litquiz.pl
    ================================================
    #!/usr/bin/perl
    use strict;
    
    
    print ' 'x 25 . "LITERATURE QUIZ\n";
    print ' 'x 15 . "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n";
    print "\n"; print "\n"; print "\n";
    print "TEST YOUR KNOWLEDGE OF CHILDREN'S LITERATURE.\n";
    print "\n"; print "THIS IS A MULTIPLE-CHOICE QUIZ.\n";
    print "TYPE A 1, 2, 3, OR 4 AFTER THE QUESTION MARK.\n";
    print "\n"; print "GOOD LUCK!\n";
    my $R=0;
    
    
    print "\n"; print "\n";
    print "IN PINOCCHIO, WHAT WAS THE NAME OF THE CAT\n";
    print "1)TIGGER, 2)CICERO, 3)FIGARO, 4)GUIPETTO";
    print "? "; chomp(my $A = );
    
    if ($A eq 3) {
    	$R++;
    	print "VERY GOOD! HERE'S ANOTHER.\n";
    	} else {
    	print "SORRY...FIGARO WAS HIS NAME.\n";
    	}
    
    
    print "\n"; print "\n";
    print "FROM WHOSE GARDEN DID BUGS BUNNY STEAL THE CARROTS?\n";
    print "1)MR. NIXON'S, 2)ELMER FUDD'S, 3)CLEM JUDD'S, 4)STROMBOLI'S";
    print "? "; chomp($A = );
    
    if ($A eq 2) {
    	print "PRETTY GOOD!\n";
    	$R=$R+1;
    	} else {
    	print "TOO BAD...IT WAS ELMER FUDD'S GARDEN.\n";
    	}
    
    
    print "\n"; print "\n";
    print "IN THE WIZARD OF OS, DOROTHY'S DOG WAS NAMED\n";
    print "1)CICERO, 2)TRIXIA, 3)KING, 4)TOTO";
    print "? "; chomp($A = );
    if ($A eq 4) {
    	print "YEA! YOU'RE A REAL LITERATURE GIANT.\n";
    	$R=$R+1;
    	} else {
    	print "BACK TO THE BOOKS,...TOTO WAS HIS NAME.\n";
    	}
    
    
    print "\n"; print "\n";
    print "WHO WAS THE FAIR MAIDEN WHO ATE THE POISON APPLE\n";
    print "1)SLEEPING BEAUTY, 2)CINDERELLA, 3)SNOW WHITE, 4)WENDY";
    print "? "; chomp($A = );
    if ($A eq 3) {
    	print "GOOD MEMORY!\n";
    	$R=$R+1;
    	} else {
    	print "OH, COME ON NOW...IT WAS SNOW WHITE.\n";
    	}
    
    
    print "\n"; print "\n";
    if ($R eq 4) {
    	print "WOW! THAT'S SUPER! YOU REALLY KNOW YOUR NURSERY\n";
    	print "YOUR NEXT QUIZ WILL BE ON 2ND CENTURY CHINESE\n";
    	print "LITERATURE (HA, HA, HA)\n";
    	exit
    	}
    if ($R<2) {
    	print "UGH. THAT WAS DEFINITELY NOT TOO SWIFT. BACK TO\n";
    	print "NURSERY SCHOOL FOR YOU, MY FRIEND.\n";
    	exit;
    	}
    
    print "NOT BAD, BUT YOU MIGHT SPEND A LITTLE MORE TIME\n";
    print "READING THE NURSERY GREATS.\n";
    exit;
    
    
    ================================================
    FILE: 57_Literature_Quiz/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 57_Literature_Quiz/python/litquiz.py
    ================================================
    """
    LITQUIZ
    
    A children's literature quiz
    
    Ported by Dave LeCompte
    """
    
    from typing import List, NamedTuple
    
    PAGE_WIDTH = 64
    
    
    class Question(NamedTuple):
        question: str
        answer_list: List[str]
        correct_number: int
        incorrect_message: str
        correct_message: str
    
        def ask(self) -> bool:
            print(self.question)
    
            options = [f"{i+1}){self.answer_list[i]}" for i in range(len(self.answer_list))]
            print(", ".join(options))
    
            response = int(input())
    
            if response == self.correct_number:
                print(self.correct_message)
                return True
            else:
                print(self.incorrect_message)
                return False
    
    
    questions = [
        Question(
            "IN PINOCCHIO, WHAT WAS THE NAME OF THE CAT?",
            ["TIGGER", "CICERO", "FIGARO", "GUIPETTO"],
            3,
            "SORRY...FIGARO WAS HIS NAME.",
            "VERY GOOD!  HERE'S ANOTHER.",
        ),
        Question(
            "FROM WHOSE GARDEN DID BUGS BUNNY STEAL THE CARROTS?",
            ["MR. NIXON'S", "ELMER FUDD'S", "CLEM JUDD'S", "STROMBOLI'S"],
            2,
            "TOO BAD...IT WAS ELMER FUDD'S GARDEN.",
            "PRETTY GOOD!",
        ),
        Question(
            "IN THE WIZARD OF OS, DOROTHY'S DOG WAS NAMED?",
            ["CICERO", "TRIXIA", "KING", "TOTO"],
            4,
            "BACK TO THE BOOKS,...TOTO WAS HIS NAME.",
            "YEA!  YOU'RE A REAL LITERATURE GIANT.",
        ),
        Question(
            "WHO WAS THE FAIR MAIDEN WHO ATE THE POISON APPLE?",
            ["SLEEPING BEAUTY", "CINDERELLA", "SNOW WHITE", "WENDY"],
            3,
            "OH, COME ON NOW...IT WAS SNOW WHITE.",
            "GOOD MEMORY!",
        ),
    ]
    
    
    def print_centered(msg: str) -> None:
        spaces = " " * ((64 - len(msg)) // 2)
        print(spaces + msg)
    
    
    def print_instructions() -> None:
        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()
    
    
    def main() -> None:
        print_centered("LITERATURE QUIZ")
        print_centered("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY")
        print()
        print()
        print()
    
        print_instructions()
    
        score = 0
    
        for q in questions:
            if q.ask():
                score += 1
            print()
            print()
    
        if score == len(questions):
            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)")
        elif score < len(questions) / 2:
            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.")
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 57_Literature_Quiz/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 57_Literature_Quiz/rust/Cargo.toml
    ================================================
    [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]
    
    
    ================================================
    FILE: 57_Literature_Quiz/rust/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Rust](https://www.rust-lang.org/)
    
    
    ================================================
    FILE: 57_Literature_Quiz/rust/src/main.rs
    ================================================
    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);
    
    }
    
    
    
    
    
    ================================================
    FILE: 57_Literature_Quiz/vbnet/LiteratureQuiz.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "LiteratureQuiz", "LiteratureQuiz.vbproj", "{7897F5C5-055B-449D-9BD5-1F631DA87D06}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{7897F5C5-055B-449D-9BD5-1F631DA87D06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{7897F5C5-055B-449D-9BD5-1F631DA87D06}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{7897F5C5-055B-449D-9BD5-1F631DA87D06}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{7897F5C5-055B-449D-9BD5-1F631DA87D06}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 57_Literature_Quiz/vbnet/LiteratureQuiz.vbproj
    ================================================
    
      
        Exe
        LiteratureQuiz
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 57_Literature_Quiz/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 58_Love/README.md
    ================================================
    ### Love
    
    This program is designed to reproduce Robert Indiana’s great art work “Love” with a message of your choice up to 60 characters long.
    
    The love program was created by David Ahl.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=105)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=120)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 58_Love/csharp/Love.csproj
    ================================================
    
    
      
        Exe
        net6.0
      
    
      
        
      
    
      
        
      
    
    
    
    
    ================================================
    FILE: 58_Love/csharp/Love.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 17
    VisualStudioVersion = 17.0.32014.148
    MinimumVisualStudioVersion = 15.0.26124.0
    Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Love", "Love.csproj", "{1C02A3CA-615B-42CF-B696-4514770CA67F}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{1C02A3CA-615B-42CF-B696-4514770CA67F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{1C02A3CA-615B-42CF-B696-4514770CA67F}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{1C02A3CA-615B-42CF-B696-4514770CA67F}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{1C02A3CA-615B-42CF-B696-4514770CA67F}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {F9C55508-108B-4EA1-ADD7-4BA8EC915E68}
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 58_Love/csharp/LovePattern.cs
    ================================================
    using System.IO;
    using System.Text;
    
    namespace Love;
    
    internal class LovePattern
    {
        private const int _lineLength = 60;
        private readonly int[] _segmentLengths = new[] {
            60, 1, 12, 26, 9, 12, 3, 8, 24, 17, 8, 4, 6, 23, 21, 6, 4, 6, 22, 12, 5,
            6, 5, 4, 6, 21, 11, 8, 6, 4, 4, 6, 21, 10, 10, 5, 4, 4, 6, 21, 9, 11, 5,
            4, 4, 6, 21, 8, 11, 6, 4, 4, 6, 21, 7, 11, 7, 4, 4, 6, 21, 6, 11, 8, 4,
            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, 4, 6, 17, 3, 1, 7, 5, 13, 4, 4, 6, 15, 5, 2, 23, 5, 1,
            29, 5, 17, 8, 1, 29, 9, 9, 12, 1, 13, 5, 40, 1, 1, 13, 5, 40, 1, 4, 6,
            13, 3, 10, 6, 12, 5, 1, 5, 6, 11, 3, 11, 6, 14, 3, 1, 5, 6, 11, 3, 11,
            6, 15, 2, 1, 6, 6, 9, 3, 12, 6, 16, 1, 1, 6, 6, 9, 3, 12, 6, 7, 1, 10,
            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, 8, 6, 5, 3, 14, 6, 7, 1, 10, 9, 6, 3, 3, 15, 6, 16, 1, 1, 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, 11, 8, 13, 27, 1, 11, 8, 13, 27, 1, 60
        };
        private readonly StringBuilder _pattern = new();
    
        public LovePattern(string message)
        {
            Fill(new SourceCharacters(_lineLength, message));
        }
    
        private void Fill(SourceCharacters source)
        {
            var lineLength = 0;
    
            foreach (var segmentLength in _segmentLengths)
            {
                foreach (var character in source.GetCharacters(segmentLength))
                {
                    _pattern.Append(character);
                }
                lineLength += segmentLength;
                if (lineLength >= _lineLength)
                {
                    _pattern.AppendLine();
                    lineLength = 0;
                }
            }
        }
    
        public override string ToString() =>
            new StringBuilder()
                .AppendLines(10)
                .Append(_pattern)
                .AppendLines(10)
                .ToString();
    }
    
    
    ================================================
    FILE: 58_Love/csharp/Program.cs
    ================================================
    using Games.Common.IO;
    using Love;
    using Love.Resources;
    
    var io = new ConsoleIO();
    
    io.Write(Resource.Streams.Intro);
    
    var message = io.ReadString("Your message, please");
    
    io.Write(new LovePattern(message));
    
    
    ================================================
    FILE: 58_Love/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 58_Love/csharp/Resources/Intro.txt
    ================================================
                                     Love
                   Creative Computing  Morristown, New Jersey
    
    
    
    A tribute to the great American artist, Robert Indiana.
    His greatest work will be reproduced with a message of
    your choice up to 60 characters.  If you can't think of
    a message, simply type the word 'LOVE'
    
    
    
    ================================================
    FILE: 58_Love/csharp/Resources/Resource.cs
    ================================================
    using System.IO;
    using System.Reflection;
    using System.Runtime.CompilerServices;
    
    namespace Love.Resources;
    
    internal static class Resource
    {
        internal static class Streams
        {
            public static Stream Intro => GetStream();
        }
    
        private static Stream GetStream([CallerMemberName] string name = null)
            => Assembly.GetExecutingAssembly().GetManifestResourceStream($"Love.Resources.{name}.txt");
    }
    
    ================================================
    FILE: 58_Love/csharp/SourceCharacters.cs
    ================================================
    using System;
    
    namespace Love;
    
    internal class SourceCharacters
    {
        private readonly int _lineLength;
        private readonly char[][] _chars;
        private int _currentRow;
        private int _currentIndex;
    
        public SourceCharacters(int lineLength, string message)
        {
            _lineLength = lineLength;
            _chars = new[] { new char[lineLength], new char[lineLength] };
    
            for (int i = 0; i < lineLength; i++)
            {
                _chars[0][i] = message[i % message.Length];
                _chars[1][i] = ' ';
            }
        }
    
        public ReadOnlySpan GetCharacters(int count)
        {
            var span = new ReadOnlySpan(_chars[_currentRow], _currentIndex, count);
    
            _currentRow = 1 - _currentRow;
            _currentIndex += count;
            if (_currentIndex >= _lineLength)
            {
                _currentIndex = _currentRow = 0;
            }
    
            return span;
        }
    }
    
    
    ================================================
    FILE: 58_Love/csharp/StringBuilderExtensions.cs
    ================================================
    using System.Text;
    
    namespace Love;
    
    internal static class StringBuilderExtensions
    {
        internal static StringBuilder AppendLines(this StringBuilder builder, int count)
        {
            for (int i = 0; i < count; i++)
            {
                builder.AppendLine();
            }
    
            return builder;
        }
    }
    
    
    ================================================
    FILE: 58_Love/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 58_Love/java/src/Love.java
    ================================================
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Scanner;
    
    /**
     * Game of Love
     * 

    * Based on the Basic game of Love here * https://github.com/coding-horror/basic-computer-games/blob/main/58%20Love/love.bas *

    * Note: The idea was to create a version of the 1970's Basic game in Java, without introducing * new features - no additional text, error checking, etc has been added. */ public class Love { // This is actually defined in the data, but made it a const for readability public static final int ROW_LENGTH = 60; // Contains the data to draw the picture private final ArrayList data; // Used for keyboard input private final Scanner kbScanner; public Love() { data = storeData(); kbScanner = new Scanner(System.in); } /** * Show an intro, accept a message, then draw the picture. */ public void process() { intro(); int rowLength = data.get(0); String message = displayTextAndGetInput("YOUR MESSAGE, PLEASE "); // ensure the string is at least 60 characters while (message.length() < rowLength) { message += message; } // chop of any extra characters so its exactly ROW_LENGTH in length if (message.length() > ROW_LENGTH) { message = message.substring(0, ROW_LENGTH); } // Print header System.out.println(message); int pos = 1; // don't read row length which is value in first element position int runningLineTotal = 0; StringBuilder lineText = new StringBuilder(); boolean outputChars = true; while (true) { int charsOrSpacesLength = data.get(pos); if (charsOrSpacesLength == ROW_LENGTH) { // EOF, so exit break; } if (outputChars) { // add characters from message string for charsOrSpacesLength characters for (int i = 0; i < charsOrSpacesLength; i++) { lineText.append(message.charAt(i + runningLineTotal)); // switch to spaces which will be in the next element of the arraylist outputChars = false; } } else { // add charsOrSpacesLength spaces to the string lineText.append(addSpaces(charsOrSpacesLength)); // Switch to chars to output on next loop outputChars = true; } // We need to know when to print the string out runningLineTotal += charsOrSpacesLength; // Are we at end of line? If so print and reset for next line if (runningLineTotal >= ROW_LENGTH) { System.out.println(lineText); lineText = new StringBuilder(); runningLineTotal = 0; outputChars = true; } // Move to next arraylist element pos++; } // Print footer System.out.println(message); } private void intro() { System.out.println(addSpaces(33) + "LOVE"); System.out.println(addSpaces(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println(); System.out.println("A TRIBUTE TO THE GREAT AMERICAN ARTIST, ROBERT INDIANA."); System.out.println("HIS GREATEST WORK WILL BE REPRODUCED WITH A MESSAGE OF"); System.out.println("YOUR CHOICE UP TO 60 CHARACTERS. IF YOU CAN'T THINK OF"); System.out.println("A MESSAGE, SIMPLE TYPE THE WORD 'LOVE'"); System.out.println(); } /* * Print a message on the screen, then accept input from Keyboard. * * @param text message to be displayed on screen. * @return what was typed by the player. */ private String displayTextAndGetInput(String text) { System.out.print(text); return kbScanner.nextLine(); } /** * Return a string of x spaces * * @param spaces number of spaces required * @return String with number of spaces */ private String addSpaces(int spaces) { char[] spacesTemp = new char[spaces]; Arrays.fill(spacesTemp, ' '); return new String(spacesTemp); } /** * Original Basic program had the data in DATA format. We're importing all the data into an array for ease of * processing. * Format of data is * FIRST int of data is 60, which is the number of characters per line. * LAST int of data is same as FIRST above. * Then the data alternates between how many characters to print and how many spaces to print * You need to keep a running total of the count of ints read and once this hits 60, its time to * print and then reset count to zero. * * @return ArrayList of type Integer containing the data */ private ArrayList storeData() { ArrayList theData = new ArrayList<>(); theData.addAll(Arrays.asList(60, 1, 12, 26, 9, 12, 3, 8, 24, 17, 8, 4, 6, 23, 21, 6, 4, 6, 22, 12, 5, 6, 5)); theData.addAll(Arrays.asList(4, 6, 21, 11, 8, 6, 4, 4, 6, 21, 10, 10, 5, 4, 4, 6, 21, 9, 11, 5, 4)); theData.addAll(Arrays.asList(4, 6, 21, 8, 11, 6, 4, 4, 6, 21, 7, 11, 7, 4, 4, 6, 21, 6, 11, 8, 4)); theData.addAll(Arrays.asList(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)); theData.addAll(Arrays.asList(4, 6, 17, 3, 1, 7, 5, 13, 4, 4, 6, 15, 5, 2, 23, 5, 1, 29, 5, 17, 8)); theData.addAll(Arrays.asList(1, 29, 9, 9, 12, 1, 13, 5, 40, 1, 1, 13, 5, 40, 1, 4, 6, 13, 3, 10, 6, 12, 5, 1)); theData.addAll(Arrays.asList(5, 6, 11, 3, 11, 6, 14, 3, 1, 5, 6, 11, 3, 11, 6, 15, 2, 1)); theData.addAll(Arrays.asList(6, 6, 9, 3, 12, 6, 16, 1, 1, 6, 6, 9, 3, 12, 6, 7, 1, 10)); theData.addAll(Arrays.asList(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)); theData.addAll(Arrays.asList(8, 6, 5, 3, 14, 6, 7, 1, 10, 9, 6, 3, 3, 15, 6, 16, 1, 1)); theData.addAll(Arrays.asList(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)); theData.addAll(Arrays.asList(11, 8, 13, 27, 1, 11, 8, 13, 27, 1, 60)); return theData; } public static void main(String[] args) { Love love = new Love(); love.process(); } } ================================================ FILE: 58_Love/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 58_Love/javascript/love.html ================================================ LOVE

    
    
    
    
    
    
    ================================================
    FILE: 58_Love/javascript/love.js
    ================================================
    // LOVE
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    var data = [60,1,12,26,9,12,3,8,24,17,8,4,6,23,21,6,4,6,22,12,5,6,5,
                4,6,21,11,8,6,4,4,6,21,10,10,5,4,4,6,21,9,11,5,4,
                4,6,21,8,11,6,4,4,6,21,7,11,7,4,4,6,21,6,11,8,4,
                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,
                4,6,17,3,1,7,5,13,4,4,6,15,5,2,23,5,1,29,5,17,8,
                1,29,9,9,12,1,13,5,40,1,1,13,5,40,1,4,6,13,3,10,6,12,5,1,
                5,6,11,3,11,6,14,3,1,5,6,11,3,11,6,15,2,1,
                6,6,9,3,12,6,16,1,1,6,6,9,3,12,6,7,1,10,
                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,
                8,6,5,3,14,6,7,1,10,9,6,3,3,15,6,16,1,1,
                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,
                11,8,13,27,1,11,8,13,27,1,60];
    
    // Main program
    async function main()
    {
        print(tab(33) + "LOVE\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        print("A TRIBUTE TO THE GREAT AMERICAN ARTIST, ROBERT INDIANA.\n");
        print("HIS GREATEST WORK WILL BE REPRODUCED WITH A MESSAGE OF\n");
        print("YOUR CHOICE UP TO 60 CHARACTERS.  IF YOU CAN'T THINK OF\n");
        print("A MESSAGE, SIMPLE TYPE THE WORD 'LOVE'\n");
        print("\n");
        print("YOUR MESSAGE, PLEASE");
        str = await input();
        l = str.length;
        ts = [];
        for (i = 1; i <= 10; i++)
            print("\n");
        ts = "";
        do {
            ts += str;
        } while (ts.length < 60) ;
        pos = 0;
        c = 0;
        while (++c < 37) {
            a1 = 1;
            p = 1;
            print("\n");
            do {
                a = data[pos++];
                a1 += a;
                if (p != 1) {
                    for (i = 1; i <= a; i++)
                        print(" ");
                    p = 1;
                } else {
                    for (i = a1 - a; i <= a1 - 1; i++)
                        print(ts[i]);
                    p = 0;
                }
            } while (a1 <= 60) ;
        }
        for (i = 1; i <= 10; i++)
            print("\n");
    }
    
    main();
    
    
    ================================================
    FILE: 58_Love/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 58_Love/love.bas
    ================================================
    2 PRINT TAB(33);"LOVE"
    4 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    6 PRINT: PRINT: PRINT
    20 PRINT "A TRIBUTE TO THE GREAT AMERICAN ARTIST, ROBERT INDIANA."
    30 PRINT "HIS GREATEST WORK WILL BE REPRODUCED WITH A MESSAGE OF"
    40 PRINT "YOUR CHOICE UP TO 60 CHARACTERS.  IF YOU CAN'T THINK OF"
    50 PRINT "A MESSAGE, SIMPLE TYPE THE WORD 'LOVE'": PRINT
    60 INPUT "YOUR MESSAGE, PLEASE";A$: L=LEN(A$)
    70 DIM T$(120): FOR I=1 TO 10: PRINT: NEXT I
    100 FOR J=0 TO INT(60/L)
    110 FOR I=1 TO L
    120 T$(J*L+I)=MID$(A$,I,1)
    130 NEXT I: NEXT J
    140 C=0
    200 A1=1: P=1: C=C+1: IF C=37 THEN 999
    205 PRINT
    210 READ A: A1=A1+A: IF P=1 THEN 300
    240 FOR I=1 TO A: PRINT " ";: NEXT I: P=1: GOTO 400
    300 FOR I=A1-A TO A1-1: PRINT T$(I);: NEXT I: P=0
    400 IF A1>60 THEN 200
    410 GOTO 210
    600 DATA 60,1,12,26,9,12,3,8,24,17,8,4,6,23,21,6,4,6,22,12,5,6,5
    610 DATA 4,6,21,11,8,6,4,4,6,21,10,10,5,4,4,6,21,9,11,5,4
    620 DATA 4,6,21,8,11,6,4,4,6,21,7,11,7,4,4,6,21,6,11,8,4
    630 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
    640 DATA 4,6,17,3,1,7,5,13,4,4,6,15,5,2,23,5,1,29,5,17,8
    650 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
    660 DATA 5,6,11,3,11,6,14,3,1,5,6,11,3,11,6,15,2,1
    670 DATA 6,6,9,3,12,6,16,1,1,6,6,9,3,12,6,7,1,10
    680 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
    690 DATA 8,6,5,3,14,6,7,1,10,9,6,3,3,15,6,16,1,1
    700 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
    710 DATA 11,8,13,27,1,11,8,13,27,1,60
    999 FOR I=1 TO 10: PRINT: NEXT I: END
    
    
    ================================================
    FILE: 58_Love/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 58_Love/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 58_Love/perl/love.pl
    ================================================
    #!/usr/bin/perl
    use strict;
    
    
    print ' 'x 33 . "LOVE\n";
    print ' 'x 15 . "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n";
    print "\n\n\n";
    print "A TRIBUTE TO THE GREAT AMERICAN ARTIST, ROBERT INDIANA.\n";
    print "HIS GREATEST WORK WILL BE REPRODUCED WITH A MESSAGE OF\n";
    print "YOUR CHOICE UP TO 60 CHARACTERS.  IF YOU CAN'T THINK OF\n";
    print "A MESSAGE, SIMPLE TYPE THE WORD 'LOVE'\n"; print "\n";
    print "YOUR MESSAGE, PLEASE? "; chomp(my $A = );
    my $L= length($A);
    
    
    #Original logic too fuzzy, remaked.
    my $Width= 60;
    my $Text= substr($A x ($Width/$L+1), 0, $Width);
    for (my $I=1; $I<10; $I++) { print "\n"; }
    
    my @Data= (
    	60,1,12,26,9,12,3,8,24,17,8,4,6,23,21,6,4,6,22,12,5,6,5,
    	4,6,21,11,8,6,4,4,6,21,10,10,5,4,4,6,21,9,11,5,4,
    	4,6,21,8,11,6,4,4,6,21,7,11,7,4,4,6,21,6,11,8,4,
    	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,
    	4,6,17,3,1,7,5,13,4,4,6,15,5,2,23,5,1,29,5,17,8,
    	1,29,9,9,12,1,13,5,40,1,1,13,5,40,1,4,6,13,3,10,6,12,5,1,
    	5,6,11,3,11,6,14,3,1,5,6,11,3,11,6,15,2,1,
    	6,6,9,3,12,6,16,1,1,6,6,9,3,12,6,7,1,10,
    	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,
    	8,6,5,3,14,6,7,1,10,9,6,3,3,15,6,16,1,1,
    	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,
    	11,8,13,27,1,11,8,13,27,1,60
    	);
    
    
    my $Pos=0;
    my $Toggle=1;
    foreach my $Size (@Data) {
    	my $Chunk= $Toggle ? substr($Text, $Pos, $Size) : " " x $Size;
    	print $Chunk;
    	$Pos+= $Size;
    	$Toggle= !$Toggle;
    	if ($Pos>= $Width) {
    		print "\n";
    		$Toggle=1;
    		$Pos=0;
    		}
    	}
    
    for (my $I=1; $I<10; $I++) { print "\n"; }
    exit;
    
    
    ================================================
    FILE: 58_Love/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 58_Love/python/love.py
    ================================================
    """
    LOVE
    
    From: BASIC Computer Games (1978)
          Edited by David H. Ahl
    
    "This program is designed to reproduce Robert Indiana's great art
     work 'Love' with a message of your choice up to 60 characters long.
    
    "The [DATA variable is] an alternating count of the number
     of characters and blanks which form the design.  These data give
     the correct proportions for a standard 10 character per inch
     Teletype or line printer.
    
    "The LOVE program was created by David Ahl."
    
    
    Python port by Jeff Jetton, 2019
    """
    
    
    # Image data. Each top-level element is a row. Each row element
    # contains alternating character and blank run lengths.
    DATA = [
        [
            60,
        ],
        [1, 12, 26, 9, 12],
        [3, 8, 24, 17, 8],
        [4, 6, 23, 21, 6],
        [4, 6, 22, 12, 5, 6, 5],
        [4, 6, 21, 11, 8, 6, 4],
        [4, 6, 21, 10, 10, 5, 4],
        [4, 6, 21, 9, 11, 5, 4],
        [4, 6, 21, 8, 11, 6, 4],
        [4, 6, 21, 7, 11, 7, 4],
        [4, 6, 21, 6, 11, 8, 4],
        [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],
        [4, 6, 17, 3, 1, 7, 5, 13, 4],
        [4, 6, 15, 5, 2, 23, 5],
        [1, 29, 5, 17, 8],
        [1, 29, 9, 9, 12],
        [1, 13, 5, 40, 1],
        [1, 13, 5, 40, 1],
        [4, 6, 13, 3, 10, 6, 12, 5, 1],
        [5, 6, 11, 3, 11, 6, 14, 3, 1],
        [5, 6, 11, 3, 11, 6, 15, 2, 1],
        [6, 6, 9, 3, 12, 6, 16, 1, 1],
        [6, 6, 9, 3, 12, 6, 7, 1, 10],
        [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],
        [8, 6, 5, 3, 14, 6, 7, 1, 10],
        [9, 6, 3, 3, 15, 6, 16, 1, 1],
        [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],
        [11, 8, 13, 27, 1],
        [11, 8, 13, 27, 1],
        [
            60,
        ],
    ]
    
    
    # Assume that the total length of the first element
    # is the line length used by every row
    ROW_LEN = sum(DATA[0])
    
    
    def main() -> None:
        # Display intro text
        print("\n                  Love")
        print("Creative Computing  Morristown, New Jersey")
        print("\n\n")
        print("A tribute to the great American artist, Robert Indiana.")
        print("His great 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'\n")  # (sic)
    
        # Get message from user
        message = input("Your message, please? ")
        if message == "":
            message = "LOVE"
    
        # Repeat the message until we get at least one line's worth
        while len(message) < ROW_LEN:
            message += message
    
        # Display image
        print("\n" * 9)
        for row in DATA:
            print_message = True
            position = 0
            line_text = ""
            for length in row:
                if print_message:
                    text = message[position : (position + length)]
                    print_message = False
                else:
                    text = " " * length
                    print_message = True
                line_text += text
                position += length
            print(line_text)
    
        print()
    
    
    if __name__ == "__main__":
        main()
    
    
    ######################################################################
    #
    # Porting Notes
    #
    #   Not too different from the original, logic-wise. The image was
    #   originally encoded as a series of BASIC "DATA" lines. Here,
    #   we've converted it to a more Pythonic nested list structure.
    #   Other changes include reducing some of the vertical spacing
    #   (since we'll probably be showing this on a screen rather than
    #   the sort of tractor-feed printer the program was written for)
    #   and having the message default to LOVE when no input is given.
    #
    #   This program uses a simple version of run-length encoding to
    #   compress a 60 x 36 image (2,160 characters) into just 252 DATA
    #   values.  That's about an 8.5-to-1 data compression ratio,
    #   which is pretty good!
    #
    #
    # Ideas for Modifications
    #
    #   Process the user's message input to remove spaces and change
    #   to uppercase.
    #
    #   Encode other images in a similar fashion and let the user choose
    #   which one they'd like to use to display their message.
    #
    #   To help with the above step, create a program that reads in a
    #   text file of any sort of similar character/space art and produces
    #   the Python code to initialize the correct nested list of values.
    #
    #   For example, if the input file were:
    #
    #     *****
    #     *  **
    #     **  *
    #
    #   Your program would output:
    #
    #    ((5, ), (1, 1, 2), (2, 1, 1))
    #
    ######################################################################
    
    
    ================================================
    FILE: 58_Love/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 58_Love/ruby/love.rb
    ================================================
    data = [60, 1, 12, 26, 9, 12, 3, 8, 24, 17, 8, 4, 6, 23, 21, 6, 4, 6, 22, 12, 5, 6, 5,
            4, 6, 21, 11, 8, 6, 4, 4, 6, 21, 10, 10, 5, 4, 4, 6, 21, 9, 11, 5, 4, 4, 6, 21,
            8, 11, 6, 4, 4, 6, 21, 7, 11, 7, 4, 4, 6, 21, 6, 11, 8, 4, 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, 4, 6, 17,
            3, 1, 7, 5, 13, 4, 4, 6, 15, 5, 2, 23, 5, 1, 29, 5, 17, 8, 1, 29, 9, 9, 12, 1,
            13, 5, 40, 1, 1, 13, 5, 40, 1, 4, 6, 13, 3, 10, 6, 12, 5, 1, 5, 6, 11, 3, 11,
            6, 14, 3, 1, 5, 6, 11, 3, 11, 6, 15, 2, 1, 6, 6, 9, 3, 12, 6, 16, 1, 1, 6, 6,
            9, 3, 12, 6, 7, 1, 10, 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, 8, 6, 5, 3, 14, 6, 7, 1, 10, 9, 6, 3, 3, 15, 6, 16, 1,
            1, 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, 11, 8, 13, 27, 1, 11, 8, 13, 27, 1, 60]
    
    puts 'LOVE'.center(60)
    puts 'stephan.com'.center(60)
    puts "\n\n"
    
    puts <<~EOLOVE
      A TRIBUTE TO THE GREAT AMERICAN ARTIST, ROBERT INDIANA.
      HIS GREATEST WORK WILL BE REPRODUCED WITH A MESSAGE OF
      YOUR CHOICE UP TO 60 CHARACTERS.  IF YOU CAN'T THINK OF
      A MESSAGE, SIMPLY TYPE THE WORD 'LOVE'\n
    EOLOVE
    
    message = gets.strip
    message = 'love' if message.empty?
    l = message.length
    
    until data.empty?
      puts
      col = 0
      p = true
      while col < 60
        run = data.shift
    
        if p
          run.times { |i| print message[(col + i) % l] }
        else
          print ' ' * run
        end
        p = !p
        col += run
      end
    end
    
    
    ================================================
    FILE: 58_Love/rust/Cargo.toml
    ================================================
    [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]
    
    
    ================================================
    FILE: 58_Love/rust/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Rust](https://www.rust-lang.org/) by Jadi.
    
    
    ================================================
    FILE: 58_Love/rust/src/main.rs
    ================================================
    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!();
        }
    }
    
    
    ================================================
    FILE: 58_Love/vbnet/Love.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Love", "Love.vbproj", "{440AD3C3-D842-4717-BB4D-C54463F59ECA}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{440AD3C3-D842-4717-BB4D-C54463F59ECA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{440AD3C3-D842-4717-BB4D-C54463F59ECA}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{440AD3C3-D842-4717-BB4D-C54463F59ECA}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{440AD3C3-D842-4717-BB4D-C54463F59ECA}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 58_Love/vbnet/Love.vbproj
    ================================================
    
      
        Exe
        Love
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 58_Love/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 59_Lunar_LEM_Rocket/README.md
    ================================================
    ### Lunar LEM Rocket
    
    This game in its many different versions and names (ROCKET, LUNAR, LEM, and APOLLO) is by far and away the single most popular computer game. It exists in various versions that start you anywhere from 500 feet to 200 miles away from the moon, or other planets, too. Some allow the control of directional stabilization rockets and/or the retro rocket. The three versions presented here represent the most popular of the many variations.
    
    In most versions of this game, the temptation is to slow up too soon and then have no fuel left for the lower part of the journey. This, of course, is disastrous (as you will find out when you land your own capsule)!
    
    LUNAR was originally in FOCAL by Jim Storer while a student at Lexington High School and subsequently converted to BASIC by David Ahl. ROCKET was written by Eric Peters at DEC and LEM by William Labaree II of Alexandria, Virginia.
    
    In this program, you set the burn rate of the retro rockets (pounds of fuel per second) every 10 seconds and attempt to achieve a soft landing on the moon. 200 lbs/sec really puts the brakes on, and 0 lbs/sec is free fall. Ignition occurs a 8 lbs/sec, so _do not_ use burn rates between 1 and 7 lbs/sec. To make the landing more of a challenge, but more closely approximate the real Apollo LEM capsule, you should make the available fuel at the start (N) equal to 16,000 lbs, and the weight of the capsule (M) equal to 32,500 lbs.
    
    #### LEM
    This is the most comprehensive of the three versions and permits you to control the time interval of firing, the thrust, and the attitude angle. It also allows you to work in the metric or English system of measurement. The instructions in the program dialog are very complete, so you shouldn’t have any trouble.
    
    #### ROCKET
    In this version, you start 500 feet above the lunar surface and control the burn rate in 1-second bursts. Each unit of fuel slows your descent by 1 ft/sec. The maximum thrust of your engine is 30 ft/sec/sec.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=106)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=121)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### 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.
    
    
    ================================================
    FILE: 59_Lunar_LEM_Rocket/csharp/LunarLEMRocket.csproj
    ================================================
    
      
        Exe
        net6.0
        10
        enable
        enable
      
    
    
    
    ================================================
    FILE: 59_Lunar_LEM_Rocket/csharp/LunarLEMRocket.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LunarLEMRocket", "LunarLEMRocket.csproj", "{12D3D8F6-5468-49BD-BDD6-E9B4D5954496}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{12D3D8F6-5468-49BD-BDD6-E9B4D5954496}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{12D3D8F6-5468-49BD-BDD6-E9B4D5954496}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{12D3D8F6-5468-49BD-BDD6-E9B4D5954496}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{12D3D8F6-5468-49BD-BDD6-E9B4D5954496}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 59_Lunar_LEM_Rocket/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 59_Lunar_LEM_Rocket/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 59_Lunar_LEM_Rocket/javascript/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells)
    
    
    ================================================
    FILE: 59_Lunar_LEM_Rocket/javascript/lem.html
    ================================================
    
    
    LEM
    
    
    
    
    
    
    
    
    
    
    ================================================
    FILE: 59_Lunar_LEM_Rocket/javascript/lem.js
    ================================================
    // LEM
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    // Main program
    async function main()
    {
        print(tab(34) + "LEM\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        // ROCKT2 is an interactive game that simulates a lunar
        // landing is similar to that of the Apollo program.
        // There is absolutely no chance involved
        zs = "GO";
        b1 = 1;
        while (1) {
            m = 17.95;
            f1 = 5.25;
            n = 7.5;
            r0 = 926;
            v0 = 1.29;
            t = 0;
            h0 = 60;
            r = r0 + h0;
            a = -3,425;
            r1 = 0;
            a1 = 8.84361e-4;
            r3 = 0;
            a3 = 0;
            m1 = 7.45;
            m0 = m1;
            b = 750;
            t1 = 0;
            f = 0;
            p = 0;
            n = 1;
            m2 = 0;
            s = 0;
            c = 0;
            if (zs == "YES") {
                print("\n");
                print("OK, DO YOU WANT THE COMPLETE INSTRUCTIONS OR THE INPUT -\n");
                print("OUTPUT STATEMENTS?\n");
                while (1) {
                    print("1=COMPLETE INSTRUCTIONS\n");
                    print("2=INPUT-OUTPUT STATEMENTS\n");
                    print("3=NEITHER\n");
                    b1 = parseInt(await input());
                    qs = "NO";
                    if (b1 == 1)
                        break;
                    qs = "YES";
                    if (b1 == 2 || b1 == 3)
                        break;
                }
            } else {
                print("\n");
                print("LUNAR LANDING SIMULATION\n");
                print("\n");
                print("HAVE YOU FLOWN AN APOLLO/LEM MISSION BEFORE");
                while (1) {
                    print(" (YES OR NO)");
                    qs = await input();
                    if (qs == "YES" || qs == "NO")
                        break;
                    print("JUST ANSWER THE QUESTION, PLEASE, ");
                }
            }
            if (qs == "YES") {
                print("\n");
                print("INPUT MEASUREMENT OPTION NUMBER");
            } else {
                print("\n");
                print("WHICH SYSTEM OF MEASUREMENT DO YOU PREFER?\n");
                print(" 1=METRIC     0=ENGLISH\n");
                print("ENTER THE APPROPRIATE NUMBER");
            }
            while (1) {
                k = parseInt(await input());
                if (k == 0 || k == 1)
                    break;
                print("ENTER THE APPROPRIATE NUMBER");
            }
            if (k == 1) {
                z = 1852.8;
                ms = "METERS";
                g3 = 3.6;
                ns = " KILOMETERS";
                g5 = 1000;
            } else {
                z = 6080;
                ms = "FEET";
                g3 = 0.592;
                ns = "N.MILES";
                g5 = z;
            }
            if (b1 != 3) {
                if (qs != "YES") {
                    print("\n");
                    print("  YOU ARE ON A LUNAR LANDING MISSION.  AS THE PILOT OF\n");
                    print("THE LUNAR EXCURSION MODULE, YOU WILL BE EXPECTED TO\n");
                    print("GIVE CERTAIN COMMANDS TO THE MODULE NAVIGATION SYSTEM.\n");
                    print("THE ON-BOARD COMPUTER WILL GIVE A RUNNING ACCOUNT\n");
                    print("OF INFORMATION NEEDED TO NAVIGATE THE SHIP.\n");
                    print("\n");
                    print("\n");
                    print("THE ATTITUDE ANGLE CALLED FOR IS DESCRIBED AS FOLLOWS.\n");
                    print("+ OR -180 DEGREES IS DIRECTLY AWAY FROM THE MOON\n");
                    print("-90 DEGREES IS ON A TANGENT IN THE DIRECTION OF ORBIT\n");
                    print("+90 DEGREES IS ON A TANGENT FROM THE DIRECTION OF ORBIT\n");
                    print("0 (ZERO) DEGREES IS DIRECTLY TOWARD THE MOON\n");
                    print("\n");
                    print(tab(30) + "-180|+180\n");
                    print(tab(34) + "^\n");
                    print(tab(27) + "-90 < -+- > +90\n");
                    print(tab(34) + "!\n");
                    print(tab(34) + "0\n");
                    print(tab(21) + "<<<< DIRECTION OF ORBIT <<<<\n");
                    print("\n");
                    print(tab(20) + "------ SURFACE OF MOON ------\n");
                    print("\n");
                    print("\n");
                    print("ALL ANGLES BETWEEN -180 AND +180 DEGREES ARE ACCEPTED.\n");
                    print("\n");
                    print("1 FUEL UNIT = 1 SEC. AT MAX THRUST\n");
                    print("ANY DISCREPANCIES ARE ACCOUNTED FOR IN THE USE OF FUEL\n");
                    print("FOR AN ATTITUDE CHANGE.\n");
                    print("AVAILABLE ENGINE POWER: 0 (ZERO) AND ANY VALUE BETWEEN\n");
                    print("10 AND 100 PERCENT.\n");
                    print("\n");
                    print("NEGATIVE THRUST OR TIME IS PROHIBITED.\n");
                    print("\n");
                }
                print("\n");
                print("INPUT: TIME INTERVAL IN SECONDS ------ (T)\n");
                print("       PERCENTAGE OF THRUST ---------- (P)\n");
                print("       ATTITUDE ANGLE IN DEGREES ----- (A)\n");
                print("\n");
                if (qs != "YES") {
                    print("FOR EXAMPLE:\n");
                    print("T,P,A? 10,65,-60\n");
                    print("TO ABORT THE MISSION AT ANY TIME, ENTER 0,0,0\n");
                    print("\n");
                }
                print("OUTPUT: TOTAL TIME IN ELAPSED SECONDS\n");
                print("        HEIGHT IN " + ms + "\n");
                print("        DISTANCE FROM LANDING SITE IN " + ms + "\n");
                print("        VERTICAL VELOCITY IN " + ms + "/SECOND\n");
                print("        HORIZONTAL VELOCITY IN " + ms + "/SECOND\n");
                print("        FUEL UNITS REMAINING\n");
                print("\n");
            }
            while (1) {
                for (i = 1; i <= n; i++) {
                    if (m1 != 0) {
                        m1 -= m2;
                        if (m1 <= 0) {
                            f = f * (1 + m1 / m2);
                            m2 = m1 + m2;
                            print("YOU ARE OUT OF FUEL.\n");
                            m1 = 0;
                        }
                    } else {
                        f = 0;
                        m2 = 0;
                    }
                    m = m - 0.5 * m2;
                    r4 = r3;
                    r3 = -0.5 * r0 * Math.pow(v0 / r, 2) + r * a1 * a1;
                    r2 = (3 * r3 - r4) / 2 + 0.00526 * f1 * f * c / m;
                    a4 = a3;
                    a3 = -2 * r1 * a1 / r;
                    a2 = (3 * a3 - a4) / 2 + 0.0056 * f1 * f * s / (m * r);
                    x = r1 * t1 + 0.5 * r2 * t1 * t1;
                    r = r + x;
                    h0 = h0 + x;
                    r1 = r1 + r2 * t1;
                    a = a + a1 * t1 + 0.5 * a2 * t1 * t1;
                    a1 = a1 + a2 * t1;
                    m = m - 0.5 * m2;
                    t = t + t1;
                    if (h0 < 3.287828e-4)
                        break;
                }
                h = h0 * z;
                h1 = r1 * z;
                d = r0 * a * z;
                d1 = r * a1 * z;
                t2 = m1 * b / m0;
                print(" " + t + "\t" + h + "\t" + d + "\t" + h1 + "\t" + d1 + "\t" + t2 + "\n");
                if (h0 < 3.287828e-4) {
                    if (r1 < -8.21957e-4 || Math.abs(r * a1) > 4.93174e-4 || h0 < -3.287828e-4) {
                        print("\n");
                        print("CRASH !!!!!!!!!!!!!!!!\n");
                        print("YOUR IMPACT CREATED A CRATER " + Math.abs(h) + " " + ms + " DEEP.\n");
                        x1 = Math.sqrt(d1 * d1 + h1 * h1) * g3;
                        print("AT CONTACT YOU WERE TRAVELING " + x1 + " " + ns + "/HR\n");
                        break;
                    }
                    if (Math.abs(d) > 10 * z) {
                        print("YOU ARE DOWN SAFELY - \n");
                        print("\n");
                        print("BUT MISSED THE LANDING SITE BY " + Math.abs(d / g5) + " " + ns + ".\n");
                        break;
                    }
                    print("\n");
                    print("TRANQUILITY BASE HERE -- THE EAGLE HAS LANDED.\n");
                    print("CONGRATULATIONS -- THERE WAS NO SPACECRAFT DAMAGE.\n");
                    print("YOU MAY NOW PROCEED WITH SURFACE EXPLORATION.\n");
                    break;
                }
                if (r0 * a > 164.474) {
                    print("\n");
                    print("YOU HAVE BEEN LOST IN SPACE WITH NO HOPE OF RECOVERY.\n");
                    break;
                }
                if (m1 > 0) {
                    while (1) {
                        print("T,P,A");
                        str = await input();
                        t1 = parseFloat(str);
                        f = parseFloat(str.substr(str.indexOf(",") + 1));
                        p = parseFloat(str.substr(str.lastIndexOf(",") + 1));
                        f = f / 100;
                        if (t1 < 0) {
                            print("\n");
                            print("THIS SPACECRAFT IS NOT ABLE TO VIOLATE THE SPACE-");
                            print("TIME CONTINUUM.\n");
                            print("\n");
                        } else if (t1 == 0) {
                            break;
                        } else if (Math.abs(f - 0.05) > 1 || Math.abs(f - 0.05) < 0.05) {
                            print("IMPOSSIBLE THRUST VALUE ");
                            if (f < 0) {
                                print("NEGATIVE\n");
                            } else if (f - 0.05 < 0.05) {
                                print("TOO SMALL\n");
                            } else {
                                print("TOO LARGE\n");
                            }
                            print("\n");
                        } else if (Math.abs(p) > 180) {
                            print("\n");
                            print("IF YOU WANT TO SPIN AROUND, GO OUTSIDE THE MODULE\n");
                            print("FOR AN E.V.A.\n");
                            print("\n");
                        } else {
                            break;
                        }
                    }
                    if (t1 == 0) {
                        print("\n");
                        print("MISSION ABENDED\n");
                        break;
                    }
                } else {
                    t1 = 20;
                    f = 0;
                    p = 0;
                }
                n = 20;
                if (t1 >= 400)
                    n = t1 / 20;
                t1 = t1 / n;
                p = p * 3.14159 / 180;
                s = Math.sin(p);
                c = Math.cos(p);
                m2 = m0 * t1 * f / b;
                r3 = -0.5 * r0 * Math.pow(v0 / r, 2) + r * a1 * a1;
                a3 = -2 * r1 * a1 / r;
            }
            print("\n");
            while (1) {
                print("DO YOU WANT TO TRY IT AGAIN (YES/NO)?\n");
                zs = await input();
                if (zs == "YES" || zs == "NO")
                    break;
            }
            if (zs != "YES")
                break;
        }
        print("\n");
        print("TOO BAD, THE SPACE PROGRAM HATES TO LOSE EXPERIENCED\n");
        print("ASTRONAUTS.\n");
    }
    
    main();
    
    
    ================================================
    FILE: 59_Lunar_LEM_Rocket/javascript/lunar.html
    ================================================
    
    
    LUNAR
    
    
    
    
    
    
    
    
    
    
    ================================================
    FILE: 59_Lunar_LEM_Rocket/javascript/lunar.js
    ================================================
    // LUNAR
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    var l;
    var t;
    var m;
    var s;
    var k;
    var a;
    var v;
    var i;
    var j;
    var q;
    var g;
    var z;
    var d;
    
    function formula_set_1()
    {
        l = l + s;
        t = t - s;
        m = m - s * k;
        a = i;
        v = j;
    }
    
    function formula_set_2()
    {
        q = s * k / m;
        j = v + g * s + z * (-q - q * q / 2 - Math.pow(q, 3) / 3 - Math.pow(q, 4) / 4 - Math.pow(q, 5) / 5);
        i = a - g * s * s / 2 - v * s + z * s * (q / 2 + Math.pow(q, 2) / 6 + Math.pow(q, 3) / 12 + Math.pow(q, 4) / 20 + Math.pow(q, 5) / 30);
    }
    
    function formula_set_3()
    {
        while (s >= 5e-3) {
            d = v + Math.sqrt(v * v + 2 * a * (g - z * k / m));
            s = 2 * a / d;
            formula_set_2();
            formula_set_1();
        }
    }
    
    // Main program
    async function main()
    {
        print(tab(33) + "LUNAR\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        print("THIS IS A COMPUTER SIMULATION OF AN APOLLO LUNAR\n");
        print("LANDING CAPSULE.\n");
        print("\n");
        print("\n");
        print("THE ON-BOARD COMPUTER HAS FAILED (IT WAS MADE BY\n");
        print("XEROX) SO YOU HAVE TO LAND THE CAPSULE MANUALLY.\n");
        while (1) {
            print("\n");
            print("SET BURN RATE OF RETRO ROCKETS TO ANY VALUE BETWEEN\n");
            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,000 LBS.\n");
            print("\n");
            print("\n");
            print("\n");
            print("GOOD LUCK\n");
            l = 0;
            print("\n");
            print("SEC\tMI + FT\t\tMPH\tLB FUEL\tBURN RATE\n");
            print("\n");
            a = 120;
            v = 1;
            m = 33000;
            n = 16500;
            g = 1e-3;
            z = 1.8;
            while (1) {
                print(l + "\t" + Math.floor(a) + " + " + Math.floor(5280 * (a - Math.floor(a))) + " \t" + Math.floor(3600 * v * 100) / 100 + "\t" + (m - n) + "\t");
                k = parseFloat(await input());
                t = 10;
                should_exit = false;
                while (1) {
                    if (m - n < 1e-3)
                        break;
                    if (t < 1e-3)
                        break;
                    s = t;
                    if (m < n + s * k)
                        s = (m - n) / k;
                    formula_set_2();
                    if (i <= 0) {
                        formula_set_3();
                        should_exit = true;
                        break;
                    }
                    if (v > 0) {
                        if (j < 0) {
                            do {
                                w = (1 - m * g / (z * k)) / 2;
                                s = m * v / (z * k * (w + Math.sqrt(w * w + v / z))) + 0.05;
                                formula_set_2();
                                if (i <= 0) {
                                    formula_set_3();
                                    should_exit = true;
                                    break;
                                }
                                formula_set_1();
                                if (j > 0)
                                    break;
                            } while (v > 0) ;
                            if (should_exit)
                                break;
                            continue;
                        }
                    }
                    formula_set_1();
                }
                if (should_exit)
                    break;
                if (m - n < 1e-3) {
                    print("FUEL OUT AT " + l + " SECOND\n");
                    s = (-v * Math.sqrt(v * v + 2 * a * g)) / g;
                    v = v + g * s;
                    l = l + s;
                    break;
                }
            }
            w = 3600 * v;
            print("ON MOON AT " + l + " SECONDS - IMPACT VELOCITY " + w + " MPH\n");
            if (w <= 1.2) {
                print("PERFECT LANDING!\n");
            } else if (w <= 10) {
                print("GOOD LANDING (COULD BE BETTER)\n");
            } else if (w <= 60) {
                print("CRAFT DAMAGE... YOU'RE STRANDED HERE UNTIL A RESCUE\n");
                print("PARTY ARRIVES. HOPE YOU HAVE ENOUGH OXYGEN!\n");
            } else {
                print("SORRY THERE WERE NO SURVIVORS. YOU BLEW IT!\n");
                print("IN FACT, YOU BLASTED A NEW LUNAR CRATER " + (w * 0.227) + " FEET DEEP!\n");
            }
            print("\n");
            print("\n");
            print("\n");
            print("TRY AGAIN??\n");
        }
    }
    
    main();
    
    
    ================================================
    FILE: 59_Lunar_LEM_Rocket/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 59_Lunar_LEM_Rocket/lem.bas
    ================================================
    2 PRINT TAB(34);"LEM"
    4 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    7 REM ROCKT2 IS AN INTERACTIVE GAME THAT SIMULATES A LUNAR
    8 REM LANDING IS SIMILAR TO THAT OF THE APOLLO PROGRAM.
    9 REM THERE IS ABSOLUTELY NO CHANCE INVOLVED
    10 Z$="GO"
    15 B1=1
    20 M=17.95
    25 F1=5.25
    30 N=7.5
    35 R0=926
    40 V0=1.29
    45 T=0
    50 H0=60
    55 R=R0+H0
    60 A=-3.425
    65 R1=0
    70 A1=8.84361E-04
    75 R3=0
    80 A3=0
    85 M1=7.45
    90 M0=M1
    95 B=750
    100 T1=0
    105 F=0
    110 P=0
    115 N=1
    120 M2=0
    125 S=0
    130 C=0
    135 IF Z$="YES" THEN 1150
    140 PRINT
    145 PRINT "LUNAR LANDING SIMULATION"
    150 PRINT
    155 PRINT "HAVE YOU FLOWN AN APOLLO/LEM MISSION BEFORE";
    160 PRINT " (YES OR NO)";
    165 INPUT Q$
    170 IF Q$="YES" THEN 190
    175 IF Q$="NO" THEN 205
    180 PRINT "JUST ANSWER THE QUESTION, PLEASE, ";
    185 GOTO 160
    190 PRINT
    195 PRINT "INPUT MEASUREMENT OPTION NUMBER";
    200 GOTO 225
    205 PRINT
    210 PRINT "WHICH SYSTEM OF MEASUREMENT DO YOU PREFER?"
    215 PRINT " 1=METRIC     0=ENGLISH"
    220 PRINT "ENTER THE APPROPRIATE NUMBER";
    225 INPUT K
    230 PRINT
    235 IF K=0 THEN 280
    240 IF K=1 THEN 250
    245 GOTO 220
    250 Z=1852.8
    255 M$="METERS"
    260 G3=3.6
    265 N$=" KILOMETERS"
    270 G5=1000
    275 GOTO 305
    280 Z=6080
    285 M$="FEET"
    290 G3=.592
    295 N$="N.MILES"
    300 G5=Z
    305 IF B1=3 THEN 670
    310 IF Q$="YES" THEN 485
    315 PRINT
    320 PRINT "  YOU ARE ON A LUNAR LANDING MISSION.  AS THE PILOT OF"
    325 PRINT "THE LUNAR EXCURSION MODULE, YOU WILL BE EXPECTED TO"
    330 PRINT "GIVE CERTAIN COMMANDS TO THE MODULE NAVIGATION SYSTEM."
    335 PRINT "THE ON-BOARD COMPUTER WILL GIVE A RUNNING ACCOUNT"
    340 PRINT "OF INFORMATION NEEDED TO NAVIGATE THE SHIP."
    345 PRINT
    350 PRINT
    355 PRINT "THE ATTITUDE ANGLE CALLED FOR IS DESCRIBED AS FOLLOWS."
    360 PRINT "+ OR -180 DEGREES IS DIRECTLY AWAY FROM THE MOON"
    365 PRINT "-90 DEGREES IS ON A TANGENT IN THE DIRECTION OF ORBIT"
    370 PRINT "+90 DEGREES IS ON A TANGENT FROM THE DIRECTION OF ORBIT"
    375 PRINT "0 (ZERO) DEGREES IS DIRECTLY TOWARD THE MOON"
    380 PRINT
    385 PRINT TAB(30);"-180|+180"
    390 PRINT TAB(34);"^"
    395 PRINT TAB(27);"-90 < -+- > +90"
    400 PRINT TAB(34);"!"
    405 PRINT TAB(34);"0"
    410 PRINT TAB(21);"<<<< DIRECTION OF ORBIT <<<<"
    415 PRINT
    420 PRINT TAB(20);"------ SURFACE OF MOON ------"
    425 PRINT
    430 PRINT
    435 PRINT "ALL ANGLES BETWEEN -180 AND +180 DEGREES ARE ACCEPTED."
    440 PRINT
    445 PRINT "1 FUEL UNIT = 1 SEC. AT MAX THRUST"
    450 PRINT "ANY DISCREPANCIES ARE ACCOUNTED FOR IN THE USE OF FUEL"
    455 PRINT "FOR AN ATTITUDE CHANGE."
    460 PRINT "AVAILABLE ENGINE POWER: 0 (ZERO) AND ANY VALUE BETWEEN"
    465 PRINT "10 AND 100 PERCENT."
    470 PRINT
    475 PRINT"NEGATIVE THRUST OR TIME IS PROHIBITED."
    480 PRINT
    485 PRINT
    490 PRINT "INPUT: TIME INTERVAL IN SECONDS ------ (T)"
    495 PRINT "       PERCENTAGE OF THRUST ---------- (P)"
    500 PRINT "       ATTITUDE ANGLE IN DEGREES ----- (A)"
    505 PRINT
    510 IF Q$="YES" THEN 535
    515 PRINT "FOR EXAMPLE:"
    520 PRINT "T,P,A? 10,65,-60"
    525 PRINT "TO ABORT THE MISSION AT ANY TIME, ENTER 0,0,0"
    530 PRINT
    535 PRINT "OUTPUT: TOTAL TIME IN ELAPSED SECONDS"
    540 PRINT "        HEIGHT IN ";M$
    545 PRINT "        DISTANCE FROM LANDING SITE IN ";M$
    550 PRINT "        VERTICAL VELOCITY IN ";M$;"/SECOND"
    555 PRINT "        HORIZONTAL VELOCITY IN ";M$;"/SECOND"
    560 PRINT "        FUEL UNITS REMAINING"
    565 PRINT
    570 GOTO 670
    575 PRINT
    580 PRINT "T,P,A";
    585 INPUT T1,F,P
    590 F=F/100
    595 IF T1<0 THEN 905
    600 IF T1=0 THEN 1090
    605 IF ABS(F-.05)>1 THEN 945
    610 IF ABS(F-.05)<.05 THEN 945
    615 IF ABS(P)>180 THEN 925
    620 N=20
    625 IF T1<400 THEN 635
    630 N=T1/20
    635 T1=T1/N
    640 P=P*3.14159/180
    645 S=SIN(P)
    650 C=COS(P)
    655 M2=M0*T1*F/B
    660 R3=-.5*R0*((V0/R)^2)+R*A1*A1
    665 A3=-2*R1*A1/R
    670 FOR I=1 TO N
    675 IF M1=0 THEN 715
    680 M1=M1-M2
    685 IF M1>0 THEN 725
    690 F=F*(1+M1/M2)
    695 M2=M1+M2
    700 PRINT "YOU ARE OUT OF FUEL."
    705 M1=0
    710 GOTO 725
    715 F=0
    720 M2=0
    725 M=M-.5*M2
    730 R4=R3
    735 R3=-.5*R0*((V0/R)^2)+R*A1*A1
    740 R2=(3*R3-R4)/2+.00526*F1*F*C/M
    745 A4=A3
    750 A3=-2*R1*A1/R
    755 A2=(3*A3-A4)/2+.0056*F1*F*S/(M*R)
    760 X=R1*T1+.5*R2*T1*T1
    765 R=R+X
    770 H0=H0+X
    775 R1=R1+R2*T1
    780 A=A+A1*T1+.5*A2*T1*T1
    785 A1=A1+A2*T1
    790 M=M-.5*M2
    795 T=T+T1
    800 IF H0<3.287828E-04 THEN 810
    805 NEXT I
    810 H=H0*Z
    815 H1=R1*Z
    820 D=R0*A*Z
    825 D1=R*A1*Z
    830 T2=M1*B/M0
    835 PRINT " ";T;TAB(10);H;TAB(23);D;
    840 PRINT TAB(37);H1;TAB(49);D1;TAB(60);T2
    845 IF H0<3.287828E-04 THEN 880
    850 IF R0*A>164.474 THEN 1050
    855 IF M1>0 THEN 580
    860 T1=20
    865 F=0
    870 P=0
    875 GOTO 620
    880 IF R1<-8.21957E-04 THEN 1020
    885 IF ABS(R*A1)>4.93174E-04 THEN 1020
    890 IF H0<-3.287828E-04 THEN 1020
    895 IF ABS(D)>10*Z THEN 1065
    900 GOTO 995
    905 PRINT
    910 PRINT "THIS SPACECRAFT IS NOT ABLE TO VIOLATE THE SPACE-";
    915 PRINT "TIME CONTINUUM."
    920 GOTO 575
    925 PRINT
    930 PRINT "IF YOU WANT TO SPIN AROUND, GO OUTSIDE THE MODULE"
    935 PRINT "FOR AN E.V.A."
    940 GOTO 575
    945 PRINT
    950 PRINT "IMPOSSIBLE THRUST VALUE ";
    955 IF F<0 THEN 985
    960 IF F-.05<.05 THEN 975
    965 PRINT "TOO LARGE"
    970 GOTO 575
    975 PRINT "TOO SMALL"
    980 GOTO 575
    985 PRINT "NEGATIVE"
    990 GOTO 575
    995 PRINT
    1000 PRINT "TRANQUILITY BASE HERE -- THE EAGLE HAS LANDED."
    1005 PRINT "CONGRATULATIONS -- THERE WAS NO SPACECRAFT DAMAGE."
    1010 PRINT "YOU MAY NOW PROCEED WITH SURFACE EXPLORATION."
    1015 GOTO 1100
    1020 PRINT
    1025 PRINT "CRASH !!!!!!!!!!!!!!!!"
    1030 PRINT "YOUR IMPACT CREATED A CRATER";ABS(H);M$;" DEEP."
    1035 X1=SQR(D1*D1+H1*H1)*G3
    1040 PRINT "AT CONTACT YOU WERE TRAVELING";X1;N$;"/HR"
    1045 GOTO 1100
    1050 PRINT
    1055 PRINT "YOU HAVE BEEN LOST IN SPACE WITH NO HOPE OF RECOVERY."
    1060 GOTO 1100
    1065 PRINT "YOU ARE DOWN SAFELY - "
    1075 PRINT
    1080 PRINT "BUT MISSED THE LANDING SITE BY";ABS(D/G5);N$;"."
    1085 GOTO 1100
    1090 PRINT
    1095 PRINT "MISSION ABENDED"
    1100 PRINT
    1105 PRINT "DO YOU WANT TO TRY IT AGAIN (YES/NO)?"
    1110 INPUT Z$
    1115 IF Z$="YES" THEN 20
    1120 IF Z$="NO" THEN 1130
    1125 GOTO 1105
    1130 PRINT
    1135 PRINT "TOO BAD, THE SPACE PROGRAM HATES TO LOSE EXPERIENCED"
    1140 PRINT "ASTRONAUTS."
    1145 STOP
    1150 PRINT
    1155 PRINT "OK, DO YOU WANT THE COMPLETE INSTRUCTIONS OR THE INPUT -"
    1160 PRINT "OUTPUT STATEMENTS?"
    1165 PRINT "1=COMPLETE INSTRUCTIONS"
    1170 PRINT "2=INPUT-OUTPUT STATEMENTS"
    1175 PRINT "3=NEITHER"
    1180 INPUT B1
    1185 Q$="NO"
    1190 IF B1=1 THEN 205
    1195 Q$="YES"
    1200 IF B1=2 THEN 190
    1205 IF B1=3 THEN 190
    1210 GOTO 1165
    1215 END
    
    
    ================================================
    FILE: 59_Lunar_LEM_Rocket/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 59_Lunar_LEM_Rocket/lunar.bas
    ================================================
    10 PRINT TAB(33);"LUNAR"
    20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"
    25 PRINT:PRINT:PRINT
    30 PRINT "THIS IS A COMPUTER SIMULATION OF AN APOLLO LUNAR"
    40 PRINT "LANDING CAPSULE.": PRINT: PRINT
    50 PRINT "THE ON-BOARD COMPUTER HAS FAILED (IT WAS MADE BY"
    60 PRINT "XEROX) SO YOU HAVE TO LAND THE CAPSULE MANUALLY."
    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,000 LBS."
    110 PRINT: PRINT: PRINT: PRINT "GOOD LUCK"
    120 L=0
    130 PRINT: PRINT "SEC","MI + FT","MPH","LB FUEL","BURN RATE":PRINT
    140 A=120:V=1:M=33000:N=16500:G=1E-03:Z=1.8
    150 PRINT L,INT(A);INT(5280*(A-INT(A))),3600*V,M-N,:INPUT K:T=10
    160 IF M-N<1E-03 THEN 240
    170 IF T<1E-03 THEN 150
    180 S=T: IF M>=N+S*K THEN 200
    190 S=(M-N)/K
    200 GOSUB 420: IF I<=0 THEN 340
    210 IF V<=0 THEN 230
    220 IF J<0 THEN 370
    230 GOSUB 330: GOTO 160
    240 PRINT "FUEL OUT AT";L;"SECONDS":S=(-V+SQR(V*V+2*A*G))/G
    250 V=V+G*S: L=L+S
    260 W=3600*V: PRINT "ON MOON AT";L;"SECONDS - IMPACT VELOCITY";W;"MPH"
    274 IF W<=1.2 THEN PRINT "PERFECT LANDING!": GOTO 440
    280 IF W<=10 THEN PRINT "GOOD LANDING (COULD BE BETTER)":GOTO 440
    282 IF W>60 THEN 300
    284 PRINT "CRAFT DAMAGE... YOU'RE STRANDED HERE UNTIL A RESCUE"
    286 PRINT "PARTY ARRIVES. HOPE YOU HAVE ENOUGH OXYGEN!"
    288 GOTO 440
    300 PRINT "SORRY THERE WERE NO SURVIVORS. YOU BLEW IT!"
    310 PRINT "IN FACT, YOU BLASTED A NEW LUNAR CRATER";W*.227;"FEET DEEP!"
    320 GOTO 440
    330 L=L+S: T=T-S: M=M-S*K: A=I: V=J: RETURN
    340 IF S<5E-03 THEN 260
    350 D=V+SQR(V*V+2*A*(G-Z*K/M)):S=2*A/D
    360 GOSUB 420: GOSUB 330: GOTO 340
    370 W=(1-M*G/(Z*K))/2: S=M*V/(Z*K*(W+SQR(W*W+V/Z)))+.05:GOSUB 420
    380 IF I<=0 THEN 340
    390 GOSUB 330: IF J>0 THEN 160
    400 IF V>0 THEN 370
    410 GOTO 160
    420 Q=S*K/M: J=V+G*S+Z*(-Q-Q*Q/2-Q^3/3-Q^4/4-Q^5/5)
    430 I=A-G*S*S/2-V*S+Z*S*(Q/2+Q^2/6+Q^3/12+Q^4/20+Q^5/30):RETURN
    440 PRINT:PRINT:PRINT:PRINT "TRY AGAIN??": GOTO 70
    
    
    ================================================
    FILE: 59_Lunar_LEM_Rocket/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 59_Lunar_LEM_Rocket/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 59_Lunar_LEM_Rocket/python/lunar.py
    ================================================
    """
    LUNAR
    
    Lunar landing simulation
    
    Ported by Dave LeCompte
    """
    
    import math
    from dataclasses import dataclass
    from typing import Any, NamedTuple
    
    PAGE_WIDTH = 64
    
    COLUMN_WIDTH = 2
    SECONDS_WIDTH = 4
    MPH_WIDTH = 6
    ALT_MI_WIDTH = 6
    ALT_FT_WIDTH = 4
    MPH_WIDTH = 6
    FUEL_WIDTH = 8
    BURN_WIDTH = 10
    
    SECONDS_LEFT = 0
    SECONDS_RIGHT = SECONDS_LEFT + SECONDS_WIDTH
    ALT_LEFT = SECONDS_RIGHT + COLUMN_WIDTH
    ALT_MI_RIGHT = ALT_LEFT + ALT_MI_WIDTH
    ALT_FT_RIGHT = ALT_MI_RIGHT + COLUMN_WIDTH + ALT_FT_WIDTH
    MPH_LEFT = ALT_FT_RIGHT + COLUMN_WIDTH
    MPH_RIGHT = MPH_LEFT + MPH_WIDTH
    FUEL_LEFT = MPH_RIGHT + COLUMN_WIDTH
    FUEL_RIGHT = FUEL_LEFT + FUEL_WIDTH
    BURN_LEFT = FUEL_RIGHT + COLUMN_WIDTH
    BURN_RIGHT = BURN_LEFT + BURN_WIDTH
    
    
    class PhysicalState(NamedTuple):
        velocity: float
        altitude: float
    
    
    def print_centered(msg: str) -> None:
        spaces = " " * ((PAGE_WIDTH - len(msg)) // 2)
        print(spaces + msg)
    
    
    def print_header(title: str) -> None:
        print_centered(title)
        print_centered("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n\n\n")
    
    
    def add_rjust(line: str, s: Any, pos: int) -> str:
        """Add a new field to a line right justified to end at pos"""
        s_str = str(s)
        slen = len(s_str)
        if len(line) + slen > pos:
            new_len = pos - slen
            line = line[:new_len]
        if len(line) + slen < pos:
            spaces = " " * (pos - slen - len(line))
            line = line + spaces
        return line + s_str
    
    
    def add_ljust(line: str, s: str, pos: int) -> str:
        """Add a new field to a line left justified starting at pos"""
        s = s
        if len(line) > pos:
            line = line[:pos]
        if len(line) < pos:
            spaces = " " * (pos - len(line))
            line = line + spaces
        return line + s
    
    
    def print_instructions() -> None:
        """Somebody had a bad experience with Xerox."""
        print("THIS IS A COMPUTER SIMULATION OF AN APOLLO LUNAR")
        print("LANDING CAPSULE.\n\n")
        print("THE ON-BOARD COMPUTER HAS FAILED (IT WAS MADE BY")
        print("XEROX) SO YOU HAVE TO LAND THE CAPSULE MANUALLY.\n")
    
    
    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,000 LBS.\n\n\n")
        print("GOOD LUCK\n")
    
    
    def format_line_for_report(
        t: Any,
        miles: Any,
        feet: Any,
        velocity: Any,
        fuel: Any,
        burn_rate: str,
        is_header: bool,
    ) -> str:
        line = add_rjust("", t, SECONDS_RIGHT)
        line = add_rjust(line, miles, ALT_MI_RIGHT)
        line = add_rjust(line, feet, ALT_FT_RIGHT)
        line = add_rjust(line, velocity, MPH_RIGHT)
        line = add_rjust(line, fuel, FUEL_RIGHT)
        if is_header:
            line = add_rjust(line, burn_rate, BURN_RIGHT)
        else:
            line = add_ljust(line, burn_rate, BURN_LEFT)
        return line
    
    
    class SimulationClock:
        def __init__(self, elapsed_time: float, time_until_next_prompt: float) -> None:
            self.elapsed_time = elapsed_time
            self.time_until_next_prompt = time_until_next_prompt
    
        def time_for_prompt(self) -> bool:
            return self.time_until_next_prompt < 1e-3
    
        def advance(self, delta_t: float) -> None:
            self.elapsed_time += delta_t
            self.time_until_next_prompt -= delta_t
    
    
    @dataclass
    class Capsule:
        altitude: float = 120  # in miles above the surface
        velocity: float = 1  # downward
        m: float = 33000  # mass_with_fuel
        n: float = 16500  # mass_without_fuel
        g: float = 1e-3
        z: float = 1.8
        fuel_per_second: float = 0
    
        def remaining_fuel(self) -> float:
            return self.m - self.n
    
        def is_out_of_fuel(self) -> bool:
            return self.remaining_fuel() < 1e-3
    
        def update_state(
            self, sim_clock: SimulationClock, delta_t: float, new_state: PhysicalState
        ) -> None:
            sim_clock.advance(delta_t)
            self.m = self.m - delta_t * self.fuel_per_second
            self.altitude = new_state.altitude
            self.velocity = new_state.velocity
    
        def fuel_time_remaining(self) -> float:
            # extrapolates out how many seconds we have at the current fuel burn rate
            assert self.fuel_per_second > 0
            return self.remaining_fuel() / self.fuel_per_second
    
        def predict_motion(self, delta_t: float) -> PhysicalState:
            # Perform an Euler's Method numerical integration of the equations of motion.
    
            q = delta_t * self.fuel_per_second / self.m
    
            # new velocity
            new_velocity = (
                self.velocity
                + self.g * delta_t
                + self.z * (-q - q**2 / 2 - q**3 / 3 - q**4 / 4 - q**5 / 5)
            )
    
            # new altitude
            new_altitude = (
                self.altitude
                - self.g * delta_t**2 / 2
                - self.velocity * delta_t
                + self.z
                * delta_t
                * (q / 2 + q**2 / 6 + q**3 / 12 + q**4 / 20 + q**5 / 30)
            )
    
            return PhysicalState(altitude=new_altitude, velocity=new_velocity)
    
        def make_state_display_string(self, sim_clock: SimulationClock) -> str:
            seconds = sim_clock.elapsed_time
            miles = int(self.altitude)
            feet = int(5280 * (self.altitude - miles))
            velocity = int(3600 * self.velocity)
            fuel = int(self.remaining_fuel())
            burn_rate = " ? "
    
            return format_line_for_report(
                seconds, miles, feet, velocity, fuel, burn_rate, False
            )
    
        def prompt_for_burn(self, sim_clock: SimulationClock) -> None:
            msg = self.make_state_display_string(sim_clock)
    
            self.fuel_per_second = float(input(msg))
            sim_clock.time_until_next_prompt = 10
    
    
    def show_landing(sim_clock: SimulationClock, capsule: Capsule) -> None:
        w = 3600 * capsule.velocity
        print(
            f"ON MOON AT {sim_clock.elapsed_time:.2f} SECONDS - IMPACT VELOCITY {w:.2f} MPH"
        )
        if w < 1.2:
            print("PERFECT LANDING!")
        elif w < 10:
            print("GOOD LANDING (COULD BE BETTER)")
        elif w <= 60:
            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(f"IN FACT, YOU BLASTED A NEW LUNAR CRATER {w*.227:.2f} FEET DEEP!")
        end_sim()
    
    
    def show_out_of_fuel(sim_clock: SimulationClock, capsule: Capsule) -> None:
        print(f"FUEL OUT AT {sim_clock.elapsed_time} SECONDS")
        delta_t = (
            -capsule.velocity
            + math.sqrt(capsule.velocity**2 + 2 * capsule.altitude * capsule.g)
        ) / capsule.g
        capsule.velocity += capsule.g * delta_t
        sim_clock.advance(delta_t)
        show_landing(sim_clock, capsule)
    
    
    def process_final_tick(
        delta_t: float, sim_clock: SimulationClock, capsule: Capsule
    ) -> None:
        # When we extrapolated our position based on our velocity
        # and delta_t, we overshot the surface. For better
        # accuracy, we will back up and do shorter time advances.
    
        while True:
            if delta_t < 5e-3:
                show_landing(sim_clock, capsule)
                return
            # line 35
            average_vel = (
                capsule.velocity
                + math.sqrt(
                    capsule.velocity**2
                    + 2
                    * capsule.altitude
                    * (capsule.g - capsule.z * capsule.fuel_per_second / capsule.m)
                )
            ) / 2
            delta_t = capsule.altitude / average_vel
            new_state = capsule.predict_motion(delta_t)
            capsule.update_state(sim_clock, delta_t, new_state)
    
    
    def handle_flyaway(sim_clock: SimulationClock, capsule: Capsule) -> bool:
        """
        The user has started flying away from the moon. Since this is a
        lunar LANDING simulation, we wait until the capsule's velocity is
        positive (downward) before prompting for more input.
    
        Returns True if landed, False if simulation should continue.
        """
    
        while True:
            w = (1 - capsule.m * capsule.g / (capsule.z * capsule.fuel_per_second)) / 2
            delta_t = (
                capsule.m
                * capsule.velocity
                / (
                    capsule.z
                    * capsule.fuel_per_second
                    * math.sqrt(w**2 + capsule.velocity / capsule.z)
                )
            ) + 0.05
    
            new_state = capsule.predict_motion(delta_t)
    
            if new_state.altitude <= 0:
                # have landed
                return True
    
            capsule.update_state(sim_clock, delta_t, new_state)
    
            if (new_state.velocity > 0) or (capsule.velocity <= 0):
                # return to normal sim
                return False
    
    
    def end_sim() -> None:
        print("\n\n\nTRY AGAIN??\n\n\n")
    
    
    def run_simulation() -> None:
        print()
        print(
            format_line_for_report("SEC", "MI", "FT", "MPH", "LB FUEL", "BURN RATE", True)
        )
    
        sim_clock = SimulationClock(0, 10)
        capsule = Capsule()
    
        capsule.prompt_for_burn(sim_clock)
    
        while True:
            if capsule.is_out_of_fuel():
                show_out_of_fuel(sim_clock, capsule)
                return
    
            if sim_clock.time_for_prompt():
                capsule.prompt_for_burn(sim_clock)
                continue
    
            # clock advance is the shorter of the time to the next prompt,
            # or when we run out of fuel.
            if capsule.fuel_per_second > 0:
                delta_t = min(
                    sim_clock.time_until_next_prompt, capsule.fuel_time_remaining()
                )
            else:
                delta_t = sim_clock.time_until_next_prompt
    
            new_state = capsule.predict_motion(delta_t)
    
            if new_state.altitude <= 0:
                process_final_tick(delta_t, sim_clock, capsule)
                return
    
            if capsule.velocity > 0 and new_state.velocity < 0:
                if handle_flyaway(sim_clock, capsule):
                    process_final_tick(delta_t, sim_clock, capsule)
                    return
    
            else:
                capsule.update_state(sim_clock, delta_t, new_state)
    
    
    def main() -> None:
        print_header("LUNAR")
        print_instructions()
        while True:
            print_intro()
            run_simulation()
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 59_Lunar_LEM_Rocket/rocket.bas
    ================================================
    10 PRINT TAB(30); "ROCKET"
    20 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    30 PRINT:PRINT:PRINT
    70 PRINT "LUNAR LANDING SIMULATION"
    80 PRINT "----- ------- ----------": PRINT
    100 INPUT "DO YOU WANT INSTRUCTIONS (YES OR NO)";A$
    110 IF A$="NO" THEN 390
    160 PRINT
    200 PRINT"YOU ARE LANDING ON THE MOON AND AND HAVE TAKEN OVER MANUAL"
    210 PRINT"CONTROL 1000 FEET ABOVE A GOOD LANDING SPOT. YOU HAVE A DOWN-"
    220 PRINT"WARD VELOCITY OF 50 FEET/SEC. 150 UNITS OF FUEL REMAIN."
    225 PRINT
    230 PRINT"HERE ARE THE RULES THAT GOVERN YOUR APOLLO SPACE-CRAFT:": PRINT
    240 PRINT"(1) AFTER EACH SECOND THE HEIGHT, VELOCITY, AND REMAINING FUEL"
    250 PRINT"    WILL BE REPORTED VIA DIGBY YOUR ON-BOARD COMPUTER."
    260 PRINT"(2) AFTER THE REPORT A '?' WILL APPEAR. ENTER THE NUMBER"
    270 PRINT"    OF UNITS OF FUEL YOU WISH TO BURN DURING THE NEXT"
    280 PRINT"    SECOND. EACH UNIT OF FUEL WILL SLOW YOUR DESCENT BY"
    290 PRINT"    1 FOOT/SEC."
    310 PRINT"(3) THE MAXIMUM THRUST OF YOUR ENGINE IS 30 FEET/SEC/SEC"
    320 PRINT"    OR 30 UNITS OF FUEL PER SECOND."
    330 PRINT"(4) WHEN YOU CONTACT THE LUNAR SURFACE. YOUR DESCENT ENGINE"
    340 PRINT"    WILL AUTOMATICALLY SHUT DOWN AND YOU WILL BE GIVEN A"
    350 PRINT"    REPORT OF YOUR LANDING SPEED AND REMAINING FUEL."
    360 PRINT"(5) IF YOU RUN OUT OF FUEL THE '?' WILL NO LONGER APPEAR"
    370 PRINT"    BUT YOUR SECOND BY SECOND REPORT WILL CONTINUE UNTIL"
    380 PRINT"    YOU CONTACT THE LUNAR SURFACE.":PRINT
    390 PRINT"BEGINNING LANDING PROCEDURE..........":PRINT
    400 PRINT"G O O D  L U C K ! ! !"
    420 PRINT:PRINT
    430 PRINT"SEC  FEET      SPEED     FUEL     PLOT OF DISTANCE"
    450 PRINT
    455 T=0:H=1000:V=50:F=150
    490 PRINT T;TAB(6);H;TAB(16);V;TAB(26);F;TAB(35);"I";TAB(H/15);"*"
    500 INPUT B
    510 IF B<0 THEN 650
    520 IF B>30 THEN B=30
    530 IF B>F THEN B=F
    540 V1=V-B+5
    560 F=F-B
    570 H=H- .5*(V+V1)
    580 IF H<=0 THEN 670
    590 T=T+1
    600 V=V1
    610 IF F>0 THEN 490
    615 IF B=0 THEN 640
    620 PRINT"**** OUT OF FUEL ****"
    640 PRINT T;TAB(4);H;TAB(12);V;TAB(20);F;TAB(29);"I";TAB(H/12+29);"*"
    650 B=0
    660 GOTO 540
    670 PRINT"***** CONTACT *****"
    680 H=H+ .5*(V1+V)
    690 IF B=5 THEN 720
    700 D=(-V+SQR(V*V+H*(10-2*B)))/(5-B)
    710 GOTO 730
    720 D=H/V
    730 V1=V+(5-B)*D
    760 PRINT"TOUCHDOWN AT";T+D;"SECONDS."
    770 PRINT"LANDING VELOCITY=";V1;"FEET/SEC."
    780 PRINT F;"UNITS OF FUEL REMAINING."
    790 IF V1<>0 THEN 810
    800 PRINT"CONGRATULATIONS! A PERFECT LANDING!!"
    805 PRINT"YOUR LICENSE WILL BE RENEWED.......LATER."
    810 IF ABS(V1)<2 THEN 840
    820 PRINT"***** SORRY, BUT YOU BLEW IT!!!!"
    830 PRINT"APPROPRIATE CONDOLENCES WILL BE SENT TO YOUR NEXT OF KIN."
    840 PRINT:PRINT:PRINT
    850 INPUT "ANOTHER MISSION";A$
    860 IF A$="YES" THEN 390
    870 PRINT: PRINT "CONTROL OUT.": PRINT
    999 END
    
    
    ================================================
    FILE: 59_Lunar_LEM_Rocket/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 59_Lunar_LEM_Rocket/rust/README.md
    ================================================
    README.md
    
    Original source downloaded from Vintage Basic
    
    This folder for chapter #59 contains three different games.  Three folders here contain the three games:
    
     - Rocket
     - LEM
     - lunar
    
    Conversion to [Rust](https://www.rust-lang.org)
    
    
    ================================================
    FILE: 59_Lunar_LEM_Rocket/rust/rocket/Cargo.toml
    ================================================
    [package]
    name = "rocket"
    version = "1.0.0"
    edition = "2021"
    
    # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
    
    [dependencies]
    text_io = "0.1.10"
    num-integer = "0.1"
    
    
    ================================================
    FILE: 59_Lunar_LEM_Rocket/rust/rocket/src/main.rs
    ================================================
    //Goal of this port is to keep Basic lang idioms where possible. Gotta have those single letter capital variables!
    use num_integer::{sqrt};
    use std::io::{self, Write};
    use text_io::{read, try_read};
    #[allow(non_snake_case)]
    fn main() {
        println!("{:>30}", "ROCKET");
        println!("{:>15}", "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
        println!();println!();println!();
        println!("LUNAR LANDING SIMULATION");
        println!("----- ------- ----------");println!();
        let A= input("DO YOU WANT INSTRUCTIONS (YES OR NO) ");
        if !(A=="NO") {
            println!();
            println!("YOU ARE LANDING ON THE MOON AND AND HAVE TAKEN OVER MANUAL");
            println!("CONTROL 1000 FEET ABOVE A GOOD LANDING SPOT. YOU HAVE A DOWN-");
            println!("WARD VELOCITY OF 50 FEET/SEC. 150 UNITS OF FUEL REMAIN.");
            println!();
            println!("HERE ARE THE RULES THAT GOVERN YOUR APOLLO SPACE-CRAFT:"); println!();
            println!("(1) AFTER EACH SECOND THE HEIGHT, VELOCITY, AND REMAINING FUEL");
            println!("    WILL BE REPORTED VIA DIGBY YOUR ON-BOARD COMPUTER.");
            println!("(2) AFTER THE REPORT A '?' WILL APPEAR. ENTER THE NUMBER");
            println!("    OF UNITS OF FUEL YOU WISH TO BURN DURING THE NEXT");
            println!("    SECOND. EACH UNIT OF FUEL WILL SLOW YOUR DESCENT BY");
            println!("    1 FOOT/SEC.");
            println!("(3) THE MAXIMUM THRUST OF YOUR ENGINE IS 30 FEET/SEC/SEC");
            println!("    OR 30 UNITS OF FUEL PER SECOND.");
            println!("(4) WHEN YOU CONTACT THE LUNAR SURFACE. YOUR DESCENT ENGINE");
            println!("    WILL AUTOMATICALLY SHUT DOWN AND YOU WILL BE GIVEN A");
            println!("    REPORT OF YOUR LANDING SPEED AND REMAINING FUEL.");
            println!("(5) IF YOU RUN OUT OF FUEL THE '?' WILL NO LONGER APPEAR");
            println!("    BUT YOUR SECOND BY SECOND REPORT WILL CONTINUE UNTIL");
            println!("    YOU CONTACT THE LUNAR SURFACE.");println!();
        }
        loop {
            println!("BEGINNING LANDING PROCEDURE..........");println!();
            println!("G O O D  L U C K ! ! !");
            println!();println!();
            println!("SEC  FEET      SPEED     FUEL     PLOT OF DISTANCE");
            println!();
            let mut T=0;let mut H:i32=1000;let mut V=50;let mut F=150;
            let D:i32; let mut V1:i32; let mut B:i32;
            'falling: loop {
                println!(" {:<4}{:<11}{:<10}{:<8}I{capsule:>high$}", T,H,V,F,high=(H/15) as usize,capsule="*");
                B = input_int("");
                if B<0 { B=0 }
                else { if B>30 { B=30 } }
                if B>F { B=F }
                'nofuel: loop {
                    V1=V-B+5;
                    F=F-B;
                    H=H- (V+V1)/2;
                    if  H<=0 { break 'falling}
                    T=T+1;
                    V=V1;
                    if F>0 { break 'nofuel }
                    if B!=0 {
                        println!("**** OUT OF FUEL ****");
                    }
                    println!(" {:<4}{:<11}{:<10}{:<8}I{capsule:>high$}", T,H,V,F,high=(H/12+29) as usize,capsule="*");
                    B=0;
                }
            }
            H=H+ (V1+V)/2;
            if B==5 {
                D=H/V;
            } else {
                D=(-V+sqrt(V*V+H*(10-2*B)))/(5-B);
                V1=V+(5-B)*D;
            }
            println!("***** CONTACT *****");
            println!("TOUCHDOWN AT {} SECONDS.",T+D);
            println!("LANDING VELOCITY={} FEET/SEC.",V1);
            println!("{} UNITS OF FUEL REMAINING.", F);
            if V1==0 {
                println!("CONGRATULATIONS! A PERFECT LANDING!!");
                println!("YOUR LICENSE WILL BE RENEWED.......LATER.");
            }
            if V1.abs()>=2 {
                println!("***** SORRY, BUT YOU BLEW IT!!!!");
                println!("APPROPRIATE CONDOLENCES WILL BE SENT TO YOUR NEXT OF KIN.");
            }
            println!();println!();println!();
            let A = input("ANOTHER MISSION");
            if !(A=="YES") { break };
        }
        println!();println!( "CONTROL OUT.");println!();
    }
    
    
    fn input(prompt:&str) -> String {
        loop {
            print!("{} ? ",prompt);io::stdout().flush().unwrap();
            let innn:String=read!("{}\n");
            let out:String = innn.trim().to_string();
            if out!="" {return out}
        }
    }
    fn input_int(prompt:&str) -> i32 {
        loop {
            print!("{} ? ",prompt);io::stdout().flush().unwrap();
            match try_read!() {
                Ok(n) => return n,
                Err(_) => println!("Enter a number 0-30"),
            }
        }
    }
    
    
    ================================================
    FILE: 59_Lunar_LEM_Rocket/vbnet/LunarLEMRocket.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "LunarLEMRocket", "LunarLEMRocket.vbproj", "{6145C5DF-CFFB-42B8-9025-913D9D4A8B10}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{6145C5DF-CFFB-42B8-9025-913D9D4A8B10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{6145C5DF-CFFB-42B8-9025-913D9D4A8B10}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{6145C5DF-CFFB-42B8-9025-913D9D4A8B10}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{6145C5DF-CFFB-42B8-9025-913D9D4A8B10}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 59_Lunar_LEM_Rocket/vbnet/LunarLEMRocket.vbproj
    ================================================
    
      
        Exe
        LunarLEMRocket
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 59_Lunar_LEM_Rocket/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 60_Mastermind/README.md
    ================================================
    ### MasterMind
    
    In that Match-April 1976 issue of _Creative_ we published a computerized version of Master Mind, a logic game. Master Mind is played by two people—one is called the code-maker; the other, the code-breaker. At the beginning of the game the code-maker forms a code, or combination of colored pegs. He hides these from the code-breaker. The code-breaker then attempts to deduce the code, by placing his own guesses, one at a time, on the board. After he makes a guess (by placing a combination of colored pegs on the board) the code-maker then gives the code-breaker clues to indicate how close the guess was to the code. For every peg in the guess that’s the right color but not in the right position, the code-breaker gets a white peg. Note that these black and white pegs do not indicate _which_ pegs in the guess are correct, but merely that they exist. For example, if the code was:
    ```
    Yellow Red Red Green
    ```
    
    and my guess was
    ```
    Red Red Yellow Black
    ```
    I would receive two white pegs and one black peg for the guess. I wouldn’t know (except by comparing previous guesses) which one of the pegs in my guess was the right color in the right position.
    
    Many people have written computer programs to play Master Mind in the passive role, i.e., the computer is the code maker and the human is the code-breaker. This is relatively trivial; the challenge is writing a program that can also play actively as a code-breaker.
    
    Actually, the task of getting the computer to deduce the correct combination is not at all difficult. Imagine, for instance, that you made a list of all possible codes. To begin, you select a guess from your list at random. Then, as you receive clues, you cross off from the list those combinations which you know are impossible. For example if your guess is Red Red Green Green and you receive no pegs, then you know that any combination containing either a red or a green peg is impossible and may be crossed of the list. The process is continued until the correct solution is reached or there are no more combinations left on the list (in which case you know that the code-maker made a mistake in giving you the clues somewhere).
    
    Note that in this particular implementation, we never actually create a list of the combinations, but merely keep track of which ones (in sequential order) may be correct. Using this system, we can easily say that the 523rd combination may be correct, but to actually produce the 523rd combination we have to count all the way from the first combination (or the previous one, if it was lower than 523). Actually, this problem could be simplified to a conversion from base 10 to base (number of colors) and then adjusting the values used in the MID$ function so as not to take a zeroth character from a string if you want to experiment. We did try a version that kept an actual list of all possible combinations (as a string array), which was significantly faster than this version, but which ate tremendous amounts of memory.
    
    At the beginning of this game, you input the number of colors and number of positions you wish to use (which will directly affect the number of combinations) and the number of rounds you wish to play. While you are playing as the code-breaker, you may type BOARD at any time to get a list of your previous guesses and clues, and QUIT to end the game. Note that this version uses string arrays, but this is merely for convenience and can easily be converted for a BASIC that has no string arrays as long as it has a MID$ function. This is because the string arrays are one-dimensional, never exceed a length greater than the number of positions and the elements never contain more than one character.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=110)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=125)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    ### How the computer deduces your guess.
    
    The computer takes the number of black pegs and white pegs that the user reports
    and uses that information as a target. It then assumes its guess is the answer
    and proceeds to compare the black and white pegs against all remaining possible
    answers. For each set of black and white pegs it gets in these comparisons, if 
    they don't match what the user reported, then they can not be part of the solution.
    This can be a non-intuitive assumption, so we'll walk through it with a three color,
    three position example (27 possible solutions.)
    
    Let's just suppose our secret code we're hiding from the computer is `BWB`
    
    First let's point out the commutative property of comparing two codes for their
    black and white pegs. A black peg meaning correct color and correct position, and
    a white peg meaning correct color and wrong position.  If the computer guesses
    `RBW` then the black/white peg report is 0 black, 2 white.  But if `RBW` is the 
    secret code and the computer guesses `BWB` the reporting for `BWB` is going to be
    the same, 0 black, 2 white. 
    
    Now lets look at a table with the reporting for every possible guess the computer 
    can make while our secret code is `BWB`.
                                                             
    | Guess | Black | White |     | Guess | Black | White |     | Guess | Black | White |
    |-------|-------|-------|-----|-------|-------|-------|-----|-------|-------|-------|
    | BBB   | 2     | 0     |     | WBB   | 1     | 2     |     | RBB   | 1     | 1     |   
    | BBW   | 1     | 2     |     | WBW   | 0     | 2     |     | RBW   | 0     | 2     |   
    | BBR   | 1     | 1     |     | WBR   | 0     | 2     |     | RBR   | 0     | 1     |    
    | BWB   | 3     | 0     |     | WWB   | 2     | 0     |     | RWB   | 2     | 0     |    
    | BWW   | 2     | 0     |     | WWW   | 1     | 0     |     | RWW   | 1     | 0     |    
    | BWR   | 2     | 0     |     | WWR   | 1     | 0     |     | RWR   | 1     | 0     |    
    | BRB   | 2     | 0     |     | WRB   | 1     | 1     |     | RRB   | 1     | 0     |    
    | BRW   | 1     | 1     |     | WRW   | 0     | 1     |     | RRW   | 0     | 1     |    
    | BRR   | 1     | 0     |     | WRR   | 0     | 1     |     | RRR   | 0     | 0     | 
    
    The computer has guessed `RBW` and the report on it is 0 black, 2 white. The code
    used to eliminate other solutions looks like this:
    
    `1060 IF B1<>B OR W1<>W THEN I(X)=0`
    
    which says set `RBW` as the secret and compare it to all remaining solutions and 
    get rid of any that don't match the same black and white report, 0 black and 2 white. 
    So let's do that.
    
    Remember, `RBW` is pretending to be the secret code here. These are the remaining
    solutions reporting their black and white pegs against `RBW`.
    
    | Guess | Black | White |     | Guess | Black | White |     | Guess | Black | White |
    |-------|-------|-------|-----|-------|-------|-------|-----|-------|-------|-------|
    | BBB   | 1     | 0     |     | WBB   | 1     | 1     |     | RBB   | 2     | 0     |   
    | BBW   | 2     | 0     |     | WBW   | 2     | 0     |     | RBW   | 3     | 0     |   
    | BBR   | 1     | 1     |     | WBR   | 1     | 2     |     | RBR   | 2     | 0     |    
    | BWB   | 0     | 2     |     | WWB   | 0     | 2     |     | RWB   | 1     | 2     |    
    | BWW   | 1     | 1     |     | WWW   | 1     | 0     |     | RWW   | 2     | 0     |    
    | BWR   | 0     | 3     |     | WWR   | 1     | 1     |     | RWR   | 1     | 1     |    
    | BRB   | 0     | 2     |     | WRB   | 0     | 3     |     | RRB   | 1     | 1     |    
    | 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** match 0 black and 2 white.
    
    | Guess    | Black | White |     | Guess    | Black | White |     | Guess    | Black | White |
    |----------|-------|-------|-----|----------|-------|-------|-----|----------|-------|-------|
    | ~~~BBB~~ | 1     | 0     |     | ~~~WBB~~ | 1     | 1     |     | ~~~RBB~~ | 2     | 0     |   
    | ~~~BBW~~ | 2     | 0     |     | ~~~WBW~~ | 2     | 0     |     | ~~~RBW~~ | 3     | 0     |   
    | ~~~BBR~~ | 1     | 1     |     | ~~~WBR~~ | 1     | 2     |     | ~~~RBR~~ | 2     | 0     |    
    | BWB      | 0     | 2     |     | WWB      | 0     | 2     |     | ~~~RWB~~ | 1     | 2     |    
    | ~~~BWW~~ | 1     | 1     |     | ~~~WWW~~ | 1     | 0     |     | ~~~RWW~~ | 2     | 0     |    
    | ~~~BWR~~ | 0     | 3     |     | ~~~WWR~~ | 1     | 1     |     | ~~~RWR~~ | 1     | 1     |    
    | BRB      | 0     | 2     |     | ~~~WRB~~ | 0     | 3     |     | ~~~RRB~~ | 1     | 1     |    
    | ~~~BRW~~ | 1     | 2     |     | ~~~WRW~~ | 1     | 1     |     | ~~~RRW~~ | 2     | 0     |    
    | BRR      | 0     | 2     |     | WRR      | 0     | 2     |     | ~~~RRR~~ | 1     | 0     |          
                                       
     That wipes out all but five solutions. Notice how the entire right column of solutions 
     is eliminated, including our original guess of `RBW`, therefore eliminating any 
     special case to specifically eliminate this guess from the solution set when we first find out
     its not the answer.
     
     Continuing on, we have the following solutions left of which our secret code, `BWB` 
     is one of them. Remember our commutative property explained previously. 
    
    | Guess | Black | White |
    |-------|-------|-------|
    | BWB   | 0     | 2     |
    | BRB   | 0     | 2     |
    | BRR   | 0     | 2     |
    | WWB   | 0     | 2     |
    | WRR   | 0     | 2     |
    
    So for its second pick, the computer will randomly pick one of these remaining solutions. Let's pick
    the middle one, `BRR`, and perform the same ritual. Our user reports to the computer 
    that it now has 1 black, 0 whites when comparing to our secret code `BWB`. Let's 
    now compare `BRR` to the remaining five solutions and eliminate any that **DON'T**
    report 1 black and 0 whites.
    
    | Guess    | Black | White |
    |----------|-------|-------|
    | BWB      | 1     | 0     |
    | ~~~BRB~~ | 2     | 0     |
    | ~~~BRR~~ | 3     | 0     |
    | ~~~WWB~~ | 0     | 1     |
    | ~~~WRR~~ | 2     | 0     | 
    
    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.
    This is because it is winnowing down the solutions 
    logarithmically on average. You noticed on the first pass, it wiped out 22 
    solutions. If it was doing this logarithmically the worst case guess would 
    still eliminate 18 of the solutions leaving 9 (32).  So we have as
    a guideline:
    
     Log(# of Colors)TotalPossibilities
     
    but TotalPossibilities = (# of Colors)# of Positions
    
    so you end up with the number of positions as a guess limit. If you consider the
    simplest non-trivial puzzle, two colors with two positions, and you guess BW or 
    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. 
    
    #### 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.)
    
    
    ================================================
    FILE: 60_Mastermind/csharp/Code.cs
    ================================================
    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace Game
    {
        /// 
        /// Represents a secret code in the game.
        /// 
        public class Code
        {
            private readonly int[] m_colors;
    
            /// 
            /// Initializes a new instance of the Code class from the given set
            /// of positions.
            /// 
            /// 
            /// Contains the color for each position.
            /// 
            public Code(IEnumerable colors)
            {
                m_colors = colors.ToArray();
                if (m_colors.Length == 0)
                    throw new ArgumentException("A code must contain at least one position");
            }
    
            /// 
            /// Compares this code with the given code.
            /// 
            /// 
            /// The code to compare.
            /// 
            /// 
            /// A number of black pegs and a number of white pegs.  The number
            /// of black pegs is the number of positions that contain the same
            /// color in both codes.  The number of white pegs is the number of
            /// colors that appear in both codes, but in the wrong positions.
            /// 
            public (int blacks, int whites) Compare(Code other)
            {
                // What follows is the O(N^2) from the original BASIC program
                // (where N is the number of positions in the code).  Note that
                // there is an O(N) algorithm.  (Finding it is left as an
                // exercise for the reader.)
                if (other.m_colors.Length != m_colors.Length)
                    throw new ArgumentException("Only codes of the same length can be compared");
    
                // Keeps track of which positions in the other code have already
                // been marked as exact or close matches.
                var consumed = new bool[m_colors.Length];
    
                var blacks = 0;
                var whites = 0;
    
                for (var i = 0; i < m_colors.Length; ++i)
                {
                    if (m_colors[i] == other.m_colors[i])
                    {
                        ++blacks;
                        consumed[i] = true;
                    }
                    else
                    {
                        // Check if the current color appears elsewhere in the
                        // other code.  We must be careful not to consider
                        // positions that are also exact matches.
                        for (var j = 0; j < m_colors.Length; ++j)
                        {
                            if (!consumed[j] &&
                                m_colors[i] == other.m_colors[j] &&
                                m_colors[j] != other.m_colors[j])
                            {
                                ++whites;
                                consumed[j] = true;
                                break;
                            }
                        }
                    }
                }
    
                return (blacks, whites);
            }
    
            /// 
            /// Gets a string representation of the code.
            /// 
            public override string ToString() =>
                new (m_colors.Select(index => Colors.List[index].ShortName).ToArray());
        }
    }
    
    
    ================================================
    FILE: 60_Mastermind/csharp/CodeFactory.cs
    ================================================
    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace Game
    {
        /// 
        /// Provides methods for generating codes with a given number of positions
        /// and colors.
        /// 
        public class CodeFactory
        {
            /// 
            /// Gets the number of colors in codes generated by this factory.
            /// 
            public int Colors { get; }
    
            /// 
            /// Gets the number of positions in codes generated by this factory.
            /// 
            public int Positions { get; }
    
            /// 
            /// Gets the number of distinct codes that this factory can
            /// generate.
            /// 
            public int Possibilities { get; }
    
            /// 
            /// Initializes a new instance of the CodeFactory class.
            /// 
            /// 
            /// The number of positions.
            /// 
            /// 
            /// The number of colors.
            /// 
            public CodeFactory(int positions, int colors)
            {
                if (positions < 1)
                    throw new ArgumentException("A code must contain at least one position");
    
                if (colors < 1)
                    throw new ArgumentException("A code must contain at least one color");
    
                if (colors > Game.Colors.List.Length)
                    throw new ArgumentException($"A code can contain no more than {Game.Colors.List.Length} colors");
    
                Positions     = positions;
                Colors        = colors;
                Possibilities = (int)Math.Pow(colors, positions);
            }
    
            /// 
            /// Creates a specified code.
            /// 
            /// 
            /// The number of the code to create from 0 to Possibilities - 1.
            /// 
            public Code Create(int number) =>
                EnumerateCodes().Skip(number).First();
    
            /// 
            /// Creates a random code using the provided random number generator.
            /// 
            /// 
            /// The random number generator.
            /// 
            public Code Create(Random random) =>
                Create(random.Next(Possibilities));
    
            /// 
            /// Generates a collection of codes containing every code that this
            /// factory can create exactly once.
            /// 
            public IEnumerable EnumerateCodes()
            {
                var current = new int[Positions];
                var position = default(int);
    
                do
                {
                    yield return new Code(current);
    
                    position = 0;
                    while (position < Positions && ++current[position] == Colors)
                        current[position++] = 0;
                }
                while (position < Positions);
            }
        }
    }
    
    
    ================================================
    FILE: 60_Mastermind/csharp/ColorInfo.cs
    ================================================
    using System;
    
    namespace Game
    {
        /// 
        /// Stores information about a color.
        /// 
        public record ColorInfo
        {
            /// 
            /// Gets a single character that represents the color.
            /// 
            public char ShortName { get; init; }
    
            /// 
            /// Gets the color's full name.
            /// 
            public string LongName { get; init; } = String.Empty;
        }
    }
    
    
    ================================================
    FILE: 60_Mastermind/csharp/Colors.cs
    ================================================
    namespace Game
    {
        /// 
        /// Provides information about the colors that can be used in codes.
        /// 
        public static class Colors
        {
            public static readonly ColorInfo[] List = new[]
            {
                new ColorInfo { ShortName = 'B', LongName = "BLACK"  },
                new ColorInfo { ShortName = 'W', LongName = "WHITE"  },
                new ColorInfo { ShortName = 'R', LongName = "RED"    },
                new ColorInfo { ShortName = 'G', LongName = "GREEN"  },
                new ColorInfo { ShortName = 'O', LongName = "ORANGE" },
                new ColorInfo { ShortName = 'Y', LongName = "YELLOW" },
                new ColorInfo { ShortName = 'P', LongName = "PURPLE" },
                new ColorInfo { ShortName = 'T', LongName = "TAN"    }
            };
        }
    }
    
    
    ================================================
    FILE: 60_Mastermind/csharp/Command.cs
    ================================================
    namespace Game
    {
        /// 
        /// Enumerates the different commands that the user can issue during
        /// the game.
        /// 
        public enum Command
        {
            MakeGuess,
            ShowBoard,
            Quit
        }
    }
    
    
    ================================================
    FILE: 60_Mastermind/csharp/Controller.cs
    ================================================
    using System;
    using System.Collections.Immutable;
    using System.Linq;
    
    namespace Game
    {
        /// 
        /// Contains functions for getting input from the end user.
        /// 
        public static class Controller
        {
            /// 
            /// Maps the letters for each color to the integer value representing
            /// that color.
            /// 
            /// 
            /// We derive this map from the Colors list rather than defining the
            /// entries directly in order to keep all color related information
            /// in one place.  (This makes it easier to change the color options
            /// later.)
            /// 
            private static ImmutableDictionary ColorsByKey = Colors.List
                .Select((info, index) => (key: info.ShortName, index))
                .ToImmutableDictionary(entry => entry.key, entry => entry.index);
    
            /// 
            /// Gets the number of colors to use in the secret code.
            /// 
            public static int GetNumberOfColors()
            {
                var maximumColors = Colors.List.Length;
                var colors = 0;
    
                while (colors < 1 || colors > maximumColors)
                {
                    colors = GetInteger(View.PromptNumberOfColors);
                    if (colors > maximumColors)
                        View.NotifyTooManyColors(maximumColors);
                }
    
                return colors;
            }
    
            /// 
            /// Gets the number of positions in the secret code.
            /// 
            /// 
            public static int GetNumberOfPositions()
            {
                // Note: We should probably ensure that the user enters a sane
                //  number of positions here.  (Things go south pretty quickly
                //  with a large number of positions.)  But since the original
                //  program did not, neither will we.
                return GetInteger(View.PromptNumberOfPositions);
            }
    
            /// 
            /// Gets the number of rounds to play.
            /// 
            public static int GetNumberOfRounds()
            {
                // Note: Silly numbers of rounds (like 0, or a negative number)
                //  are harmless, but it would still make sense to validate.
                return GetInteger(View.PromptNumberOfRounds);
            }
    
            /// 
            /// Gets a command from the user.
            /// 
            /// 
            /// The current move number.
            /// 
            /// 
            /// The number of code positions.
            /// 
            /// 
            /// The maximum number of code colors.
            /// 
            /// 
            /// The entered command and guess (if applicable).
            /// 
            public static (Command command, Code? guess) GetCommand(int moveNumber, int positions, int colors)
            {
                while (true)
                {
                    View.PromptGuess (moveNumber);
    
                    var input = Console.ReadLine();
                    if (input is null)
                        Environment.Exit(0);
    
                    switch (input.ToUpperInvariant())
                    {
                        case "BOARD":
                            return (Command.ShowBoard, null);
                        case "QUIT":
                            return (Command.Quit, null);
                        default:
                            if (input.Length != positions)
                                View.NotifyBadNumberOfPositions();
                            else
                            if (input.FindFirstIndex(c => !TranslateColor(c).HasValue) is int invalidPosition)
                                View.NotifyInvalidColor(input[invalidPosition]);
                            else
                                return (Command.MakeGuess, new Code(input.Select(c => TranslateColor(c)!.Value)));
    
                            break;
                    }
                }
            }
    
            /// 
            /// Waits until the user indicates that he or she is ready to continue.
            /// 
            public static void WaitUntilReady()
            {
                View.PromptReady();
                var input = Console.ReadLine();
                if (input is null)
                    Environment.Exit(0);
            }
    
            /// 
            /// Gets the number of blacks and whites for the given code from the
            /// user.
            /// 
            public static (int blacks, int whites) GetBlacksWhites(Code code)
            {
                while (true)
                {
                    View.PromptBlacksWhites(code);
    
                    var input = Console.ReadLine();
                    if (input is null)
                        Environment.Exit(0);
    
                    var parts = input.Split(',');
    
                    if (parts.Length != 2)
                        View.PromptTwoValues();
                    else
                    if (!Int32.TryParse(parts[0], out var blacks) || !Int32.TryParse(parts[1], out var whites))
                        View.PromptValidInteger();
                    else
                        return (blacks, whites);
                }
            }
    
            /// 
            /// Gets an integer value from the user.
            /// 
            private static int GetInteger(Action prompt)
            {
                while (true)
                {
                    prompt();
    
                    var input = Console.ReadLine();
                    if (input is null)
                        Environment.Exit(0);
    
                    if (Int32.TryParse(input, out var result))
                        return result;
                    else
                        View.PromptValidInteger();
                }
            }
    
            /// 
            /// Translates the given character into the corresponding color.
            /// 
            private static int? TranslateColor(char c) =>
                ColorsByKey.TryGetValue(c, out var index) ? index : null;
        }
    }
    
    
    ================================================
    FILE: 60_Mastermind/csharp/EnumerableExtensions.cs
    ================================================
    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace Game
    {
        /// 
        /// Provides additional methods for the 
        /// interface.
        /// 
        public static class EnumerableExtensions
        {
            /// 
            /// Cycles through the integer values in the range [0, count).
            /// 
            /// 
            /// The first value to return.
            /// 
            /// 
            /// The number of values to return.
            /// 
            public static IEnumerable Cycle(int start, int count)
            {
                if (count < 1)
                    throw new ArgumentException("count must be at least 1");
    
                if (start < 0 || start >= count)
                    throw new ArgumentException("start must be in the range [0, count)");
    
                for (var i = start; i < count; ++i)
                    yield return i;
    
                for (var i = 0; i < start; ++i)
                    yield return i;
            }
    
            /// 
            /// Finds the index of the first item in the given sequence that
            /// satisfies the given predicate.
            /// 
            /// 
            /// The type of elements in the sequence.
            /// 
            /// 
            /// The source sequence.
            /// 
            /// 
            /// The predicate function.
            /// 
            /// 
            /// The index of the first element in the source sequence for which
            /// predicate(element) is true.  If there is no such element, return
            /// is null.
            /// 
            public static int? FindFirstIndex(this IEnumerable source, Func predicate) =>
                source.Select((element, index) => predicate(element) ? index : default(int?))
                    .FirstOrDefault(index => index.HasValue);
    
            /// 
            /// Returns the first item in the given sequence that matches the
            /// given predicate.
            /// 
            /// 
            /// The type of elements in the sequence.
            /// 
            /// 
            /// The source sequence.
            /// 
            /// 
            /// The predicate to check against each element.
            /// 
            /// 
            /// The value to return if no elements match the predicate.
            /// 
            /// 
            /// The first item in the source sequence that matches the given
            /// predicate, or the provided default value if none do.
            /// 
            public static T FirstOrDefault(this IEnumerable source, Func predicate, T defaultValue)
            {
                foreach (var element in source)
                    if (predicate(element))
                        return element;
    
                return defaultValue;
            }
        }
    }
    
    
    ================================================
    FILE: 60_Mastermind/csharp/Mastermind.csproj
    ================================================
    
      
        Exe
        net5.0
        enable
      
    
    
    
    ================================================
    FILE: 60_Mastermind/csharp/Mastermind.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 17
    VisualStudioVersion = 17.0.32014.148
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mastermind", "Mastermind.csproj", "{AEC839CD-C6D3-4476-AA85-79B160AF78BE}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{AEC839CD-C6D3-4476-AA85-79B160AF78BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{AEC839CD-C6D3-4476-AA85-79B160AF78BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{AEC839CD-C6D3-4476-AA85-79B160AF78BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{AEC839CD-C6D3-4476-AA85-79B160AF78BE}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {1BDFBEE6-8345-438C-8FCE-B2C9394CC080}
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 60_Mastermind/csharp/Program.cs
    ================================================
    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace Game
    {
        // MASTERMIND II
        // STEVE NORTH
        // CREATIVE COMPUTING
        // PO BOX 789-M MORRISTOWN NEW JERSEY 07960
        class Program
        {
            public const int MaximumGuesses = 10;
    
            static void Main()
            {
                var (codeFactory, rounds) = StartGame();
    
                var random        = new Random();
                var humanScore    = 0;
                var computerScore = 0;
    
                for (var round = 1; round <= rounds; ++round)
                {
                    View.ShowStartOfRound(round);
    
                    if (!HumanTakesTurn())
                        return;
    
                    while (!ComputerTakesTurn())
                        View.ShowInconsistentInformation();
                }
    
                View.ShowScores(humanScore, computerScore, isFinal: true);
    
                /// 
                /// Gets the game start parameters from the user.
                /// 
                (CodeFactory codeFactory, int rounds) StartGame()
                {
                    View.ShowBanner();
    
                    var colors    = Controller.GetNumberOfColors();
                    var positions = Controller.GetNumberOfPositions();
                    var rounds    = Controller.GetNumberOfRounds();
    
                    var codeFactory = new CodeFactory(positions, colors);
    
                    View.ShowTotalPossibilities(codeFactory.Possibilities);
                    View.ShowColorTable(codeFactory.Colors);
    
                    return (codeFactory, rounds);
                }
    
                /// 
                /// Executes the human's turn.
                /// 
                /// 
                /// True if thue human completed his or her turn and false if
                /// he or she quit the game.
                /// 
                bool HumanTakesTurn()
                {
                    // Store a history of the human's guesses (used for the show
                    // board command below).
                    var history     = new List();
                    var code        = codeFactory.Create(random);
                    var guessNumber = default(int);
    
                    for (guessNumber = 1; guessNumber <= MaximumGuesses; ++guessNumber)
                    {
                        var guess = default(Code);
    
                        while (guess is null)
                        {
                            switch (Controller.GetCommand(guessNumber, codeFactory.Positions, codeFactory.Colors))
                            {
                                case (Command.MakeGuess, Code input):
                                    guess = input;
                                    break;
                                case (Command.ShowBoard, _):
                                    View.ShowBoard(history);
                                    break;
                                case (Command.Quit, _):
                                    View.ShowQuitGame(code);
                                    return false;
                            }
                        }
    
                        var (blacks, whites) = code.Compare(guess);
                        if (blacks == codeFactory.Positions)
                            break;
    
                        View.ShowResults(blacks, whites);
    
                        history.Add(new TurnResult(guess, blacks, whites));
                    }
    
                    if (guessNumber <= MaximumGuesses)
                        View.ShowHumanGuessedCode(guessNumber);
                    else
                        View.ShowHumanFailedToGuessCode(code);
    
                    humanScore += guessNumber;
    
                    View.ShowScores(humanScore, computerScore, isFinal: false);
                    return true;
                }
    
                /// 
                /// Executes the computers turn.
                /// 
                /// 
                /// True if the computer completes its turn successfully and false
                /// if it does not (due to human error).
                /// 
                bool ComputerTakesTurn()
                {
                    var isCandidate = new bool[codeFactory.Possibilities];
                    var guessNumber = default(int);
    
                    Array.Fill(isCandidate, true);
    
                    View.ShowComputerStartTurn();
                    Controller.WaitUntilReady();
    
                    for (guessNumber = 1; guessNumber <= MaximumGuesses; ++guessNumber)
                    {
                        // Starting with a random code, cycle through codes until
                        // we find one that is still a candidate solution.  If
                        // there are no remaining candidates, then it implies that
                        // the user made an error in one or more responses.
                        var codeNumber = EnumerableExtensions.Cycle(random.Next(codeFactory.Possibilities), codeFactory.Possibilities)
                            .FirstOrDefault(i => isCandidate[i], -1);
    
                        if (codeNumber < 0)
                            return false;
    
                        var guess = codeFactory.Create(codeNumber);
    
                        var (blacks, whites) = Controller.GetBlacksWhites(guess);
                        if (blacks == codeFactory.Positions)
                            break;
    
                        // Mark codes which are no longer potential solutions.  We
                        // know that the current guess yields the above number of
                        // blacks and whites when compared to the solution, so any
                        // code that yields a different number of blacks or whites
                        // can't be the answer.
                        foreach (var (candidate, index) in codeFactory.EnumerateCodes().Select((candidate, index) => (candidate, index)))
                        {
                            if (isCandidate[index])
                            {
                                var (candidateBlacks, candidateWhites) = guess.Compare(candidate);
                                if (blacks != candidateBlacks || whites != candidateWhites)
                                    isCandidate[index] = false;
                            }
                        }
                    }
    
                    if (guessNumber <= MaximumGuesses)
                        View.ShowComputerGuessedCode(guessNumber);
                    else
                        View.ShowComputerFailedToGuessCode();
    
                    computerScore += guessNumber;
                    View.ShowScores(humanScore, computerScore, isFinal: false);
    
                    return true;
                }
            }
        }
    }
    
    
    ================================================
    FILE: 60_Mastermind/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 60_Mastermind/csharp/TurnResult.cs
    ================================================
    namespace Game
    {
        /// 
        /// Stores the result of a player's turn.
        /// 
        public record TurnResult
        {
            /// 
            /// Gets the code guessed by the player.
            /// 
            public Code Guess { get; }
    
            /// 
            /// Gets the number of black pegs resulting from the guess.
            /// 
            public int Blacks { get; }
    
            /// 
            /// Gets the number of white pegs resulting from the guess.
            /// 
            public int Whites { get; }
    
            /// 
            /// Initializes a new instance of the TurnResult record.
            /// 
            /// 
            /// The player's guess.
            /// 
            /// 
            /// The number of black pegs.
            /// 
            /// 
            /// The number of white pegs.
            /// 
            public TurnResult(Code guess, int blacks, int whites) =>
                (Guess, Blacks, Whites) = (guess, blacks, whites);
        }
    }
    
    
    ================================================
    FILE: 60_Mastermind/csharp/View.cs
    ================================================
    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace Game
    {
        /// 
        /// Contains functions for displaying information to the end user.
        /// 
        public static class View
        {
            public static void ShowBanner()
            {
                Console.WriteLine("                              MASTERMIND");
                Console.WriteLine("               CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine();
            }
    
            public static void ShowTotalPossibilities(int possibilities)
            {
                Console.WriteLine($"TOTAL POSSIBILITIES = {possibilities}");
                Console.WriteLine();
            }
    
            public static void ShowColorTable(int numberOfColors)
            {
                Console.WriteLine();
                Console.WriteLine("COLOR     LETTER");
                Console.WriteLine("=====     ======");
    
                foreach (var color in Colors.List.Take(numberOfColors))
                    Console.WriteLine($"{color.LongName,-13}{color.ShortName}");
    
                Console.WriteLine();
            }
    
            public static void ShowStartOfRound(int roundNumber)
            {
                Console.WriteLine();
                Console.WriteLine($"ROUND NUMBER {roundNumber} ----");
                Console.WriteLine();
                Console.WriteLine("GUESS MY COMBINATION.");
                Console.WriteLine();
            }
    
            public static void ShowBoard(IEnumerable history)
            {
                Console.WriteLine();
                Console.WriteLine("BOARD");
                Console.WriteLine("MOVE     GUESS          BLACK     WHITE");
    
                var moveNumber = 0;
                foreach (var result in history)
                    Console.WriteLine($"{++moveNumber,-9}{result.Guess,-16}{result.Blacks,-10}{result.Whites}");
    
                Console.WriteLine();
            }
    
            public static void ShowQuitGame(Code code)
            {
                Console.WriteLine($"QUITTER!  MY COMBINATION WAS: {code}");
                Console.WriteLine("GOOD BYE");
            }
    
            public static void ShowResults(int blacks, int whites)
            {
                Console.WriteLine($"YOU HAVE  {blacks}  BLACKS AND  {whites}  WHITES.");
            }
    
            public static void ShowHumanGuessedCode(int guessNumber)
            {
                Console.WriteLine($"YOU GUESSED IT IN  {guessNumber}  MOVES!");
            }
    
            public static void ShowHumanFailedToGuessCode(Code code)
            {
                // Note: The original code did not print out the combination, but
                // this appears to be a bug.
                Console.WriteLine("YOU RAN OUT OF MOVES!  THAT'S ALL YOU GET!");
                Console.WriteLine($"THE ACTUAL COMBINATION WAS: {code}");
            }
    
            public static void ShowScores(int humanScore, int computerScore, bool isFinal)
            {
                if (isFinal)
                {
                    Console.WriteLine("GAME OVER");
                    Console.WriteLine("FINAL SCORE:");
                }
                else
                    Console.WriteLine("SCORE:");
    
                Console.WriteLine($"     COMPUTER  {computerScore}");
                Console.WriteLine($"     HUMAN     {humanScore}");
                Console.WriteLine();
            }
    
            public static void ShowComputerStartTurn()
            {
                Console.WriteLine("NOW I GUESS.  THINK OF A COMBINATION.");
            }
    
            public static void ShowInconsistentInformation()
            {
                Console.WriteLine("YOU HAVE GIVEN ME INCONSISTENT INFORMATION.");
                Console.WriteLine("TRY AGAIN, AND THIS TIME PLEASE BE MORE CAREFUL.");
            }
    
            public static void ShowComputerGuessedCode(int guessNumber)
            {
                Console.WriteLine($"I GOT IT IN  {guessNumber}  MOVES!");
            }
    
            public static void ShowComputerFailedToGuessCode()
            {
                Console.WriteLine("I USED UP ALL MY MOVES!");
                Console.WriteLine("I GUESS MY CPU IS JUST HAVING AN OFF DAY.");
            }
    
            public static void PromptNumberOfColors()
            {
                Console.Write("NUMBER OF COLORS? ");
            }
    
            public static void PromptNumberOfPositions()
            {
                Console.Write("NUMBER OF POSITIONS? ");
            }
    
            public static void PromptNumberOfRounds()
            {
                Console.Write("NUMBER OF ROUNDS? ");
            }
    
            public static void PromptGuess(int moveNumber)
            {
                Console.Write($"MOVE #  {moveNumber}  GUESS ? ");
            }
    
            public static void PromptReady()
            {
                Console.Write("HIT RETURN WHEN READY ? ");
            }
    
            public static void PromptBlacksWhites(Code code)
            {
                Console.Write($"MY GUESS IS: {code}");
                Console.Write("  BLACKS, WHITES ? ");
            }
    
            public static void PromptTwoValues()
            {
                Console.WriteLine("PLEASE ENTER TWO VALUES, SEPARATED BY A COMMA");
            }
    
            public static void PromptValidInteger()
            {
                Console.WriteLine("PLEASE ENTER AN INTEGER VALUE");
            }
    
            public static void NotifyBadNumberOfPositions()
            {
                Console.WriteLine("BAD NUMBER OF POSITIONS");
            }
    
            public static void NotifyInvalidColor(char colorKey)
            {
                Console.WriteLine($"'{colorKey}' IS UNRECOGNIZED.");
            }
    
            public static void NotifyTooManyColors(int maxColors)
            {
                Console.WriteLine($"NO MORE THAN {maxColors}, PLEASE!");
            }
        }
    }
    
    
    ================================================
    FILE: 60_Mastermind/java/Mastermind.java
    ================================================
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.BitSet;
    import java.util.List;
    import java.util.Objects;
    import java.util.Random;
    import java.util.Scanner;
    import java.util.function.Function;
    import java.util.function.Predicate;
    import java.util.stream.Collectors;
    import java.util.stream.IntStream;
    
    /**
     * A port of the BASIC Mastermind game in java.
     *
     * Differences between this and the original BASIC:
     *    Uses a number base conversion approach to converting solution ids to
     *    color code strings. The original performs an inefficient add by 1
     *    with carry technique where every carry increments the next positions
     *    color id.
     *
     *    Implements a ceiling check on the number of positions in a secret code
     *    to not run out of memory. Because of the algorithm that the computer
     *    uses to deduce the players secret code, it searches through the entire
     *    possible spectrum of solutions. This can be a very large number because
     *    it's (number of colors) ^ (number of positions). The original will
     *    happily try to allocate all the memory on the system if this number is
     *    too large. If it did successfully allocate the memory on a large solution
     *    set then it would also take too long to compute code strings via its
     *    technique mentioned in the previous note.
     *
     *    An extra message is given at the start to alert the player to the
     *    BOARD and QUIT commands.
     */
    public class Mastermind {
      final Random random = new Random();
      // some less verbose printing methods
      static private void pf(String s, Object... o){ System.out.printf(s, o);}
      static private void pl(String s){System.out.println(s);}
      static private void pl(){System.out.println();}
    
      public static void main(String[] args) {
        title();
        Mastermind game = setup();
        game.play();
      }
    
      /**
       * The eight possible color codes.
       */
      private enum Color {
        B("BLACK"), W("WHITE"), R("RED"), G("GREEN"),
        O("ORANGE"), Y("YELLOW"), P("PURPLE"), T("TAN");
        public final String name;
    
        Color(String name) {
          this.name = name;
        }
      }
    
      /**
       * Represents a guess and the subsequent number of colors in the correct
       * position (blacks), and the number of colors present but not in the correct
       * position (whites.)
       */
      private record Guess(int guessNum, String guess, int blacks, int whites){}
    
    
      private void play() {
        IntStream.rangeClosed(1,rounds).forEach(this::playRound);
        pl("GAME OVER");
        pl("FINAL SCORE: ");
        pl(getScore());
      }
    
      /**
       * Builder-ish pattern for creating Mastermind game
       * @return Mastermind game object
       */
      private static Mastermind setup() {
        int numOfColors;
        pf("NUMBER OF COLORS? > ");
        numOfColors = getPositiveNumberUpTo(Color.values().length);
        int maxPositions = getMaxPositions(numOfColors);
        pf("NUMBER OF POSITIONS (MAX %d)? > ", maxPositions);
        int positions = getPositiveNumberUpTo(maxPositions);
        pf("NUMBER OF ROUNDS? > ");
        int rounds = getPositiveNumber();
        pl("ON YOUR TURN YOU CAN ENTER 'BOARD' TO DISPLAY YOUR PREVIOUS GUESSES,");
        pl("OR 'QUIT' TO GIVE UP.");
        return new Mastermind(numOfColors, positions, rounds, 10);
      }
    
      /**
       * Computes the number of allowable positions to prevent the total possible
       * solution set that the computer has to check to a reasonable number, and
       * to prevent out of memory errors.
       *
       * The computer guessing algorithm uses a BitSet which has a limit of 2^31
       * bits (Integer.MAX_VALUE bits). Since the number of possible solutions to
       * any mastermind game is (numColors) ^ (numPositions) we need find the
       * maximum number of positions by finding the Log|base-NumOfColors|(2^31)
       *
       * @param numOfColors  number of different colors
       * @return             max number of positions in the secret code.
       */
      private static int getMaxPositions(int numOfColors){
        return (int)(Math.log(Integer.MAX_VALUE)/Math.log(numOfColors));
      }
    
      final int numOfColors, positions, rounds, possibilities;
      int humanMoves, computerMoves;
      final BitSet solutionSet;
      final Color[] colors;
      final int maxTries;
    
      // A recording of human guesses made during the round for the BOARD command.
      final List guesses = new ArrayList<>();
    
      // A regular expression to validate user guess strings
      final String guessValidatorRegex;
    
      public Mastermind(int numOfColors, int positions, int rounds, int maxTries) {
        this.numOfColors = numOfColors;
        this.positions = positions;
        this.rounds = rounds;
        this.maxTries = maxTries;
        this.humanMoves = 0;
        this.computerMoves = 0;
        String colorCodes = Arrays.stream(Color.values())
                                  .limit(numOfColors)
                                  .map(Color::toString)
                                  .collect(Collectors.joining());
        // regex that limits the number of color codes and quantity for a guess.
        this.guessValidatorRegex = "^[" + colorCodes + "]{" + positions + "}$";
        this.colors = Color.values();
        this.possibilities = (int) Math.round(Math.pow(numOfColors, positions));
        pf("TOTAL POSSIBILITIES =% d%n", possibilities);
        this.solutionSet = new BitSet(possibilities);
        displayColorCodes(numOfColors);
      }
    
      private void playRound(int round) {
        pf("ROUND NUMBER % d ----%n%n",round);
        humanTurn();
        computerTurn();
        pl(getScore());
      }
    
      private void humanTurn() {
        guesses.clear();
        String secretCode = generateColorCode();
        pl("GUESS MY COMBINATION. \n");
        int guessNumber = 1;
        while (true) {   // User input loop
          pf("MOVE #%d GUESS ?", guessNumber);
          final String guess = getWord();
          if (guess.equals(secretCode)) {
            guesses.add(new Guess(guessNumber, guess, positions, 0));
            pf("YOU GUESSED IT IN %d MOVES!%n", guessNumber);
            humanMoves++;
            pl(getScore());
            return;
          } else if ("BOARD".equals(guess)) {  displayBoard();
          } else if ("QUIT".equals(guess))  {  quit(secretCode);
          } else if (!validateGuess(guess)) {  pl(guess + " IS UNRECOGNIZED.");
          } else {
            Guess g = evaluateGuess(guessNumber, guess, secretCode);
            pf("YOU HAVE %d BLACKS AND %d WHITES.%n", g.blacks(), g.whites());
            guesses.add(g);
            humanMoves++;
            guessNumber++;
          }
          if (guessNumber > maxTries) {
            pl("YOU RAN OUT OF MOVES!  THAT'S ALL YOU GET!");
            pl("THE ACTUAL COMBINATION WAS: " + secretCode);
            return;
          }
        }
      }
    
      private void computerTurn(){
        while (true) {
          pl("NOW I GUESS.  THINK OF A COMBINATION.");
          pl("HIT RETURN WHEN READY:");
          solutionSet.set(0, possibilities);  // set all bits to true
          getInput("RETURN KEY", Scanner::nextLine, Objects::nonNull);
          int guessNumber = 1;
          while(true){
            if (solutionSet.cardinality() == 0) {
              // user has given wrong information, thus we have cancelled out
              // any remaining possible valid solution.
              pl("YOU HAVE GIVEN ME INCONSISTENT INFORMATION.");
              pl("TRY AGAIN, AND THIS TIME PLEASE BE MORE CAREFUL.");
              break;
            }
            // Randomly pick an untried solution.
            int solution = solutionSet.nextSetBit(generateSolutionID());
            if (solution == -1) {
              solution = solutionSet.nextSetBit(0);
            }
            String guess = solutionIdToColorCode(solution);
            pf("MY GUESS IS: %s  BLACKS, WHITES ? ",guess);
            int[] bAndWPegs = getPegCount(positions);
            if (bAndWPegs[0] == positions) {
              pf("I GOT IT IN % d MOVES!%n", guessNumber);
              computerMoves+=guessNumber;
              return;
            }
            // wrong guess, first remove this guess from solution set
            solutionSet.clear(solution);
            int index = 0;
            // Cycle through remaining solution set, marking any solutions as invalid
            // that don't exactly match what the user said about our guess.
            while ((index = solutionSet.nextSetBit(index)) != -1) {
              String solutionStr = solutionIdToColorCode(index);
              Guess possibleSolution = evaluateGuess(0, solutionStr, guess);
              if (possibleSolution.blacks() != bAndWPegs[0] ||
                  possibleSolution.whites() != bAndWPegs[1]) {
                solutionSet.clear(index);
              }
              index++;
            }
            guessNumber++;
          }
        }
      }
    
      // tally black and white pegs
      private Guess evaluateGuess(int guessNum, String guess, String secretCode) {
        int blacks = 0, whites = 0;
        char[] g = guess.toCharArray();
        char[] sc = secretCode.toCharArray();
        // An incremented number that marks this position as having been counted
        // as a black or white peg already.
        char visited = 0x8000;
        // Cycle through guess letters and check for color and position match
        // with the secretCode. If both match, mark it black.
        // Else cycle through remaining secretCode letters and check if color
        // matches. If this matches, a preventative check must be made against
        // the guess letter matching the secretCode letter at this position in
        // case it would be counted as a black in one of the next passes.
        for (int j = 0; j < positions; j++) {
          if (g[j] == sc[j]) {
            blacks++;
            g[j] = visited++;
            sc[j] = visited++;
          }
          for (int k = 0; k < positions; k++) {
            if (g[j] == sc[k] && g[k] != sc[k]) {
              whites++;
              g[j] = visited++;
              sc[k] = visited++;
            }
          }
        }
        return new Guess(guessNum, guess, blacks, whites);
      }
    
      private boolean validateGuess(String guess) {
        return guess.length() == positions && guess.matches(guessValidatorRegex);
      }
    
      private String getScore() {
        return "SCORE:%n\tCOMPUTER \t%d%n\tHUMAN \t%d%n"
            .formatted(computerMoves, humanMoves);
      }
    
      private void printGuess(Guess g){
        pf("% 3d%9s% 15d% 10d%n",g.guessNum(),g.guess(),g.blacks(),g.whites());
      }
      
      private void displayBoard() {
        pl();
        pl("BOARD");
        pl("MOVE     GUESS          BLACK     WHITE");
        guesses.forEach(this::printGuess);
        pl();
      }
    
      private void quit(String secretCode) {
        pl("QUITTER!  MY COMBINATION WAS: " + secretCode);
        pl("GOOD BYE");
        System.exit(0);
      }
    
      /**
       * Generates a set of color codes randomly.
       */
      private String generateColorCode() {
        int solution = generateSolutionID();
        return solutionIdToColorCode(solution);
      }
    
      /**
       * From the total possible number of solutions created at construction, choose
       * one randomly.
       *
       * @return one of many possible solutions
       */
      private int generateSolutionID() {
        return random.nextInt(0, this.possibilities);
      }
    
      /**
       * Given the number of colors and positions in a secret code, decode one of
       * those permutations, a solution number, into a string of letters
       * representing colored pegs.
       *
       * The pattern can be decoded easily as a number with base `numOfColors` and
       * `positions` representing the digits. For example if numOfColors is 5 and
       * positions is 3 then the pattern is converted to a number that is base 5
       * with three digits. Each digit then maps to a particular color.
       *
       * @param solution one of many possible solutions
       * @return String representing this solution's color combination.
       */
      private String solutionIdToColorCode(final int solution) {
        StringBuilder secretCode = new StringBuilder();
        int pos = possibilities;
        int remainder = solution;
        for (int i = positions - 1; i > 0; i--) {
          pos = pos / numOfColors;
          secretCode.append(colors[remainder / pos].toString());
          remainder = remainder % pos;
        }
        secretCode.append(colors[remainder].toString());
        return secretCode.toString();
      }
    
      private static void displayColorCodes(int numOfColors) {
        pl("\n\nCOLOR     LETTER\n=====     ======");
        Arrays.stream(Color.values())
              .limit(numOfColors)
              .map(c -> c.name + " ".repeat(13 - c.name.length()) + c)
              .forEach(Mastermind::pl);
        pl();pl();
      }
    
      private static void title() {
        pl("""    
                                      MASTERMIND
                       CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY%n%n%n
        """);
      }
    
      /////////////////////////////////////////////////////////
      // User input functions from here on
    
      /**
       * Base input function to be called from a specific input function.
       * Re-prompts upon unexpected or invalid user input.
       * Discards any remaining input in the line of user entered input once
       * it gets what it wants.
       * @param descriptor  Describes explicit type of input expected
       * @param extractor   The method performed against a Scanner object to parse
       *                    the type of input.
       * @param conditional A test that the input meets a minimum validation.
       * @param          Input type returned.
       * @return            input type for this line of user input.
       */
      private static  T getInput(String descriptor,
                                    Function extractor,
                                    Predicate conditional) {
    
        Scanner scanner = new Scanner(System.in);
        while (true) {
          try {
            T input = extractor.apply(scanner);
            if (conditional.test(input)) {
              return input;
            }
          } catch (Exception ex) {
            try {
              // If we are here then a call on the scanner was most likely unable to
              // parse the input. We need to flush whatever is leftover from this
              // line of interactive user input so that we can re-prompt for new input.
              scanner.nextLine();
            } catch (Exception ns_ex) {
              // if we are here then the input has been closed, or we received an
              // EOF (end of file) signal, usually in the form of a ctrl-d or
              // in the case of Windows, a ctrl-z.
              pl("END OF INPUT, STOPPING PROGRAM.");
              System.exit(1);
            }
          }
          pf("!%s EXPECTED - RETRY INPUT LINE%n? ", descriptor);
        }
      }
    
      private static int getPositiveNumber() {
        return getInput("NUMBER", Scanner::nextInt, num -> num > 0);
      }
    
      private static int getPositiveNumberUpTo(long to) {
        return getInput(
            "NUMBER FROM 1 TO " + to,
            Scanner::nextInt,
            num -> num > 0 && num <= to);
      }
    
      private static int[] getPegCount(int upperBound) {
        int[] nums = {Integer.MAX_VALUE, Integer.MAX_VALUE};
        while (true) {
          String input = getInput(
              "NUMBER, NUMBER",
              Scanner::nextLine,
              s -> s.matches("\\d+[\\s,]+\\d+$"));
          String[] numbers = input.split("[\\s,]+");
          nums[0] = Integer.parseInt(numbers[0].trim());
          nums[1] = Integer.parseInt(numbers[1].trim());
          if (nums[0] <= upperBound && nums[1] <= upperBound &&
              nums[0] >= 0 && nums[1] >= 0) {
            return nums;
          }
          pf("NUMBERS MUST BE FROM 0 TO %d.%n? ", upperBound);
        }
      }
    
      private static String getWord() {
        return getInput("WORD", Scanner::next, word -> !"".equals(word));
      }
    }
    
    
    ================================================
    FILE: 60_Mastermind/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 60_Mastermind/javascript/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells)
    
    
    ================================================
    FILE: 60_Mastermind/javascript/mastermind.html
    ================================================
    
    
    MASTERMIND
    
    
    
    
    
    
    
    
    
    
    ================================================
    FILE: 60_Mastermind/javascript/mastermind.js
    ================================================
    // MASTERMIND
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    var p9;
    var c9;
    var b;
    var w;
    var f;
    var m;
    
    var qa;
    var sa;
    var ss;
    var as;
    var gs;
    var hs;
    
    function initialize_qa()
    {
        for (s = 1; s <= p9; s++)
            qa[s] = 0;
    }
    
    function increment_qa()
    {
        if (qa[1] <= 0) {
            // If zero, this is our firt increment: make all ones
            for (s = 1; s <= p9; s++)
                qa[s] = 1;
        } else {
            q = 1;
            while (1) {
                qa[q] = qa[q] + 1;
                if (qa[q] <= c9)
                    return;
                qa[q] = 1;
                q++;
            }
        }
    }
    
    function convert_qa()
    {
        for (s = 1; s <= p9; s++) {
            as[s] = ls.substr(qa[s] - 1, 1);
        }
    }
    
    function get_number()
    {
        b = 0;
        w = 0;
        f = 0;
        for (s = 1; s <= p9; s++) {
            if (gs[s] == as[s]) {
                b++;
                gs[s] = String.fromCharCode(f);
                as[s] = String.fromCharCode(f + 1);
                f += 2;
            } else {
                for (t = 1; t <= p9; t++) {
                    if (gs[s] == as[t] && gs[t] != as[t]) {
                        w++;
                        as[t] = String.fromCharCode(f);
                        gs[s] = String.fromCharCode(f + 1);
                        f += 2;
                        break;
                    }
                }
            }
        }
    }
    
    function convert_qa_hs()
    {
        for (s = 1; s <= p9; s++) {
            hs[s] = ls.substr(qa[s] - 1, 1);
        }
    }
    
    function copy_hs()
    {
        for (s = 1; s <= p9; s++) {
            gs[s] = hs[s];
        }
    }
    
    function board_printout()
    {
        print("\n");
        print("BOARD\n");
        print("MOVE     GUESS          BLACK     WHITE\n");
        for (z = 1; z <= m - 1; z++) {
            str = " " + z + " ";
            while (str.length < 9)
                str += " ";
            str += ss[z];
            while (str.length < 25)
                str += " ";
            str += sa[z][1];
            while (str.length < 35)
                str += " ";
            str += sa[z][2];
            print(str + "\n");
        }
        print("\n");
    }
    
    function quit()
    {
        print("QUITTER!  MY COMBINATION WAS: ");
        convert_qa();
        for (x = 1; x <= p9; x++) {
            print(as[x]);
        }
        print("\n");
        print("GOOD BYE\n");
    }
    
    function show_score()
    {
        print("SCORE:\n");
        show_points();
    }
    
    function show_points()
    {
        print("     COMPUTER " + c + "\n");
        print("     HUMAN    " + h + "\n");
        print("\n");
    }
    
    var color = ["BLACK", "WHITE", "RED", "GREEN",
                 "ORANGE", "YELLOW", "PURPLE", "TAN"];
    
    // Main program
    async function main()
    {
        print(tab(30) + "MASTERMIND\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        //
        //  MASTERMIND II
        //  STEVE NORTH
        //  CREATIVE COMPUTING
        //  PO BOX 789-M MORRISTOWN NEW JERSEY 07960
        //
        //
        while (1) {
            print("NUMBER OF COLORS");
            c9 = parseInt(await input());
            if (c9 <= 8)
                break;
            print("NO MORE THAN 8, PLEASE!\n");
        }
        print("NUMBER OF POSITIONS");
        p9 = parseInt(await input());
        print("NUMBER OF ROUNDS");
        r9 = parseInt(await input());
        p = Math.pow(c9, p9);
        print("TOTAL POSSIBILITIES = " + p + "\n");
        h = 0;
        c = 0;
        qa = [];
        sa = [];
        ss = [];
        as = [];
        gs = [];
        ia = [];
        hs = [];
        ls = "BWRGOYPT";
        print("\n");
        print("\n");
        print("COLOR    LETTER\n");
        print("=====    ======\n");
        for (x = 1; x <= c9; x++) {
            str = color[x - 1];
            while (str.length < 13)
                str += " ";
            str += ls.substr(x - 1, 1);
            print(str + "\n");
        }
        print("\n");
        for (r = 1; r <= r9; r++) {
            print("\n");
            print("ROUND NUMBER " + r + " ----\n");
            print("\n");
            print("GUESS MY COMBINATION.\n");
            print("\n");
            // Get a combination
            a = Math.floor(p * Math.random() + 1);
            initialize_qa();
            for (x = 1; x <= a; x++) {
                increment_qa();
            }
            for (m = 1; m <= 10; m++) {
                while (1) {
                    print("MOVE # " + m + " GUESS ");
                    str = await input();
                    if (str == "BOARD") {
                        board_printout();
                    } else if (str == "QUIT") {
                        quit();
                        return;
                    } else if (str.length != p9) {
                        print("BAD NUMBER OF POSITIONS.\n");
                    } else {
                        // Unpack str into gs(1-p9)
                        for (x = 1; x <= p9; x++) {
                            y = ls.indexOf(str.substr(x - 1, 1));
                            if (y < 0) {
                                print("'" + str.substr(x - 1, 1) + "' IS UNRECOGNIZED.\n");
                                break;
                            }
                            gs[x] = str.substr(x - 1, 1);
                        }
                        if (x > p9)
                            break;
                    }
                }
                // Now we convert qa(1-p9) into as(1-p9) [ACTUAL GUESS]
                convert_qa();
                // And get number of blacks and white
                get_number();
                if (b == p9) {
                    print("YOU GUESSED IT IN " + m + " MOVES!\n");
                    break;
                }
                //tell human results
                print("YOU HAVE " + b + " BLACKS AND " + w + " WHITES.")
                // Save all this stuff for board printout later
                ss[m] = str;
                sa[m] = [];
                sa[m][1] = b;
                sa[m][2] = w;
            }
            if (m > 10) {
                print("YOU RAN OUT OF MOVES!  THAT'S ALL YOU GET!\n");
            }
            h += m;
            show_score();
    
            //
            // Now computer guesses
            //
            for (x = 1; x <= p; x++)
                ia[x] = 1;
            print("NOW I GUESS.  THINK OF A COMBINATION.\n");
            print("HIT RETURN WHEN READY:");
            str = await input();
            for (m = 1; m <= 10; m++) {
                initialize_qa();
                // Find a guess
                g = Math.floor(p * Math.random() + 1);
                if (ia[g] != 1) {
                    for (x = g; x <= p; x++) {
                        if (ia[x] == 1)
                            break;
                    }
                    if (x > p) {
                        for (x = 1; x <= g; x++) {
                            if (ia[x] == 1)
                                break;
                        }
                        if (x > g) {
                            print("YOU HAVE GIVEN ME INCONSISTENT INFORMATION.\n");
                            print("TRY AGAIN, AND THIS TIME PLEASE BE MORE CAREFUL.\n");
                            for (x = 1; x <= p; x++)
                                ia[x] = 1;
                            print("NOW I GUESS.  THINK OF A COMBINATION.\n");
                            print("HIT RETURN WHEN READY:");
                            str = await input();
                            m = 0;
                            continue;
                        }
                    }
                    g = x;
                }
                // Now we convert guess #g into gs
                for (x = 1; x <= g; x++) {
                    increment_qa();
                }
                convert_qa_hs();
                print("MY GUESS IS: ");
                for (x = 1; x <= p9; x++) {
                    print(hs[x]);
                }
                print("  BLACKS, WHITES ");
                str = await input();
                b1 = parseInt(str);
                w1 = parseInt(str.substr(str.indexOf(",") + 1));
                if (b1 == p9) {
                    print("I GOT IT IN " + m + " MOVES!\n");
                    break;
                }
                initialize_qa();
                for (x = 1; x <= p; x++) {
                    increment_qa();
                    if (ia[x] != 0) {
                        copy_hs();
                        convert_qa();
                        get_number();
                        if (b1 != b || w1 != w)
                            ia[x] = 0;
                    }
                }
            }
            if (m > 10) {
                print("I USED UP ALL MY MOVES!\n");
                print("I GUESS MY CPU I JUST HAVING AN OFF DAY.\n");
            }
            c += m;
            show_score();
        }
        print("GAME OVER\n");
        print("FINAL SCORE:\n");
        show_points();
    }
    
    main();
    
    
    ================================================
    FILE: 60_Mastermind/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 60_Mastermind/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 60_Mastermind/mastermind.bas
    ================================================
    2 PRINT TAB(30);"MASTERMIND"
    4 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    6 PRINT: PRINT: PRINT
    10 REM
    20 REM     MASTERMIND II
    30 REM     STEVE NORTH
    40 REM     CREATIVE COMPUTING
    50 REM     PO BOX 789-M MORRISTOWN NEW JERSEY 07960
    60 REM
    70 REM
    80 INPUT "NUMBER OF COLORS";C9
    90 IF C9>8 THEN PRINT "NO MORE THAN 8, PLEASE!":GOTO 80
    100 INPUT "NUMBER OF POSITIONS";P9
    110 INPUT "NUMBER OF ROUNDS";R9
    120 P=C9^P9
    130 PRINT "TOTAL POSSIBILITIES =";P
    140 H=0:C=0
    150 DIM Q(P9),S(10,2),S$(10),A$(P9),G$(P9),I(P),H$(P9)
    160 L$="BWRGOYPT"
    170 PRINT
    180 PRINT
    190 PRINT "COLOR     LETTER"
    200 PRINT "=====     ======"
    210 FOR X=1 TO C9
    220 READ X$
    230 PRINT X$;TAB(13);MID$(L$,X,1)
    240 NEXT X
    250 PRINT
    260 FOR R=1 TO R9
    270 PRINT
    280 PRINT "ROUND NUMBER";R;"----"
    290 PRINT
    300 PRINT "GUESS MY COMBINATION.":PRINT
    310 REM     GET A COMBINATION
    320 A=INT(P*RND(1)+1)
    330 GOSUB 3000
    340 FOR X=1 TO A
    350 GOSUB 3500
    360 NEXT X
    370 FOR M=1 TO 10
    380 PRINT "MOVE # ";M;" GUESS ";:INPUT X$
    390 IF X$="BOARD" THEN 2000
    400 IF X$="QUIT" THEN 2500
    410 IF LEN(X$)<>P9 THEN PRINT "BAD NUMBER OF POSITIONS.":GOTO 380
    420 REM     UNPACK X$ INTO G$(1-P9)
    430 FOR X=1 TO P9
    440 FOR Y=1 TO C9
    450 IF MID$(X$,X,1)=MID$(L$,Y,1) THEN 480
    460 NEXT Y
    470 PRINT "'"; MID$(X$,X,1); "' IS UNRECOGNIZED.":GOTO 380
    480 G$(X)=MID$(X$,X,1)
    490 NEXT X
    500 REM     NOW WE CONVERT Q(1-P9) INTO A$(1-P9) [ACTUAL GUESS]
    510 GOSUB 4000
    520 REM     AND GET NUMBER OF BLACKS AND WHITES
    530 GOSUB 4500
    540 IF B=P9 THEN 630
    550 REM     TELL HUMAN RESULTS
    560 PRINT "YOU HAVE ";B;" BLACKS AND ";W;" WHITES."
    570 REM     SAVE ALL THIS STUFF FOR BOARD PRINTOUT LATER
    580 S$(M)=X$
    590 S(M,1)=B
    600 S(M,2)=W
    610 NEXT M
    620 PRINT "YOU RAN OUT OF MOVES!  THAT'S ALL YOU GET!":GOTO 640
    622 GOSUB 4000
    623 PRINT "THE ACTUAL COMBINATION WAS: ";
    624 FOR X=1 TO P9
    625 PRINT A$(X);
    626 NEXT X
    627 PRINT
    630 PRINT "YOU GUESSED IT IN ";M;" MOVES!"
    640 H=H+M
    650 GOSUB 5000
    660 REM
    670 REM     NOW COMPUTER GUESSES
    680 REM
    690 FOR X=1 TO P
    700 I(X)=1
    710 NEXT X
    720 PRINT "NOW I GUESS.  THINK OF A COMBINATION."
    730 INPUT "HIT RETURN WHEN READY:";X$
    740 FOR M=1 TO 10
    750 GOSUB 3000
    760 REM     FIND A GUESS
    770 G=INT(P*RND(1)+1)
    780 IF I(G)=1 THEN 890
    790 FOR X=G TO P
    800 IF I(X)=1 THEN 880
    810 NEXT X
    820 FOR X=1 TO G
    830 IF I(X)=1 THEN 880
    840 NEXT X
    850 PRINT "YOU HAVE GIVEN ME INCONSISTENT INFORMATION."
    860 PRINT "TRY AGAIN, AND THIS TIME PLEASE BE MORE CAREFUL."
    870 GOTO 660
    880 G=X
    890 REM     NOW WE CONVERT GUESS #G INTO G$
    900 FOR X=1 TO G
    910 GOSUB 3500
    920 NEXT X
    930 GOSUB 6000
    940 PRINT "MY GUESS IS: ";
    950 FOR X=1 TO P9
    960 PRINT H$(X);
    970 NEXT X
    980 INPUT "  BLACKS, WHITES ";B1,W1
    990 IF B1=P9 THEN 1120
    1000 GOSUB 3000
    1010 FOR X=1 TO P
    1020 GOSUB 3500
    1030 IF I(X)=0 THEN 1070
    1035 GOSUB 6500
    1040 GOSUB 4000
    1050 GOSUB 4500
    1060 IF B1<>B OR W1<>W THEN I(X)=0
    1070 NEXT X
    1080 NEXT M
    1090 PRINT "I USED UP ALL MY MOVES!"
    1100 PRINT "I GUESS MY CPU IS JUST HAVING AN OFF DAY."
    1110 GOTO 1130
    1120 PRINT "I GOT IT IN ";M;" MOVES!"
    1130 C=C+M
    1140 GOSUB 5000
    1150 NEXT R
    1160 PRINT "GAME OVER"
    1170 PRINT "FINAL SCORE:"
    1180 GOSUB 5040
    1190 STOP
    2000 REM
    2010 REM     BOARD PRINTOUT ROUTINE
    2020 REM
    2025 PRINT
    2030 PRINT "BOARD"
    2040 PRINT "MOVE     GUESS          BLACK     WHITE"
    2050 FOR Z=1 TO M-1
    2060 PRINT Z;TAB(9);S$(Z);TAB(25);S(Z,1);TAB(35);S(Z,2)
    2070 NEXT Z
    2075 PRINT
    2080 GOTO 380
    2500 REM
    2510 REM     QUIT ROUTINE
    2520 REM
    2530 PRINT "QUITTER!  MY COMBINATION WAS: ";
    2535 GOSUB 4000
    2540 FOR X=1 TO P9
    2550 PRINT A$(X);
    2560 NEXT X
    2565 PRINT
    2570 PRINT "GOOD BYE"
    2580 STOP
    3000 REM
    3010 REM     INITIALIZE Q(1-P9) TO ZEROS
    3020 REM
    3030 FOR S=1 TO P9
    3040 Q(S)=0
    3050 NEXT S
    3060 RETURN
    3500 REM
    3510 REM     INCREMENT Q(1-P9)
    3520 REM
    3522 IF Q(1)>0 THEN 3530
    3524 REM  IF ZERO, THIS IS OUR FIRST INCREMENT: MAKE ALL ONES
    3526 FOR S=1 TO P9
    3527 Q(S)=1
    3528 NEXT S
    3529 RETURN
    3530 Q=1
    3540 Q(Q)=Q(Q)+1
    3550 IF Q(Q)<=C9 THEN RETURN
    3560 Q(Q)=1
    3570 Q=Q+1
    3580 GOTO 3540
    4000 REM
    4010 REM     CONVERT Q(1-P9) TO A$(1-P9)
    4020 REM
    4030 FOR S=1 TO P9
    4040 A$(S)=MID$(L$,Q(S),1)
    4050 NEXT S
    4060 RETURN
    4500 REM
    4510 REM     GET NUMBER OF BLACKS (B) AND WHITES (W)
    4520 REM     MASHES G$ AND A$ IN THE PROCESS
    4530 REM
    4540 B=0:W=0:F=0
    4550 FOR S=1 TO P9
    4560 IF G$(S)<>A$(S) THEN 4620
    4570 B=B+1
    4580 G$(S)=CHR$(F)
    4590 A$(S)=CHR$(F+1)
    4600 F=F+2
    4610 GOTO 4660
    4620 FOR T=1 TO P9
    4630 IF G$(S)<>A$(T) THEN 4650
    4640 IF G$(T)=A$(T) THEN 4650
    4645 W=W+1:A$(T)=CHR$(F):G$(S)=CHR$(F+1):F=F+2:GOTO 4660
    4650 NEXT T
    4660 NEXT S
    4670 RETURN
    5000 REM
    5010 REM     PRINT SCORE
    5020 REM
    5030 PRINT "SCORE:"
    5040 PRINT "     COMPUTER ";C
    5050 PRINT "     HUMAN    ";H
    5060 PRINT
    5070 RETURN
    5500 REM
    5510 REM     CONVERT Q(1-P9) INTO G$(1-P9)
    5520 REM
    5530 FOR S=1 TO P9
    5540 G$(S)=MID$(L$,Q(S),1)
    5550 NEXT S
    5560 RETURN
    6000 REM
    6010 REM     CONVERT Q(1-P9) TO H$(1-P9)
    6020 REM
    6030 FOR S=1 TO P9
    6040 H$(S)=MID$(L$,Q(S),1)
    6050 NEXT S
    6060 RETURN
    6500 REM
    6510 REM     COPY H$ INTO G$
    6520 REM
    6530 FOR S=1 TO P9
    6540 G$(S)=H$(S)
    6550 NEXT S
    6560 RETURN
    8000 REM     PROGRAM DATA FOR COLOR NAMES
    8010 DATA BLACK,WHITE,RED,GREEN,ORANGE,YELLOW,PURPLE,TAN
    9998 REM   ...WE'RE SORRY BUT IT'S TIME TO GO...
    9999 END
    
    
    ================================================
    FILE: 60_Mastermind/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    This is pretty much a re-implementation of the BASIC, taking advantage
    of Perl's array functionality and working directly with the alphabetic
    color codes.
    
    
    ================================================
    FILE: 60_Mastermind/perl/mastermind.pl
    ================================================
    #!/usr/bin/env perl
    
    use 5.010;      # To get 'state' and 'say'
    
    use strict;     # Require explicit declaration of variables
    use warnings;   # Enable optional compiler warnings
    
    use English;    # Use more friendly names for Perl's magic variables
    use List::Util qw{ min sum };   # Convenient list utilities
    use Term::ReadLine;     # Prompt and return user input
    
    our $VERSION = '0.000_01';
    
    use constant MAX_GUESSES    => 10;
    
    print <<'EOD';
                                   MASTERMIND
                   Creative Computing  Morristown, New Jersey
    
    
    EOD
    
    =begin comment
    
         MASTERMIND II
         STEVE NORTH
         CREATIVE COMPUTING
         PO BOX 789-M MORRISTOWN NEW JERSEY 07960
    
    =end comment
    
    =cut
    
    # NOTE that mixed-case 'my' variables are 'global' in the sense that
    # they are used in subroutines, but not passed to them.
    
    say '';
    
    my $number_of_colors = get_input(
        'Number of colors [1-8]: ',
        sub { m/ \A [1-8] \z /smx },
        "No more than 8, please!\n",
    );
    
    say '';
    
    my $Number_of_Positions = get_input(
        'Number of positions: ',
        sub { m/ \A [0-9]+ \z /smx && $ARG },
        "A positive number, please\n",
    );
    
    say '';
    
    my $number_of_rounds = get_input(
        'Number of rounds: ',
        sub { m/ \A [0-9]+ \z /smx && $ARG },
        "A positive number, please\n",
    );
    
    my $P = $number_of_colors ** $Number_of_Positions;
    say 'Total possibilities = ', $P;
    
    my @colors = ( qw{
        Black White Red Green Orange Yellow Purple Tan
    })[ 0 .. $number_of_colors - 1 ];
    my @Color_Codes = map { uc substr $ARG, 0, 1 } @colors;
    
    print <<'EOD';
    
    
    Color        Letter
    =====        ======
    EOD
    
    foreach my $inx ( 0 .. $#colors ) {
        printf "%-13s%s\n", $colors[$inx], $Color_Codes[$inx];
    }
    
    say '';
    
    my $computer_score = 0;  # Computer score
    my $human_score = 0;  # Human score
    
    foreach my $round_number ( 1 .. $number_of_rounds ) {
    
        print <<"EOD";
    
    Round number $round_number ----
    
    Guess my combination.
    
    EOD
    
        $human_score += human_guesses( $Number_of_Positions );
    
        print_score( $computer_score, $human_score );
    
        $computer_score += computer_guesses();
    
        print_score( $computer_score, $human_score );
    
    }
    
    # Make a $pattern into a hash with one key for each possible color. The
    # value for each color is the number of times it appears in the pattern.
    sub hashify_pattern {
        my $pattern = uc $ARG[0];
        my %p = map { $ARG => 0 } @Color_Codes;
        $p{$ARG}++ for split qr//, $pattern;
        return \%p;
    }
    
    # Given a $pattern, a $guess at that pattern, and $black and $white
    # scores, return a true value if the $black and $white scores of the
    # $guess are those supplied as arguments; otherwise return a false
    # value. This is used by computer_guesses() to eliminate possibilities.
    sub analyze_black_white {
        my ( $pattern, $guess, $black, $white ) = @ARG;
        my $info = analyze_guess( $pattern, $guess );
        return $info->{black} == $black && $info->{white} == $white;
    }
    
    # Given a $pattern and a $guess at that pattern, return a reference to a
    # hash with the following keys:
    #  {guess} is the guess;
    #  {black} is the black score of the guess
    #  {white} is the white score of the guess
    sub analyze_guess {
        my ( $pattern, $guess ) = @ARG;
        my $pattern_hash = hashify_pattern( $pattern );
        my $guess_hash = hashify_pattern( $guess );
        my $white = sum(
            map { min( $pattern_hash->{$ARG}, $guess_hash->{$ARG} ) } @Color_Codes,
        );
        my $black = 0;
        foreach my $inx ( 0 .. length( $pattern ) - 1 ) {
            if ( substr( $pattern, $inx, 1 ) eq substr( $guess, $inx, 1 ) )
            {
                $black++;
                --$white;
            }
        }
        return +{
            guess   => $guess,
            black   => $black,
            white   => $white,
        }
    }
    
    # Used by the computer to guess the human's choice. The return is the
    # number of guesses the computer took. The return is the maximum plus
    # one if the computer failed to guess.
    sub computer_guesses {
    
        print <<'EOD';
    
    Now I guess. Think of a combination.
    EOD
        get_input(
            'Hit  when ready:',
        );
    
        # Generate all possible permutations.
        my @possible;
        foreach my $permutation ( 0 .. @Color_Codes ** $Number_of_Positions - 1 ) {
            my $guess;
            for ( 1 .. $Number_of_Positions ) {
                my $inx = $permutation % @Color_Codes;
                $guess .= $Color_Codes[ $inx ];
                $permutation = int( $permutation / @Color_Codes );
            }
            push @possible, $guess;
        }
    
        # Guess ...
        foreach my $guess_num ( 1 .. MAX_GUESSES ) {
    
            # Guess a possible permutation at random, removing it from the
            # list.
            my $guess = splice @possible, int rand @possible, 1;
            say 'My guess is: ', $guess;
    
            # Find out its black/white score.
            my ( $black, $white ) = split qr< , >smx, get_input(
                'Blacks, Whites: ',
                sub { m/ \A [0-9]+ , [0-9]+ \z /smx },
                "Please enter two unsigned integers\n",
            );
    
            # If it's all black, the computer wins.
            if ( $black == $Number_of_Positions ) {
                say "I got it in $guess_num moves!";
                return $guess_num;
            }
    
            # Eliminate all possible permutations that give the black/white
            # score that our guess got. If there are any left, take another
            # guess.
            next if @possible = grep { analyze_black_white( $ARG, $guess, $black,
                $white ) } @possible;
    
            # There were no permutations left. Complain.
            print <<'EOD';
    You have given me inconsistent information.
    Try again, and this time please be more careful.
    EOD
    
            goto &computer_guesses; # Tail-call ourselves to try again.
        }
    
        print <<'EOD';
    I used up all my moves!
    I guess my CPU is just having an off day.
    EOD
    
        return MAX_GUESSES + 1;
    }
    
    # Used to generate a pattern and process the human's guesses. The return
    # is the number of guesses the human took. The return is the maximum
    # plus one if the human failed to guess.
    sub human_guesses {
    
        my @saved_moves;  # Saved moves
        my $pattern = uc join '',
            map { $Color_Codes[ rand @Color_Codes ] } 1 .. $Number_of_Positions;
    
        foreach my $guess_num ( 1 .. MAX_GUESSES ) {
    
            my $guess = uc get_input(
                "Move # $guess_num guess: ",
                sub {
    
                    # If the user entered 'quit', bail out.
                    if ( m/ \A quit \z /smxi ) {
                        die "Quitter!  My combination was $pattern\n\nGood bye\n";
                    }
    
                    # If the user entered 'board', display the board so far.
                    # We return success to prevent the warning message, but
                    # we also clear $ARG. The caller's caller sees this and
                    # re-queries.
                    if ( m/ \A board \z /smxi ) {
                        print <<'EOD';
    
    Board
    Move     Guess          Black     White
    EOD
                        my $number = 1;
                        foreach my $item ( @saved_moves ) {
                            printf "%4d     %-13s  %3d       %3d\n", $number++,
                            @{ $item }{ qw{ guess black white } };
                        }
                        return undef;   # Validation failure, but suppress warning.
                    }
    
                    # End of special-case code. Below here we are dealing
                    # with guess input.
    
                    # The length of the input must equal the number of
                    # positions.
                    if ( $Number_of_Positions != length ) {
                        warn "Bad number of positions\n";
                        return 0;
                    }
    
                    # The input may contain only valid color codes.
                    state $invalid_color = do { # Evaluated only once
                        local $LIST_SEPARATOR = '';
                        qr< [^@Color_Codes] >smxi;
                    };
                    if ( m/ ( $invalid_color ) /smxi ) {
                        warn "'$1' is unrecognized.\n";
                        return 0;
                    }
    
                    # We're good.
                    return 1;
                },
                 "Please enter 'board', 'quit', or any $Number_of_Positions of @{[
                     join ', ', map { qq<'$ARG'> } @Color_Codes ]}.\n",
            );
    
            my $rslt = analyze_guess( $pattern, $guess );
    
            push @saved_moves, $rslt;
    
            if ( $rslt->{black} == $Number_of_Positions ) {
                say "You guessed it in $guess_num moves.";
                return $guess_num;
            }
    
            say "You have $rslt->{black} blacks and $rslt->{white} whites.";
    
        }
    
        print <<"EOD";
    You ran out of moves.  That's all you get.
    
    The actual combination was: $pattern
    EOD
    
        return MAX_GUESSES + 1;
    }
    
    # Print the $computer and $human score
    sub print_score {
        my ( $computer, $human ) = @ARG;
        print <<"EOD";
    Score:
         Computer: $computer
            Human: $human
    EOD
        return;
    }
    
    # Get input from the user. The arguments are:
    # * The prompt
    # * A reference to validation code. This code receives the response in
    #   $ARG and returns true for a valid response.
    # * A warning to print if the response is not valid. This must end in a
    #   return. It is suppressed if the validation code returned undef.
    # The first valid response is returned. An end-of-file terminates the
    # script.
    sub get_input {
        my ( $prompt, $validate, $warning ) = @ARG;
    
        # If no validator is passed, default to one that always returns
        # true.
        $validate ||= sub { 1 };
    
        # Create the readline object. The 'state' causes the variable to be
        # initialized only once, no matter how many times this subroutine is
        # called. The do { ... } is a compound statement used because we
        # need to tweak the created object before we store it.
        state $term = do {
            my $obj = Term::ReadLine->new( 'reverse' );
            $obj->ornaments( 0 );
            $obj;
        };
    
        while ( 1 ) {   # Iterate indefinitely
    
            # Read the input into the topic variable, localized to prevent
            # Spooky Action at a Distance. We exit on undef, which signals
            # end-of-file.
            exit unless defined( local $ARG = $term->readline( $prompt ) );
    
            # Return the input if it is valid.
            return $ARG if my $rslt = $validate->();
    
            # Issue the warning, and go around the merry-go-round again.
            warn $warning if defined $rslt;
        }
    }
    
    # NOTE the following is unused, but left in place in case someone wants
    # to add a 'Do you want instructions?'
    #
    # Get a yes-or-no answer. The argument is the prompt, which will have
    # '? [y/n]: ' appended. The donkey work is done by get_input(), which is
    # requested to validate the response as beginning with 'y' or 'n',
    # case-insensitive. The return is a true value for 'y' and a false value
    # for 'n'.
    sub get_yes_no {
        my ( $prompt ) = @ARG;
        state $map_answer = {
            n   => 0,
            y   => 1,
        };
        my $resp = lc get_input(
            "$prompt? [y/n]: ",
            sub { m/ \A [yn] /smxi },
            "Please respond 'y' or 'n'\n",
        );
        return $map_answer->{ substr $resp, 0, 1 };
    }
    
    __END__
    
    =head1 TITLE
    
    mastermind - Play the game 'Mastermind' from Basic Computer Games
    
    =head1 SYNOPSIS
    
     mastermind.pl
    
    =head1 DETAILS
    
    This Perl script is a port of mastermind, which is the 60th
    entry in Basic Computer Games.
    
    This is pretty much a re-implementation of the BASIC, taking advantage
    of Perl's array functionality and working directly with the alphabetic
    color codes.
    
    =head1 PORTED BY
    
    Thomas R. Wyant, III F
    
    =head1 COPYRIGHT AND LICENSE
    
    Copyright (C) 2022 by Thomas R. Wyant, III
    
    This program is free software; you can redistribute it and/or modify it
    under the same terms as Perl 5.10.0. For more details, see the Artistic
    License 1.0 at
    L, and/or the
    Gnu GPL at L.
    
    This program is distributed in the hope that it will be useful, but
    without any warranty; without even the implied warranty of
    merchantability or fitness for a particular purpose.
    
    =cut
    
    # ex: set expandtab tabstop=4 textwidth=72 :
    
    
    ================================================
    FILE: 60_Mastermind/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 60_Mastermind/python/mastermind.py
    ================================================
    import random
    import sys
    from typing import List, Union, Tuple
    
    
    #  define some parameters for the game which should not be modified.
    def setup_game() -> Tuple[int, int, int, int]:
        print("""
                                      MASTERMIND
                       CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY
    
    
    
        """)
        # get user inputs for game conditions
        num_colors: int = len(COLOR_LETTERS) + 1
        while num_colors > len(COLOR_LETTERS):
            num_colors = int(input("Number of colors (max 8): "))  # C9 in BASIC
        num_positions = int(input("Number of positions: "))  # P9 in BASIC
        num_rounds = int(input("Number of rounds: "))  # R9 in BASIC
        possibilities = num_colors**num_positions
    
        print(f"Number of possibilities {possibilities}")
        print("Color\tLetter")
        print("=====\t======")
        for element in range(0, num_colors):
            print(f"{COLORS[element]}\t{COLORS[element][0]}")
        return num_colors, num_positions, num_rounds, possibilities
    
    
    # Global variables
    COLORS = ["BLACK", "WHITE", "RED", "GREEN", "ORANGE", "YELLOW", "PURPLE", "TAN"]
    COLOR_LETTERS = "BWRGOYPT"
    NUM_COLORS, NUM_POSITIONS, NUM_ROUNDS, POSSIBILITIES = setup_game()
    human_score = 0
    computer_score = 0
    
    
    def main() -> None:
        current_round = 1
        while current_round <= NUM_ROUNDS:
            print(f"Round number {current_round}")
            human_turn()
            computer_turn()
            current_round += 1
        print_score(is_final_score=True)
        sys.exit()
    
    
    def human_turn() -> None:
        global human_score
        num_moves = 1
        guesses: List[List[Union[str, int]]] = []
        print("Guess my combination ...")
        secret_combination = int(POSSIBILITIES * random.random())
        answer = possibility_to_color_code(secret_combination)
        while True:
            print(f"Move # {num_moves} Guess : ")
            user_command = input("Guess ")
            if user_command == "BOARD":
                print_board(guesses)  # 2000
            elif user_command == "QUIT":  # 2500
                print(f"QUITTER! MY COMBINATION WAS: {answer}")
                print("GOOD BYE")
                quit()
            elif len(user_command) != NUM_POSITIONS:  # 410
                print("BAD NUMBER OF POSITIONS")
            else:
                invalid_letters = get_invalid_letters(user_command)
                if invalid_letters > "":
                    print(f"INVALID GUESS: {invalid_letters}")
                else:
                    guess_results = compare_two_positions(user_command, answer)
                    if guess_results[1] == NUM_POSITIONS:  # correct guess
                        print(f"You guessed it in {num_moves} moves!")
                        human_score = human_score + num_moves
                        print_score()
                        return  # from human turn, triumphant
                    else:
                        print(f"You have {guess_results[1]} blacks and {guess_results[2]} whites")
                        guesses.append(guess_results)
                        num_moves += 1
    
            if num_moves > 10:  # RAN OUT OF MOVES
                print("YOU RAN OUT OF MOVES! THAT'S ALL YOU GET!")
                print(f"THE ACTUAL COMBINATION WAS: {answer}")
                human_score = human_score + num_moves
                print_score()
                return  # from human turn, defeated
    
    
    def computer_turn() -> None:
        global computer_score
        while True:
            all_possibilities = [1] * POSSIBILITIES
            num_moves = 1
            print("NOW I GUESS. THINK OF A COMBINATION.")
            input("HIT RETURN WHEN READY: ")
            while True:
                possible_guess = find_first_solution_of(all_possibilities)
                if possible_guess < 0:  # no solutions left :(
                    print("YOU HAVE GIVEN ME INCONSISTENT INFORMATION.")
                    print("TRY AGAIN, AND THIS TIME PLEASE BE MORE CAREFUL.")
                    break  # out of inner while loop, restart computer turn
    
                computer_guess = possibility_to_color_code(possible_guess)
                print(f"My guess is: {computer_guess}")
                blacks_str, whites_str = input(
                    "ENTER BLACKS, WHITES (e.g. 1,2): "
                ).split(",")
                blacks = int(blacks_str)
                whites = int(whites_str)
                if blacks == NUM_POSITIONS:  # Correct guess
                    print(f"I GOT IT IN {num_moves} MOVES")
                    computer_score = computer_score + num_moves
                    print_score()
                    return  # from computer turn
    
                # computer guessed wrong, deduce which solutions to eliminate.
                for i in range(0, POSSIBILITIES):
                    if all_possibilities[i] == 0:  # already ruled out
                        continue
                    possible_answer = possibility_to_color_code(i)
                    comparison = compare_two_positions(
                        possible_answer, computer_guess
                    )
                    if (blacks != comparison[1]) or (whites != comparison[2]):
                        all_possibilities[i] = 0
    
                if num_moves == 10:
                    print("I USED UP ALL MY MOVES!")
                    print("I GUESS MY CPU IS JUST HAVING AN OFF DAY.")
                    computer_score = computer_score + num_moves
                    print_score()
                    return  # from computer turn, defeated.
                num_moves += 1
    
    
    def find_first_solution_of(all_possibilities: List[int]) -> int:
        """Scan through all_possibilities for first remaining non-zero marker,
        starting from some random position and wrapping around if needed.
        If not found return -1."""
        start = int(POSSIBILITIES * random.random())
        for i in range(0, POSSIBILITIES):
            solution = (i + start) % POSSIBILITIES
            if all_possibilities[solution]:
                return solution
        return -1
    
    
    # 470
    def get_invalid_letters(user_command) -> str:
        """Makes sure player input consists of valid colors for selected game configuration."""
        valid_colors = COLOR_LETTERS[:NUM_COLORS]
        invalid_letters = ""
        for letter in user_command:
            if letter not in valid_colors:
                invalid_letters = invalid_letters + letter
        return invalid_letters
    
    
    # 2000
    def print_board(guesses) -> None:
        """Print previous guesses within the round."""
        print("Board")
        print("Move\tGuess\tBlack White")
        for idx, guess in enumerate(guesses):
            print(f"{idx + 1}\t{guess[0]}\t{guess[1]}     {guess[2]}")
    
    
    def possibility_to_color_code(possibility: int) -> str:
        """Accepts a (decimal) number representing one permutation in the realm of
        possible secret codes and returns the color code mapped to that permutation.
        This algorithm is essentially converting a decimal  number to a number with
        a base of #num_colors, where each color code letter represents a digit in
        that #num_colors base."""
        color_code: str = ""
        pos: int = NUM_COLORS ** NUM_POSITIONS  # start with total possibilities
        remainder = possibility
        for _ in range(NUM_POSITIONS - 1, 0, -1):  # process all but the last digit
            pos = pos // NUM_COLORS
            color_code += COLOR_LETTERS[remainder // pos]
            remainder = remainder % pos
        color_code += COLOR_LETTERS[remainder]  # last digit is what remains
        return color_code
    
    
    # 4500
    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)."""
        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 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:]
                        increment = increment + 2
            else:  # correct color and placement
                blacks = blacks + 1
                # THIS IS DEVIOUSLY CLEVER
                guess = guess[:pos] + chr(increment + 1) + guess[pos + 1:]
                answer = answer[:pos] + chr(increment) + answer[pos + 1:]
                increment = increment + 2
        return [initial_guess, blacks, whites]
    
    
    # 5000 + logic from 1160
    def print_score(is_final_score: bool = False) -> None:
        """Print score after each turn ends, including final score at end of game."""
        if is_final_score:
            print("GAME OVER")
            print("FINAL SCORE:")
        else:
            print("SCORE:")
        print(f"     COMPUTER {computer_score}")
        print(f"     HUMAN    {human_score}")
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 60_Mastermind/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 60_Mastermind/rust/Mastermind/Cargo.toml
    ================================================
    [package]
    name = "mastermind"
    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"
    
    
    ================================================
    FILE: 60_Mastermind/rust/Mastermind/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Rust](https://www.rust-lang.org/) by Anthony Rubick [AnthonyMichaelTDM](https://github.com/AnthonyMichaelTDM)
    
    
    ================================================
    FILE: 60_Mastermind/rust/Mastermind/src/main.rs
    ================================================
    use rand::{Rng, prelude::{thread_rng, ThreadRng}};
    use std::{io, fmt::Display, str::FromStr};
    
    //DATA
    const COLORS: [&str;8] = ["Black ", "White ","Red ","Green ","Orange ","Yellow ", "Purple ", "Tan "]; //all available colors
    const LETTERS: &str = "BWRGOYPT"; //letters representing the above colors
    
    struct CODE {
        code: Vec, //maybe use a char array later, idk
    
    }
    impl CODE {
        /**
         * create generic, empty code
         */
        fn new() -> CODE {
            return CODE{code: Vec::new()};
        }
        /**
         * generates and returns a random CODE with the given parameters
         */
        fn new_random(rng: &mut ThreadRng, num_colors: usize, num_positions: usize) -> CODE {
            //data
            let mut code = CODE{code: Vec::new()};
            //generate random combination of colors
            for _i in 0..num_positions {
                code.code.push(rng.gen_range(0..num_colors));
            }
            return code;
        }
        /**
         * converts input_int from base 10 to base num_colors to generate the code
         * input_int must be between 0 and num_colors.pow(num_positions)
         */
        fn new_from_int(mut input_int: usize, num_colors: usize, num_positions: usize) -> CODE {
            //DATA
            let mut converted_number:Vec<_> = Vec::new();
            assert!(2 <= num_colors && num_colors <= 36); //if num_colors is outside of this range, things break later on
    
            //convert input_int into a code by effectively converting input_int from base 10 to base n where n is num_colors, uses some fancy stuff to do this
            loop {
                converted_number.push(std::char::from_digit((input_int % num_colors) as u32, num_colors as u32).unwrap()); //
                input_int /= num_colors;
                if input_int == 0 {break}
            }
    
            while converted_number.len() < num_positions {converted_number.push('0');} // fill remaining space with zero's
            let converted_number: Vec<_> = converted_number.iter().rev().map(|e| e.to_digit(num_colors as u32).unwrap() as usize).collect(); //reverse the vector and convert it to integers
            return CODE{code: converted_number};
        }
        /**
         * returns a code parsed from the passed string
         */
        fn new_from_string(input_string: String, num_colors: usize) -> Option {
            let valid_chars = &LETTERS[0..num_colors];
            //DATA
            let new_code = CODE{
                code:
                input_string.to_ascii_uppercase().chars() //get an iterator with all the chars in input string converted to uppercase
                .filter( |c| { valid_chars.contains(*c)}) //remove chars that aren't in LETTERS
                .map( |x| -> usize {valid_chars.find(x).expect("invalid character")})//convert all the chars into usizes representing their index in LETTERS
                .collect() //wrap this iterator up into a vector
            };
            //if code is empty, return None, otherwise return Some(code)
            if new_code.code.is_empty() {return None;}
            else {return Some(new_code);}
        }
    
        /**
         * returns a string containing the code represented as characters
         */
        fn _as_human_readible_chars(&self) -> String {
            return self.code.iter().map(|i|->char{LETTERS.chars().nth(*i).expect("index out of bounds")}).collect();
        }
        /**
         * returns a string containing the code represented as words
         */
        fn _as_human_readible_words(&self) -> String {
            return self.code.iter().map(|i|->&str{COLORS.iter().nth(*i).expect("index out of bounds")}).collect();
        }
    }
    struct GUESS {
        code: CODE,
        blacks: usize,
        whites: usize,
    }
    impl GUESS {
        /**
         * create a new guess, and evaluate it
         */
        fn new(code: CODE) -> GUESS {
            return GUESS{code:code, blacks:0,whites:0 };
        }
    
        /**
         * evaulates itself for the number of black and white pegs it should have for a given answer
         */
        fn evaluate(&mut self, answer:&CODE) {
            //data
            let mut consumed = vec![false;answer.code.len()];
    
            if self.code.code.len() != answer.code.len() {
                panic!("only codes of the same length can be compared");
            }
    
            for i in 0..answer.code.len() {
                if self.code.code[i] == answer.code[i] { //correct value correct place
                    self.blacks += 1;
                    consumed[i] = true;
                }
                else {
                    //check for correct value incorrect place, don't count positions that are already exact matches
                    for j in 0..answer.code.len() {
                        if !consumed[j] && self.code.code[i] == answer.code[j] && self.code.code[j] != answer.code[j] {
                            self.whites += 1;
                            consumed[j] = true;
                            break;
                        }
                    }
                }
            }
        }
    }
    
    fn main() {
        //DATA
        let mut rng = thread_rng();
        let num_colors: usize;
        let num_positions: usize;
        let num_rounds: usize;
        let num_guesses: usize;
        let total_posibilities: usize;
    
        let mut human_score: usize = 0;
        let mut computer_score: usize = 0;
    
        //print welcome message
        welcome();
    
        //ask user for a number of colors, positions, and rounds
        num_colors = get_number_from_user_input("NUMBER OF COLORS", "", 1, COLORS.len());
        num_positions = get_number_from_user_input("NUMBER OF POSITIONS", "", 2, 10);
        num_rounds = get_number_from_user_input("NUMBER OF ROUNDS", "", 1, 10);
        num_guesses = get_number_from_user_input("NUMBER OF GUESSES", "", 10, 0);
    
    
        //print number of posibilities
        total_posibilities = num_colors.pow(num_positions as u32);
        println!("\nTOTAL POSSIBILITIES = {}\n", total_posibilities);
    
        //print color letter table
        print_color_letter_table(num_colors);
    
        //game loop
        for round_num in 1..=num_rounds {
            //data
            let mut num_moves: usize = 1;
            let mut answer: CODE;
            let mut guess: GUESS;
            let mut guesses: Vec = Vec::new();
            let mut all_possibilities = vec![true; total_posibilities];
    
    
            //print round number
            println!("\n\nROUND NUMBER: {}", round_num);
    
            //human player is code-breaker, computer is code-maker
            //generate a combination
            answer = CODE::new_random(&mut rng, num_colors, num_positions);
            //println!("CODE: {:?}", answer._as_human_readible_chars()); //this is for troubleshooting, prints the code converted back into characters
    
            //round loop
            loop {
                //loop condition
                if num_moves > num_guesses {
                    println!("YOU RAN OUT OF MOVES!  THAT'S ALL YOU GET!");
                    println!("THE ACTUAL COMBINATION WAS: {}", answer._as_human_readible_chars());
                    human_score += num_moves;
                    print_scores(human_score,computer_score);
                    break;
                }
    
                //input loop
                guess = GUESS::new(loop {
                    println!("\nMOVE # {} GUESS: ", num_moves);
    
                    //get player move
                    let mut raw_input = String::new(); //temp variable to store user input
                    io::stdin().read_line(&mut raw_input).expect("CANNOT READ INPUT!"); //read user input from standard input and store it to raw_input
    
                    //attempt to parse input
                    if raw_input.trim().eq_ignore_ascii_case("board") {
                        //print the board state
                        print_board(&guesses);
                        continue; //run loop again
                    }
                    else if raw_input.trim().eq_ignore_ascii_case("quit") {
                        //quit the game
                        println!("QUITTER!  MY COMBINATION WAS: {}\nGOOD BYE", answer._as_human_readible_words());
                        return; //exit the game
                    }
                    else {
                        //parse input for a code
                        match CODE::new_from_string(raw_input, num_colors) {
                            Some(code) => {
                                //ensure code is correct length
                                if code.code.len() != num_positions { // if not
                                    println!("BAD NUMBER OF POSITIONS.");
                                    continue; //run loop again
                                }
                                else {break code;}//break with the code
                            },
                            None => continue, //run loop again
                        }
                    }
                });
    
                //evaluate guess
                guess.evaluate(&answer);
                let blacks = guess.blacks;
                let whites = guess.whites;
                //add guess to the list of guesses
                guesses.push(guess);
    
                //tell human the results
                if blacks >= num_positions { //guessed it correctly
                    println!("YOU GUESSED IT IN {} MOVES!", num_moves);
                    human_score += num_moves;
                    print_scores(human_score,computer_score);
                    break; //break from loop
                } else { //didn't
                    println!("YOU HAVE {} BLACKS AND {} WHITES.", blacks, whites);
                    //increment moves
                    num_moves += 1;
                }
            }
    
            //computer is code-breaker, human player is code-maker
            println!("\nNOW I GUESS.  THINK OF A COMBINATION.\nHIT RETURN WHEN READY: ");
            //prompt user to give a valid combination #730
            //input loop
            answer = loop {
                let mut raw_input = String::new(); //temp variable to store user input
                io::stdin().read_line(&mut raw_input).expect("CANNOT READ INPUT!"); //read user input from standard input and store it to raw_input
    
                //attempt to create a code from the user input, if successful break the loop returning the code
                if let Some(code) = CODE::new_from_string(raw_input, num_colors) {
                    if code.code.len() == num_positions {break code;} //exit loop with code
                    else {println!("CODE MUST HAVE {} POSITIONS", num_positions);continue;} //tell them to try again
                }
    
                println!("INVALID CODE.  TRY AGAIN"); //if unsuccessful, this is printed and the loop runs again
            };
    
            //reset some things in preparation for computer play
            guesses.clear();
            num_moves = 0;
            //let num_colors = *answer.code.iter().max().unwrap(); //figure out the number of colors from the code | Commented bc we're enforcing that the computer cracks the same size code as the human
            //let num_positions = answer.code.len(); //figure out the number of positions from the code | Commented bc we're enforcing that the computer cracks the same size code as the human
    
            //round loop
            loop {
                //loop condition
                if num_moves > num_guesses {
                    println!("I USED UP ALL MY MOVES!");
                    println!("I GUESS MY CPU IS JUST HAVING AN OFF DAY.");
                    computer_score += num_moves;
                    print_scores(human_score,computer_score);
                    break;
                }
    
                //randomly generate a guess //770
                let mut guess_int = rng.gen_range(0..total_posibilities);
                guess = GUESS::new(CODE::new());
                //if it's possible, use it //780
                if all_possibilities[guess_int] {
                    guess = GUESS::new(CODE::new_from_int(guess_int, num_colors, num_positions)); //create guess
                }
                else {//if it's not possible:
                    //  search all possibilities after guess, use first valid one //790
                    for g in guess_int..total_posibilities {
                        if all_possibilities[g] {
                            guess_int=g;
                            guess = GUESS::new(CODE::new_from_int(guess_int, num_colors, num_positions)); //create guess
                            break;
                        }
                    }
                    //if none was found
                    //  search all possibilities before guess, use first valid one //820
                    if guess.code.code.is_empty() {
                        for g in (0..guess_int).rev() {
                            if all_possibilities[g] {
                                guess_int=g;
                                guess = GUESS::new(CODE::new_from_int(guess_int, num_colors, num_positions)); //create guess
                                break;
                            }
                        }
                    }
                    // if none where found, tell the user and start over #850
                    if guess.code.code.is_empty() {
                        println!("YOU HAVE GIVEN ME INCONSISTENT INFORMATION.");
                        println!("PLAY AGAIN, AND THIS TIME PLEASE BE MORE CAREFUL.");
                        return; //exit game
                    };
                }
    
                //convert guess into something readible #890
                //print it #940
                println!("MY GUESS IS: {}", guess.code._as_human_readible_chars());
                //ask user for feedback, #980
                let blacks=get_number_from_user_input("BLACKS: ", "", 0, num_positions);
                let whites=get_number_from_user_input("WHITES: ", "", 0, num_positions);
    
                //if we got it, end #990
                if blacks >= num_positions { //guessed it correctly
                    println!("I GOT IT IN {} MOVES!", num_moves);
                    computer_score += num_moves;
                    print_scores(human_score,computer_score);
                    break; //break from loop
                } else { //didn't
                    all_possibilities[guess_int] = false;
                    //if we didn't, eliminate the combinations that don't work
                    //we know the number of black and white pegs for a valid answer, so eleminate all that get different amounts
                    all_possibilities.iter_mut().enumerate().for_each(|b| {
                        if *b.1 { //filter out ones we already know aren't possible
                            let mut tmp_guess = GUESS::new(CODE::new_from_int(b.0, num_colors, num_positions));
                            tmp_guess.evaluate(&guess.code); //compare with computer guess
                            if blacks != tmp_guess.blacks || whites != tmp_guess.whites { //if number of blacks/whites is different, set it to false
                                *b.1 = false;
                            }
                        }
                    });
    
                    //increment moves
                    num_moves += 1;
                }
    
    
            }
    
        }
    }
    
    /**
     * print the welcome message
     */
    fn welcome() {
        println!("
                                 MASTERMIND
                  CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY
    
    
        ");
    }
    
    /**
     * print scores
     */
    fn print_scores(human_score:usize, computer_score:usize) {
        println!("SCORE\n\tCOMPUTER: {}\n\tHUMAN: {}", computer_score, human_score);
    }
    
    /**
     * print the color - letter table
     * only prints the first num_colors pairs
     */
    fn print_color_letter_table(num_colors: usize) {
        println!("COLOR\tLETTER");
        println!("=====\t======");
        for i in 0..num_colors {
            println!("{}\t{}", COLORS[i], &LETTERS[i..i+1]);
        }
    }
    
    fn print_board(guesses: &Vec) {
        println!("BOARD");
        println!("MOVE\tGUESS\t\tBLACK\tWhite");
        for guess in guesses.iter().enumerate() {
            println!("{}\t{}\t\t{}\t{}", guess.0,guess.1.code._as_human_readible_chars(),guess.1.blacks,guess.1.whites);
        }
    }
    
    /**
     * gets a number from user input
     * pass an empty &str for error_message if you don't want one printed
     * pass a min lower  than the max to have minimun and maximun bounds
     * pass a min higher than the max to only have a minumum bound
     * pass a min equal   to  the max to only have a maximun bound
     */
    fn get_number_from_user_input(prompt: &str, error_message: &str, min:T, max:T) -> T {
        //DATA
        let mut raw_input = String::new(); // temporary variable for user input that can be parsed later
    
        //input loop
        return loop {
    
            //print prompt
            println!("{}", prompt);
            //read user input from standard input, and store it to raw_input
            raw_input.clear(); //clear input
            io::stdin().read_line(&mut raw_input).expect( "CANNOT READ INPUT!");
    
            //from input, try to read a number
            if let Ok(i) = raw_input.trim().parse() {
                //what bounds must the input fall into
                if min < max {  //have a min and max bound: [min,max]
                    if i >= min && i <= max {//is input valid, within bounds
                        break i; //exit the loop with the value i, returning it
                    } else {println!("ONLY BETWEEN {} AND {}, PLEASE!", min, max);} //print error message specific to this case
                } else if min > max { //only a min bound: [min, infinity)
                    if i >= min {break i;} else {println!("NO LESS THAN {}, PLEASE!", min);}
                } else { //only a max bound: (-infinity, max]
                    if i <= max {break i;} else {println!("NO MORE THAN {}, PLEASE!", max);}
                }
                continue; //continue to the next loop iteration
            };
            //this is only reached if a number couldn't be parsed from the input
            if !error_message.is_empty() {println!("{}",error_message);} //if they gave an error message to use, print it
        };
    }
    
    
    ================================================
    FILE: 60_Mastermind/rust/Mastermind_refactored_for_conventions/Cargo.toml
    ================================================
    [package]
    name = "mastermind_refactored_for_conventions"
    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"
    
    ================================================
    FILE: 60_Mastermind/rust/Mastermind_refactored_for_conventions/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Rust](https://www.rust-lang.org/) by Anthony Rubick [AnthonyMichaelTDM](https://github.com/AnthonyMichaelTDM)
    
    
    ================================================
    FILE: 60_Mastermind/rust/Mastermind_refactored_for_conventions/src/lib.rs
    ================================================
    /*
     lib.rs contains all the logic of the program
    */
    use rand::{Rng, prelude::thread_rng}; //rng
    use std::error::Error; //better errors
    use std::io::{self, Write}; //io interactions
    use std::{str::FromStr, fmt::Display}; //traits
    
    //DATA
    const COLORS: [&str;8] = ["Black ", "White ","Red ","Green ","Orange ","Yellow ", "Purple ", "Tan "]; //all available colors
    const LETTERS: &str = "BWRGOYPT"; //letters representing the above colors
    
    /// handles setup for the game
    pub struct Config {
        num_colors: usize,
        num_positions: usize,
        num_rounds: usize,
        num_guesses: usize,
        total_possibilities: usize,
    }
    impl Config {
        /// creates and returns a new Config from user input
        pub fn new() -> Result> {
            //DATA
            let mut config: Config = Config { 
                num_colors: 0,
                num_positions: 0,
                num_rounds: 0,
                num_guesses: 0,
                total_possibilities: 0,
            };
    
            //get data from user input
            //input loop
            loop {match get_number_from_input("NUMBER OF COLORS: ", 2, COLORS.len()) {
                Ok(num) => {
                    config.num_colors = num;
                    break;
                },
                Err(e) => eprintln!("{}",e),
            }}
            //input loop
            loop {match get_number_from_input("NUMBER OF POSITIONS: ", 2, 10) {
                Ok(num) => {
                    config.num_positions = num;
                    break;
                },
                Err(e) => eprintln!("{}",e),
            }}
            //input loop
            loop {match get_number_from_input("NUMBER OF ROUNDS: ", 1, 10) {
                Ok(num) => {
                    config.num_rounds = num;
                    break;
                },
                Err(e) => eprintln!("{}",e),
            }}
            //input loop
            loop {match get_number_from_input("NUMBER OF GUESSES: ", 10, 0) {
                Ok(num) => {
                    config.num_guesses = num;
                    break;
                },
                Err(e) => eprintln!("{}",e),
            }}
            //calc total posibilities
            config.total_possibilities = config.num_colors.pow(config.num_positions as u32);
    
            //return new config
            return Ok(config);
        }
    }
    
    /// run the program
    pub fn run(config: &Config) -> Result<(), Box> {
        //DATA
        let mut human_score: usize = 0;
        let mut computer_score: usize = 0;
    
        //print number of possibilities
        println!("\nTOTAL POSSIBILITIES = {}\n", config.total_possibilities);
    
        //print color letter table
        print_color_letter_table(config.num_colors);
    
        //for every round
        for round in 1..=config.num_rounds {
            //print round number
            println!("\n\nROUND NUMBER: {}", round);
    
            //computer as code-maker
            if let Some(score) = play_round_computer_codemaker(config) {
                human_score += score;
            } else {break;}
    
            //human as code-maker
            if let Some(score) = play_round_human_codemaker(config) {
                computer_score += score;
            } else {break;}
    
            //print scores
            print_scores(human_score,computer_score);
        }
    
        //return to main
        Ok(())
    }
    
    /// run a round with computer as code-maker
    /// returns the number of turns it takes the human to guess the secret code
    fn play_round_computer_codemaker(config: &Config) -> Option {
        //DATA
        let mut rng = thread_rng();
        let mut guesses: Vec = Vec::new();
        let secret: Code;
    
        //generate secret
        secret = Code::new_from_int(rng.gen_range(0..config.num_colors.pow(config.num_positions.try_into().unwrap())), config);
    
        //round loop
        for human_moves in 1..=config.num_guesses {
            //get guess from user input
            //input loop
            let mut guess = loop {
                //get input
                let user_input = get_string_from_user_input(format!("\nMOVE # {} GUESS: ", human_moves).as_str()).expect("something went wrong getting user guess");
    
                //parse input
                if user_input.trim().eq_ignore_ascii_case("board") { //print the board state
                    print_board(&guesses);
                    continue; //run input loop again
                } else if user_input.trim().eq_ignore_ascii_case("quit") { //quit the game
                    println!("QUITTER!  MY COMBINATION WAS: {}\nGOOD BYE", secret.as_human_readible_chars());
                    return None; //exit the game
                } else {
                    //parse input for a code
                    match Code::new_from_string(&user_input, &config) {
                        Ok(code) => {
                            //ensure code is correct length
                            if code.code.len() != config.num_positions { // if not
                                println!("BAD NUMBER OF POSITIONS.");
                                continue; //run loop again
                            }
                            else {break code;}//break with the code
                        },
                        Err(e) => {eprintln!("{}",e); continue;}, //run loop again
                    }
                }
            };
            
            //evaluate guess
            guess.evaluate(&secret).expect("something went wrong evaluating user guess");
    
            //tell user the results
            if guess.black_pins >= config.num_positions { //guessed it correctly
                println!("YOU GUESSED IT IN {} MOVES!", human_moves);
                return Some(human_moves); //exit function
            } else { //didn't
                println!("YOU HAVE {} BLACKS AND {} WHITES.", guess.black_pins, guess.white_pins);
            }
    
            //add guess to the list of guesses
            guesses.push(guess);
        }
    
        //only runs if user doesn't guess the code
        println!("YOU RAN OUT OF MOVES!  THAT'S ALL YOU GET!");
        println!("THE ACTUAL COMBINATION WAS: {}", secret.as_human_readible_chars());
        return Some(config.num_guesses); //max score gain per round
    }
    
    /// run a round with human as code-maker
    /// returns the number of turns it takes the computer to guess the secret code
    fn play_round_human_codemaker(config: &Config) -> Option{
        //DATA
        let mut rng = thread_rng();
        let mut all_possibilities = vec![true; config.total_possibilities];
        let _secret: Code;
    
    
        //get a secret code from user input
        println!("\nNOW I GUESS.  THINK OF A COMBINATION.\nHIT RETURN WHEN READY: ");
        // input loop
        _secret = loop {
            //get input
            let user_input = get_string_from_user_input("").expect("something went wrong getting secret from user");
    
            //parse input
            if let Ok(code) = Code::new_from_string(&user_input, config) {
                if code.code.len() == config.num_positions {break code;} //exit loop with code
                else {println!("CODE MUST HAVE {} POSITIONS", config.num_positions);continue;} //tell them to try again
            }
            println!("INVALID CODE.  TRY AGAIN"); //if unsuccessful, this is printed and the loop runs again
        };
    
        //round loop
        for computer_moves in 1..=config.num_guesses {
            let mut guess: Code = Code::new();
    
            //randomly generate a guess //770
            let mut guess_int = rng.gen_range(0..config.total_possibilities);
            // if possible, use it //780
            if all_possibilities[guess_int] {
                guess = Code::new_from_int(guess_int, &config); //create guess
            }
            else {// if not possible:
                // search all possibilities after guess, use first valid one //790
                for g in guess_int..config.total_possibilities {
                    if all_possibilities[g] {
                        guess_int=g;
                        guess = Code::new_from_int(guess_int, &config); //create guess
                        break;
                    }
                }
                // if none was found
                //  search all possibilities before guess, use first valid one //820
                if guess.code.is_empty() {
                    for g in (0..guess_int).rev() {
                        if all_possibilities[g] {
                            guess_int=g;
                            guess = Code::new_from_int(guess_int, &config); //create guess
                            break;
                        }
                    }
                }
    
                // if none where found, tell the user and start over #850
                if guess.code.is_empty() {
                    println!("YOU HAVE GIVEN ME INCONSISTENT INFORMATION.");
                    println!("PLAY AGAIN, AND THIS TIME PLEASE BE MORE CAREFUL.");
                    return None; //exit game
                };
            }
    
            //convert guess into something readible #890
            //print it #940
            println!("MY GUESS IS: {}", guess.as_human_readible_chars());
    
            //ask user for feedback, #980
            // input loop for black pegs
            loop {match get_number_from_input("BLACKS: ", 0, config.num_positions) {
                Ok(num) => {
                    guess.black_pins = num;
                    break;
                },
                Err(e) => eprintln!("{}",e),
            }}
            // input loop for white pegs
            loop {match get_number_from_input("WHITES: ", 0, config.num_positions) {
                Ok(num) => {
                    guess.white_pins = num;
                    break;
                },
                Err(e) => eprintln!("{}",e),
            }}
    
            //if computer guessed it, end #990
            if guess.black_pins >= config.num_positions { //guessed it correctly
                println!("I GOT IT IN {} MOVES!", computer_moves);
                return Some(computer_moves); //exit function
            } else { //didn't
                all_possibilities[guess_int] = false;
                //if we didn't, eliminate the combinations that don't work
                //we know the number of black and white pegs for a valid answer, so eleminate all that get different amounts
                all_possibilities.iter_mut().enumerate().for_each(|b| {
                    if *b.1 { //filter out ones we already know aren't possible
                        let mut tmp_guess = Code::new_from_int(b.0, &config);
                        tmp_guess.evaluate(&guess).expect("something went wrong evaluation the computer's guess"); //compare with computer guess
                        if (guess.black_pins != tmp_guess.black_pins) || (guess.white_pins != tmp_guess.white_pins) { //if number of blacks/whites is different, set it to false
                            *b.1 = false;
                        }
                    }
                });
            }
        }
    
        //only runs if computer doesn't guess the code
        println!("I USED UP ALL MY MOVES!");
        println!("I GUESS MY CPU IS JUST HAVING AN OFF DAY.");
        return Some(config.num_guesses); //max moves the computer could've taken
    }
    
    struct Code {
        code: Vec,
        black_pins: usize,
        white_pins: usize,
    }
    impl Code {
        /// create generic, empty code
        fn new() -> Code {
            return Code{code: Vec::new(), black_pins:0, white_pins:0};
        }
        /// converts input_int from base 10 to base num_colors to generate the code
        /// input_int must be between 0 and num_colors.pow(num_positions)
        fn new_from_int(mut input_int: usize, config: &Config) -> Code {
            //DATA
            let mut converted_number:Vec<_> = Vec::new();
            assert!(2 <= config.num_colors && config.num_colors <= 36); //if num_colors is outside of this range, things break later on
    
            //convert input_int into a code by effectively converting input_int from base 10 to base n where n is num_colors, uses some fancy stuff to do this
            loop {
                converted_number.push(std::char::from_digit((input_int % config.num_colors).try_into().unwrap(), config.num_colors.try_into().unwrap()).unwrap()); //
                input_int /= config.num_colors;
                if input_int == 0 {break}
            }
            
            while converted_number.len() < config.num_positions {converted_number.push('0');} // fill remaining space with zero's
            let converted_number: Vec<_> = converted_number.iter().rev().map(|e| e.to_digit(config.num_colors.try_into().unwrap()).unwrap() .try_into().unwrap()).collect(); //reverse the vector and convert it to integers
            return Code{code: converted_number, black_pins:0, white_pins:0};
        }
        /// returns a code parsed from the passed string
        fn new_from_string(input_string: &str, config: &Config) -> Result> {
            let valid_chars = &LETTERS[0..config.num_colors];
            //DATA
            let new_code = Code{
                code: {
                    input_string.to_ascii_uppercase().chars() //get an iterator with all the chars in input string converted to uppercase
                    .filter( |c| { valid_chars.contains(*c)}) //remove chars that aren't in LETTERS
                    .map( |x| -> usize {valid_chars.find(x).expect("invalid character")})//convert all the chars into usizes representing their index in LETTERS
                    .collect() //wrap this iterator up into a vector
                },
                black_pins: 0,
                white_pins: 0,
            };
            //if code is empty, return None, otherwise return Some(code)
            if new_code.code.is_empty() {return Err(String::from("Input String did not contain enough valid characters").into());}
            else {return Ok(new_code);}
        }
    
        /// returns a string containing the code represented as characters
        fn as_human_readible_chars(&self) -> String {
            return self.code.iter().map(|i|->char{LETTERS.chars().nth(*i).expect("index out of bounds")}).collect();
        }
    
        /**
         * evaulates itself for the number of black and white pegs it should have when compared to a given secret
         */
        fn evaluate(&mut self, secret:&Code) -> Result<(),Box> {
            //data
            let mut consumed = vec![false;secret.code.len()];
    
            if self.code.len() != secret.code.len() {
                return Err(String::from("only codes of the same length can be compared").into());
            }
    
            for i in 0..secret.code.len() {
                if self.code[i] == secret.code[i] { //correct value correct place
                    self.black_pins += 1;
                    consumed[i] = true;
                }
                else {
                    //check for correct value incorrect place, don't count positions that are already exact matches
                    for j in 0..secret.code.len() {
                        if !consumed[j] && self.code[i] == secret.code[j] && self.code[j] != secret.code[j] {
                            self.white_pins += 1;
                            consumed[j] = true;
                            break;
                        }
                    }
                }
            }
    
            return Ok(())
        }
    }
    
    
    
    /// print scores
    fn print_scores(human_score:usize, computer_score:usize) {
        println!("SCORE\n\tCOMPUTER: {}\n\tHUMAN: {}", computer_score, human_score);
    }
    /// print the color - letter table
    /// only prints the first num_colors pairs
    fn print_color_letter_table(num_colors:usize) {
        println!("COLOR\tLETTER");
        println!("=====\t======");
        for i in 0..num_colors {
            println!("{}\t{}", COLORS[i], &LETTERS[i..i+1]);
        }
    }
    /// prints the board state, previous guesses and the number of black/white pins for each
    fn print_board(guesses: &[Code]) {
        println!("BOARD");
        println!("MOVE\tGUESS\t\tBLACK\tWhite");
        for guess in guesses.iter().enumerate() {
            println!("{}\t{}\t\t{}\t{}", guess.0,guess.1.as_human_readible_chars(),guess.1.black_pins,guess.1.white_pins);
        }
    }
    
    
    
    /// gets a string from user input
    fn get_string_from_user_input(prompt: &str) -> Result> {
        //DATA
        let mut raw_input = String::new();
    
        //print prompt
        print!("{}", prompt);
        //make sure it's printed before getting input
        io::stdout().flush().expect("couldn't flush stdout");
    
        //read user input from standard input, and store it to raw_input, then return it or an error as needed
        raw_input.clear(); //clear input
        match io::stdin().read_line(&mut raw_input) {
            Ok(_num_bytes_read) => return Ok(String::from(raw_input.trim())),
            Err(err) => return Err(format!("ERROR: CANNOT READ INPUT!: {}", err).into()),
        }
    }
    /// generic function to get a number from the passed string (user input)
    /// pass a min lower  than the max to have minimun and maximun bounds
    /// pass a min higher than the max to only have a minumum bound
    /// pass a min equal   to  the max to only have a maximun bound
    /// 
    /// Errors:
    /// no number on user input
    fn get_number_from_input(prompt: &str, min:T, max:T) -> Result> {
        //DATA
        let raw_input: String;
        let processed_input: String;
    
        
        //input looop
        raw_input = loop {
            match get_string_from_user_input(prompt) {
                Ok(input) => break input,
                Err(e) => {
                    eprintln!("{}",e);
                    continue;
                },
            }
        };
    
        //filter out num-numeric characters from user input
        processed_input = raw_input.chars().filter(|c| c.is_numeric()).collect();
    
        //from input, try to read a number
        match processed_input.trim().parse() {
            Ok(i) => {
                //what bounds must the input fall into
                if min < max {  //have a min and max bound: [min,max]
                    if i >= min && i <= max {//is input valid, within bounds
                        return Ok(i); //exit the loop with the value i, returning it
                    } else { //print error message specific to this case
                        return Err(format!("ONLY BETWEEN {} AND {}, PLEASE!", min, max).into());
                    } 
                } else if min > max { //only a min bound: [min, infinity)
                    if i >= min {
                        return Ok(i);
                    } else {
                        return Err(format!("NO LESS THAN {}, PLEASE!", min).into());
                    }
                } else { //only a max bound: (-infinity, max]
                    if i <= max {
                        return Ok(i);
                    } else {
                        return Err(format!("NO MORE THAN {}, PLEASE!", max).into());
                    }
                }
            },
            Err(_e) => return Err(format!("Error: couldn't find a valid number in {}",raw_input).into()),
        }
    }
    
    
    ================================================
    FILE: 60_Mastermind/rust/Mastermind_refactored_for_conventions/src/main.rs
    ================================================
    use std::process;//allows for some better error handling
    
    mod lib; //allows access to lib.rs
    use lib::Config;
    
    /// main function
    /// responsibilities:
    /// - Calling the command line logic with the argument values
    /// - Setting up any other configuration
    /// - Calling a run function in lib.rs
    /// - Handling the error if run returns an error
    fn main() {
        //greet user
        welcome();
    
        // set up other configuration
        let mut config = Config::new().unwrap_or_else(|err| {
            eprintln!("Problem configuring program: {}", err);
            process::exit(1);
        });
    
        // run the program
        if let Err(e) = lib::run(&mut config) {
            eprintln!("Application Error: {}", e); //use the eprintln! macro to output to standard error
            process::exit(1); //exit the program with an error code
        }
    
        //end of program
        println!("THANKS FOR PLAYING!");
    }
    
    /// print the welcome message
    fn welcome() {
        println!("
                                 MASTERMIND
                  CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY
    
    
        ");
    }
    
    
    ================================================
    FILE: 60_Mastermind/vbnet/Mastermind.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Mastermind", "Mastermind.vbproj", "{B32A8054-8790-4810-93A8-CD0C740CEBD5}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{B32A8054-8790-4810-93A8-CD0C740CEBD5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{B32A8054-8790-4810-93A8-CD0C740CEBD5}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{B32A8054-8790-4810-93A8-CD0C740CEBD5}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{B32A8054-8790-4810-93A8-CD0C740CEBD5}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 60_Mastermind/vbnet/Mastermind.vbproj
    ================================================
    
      
        Exe
        Mastermind
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 60_Mastermind/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 61_Math_Dice/README.md
    ================================================
    ### Math Dice
    
    The program presents pictorial drill on addition facts using printed dice with no reading involved. It is good for beginning addition, since the answer can be derived from counting spots on the dice as well as by memorizing math facts or awareness of number concepts. It is especially effective run on a CRT terminal.
    
    It was originally written by Jim Gerrish, a teacher at the Bernice A. Ray School in Hanover, New Hampshire.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=113)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=128)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 61_Math_Dice/csharp/GameState.cs
    ================================================
    namespace MathDice
    {
        public enum GameState
        {
            FirstAttempt = 0,
            SecondAttempt = 1,
        }
    }
    
    
    ================================================
    FILE: 61_Math_Dice/csharp/MathDice.csproj
    ================================================
    
    
      
        Exe
        net5.0
        MathDice
      
    
    
    
    
    ================================================
    FILE: 61_Math_Dice/csharp/MathDice.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.31025.194
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MathDice", "MathDice.csproj", "{4F28D7EB-3CD6-434F-B643-0CEA5F09F96D}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{4F28D7EB-3CD6-434F-B643-0CEA5F09F96D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{4F28D7EB-3CD6-434F-B643-0CEA5F09F96D}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{4F28D7EB-3CD6-434F-B643-0CEA5F09F96D}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{4F28D7EB-3CD6-434F-B643-0CEA5F09F96D}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {A1337DCA-4EFA-45EE-A1A9-22E37E74F362}
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 61_Math_Dice/csharp/Program.cs
    ================================================
    using System;
    
    namespace MathDice
    {
        public static class Program
        {
            readonly static Random random = new Random();
    
            static int DieOne = 0;
            static int DieTwo = 0;
    
            private const string NoPips = "I     I";
            private const string LeftPip = "I *   I";
            private const string CentrePip = "I  *  I";
            private const string RightPip = "I   * I";
            private const string TwoPips = "I * * I";
            private const string Edge = " ----- ";
    
            static void Main(string[] args)
            {
                int answer;
    
                GameState gameState = GameState.FirstAttempt;
    
                Console.WriteLine("MATH DICE".CentreAlign());
                Console.WriteLine("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY".CentreAlign());
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine("THIS PROGRAM GENERATES SUCCESSIVE PICTURES OF TWO DICE.");
                Console.WriteLine("WHEN TWO DICE AND AN EQUAL SIGN FOLLOWED BY A QUESTION");
                Console.WriteLine("MARK HAVE BEEN PRINTED, TYPE YOUR ANSWER AND THE RETURN KEY.");
                Console.WriteLine("TO CONCLUDE THE LESSON, TYPE CONTROL-C AS YOUR ANSWER.");
                Console.WriteLine();
                Console.WriteLine();
    
                while (true)
                {
                    if (gameState == GameState.FirstAttempt)
                    {
                        Roll(ref DieOne);
                        Roll(ref DieTwo);
    
                        DrawDie(DieOne);
                        Console.WriteLine("   +");
                        DrawDie(DieTwo);
                    }
    
                    answer = GetAnswer();
    
                    if (answer == DieOne + DieTwo)
                    {
                        Console.WriteLine("RIGHT!");
                        Console.WriteLine();
                        Console.WriteLine("THE DICE ROLL AGAIN...");
    
                        gameState = GameState.FirstAttempt;
                    }
                    else
                    {
                        if (gameState == GameState.FirstAttempt)
                        {
                            Console.WriteLine("NO, COUNT THE SPOTS AND GIVE ANOTHER ANSWER.");
                            gameState = GameState.SecondAttempt;
                        }
                        else
                        {
                            Console.WriteLine($"NO, THE ANSWER IS{DieOne + DieTwo}");
                            Console.WriteLine();
                            Console.WriteLine("THE DICE ROLL AGAIN...");
                            gameState = GameState.FirstAttempt;
                        }
                    }
                }
            }
    
            private static int GetAnswer()
            {
                int answer;
    
                Console.Write("      =?");
                var input = Console.ReadLine();
    
                int.TryParse(input, out answer);
    
                return answer;
            }
    
            private static void DrawDie(int pips)
            {
                Console.WriteLine(Edge);
                Console.WriteLine(OuterRow(pips, true));
                Console.WriteLine(CentreRow(pips));
                Console.WriteLine(OuterRow(pips, false));
                Console.WriteLine(Edge);
                Console.WriteLine();
            }
    
            private static void Roll(ref int die) => die = random.Next(1, 7);
    
            private static string OuterRow(int pips, bool top)
            {
                return pips switch
                {
                    1 => NoPips,
                    var x when x == 2 || x == 3 => top ? LeftPip : RightPip,
                    _ => TwoPips
                };
            }
    
            private static string CentreRow(int pips)
            {
                return pips switch
                {
                    var x when x == 2 || x == 4 => NoPips,
                    6 => TwoPips,
                    _ => CentrePip
                };
            }
        }
    }
    
    
    ================================================
    FILE: 61_Math_Dice/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    Conversion Notes
    
    - There are minor spacing issues which have been preserved in this port.
    - This implementation uses switch expressions to concisely place the dice pips in the right place.
    - Random() is only pseudo-random but perfectly adequate for the purposes of simulating dice rolls.
    - Console width is assumed to be 120 chars for the purposes of centrally aligned the intro text.
    
    
    ================================================
    FILE: 61_Math_Dice/csharp/StringExtensions.cs
    ================================================
    namespace MathDice
    {
        public static class StringExtensions
        {
            private const int ConsoleWidth = 120; // default console width
    
            public static string CentreAlign(this string value)
            {
                int spaces = ConsoleWidth - value.Length;
                int leftPadding = spaces / 2 + value.Length;
    
                return value.PadLeft(leftPadding).PadRight(ConsoleWidth);
            }
        }
    }
    
    
    ================================================
    FILE: 61_Math_Dice/java/Die.java
    ================================================
    import java.util.Random;
    
    public class Die {
        private static final int DEFAULT_SIDES = 6;
        private int faceValue;
        private int sides;
        private Random generator = new Random();
    
        /**
         * Construct a new Die with default sides
         */
        public Die() {
            this.sides = DEFAULT_SIDES;
            this.faceValue = 1 + generator.nextInt(sides);
        }
    
        /**
         * Generate a new random number between 1 and sides to be stored in faceValue
         */
        private void throwDie() {
            this.faceValue = 1 + generator.nextInt(sides);
        }
    
    
        /**
         * @return the faceValue
         */
        public int getFaceValue() {
            return faceValue;
        }
    
    
        public void printDie() {
            throwDie();
            int x = this.getFaceValue();
    
            System.out.println(" ----- ");
    
            if(x==4||x==5||x==6) {
                printTwo();
            } else if(x==2||x==3) {
                System.out.println("| *   |");
            } else {
                printZero();
            }
    
            if(x==1||x==3||x==5) {
                System.out.println("|  *  |");
            } else if(x==2||x==4) {
                printZero();
            } else {
                printTwo();
            }
    
            if(x==4||x==5||x==6) {
                printTwo();
            } else if(x==2||x==3) {
                System.out.println("|   * |");
            } else {
                printZero();
            }
    
            System.out.println(" ----- ");
        }
    
        private void printZero() {
            System.out.println("|     |");
        }
    
        private void printTwo() {
            System.out.println("| * * |");
        }
    }
    
    
    ================================================
    FILE: 61_Math_Dice/java/MathDice.java
    ================================================
    import java.util.Scanner;
    
    public class MathDice {
    
        public static void main(String[] args) {
            Scanner in = new Scanner(System.in);
            Die dieOne = new Die();
            Die dieTwo = new Die();
            int guess = 1;
            int answer;
    
            System.out.println("Math Dice");
            System.out.println("https://github.com/coding-horror/basic-computer-games");
            System.out.println();
            System.out.print("This program generates images of two dice.\n"
                    + "When two dice and an equals sign followed by a question\n"
                    + "mark have been printed, type your answer, and hit the ENTER\n" + "key.\n"
                    + "To conclude the program, type 0.\n");
    
            while (true) {
                dieOne.printDie();
                System.out.println("   +");
                dieTwo.printDie();
                System.out.println("   =");
                int tries = 0;
                answer = dieOne.getFaceValue() + dieTwo.getFaceValue();
    
                while (guess!=answer && tries < 2) {
                    if(tries == 1)
                        System.out.println("No, count the spots and give another answer.");
                    try{
                        guess = in.nextInt();
                    } catch(Exception e) {
                        System.out.println("Thats not a number!");
                        in.nextLine();
                    }
    
                    if(guess == 0)
                        System.exit(0);
    
                    tries++;
                }
    
                if(guess != answer){
                    System.out.println("No, the answer is " + answer + "!");
                } else {
                    System.out.println("Correct");
                }
                System.out.println("The dice roll again....");
            }
        }
    
    }
    
    
    ================================================
    FILE: 61_Math_Dice/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 61_Math_Dice/javascript/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells)
    
    
    ================================================
    FILE: 61_Math_Dice/javascript/mathdice.html
    ================================================
    
    
    MATH DICE
    
    
    
    
    
    
    
    
    
    
    ================================================
    FILE: 61_Math_Dice/javascript/mathdice.js
    ================================================
    // MATH DICE
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    // Main program
    async function main()
    {
        print(tab(31) + "MATH DICE\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        print("THIS PROGRAM GENERATES SUCCESSIVE PICTURES OF TWO DICE.\n");
        print("WHEN TWO DICE AND AN EQUAL SIGN FOLLOWED BY A QUESTION\n");
        print("MARK HAVE BEEN PRINTED, TYPE YOUR ANSWER AND THE RETURN KEY.\n"),
        print("TO CONCLUDE THE LESSON, TYPE ZERO AS YOUR ANSWER.\n");
        print("\n");
        print("\n");
        n = 0;
        while (1) {
            n++;
            d = Math.floor(6 * Math.random() + 1);
            print(" ----- \n");
            if (d == 1)
                print("I     I\n");
            else if (d == 2 || d == 3)
                print("I *   I\n");
            else
                print("I * * I\n");
            if (d == 2 || d == 4)
                print("I     I\n");
            else if (d == 6)
                print("I * * I\n");
            else
                print("I  *  I\n");
            if (d == 1)
                print("I     I\n");
            else if (d == 2 || d == 3)
                print("I   * I\n");
            else
                print("I * * I\n");
            print(" ----- \n");
            print("\n");
            if (n != 2) {
                print("   +\n");
                print("\n");
                a = d;
                continue;
            }
            t = d + a;
            print("      =");
            t1 = parseInt(await input());
            if (t1 == 0)
                break;
            if (t1 != t) {
                print("NO, COUNT THE SPOTS AND GIVE ANOTHER ANSWER.\n");
                print("      =");
                t1 = parseInt(await input());
                if (t1 != t) {
                    print("NO, THE ANSWER IS " + t + "\n");
                }
            }
            if (t1 == t) {
                print("RIGHT!\n");
            }
            print("\n");
            print("THE DICE ROLL AGAIN...\n");
            print("\n");
            n = 0;
        }
    }
    
    main();
    
    
    ================================================
    FILE: 61_Math_Dice/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 61_Math_Dice/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 61_Math_Dice/mathdice.bas
    ================================================
    10 PRINT TAB(31);"MATH DICE"
    20 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    30 PRINT:PRINT:PRINT
    40 PRINT "THIS PROGRAM GENERATES SUCCESSIVE PICTURES OF TWO DICE."
    50 PRINT "WHEN TWO DICE AND AN EQUAL SIGN FOLLOWED BY A QUESTION"
    60 PRINT "MARK HAVE BEEN PRINTED, TYPE YOUR ANSWER AND THE RETURN KEY."
    70 PRINT "TO CONCLUDE THE LESSON, TYPE CONTROL-C AS YOUR ANSWER."
    80 PRINT
    90 PRINT
    100 N=N+1
    110 D=INT(6*RND(1)+1)
    120 PRINT" ----- "
    130 IF D=1 THEN 200
    140 IF D=2 THEN 180
    150 IF D=3 THEN 180
    160 PRINT "I * * I"
    170 GOTO 210
    180 PRINT "I *   I"
    190 GOTO 210
    200 PRINT "I     I"
    210 IF D=2 THEN 260
    220 IF D=4 THEN 260
    230 IF D=6 THEN 270
    240 PRINT "I  *  I"
    250 GOTO 280
    260 PRINT "I     I"
    265 GOTO 280
    270 PRINT "I * * I"
    280 IF D=1 THEN 350
    290 IF D=2 THEN 330
    300 IF D=3 THEN 330
    310 PRINT "I * * I"
    320 GOTO 360
    330 PRINT "I   * I"
    340 GOTO 360
    350 PRINT "I     I"
    360 PRINT " ----- "
    370 PRINT
    375 IF N=2 THEN 500
    380 PRINT "   +"
    381 PRINT
    400 A=D
    410 GOTO 100
    500 T=D+A
    510 PRINT "      =";
    520 INPUT T1
    530 IF T1=T THEN 590
    540 PRINT "NO, COUNT THE SPOTS AND GIVE ANOTHER ANSWER."
    541 PRINT "      =";
    550 INPUT T2
    560 IF T2=T THEN 590
    570 PRINT "NO, THE ANSWER IS";T
    580 GOTO 600
    590 PRINT "RIGHT!"
    600 PRINT
    601 PRINT "THE DICE ROLL AGAIN..."
    610 PRINT
    615 N=0
    620 GOTO 100
    999 END
    
    
    ================================================
    FILE: 61_Math_Dice/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 61_Math_Dice/perl/mathdice.pl
    ================================================
    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    &main;
    
    # Main subroutine
    
    sub main {
    	&print_intro;
    	while (1==1) {
    		&game_play; 					#function that actually plays the game
    	}
    }
    
    sub game_play {
    	my $num = 0;
    	my $sum = 0;
    	my $tries = 0;
    	until ($num == 2) {               	# there are 2 dice rolls so we do it until the num equals 2
    		$num++;
    		my $roll = 1+int rand(6);		# getting a random number between 1 and 6
    		&print_dice($roll);				# function call to print out the dice
    		$sum = $sum + $roll;			# keeping track of the summary
    		#print "Sum: $sum Roll: $roll\n";
    		if ($num == 1) {
    			print "\n   +\n\n";			# if its the first roll then print an addition sign
    		}
    		if ($num == 2) {
    			print "     =? ";			# if its the second roll print the equals sign and wait for an answer
    			my $answer = ;
    			chomp($answer);
    			if ($answer == 0) {
    				die "You input '0', Thanks for playing!\n";
    			}
    			elsif ($answer == $sum) {
    				print "RIGHT!\n\nTHE DICE ROLL AGAIN\n\n";
    			}
    			else {						# code execution if they don't get the right answer
    				print "NO,COUNT THE SPOTS AND GIVE ANOTHER ANSWER\n";
    				print "     =? ";
    				$answer = ;
    				chomp($answer);
    				if ($answer == $sum){
    					print "RIGHT!\n\nTHE DICE ROLL AGAIN\n\n";
    				}
    			    else {
    					print "N0, THE ANSWER IS $sum\n";
    				}
    
    			}
    		}
    	}
    }
    
    sub print_dice {
    	my $roll = shift;
    	print " -----\n";
    	if ($roll == 1) {
    		&print_blank;
    		&print_one_mid;
    		&print_blank;
    	}
    	if ($roll == 2) {
    		&print_one_left;
    		&print_blank;
    		&print_one_right;
    	}
    	if ($roll == 3) {
    		&print_one_left;
    		&print_one_mid;
    		&print_one_right;
    	}
    	if ($roll == 4) {
    		&print_two;
    		&print_blank;
    		&print_two;
    	}
    	if ($roll == 5) {
    		&print_two;
    		&print_one_mid;
    		&print_two;
    	}
    	if ($roll == 6) {
    		&print_two;
    		&print_two;
    		&print_two;
    	}
    	print " -----\n";
    }
    
    sub print_one_left {
    	print "I *   I\n";
    }
    
    sub print_one_mid {
    	print "I  *  I\n";
    }
    
    sub print_one_right {
    	print "I   * I\n";
    }
    
    sub print_two {
    	print "I * * I\n";
    }
    
    sub print_blank {
    	print "I     I\n";
    }
    
    sub print_intro {
    	my $spaces = " "x31;
    	print "$spaces MATH DICE\n";
    	$spaces = " "x15;
    	print "$spaces CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n";
    	print "THIS PROGRAM GENERATES SUCCESSIVE PICTURES OF TWO DICE.\n";
    	print "WHEN TWO DICE AND AN EQUAL SIGN FOLLOWED BY A QUESTION\n";
    	print "MARK HAVE BEEN PRINTED, TYPE YOUR ANSWER AND THE RETURN KEY.\n";
    	print "TO CONCLUDE THE LESSON, TYPE '0' AS YOUR ANSWER.\n\n\n";
    }
    
    
    ================================================
    FILE: 61_Math_Dice/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 61_Math_Dice/python/mathdice.py
    ================================================
    from random import randint
    
    print("Math Dice")
    print("https://github.com/coding-horror/basic-computer-games")
    print()
    print(
        """This program generates images of two dice.
    When two dice and an equals sign followed by a question
    mark have been printed, type your answer, and hit the ENTER
    key.
    To conclude the program, type 0.
    """
    )
    
    
    def print_dice(n: int) -> None:
        def print_0() -> None:
            print("|     |")
    
        def print_2() -> None:
            print("| * * |")
    
        print(" ----- ")
    
        if n in {4, 5, 6}:
            print_2()
        elif n in {2, 3}:
            print("| *   |")
        else:
            print_0()
    
        if n in {1, 3, 5}:
            print("|  *  |")
        elif n in {2, 4}:
            print_0()
        else:
            print_2()
    
        if n in {4, 5, 6}:
            print_2()
        elif n in {2, 3}:
            print("|   * |")
        else:
            print_0()
    
        print(" ----- ")
    
    
    def main() -> None:
    
        while True:
            d1 = randint(1, 6)
            d2 = randint(1, 6)
            guess = 13
    
            print_dice(d1)
            print("   +")
            print_dice(d2)
            print("   =")
    
            tries = 0
            while guess != (d1 + d2) and tries < 2:
                if tries == 1:
                    print("No, count the spots and give another answer.")
                try:
                    guess = int(input())
                except ValueError:
                    print("That's not a number!")
                if guess == 0:
                    exit()
                tries += 1
    
            if guess != (d1 + d2):
                print(f"No, the answer is {d1 + d2}!")
            else:
                print("Correct!")
    
            print("The dice roll again....")
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 61_Math_Dice/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 61_Math_Dice/ruby/mathdice.rb
    ================================================
    def intro
      puts "                               MATH DICE
                     CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY
    
    THIS PROGRAM GENERATES SUCCESSIVE PICTURES OF TWO DICE.
    WHEN TWO DICE AND AN EQUAL SIGN FOLLOWED BY A QUESTION
    MARK HAVE BEEN PRINTED, TYPE YOUR ANSWER AND THE RETURN KEY.
    TO CONCLUDE THE LESSON, TYPE '0' AS YOUR ANSWER.
    "
    end
    
    def game_play
      num = 0
      sum = 0
      tries = 0
      until num == 2 do
        num+=1
        roll = rand(6) + 1
        print_dice(roll)
        sum = sum + roll
        if num == 1
          print "\n   +\n\n"
        end
        if num == 2
          print "\n   =? "
          ans = gets.chomp
          if ans.to_i == 0
            #END GAME
            exit(0)
          elsif ans.to_i == sum
            puts "RIGHT!"
            puts "THE DICE ROLL AGAIN"
          else
            puts "NO, COUNT THE SPOTS AND GIVE ANOTHER ANSWER"
            print "\n   =? "
            ans = gets.chomp
            if ans.to_i == sum
              puts "RIGHT!"
              puts "THE DICE ROLL AGAIN"
            elsif ans.to_i == 0
              exit(0)
            else
              puts "NO, THE ANSWER IS #{sum}"
            end
          end
        end
      end
    end
    
    
    
    def print_dice(roll)
      puts " -----"
      if roll == 1
        print_blank
        print_one_mid
        print_blank
      elsif roll == 2
        print_one_left
        print_blank
        print_one_right
      elsif roll == 3
        print_one_left
        print_one_mid
        print_one_right
      elsif roll == 4
        print_two
        print_blank
        print_two
      elsif roll == 5
        print_two
        print_one_mid
        print_two
      elsif roll == 6
        print_two
        print_two
        print_two
      else
        puts "not a legit dice roll"
      end
      puts " -----"
    end
    
    
    def print_one_left
      puts "I *   I"
    end
    
    def print_one_mid
      puts "I  *  I"
    end
    
    def print_one_right
      puts "I   * I"
    end
    
    def print_two
      puts "I * * I"
    end
    
    def print_blank
      puts "I     I"
    end
    
    
    
    def main
      intro
      #Continue playing forever until it terminates with exit in game_play
      while 1 == 1 do
        game_play
      end
    end
    
    main
    
    
    ================================================
    FILE: 61_Math_Dice/rust/Cargo.toml
    ================================================
    [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"
    
    
    ================================================
    FILE: 61_Math_Dice/rust/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Rust](https://www.rust-lang.org/) by Anthony Rubick [AnthonyMichaelTDM](https://github.com/AnthonyMichaelTDM)
    
    If you wish to give the user more than 2 attempts to get the number, change value assigned to the num_tries variable at the start of the main function
    
    
    ================================================
    FILE: 61_Math_Dice/rust/src/main.rs
    ================================================
    use rand::{Rng, prelude::{thread_rng}};
    use std::io;
    
    fn main() {
        //DATA
        let num_tries:u8 = 2; //number of tries the player gets each round, must be at least 1
        let mut rng = thread_rng();
        let mut user_guess: u8;
        let mut dice_1:u8;
        let mut dice_2:u8;
    
        //print welcome message
        welcome();
    
        //game loop
        loop {
            //roll dice
            dice_1 = rng.gen_range(1..=6);
            dice_2 = rng.gen_range(1..=6);
    
            //print dice
            print_dice(dice_1);
            println!("   +");
            print_dice(dice_2);
            println!("   =");
    
            //get user guess, they have 2 tries
            for t in 0..num_tries {
                //get guess
                user_guess = get_number_from_user_input("", "That's not a valid number!", 1, 12);
    
                //if they get it wrong
                if user_guess != (dice_1+dice_2) {
                    //print different message depending on what try they're on
                    if t < num_tries-1 { // user has tries left
                        println!("NO, COUNT THE SPOTS AND GIVE ANOTHER ANSWER.");
                        println!("   =");
                    }
                    else { //this is their last try
                        println!("NO, THE ANSWER IS {}", dice_1+dice_2);
                    }
                }
                else {
                    println!("RIGHT!");
                    break;
                }
            }
    
            //play again
            println!("\nThe dice roll again....");
        }
    
    }
    
    /**
     * prints the welcome message to the console
     */
    fn welcome() {
        println!("
                                  MATH DICE
                  CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY
        \n\n
        THIS PROGRAM GENERATES SUCCESSIVE PICTURES OF TWO DICE.
        WHEN TWO DICE AND AN EQUAL SIGN FOLLOWED BY A QUESTION
        MARK HAVE BEEN PRINTED, TYPE YOUR ANSWER AND THE RETURN KEY.
        TO CONCLUDE THE LESSON, PRESS Ctrl+C AS YOUR ANSWER.\n
        ");
    }
    
    /**
     * print the dice,
     */
    fn print_dice(dice_value:u8) {
        //data
    
        //top
        println!(" ----- ");
        //first layer
        match dice_value {
            4|5|6 => println!("| * * |"),
            2|3 => println!("| *   |"),
            _=>println!("|     |"),
        }
    
        //second layer
        match dice_value {
            1|3|5 => println!("|  *  |"),
            2|4 => println!("|     |"),
            _=>println!("| * * |"),
        }
    
        //third layer
        match dice_value {
            4|5|6 => println!("| * * |"),
            2|3 => println!("|   * |"),
            _=>println!("|     |"),
        }
    
        //bottom
        println!(" ----- ");
    }
    
    /**
     * gets a integer from user input
     */
    fn get_number_from_user_input(prompt: &str, error_message: &str, min:u8, max:u8) -> u8 {
        //input loop
        return loop {
            let mut raw_input = String::new(); // temporary variable for user input that can be parsed later
    
            //print prompt
            println!("{}", prompt);
            //read user input from standard input, and store it to raw_input
            //raw_input.clear(); //clear input
            io::stdin().read_line(&mut raw_input).expect( "CANNOT READ INPUT!");
    
            //from input, try to read a number
            match raw_input.trim().parse::() {
                Ok(i) => {
                    if i < min || i > max { //input out of desired range
                        println!("{}  ({}-{})", error_message, min,max);
                        continue; // run the loop again
                    }
                    else {
                        break i;// this escapes the loop, returning i
                    }
                },
                Err(e) => {
                    println!("{}  {}", error_message, e.to_string().to_uppercase());
                    continue; // run the loop again
                }
            };
        };
    }
    
    
    ================================================
    FILE: 61_Math_Dice/vbnet/MathDice.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "MathDice", "MathDice.vbproj", "{1341C416-E919-4262-B378-113A6B6317DD}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{1341C416-E919-4262-B378-113A6B6317DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{1341C416-E919-4262-B378-113A6B6317DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{1341C416-E919-4262-B378-113A6B6317DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{1341C416-E919-4262-B378-113A6B6317DD}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 61_Math_Dice/vbnet/MathDice.vbproj
    ================================================
    
      
        Exe
        MathDice
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 61_Math_Dice/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 62_Mugwump/README.md
    ================================================
    ### Mugwump
    
    Your objective in this game is to find the four Mugwumps hiding on various squares of a 10 by 10 grid. Homebase (lower left) is position (0,0) and a guess is a pair of whole numbers (0 to 9), separated by commas. The first number is the number of units to the right of homebase and the second number is the distance above homebase.
    
    You get ten guesses to locate the four Mugwumps; after each guess, the computer tells you how close you are to each Mugwump. Playing the game with the aid of graph paper and a compass should allow you to find all the Mugwumps in six or seven moves using triangulation similar to Loran radio navigation.
    
    If you want to make the game somewhat more difficult, you can print the distance to each Mugwump either rounded or truncated to the nearest integer.
    
    This program was modified slightly by Bob Albrecht of People’s Computer Company. It was originally written by students of Bud Valenti of Project SOLO in Pittsburg, Pennsylvania.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=114)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=129)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 62_Mugwump/csharp/Distance.cs
    ================================================
    namespace Mugwump;
    
    internal struct Distance
    {
        private readonly float _value;
    
        public Distance(float deltaX, float deltaY)
        {
            _value = (float)Math.Sqrt(deltaX * deltaX + deltaY * deltaY);
        }
    
        public override string ToString() => _value.ToString("0.0");
    }
    
    
    ================================================
    FILE: 62_Mugwump/csharp/Game.cs
    ================================================
    using System.Reflection;
    
    namespace Mugwump;
    
    internal class Game
    {
        private readonly TextIO _io;
        private readonly IRandom _random;
    
        internal Game(TextIO io, IRandom random)
        {
            _io = io;
            _random = random;
        }
    
        internal void Play(Func playAgain = null)
        {
            DisplayIntro();
    
            while (playAgain?.Invoke() ?? true)
            {
                Play(new Grid(_io, _random));
    
                _io.WriteLine();
                _io.WriteLine("That was fun! Let's play again.......");
                _io.WriteLine("Four more mugwumps are now in hiding.");
            }
        }
    
        private void DisplayIntro()
        {
            using var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Mugwump.Strings.Intro.txt");
    
            _io.Write(stream);
        }
    
        private void Play(Grid grid)
        {
            for (int turn = 1; turn <= 10; turn++)
            {
                var guess = _io.ReadGuess($"Turn no. {turn} -- what is your guess");
    
                if (grid.Check(guess))
                {
                    _io.WriteLine();
                    _io.WriteLine($"You got them all in {turn} turns!");
                    return;
                }
            }
    
            _io.WriteLine();
            _io.WriteLine("Sorry, that's 10 tries.  Here is where they're hiding:");
            grid.Reveal();
        }
    }
    
    
    ================================================
    FILE: 62_Mugwump/csharp/Grid.cs
    ================================================
    using System.Collections.Generic;
    using System.Linq;
    
    namespace Mugwump;
    
    internal class Grid
    {
        private readonly TextIO _io;
        private readonly List _mugwumps;
    
        public Grid(TextIO io, IRandom random)
        {
            _io = io;
            _mugwumps = Enumerable.Range(1, 4).Select(id => new Mugwump(id, random.NextPosition(10, 10))).ToList();
        }
    
        public bool Check(Position guess)
        {
            foreach (var mugwump in _mugwumps.ToList())
            {
                var (found, distance) = mugwump.FindFrom(guess);
    
                _io.WriteLine(found ? $"You have found {mugwump}" : $"You are {distance} units from {mugwump}");
                if (found)
                {
                    _mugwumps.Remove(mugwump);
                }
            }
    
            return _mugwumps.Count == 0;
        }
    
        public void Reveal()
        {
            foreach (var mugwump in _mugwumps)
            {
                _io.WriteLine(mugwump.Reveal());
            }
        }
    }
    
    
    ================================================
    FILE: 62_Mugwump/csharp/IRandomExtensions.cs
    ================================================
    namespace Mugwump;
    
    internal static class IRandomExtensions
    {
        internal static Position NextPosition(this IRandom random, int maxX, int maxY) =>
            new(random.Next(maxX), random.Next(maxY));
    }
    
    
    ================================================
    FILE: 62_Mugwump/csharp/Mugwump.cs
    ================================================
    namespace Mugwump;
    
    internal class Mugwump
    {
        private readonly int _id;
        private readonly Position _position;
    
        public Mugwump(int id, Position position)
        {
            _id = id;
            _position = position;
        }
    
        public (bool, Distance) FindFrom(Position guess) => (guess == _position, guess - _position);
    
        public string Reveal() => $"{this} is at {_position}";
    
        public override string ToString() => $"Mugwump {_id}";
    }
    
    
    ================================================
    FILE: 62_Mugwump/csharp/Mugwump.csproj
    ================================================
    
    
      
        Exe
        net6.0
      
    
      
        
      
    
      
        
      
    
    
    
    
    ================================================
    FILE: 62_Mugwump/csharp/Mugwump.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 17
    VisualStudioVersion = 17.0.32014.148
    MinimumVisualStudioVersion = 15.0.26124.0
    Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mugwump", "Mugwump.csproj", "{DB23BDB0-10A4-4771-B942-E646A1A5C416}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{DB23BDB0-10A4-4771-B942-E646A1A5C416}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{DB23BDB0-10A4-4771-B942-E646A1A5C416}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{DB23BDB0-10A4-4771-B942-E646A1A5C416}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{DB23BDB0-10A4-4771-B942-E646A1A5C416}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {1FE34948-9066-4F57-A4C1-423293A430C5}
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 62_Mugwump/csharp/Position.cs
    ================================================
    namespace Mugwump;
    
    internal record struct Position(float X, float Y)
    {
        public override string ToString() => $"( {X} , {Y} )";
    
        public static Distance operator -(Position p1, Position p2) => new(p1.X - p2.X, p1.Y - p2.Y);
    }
    
    
    ================================================
    FILE: 62_Mugwump/csharp/Program.cs
    ================================================
    global using System;
    global using Games.Common.IO;
    global using Games.Common.Randomness;
    
    using Mugwump;
    
    var random = new RandomNumberGenerator();
    var io = new ConsoleIO();
    
    var game = new Game(io, random);
    
    game.Play();
    
    
    ================================================
    FILE: 62_Mugwump/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 62_Mugwump/csharp/Strings/Intro.txt
    ================================================
                                     Mugwump
                   Creative Computing  Morristown, New Jersey
    
    
    
    The object of this game is to find four mugwumps
    hidden on a 10 by 10 grid.  Homebase is position 0,0.
    Any guess you make must be two numbers with each
    number between 0 and 9, inclusive.  First number
    is distance to right of homebase and second number
    is distance above homebase.
    
    You get 10 tries.  After each try, I will tell
    you how far you are from each wugwump.
    
    
    ================================================
    FILE: 62_Mugwump/csharp/TextIOExtensions.cs
    ================================================
    namespace Mugwump;
    
    // Provides input methods which emulate the BASIC interpreter's keyboard input routines
    internal static class TextIOExtensions
    {
        internal static Position ReadGuess(this TextIO io, string prompt)
        {
            io.WriteLine();
            io.WriteLine();
            var (x, y) = io.Read2Numbers(prompt);
            return new Position(x, y);
        }
    }
    
    
    ================================================
    FILE: 62_Mugwump/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 62_Mugwump/java/src/Mugwump.java
    ================================================
    import java.util.Arrays;
    import java.util.Scanner;
    
    /**
     * Game of Mugwump
     * 

    * Based on the Basic game of Mugwump here * https://github.com/coding-horror/basic-computer-games/blob/main/62%20Mugwump/mugwump.bas *

    * Note: The idea was to create a version of the 1970's Basic game in Java, without introducing * new features - no additional text, error checking, etc has been added. */ public class Mugwump { public static final int NUMBER_OF_MUGWUMPS = 4; public static final int MAX_TURNS = 10; public static final int FOUND = -1; // Used for keyboard input private final Scanner kbScanner; private enum GAME_STATE { INIT, GAME_START, PLAY_TURN } // Current game state private GAME_STATE gameState; int[][] mugwumpLocations; int turn; public Mugwump() { kbScanner = new Scanner(System.in); gameState = GAME_STATE.INIT; } /** * Main game loop */ public void play() { do { switch (gameState) { case INIT: intro(); gameState = GAME_STATE.GAME_START; break; case GAME_START: turn = 0; // initialise all array elements with 0 mugwumpLocations = new int[NUMBER_OF_MUGWUMPS][2]; // Place 4 mugwumps for (int i = 0; i < NUMBER_OF_MUGWUMPS; i++) { for (int j = 0; j < 2; j++) { mugwumpLocations[i][j] = (int) (Math.random() * 10); } } gameState = GAME_STATE.PLAY_TURN; break; case PLAY_TURN: turn++; String locations = displayTextAndGetInput("TURN NO." + turn + " -- WHAT IS YOUR GUESS? "); int distanceRightGuess = getDelimitedValue(locations, 0); int distanceUpGuess = getDelimitedValue(locations, 1); int numberFound = 0; for (int i = 0; i < NUMBER_OF_MUGWUMPS; i++) { if (mugwumpLocations[i][0] == FOUND) { numberFound++; } int right = mugwumpLocations[i][0]; int up = mugwumpLocations[i][1]; if (right == distanceRightGuess && up == distanceUpGuess) { if (right != FOUND) { System.out.println("YOU HAVE FOUND MUGWUMP " + (i + 1)); mugwumpLocations[i][0] = FOUND; } numberFound++; } else { // Not found so show distance if (mugwumpLocations[i][0] != FOUND) { double distance = Math.sqrt((Math.pow(right - distanceRightGuess, 2.0d)) + (Math.pow(up - distanceUpGuess, 2.0d))); System.out.println("YOU ARE " + (int) ((distance * 10) / 10) + " UNITS FROM MUGWUMP"); } } } if (numberFound == NUMBER_OF_MUGWUMPS) { System.out.println("YOU GOT THEM ALL IN " + turn + " TURNS!"); gameState = GAME_STATE.GAME_START; } else if (turn >= MAX_TURNS) { System.out.println("SORRY, THAT'S " + MAX_TURNS + " TRIES. HERE IS WHERE THEY'RE HIDING"); for (int i = 0; i < NUMBER_OF_MUGWUMPS; i++) { if (mugwumpLocations[i][0] != FOUND) { System.out.println("MUGWUMP " + (i + 1) + " IS AT (" + mugwumpLocations[i][0] + "," + mugwumpLocations[i][1] + ")"); } } gameState = GAME_STATE.GAME_START; } // Game ended? if (gameState != GAME_STATE.PLAY_TURN) { System.out.println("THAT WAS FUN! LET'S PLAY AGAIN......."); System.out.println("FOUR MORE MUGWUMPS ARE NOW IN HIDING."); } } // Infinite loop - based on original basic version } while (true); } private void intro() { System.out.println(addSpaces(33) + "MUGWUMP"); System.out.println(addSpaces(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println(); System.out.println("THE OBJECT OF THIS GAME IS TO FIND FOUR MUGWUMPS"); System.out.println("HIDDEN ON A 10 BY 10 GRID. HOMEBASE IS POSITION 0,0."); System.out.println("ANY GUESS YOU MAKE MUST BE TWO NUMBERS WITH EACH"); System.out.println("NUMBER BETWEEN 0 AND 9, INCLUSIVE. FIRST NUMBER"); System.out.println("IS DISTANCE TO RIGHT OF HOMEBASE AND SECOND NUMBER"); System.out.println("IS DISTANCE ABOVE HOMEBASE."); System.out.println(); System.out.println("YOU GET 10 TRIES. AFTER EACH TRY, I WILL TELL"); System.out.println("YOU HOW FAR YOU ARE FROM EACH MUGWUMP."); } /** * Accepts a string delimited by comma's and returns the pos'th delimited * value (starting at count 0). * * @param text - text with values separated by comma's * @param pos - which position to return a value for * @return the int representation of the value */ private int getDelimitedValue(String text, int pos) { String[] tokens = text.split(","); return Integer.parseInt(tokens[pos]); } /* * Print a message on the screen, then accept input from Keyboard. * * @param text message to be displayed on screen. * @return what was typed by the player. */ private String displayTextAndGetInput(String text) { System.out.print(text); return kbScanner.nextLine(); } /** * Return a string of x spaces * * @param spaces number of spaces required * @return String with number of spaces */ private String addSpaces(int spaces) { char[] spacesTemp = new char[spaces]; Arrays.fill(spacesTemp, ' '); return new String(spacesTemp); } public static void main(String[] args) { Mugwump mugwump = new Mugwump(); mugwump.play(); } } ================================================ FILE: 62_Mugwump/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 62_Mugwump/javascript/mugwump.html ================================================ MUGWUMP

    
    
    
    
    
    
    ================================================
    FILE: 62_Mugwump/javascript/mugwump.js
    ================================================
    // MUGWUMP
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    var p = [];
    
    // Main program
    async function main()
    {
        print(tab(33) + "MUGWUMP\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        // Courtesy People's Computer Company
        print("THE OBJECT OF THIS GAME IS TO FIND FOUR MUGWUMPS\n");
        print("HIDDEN ON A 10 BY 10 GRID.  HOMEBASE IS POSITION 0,0.\n");
        print("ANY GUESS YOU MAKE MUST BE TWO NUMBERS WITH EACH\n");
        print("NUMBER BETWEEN 0 AND 9, INCLUSIVE.  FIRST NUMBER\n");
        print("IS DISTANCE TO RIGHT OF HOMEBASE AND SECOND NUMBER\n");
        print("IS DISTANCE ABOVE HOMEBASE.\n");
        print("\n");
        print("YOU GET 10 TRIES.  AFTER EACH TRY, I WILL TELL\n");
        print("YOU HOW FAR YOU ARE FROM EACH MUGWUMP.\n");
        print("\n");
        while (1) {
            for (i = 1; i <= 4; i++) {
                p[i] = [];
                for (j = 1; j <= 2; j++) {
                    p[i][j] = Math.floor(10 * Math.random());
                }
            }
            t = 0;
            do {
                t++;
                print("\n");
                print("\n");
                print("TURN NO. " + t + " -- WHAT IS YOUR GUESS");
                str = await input();
                m = parseInt(str);
                n = parseInt(str.substr(str.indexOf(",") + 1));
                for (i = 1; i <= 4; i++) {
                    if (p[i][1] == -1)
                        continue;
                    if (p[i][1] == m && p[i][2] == n) {
                        p[i][1] = -1;
                        print("YOU HAVE FOUND MUGWUMP " + i + "\n");
                    } else {
                        d = Math.sqrt(Math.pow(p[i][1] - m, 2) + Math.pow(p[i][2] - n, 2));
                        print("YOU ARE " + Math.floor(d * 10) / 10 + " UNITS FROM MUGWUMP " + i + "\n");
                    }
                }
                for (j = 1; j <= 4; j++) {
                    if (p[j][1] != -1)
                        break;
                }
                if (j > 4) {
                    print("\n");
                    print("YOU GOT THEM ALL IN " + t + " TURNS!\n");
                    break;
                }
            } while (t < 10) ;
            if (t == 10) {
                print("\n");
                print("SORRY, THAT'S 10 TRIES.  HERE IS WHERE THEY'RE HIDING:\n");
                for (i = 1; i <= 4; i++) {
                    if (p[i][1] != -1)
                        print("MUGWUMP " + i + " IS AT (" + p[i][1] + "," + p[i][2] + ")\n");
                }
            }
            print("\n");
            print("THAT WAS FUN! LET'S PLAY AGAIN.......\n");
            print("FOUR MORE MUGWUMPS ARE NOW IN HIDING.\n");
        }
    }
    
    main();
    
    
    ================================================
    FILE: 62_Mugwump/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 62_Mugwump/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 62_Mugwump/mugwump.bas
    ================================================
    1 PRINT TAB(33);"MUGWUMP"
    2 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    3 PRINT:PRINT:PRINT
    4 REM     COURTESY PEOPLE'S COMPUTER COMPANY
    10 DIM P(4,2)
    20 PRINT "THE OBJECT OF THIS GAME IS TO FIND FOUR MUGWUMPS"
    30 PRINT "HIDDEN ON A 10 BY 10 GRID.  HOMEBASE IS POSITION 0,0."
    40 PRINT "ANY GUESS YOU MAKE MUST BE TWO NUMBERS WITH EACH"
    50 PRINT "NUMBER BETWEEN 0 AND 9, INCLUSIVE.  FIRST NUMBER"
    60 PRINT "IS DISTANCE TO RIGHT OF HOMEBASE AND SECOND NUMBER"
    70 PRINT "IS DISTANCE ABOVE HOMEBASE."
    80 PRINT
    90 PRINT "YOU GET 10 TRIES.  AFTER EACH TRY, I WILL TELL"
    100 PRINT "YOU HOW FAR YOU ARE FROM EACH MUGWUMP."
    110 PRINT
    240 GOSUB 1000
    250 T=0
    260 T=T+1
    270 PRINT
    275 PRINT
    290 PRINT "TURN NO.";T;"-- WHAT IS YOUR GUESS";
    300 INPUT M,N
    310 FOR I=1 TO 4
    320 IF P(I,1)=-1 THEN 400
    330 IF P(I,1)<>M THEN 380
    340 IF P(I,2)<>N THEN 380
    350 P(I,1)=-1
    360 PRINT "YOU HAVE FOUND MUGWUMP";I
    370 GOTO 400
    380 D=SQR((P(I,1)-M)^2+(P(I,2)-N)^2)
    390 PRINT "YOU ARE";(INT(D*10))/10;"UNITS FROM MUGWUMP";I
    400 NEXT I
    410 FOR J=1 TO 4
    420 IF P(J,1)<>-1 THEN 470
    430 NEXT J
    440 PRINT
    450 PRINT "YOU GOT THEM ALL IN";T;"TURNS!"
    460 GOTO 580
    470 IF T<10 THEN 260
    480 PRINT
    490 PRINT "SORRY, THAT'S 10 TRIES.  HERE IS WHERE THEY'RE HIDING:"
    540 FOR I=1 TO 4
    550 IF P(I,1)=-1 THEN 570
    560 PRINT "MUGWUMP";I;"IS AT (";P(I,1);",";P(I,2);")"
    570 NEXT I
    580 PRINT
    600 PRINT "THAT WAS FUN! LET'S PLAY AGAIN......."
    610 PRINT "FOUR MORE MUGWUMPS ARE NOW IN HIDING."
    630 GOTO 240
    1000 FOR J=1 TO 2
    1010 FOR I=1 TO 4
    1020 P(I,J)=INT(10*RND(1))
    1030 NEXT I
    1040 NEXT J
    1050 RETURN
    1099 END
    
    
    ================================================
    FILE: 62_Mugwump/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 62_Mugwump/perl/mugwump.pl
    ================================================
    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    # global variables defined here
    my(@MUGWUMP) = ();
    
    # subroutines defined here
    
    # init_mugwump: pick the random places for the Mugwumps
    sub init_mugwump() {
        @MUGWUMP = ();
        for (1 .. 4) {
            push @MUGWUMP, [ int(rand 10), int(rand 10) ];
        }
    }
    
    
    # main code starts here
    
    # print introductory text
    print <
    \n"); for (x = 1; x <= 2; x++) { str = await input(); y = parseInt(str); z = parseInt(str.substr(str.indexOf(",") + 1)); ba[y][z] = 1; } print("DESTROYER\n"); for (x = 1; x <= 2; x++) { str = await input(); y = parseInt(str); z = parseInt(str.substr(str.indexOf(",") + 1)); ba[y][z] = 0.5; } while (1) { print("DO YOU WANT TO START"); js = await input(); if (js == "WHERE ARE YOUR SHIPS?") { print("BATTLESHIP\n"); for (z = 1; z <= 5; z++) print(" " + fa[z] + " " + ga[z] + "\n"); print("CRUISER\n"); print(" " + fa[6] + " " + ga[6] + "\n"); print(" " + fa[7] + " " + ga[7] + "\n"); print(" " + fa[8] + " " + ga[8] + "\n"); print("DESTROYER\n"); print(" " + fa[9] + " " + ga[9] + "\n"); print(" " + fa[10] + " " + ga[10] + "\n"); print("DESTROYER\n"); print(" " + fa[11] + " " + ga[11] + "\n"); print(" " + fa[12] + " " + ga[12] + "\n"); } else { break; } } c = 0; print("DO YOU WANT TO SEE MY SHOTS"); ks = await input(); print("\n"); if (js != "YES") first_time = true; else first_time = false; while (1) { if (first_time) { first_time = false; } else { if (js == "YES") { c++; print("\n"); print("TURN " + c + "\n"); } a = 0; for (w = 0.5; w <= 3; w += 0.5) { loop1: for (x = 1; x <= 10; x++) { for (y = 1; y <= 10; y++) { if (ba[x][y] == w) { a += Math.floor(w + 0.5); break loop1; } } } } for (w = 1; w <= 7; w++) { ca[w] = 0; da[w] = 0; fa[w] = 0; ga[w] = 0; } p3 = 0; for (x = 1; x <= 10; x++) { for (y = 1; y <= 10; y++) { if (aa[x][y] <= 10) p3++; } } print("YOU HAVE " + a + " SHOTS.\n"); if (p3 < a) { print("YOU HAVE MORE SHOTS THAN THERE ARE BLANK SQUARES.\n"); print("YOU HAVE WON.\n"); return; } if (a == 0) { print("I HAVE WON.\n"); return; } for (w = 1; w <= a; w++) { while (1) { str = await input(); x = parseInt(str); y = parseInt(str.substr(str.indexOf(",") + 1)); if (x >= 1 && x <= 10 && y >= 1 && y <= 10) { if (aa[x][y] > 10) { print("YOU SHOT THERE BEFORE ON TURN " + (aa[x][y] - 10) + "\n"); continue; } break; } print("ILLEGAL, ENTER AGAIN.\n"); } ca[w] = x; da[w] = y; } for (w = 1; w <= a; w++) { if (aa[ca[w]][da[w]] == 3) { print("YOU HIT MY BATTLESHIP.\n"); } else if (aa[ca[w]][da[w]] == 2) { print("YOU HIT MY CRUISER.\n"); } else if (aa[ca[w]][da[w]] == 1) { print("YOU HIT MY DESTROYER.\n"); } else if (aa[ca[w]][da[w]] == 0.5) { print("YOU HIT MY DESTROYER.\n"); } aa[ca[w]][da[w]] = 10 + c; } } a = 0; if (js != "YES") { c++; print("\n"); print("TURN " + c + "\n"); } a = 0; for (w = 0.5; w <= 3; w += 0.5) { loop2: for (x = 1; x <= 10; x++) { for (y = 1; y <= 10; y++) { if (ba[x][y] == w) { a += Math.floor(w + 0.5); break loop2; } } } } p3 = 0; for (x = 1; x <= 10; x++) { for (y = 1; y <= 10; y++) { if (aa[x][y] <= 10) p3++; } } print("I HAVE " + a + " SHOTS.\n"); if (p3 < a) { print("I HAVE MORE SHOTS THAN BLANK SQUARES.\n"); print("I HAVE WON.\n"); return; } if (a == 0) { print("YOU HAVE WON.\n"); return; } for (w = 1; w <= 12; w++) { if (ha[w] > 0) break; } if (w <= 12) { for (r = 1; r <= 10; r++) { ka[r] = []; for (s = 1; s <= 10; s++) ka[r][s] = 0; } for (u = 1; u <= 12; u++) { if (ea[u] >= 10) continue; for (r = 1; r <= 10; r++) { for (s = 1; s <= 10; s++) { if (ba[r][s] >= 10) { ka[r][s] = -10000000; } else { for (m = sgn(1 - r); m <= sgn(10 - r); m++) { for (n = sgn(1 - s); n <= sgn(10 - s); n++) { if (n + m + n * m != 0 && ba[r + m][s + n] == ea[u]) ka[r][s] += ea[u] - s * Math.floor(ha[u] + 0.5); } } } } } } for (r = 1; r <= a; r++) { fa[r] = r; ga[r] = r; } for (r = 1; r <= 10; r++) { for (s = 1; s <= 10; s++) { q9 = 1; for (m = 1; m <= a; m++) { if (ka[fa[m]][ga[m]] < ka[fa[q9]][ga[q9]]) q9 = m; } if ((r > a || r != s) && ka[r][s] >= ka[fa[q9]][ga[q9]]) { for (m = 1; m <= a; m++) { if (fa[m] != r) { fa[q9] = r; ga[q9] = s; break; } if (ga[m] == s) break; } } } } } else { // RANDOM w = 0; r3 = 0; generate_random(); r2 = 0; while (1) { r3++; if (r3 > 100) { generate_random(); r2 = 0; r3 = 1; } if (x > 10) { x = 10 - Math.floor(Math.random() * 2.5); } else if (x <= 0) { x = 1 + Math.floor(Math.random() * 2.5); } if (y > 10) { y = 10 - Math.floor(Math.random() * 2.5); } else if (y <= 0) { y = 1 + Math.floor(Math.random() * 2.5); } while (1) { valid = true; if (x < 1 || x > 10 || y < 1 || y > 10 || ba[x][y] > 10) { valid = false; } else { for (q9 = 1; q9 <= w; q9++) { if (fa[q9] == x && ga[q9] == y) { valid = false; break; } } if (q9 > w) w++; } if (valid) { fa[w] = x; ga[w] = y; if (w == a) { finish = true; break; } } if (r2 == 6) { r2 = 0; finish = false; break; } x1 = [1,-1, 1,1,0,-1][r2]; y1 = [1, 1,-3,1,2, 1][r2]; r2++; x += x1; y += y1; } if (finish) break; } } if (ks == "YES") { for (z5 = 1; z5 <= a; z5++) print(" " + fa[z5] + " " + ga[z5] + "\n"); } for (w = 1; w <= a; w++) { hit = false; if (ba[fa[w]][ga[w]] == 3) { print("I HIT YOUR BATTLESHIP.\n"); hit = true; } else if (ba[fa[w]][ga[w]] == 2) { print("I HIT YOUR CRUISER.\n"); hit = true; } else if (ba[fa[w]][ga[w]] == 1) { print("I HIT YOUR DESTROYER.\n"); hit = true; } else if (ba[fa[w]][ga[w]] == 0.5) { print("I HIT YOUR DESTROYER.\n"); hit = true; } if (hit) { for (q = 1; q <= 12; q++) { if (ea[q] != -1) continue; ea[q] = 10 + c; ha[q] = ba[fa[w]][ga[w]]; m3 = 0; for (m2 = 1; m2 <= 12; m2++) { if (ha[m2] == ha[q]) m3++; } if (m3 == Math.floor(ha[q] + 0.5) + 1 + Math.floor(Math.floor(ha[q] + 0.5) / 3)) { for (m2 = 1; m2 <= 12; m2++) { if (ha[m2] == ha[q]) { ea[m2] = -1; ha[m2] = -1; } } } break; } if (q > 12) { print("PROGRAM ABORT:\n"); for (q = 1; q <= 12; q++) { print("ea[" + q + "] = " + ea[q] + "\n"); print("ha[" + q + "] = " + ha[q] + "\n"); } return; } } ba[fa[w]][ga[w]] = 10 + c; } } } main(); ================================================ FILE: 77_Salvo/kotlin/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Kotlin](https://kotlinlang.org/) ================================================ FILE: 77_Salvo/lua/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Lua](https://www.lua.org/) ================================================ FILE: 77_Salvo/perl/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Perl](https://www.perl.org/) ================================================ FILE: 77_Salvo/python/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Python](https://www.python.org/about/) ================================================ FILE: 77_Salvo/python/salvo.py ================================================ import random import re from typing import List, Optional, Tuple BoardType = List[List[Optional[int]]] CoordinateType = Tuple[int, int] BOARD_WIDTH = 10 BOARD_HEIGHT = 10 # data structure keeping track of information # about the ships in the game. for each ship, # the following information is provided: # # name - string representation of the ship # length - number of "parts" on the ship that # can be shot # shots - number of shots the ship counts for SHIPS = [ ("BATTLESHIP", 5, 3), ("CRUISER", 3, 2), ("DESTROYER", 2, 1), ("DESTROYER", 2, 1), ] VALID_MOVES = [ [-1, 0], # North [-1, 1], # North East [0, 1], # East [1, 1], # South East [1, 0], # South [1, -1], # South West [0, -1], # West [-1, -1], # North West ] COORD_REGEX = "[ \t]{0,}(-?[0-9]{1,3})[ \t]{0,},[ \t]{0,}(-?[0-9]{1,2})" # array of BOARD_HEIGHT arrays, BOARD_WIDTH in length, # representing the human player and computer player_board: BoardType = [] computer_board: BoardType = [] # array representing the coordinates # for each ship for player and computer # array is in the same order as SHIPS computer_ship_coords: List[List[CoordinateType]] = [] #################################### # # SHOTS # # The number of shots computer/player # has is determined by the shot "worth" # of each ship the computer/player # possesses. As long as the ship has one # part not hit (i.e., ship was not # sunk), the player gets all the shots # from that ship. # flag indicating if computer's shots are # printed out during computer's turn print_computer_shots = False # keep track of the number # of available computer shots # inital shots are 7 num_computer_shots = 7 # keep track of the number # of available player shots # initial shots are 7 num_player_shots = 7 # # SHOTS # #################################### # flag indicating whose turn it currently is COMPUTER = False PLAYER = True active_turn = COMPUTER #################### # # game functions # #################### # random number functions # # seed the random number generator random.seed() # random_x_y # def random_x_y() -> CoordinateType: """Generate a valid x,y coordinate on the board""" x = random.randrange(1, BOARD_WIDTH + 1) y = random.randrange(1, BOARD_HEIGHT + 1) return (x, y) def input_coord() -> CoordinateType: """ Ask user for single (x,y) coordinate validate the coordinates are within the bounds of the board width and height. mimic the behavior of the original program which exited with error messages if coordinates where outside of array bounds. if input is not numeric, print error out to user and let them try again. """ match = None while not match: coords = input("? ") match = re.match(COORD_REGEX, coords) if not match: print("!NUMBER EXPECTED - RETRY INPUT LINE") x = int(match.group(1)) y = int(match.group(2)) if x > BOARD_HEIGHT or y > BOARD_WIDTH: print("!OUT OF ARRAY BOUNDS IN LINE 1540") exit() if x <= 0 or y <= 0: print("!NEGATIVE ARRAY DIM IN LINE 1540") exit() return x, y def generate_ship_coordinates(ship: int) -> List[CoordinateType]: """ given a ship from the SHIPS array, generate the coordinates of the ship. the starting point of the ship's first coordinate is generated randomly. once the starting coordinates are determined, the possible directions of the ship, accounting for the edges of the board, are determined. once possible directions are found, a direction is randomly determined and the remaining coordinates are generated by adding or substraction from the starting coordinates as determined by direction. arguments: ship - index into the SHIPS array returns: array of sets of coordinates (x,y) """ # randomly generate starting x,y coordinates start_x, start_y = random_x_y() # using starting coordinates and the ship type, # generate a vector of possible directions the ship # could be placed. directions are numbered 0-7 along # points of the compass (N, NE, E, SE, S, SW, W, NW) # 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 _ 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] dirs[4] = (start_x + ship_len) <= BOARD_HEIGHT dirs[3] = dirs[2] and dirs[4] dirs[6] = (start_y - ship_len) >= 1 dirs[5] = dirs[4] and dirs[6] dirs[7] = dirs[6] and dirs[0] directions = [p for p in range(len(dirs)) if dirs[p]] # using the vector of valid directions, pick a # random direction to place the ship dir_idx = random.randrange(len(directions)) direction = directions[dir_idx] # using the starting x,y, direction and ship # type, return the coordinates of each point # of the ship. VALID_MOVES is a staic array # of coordinate offsets to walk from starting # coordinate to the end coordinate in the # chosen direction ship_len = SHIPS[ship][1] - 1 d_x = VALID_MOVES[direction][0] d_y = VALID_MOVES[direction][1] coords = [(start_x, start_y)] x_coord = start_x y_coord = start_y for _ in range(ship_len): x_coord = x_coord + d_x y_coord = y_coord + d_y coords.append((x_coord, y_coord)) return coords def create_blank_board() -> BoardType: """Create a blank game board""" return [[None for _y in range(BOARD_WIDTH)] for _x in range(BOARD_HEIGHT)] def print_board(board: BoardType) -> None: """Print out the game board for testing purposes""" # print board header (column numbers) print(" ", end="") for z in range(BOARD_WIDTH): print(f"{z+1:3}", end="") print() for x in range(len(board)): print(f"{x+1:2}", end="") for y in range(len(board[x])): if board[x][y] is None: print(f"{' ':3}", end="") else: print(f"{board[x][y]:3}", end="") print() def place_ship(board: BoardType, coords: List[CoordinateType], ship: int) -> None: """ Place a ship on a given board. updates the board's row,column value at the given coordinates to indicate where a ship is on the board. inputs: board - array of BOARD_HEIGHT by BOARD_WIDTH coords - array of sets of (x,y) coordinates of each part of the given ship ship - integer representing the type of ship (given in SHIPS) """ for coord in coords: board[coord[0] - 1][coord[1] - 1] = ship def generate_board() -> Tuple[BoardType, List[List[CoordinateType]]]: """ NOTE: A little quirk that exists here and in the orginal game: Ships are allowed to cross each other! For example: 2 destroyers, length 2, one at [(1,1),(2,2)] and other at [(2,1),(1,2)] """ board = create_blank_board() ship_coords = [] for ship in range(len(SHIPS)): placed = False coords = [] while not placed: coords = generate_ship_coordinates(ship) clear = all(board[coord[0] - 1][coord[1] - 1] is None for coord in coords) if clear: placed = True place_ship(board, coords, ship) ship_coords.append(coords) return board, ship_coords def execute_shot( turn: bool, board: BoardType, x: int, y: int, current_turn: int ) -> int: """ given a board and x, y coordinates, execute a shot. returns True if the shot is valid, False if not """ square = board[x - 1][y - 1] ship_hit = -1 if square is not None and square >= 0 and square < len(SHIPS): ship_hit = square board[x - 1][y - 1] = 10 + current_turn return ship_hit def calculate_shots(board: BoardType) -> int: """Examine each board and determine how many shots remaining""" 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 return sum( SHIPS[ship][2] for ship in range(len(ships_found)) if ships_found[ship] == 1 ) def initialize_game() -> None: # initialize the global player and computer boards global player_board player_board = create_blank_board() # generate the ships for the computer's board global computer_board global computer_ship_coords computer_board, computer_ship_coords = generate_board() # print out the title 'screen' print("{:>38}".format("SALVO")) print("{:>57s}".format("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY")) print() print("{:>52s}".format("ORIGINAL BY LAWRENCE SIEGEL, 1973")) print("{:>56s}".format("PYTHON 3 PORT BY TODD KAISER, MARCH 2021")) print("\n") # ask the player for ship coordinates print("ENTER COORDINATES FOR...") ship_coords = [] for ship in SHIPS: print(ship[0]) list = [] for _ in range(ship[1]): x, y = input_coord() list.append((x, y)) ship_coords.append(list) # add ships to the user's board for ship_index in range(len(SHIPS)): place_ship(player_board, ship_coords[ship_index], ship_index) # see if the player wants the computer's ship # locations printed out and if the player wants to # start input_loop = True player_start = "YES" while input_loop: player_start = input("DO YOU WANT TO START? ") if player_start == "WHERE ARE YOUR SHIPS?": for ship_index in range(len(SHIPS)): print(SHIPS[ship_index][0]) coords = computer_ship_coords[ship_index] for coord in coords: x = coord[0] y = coord[1] print(f"{x:2}", f"{y:2}") else: input_loop = False # ask the player if they want the computer's shots # printed out each turn global print_computer_shots see_computer_shots = input("DO YOU WANT TO SEE MY SHOTS? ") if see_computer_shots.lower() == "yes": print_computer_shots = True global first_turn if player_start.lower() != "yes": first_turn = COMPUTER # calculate the initial number of shots for each global num_computer_shots, num_player_shots num_player_shots = calculate_shots(player_board) num_computer_shots = calculate_shots(computer_board) #################################### # # Turn Control # # define functions for executing the turns for # the player and the computer. By defining this as # functions, we can easily start the game with # either computer or player and alternate back and # forth, replicating the gotos in the original game # initialize the first_turn function to the player's turn first_turn = PLAYER def execute_turn(turn: bool, current_turn: int) -> int: global num_computer_shots, num_player_shots # print out the number of shots the current player has board = None num_shots = 0 if turn == COMPUTER: print(f"I HAVE {num_computer_shots} SHOTS.") board = player_board num_shots = num_computer_shots else: print(f"YOU HAVE {num_player_shots} SHOTS.") board = computer_board num_shots = num_player_shots shots = [] for _shot in range(num_shots): valid_shot = False x = -1 y = -1 # loop until we have a valid shot. for the # computer, we randomly pick a shot. for the # player we request shots while not valid_shot: 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: print("YOU SHOT THERE BEFORE ON TURN", square - 10) continue shots.append((x, y)) valid_shot = True hits = [] for shot in shots: hit = execute_shot(turn, board, shot[0], shot[1], current_turn) if hit >= 0: hits.append(hit) if turn == COMPUTER and print_computer_shots: print(shot[0], shot[1]) for hit in hits: if turn == COMPUTER: print("I HIT YOUR", SHIPS[hit][0]) else: print("YOU HIT MY", SHIPS[hit][0]) if turn == COMPUTER: num_player_shots = calculate_shots(board) return num_player_shots else: num_computer_shots = calculate_shots(board) return num_computer_shots # # Turn Control # ###################################### def main() -> None: current_turn = 0 initialize_game() # execute turns until someone wins or we run # out of squares to shoot game_over = False while not game_over: current_turn += 1 print("\n") print("TURN", current_turn) if ( execute_turn(first_turn, current_turn) == 0 or execute_turn(not first_turn, current_turn) == 0 ): game_over = True continue if __name__ == "__main__": main() ================================================ FILE: 77_Salvo/ruby/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Ruby](https://www.ruby-lang.org/en/) ================================================ FILE: 77_Salvo/salvo.bas ================================================ 1000 PRINT TAB(33);"SALVO" 1010 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 1020 PRINT:PRINT:PRINT 1030 REM 1040 DIM A(10,10),B(10,10),C(7),D(7),E(12),F(12),G(12),H(12),K(10,10) 1050 Z8=0 1060 FOR W=1 TO 12 1070 E(W)=-1 1080 H(W)=-1 1090 NEXT W 1100 FOR X=1 TO 10 1110 FOR Y=1 TO 10 1120 B(X,Y)=0 1130 NEXT Y 1140 NEXT X 1150 FOR X=1 TO 12 1160 F(X)=0 1170 G(X)=0 1180 NEXT X 1190 FOR X=1 TO 10 1200 FOR Y=1 TO 10 1210 A(X,Y)=0 1220 NEXT Y 1230 NEXT X 1240 FOR K=4 TO 1 STEP -1 1250 U6=0 1260 GOSUB 2910 1270 DEF FNA(K)=(5-K)*3-2*INT(K/4)+SGN(K-1)-1 1280 DEF FNB(K)=K+INT(K/4)-SGN(K-1) 1290 IF V+V2+V*V2=0 THEN 1260 1300 IF Y+V*FNB(K)>10 THEN 1260 1310 IF Y+V*FNB(K)<1 THEN 1260 1320 IF X+V2*FNB(K)>10 THEN 1260 1330 IF X+V2*FNB(K)<1 THEN 1260 1340 U6=U6+1 1350 IF U6>25 THEN 1190 1360 FOR Z=0 TO FNB(K) 1370 F(Z+FNA(K))=X+V2*Z 1380 G(Z+FNA(K))=Y+V*Z 1390 NEXT Z 1400 U8=FNA(K) 1405 IF U8>U8+FNB(K) THEN 1460 1410 FOR Z2= U8 TO U8+FNB(K) 1415 IF U8<2 THEN 1450 1420 FOR Z3=1 TO U8-1 1430 IF SQR((F(Z3)-F(Z2))^2 + (G(Z3)-G(Z2))^2) < 3.59 THEN 1260 1440 NEXT Z3 1450 NEXT Z2 1460 FOR Z=0 TO FNB(K) 1470 A(F(Z+U8),G(Z+U8))=.5+SGN(K-1)*(K-1.5) 1480 NEXT Z 1490 NEXT K 1500 PRINT "ENTER COORDINATES FOR..." 1510 PRINT "BATTLESHIP" 1520 FOR X=1 TO 5 1530 INPUT Y,Z 1540 B(Y,Z)=3 1550 NEXT X 1560 PRINT "CRUISER" 1570 FOR X=1 TO 3 1580 INPUT Y,Z 1590 B(Y,Z)=2 1600 NEXT X 1610 PRINT "DESTROYER" 1620 FOR X=1 TO 2 1630 INPUT Y,Z 1640 B(Y,Z)=1 1650 NEXT X 1660 PRINT "DESTROYER" 1670 FOR X=1 TO 2 1680 INPUT Y,Z 1690 B(Y,Z)=.5 1700 NEXT X 1710 PRINT "DO YOU WANT TO START"; 1720 INPUT J$ 1730 IF J$<>"WHERE ARE YOUR SHIPS?" THEN 1890 1740 PRINT "BATTLESHIP" 1750 FOR Z=1 TO 5 1760 PRINT F(Z);G(Z) 1770 NEXT Z 1780 PRINT "CRUISER" 1790 PRINT F(6);G(6) 1800 PRINT F(7);G(7) 1810 PRINT F(8);G(8) 1820 PRINT "DESTROYER" 1830 PRINT F(9);G(9) 1840 PRINT F(10);G(10) 1850 PRINT "DESTROYER" 1860 PRINT F(11);G(11) 1870 PRINT F(12);G(12) 1880 GOTO 1710 1890 C=0 1900 PRINT "DO YOU WANT TO SEE MY SHOTS"; 1910 INPUT K$ 1920 PRINT 1930 IF J$<>"YES" THEN 2620 1940 REM*******************START 1950 IF J$<>"YES" THEN 1990 1960 C=C+1 1970 PRINT 1980 PRINT "TURN";C 1990 A=0 2000 FOR W=.5 TO 3 STEP .5 2010 FOR X=1 TO 10 2020 FOR Y=1 TO 10 2030 IF B(X,Y)=W THEN 2070 2040 NEXT Y 2050 NEXT X 2060 GOTO 2080 2070 A=A+INT(W+.5) 2080 NEXT W 2090 FOR W=1 TO 7 2100 C(W)=0 2110 D(W)=0 2120 F(W)=0 2130 G(W)=0 2140 NEXT W 2150 P3=0 2160 FOR X=1 TO 10 2170 FOR Y=1 TO 10 2180 IF A(X,Y)>10 THEN 2200 2190 P3=P3+1 2200 NEXT Y 2210 NEXT X 2220 PRINT "YOU HAVE";A;"SHOTS." 2230 IF P3>=A THEN 2260 2240 PRINT "YOU HAVE MORE SHOTS THAN THERE ARE BLANK SQUARES." 2250 GOTO 2890 2260 IF A<>0 THEN 2290 2270 PRINT "I HAVE WON." 2280 STOP 2290 FOR W=1 TO A 2300 INPUT X,Y 2310 IF X<>INT(X) THEN 2370 2320 IF X>10 THEN 2370 2330 IF X<1 THEN 2370 2340 IF Y<>INT(Y) THEN 2370 2350 IF Y>10 THEN 2370 2360 IF Y>=1 THEN 2390 2370 PRINT "ILLEGAL, ENTER AGAIN." 2380 GOTO 2300 2390 IF A(X,Y)>10 THEN 2440 2400 C(W)=X 2410 D(W)=Y 2420 NEXT W 2430 GOTO 2460 2440 PRINT "YOU SHOT THERE BEFORE ON TURN";A(X,Y)-10 2450 GOTO 2300 2460 FOR W=1 TO A 2470 IF A(C(W),D(W))=3 THEN 2540 2480 IF A(C(W),D(W))=2 THEN 2560 2490 IF A(C(W),D(W))=1 THEN 2580 2500 IF A(C(W),D(W))=.5 THEN 2600 2510 A(C(W),D(W))=10+C 2520 NEXT W 2530 GOTO 2620 2540 PRINT "YOU HIT MY BATTLESHIP." 2550 GOTO 2510 2560 PRINT "YOU HIT MY CRUISER." 2570 GOTO 2510 2580 PRINT "YOU HIT MY DESTROYER." 2590 GOTO 2510 2600 PRINT "YOU HIT MY DESTROYER." 2610 GOTO 2510 2620 A=0 2630 IF J$="YES" THEN 2670 2640 C=C+1 2650 PRINT 2660 PRINT "TURN";C 2670 A=0 2680 FOR W=.5 TO 3 STEP .5 2690 FOR X=1 TO 10 2700 FOR Y=1 TO 10 2710 IF A(X,Y)=W THEN 2750 2720 NEXT Y 2730 NEXT X 2740 GOTO 2760 2750 A=A+INT(W+.5) 2760 NEXT W 2770 P3=0 2780 FOR X=1 TO 10 2790 FOR Y=1 TO 10 2800 IF A(X,Y)>10 THEN 2820 2810 P3=P3+1 2820 NEXT Y 2830 NEXT X 2840 PRINT "I HAVE";A;"SHOTS." 2850 IF P3>A THEN 2880 2860 PRINT "I HAVE MORE SHOTS THAN BLANK SQUARES." 2870 GOTO 2270 2880 IF A<>0 THEN 2960 2890 PRINT "YOU HAVE WON." 2900 STOP 2910 X=INT(RND(1)*10+1) 2920 Y=INT(RND(1)*10+1) 2930 V=INT(3*RND(1)-1) 2940 V2=INT(3*RND(1)-1) 2950 RETURN 2960 FOR W=1 TO 12 2970 IF H(W)>0 THEN 3800 2980 NEXT W 2990 REM*******************RANDOM 3000 W=0 3010 R3=0 3020 GOSUB 2910 3030 RESTORE 3040 R2=0 3050 R3=R3+1 3060 IF R3>100 THEN 3010 3070 IF X>10 THEN 3110 3080 IF X>0 THEN 3120 3090 X=1+INT(RND(1)*2.5) 3100 GOTO 3120 3110 X=10-INT(RND(1)*2.5) 3120 IF Y>10 THEN 3160 3130 IF Y>0 THEN 3270 3140 Y=1+INT(RND(1)*2.5) 3150 GOTO 3270 3160 Y=10-INT(RND(1)*2.5) 3170 GOTO 3270 3180 F(W)=X 3190 G(W)=Y 3200 IF W=A THEN 3380 3210 IF R2=6 THEN 3030 3220 READ X1,Y1 3230 R2=R2+1 3240 DATA 1,1,-1,1,1,-3,1,1,0,2,-1,1 3250 X=X+X1 3260 Y=Y+Y1 3270 IF X>10 THEN 3210 3280 IF X<1 THEN 3210 3290 IF Y>10 THEN 3210 3300 IF Y<1 THEN 3210 3310 IF B(X,Y)>10 THEN 3210 3320 FOR Q9=1 TO W 3330 IF F(Q9)<>X THEN 3350 3340 IF G(Q9)=Y THEN 3210 3350 NEXT Q9 3360 W=W+1 3370 GOTO 3180 3380 IF K$<>"YES" THEN 3420 3390 FOR Z5=1 TO A 3400 PRINT F(Z5);G(Z5) 3410 NEXT Z5 3420 FOR W=1 TO A 3430 IF B(F(W),G(W))=3 THEN 3500 3440 IF B(F(W),G(W))=2 THEN 3520 3450 IF B(F(W),G(W))=1 THEN 3560 3460 IF B(F(W),G(W))=.5 THEN 3540 3470 B(F(W),G(W))=10+C 3480 NEXT W 3490 GOTO 1950 3500 PRINT "I HIT YOUR BATTLESHIP" 3510 GOTO 3570 3520 PRINT "I HIT YOUR CRUISER" 3530 GOTO 3570 3540 PRINT "I HIT YOUR DESTROYER" 3550 GOTO 3570 3560 PRINT "I HIT YOUR DESTROYER" 3570 FOR Q=1 TO 12 3580 IF E(Q)<>-1 THEN 3730 3590 E(Q)=10+C 3600 H(Q)=B(F(W),G(W)) 3610 M3=0 3620 FOR M2=1 TO 12 3630 IF H(M2)<>H(Q) THEN 3650 3640 M3=M3+1 3650 NEXT M2 3660 IF M3<>INT(H(Q)+.5)+1+INT(INT(H(Q)+.5)/3) THEN 3470 3670 FOR M2=1 TO 12 3680 IF H(M2)<>H(Q) THEN 3710 3690 E(M2)=-1 3700 H(M2)=-1 3710 NEXT M2 3720 GOTO 3470 3730 NEXT Q 3740 PRINT "PROGRAM ABORT:" 3750 FOR Q=1 TO 12 3760 PRINT "E(";Q;") =";E(Q) 3770 PRINT "H(";Q;") =";H(Q) 3780 NEXT Q 3790 STOP 3800 REM************************USINGEARRAY 3810 FOR R=1 TO 10 3820 FOR S=1 TO 10 3830 K(R,S)=0 3840 NEXT S 3850 NEXT R 3860 FOR U=1 TO 12 3870 IF E(U)<10 THEN 4020 3880 FOR R=1 TO 10 3890 FOR S=1 TO 10 3900 IF B(R,S)<10 THEN 3930 3910 K(R,S)=-10000000 3920 GOTO 4000 3930 FOR M=SGN(1-R) TO SGN(10-R) 3940 FOR N=SGN(1-S) TO SGN(10-S) 3950 IF N+M+N*M=0 THEN 3980 3960 IF B(R+M,S+N)<>E(U) THEN 3980 3970 K(R,S)=K(R,S)+E(U)-S*INT(H(U)+.5) 3980 NEXT N 3990 NEXT M 4000 NEXT S 4010 NEXT R 4020 NEXT U 4030 FOR R=1 TO A 4040 F(R)=R 4050 G(R)=R 4060 NEXT R 4070 FOR R=1 TO 10 4080 FOR S=1 TO 10 4090 Q9=1 4100 FOR M=1 TO A 4110 IF K(F(M),G(M))>=K(F(Q9),G(Q9)) THEN 4130 4120 Q9=M 4130 NEXT M 4131 IF R>A THEN 4140 4132 IF R=S THEN 4210 4140 IF K(R,S)R THEN 4190 4170 IF G(M)=S THEN 4210 4180 NEXT M 4190 F(Q9)=R 4200 G(Q9)=S 4210 NEXT S 4220 NEXT R 4230 GOTO 3380 4240 END ================================================ FILE: 77_Salvo/vbnet/README.md ================================================ Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET) ================================================ FILE: 77_Salvo/vbnet/Salvo.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30114.105 MinimumVisualStudioVersion = 10.0.40219.1 Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Salvo", "Salvo.vbproj", "{849885BF-24BD-4FEB-A224-A9502742D4B0}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {849885BF-24BD-4FEB-A224-A9502742D4B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {849885BF-24BD-4FEB-A224-A9502742D4B0}.Debug|Any CPU.Build.0 = Debug|Any CPU {849885BF-24BD-4FEB-A224-A9502742D4B0}.Release|Any CPU.ActiveCfg = Release|Any CPU {849885BF-24BD-4FEB-A224-A9502742D4B0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal ================================================ FILE: 77_Salvo/vbnet/Salvo.vbproj ================================================ Exe Salvo net6.0 16.9 ================================================ FILE: 78_Sine_Wave/README.md ================================================ ### 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 of different versions. At least those CRTs don’t look so lifeless anymore. --- As published in Basic Computer Games (1978): - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=146) - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=161) Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html #### Porting Notes (please note any difficulties or challenges in porting here) ================================================ FILE: 78_Sine_Wave/csharp/Program.cs ================================================ using System; Console.WriteLine(Tab(30) + "Sine Wave"); Console.WriteLine(Tab(15) + "Creative Computing Morristown, New Jersey\n\n\n\n\n"); bool isCreative = true; for (double t = 0.0; t <= 40.0; t += 0.25) { int a = (int)(26 + 25 * Math.Sin(t)); string word = isCreative ? "Creative" : "Computing"; Console.WriteLine($"{Tab(a)}{word}"); isCreative = !isCreative; } static string Tab(int n) => new string(' ', n); ================================================ FILE: 78_Sine_Wave/csharp/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/) ================================================ FILE: 78_Sine_Wave/csharp/SineWave.csproj ================================================ Exe net5.0 ================================================ FILE: 78_Sine_Wave/csharp/SineWave.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.31005.135 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SineWave", "SineWave.csproj", "{730FA2CC-5AA5-4BE2-8DF9-8E55FDC8FB30}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {730FA2CC-5AA5-4BE2-8DF9-8E55FDC8FB30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {730FA2CC-5AA5-4BE2-8DF9-8E55FDC8FB30}.Debug|Any CPU.Build.0 = Debug|Any CPU {730FA2CC-5AA5-4BE2-8DF9-8E55FDC8FB30}.Release|Any CPU.ActiveCfg = Release|Any CPU {730FA2CC-5AA5-4BE2-8DF9-8E55FDC8FB30}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {32A37343-2955-4124-8765-9143F6C529DC} EndGlobalSection EndGlobal ================================================ FILE: 78_Sine_Wave/java/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Oracle Java](https://openjdk.java.net/) ================================================ FILE: 78_Sine_Wave/java/src/SineWave.java ================================================ /** * Sine Wave * * Based on the Sine Wave program here * https://github.com/coding-horror/basic-computer-games/blob/main/78%20Sine%20Wave/sinewave.bas * * Note: The idea was to create a version of the 1970's Basic program in Java, without introducing * new features - no additional text, error checking, etc has been added. */ public class SineWave { public static void main(String[] args) { System.out.println(""" SINE WAVE CREATIVE COMPUTING MORRISTOWN, NEW JERSEY """); var isCreative = true; for(var t = 0d; t<40; t += .25) { //Indent output var indentations = 26 + (int) (25 * Math.sin(t)); System.out.print(" ".repeat(indentations)); //Change output every iteration var word = isCreative ? "CREATIVE" : "COMPUTING"; System.out.println(word); isCreative = !isCreative ; } } } ================================================ FILE: 78_Sine_Wave/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 78_Sine_Wave/javascript/sinewave.mjs ================================================ #!/usr/bin/env node import { println, tab } from '../../00_Common/javascript/common.mjs'; println(tab(30), "SINE WAVE"); println(tab(15), "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); println("\n".repeat(4)); // REMARKABLE PROGRAM BY DAVID AHL // Transliterated to Javascript by Les Orchard let toggleWord = true; for (let step = 0; step < 40; step += 0.25) { let indent = Math.floor(26 + 25 * Math.sin(step)); println(tab(indent), toggleWord ? "CREATIVE" : "COMPUTING"); toggleWord = !toggleWord; } ================================================ FILE: 78_Sine_Wave/kotlin/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Kotlin](https://kotlinlang.org/) ================================================ FILE: 78_Sine_Wave/lua/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Lua](https://www.lua.org/) ================================================ FILE: 78_Sine_Wave/lua/sinewave.lua ================================================ require("math") require("string") print("\n Sine Wave") print(" Creative Computing Morriston, New Jersy") print("\n\n\n\n") -- Original BASIC version by David Ahl -- Ported to lua by BeeverFeever(github), 2022 local toggleWord = true for t = 0, 40, 0.25 do local gap = math.floor(26 + 25 * math.sin(t)) if toggleWord == true then -- string.rep used to add the gat at the front of the printed out words print(string.rep(" ", math.floor(gap)) .. "Creative") elseif toggleWord == false then print(string.rep(" ", math.floor(gap)) .. "Computing") end end ================================================ FILE: 78_Sine_Wave/perl/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Perl](https://www.perl.org/) ================================================ FILE: 78_Sine_Wave/perl/sinewave.pl ================================================ #!/usr/bin/perl use strict; use warnings; print ' ' x 30 ."SINE WAVE\n"; print ' ' x 15 ."CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n"; print "\n\n\n\n\n"; my $B=0; for (my $T=0; $T<40; $T+=.25) { my $A=int(26+25*sin($T)); print ' ' x $A; if ($B==0) { print "CREATIVE\n"; } if ($B==1) { print "COMPUTING\n"; } $B= !$B; #Toggle } ================================================ FILE: 78_Sine_Wave/python/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Python](https://www.python.org/about/) ================================================ FILE: 78_Sine_Wave/python/sinewave.py ================================================ ######################################################## # # Sine Wave # # From: BASIC Computer Games (1978) # Edited by David H. Ahl # # "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." # # Original BASIC version by David Ahl # # Python port by Jeff Jetton, 2019 # ######################################################## import math import time def main() -> None: # Constants STRINGS = ("Creative", "Computing") # Text to display MAX_LINES = 160 STEP_SIZE = 0.25 # Number of radians to increase at each # line. Controls speed of horizontal # printing movement. CENTER = 26 # Controls left edge of "middle" string DELAY = 0.05 # Amount of seconds to wait between lines # Display "intro" text print("\n Sine Wave") print(" Creative Computing Morristown, New Jersey") print("\n\n\n\n") # "REMarkable program by David Ahl" string_index = 0 radians: float = 0 width = CENTER - 1 # "Start long loop" for _line_num in range(MAX_LINES): # Get string to display on this line curr_string = STRINGS[string_index] # Calculate how far over to print the text sine = math.sin(radians) padding = int(CENTER + width * sine) print(curr_string.rjust(padding + len(curr_string))) # Increase radians and increment our tuple index radians += STEP_SIZE string_index += 1 if string_index >= len(STRINGS): string_index = 0 # Make sure the text doesn't fly by too fast... time.sleep(DELAY) if __name__ == "__main__": main() ######################################################## # # Porting Notes # # The original BASIC version hardcoded two words in # the body of the code and then used a sentinel flag # (flipping between 0 and 1) with IF statements to # determine the word to display next. # # Here, the words have been moved to a Python tuple, # which is iterated over without any assumptions about # how long it is. The STRINGS tuple can therefore be # modified to have to program print out any sequence # of any number of lines of text. # # Since a modern computer running Python will print # to the screen much more quickly than a '70s-era # computer running BASIC would, a delay component # has been introduced in this version to make the # output more historically accurate. # # # Ideas for Modifications # # Ask the user for desired number of lines (perhaps # with an "infinite" option) and/or step size. # # Let the user input the text strings to display, # rather than having it pre-defined in a constant. # Calculate an appropriate CENTER based on length of # longest string. # # Try changing STINGS so that it only includes a # single string, just like this: # # STRINGS = ('Howdy!') # # What happens? Why? How would you fix it? # ######################################################## ================================================ FILE: 78_Sine_Wave/ruby/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Ruby](https://www.ruby-lang.org/en/) ================================================ FILE: 78_Sine_Wave/ruby/sinewave.rb ================================================ def intro puts " SINE WAVE CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n\n\n\n" end def main intro (0..40).step(0.25).each do |t| a = (26 + 25 * Math.sin(t)).to_i text = (t % 0.5) == 0 ? "CREATIVE" : "COMPUTING" puts " " * a + text end end main ================================================ FILE: 78_Sine_Wave/rust/Cargo.toml ================================================ [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] ================================================ FILE: 78_Sine_Wave/rust/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Rust](https://www.rust-lang.org/) by Anthony Rubick [AnthonyMichaelTDM](https://github.com/AnthonyMichaelTDM) ================================================ FILE: 78_Sine_Wave/rust/src/main.rs ================================================ fn main() { let mut ticker:f64 = 0.0; let mut spaces ; //pring welcome message welcome(); //drawing loop loop { //print however many spaces spaces = (26.0 + 25.0*ticker.sin()).round() as i32; for _i in 0..=spaces{ print!(" "); //print a space } //print Creative or Computing if (ticker.round() as i32) % 2 == 0 { println!("CREATIVE"); } else { println!("COMPUTING"); } //increment ticker ticker += 0.25; } } /** * prints welcome message */ fn welcome() { println!(" SINE WAVE CREATIVE COMPUTING MORRISTOWN, NEW JERSEY "); } ================================================ FILE: 78_Sine_Wave/sinewave.bas ================================================ 10 PRINT TAB(30);"SINE WAVE" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 30 PRINT: PRINT: PRINT: PRINT: PRINT 40 REMARKABLE PROGRAM BY DAVID AHL 50 B=0 100 REM START LONG LOOP 110 FOR T=0 TO 40 STEP .25 120 A=INT(26+25*SIN(T)) 130 PRINT TAB(A); 140 IF B=1 THEN 180 150 PRINT "CREATIVE" 160 B=1 170 GOTO 200 180 PRINT "COMPUTING" 190 B=0 200 NEXT T 999 END ================================================ FILE: 78_Sine_Wave/vbnet/README.md ================================================ Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET) ================================================ FILE: 78_Sine_Wave/vbnet/SineWave.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30114.105 MinimumVisualStudioVersion = 10.0.40219.1 Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "SineWave", "SineWave.vbproj", "{204DD0CD-1DA5-4B4B-992F-1C3ED089A147}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {204DD0CD-1DA5-4B4B-992F-1C3ED089A147}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {204DD0CD-1DA5-4B4B-992F-1C3ED089A147}.Debug|Any CPU.Build.0 = Debug|Any CPU {204DD0CD-1DA5-4B4B-992F-1C3ED089A147}.Release|Any CPU.ActiveCfg = Release|Any CPU {204DD0CD-1DA5-4B4B-992F-1C3ED089A147}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal ================================================ FILE: 78_Sine_Wave/vbnet/SineWave.vbproj ================================================ Exe SineWave net6.0 16.9 ================================================ FILE: 79_Slalom/README.md ================================================ ### Slalom This game simulates a slalom run down a course with one to 25 gates. The user picks the number of gates and has some control over his speed down the course. If you’re not a skier, here’s your golden opportunity to try it with minimal risk. If you are a skier, here’s something to do while your leg is in a cast. SLALOM was written by J. Panek while a student at Dartmouth College. --- As published in Basic Computer Games (1978): - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=147) - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=162) Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html #### 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'. - 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. #### Porting Notes ================================================ FILE: 79_Slalom/csharp/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/) ================================================ FILE: 79_Slalom/csharp/Slalom.csproj ================================================ Exe net6.0 10 enable enable ================================================ FILE: 79_Slalom/csharp/Slalom.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30114.105 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Slalom", "Slalom.csproj", "{6D0607CF-B01C-4E17-A4DE-D15514AE5F84}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {6D0607CF-B01C-4E17-A4DE-D15514AE5F84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6D0607CF-B01C-4E17-A4DE-D15514AE5F84}.Debug|Any CPU.Build.0 = Debug|Any CPU {6D0607CF-B01C-4E17-A4DE-D15514AE5F84}.Release|Any CPU.ActiveCfg = Release|Any CPU {6D0607CF-B01C-4E17-A4DE-D15514AE5F84}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal ================================================ FILE: 79_Slalom/csharp/program.cs ================================================ using System.Text; namespace Slalom { class Slalom { private int[] GateMaxSpeed = { 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 }; private int GoldMedals = 0; private int SilverMedals = 0; private int BronzeMedals = 0; private void DisplayIntro() { Console.WriteLine(""); Console.WriteLine("SLALOM".PadLeft(23)); Console.WriteLine("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); Console.WriteLine(""); } private void DisplayInstructions() { Console.WriteLine(); Console.WriteLine("*** Slalom: This is the 1976 Winter Olympic Giant Slalom. You are"); Console.WriteLine(" the American team's only hope of a gold medal."); Console.WriteLine(); Console.WriteLine(" 0 -- Type this if you want to see how long you've taken."); Console.WriteLine(" 1 -- Type this if you want to speed up a lot."); Console.WriteLine(" 2 -- Type this if you want to speed up a little."); Console.WriteLine(" 3 -- Type this if you want to speed up a teensy."); Console.WriteLine(" 4 -- Type this if you want to keep going the same speed."); Console.WriteLine(" 5 -- Type this if you want to check a teensy."); Console.WriteLine(" 6 -- Type this if you want to check a litte."); Console.WriteLine(" 7 -- Type this if you want to check a lot."); Console.WriteLine(" 8 -- Type this if you want to cheat and try to skip a gate."); Console.WriteLine(); Console.WriteLine(" The place to use these options is when the computer asks:"); Console.WriteLine(); Console.WriteLine("Option?"); Console.WriteLine(); Console.WriteLine(" Good Luck!"); Console.WriteLine(); } private bool PromptYesNo(string Prompt) { bool Success = false; while (!Success) { Console.Write(Prompt); string LineInput = Console.ReadLine().Trim().ToLower(); if (LineInput.Equals("yes")) return true; else if (LineInput.Equals("no")) return false; else Console.WriteLine("Please type 'YES' or 'NO'"); } return false; } private int PromptForGates() { bool Success = false; int NumberOfGates = 0; while (!Success) { Console.Write("How many gates does this course have (1 to 25) "); string LineInput = Console.ReadLine().Trim().ToLower(); if (int.TryParse(LineInput, out NumberOfGates)) { if (NumberOfGates >= 1 && NumberOfGates <= 25) { Success = true; } else if (NumberOfGates < 1) { Console.WriteLine("Try again,"); } else // greater than 25 { Console.WriteLine("25 is the limit."); NumberOfGates = 25; Success = true; } } else { Console.WriteLine("Try again,"); } } return NumberOfGates; } private int PromptForRate() { bool Success = false; int Rating = 0; while (!Success) { Console.Write("Rate yourself as a skier, (1=worst, 3=best) "); string LineInput = Console.ReadLine().Trim().ToLower(); if (int.TryParse(LineInput, out Rating)) { if (Rating >= 1 && Rating <= 3) { Success = true; } else { Console.WriteLine("The bounds are 1-3"); } } else { Console.WriteLine("The bounds are 1-3"); } } return Rating; } private int PromptForOption() { bool Success = false; int Option = 0; while (!Success) { Console.Write("Option? "); string LineInput = Console.ReadLine().Trim().ToLower(); if (int.TryParse(LineInput, out Option)) { if (Option >= 0 && Option <= 8) { Success = true; } else if (Option > 8) { Console.WriteLine("What?"); } } else { Console.WriteLine("What?"); } } return Option; } private string PromptForCommand() { bool Success = false; string Result = ""; Console.WriteLine(); Console.WriteLine("Type \"INS\" for intructions"); Console.WriteLine("Type \"MAX\" for approximate maximum speeds"); Console.WriteLine("Type \"RUN\" for the beginning of the race"); while (!Success) { Console.Write("Command--? "); string LineInput = Console.ReadLine().Trim().ToLower(); if (LineInput.Equals("ins") || LineInput.Equals("max") || LineInput.Equals("run")) { Result = LineInput; Success = true; } else { Console.WriteLine(); Console.WriteLine(); Console.WriteLine("\"{0}\" is an illegal command--retry", LineInput); } } return Result; } private bool ExceedGateSpeed(double MaxGateSpeed, double MPH, double Time) { Random rand = new Random(); Console.WriteLine("{0:N0} M.P.H.", MPH); if (MPH > MaxGateSpeed) { Console.Write("You went over the maximum speed "); if (rand.NextDouble() < ((MPH - (double)MaxGateSpeed) * 0.1) + 0.2) { Console.WriteLine("and made it!"); } else { if (rand.NextDouble() < 0.5) { Console.WriteLine("snagged a flag!"); } else { Console.WriteLine("wiped out!"); } Console.WriteLine("You took {0:N2} seconds", rand.NextDouble() + Time); return false; } } else if (MPH > (MaxGateSpeed - 1)) { Console.WriteLine("Close one!"); } return true; } private void DoARun(int NumberOfGates, int Rating) { Random rand = new Random(); double MPH = 0; double Time = 0; int Option = 0; double MaxGateSpeed = 0; // Q double PreviousMPH = 0; double Medals = 0; Console.WriteLine("The starter counts down...5...4...3...2...1...GO!"); MPH = rand.NextDouble() * (18-9)+9; Console.WriteLine(); Console.WriteLine("You're off!"); for (int GateNumber = 1; GateNumber <= NumberOfGates; GateNumber++) { MaxGateSpeed = GateMaxSpeed[GateNumber-1]; Console.WriteLine(); Console.WriteLine("Here comes Gate # {0}:", GateNumber); Console.WriteLine("{0:N0} M.P.H.", MPH); PreviousMPH = MPH; Option = PromptForOption(); while (Option == 0) { Console.WriteLine("You've taken {0:N2} seconds.", Time); Option = PromptForOption(); } switch (Option) { case 1: MPH = MPH + (rand.NextDouble() * (10-5)+5); if (ExceedGateSpeed(MaxGateSpeed, MPH, Time)) break; else return; case 2: MPH = MPH + (rand.NextDouble() * (5-3)+3); if (ExceedGateSpeed(MaxGateSpeed, MPH, Time)) break; else return; case 3: MPH = MPH + (rand.NextDouble() * (4-1)+1); if (ExceedGateSpeed(MaxGateSpeed, MPH, Time)) break; else return; case 4: if (ExceedGateSpeed(MaxGateSpeed, MPH, Time)) break; else return; case 5: MPH = MPH - (rand.NextDouble() * (4-1)+1); if (ExceedGateSpeed(MaxGateSpeed, MPH, Time)) break; else return; case 6: MPH = MPH - (rand.NextDouble() * (5-3)+3); if (ExceedGateSpeed(MaxGateSpeed, MPH, Time)) break; else return; case 7: MPH = MPH - (rand.NextDouble() * (10-5)+5); if (ExceedGateSpeed(MaxGateSpeed, MPH, Time)) break; else return; case 8: // Cheat! Console.WriteLine("***Cheat"); if (rand.NextDouble() < 0.7) { Console.WriteLine("An official caught you!"); Console.WriteLine("You took {0:N2} seconds.", Time); return; } else { Console.WriteLine("You made it!"); Time = Time + 1.5; } break; } if (MPH < 7) { Console.WriteLine("Let's be realistic, OK? Let's go back and try again..."); MPH = PreviousMPH; } else { Time = Time + (MaxGateSpeed - MPH + 1); if (MPH > MaxGateSpeed) { Time = Time + 0.5; } } } Console.WriteLine(); Console.WriteLine("You took {0:N2} seconds.", Time); Medals = Time; Medals = Medals / NumberOfGates; if (Medals < (1.5 - (Rating * 0.1))) { Console.WriteLine("You won a gold medal!"); GoldMedals++; } else if (Medals < (2.9 - (Rating * 0.1))) { Console.WriteLine("You won a silver medal!"); SilverMedals++; } else if (Medals < (4.4 - (Rating * 0.01))) { Console.WriteLine("You won a bronze medal!"); BronzeMedals++; } } private void PlayOneRound() { int NumberOfGates = 0; string Command = "first"; bool KeepPlaying = false; int Rating = 0; Console.WriteLine(""); NumberOfGates = PromptForGates(); while (!Command.Equals("")) { Command = PromptForCommand(); // Display instructions if (Command.Equals("ins")) { DisplayInstructions(); } else if (Command.Equals("max")) { Console.WriteLine("Gate Max"); Console.WriteLine(" # M.P.H."); Console.WriteLine("----------"); for (int i = 0; i < NumberOfGates; i++) { Console.WriteLine(" {0} {1}", i+1, GateMaxSpeed[i]); } } else // do a run! { Rating = PromptForRate(); do { DoARun(NumberOfGates, Rating); KeepPlaying = PromptYesNo("Do you want to race again? "); } while (KeepPlaying); Console.WriteLine("Thanks for the race"); if (GoldMedals > 0) Console.WriteLine("Gold Medals: {0}", GoldMedals); if (SilverMedals > 0) Console.WriteLine("Silver Medals: {0}", SilverMedals); if (BronzeMedals > 0) Console.WriteLine("Bronze Medals: {0}", BronzeMedals); return; } } } public void PlayTheGame() { DisplayIntro(); PlayOneRound(); } } class Program { static void Main(string[] args) { new Slalom().PlayTheGame(); } } } ================================================ FILE: 79_Slalom/java/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Oracle Java](https://openjdk.java.net/) ================================================ FILE: 79_Slalom/java/Slalom.java ================================================ import java.util.Arrays; import java.util.InputMismatchException; import java.util.Random; import java.util.Scanner; /** * Slalom *

    * Converted from BASIC to Java by Aldrin Misquitta (@aldrinm) * * There is a bug in the original version where the data pointer doesn't reset after a race is completed. This causes subsequent races to error at * some future point on line "540 READ Q" */ public class Slalom { private static final int MAX_NUM_GATES = 25; private static final int[] MAX_SPEED = { 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 }; public static void main(String[] args) { var random = new Random(); printIntro(); Scanner scanner = new Scanner(System.in); int numGates = readNumberOfGatesChoice(scanner); printMenu(); MenuChoice menuChoice; do { menuChoice = readMenuOption(scanner); switch (menuChoice) { case INS: printInstructions(); break; case MAX: printApproxMaxSpeeds(numGates); break; case RUN: run(numGates, scanner, random); break; } } while (menuChoice != MenuChoice.RUN); } private static void run(int numGates, Scanner scan, Random random) { int rating = readSkierRating(scan); boolean gameInProgress = true; var medals = new Medals(0, 0, 0); while (gameInProgress) { System.out.println("THE STARTER COUNTS DOWN...5...4...3...2...1...GO!"); System.out.println("YOU'RE OFF!"); int speed = random.nextInt(18 - 9) + 9; float totalTimeTaken = 0; try { totalTimeTaken = runThroughGates(numGates, scan, random, speed); System.out.printf("%nYOU TOOK %.2f SECONDS.%n", totalTimeTaken + random.nextFloat()); medals = evaluateAndUpdateMedals(totalTimeTaken, numGates, rating, medals); } catch (WipedOutOrSnaggedAFlag | DisqualifiedException e) { //end of this race! Print time taken and stop System.out.printf("%nYOU TOOK %.2f SECONDS.%n", totalTimeTaken + random.nextFloat()); } gameInProgress = readRaceAgainChoice(scan); } System.out.println("THANKS FOR THE RACE"); if (medals.getGold() >= 1) System.out.printf("GOLD MEDALS: %d%n", medals.getGold()); if (medals.getSilver() >= 1) System.out.printf("SILVER MEDALS: %d%n", medals.getSilver()); if (medals.getBronze() >= 1) System.out.printf("BRONZE MEDALS: %d%n", medals.getBronze()); } private static Medals evaluateAndUpdateMedals(float totalTimeTaken, int numGates, int rating, Medals medals) { var m = totalTimeTaken; m = m / numGates; int goldMedals = medals.getGold(); int silverMedals = medals.getSilver(); int bronzeMedals = medals.getBronze(); if (m < 1.5 - (rating * 0.1)) { System.out.println("YOU WON A GOLD MEDAL!"); goldMedals++; } else if (m < 2.9 - rating * 0.1) { System.out.println("YOU WON A SILVER MEDAL"); silverMedals++; } else if (m < 4.4 - rating * 0.01) { System.out.println("YOU WON A BRONZE MEDAL"); bronzeMedals++; } return new Medals(goldMedals, silverMedals, bronzeMedals); } /** * @return the total time taken through all the gates. */ private static float runThroughGates(int numGates, Scanner scan, Random random, int speed) throws DisqualifiedException, WipedOutOrSnaggedAFlag { float totalTimeTaken = 0.0f; for (int i = 0; i < numGates; i++) { var gateNum = i + 1; boolean stillInRace = true; boolean gateCompleted = false; while (!gateCompleted) { System.out.printf("%nHERE COMES GATE # %d:%n", gateNum); printSpeed(speed); var tmpSpeed = speed; int chosenOption = readOption(scan); switch (chosenOption) { case 0: //how long printHowLong(totalTimeTaken, random); break; case 1: //speed up a lot speed = speed + random.nextInt(10 - 5) + 5; break; case 2: //speed up a little speed = speed + random.nextInt(5 - 3) + 3; break; case 3: //speed up a teensy speed = speed + random.nextInt(4 - 1) + 1; break; case 4: //keep going at the same speed break; case 5: //check a teensy speed = speed - random.nextInt(4 - 1) + 1; break; case 6: //check a little speed = speed - random.nextInt(5 - 3) + 3; break; case 7: //check a lot speed = speed - random.nextInt(10 - 5) + 5; break; case 8: //cheat System.out.println("***CHEAT"); if (random.nextFloat() < 0.7) { System.out.println("AN OFFICIAL CAUGHT YOU!"); stillInRace = false; } else { System.out.println("YOU MADE IT!"); totalTimeTaken = totalTimeTaken + 1.5f; } break; } if (stillInRace) { printSpeed(speed); stillInRace = checkAndProcessIfOverMaxSpeed(random, speed, MAX_SPEED[i]); if (!stillInRace) throw new WipedOutOrSnaggedAFlag(); } else { throw new DisqualifiedException();//we've been dis-qualified } if (speed < 7) { System.out.println("LET'S BE REALISTIC, OK? LET'S GO BACK AND TRY AGAIN..."); speed = tmpSpeed; gateCompleted = false; } else { totalTimeTaken = totalTimeTaken + (MAX_SPEED[i] - speed + 1); if (speed > MAX_SPEED[i]) { totalTimeTaken = totalTimeTaken + 0.5f; } gateCompleted = true; } } } return totalTimeTaken; } private static boolean checkAndProcessIfOverMaxSpeed(Random random, int speed, int maxSpeed) { boolean stillInRace = true; if (speed > maxSpeed) { if (random.nextFloat() >= (speed - maxSpeed) * 0.1 + 0.2) { System.out.println("YOU WENT OVER THE MAXIMUM SPEED AND MADE IT!"); } else { System.out.print("YOU WENT OVER THE MAXIMUM SPEED AND "); if (random.nextBoolean()) { System.out.println("WIPED OUT!"); } else { System.out.println("SNAGGED A FLAG!"); } stillInRace = false; } } else if (speed > maxSpeed - 1) { System.out.println("CLOSE ONE!"); } return stillInRace; } private static boolean readRaceAgainChoice(Scanner scan) { System.out.print("\nDO YOU WANT TO RACE AGAIN? "); String raceAgain = ""; final String YES = "YES"; final String NO = "NO"; while (!YES.equals(raceAgain) && !NO.equals(raceAgain)) { raceAgain = scan.nextLine(); if (!(YES.equals(raceAgain) || NO.equals(raceAgain))) { System.out.println("PLEASE TYPE 'YES' OR 'NO'"); } } return raceAgain.equals(YES); } private static void printSpeed(int speed) { System.out.printf("%3d M.P.H.%n", speed); } private static void printHowLong(float t, Random random) { System.out.printf("YOU'VE TAKEN %.2f SECONDS.%n", t + random.nextFloat()); } private static int readOption(Scanner scan) { Integer option = null; while (option == null) { System.out.print("OPTION? "); try { option = scan.nextInt(); } catch (InputMismatchException ex) { System.out.println("!NUMBER EXPECTED - RETRY INPUT LINE\n"); } scan.nextLine(); if (option != null && (option > 8 || option < 0)) { System.out.println("WHAT?"); option = null; } } return option; } private static int readSkierRating(Scanner scan) { int rating = 0; while (rating < 1 || rating > 3) { System.out.print("RATE YOURSELF AS A SKIER, (1=WORST, 3=BEST)? "); try { rating = scan.nextInt(); if (rating < 1 || rating > 3) { System.out.println("THE BOUNDS ARE 1-3"); } } catch (InputMismatchException ex) { System.out.println("!NUMBER EXPECTED - RETRY INPUT LINE\n"); } scan.nextLine(); } return rating; } private static void printApproxMaxSpeeds(int numGates) { System.out.println("GATE MAX"); System.out.println(" # M.P.H."); System.out.println("---------"); for (int i = 0; i < numGates; i++) { System.out.println((i+1) + " " + MAX_SPEED[i]); } } private static void printInstructions() { System.out.println("\n*** SLALOM: THIS IS THE 1976 WINTER OLYMPIC GIANT SLALOM. YOU ARE"); System.out.println(" THE AMERICAN TEAM'S ONLY HOPE OF A GOLD MEDAL."); System.out.println(); System.out.println(" 0 -- TYPE THIS IS YOU WANT TO SEE HOW LONG YOU'VE TAKEN."); System.out.println(" 1 -- TYPE THIS IF YOU WANT TO SPEED UP A LOT."); System.out.println(" 2 -- TYPE THIS IF YOU WANT TO SPEED UP A LITTLE."); System.out.println(" 3 -- TYPE THIS IF YOU WANT TO SPEED UP A TEENSY."); System.out.println(" 4 -- TYPE THIS IF YOU WANT TO KEEP GOING THE SAME SPEED."); System.out.println(" 5 -- TYPE THIS IF YOU WANT TO CHECK A TEENSY."); System.out.println(" 6 -- TYPE THIS IF YOU WANT TO CHECK A LITTLE."); System.out.println(" 7 -- TYPE THIS IF YOU WANT TO CHECK A LOT."); System.out.println(" 8 -- TYPE THIS IF YOU WANT TO CHEAT AND TRY TO SKIP A GATE."); System.out.println(); System.out.println(" THE PLACE TO USE THESE OPTIONS IS WHEN THE COMPUTER ASKS:"); System.out.println(); System.out.println("OPTION?"); System.out.println(); System.out.println(" GOOD LUCK!"); } private static MenuChoice readMenuOption(Scanner scan) { System.out.print("COMMAND--? "); MenuChoice menuChoice = null; while (menuChoice == null) { String choice = scan.next(); if (Arrays.stream(MenuChoice.values()).anyMatch(a -> a.name().equals(choice))) { menuChoice = MenuChoice.valueOf(choice); } else { System.out.print("\""+ choice + "\" IS AN ILLEGAL COMMAND--RETRY? "); } scan.nextLine(); } return menuChoice; } private static void printMenu() { System.out.println("TYPE INS FOR INSTRUCTIONS"); System.out.println("TYPE MAX FOR APPROXIMATE MAXIMUM SPEEDS"); System.out.println("TYPE RUN FOR THE BEGINNING OF THE RACE"); } private static int readNumberOfGatesChoice(Scanner scan) { int numGates = 0; while (numGates < 1) { System.out.print("HOW MANY GATES DOES THIS COURSE HAVE (1 TO 25)? "); numGates = scan.nextInt(); if (numGates > MAX_NUM_GATES) { System.out.println(MAX_NUM_GATES + " IS THE LIMIT."); numGates = MAX_NUM_GATES; } } return numGates; } private static void printIntro() { System.out.println(" SLALOM"); System.out.println(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println("\n\n"); } private enum MenuChoice { INS, MAX, RUN } private static class DisqualifiedException extends Exception { } private static class WipedOutOrSnaggedAFlag extends Exception { } private static class Medals { private int gold = 0; private int silver = 0; private int bronze = 0; public Medals(int gold, int silver, int bronze) { this.gold = gold; this.silver = silver; this.bronze = bronze; } public int getGold() { return gold; } public int getSilver() { return silver; } public int getBronze() { return bronze; } } } ================================================ FILE: 79_Slalom/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 79_Slalom/javascript/slalom.html ================================================ SLALOM

    
    
    
    
    
    
    ================================================
    FILE: 79_Slalom/javascript/slalom.js
    ================================================
    // SLALOM
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    var speed = [,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];
    
    function show_instructions()
    {
        print("\n");
        print("*** SLALOM: THIS IS THE 1976 WINTER OLYMPIC GIANT SLALOM.  YOU ARE\n");
        print("            THE AMERICAN TEAM'S ONLY HOPE OF A GOLD MEDAL.\n");
        print("\n");
        print("     0 -- TYPE THIS IS YOU WANT TO SEE HOW LONG YOU'VE TAKEN.\n");
        print("     1 -- TYPE THIS IF YOU WANT TO SPEED UP A LOT.\n");
        print("     2 -- TYPE THIS IF YOU WANT TO SPEED UP A LITTLE.\n");
        print("     3 -- TYPE THIS IF YOU WANT TO SPEED UP A TEENSY.\n");
        print("     4 -- TYPE THIS IF YOU WANT TO KEEP GOING THE SAME SPEED.\n");
        print("     5 -- TYPE THIS IF YOU WANT TO CHECK A TEENSY.\n");
        print("     6 -- TYPE THIS IF YOU WANT TO CHECK A LITTLE.\n");
        print("     7 -- TYPE THIS IF YOU WANT TO CHECK A LOT.\n");
        print("     8 -- TYPE THIS IF YOU WANT TO CHEAT AND TRY TO SKIP A GATE.\n");
        print("\n");
        print(" THE PLACE TO USE THESE OPTIONS IS WHEN THE COMPUTER ASKS:\n");
        print("\n");
        print("OPTION?\n");
        print("\n");
        print("                GOOD LUCK!\n");
        print("\n");
    }
    
    function show_speeds()
    {
        print("GATE MAX\n");
        print(" #  M.P.H.\n");
        print("----------\n");
        for (var b = 1; b <= v; b++) {
            print(" " + b + "  " + speed[b] + "\n");
        }
    }
    
    // Main program
    async function main()
    {
        var gold = 0;
        var silver = 0;
        var bronze = 0;
    
        print(tab(33) + "SLALOM\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        while (1) {
            print("HOW MANY GATES DOES THIS COURSE HAVE (1 TO 25)");
            v = parseInt(await input());
            if (v >= 25) {
                print("25 IS THE LIMIT\n");
                v = 25;
            } else if (v < 1) {
                print("TRY AGAIN.\n");
            } else {
                break;
            }
        }
        print("\n");
        print("TYPE \"INS\" FOR INSTRUCTIONS\n");
        print("TYPE \"MAX\" FOR APPROXIMATE MAXIMUM SPEEDS\n");
        print("TYPE \"RUN\" FOR THE BEGINNING OF THE RACE\n");
        while (1) {
            print("COMMAND--");
            str = await input();
            if (str == "INS") {
                show_instructions();
            } else if (str == "MAX") {
                show_speeds();
            } else if (str == "RUN") {
                break;
            } else {
                print("\"" + str + "\" IS AN ILLEGAL COMMAND--RETRY");
            }
        }
        while (1) {
            print("RATE YOURSELF AS A SKIER, (1=WORST, 3=BEST)");
            a = parseInt(await input());
            if (a < 1 || a > 3)
                print("THE BOUNDS ARE 1-3\n");
            else
                break;
        }
        while (1) {
            print("THE STARTER COUNTS DOWN...5...4...3...2...1...GO!");
            t = 0;
            s = Math.floor(Math.random(1) * (18 - 9) + 9);
            print("\n");
            print("YOU'RE OFF!\n");
            for (o = 1; o <= v; o++) {
                q = speed[o];
                print("\n");
                print("HERE COMES GATE #" + o + " :\n");
                print(s + " M.P.H.\n");
                s1 = s;
                while (1) {
                    print("OPTION");
                    o1 = parseInt(await input());
                    if (o1 < 0 || o1 > 8)
                        print("WHAT?\n");
                    else if (o1 == 0)
                        print("YOU'VE TAKEN " + (t + Math.random()) + " SECONDS.\n");
                    else
                        break;
                }
                finish = false;
                switch (o1) {
                    case 1:
                        s += Math.floor(Math.random() * (10 - 5) + 5);
                        break;
                    case 2:
                        s += Math.floor(Math.random() * (5 - 3) + 3);
                        break;
                    case 3:
                        s += Math.floor(Math.random() * (4 - 1) + 1);
                        break;
                    case 4:
                        break;
                    case 5:
                        s -= Math.floor(Math.random() * (4 - 1) + 1);
                        break;
                    case 6:
                        s -= Math.floor(Math.random() * (5 - 3) + 3);
                        break;
                    case 7:
                        s -= Math.floor(Math.random() * (10 - 5) + 5);
                        break;
                    case 8:
                        print("***CHEAT\n");
                        if (Math.random() >= 0.7) {
                            print("YOU MADE IT!\n");
                            t += 1.5;
                        } else {
                            print("AN OFFICIAL CAUGHT YOU!\n");
                            print("YOU TOOK " + (t + Math.random()) + " SECONDS.\n");
                            finish = true;
                        }
                        break;
                }
                if (!finish) {
                    if (o1 != 4)
                        print(s + " M.P.H.\n");
                    if (s > q) {
                        if (Math.random() < ((s - q) * 0.1) + 0.2) {
                            print("YOU WENT OVER THE MAXIMUM SPEED AND ");
                            if (Math.random() < 0.5) {
                                print("SNAGGED A FLAG!\n");
                            } else {
                                print("WIPED OUT!\n");
                            }
                            print("YOU TOOK " + (t + Math.random()) + " SECONDS.\n");
                            finish = true;
                        } else {
                            print("YOU WENT OVER THE MAXIMUM SPEED AND MADE IT!\n");
                        }
                    } else if (s > q - 1) {
                        print("CLOSE ONE!\n");
                    }
                }
                if (finish)
                    break;
                if (s < 7) {
                    print("LET'S BE REALISTIC, OK?  LET'S GO BACK AND TRY AGAIN...\n");
                    s = s1;
                    o--;
                    continue;
                }
                t += q - s + 1;
                if (s > q) {
                    t += 0.5;
                }
            }
            if (!finish) {
                print("\n");
                print("YOU TOOK " + (t + Math.random()) + " SECONDS.\n");
                m = t;
                m /= v;
                if (m < 1.5 - (a * 0.1)) {
                    print("YOU WON A GOLD MEDAL!\n");
                    gold++;
                } else if (m < 2.9 - (a * 0.1)) {
                    print("YOU WON A SILVER MEDAL\n");
                    silver++;
                } else if (m < 4.4 - (a * 0.1)) {
                    print("YOU WON A BRONZE MEDAL\n");
                    bronze++;
                }
            }
            while (1) {
                print("\n");
                print("DO YOU WANT TO RACE AGAIN");
                str = await input();
                if (str != "YES" && str != "NO")
                    print("PLEASE TYPE 'YES' OR 'NO'\n");
                else
                    break;
            }
            if (str != "YES")
                break;
        }
        print("THANKS FOR THE RACE\n");
        if (gold >= 1)
            print("GOLD MEDALS: " + gold + "\n");
        if (silver >= 1)
            print("SILVER MEDALS: " + silver + "\n");
        if (bronze >= 1)
            print("BRONZE MEDALS: " + bronze + "\n");
    }
    
    main();
    
    
    ================================================
    FILE: 79_Slalom/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 79_Slalom/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 79_Slalom/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 79_Slalom/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 79_Slalom/python/slalom.py
    ================================================
    from random import random
    
    medals = {
        "gold": 0,
        "silver": 0,
        "bronze": 0,
    }
    
    
    def ask(question: str) -> str:
        print(question, end="? ")
        return input().upper()
    
    
    def ask_int(question: str) -> int:
        reply = ask(question)
        return int(reply) if reply.isnumeric() else -1
    
    
    def pre_run(gates, max_speeds) -> None:
        print('\nType "INS" for instructions')
        print('Type "MAX" for approximate maximum speeds')
        print('Type "RUN" for the beginning of the race')
        cmd = ask("Command--")
        while cmd != "RUN":
            if cmd == "INS":
                print("\n*** Slalom: This is the 1976 Winter Olypic Giant Slalom.  You are")
                print("            the American team's only hope for a gold medal.\n")
                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.\n")
                print(" The place to use these options is when the Computer asks:\n")
                print("Option?\n")
                print("                Good Luck!\n")
                cmd = ask("Command--")
            elif cmd == "MAX":
                print("Gate Max")
                print(" # M.P.H.")
                print("----------")
                for i in range(0, gates):
                    print(f" {i + 1}  {max_speeds[i]}")
                cmd = ask("Command--")
            else:
                cmd = ask(f'"{cmd}" is an illegal command--Retry')
    
    
    def run(gates, lvl, max_speeds) -> None:
        global medals
        print("The starter counts down...5...4...3...2...1...Go!")
        time: float = 0
        speed = int(random() * (18 - 9) + 9)
        print("You're off")
        for i in range(0, gates):
            while True:
                print(f"\nHere comes gate #{i + 1}:")
                print(f" {int(speed)} M.P.H.")
                old_speed = speed
                opt = ask_int("Option")
                while opt < 1 or opt > 8:
                    if opt == 0:
                        print(f"You've taken {int(time)} seconds.")
                    else:
                        print("What?")
                    opt = ask_int("Option")
    
                if opt == 8:
                    print("***Cheat")
                    if random() < 0.7:
                        print("An official caught you!")
                        print(f"You took {int(time + random())} seconds.")
                        return
                    else:
                        print("You made it!")
                        time += 1.5
                else:
                    match opt:
                        case 1:
                            speed += int(random() * (10 - 5) + 5)
    
                        case 2:
                            speed += int(random() * (5 - 3) + 3)
    
                        case 3:
                            speed += int(random() * (4 - 1) + 1)
    
                        case 5:
                            speed -= int(random() * (4 - 1) + 1)
    
                        case 6:
                            speed -= int(random() * (5 - 3) + 3)
    
                        case 7:
                            speed -= int(random() * (10 - 5) + 5)
                    print(f" {speed} M.P.H.")
                    if speed > max_speeds[i]:
                        if random() < ((speed - max_speeds[i]) * 0.1) + 0.2:
                            print(
                                f"You went over the maximum speed and {'snagged a flag' if random() < .5 else 'wiped out'}!"
                            )
                            print(f"You took {int(time + random())} seconds")
                            return
                        else:
                            print("You went over the maximum speed and made it!")
                    if speed > max_speeds[i] - 1:
                        print("Close one!")
                if speed < 7:
                    print("Let's be realistic, ok? Let's go back and try again...")
                    speed = old_speed
                else:
                    time += max_speeds[i] - speed + 1
                    if speed > max_speeds[i]:
                        time += 0.5
                    break
        print(f"\nYou took {int(time + random())} seconds.")
        avg = time / gates
        if avg < 1.5 - (lvl * 0.1):
            print("Yout won a gold medal!")
            medals["gold"] += 1
        elif avg < 2.9 - (lvl * 0.1):
            print("You won a silver medal!")
            medals["silver"] += 1
        elif avg < 4.4 - (lvl * 0.01):
            print("You won a bronze medal!")
            medals["bronze"] += 1
    
    
    def main() -> None:
        print("Slalom".rjust(39))
        print("Creative Computing Morristown, New Jersey\n\n\n".rjust(57))
    
        max_speeds = [
            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,
        ]
    
        while True:
            gates = ask_int("How many gates does this course have (1 to 25)")
            if gates < 1:
                print("Try again,")
            else:
                if gates > 25:
                    print("25 is the limit.")
                break
    
        pre_run(gates, max_speeds)
    
        while True:
            lvl = ask_int("Rate yourself as a skier, (1=Worst, 3=Best)")
            if lvl < 1 or lvl > 3:
                print("The bounds are 1-3.")
            else:
                break
    
        while True:
            run(gates, lvl, max_speeds)
            while True:
                answer = ask("Do you want to play again?")
                if answer in ["YES", "NO"]:
                    break
                else:
                    print('Please type "YES" or "NO"')
            if answer == "NO":
                break
    
        print("Thanks for the race")
        if medals["gold"] > 0:
            print(f"Gold medals: {medals['gold']}")
        if medals["silver"] > 0:
            print(f"Silver medals: {medals['silver']}")
        if medals["bronze"] > 0:
            print(f"Bronze medals: {medals['bronze']}")
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 79_Slalom/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 79_Slalom/slalom.bas
    ================================================
    10 PRINT TAB(33);"SLALOM"
    20 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    30 PRINT:PRINT:PRINT
    310 PRINT "HOW MANY GATES DOES THIS COURSE HAVE (1 TO 25)";
    320 INPUT V
    330 IF V>25 THEN 360
    340 IF V<1 THEN 390
    350 GOTO 1440
    360 PRINT "25 IS THE LIMIT."
    370 LET V=25
    380 GOTO 1440
    390 PRINT "TRY AGAIN,"
    400 GOTO 310
    410 PRINT "RATE YOURSELF AS A SKIER, (1=WORST, 3=BEST)";
    420 INPUT A
    430 IF A<1 THEN 460
    440 IF A>3 THEN 460
    450 GOTO 480
    460 PRINT "THE BOUNDS ARE 1-3"
    470 GOTO 410
    480 PRINT"THE STARTER COUNTS DOWN...5...4...3...2...1...GO!";
    490 REM
    500 LET T=0
    510 LET S=INT(RND(1)*(18-9)+9)
    520 PRINT
    525 PRINT "YOU'RE OFF!"
    530 FOR O=1 TO V
    540    READ Q
    550    PRINT
    555    PRINT "HERE COMES GATE #";STR$(O);":"
    560    PRINT S;"M.P.H."
    570    LET S1=S
    580    PRINT "OPTION";
    590    INPUT O1
    600    IF O1=0 THEN 970
    610   IF O1>8 THEN 1420
    620    IF O1<1 THEN 1420
    630    GOSUB 990
    640    IF S<7 THEN 1390
    650    LET T=T+(Q-S+1)
    660    IF S>Q THEN 1630
    670 NEXT O
    680 PRINT:PRINT "YOU TOOK";(T+RND(1));"SECONDS."
    690 LET M=T
    700 LET M=M/V
    710 IF M<1.5-(A*.1) THEN 1650
    720 IF M<2.9-(A*.1) THEN 1680
    730 IF M<4.4-(A*.01) THEN 1710
    740 PRINT:PRINT "DO YOU WANT TO RACE AGAIN";
    750 INPUT B$
    760 REM
    770 IF B$="NO" THEN 1740
    780 IF B$="YES" THEN 480
    790 PRINT "PLEASE TYPE 'YES' OR 'NO'"
    800 GOTO 740
    810 STOP
    820 PRINT
    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 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."
    880 PRINT "     4 -- TYPE THIS IF YOU WANT TO KEEP GOING THE SAME SPEED."
    890 PRINT "     5 -- TYPE THIS IF YOU WANT TO CHECK A TEENSY."
    900 PRINT "     6 -- TYPE THIS IF YOU WANT TO CHECK A LITTLE."
    910 PRINT "     7 -- TYPE THIS IF YOU WANT TO CHECK A LOT."
    920 PRINT "     8 -- TYPE THIS IF YOU WANT TO CHEAT AND TRY TO SKIP A GATE."
    930 PRINT
    935 PRINT " THE PLACE TO USE THESE OPTIONS IS WHEN THE COMPUTER ASKS:"
    940 PRINT
    945 PRINT "OPTION?"
    950 PRINT
    955 PRINT "                GOOD LUCK!"
    957 PRINT
    960 GOTO 1470
    970 PRINT "YOU'VE TAKEN";(T+RND(1));"SECONDS."
    980 GOTO 580
    990 ON O1 GOTO 1130,1010,1170,1080,1190,1100,1150,1210
    1000 STOP
    1010 LET S=S+INT(RND(1)*(5-3)+3)
    1020 PRINT S;"M.P.H."
    1030 IF S>Q THEN 1290
    1040 IF S>Q-1 THEN 1060
    1050 RETURN
    1060 PRINT "CLOSE ONE!"
    1070 RETURN
    1080 PRINT S;"M.P.H."
    1090 GOTO 1030
    1100 LET S=S-INT(RND(1)*(5-3)+3)
    1110 PRINT S;"M.P.H."
    1120 GOTO 1030
    1130 LET S=S+INT(RND(1)*(10-5)+5)
    1140 GOTO 1080
    1150 LET S=S-INT(RND(1)*(10-5)+5)
    1160 GOTO 1110
    1170 LET S=S+INT(RND(1)*(4-1)+1)
    1180 GOTO 1110
    1190 LET S=S-INT(RND(1)*(4-1)+1)
    1200 GOTO 1110
    1210 PRINT "***CHEAT"
    1220 IF RND(1)<.7 THEN 1260
    1230 PRINT "YOU MADE IT!"
    1240 LET T=T+1.5
    1250 RETURN
    1260 PRINT "AN OFFICIAL CAUGHT YOU!"
    1270 PRINT "YOU TOOK";(T+RND(1));"SECONDS."
    1280 GOTO 740
    1290 IF RND(1)<((S-Q)*.1)+.2 THEN 1320
    1300 PRINT "YOU WENT OVER THE NAXIMUM SPEED AND MADE IT!"
    1310 RETURN
    1320 PRINT "YOU WENT OVER THE MAXIMUM SPEED AND ";
    1330 IF RND(1)<.5 THEN 1370
    1340 PRINT "WIPED OUT!"
    1350 PRINT "YOU TOOK";(T+RND(1));"SECONDS"
    1360 GOTO 740
    1370 PRINT "SNAGGED A FLAG!"
    1380 GOTO 1350
    1390 PRINT "LET'S BE REALISTIC, OK?  LET'S GO BACK AND TRY AGAIN..."
    1400 LET S=S1
    1410 GOTO 550
    1420 PRINT "WHAT?"
    1430 GOTO 580
    1440 PRINT
    1445 PRINT "TYPE ";CHR$(34);"INS";CHR$(34);" FOR INSTRUCTIONS"
    1450 PRINT "TYPE ";CHR$(34);"MAX";CHR$(34);" FOR APPROXIMATE MAXIMUM SPEEDS"
    1460 PRINT "TYPE ";CHR$(34);"RUN";CHR$(34);" FOR THE BEGINNING OF THE RACE"
    1470 PRINT "COMMAND--";
    1480 INPUT A$
    1490 REM
    1500 IF A$="INS" THEN 820
    1510 IF A$="MAX" THEN 1550
    1520 IF A$="RUN" THEN 410
    1530 PRINT CHR$(34);A$;CHR$(34);" IS AN ILLEGAL COMMAND--RETRY";
    1540 GOTO 1480
    1550 PRINT "GATE MAX"
    1560 PRINT " #  M.P.H."
    1570 PRINT"----------"
    1580 FOR B=1 TO V
    1590    READ Q
    1600    PRINT B;"  ";Q
    1610 NEXT B
    1620 GOTO 1470
    1630 LET T=T+.5
    1640 GOTO 670
    1650 PRINT "YOU WON A GOLD MEDAL!"
    1660 LET G(1)=G(1)+1
    1670 GOTO 1730
    1680 PRINT "YOU WON A SILVER MEDAL"
    1690 LET S(1)=S(1)+1
    1700 GOTO 1730
    1710 PRINT "YOU WON A BRONZE MEDAL"
    1720 LET B(1)=B(1)+1
    1730 GOTO 740
    1740 PRINT "THANKS FOR THE RACE"
    1750 IF G(1)<1 THEN 1770
    1760 PRINT "GOLD MEDALS:";G(1)
    1770 IF S(1)<1 THEN 1790
    1780 PRINT "SILVER MEDALS:";S(1)
    1790 IF B(1)<1 THEN 1830
    1800 PRINT "BRONZE MEDALS:";B(1)
    1810 DATA 14,18,26,29,18,25,28,32,29,20,29,29,25,21,26,29,20,21,20
    1820 DATA 18,26,25,33,31,22
    1830 END
    
    
    ================================================
    FILE: 79_Slalom/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 79_Slalom/vbnet/Slalom.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Slalom", "Slalom.vbproj", "{9A7FDEAB-071F-404C-BBA4-91D77E797DF1}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{9A7FDEAB-071F-404C-BBA4-91D77E797DF1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{9A7FDEAB-071F-404C-BBA4-91D77E797DF1}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{9A7FDEAB-071F-404C-BBA4-91D77E797DF1}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{9A7FDEAB-071F-404C-BBA4-91D77E797DF1}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 79_Slalom/vbnet/Slalom.vbproj
    ================================================
    
      
        Exe
        Slalom
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 80_Slots/README.md
    ================================================
    ### Slots
    
    The slot machine or one-arm bandit is a mechanical device that will absorb coins just about as fast as you can feed it. After inserting a coin, you pull a handle that sets three independent reels spinning. If the reels stop with certain symbols appearing in the pay line, you get a certain payoff. The original slot machine, called the Liberty Bell, was invented in 1895 by Charles Fey in San Francisco. Fey refused to sell or lease the manufacturing rights, so H.S. Mills in Chicago built a similar, but much improved machine called the Operators Bell. This has survived nearly unchanged to today.
    
    On the Operators Bell and other standard slot machines, there are 20 symbols on each wheel but they are not distributed evenly among the objects (cherries, bar, apples, etc.). Of the 8,000 passible combinations, the expected payoff (to the player) is 7,049 or $89.11 for every $100.00 put in, one of the lowest expected payoffs in all casino games.
    
    In the program here, the payoff is considerably more liberal; indeed it appears to favor the player by 11% — i.e., an expected payoff of $111 for each $100 bet.
    
    The program was originally written by Fred Mirabella and Bob Harper.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=149)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=164)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### 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)
    
    
    ================================================
    FILE: 80_Slots/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    This C# implementation of slots was done using a [C# script](https://github.com/filipw/dotnet-script).
    
    # Required
    [.NET Core SDK (i.e., .NET 6.0)](https://dotnet.microsoft.com/en-us/download)
    
    Install dotnet-script.  On the command line run:
    ```
    dotnet tool install -g dotnet-script
    ```
    
    # Run
    ```
    dotnet script .\slots.csx
    ```
    
    
    ================================================
    FILE: 80_Slots/csharp/Slots.csproj
    ================================================
    
      
        Exe
        net6.0
        10
        enable
        enable
      
    
    
    
    ================================================
    FILE: 80_Slots/csharp/Slots.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Slots", "Slots.csproj", "{D855ECF3-DF8C-46DD-8BEF-2ADFF3AE7817}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{D855ECF3-DF8C-46DD-8BEF-2ADFF3AE7817}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{D855ECF3-DF8C-46DD-8BEF-2ADFF3AE7817}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{D855ECF3-DF8C-46DD-8BEF-2ADFF3AE7817}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{D855ECF3-DF8C-46DD-8BEF-2ADFF3AE7817}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 80_Slots/csharp/slots.csx
    ================================================
    Print("SLOTS");
    Print("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
    Print(); Print(); Print();
    Print("YOU ARE IN THE H&M CASINO,IN FRONT OF ONE OF OUR");
    Print("ONE-ARM BANDITS. BET FROM $1 TO $100.");
    Print("TO PULL THE ARM, PUNCH THE RETURN KEY AFTER MAKING YOUR BET.");
    
    var _standings = 0;
    
    var play = true;
    while(play)
    {
        Play();
        play = PlayAgain();
    }
    
    Done();
    
    public void Play()
    {
        var bet = GetBet();
        Print();
        Ring();
    
        var random = new Random();
        var x = GetSlot();
        var y = GetSlot();
        var z = GetSlot();
    
        Print();
        Print($"{x.ToString()} {y.ToString()} {z.ToString()}");
    
        if(x == y && x == z)
        {
            if(z == Slot.BAR)
            {
                // BAR BAR BAR
                Print();
                Print("***JACKPOT***");
                Print("YOU WON!");
                _standings = (100*bet) + bet + _standings;
            }
            else
            {
                Print();
                Print("**TOP DOLLAR**");
                Print("YOU WON!");
                _standings = (10*bet) + bet + _standings;
            }
        }
        else if(x == y)
        {
            if(y == Slot.BAR)
            {
                DoubleBar(bet);
            }
            else
            {
                Double(bet);
            }
        }
        else if(x == z)
        {
            if(z == Slot.BAR)
            {
                DoubleBar(bet);
            }
            else
            {
                Lost(bet);
            }
        }
        else if(y == z)
        {
            if(z == Slot.BAR)
            {
                DoubleBar(bet);
            }
            else
            {
                Double(bet);
            }
        }
        else
        {
            Lost(bet);
        }
    
        Print($"YOUR STANDINGS ARE ${_standings}");
    }
    
    public bool PlayAgain()
    {
        Console.Write("AGAIN? (Y) ");
        var playAgain = Console.ReadKey(true);
        Print();
        return playAgain.Key == ConsoleKey.Y || playAgain.Key == ConsoleKey.Enter;
    }
    
    public void Done()
    {
        Print();
        if(_standings < 0)
        {
            Print("PAY UP!  PLEASE LEAVE YOUR MONEY ON THE TERMINAL.");
        }
        else if (_standings == 0)
        {
            Print("HEY, YOU BROKE EVEN.");
        }
        else
        {
            Print("COLLECT YOUR WINNINGS FROM THE H&M CASHIER");
        }
    }
    
    // Prints the text provided.  Default is a blank line
    public void Print(string line = "")
    {
        Console.WriteLine(line);
    }
    
    public int GetBet()
    {
        Print();
        Console.Write("YOUR BET ");
        var betInput = ReadLine();
        int bet;
        var inputValid = int.TryParse(betInput, out bet);
        if (!inputValid)
        {
            Print("NUMBER EXPECTED - RETRY");
            return GetBet();
        }
    
        if(bet > 100)
        {
            Print("HOUSE LIMITS ARE $100");
            inputValid = false;
        }
        else if(bet < 1)
        {
            Print("MINIMUM BET IS $1");
            inputValid = false;
        }
    
        return inputValid ? bet : GetBet();
    }
    
    public enum Slot { BAR, BELL, ORANGE, LEMON, PLUM, CHERRY };
    
    public Slot GetSlot()
    {
        var rand = new Random();
        var num = rand.Next(0, 5);
        return (Slot)num;
    }
    
    public void DoubleBar(int bet)
    {
        Print();
        Print("*DOUBLE BAR*");
        Print("YOU WON!");
        _standings = (5*bet) + bet + _standings;
    }
    
    public void Double(int bet)
    {
        Print();
        Print("DOUBLE!!");
        Print("YOU WON!");
        _standings = (2*bet) + bet + _standings;
    }
    
    public void Lost(int bet)
    {
        Print();
        Print("YOU LOST.");
        _standings = _standings - bet;
    }
    
    public void Ring()
    {
        for(int i = 1; i <= 10; i++)
        {
            // https://stackoverflow.com/a/321148/1497
            Console.Beep();
            // Console.Beep(800, 501 - (i * 50)); // Uncomment for a fancier bell
        }
    }
    
    
    ================================================
    FILE: 80_Slots/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 80_Slots/java/src/Slots.java
    ================================================
    import java.util.Arrays;
    import java.util.Scanner;
    
    /**
     * Game of Slots
     * 

    * Based on the Basic game of Slots here * https://github.com/coding-horror/basic-computer-games/blob/main/80%20Slots/slots.bas *

    * Note: The idea was to create a version of the 1970's Basic game in Java, without introducing * new features - no additional text, error checking, etc has been added. */ public class Slots { public static final String[] SLOT_SYMBOLS = {"BAR", "BELL", "ORANGE", "LEMON", "PLUM", "CHERRY"}; public static final int NUMBER_SYMBOLS = SLOT_SYMBOLS.length; // Jackpot symbol (BAR) public static final int BAR_SYMBOL = 0; // Indicator that the current spin won nothing public static final int NO_WINNER = -1; // Used for keyboard input private final Scanner kbScanner; private enum GAME_STATE { START_GAME, ONE_SPIN, RESULTS, GAME_OVER } // Current game state private GAME_STATE gameState; // Different types of spin results private enum WINNINGS { JACKPOT(100), TOP_DOLLAR(10), DOUBLE_BAR(5), REGULAR(2), NO_WIN(0); private final int multiplier; WINNINGS(int mult) { multiplier = mult; } // No win returns the negative amount of net // otherwise calculate winnings based on // multiplier public int calculateWinnings(int bet) { if (multiplier == 0) { return -bet; } else { // Return original bet plus a multipler // of the win type return (multiplier * bet) + bet; } } } private int playerBalance; public Slots() { kbScanner = new Scanner(System.in); gameState = GAME_STATE.START_GAME; } /** * Main game loop */ public void play() { int[] slotReel = new int[3]; do { // Results of a single spin WINNINGS winnings; switch (gameState) { case START_GAME: intro(); playerBalance = 0; gameState = GAME_STATE.ONE_SPIN; break; case ONE_SPIN: int playerBet = displayTextAndGetNumber("YOUR BET? "); slotReel[0] = randomSymbol(); slotReel[1] = randomSymbol(); slotReel[2] = randomSymbol(); // Store which symbol (if any) matches at least one other reel int whichSymbolWon = winningSymbol(slotReel[0], slotReel[1], slotReel[2]); // Display the three randomly drawn symbols StringBuilder output = new StringBuilder(); for (int i = 0; i < 3; i++) { if (i > 0) { output.append(" "); } output.append(SLOT_SYMBOLS[slotReel[i]]); } System.out.println(output); // Calculate results if (whichSymbolWon == NO_WINNER) { // No symbols match = nothing won winnings = WINNINGS.NO_WIN; } else if (slotReel[0] == slotReel[1] && slotReel[0] == slotReel[2]) { // Top dollar, 3 matching symbols winnings = WINNINGS.TOP_DOLLAR; if (slotReel[0] == BAR_SYMBOL) { // All 3 symbols are BAR. Jackpot! winnings = WINNINGS.JACKPOT; } } else { // At this point the remaining options are a regular win // or a double, since the rest (including not winning) have already // been checked above. // Assume a regular win winnings = WINNINGS.REGULAR; // But if it was the BAR symbol that matched, its a double bar if (slotReel[0] == BAR_SYMBOL) { winnings = WINNINGS.DOUBLE_BAR; } } // Update the players balance with the amount won or lost on this spin playerBalance += winnings.calculateWinnings(playerBet); System.out.println(); // Output what happened on this spin switch (winnings) { case NO_WIN: System.out.println("YOU LOST."); break; case REGULAR: System.out.println("DOUBLE!!"); System.out.println("YOU WON!"); break; case DOUBLE_BAR: System.out.println("*DOUBLE BAR*"); System.out.println("YOU WON!"); break; case TOP_DOLLAR: System.out.println(); System.out.println("**TOP DOLLAR**"); System.out.println("YOU WON!"); break; case JACKPOT: System.out.println(); System.out.println("***JACKPOT***"); System.out.println("YOU WON!"); break; } System.out.println("YOUR STANDINGS ARE $" + playerBalance); // If player does not elect to play again, show results of session if (!yesEntered(displayTextAndGetInput("AGAIN? "))) { gameState = GAME_STATE.RESULTS; } break; case RESULTS: if (playerBalance == 0) { System.out.println("HEY, YOU BROKE EVEN."); } else if (playerBalance > 0) { System.out.println("COLLECT YOUR WINNINGS FROM THE H&M CASHIER."); } else { // Lost System.out.println("PAY UP! PLEASE LEAVE YOUR MONEY ON THE TERMINAL."); } gameState = GAME_STATE.GAME_OVER; break; } } while (gameState != GAME_STATE.GAME_OVER); } private void intro() { System.out.println(simulateTabs(30) + "SLOTS"); System.out.println(simulateTabs(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println(); System.out.println("YOU ARE IN THE H&M CASINO,IN FRONT OF ONE OF OUR"); System.out.println("ONE-ARM BANDITS. BET FROM $1 TO $100."); System.out.println("TO PULL THE ARM, PUNCH THE RETURN KEY AFTER MAKING YOUR BET."); } /* * Print a message on the screen, then accept input from Keyboard. * Converts input to an Integer * * @param text message to be displayed on screen. * @return what was typed by the player. */ private int displayTextAndGetNumber(String text) { return Integer.parseInt(displayTextAndGetInput(text)); } /* * Print a message on the screen, then accept input from Keyboard. * * @param text message to be displayed on screen. * @return what was typed by the player. */ private String displayTextAndGetInput(String text) { System.out.print(text); return kbScanner.next(); } /** * Checks whether player entered Y or YES to a question. * * @param text player string from kb * @return true of Y or YES was entered, otherwise false */ private boolean yesEntered(String text) { return stringIsAnyValue(text, "Y", "YES"); } /** * Check whether a string equals one of a variable number of values * Useful to check for Y or YES for example * Comparison is case insensitive. * * @param text source string * @param values a range of values to compare against the source string * @return true if a comparison was found in one of the variable number of strings passed */ private boolean stringIsAnyValue(String text, String... values) { return Arrays.stream(values).anyMatch(str -> str.equalsIgnoreCase(text)); } /** * Simulate the old basic tab(xx) command which indented text by xx spaces. * * @param spaces number of spaces required * @return String with number of spaces */ private String simulateTabs(int spaces) { char[] spacesTemp = new char[spaces]; Arrays.fill(spacesTemp, ' '); return new String(spacesTemp); } /** * Find the symbol that won this round i.e. the first reel that matched another reel * * @param reel1 reel1 spin result * @param reel2 reel2 spin result * @param reel3 reel3 spin result * @return NO_WINNER if no reels match otherwise an int 0-2 to indicate the reel that matches another */ private int winningSymbol(int reel1, int reel2, int reel3) { if (reel1 == reel2) { return 0; } else if (reel1 == reel3) { return 0; } else if (reel2 == reel3) { return 1; } else { return NO_WINNER; } } /** * Random symbol for a slot wheel * * @return number between 0-5 */ private int randomSymbol() { return (int) (Math.random() * NUMBER_SYMBOLS); } } ================================================ FILE: 80_Slots/java/src/SlotsGame.java ================================================ public class SlotsGame { public static void main(String[] args) { Slots slots = new Slots(); slots.play(); } } ================================================ FILE: 80_Slots/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 80_Slots/javascript/slots.html ================================================ SLOTS

    
    
    
    
    
    
    ================================================
    FILE: 80_Slots/javascript/slots.js
    ================================================
    // SLOTS
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    var figures = [, "BAR", "BELL", "ORANGE", "LEMON", "PLUM", "CHERRY"];
    
    // Main program
    async function main()
    {
        print(tab(30) + "SLOTS\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        // Produced by Fred Mirabelle and Bob Harper on Jan 29, 1973
        // It simulates the slot machine.
        print("YOU ARE IN THE H&M CASINO,IN FRONT ON ONE OF OUR\n");
        print("ONE-ARM BANDITS. BET FROM $1 TO $100.\n");
        print("TO PULL THE ARM, PUNCH THE RETURN KEY AFTER MAKING YOUR BET.\n");
        p = 0;
        while (1) {
            while (1) {
                print("\n");
                print("YOUR BET");
                m = parseInt(await input());
                if (m > 100) {
                    print("HOUSE LIMITS ARE $100\n");
                } else if (m < 1) {
                    print("MINIMUM BET IS $1\n");
                } else {
                    break;
                }
            }
            // Not implemented: GOSUB 1270 ten chimes
            print("\n");
            x = Math.floor(6 * Math.random() + 1);
            y = Math.floor(6 * Math.random() + 1);
            z = Math.floor(6 * Math.random() + 1);
            print("\n");
            // Not implemented: GOSUB 1310 seven chimes after figure x and y
            print(figures[x] + " " + figures[y] + " " + figures[z] + "\n");
            lost = false;
            if (x == y && y == z) {  // Three figure
                print("\n");
                if (z != 1) {
                    print("**TOP DOLLAR**\n");
                    p += ((10 * m) + m);
                } else {
                    print("***JACKPOT***\n");
                    p += ((100 * m) + m);
                }
                print("YOU WON!\n");
            } else if (x == y || y == z || x == z) {
                if (x == y)
                    c = x;
                else
                    c = z;
                if (c == 1) {
                    print("\n");
                    print("*DOUBLE BAR*\n");
                    print("YOU WON\n");
                    p += ((5 * m) + m);
                } else if (x != z) {
                    print("\n");
                    print("DOUBLE!!\n");
                    print("YOU WON!\n");
                    p += ((2 * m) + m);
                } else {
                    lost = true;
                }
            } else {
                lost = true;
            }
            if (lost) {
                print("\n");
                print("YOU LOST.\n");
                p -= m;
            }
            print("YOUR STANDINGS ARE $" + p + "\n");
            print("AGAIN");
            str = await input();
            if (str.substr(0, 1) != "Y")
                break;
        }
        print("\n");
        if (p < 0) {
            print("PAY UP!  PLEASE LEAVE YOUR MONEY ON THE TERMINAL.\n");
        } else if (p == 0) {
            print("HEY, YOU BROKE EVEN.\n");
        } else {
            print("COLLECT YOUR WINNINGS FROM THE H&M CASHIER.\n");
        }
    }
    
    main();
    
    
    ================================================
    FILE: 80_Slots/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 80_Slots/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 80_Slots/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    This Perl script is a port of slots, which is the 80th entry in Basic
    Computer Games.
    
    I know nothing about slot machines, and my research into them says to me
    that the payout tables can be fairly arbitrary. But I have taken the
    liberty of deeming the BASIC program's refusal to pay on LEMON CHERRY
    LEMON a bug, and made that case a double.
    
    My justification for this is that at the point where the BASIC has
    detected the double in the first and third reels it has already detected
    that there is no double in the first and second reels. After the check
    for a bar (and therefore a double bar) fails it goes back and checks for
    a double on the second and third reels. But we know this check will
    fail, since the check for a double on the first and second reels failed.
    So if a loss was intended at this point, why not just call it a loss?
    
    To restore the original behavior, comment out the entire line commented
    '# Bug fix?' (about line 75) and uncomment the line with the trailing
    comment '# Bug?' (about line 83).
    
    
    ================================================
    FILE: 80_Slots/perl/slots.pl
    ================================================
    #!/usr/bin/env perl
    
    use 5.010;      # To get 'state' and 'say'
    
    use strict;     # Require explicit declaration of variables
    use warnings;   # Enable optional compiler warnings
    
    use English;    # Use more friendly names for Perl's magic variables
    use List::Util qw{ shuffle };   # Shuffle an array.
    use Scalar::Util qw{ looks_like_number };
    use Term::ReadLine;     # Prompt and return user input
    
    our $VERSION = '0.000_01';
    
    print <<'EOD';
                                     SLOTS
                   Creative Computing  Morristown, New Jersey
    
    
    
    You are in the H&M casino, in front of one of our
    one-arm bandits.  Bet from $1 to $100.
    To pull the arm, punch the return key after making your bet.
    EOD
    
    my $winnings = 0;  # Winnings
    
    while ( 1 ) {   # Iterate indefinitely
    
        say '';
    
        my $bet = get_input( 'Your bet? ',
            sub { m/ \A [0-9]+ \z /smx },
            'Please enter a whole number between 0 and 100',
        );
        if ( $bet > 100 ) {
            say 'The house limit is $100';
            next;
        }
        if ( $bet < 1 ) {
            say 'The minimum bet is $1';
            next;
        }
    
        say "\a" x 10;
        my $reel_x = int( 6 * rand() );
        my $reel_y = int( 6 * rand() );
        my $reel_z = int( 6 * rand() );
        foreach my $column ( $reel_x, $reel_y, $reel_z ) {
            state $symbol = [ qw{ Bar Bell Orange Lemon Plum Cherry } ];
            print $symbol->[$column], "\a" x 5, ' ';
        }
    
        use constant YOU_WON    => 'You won!';
        use constant YOU_LOST   => 'You lost.';
    
        say '';
        if ( $reel_x == $reel_y ) {
            if ( $reel_y == $reel_z ) {
                if ( $reel_z ) {
                    say '** TOP DOLLAR **';
                    $winnings += 11 * $bet;
                } else {
                    say '*** JACKPOT ***';
                    $winnings += 101 * $bet;
                }
                say YOU_WON;
            } elsif ( $reel_y ) {
                $winnings += double( $bet );
            } else {
                $winnings += double_bar( $bet );
            }
        } elsif ( $reel_x == $reel_z ) {
            if ( $reel_z ) {
                $winnings += double( $bet );        # Bug fix?
                # NOTE that the below code is what is actually implemented
                # in the basic, but it is implemented strangely enough (a
                # GOTO a line that contains a test that, if I understand the
                # control flow, must fail) that I wonder if it is an error.
                # I know nothing about slot machines, but research suggests
                # the payoff table is fairly arbitrary. The code above makes
                # code above makes the game orthogonal.
                # $winnings += you_lost( $bet );    # Bug?
            } else {
                $winnings += double_bar( $bet );
            }
        } elsif ( $reel_y == $reel_z ) {
            if ( $reel_z ) {
                $winnings += double( $bet );
            } else {
                $winnings += double_bar( $bet );
            }
        } else {
            $winnings += you_lost( $bet );
        }
    
        say 'Your standings are $', $winnings;
    
        last unless get_yes_no( 'Again' );
    
    }
    
    if ( $winnings < 0 ) {
        say 'Pay up!  Please leave your money on the terminal.';
    } elsif ( $winnings > 0 ) {
        say 'Collect your winnings from the H&M cashier.';
    } else {
        say 'Hey, you broke even.';
    }
    
    sub double {
        my ( $bet ) = @_;
        say 'DOUBLE!';
        say YOU_WON;
        return 3 * $bet;
    }
    
    sub double_bar {
        my ( $bet ) = @_;
        say '* DOUBLE BAR *';
        say YOU_WON;
        return 6 * $bet;
    }
    
    sub you_lost {
        my ( $bet ) = @_;
        say YOU_LOST;
        return -$bet;
    }
    
    # Get input from the user. The arguments are:
    # * The prompt
    # * A reference to validation code. This code receives the response in
    #   $ARG and returns true for a valid response.
    # * A warning to print if the response is not valid. This must end in a
    #   return.
    # The first valid response is returned. An end-of-file terminates the
    # script.
    sub get_input {
        my ( $prompt, $validate, $warning ) = @ARG;
    
        # If no validator is passed, default to one that always returns
        # true.
        $validate ||= sub { 1 };
    
        # Create the readline object. The 'state' causes the variable to be
        # initialized only once, no matter how many times this subroutine is
        # called. The do { ... } is a compound statement used because we
        # need to tweak the created object before we store it.
        state $term = do {
            my $obj = Term::ReadLine->new( 'reverse' );
            $obj->ornaments( 0 );
            $obj;
        };
    
        while ( 1 ) {   # Iterate indefinitely
    
            # Read the input into the topic variable, localized to prevent
            # Spooky Action at a Distance. We exit on undef, which signals
            # end-of-file.
            exit unless defined( local $ARG = $term->readline( $prompt ) );
    
            # Return the input if it is valid.
            return $ARG if $validate->();
    
            # Issue the warning, and go around the merry-go-round again.
            warn $warning;
        }
    }
    
    # Get a yes-or-no answer. The argument is the prompt, which will have
    # '? [y/n]: ' appended. The donkey work is done by get_input(), which is
    # requested to validate the response as beginning with 'y' or 'n',
    # case-insensitive. The return is a true value for 'y' and a false value
    # for 'n'.
    sub get_yes_no {
        my ( $prompt ) = @ARG;
        state $map_answer = {
            n   => 0,
            y   => 1,
        };
        my $resp = lc get_input(
            "$prompt? [y/n]: ",
            sub { m/ \A [yn] /smxi },
            "Please respond 'y' or 'n'\n",
        );
        return $map_answer->{ substr $resp, 0, 1 };
    }
    
    __END__
    
    =head1 TITLE
    
    slots - Play the game 'Slots' from Basic Computer Games
    
    =head1 SYNOPSIS
    
     slots.pl
    
    =head1 DETAILS
    
    This Perl script is a port of C, which is the 80th entry in Basic
    Computer Games.
    
    I know nothing about slot machines, and my research into them says to me
    that the payout tables can be fairly arbitrary. But I have taken the
    liberty of deeming the BASIC program's refusal to pay on LEMON CHERRY
    LEMON a bug, and made that case a double.
    
    My justification for this is that at the point where the BASIC has
    detected the double in the first and third reels it has already detected
    that there is no double in the first and second reels. After the check
    for a bar (and therefore a double bar) fails it goes back and checks for
    a double on the second and third reels. But we know this check will
    fail, since the check for a double on the first and second reels failed.
    So if a loss was intended at this point, why not just call it a loss?
    
    To restore the original behavior, comment out the entire line commented
    C<'# Bug fix?'> (about line 75) and uncomment the line with the trailing
    comment C<'# Bug?'> (about line 83).
    
    =head1 PORTED BY
    
    Thomas R. Wyant, III F
    
    =head1 COPYRIGHT AND LICENSE
    
    Copyright (C) 2022 by Thomas R. Wyant, III
    
    This program is free software; you can redistribute it and/or modify it
    under the same terms as Perl 5.10.0. For more details, see the Artistic
    License 1.0 at
    L, and/or the
    Gnu GPL at L.
    
    This program is distributed in the hope that it will be useful, but
    without any warranty; without even the implied warranty of
    merchantability or fitness for a particular purpose.
    
    =cut
    
    # ex: set expandtab tabstop=4 textwidth=72 :
    
    
    ================================================
    FILE: 80_Slots/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 80_Slots/python/slots.py
    ================================================
    ########################################################
    #
    # Slots
    #
    # From Basic Computer Games (1978)
    #
    #    "The slot machine or one-arm bandit is a mechanical
    #   device that will absorb coins just about as fast as
    #   you can feed it. After inserting a coin, you pull a
    #   handle that sets three indepent reels spining. If the
    #   reels stop with certain symbols appearing in the pay
    #   line, you get a certain payoff. The original slot
    #   machine, called the Liberty bell, was invented in 1895
    #   by Charles Fey in San Francisco. Fey refused to sell
    #   or lease the manufacturing rights, so H.S. Mills in
    #   Chicago built a similar, but much improved, machine
    #   called the Operators Bell. This has survived nearly
    #   unchanged to today.
    #     On the operators Bell and other standard slot
    #   machines, there are 20 symbols on each wheel but they
    #   are not distributed evenly among the objects(cherries,
    #   bar, apples, etc). Of the 8000 possible combinations,
    #   the expected payoff(to the player) is 7049 or $89.11
    #   for every $100.00 put in, one of the lowest expected
    #   payoffs of all casino games.
    #     In the program here, the payoff is considerably more
    #   liberal; indeed it appears to favor the player by 11%
    #   -- i.e., an expected payoff of $111 for each $100 bet."
    #     The program was originally written by Fred Mirabelle
    #   and Bob Harper
    #
    ########################################################
    
    import sys
    from collections import Counter
    from random import choices
    from typing import List
    
    
    def initial_message() -> None:
        print(" " * 30 + "Slots")
        print(" " * 15 + "Creative Computing Morrison, New Jersey")
        print("\n" * 3)
        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.")
    
    
    def input_betting() -> int:
        print("\n")
        b = -1
        while b < 1 or b > 100:
            try:
                b = int(input("Your bet:"))
            except ValueError:
                b = -1
            if b > 100:
                print("House limits are $100")
            elif b < 1:
                print("Minium bet is $1")
        beeping()
        return b
    
    
    def beeping() -> None:
        # Function to produce a beep sound.
        # In the original program is the subroutine at line 1270
        for _ in range(5):
            sys.stdout.write("\a")
            sys.stdout.flush()
    
    
    def spin_wheels() -> List[str]:
        possible_fruits = ["Bar", "Bell", "Orange", "Lemon", "Plum", "Cherry"]
        wheel = choices(possible_fruits, k=3)
    
        print(*wheel)
        beeping()
    
        return wheel
    
    
    def adjust_profits(wheel: List[str], m: int, profits: int) -> int:
        # we remove the duplicates
        s = set(wheel)
    
        if len(s) == 1:
            # the three fruits are the same
            fruit = s.pop()
    
            if fruit == "Bar":
                print("\n***Jackpot***")
                profits = ((100 * m) + m) + profits
            else:
                print("\n**Top Dollar**")
                profits = ((10 * m) + m) + profits
    
            print("You Won!")
        elif len(s) == 2:
            # two fruits are equal
            c = Counter(wheel)
            # we get the fruit that appears two times
            fruit = sorted(c.items(), key=lambda x: x[1], reverse=True)[0][0]
    
            if fruit == "Bar":
                print("\n*Double Bar*")
                profits = ((5 * m) + m) + profits
            else:
                print("\nDouble!!")
                profits = ((2 * m) + m) + profits
    
            print("You Won!")
        else:
            # three different fruits
            print("\nYou Lost.")
            profits -= m
    
        return profits
    
    
    def final_message(profits: int) -> None:
        if profits < 0:
            print("Pay up!  Please leave your money on the terminal")
        elif profits == 0:
            print("Hey, You broke even.")
        else:
            print("Collect your winings from the H&M cashier.")
    
    
    def main() -> None:
        profits = 0
        keep_betting = True
    
        initial_message()
        while keep_betting:
            m = input_betting()
            w = spin_wheels()
            profits = adjust_profits(w, m, profits)
    
            print(f"Your standings are ${profits}")
            answer = input("Again?")
    
            try:
                if answer[0].lower() != "y":
                    keep_betting = False
            except IndexError:
                keep_betting = False
    
        final_message(profits)
    
    
    if __name__ == "__main__":
        main()
    
    ######################################################################
    #
    # Porting notes
    #
    #   The selections of the fruits(Bar, apples, lemon, etc.) are made
    #   with equal probability, accordingly to random.choices documentation.
    #   It could be added a weights list to the function and therefore
    #   adjust the expected payoff
    #
    ######################################################################
    
    
    ================================================
    FILE: 80_Slots/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 80_Slots/ruby/slots.rb
    ================================================
    $pot = 0
    
    def greeting
        puts "SLOTS".center(80)
        puts "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY".center(80)
        puts "\n\n"
    
        # PRODUCED BY FRED MIRABELLE AND BOB HARPER ON JAN 29, 1973
        # IT SIMULATES THE SLOT MACHINE.
    
        puts "You are in the H&M Casino, in front of one of our"
        puts "one-arm bandits. You can bet from $1 to $100."
        puts "To pull the arm, punch the return key after making your bet."
        puts "\nBet zero to end the game."
    end
    
    def overLimit
        puts "House Limit is $100"
    end
    
    def underMinimum
        puts "Minimum Bet is $1"
    end
    
    # bells don't work on my machine. YMMV
    # I'm displaying dashes between the reels
    
    def tenBells
        10.times do
            # beep if you can
            print "-"
        end
    end
    
    def fiveBells
        "-----"
    end
    
    def goodbye
        puts "\n\n\n"
        # end the game
        exit
    end
    
    def payUp
        puts "PAY UP!  PLEASE LEAVE YOUR MONEY ON THE TERMINAL."
    end
    
    def brokeEven
        puts "HEY, YOU BROKE EVEN."
    end
    
    def collectWinnings
        puts "COLLECT YOUR WINNINGS FROM THE H&M CASHIER."
    end
    
    def win winType, bet
        case winType
            when "jackpot"
                winMessage = "***JACKPOT***"
                winnings = 101
            when "topDollar"
                winMessage = "**TOP DOLLAR**"
                winnings = 11
            when "doubleBar"
                winMessage = "*DOUBLE BAR!!*"
                winnings = 6
            when "double"
                winMessage = "DOUBLE!!"
                winnings = 3
        end
        puts "\nYou won: " + winMessage
        $pot += (winnings * bet)
    end
    
    greeting
    
    #$pot = 0
    while true
        reelArray = ["BAR","BELL","ORANGE","LEMON","PLUM","CHERRY"]
        print "\nYOUR BET? "
        # get input, remove leading and trailing whitespace, cast to integer
        bet = gets.strip.to_i
    
        if bet == 0 then
            goodbye
        elsif bet > 100 then
            overLimit # error if more than $100
        elsif bet < 1 then
            underMinimum # have to bet at least a dollar
        else
            # valid bet, continue
            tenBells # ding
    
            # assign a random value from the array to each of the three reels
            reel1 = reelArray[rand(5)]
            reel2 = reelArray[rand(5)]
            reel3 = reelArray[rand(5)]
    
            # print the slot machine reels
            puts "\n\n" + reel1 + fiveBells + reel2 + fiveBells + reel3
    
            # see if we have a match in the first two reels
            if reel1 == reel2 then
                if reel2 == reel3 then
                    if reel3 == "BAR" then
                        # all three reels are "BAR"
                        win "jackpot", bet
                     else
                       # all three reels match but aren't "BAR"
                       win "topDollar", bet
                    end
                elsif reel2 == "BAR" then
                    # reels 1 and 2 are both "BAR"
                    win "doubleBar", bet
                 else
                    # reels 1 and 2 match but aren't "BAR"
                    win "double", bet
                end
            # otherwise see if there's a match in the remaining reels
            elsif reel1 == reel3 or reel2 == reel3 then
                if reel3 == "BAR" then
                    # two reels match, both "BAR"
                    win "doubleBar", bet
                else
                    # two reels match, but not "BAR"
                    win "double", bet
                end
            else
                # bad news - no matches
                puts "\nYou lost"
                # decrement your standings by the bet amount
                $pot -= bet
            end
    
            puts "Your standings are: " + $pot.to_s
            print "\nAgain? " # YES to continue
            # get input, remove leading and trailing whitespace, make uppercase
            again = gets.strip.upcase
            if again != "Y" && again != "YES" then
                # that's enough... evaluate the pot and quit
                if $pot < 0 then
                    payUp
                elsif $pot == 0 then
                    brokeEven
                else # yay!
                    collectWinnings
                end
                goodbye
            end
        end
    end
    
    
    ================================================
    FILE: 80_Slots/slots.bas
    ================================================
    10 PRINT TAB(30);"SLOTS"
    20 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    30 PRINT: PRINT: PRINT
    100 REM PRODUCED BY FRED MIRABELLE AND BOB HARPER ON JAN 29, 1973
    110 REM IT SIMULATES THE SLOT MACHINE.
    120 PRINT "YOU ARE IN THE H&M CASINO,IN FRONT OF ONE OF OUR"
    130 PRINT "ONE-ARM BANDITS. BET FROM $1 TO $100."
    140 PRINT "TO PULL THE ARM, PUNCH THE RETURN KEY AFTER MAKING YOUR BET."
    150 LET P=0
    160 PRINT: PRINT"YOUR BET";
    170 INPUT M
    180 IF M>100 THEN 860
    190 IF M<1 THEN 880
    200 M=INT(M)
    210 GOSUB 1270
    220 PRINT
    230 LET X=INT(6*RND(1)+1)
    240 LET Y=INT(6*RND(1)+1)
    250 LET Z=INT(6*RND(1)+1)
    260 PRINT
    270 IF X=1 THEN 910
    280 IF X=2 THEN 930
    290 IF X=3 THEN 950
    300 IF X=4 THEN 970
    310 IF X=5 THEN 990
    320 IF X=6 THEN 1010
    330 IF Y=1 THEN 1030
    340 IF Y=2 THEN 1050
    350 IF Y=3 THEN 1070
    360 IF Y=4 THEN 1090
    370 IF Y=5 THEN 1110
    380 IF Y=6 THEN 1130
    390 IF Z=1 THEN 1150
    400 IF Z=2 THEN 1170
    410 IF Z=3 THEN 1190
    420 IF Z=4 THEN 1210
    430 IF Z=5 THEN 1230
    440 IF Z=6 THEN 1250
    450 IF X=Y THEN 600
    460 IF X=Z THEN 630
    470 IF Y=Z THEN 650
    480 PRINT
    490 PRINT "YOU LOST."
    500 LET P=P-M
    510 PRINT "YOUR STANDINGS ARE $"P
    520 PRINT "AGAIN";
    530 INPUT A$
    540 IF A$="Y" THEN 160
    550 PRINT
    560 IF P<0 THEN 670
    570 IF P=0 THEN 690
    580 IF P>0 THEN 710
    590 GOTO 1350
    600 IF Y=Z THEN 730
    610 IF Y=1 THEN 820
    620 GOTO 1341
    630 IF Z=1 THEN 820
    640 GOTO 470
    650 IF Z=1 THEN 820
    660 GOTO 1341
    670 PRINT "PAY UP!  PLEASE LEAVE YOUR MONEY ON THE TERMINAL."
    680 GOTO 1350
    690 PRINT"HEY, YOU BROKE EVEN."
    700 GOTO 1350
    710 PRINT "COLLECT YOUR WINNINGS FROM THE H&M CASHIER."
    720 GOTO 1350
    730 IF Z=1 THEN 780
    740 PRINT: PRINT"**TOP DOLLAR**"
    750 PRINT "YOU WON!"
    760 P=(((10*M)+M)+P)
    770 GOTO 510
    780 PRINT:PRINT"***JACKPOT***"
    790 PRINT "YOU WON!"
    800 P=(((100*M)+M)+P)
    810 GOTO 510
    820 PRINT:PRINT"*DOUBLE BAR*"
    830 PRINT"YOU WON!"
    840 P=(((5*M)+M)+P)
    850 GOTO 510
    860 PRINT"HOUSE LIMITS ARE $100"
    870 GOTO 160
    880 PRINT"MINIMUM BET IS $1"
    890 GOTO 160
    900 GOTO 220
    910 PRINT"BAR";:GOSUB 1310
    920 GOTO 330
    930 PRINT"BELL";:GOSUB 1310
    940 GOTO 330
    950 PRINT"ORANGE";:GOSUB 1310
    960 GOTO 330
    970 PRINT"LEMON";:GOSUB 1310
    980 GOTO 330
    990 PRINT"PLUM";:GOSUB 1310
    1000 GOTO 330
    1010 PRINT"CHERRY";:GOSUB 1310
    1020 GOTO 330
    1030 PRINT" BAR";:GOSUB 1310
    1040 GOTO 390
    1050 PRINT" BELL";:GOSUB 1310
    1060 GOTO 390
    1070 PRINT" ORANGE";:GOSUB 1310
    1080 GOTO 390
    1090 PRINT" LEMON";:GOSUB 1310
    1100 GOTO 390
    1110 PRINT" PLUM";:GOSUB 1310
    1120 GOTO 390
    1130 PRINT" CHERRY";:GOSUB 1310
    1140 GOTO 390
    1150 PRINT" BAR"
    1160 GOTO 450
    1170 PRINT" BELL"
    1180 GOTO 450
    1190 PRINT" ORANGE"
    1200 GOTO 450
    1210 PRINT" LEMON"
    1220 GOTO 450
    1230 PRINT" PLUM"
    1240 GOTO 450
    1250 PRINT" CHERRY"
    1260 GOTO 450
    1270 FOR Q4=1 TO 10
    1280 PRINT CHR$(7);
    1290 NEXT Q4
    1300 RETURN
    1310 FOR T8=1 TO 5
    1320 PRINT CHR$(7);
    1330 NEXT T8
    1340 RETURN
    1341 PRINT: PRINT "DOUBLE!!"
    1342 PRINT"YOU WON!"
    1343 P=(((2*M)+M)+P)
    1344 GOTO 510
    1350 STOP
    9999 END
    
    
    ================================================
    FILE: 80_Slots/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 80_Slots/vbnet/Slots.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Slots", "Slots.vbproj", "{F64CB361-215F-4984-B154-304FF663A60C}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{F64CB361-215F-4984-B154-304FF663A60C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{F64CB361-215F-4984-B154-304FF663A60C}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{F64CB361-215F-4984-B154-304FF663A60C}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{F64CB361-215F-4984-B154-304FF663A60C}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 80_Slots/vbnet/Slots.vbproj
    ================================================
    
      
        Exe
        Slots
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 81_Splat/README.md
    ================================================
    ### Splat
    
    SPLAT simulates a parachute jump in which you try to open your parachute at the last possible moment without going splat! You may select your own terminal velocity or let the computer do it for you. You many also select the acceleration due to gravity or, again, let the computer do it in which case you might wind up on any of eight planets (out to Neptune), the moon, or the sun.
    
    The computer then tells you the height you’re jumping from and asks for the seconds of free fall. It then divides your free fall time into eight intervals and gives you progress reports on your way down. The computer also keeps track of all prior jumps in the array A and lets you know how you compared with previous successful jumps. If you want to recall information from previous runs, then you should store array A in a disk or take file and read it before each run.
    
    John Yegge created this program while at the Oak Ridge Associated Universities.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=151)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=166)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 81_Splat/csharp/Program.cs
    ================================================
    using System.Collections;
    using System.Text;
    
    namespace Splat
    {
        class Splat
        {
            private ArrayList DistanceLog = new ArrayList();
    
            private string[][] AccelerationData =
            {
                new string[] {"Fine. You're on Mercury. Acceleration={0} ft/sec/sec", "12.2"},
                new string[] {"All right.  You're on Venus. Acceleration={0} ft/sec/sec", "28.3"},
                new string[] {"Then you're on Earth. Acceleration={0} ft/sec/sec", "32.16"},
                new string[] {"Fine. You're on the Moon. Acceleration={0} ft/sec/sec", "5.15"},
                new string[] {"All right. You're on Mars. Acceleration={0} ft/sec/sec", "12.5"},
                new string[] {"Then you're on Jupiter. Acceleration={0} ft/sec/sec", "85.2"},
                new string[] {"Fine. You're on Saturn. Acceleration={0} ft/sec/sec", "37.6"},
                new string[] {"All right. You're on Uranus. Acceleration={0} ft/sec/sec", "33.8"},
                new string[] {"Then you're on Neptune. Acceleration={0} ft/sec/sec", "39.6"},
                new string[] {"Fine. You're on the Sun. Acceleration={0} ft/sec/sec", "896"}
            };
    
            private void DisplayIntro()
            {
                Console.WriteLine("");
                Console.WriteLine("SPLAT".PadLeft(23));
                Console.WriteLine("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
                Console.WriteLine("");
                Console.WriteLine("Welcome to 'Splat' -- the game that simulates a parachute");
                Console.WriteLine("jump.  Try to open your chute at the last possible");
                Console.WriteLine("moment without going splat.");
                Console.WriteLine("");
            }
    
            private bool PromptYesNo(string Prompt)
            {
                bool Success = false;
    
                while (!Success)
                {
                    Console.Write(Prompt);
                    string LineInput = Console.ReadLine().Trim().ToLower();
    
                    if (LineInput.Equals("yes"))
                        return true;
                    else if (LineInput.Equals("no"))
                        return false;
                    else
                        Console.WriteLine("Yes or No");
                }
    
                return false;
            }
    
            private void WriteRandomBadResult()
            {
               string[] BadResults = {"Requiescat in pace.","May the Angel of Heaven lead you into paradise.",
                    "Rest in peace.","Son-of-a-gun.","#$%&&%!$","A kick in the pants is a boost if you're headed right.",
                    "Hmmm. Should have picked a shorter time.","Mutter. Mutter. Mutter.","Pushing up daisies.",
                    "Easy come, easy go."};
    
                Random rand = new Random();
    
                Console.WriteLine(BadResults[rand.Next(BadResults.Length)]);
            }
    
            private void WriteColumnOutput(double Column1, double Column2)
            {
    
                Console.WriteLine("{0,-11:N3}    {1,-17:N2}", Column1, Column2);
    
            }
    
            private void WriteColumnOutput(double Column1, string Column2)
            {
    
                Console.WriteLine("{0,-11:N3}    {1,-17}", Column1, Column2);
    
            }
    
            private void WriteColumnOutput(string Column1, string Column2)
            {
    
                Console.WriteLine("{0,-11}    {1,-17}", Column1, Column2);
    
            }
    
            private void WriteSuccessfulResults(double Distance)
            {
                // Add new result
                DistanceLog.Add(Distance);
    
                // Sort by distance
                DistanceLog.Sort();
    
                int ArrayLength = DistanceLog.Count;
    
                // If 1st, 2nd, or 3rd jump then write a special message
                if (ArrayLength <= 3)
                {
                    Console.Write("Amazing!!! Not bad for your ");
                    if (ArrayLength == 1)
                        Console.Write("1st ");
                    else if (ArrayLength == 2)
                        Console.Write("2nd ");
                    else
                        Console.Write("3rd ");
                    Console.WriteLine("successful jump!!!");
                }
                // Otherwise write a message based on where this jump falls in the list
                else
                {
                    int JumpPosition = DistanceLog.IndexOf(Distance);
    
    
                    if (ArrayLength - JumpPosition <= .1 * ArrayLength)
                    {
                        Console.WriteLine("Wow! That's some jumping. Of the {0} successful jumps", ArrayLength);
                        Console.WriteLine("before yours, only {0} opened their chutes lower than", (ArrayLength - JumpPosition));
                        Console.WriteLine("you did.");
                    }
                    else if (ArrayLength - JumpPosition <= .25 * ArrayLength)
                    {
                        Console.WriteLine("Pretty good! {0} successful jumps preceded yours and only", ArrayLength - 1);
                        Console.WriteLine("{0} of them got lower than you did before their chutes", (ArrayLength - 1 - JumpPosition));
                        Console.WriteLine("opened.");
                    }
                    else if (ArrayLength - JumpPosition <= .5 * ArrayLength)
                    {
                        Console.WriteLine("Not bad. There have been  {0} successful jumps before yours.", ArrayLength - 1);
                        Console.WriteLine("You were beaten out by {0} of them.", (ArrayLength - 1 - JumpPosition));
                    }
                    else if (ArrayLength - JumpPosition <= .75 * ArrayLength)
                    {
                        Console.WriteLine("Conservative aren't you? You ranked only {0} in the", (ArrayLength - JumpPosition));
                        Console.WriteLine("{0} successful jumps before yours.", ArrayLength - 1);
                    }
                    else if (ArrayLength - JumpPosition <= .9 * ArrayLength)
                    {
                        Console.WriteLine("Humph! Don't you have any sporting blood? There were");
                        Console.WriteLine("{0} successful jumps before yours and you came in {1} jumps", ArrayLength - 1, JumpPosition);
                        Console.WriteLine("better than the worst. Shape up!!!");
                    }
                    else
                    {
                        Console.WriteLine("Hey! You pulled the rip cord much too soon. {0} successful", ArrayLength - 1);
                        Console.WriteLine("jumps before yours and you came in number {0}! Get with it!", (ArrayLength - JumpPosition));
                    }
                }
    
            }
    
            private void PlayOneRound()
            {
                bool InputSuccess = false;
                Random rand = new Random();
                double Velocity = 0;
                double TerminalVelocity = 0;
                double Acceleration = 0;
                double AccelerationInput = 0;
                double Altitude = ((9001 * rand.NextDouble()) + 1000);
                double SecondsTimer = 0;
                double Distance = 0;
                bool TerminalVelocityReached = false;
    
                Console.WriteLine("");
    
                // Determine the terminal velocity (user or system)
                if (PromptYesNo("Select your own terminal velocity (yes or no)? "))
                {
                    // Prompt user to enter the terminal velocity of their choice
                    while (!InputSuccess)
                    {
                        Console.Write("What terminal velocity (mi/hr)? ");
                        string Input = Console.ReadLine().Trim();
                        InputSuccess = double.TryParse(Input, out TerminalVelocity);
                        if (!InputSuccess)
                            Console.WriteLine("*** Please enter a valid number ***");
                     }
                }
                else
                {
                    TerminalVelocity = rand.NextDouble() * 1000;
                    Console.WriteLine("OK.  Terminal Velocity = {0:N0} mi/hr", (TerminalVelocity));
                }
    
                // Convert Terminal Velocity to ft/sec
                TerminalVelocity = TerminalVelocity * 5280 / 3600;
    
                // Not sure what this calculation is
                Velocity = TerminalVelocity + ((TerminalVelocity * rand.NextDouble()) / 20) - ((TerminalVelocity * rand.NextDouble()) / 20);
    
                // Determine acceleration due to gravity (user or system)
                if (PromptYesNo("Want to select acceleration due to gravity (yes or no)? "))
                {
                     // Prompt user to enter the acceleration of their choice
                    InputSuccess = false;
                    while (!InputSuccess)
                    {
                        Console.Write("What acceleration (ft/sec/sec)? ");
                        string Input = Console.ReadLine().Trim();
                        InputSuccess = double.TryParse(Input, out AccelerationInput);
                        if (!InputSuccess)
                            Console.WriteLine("*** Please enter a valid number ***");
                     }
                }
                else
                {
                    // Choose a random acceleration entry from the data array
                    int Index = rand.Next(0, AccelerationData.Length);
                    Double.TryParse(AccelerationData[Index][1], out AccelerationInput);
    
                    // Display the corresponding planet this acceleration exists on and the value
                    Console.WriteLine(AccelerationData[Index][0], AccelerationInput.ToString());
                }
    
                Acceleration = AccelerationInput + ((AccelerationInput * rand.NextDouble()) / 20) - ((AccelerationInput * rand.NextDouble()) / 20);
    
                Console.WriteLine("");
                Console.WriteLine("    Altitude         = {0:N0} ft", Altitude);
                Console.WriteLine("    Term. Velocity   = {0:N3} ft/sec +/-5%", TerminalVelocity);
                Console.WriteLine("    Acceleration     = {0:N2} ft/sec/sec +/-5%", AccelerationInput);
                Console.WriteLine("Set the timer for your freefall.");
    
                // Prompt for how many seconds the fall should be before opening the chute
                InputSuccess = false;
                while (!InputSuccess)
                {
                    Console.Write("How many seconds? ");
                    string Input = Console.ReadLine().Trim();
                    InputSuccess = double.TryParse(Input, out SecondsTimer);
                    if (!InputSuccess)
                        Console.WriteLine("*** Please enter a valid number ***");
                }
    
                // Begin the drop!
                Console.WriteLine("Here we go.");
                Console.WriteLine("");
    
                WriteColumnOutput("Time (sec)", "Dist to Fall (ft)");
                WriteColumnOutput("==========", "=================");
    
                // Loop through the number of seconds stepping by 8 intervals
                for (double i = 0; i < SecondsTimer; i+=(SecondsTimer/8))
                {
                    if (i > (Velocity / Acceleration))
                    {
                        // Terminal Velocity achieved.  Only print out the warning once.
                        if (TerminalVelocityReached == false)
                            Console.WriteLine("Terminal velocity reached at T plus {0:N4} seconds.", (Velocity / Acceleration));
    
                        TerminalVelocityReached = true;
                    }
    
                    // Calculate distance dependent upon whether terminal velocity has been reached
                    if (TerminalVelocityReached)
                    {
                        Distance = Altitude - ((Math.Pow(Velocity,2) / (2 * Acceleration)) + (Velocity * (i - (Velocity / Acceleration))));
                    }
                    else
                    {
                        Distance = Altitude - ((Acceleration / 2) * Math.Pow(i,2));
                    }
    
                    // Was the ground hit?  If so, then SPLAT!
                    if (Distance <= 0)
                    {
                        if (TerminalVelocityReached)
                        {
                            WriteColumnOutput((Velocity / Acceleration) + ((Altitude - (Math.Pow(Velocity,2) / (2 * Acceleration))) / Velocity).ToString(), "SPLAT");
                        }
                        else
                        {
                            WriteColumnOutput(Math.Sqrt(2 * Altitude / Acceleration), "SPLAT");
                        }
    
                        WriteRandomBadResult();
    
                        Console.WriteLine("I'll give you another chance.");
                        break;
                    }
                    else
                    {
                        WriteColumnOutput(i, Distance);
                    }
                }
    
                // If the number of seconds of drop ended and we are still above ground then success!
                if (Distance > 0)
                {
                    // We made it!  Chutes open!
                    Console.WriteLine("Chute Open");
    
                    // Store succesful jump and write out a fun message
                    WriteSuccessfulResults(Distance);
                }
    
            }
    
            public void PlayTheGame()
            {
                bool ContinuePlay = false;
    
                DisplayIntro();
    
                do
                {
                    PlayOneRound();
    
                    ContinuePlay = PromptYesNo("Do you want to play again? ");
                    if (!ContinuePlay)
                        ContinuePlay = PromptYesNo("Please? ");
                }
                while (ContinuePlay);
    
                Console.WriteLine("SSSSSSSSSS.");
    
            }
        }
        class Program
        {
            static void Main(string[] args)
            {
    
                new Splat().PlayTheGame();
    
            }
        }
    }
    
    
    ================================================
    FILE: 81_Splat/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 81_Splat/csharp/Splat.csproj
    ================================================
    
      
        Exe
        net6.0
        10
        enable
        enable
      
    
    
    
    ================================================
    FILE: 81_Splat/csharp/Splat.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Splat", "Splat.csproj", "{95640CEE-A1A6-4824-A2C7-CF72CADA40FB}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{95640CEE-A1A6-4824-A2C7-CF72CADA40FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{95640CEE-A1A6-4824-A2C7-CF72CADA40FB}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{95640CEE-A1A6-4824-A2C7-CF72CADA40FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{95640CEE-A1A6-4824-A2C7-CF72CADA40FB}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 81_Splat/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 81_Splat/java/src/Splat.java
    ================================================
    import java.util.*;
    
    /**
     * SPLAT simulates a parachute jump in which you try to open your parachute at the last possible moment without going
     * splat! You may select your own terminal velocity or let the computer do it for you. You many also select the
     * acceleration due to gravity or, again, let the computer do it in which case you might wind up on any of eight
     * planets (out to Neptune), the moon, or the sun.
     * 

    * The computer then tells you the height you’re jumping from and asks for the seconds of free fall. It then divides * your free fall time into eight intervals and gives you progress reports on your way down. The computer also keeps * track of all prior jumps in the array A and lets you know how you compared with previous successful jumps. If you * want to recall information from previous runs, then you should store array A in a disk or take file and read it * before each run. *

    * John Yegge created this program while at the Oak Ridge Associated Universities. *

    * Ported from BASIC by jason plumb (@breedx2) *

    */ public class Splat { private static final Random random = new Random(); private final Scanner scanner = new Scanner(System.in); private final List pastSuccessfulJumpDistances = new ArrayList<>(); public static void main(String[] args) { new Splat().run(); } public void run() { showIntroduction(); while (true) { InitialJumpConditions initial = buildInitialConditions(); System.out.println(); System.out.printf(" ALTITUDE = %d FT\n", initial.getAltitude()); System.out.printf(" TERM. VELOCITY = %.2f FT/SEC +/-5%%\n", initial.getOriginalTerminalVelocity()); System.out.printf(" ACCELERATION = %.2f FT/SEC/SEC +/-5%%\n", initial.getOriginalAcceleration()); System.out.println("SET THE TIMER FOR YOUR FREEFALL."); float freefallTime = promptFloat("HOW MANY SECONDS "); System.out.println("HERE WE GO.\n"); System.out.println("TIME (SEC) DIST TO FALL (FT)"); System.out.println("========== ================="); JumpResult jump = executeJump(initial, freefallTime); showJumpResults(initial, jump); if (!playAgain()) { System.out.println("SSSSSSSSSS."); return; } } } private void showIntroduction() { System.out.printf("%33s%s\n", " ", "SPLAT"); System.out.printf("%15s%s\n", " ", "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.print("\n\n\n"); System.out.println("WELCOME TO 'SPLAT' -- THE GAME THAT SIMULATES A PARACHUTE"); System.out.println("JUMP. TRY TO OPEN YOUR CHUTE AT THE LAST POSSIBLE"); System.out.println("MOMENT WITHOUT GOING SPLAT."); } private InitialJumpConditions buildInitialConditions() { System.out.print("\n\n"); float terminalVelocity = promptTerminalVelocity(); float acceleration = promptGravitationalAcceleration(); return InitialJumpConditions.create(terminalVelocity, acceleration); } private float promptTerminalVelocity() { if (askYesNo("SELECT YOUR OWN TERMINAL VELOCITY")) { float terminalVelocity = promptFloat("WHAT TERMINAL VELOCITY (MI/HR) "); return mphToFeetPerSec(terminalVelocity); } float terminalVelocity = (int) (1000 * random.nextFloat()); System.out.printf("OK. TERMINAL VELOCITY = %.2f MI/HR\n", terminalVelocity); return mphToFeetPerSec(terminalVelocity); } private float promptFloat(String prompt){ while(true){ System.out.print(prompt); try { return scanner.nextFloat(); } catch (Exception e) { scanner.next(); // clear current input } } } private float promptGravitationalAcceleration() { if (askYesNo("WANT TO SELECT ACCELERATION DUE TO GRAVITY")) { return promptFloat("WHAT ACCELERATION (FT/SEC/SEC) "); } return chooseRandomAcceleration(); } private JumpResult executeJump(InitialJumpConditions initial, float chuteOpenTime) { JumpResult jump = new JumpResult(initial.getAltitude()); for (float time = 0.0f; time < chuteOpenTime; time += chuteOpenTime / 8) { if (!jump.hasReachedTerminalVelocity() && time > initial.getTimeOfTerminalAccelerationReached()) { jump.setReachedTerminalVelocity(); System.out.printf("TERMINAL VELOCITY REACHED AT T PLUS %f SECONDS.\n", initial.getTimeOfTerminalAccelerationReached()); } float newDistance = computeDistance(initial, time, jump.hasReachedTerminalVelocity()); jump.setDistance(newDistance); if (jump.isSplat()) { return jump; } System.out.printf("%10.2f %f\n", time, jump.getDistance()); } return jump; } private float computeDistance(InitialJumpConditions initial, float i, boolean hasReachedTerminalVelocity) { final float V = initial.getTerminalVelocity(); final float A = initial.getAcceleration(); if (hasReachedTerminalVelocity) { return initial.getAltitude() - ((V * V / (2 * A)) + (V * (i - (V / A)))); } return initial.getAltitude() - ((A / 2) * i * i); } private void showJumpResults(InitialJumpConditions initial, JumpResult jump) { if (jump.isSplat()) { showSplatMessage(initial, jump); showCleverSplatMessage(); return; } System.out.println("CHUTE OPEN"); int worseJumpCount = countWorseHistoricalJumps(jump); int successfulJumpCt = pastSuccessfulJumpDistances.size(); pastSuccessfulJumpDistances.add(jump.getDistance()); if (pastSuccessfulJumpDistances.size() <= 2) { List ordinals = Arrays.asList("1ST", "2ND", "3RD"); System.out.printf("AMAZING!!! NOT BAD FOR YOUR %s SUCCESSFUL JUMP!!!\n", ordinals.get(successfulJumpCt)); return; } int betterThanCount = successfulJumpCt - worseJumpCount; if (betterThanCount <= 0.1 * successfulJumpCt) { System.out.printf("WOW! THAT'S SOME JUMPING. OF THE %d SUCCESSFUL JUMPS\n", successfulJumpCt); System.out.printf("BEFORE YOURS, ONLY %d OPENED THEIR CHUTES LOWER THAN\n", betterThanCount); System.out.println("YOU DID."); } else if (betterThanCount <= 0.25 * successfulJumpCt) { System.out.printf("PRETTY GOOD! %d SUCCESSFUL JUMPS PRECEDED YOURS AND ONLY\n", successfulJumpCt); System.out.printf("%d OF THEM GOT LOWER THAN YOU DID BEFORE THEIR CHUTES\n", betterThanCount); System.out.println("OPENED."); } else if (betterThanCount <= 0.5 * successfulJumpCt) { System.out.printf("NOT BAD. THERE HAVE BEEN %d SUCCESSFUL JUMPS BEFORE YOURS.\n", successfulJumpCt); System.out.printf("YOU WERE BEATEN OUT BY %d OF THEM.\n", betterThanCount); } else if (betterThanCount <= 0.75 * successfulJumpCt) { System.out.printf("CONSERVATIVE, AREN'T YOU? YOU RANKED ONLY %d IN THE\n", betterThanCount); System.out.printf("%d SUCCESSFUL JUMPS BEFORE YOURS.\n", successfulJumpCt); } else if (betterThanCount <= -0.9 * successfulJumpCt) { System.out.println("HUMPH! DON'T YOU HAVE ANY SPORTING BLOOD? THERE WERE"); System.out.printf("%d SUCCESSFUL JUMPS BEFORE YOURS AND YOU CAME IN %d JUMPS\n", successfulJumpCt, worseJumpCount); System.out.println("BETTER THAN THE WORST. SHAPE UP!!!\n"); } else { System.out.printf("HEY! YOU PULLED THE RIP CORD MUCH TOO SOON. %d SUCCESSFUL\n", successfulJumpCt); System.out.printf("JUMPS BEFORE YOURS AND YOU CAME IN NUMBER %d! GET WITH IT!\n", betterThanCount); } } private void showSplatMessage(InitialJumpConditions initial, JumpResult jump) { double timeOfSplat = computeTimeOfSplat(initial, jump); System.out.printf("%10.2f SPLAT\n", timeOfSplat); } /** * Returns the number of jumps for which this jump was better */ private double computeTimeOfSplat(InitialJumpConditions initial, JumpResult jump) { final float V = initial.getTerminalVelocity(); final float A = initial.getAcceleration(); if (jump.hasReachedTerminalVelocity()) { return (V / A) + ((initial.getAltitude() - (V * V / (2 * A))) / V); } return Math.sqrt(2 * initial.getAltitude() / A); } private int countWorseHistoricalJumps(JumpResult jump) { return (int) pastSuccessfulJumpDistances.stream() .filter(distance -> jump.getDistance() < distance) .count(); } private void showCleverSplatMessage() { List messages = Arrays.asList( "REQUIESCAT IN PACE.", "MAY THE ANGEL OF HEAVEN LEAD YOU INTO PARADISE.", "REST IN PEACE.", "SON-OF-A-GUN.", "#$%&&%!$", "A KICK IN THE PANTS IS A BOOST IF YOU'RE HEADED RIGHT.", "HMMM. SHOULD HAVE PICKED A SHORTER TIME.", "MUTTER. MUTTER. MUTTER.", "PUSHING UP DAISIES.", "EASY COME, EASY GO." ); System.out.println(messages.get(random.nextInt(10))); } private boolean playAgain() { if (askYesNo("DO YOU WANT TO PLAY AGAIN ")) { return true; } return askYesNo("PLEASE"); } private float mphToFeetPerSec(float speed) { return speed * (5280.0f / 3600.0f); } private boolean askYesNo(String prompt) { System.out.printf("%s (YES OR NO) ", prompt); while (true) { String answer = scanner.next(); switch (answer) { case "YES": return true; case "NO": return false; default: System.out.print("YES OR NO "); } } } private float chooseRandomAcceleration() { Planet planet = Planet.pickRandom(); System.out.printf("%s %s. ACCELERATION=%.2f FT/SEC/SEC.\n", planet.getMessage(), planet.name(), planet.getAcceleration()); return planet.getAcceleration(); } enum Planet { MERCURY("FINE. YOU'RE ON", 12.2f), VENUS("ALL RIGHT. YOU'RE ON", 28.3f), EARTH("THEN YOU'RE ON", 32.16f), MOON("FINE. YOU'RE ON THE", 5.15f), MARS("ALL RIGHT. YOU'RE ON", 12.5f), JUPITER("THEN YOU'RE ON", 85.2f), SATURN("FINE. YOU'RE ON", 37.6f), URANUS("ALL RIGHT. YOU'RE ON", 33.8f), NEPTUNE("THEN YOU'RE ON", 39.6f), SUN("FINE. YOU'RE ON THE", 896.0f); private static final Random random = new Random(); private final String message; private final float acceleration; Planet(String message, float acceleration) { this.message = message; this.acceleration = acceleration; } static Planet pickRandom() { return values()[random.nextInt(Planet.values().length)]; } String getMessage() { return message; } float getAcceleration() { return acceleration; } } // Mutable static class JumpResult { private boolean reachedTerminalVelocity = false; private float distance; // from the ground public JumpResult(float distance) { this.distance = distance; } boolean isSplat() { return distance <= 0; } boolean hasReachedTerminalVelocity() { return reachedTerminalVelocity; } float getDistance() { return distance; } void setDistance(float distance) { this.distance = distance; } void setReachedTerminalVelocity() { reachedTerminalVelocity = true; } } // Immutable static class InitialJumpConditions { private final float originalTerminalVelocity; private final float originalAcceleration; private final float terminalVelocity; private final float acceleration; private final int altitude; private InitialJumpConditions(float originalTerminalVelocity, float originalAcceleration, float terminalVelocity, float acceleration, int altitude) { this.originalTerminalVelocity = originalTerminalVelocity; this.originalAcceleration = originalAcceleration; this.terminalVelocity = terminalVelocity; this.acceleration = acceleration; this.altitude = altitude; } // Create initial jump conditions with adjusted velocity/acceleration and a random initial altitude private static InitialJumpConditions create(float terminalVelocity, float gravitationalAcceleration) { final int altitude = (int) (9001.0f * random.nextFloat() + 1000); return new InitialJumpConditions(terminalVelocity, gravitationalAcceleration, plusMinus5Percent(terminalVelocity), plusMinus5Percent(gravitationalAcceleration), altitude); } private static float plusMinus5Percent(float value) { return value + ((value * random.nextFloat()) / 20.0f) - ((value * random.nextFloat()) / 20.0f); } float getOriginalTerminalVelocity() { return originalTerminalVelocity; } float getOriginalAcceleration() { return originalAcceleration; } float getTerminalVelocity() { return terminalVelocity; } float getAcceleration() { return acceleration; } int getAltitude() { return altitude; } float getTimeOfTerminalAccelerationReached() { return terminalVelocity / acceleration; } } } ================================================ FILE: 81_Splat/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 81_Splat/javascript/splat.html ================================================ SPLAT
    
    
    
    
    
    
    ================================================
    FILE: 81_Splat/javascript/splat.js
    ================================================
    // SPLAT
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    var aa = [];
    
    // Main program
    async function main()
    {
        print(tab(33) + "SPLAT\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        for (i = 0; i <= 42; i++)
            aa[i] = 0;
        print("WELCOME TO 'SPLAT' -- THE GAME THAT SIMULATES A PARACHUTE\n");
        print("JUMP.  TRY TO OPEN YOUR CHUTE AT THE LAST POSSIBLE\n");
        print("MOMENT WITHOUT GOING SPLAT.\n");
        while (1) {
            print("\n");
            print("\n");
            d1 = 0;
            v = 0;
            a = 0;
            n = 0;
            m = 0;
            d1 = Math.floor(9001 * Math.random() + 1000);
            print("SELECT YOUR OWN TERMINAL VELOCITY (YES OR NO)");
            while (1) {
                a1s = await input();
                if (a1s == "YES" || a1s == "NO")
                    break;
                print("YES OR NO");
            }
            if (a1s == "YES") {
                print("WHAT TERMINAL VELOCITY (MI/HR)");
                v1 = parseFloat(await input());
                v1 = v1 * (5280 / 3600);
            } else {
                v1 = Math.floor(1000 * Math.random());
                print("OK.  TERMINAL VELOCITY = " + v1 + " MI/HR\n");
            }
            v = v1 + ((v1 * Math.random()) / 20) - ((v1 * Math.random()) / 20);
            print("WANT TO SELECT ACCELERATION DUE TO GRAVITY (YES OR NO)");
            while (1) {
                b1s = await input();
                if (b1s == "YES" || b1s == "NO")
                    break;
                print("YES OR NO");
            }
            if (b1s == "YES") {
                print("WHAT ACCELERATION (FT/SEC/SEC)");
                a2 = parseFloat(await input());
            } else {
                switch (Math.floor(1 + (10 * Math.random()))) {
                    case 1:
                        print("FINE. YOU'RE ON MERCURY. ACCELERATION=12.2 FT/SEC/SEC.\n");
                        a2 = 12.2;
                        break;
                    case 2:
                        print("ALL RIGHT. YOU'RE ON VENUS. ACCELERATION=28.3 FT/SEC/SEC.\n");
                        a2 = 28.3;
                        break;
                    case 3:
                        print("THEN YOU'RE ON EARTH. ACCELERATION=32.16 FT/SEC/SEC.\n");
                        a2 = 32.16;
                        break;
                    case 4:
                        print("FINE. YOU'RE ON THE MOON. ACCELERATION=5.15 FT/SEC/SEC.\n");
                        a2 = 5.15;
                        break;
                    case 5:
                        print("ALL RIGHT. YOU'RE ON MARS. ACCELERATION=12.5 FT/SEC/SEC.\n");
                        a2 = 12.5;
                        break;
                    case 6:
                        print("THEN YOU'RE ON JUPITER. ACCELERATION=85.2 FT/SEC/SEC.\n");
                        a2 = 85.2;
                        break;
                    case 7:
                        print("FINE. YOU'RE ON SATURN. ACCELERATION=37.6 FT/SEC/SEC.\n");
                        a2 = 37.6;
                        break;
                    case 8:
                        print("ALL RIGHT. YOU'RE ON URANUS. ACCELERATION=33.8 FT/SEC/SEC.\n");
                        a2 = 33.8;
                        break;
                    case 9:
                        print("THEN YOU'RE ON NEPTUNE. ACCELERATION=39.6 FT/SEC/SEC.\n");
                        a2 = 39.6;
                        break;
                    case 10:
                        print("FINE. YOU'RE ON THE SUN. ACCELERATION=896 FT/SEC/SEC.\n");
                        a2 = 896;
                        break;
                }
            }
            a = a2 + ((a2 * Math.random()) / 20) - ((a2 * Math.random()) / 20);
            print("\n");
            print("    ALTITUDE         = " + d1 + " FT\n");
            print("    TERM. VELOCITY   = " + v1 + " FT/SEC +/-5%\n");
            print("    ACCELERATION     = " + a2 + " FT/SEC/SEC +/-5%\n");
            print("SET THE TIMER FOR YOUR FREEFALL.\n");
            print("HOW MANY SECONDS");
            t = parseFloat(await input());
            print("HERE WE GO.\n");
            print("\n");
            print("TIME (SEC)\tDIST TO FALL (FT)\n");
            print("==========\t=================\n");
            terminal = false;
            crash = false;
            for (i = 0; i <= t; i += t / 8) {
                if (i > v / a) {
                    terminal = true;
                    break;
                }
                d = d1 - ((a / 2) * Math.pow(i, 2));
                if (d <= 0) {
                    print(Math.sqrt(2 * d1 / a) + "\tSPLAT\n");
                    crash = true;
                    break;
                }
                print(i + "\t" + d + "\n");
            }
            if (terminal) {
                print("TERMINAL VELOCITY REACHED AT T PLUS " + v/a + " SECONDS.\n");
                for (; i <= t; i += t / 8) {
                    d = d1 - ((Math.pow(v, 2) / (2 * a)) + (v * (i - (v / a))));
                    if (d <= 0) {
                        print(((v / a) + ((d1 - (Math.pow(v, 2) / (2 * a))) / v)) + "\tSPLAT\n");
                        crash = true;
                        break;
                    }
                    print(i + "\t" + d + "\n");
                }
            }
            if (!crash) {
                print("CHUTE OPEN\n");
                k = 0;
                k1 = 0;
                for (j = 0; j <= 42; j++) {
                    if (aa[j] == 0)
                        break;
                    k++;
                    if (d < aa[j])
                        k1++;
                }
                // In original jumps to line 540 (undefined) when table is full
                aa[j] = d;
                if (j <= 2) {
                    print("AMAZING!!! NOT BAD FOR YOUR ");
                    if (j == 0)
                        print("1ST ");
                    else if (j == 1)
                        print("2ND ");
                    else
                        print("3RD ");
                    print("SUCCESSFUL JUMP!!!\n");
                } else {
                    if (k - k1 <= 0.1 * k) {
                        print("WOW!  THAT'S SOME JUMPING.  OF THE " + k + " SUCCESSFUL JUMPS\n");
                        print("BEFORE YOURS, ONLY " + (k - k1) + " OPENED THEIR CHUTES LOWER THAN\n");
                        print("YOU DID.\n");
                    } else if (k - k1 <= 0.25 * k) {
                        print("PRETTY GOOD! " + k + " SUCCESSFUL JUMPS PRECEDED YOURS AND ONLY\n");
                        print((k - k1) + " OF THEM GOT LOWER THAN YOU DID BEFORE THEIR CHUTES\n");
                        print("OPENED.\n");
                    } else if (k - k1 <= 0.5 * k) {
                        print("NOT BAD.  THERE HAVE BEEN " + k + " SUCCESSFUL JUMPS BEFORE YOURS.\n");
                        print("YOU WERE BEATEN OUT BY " + (k - k1) + " OF THEM.\n");
                    } else if (k - k1 <= 0.75 * k) {
                        print("CONSERVATIVE, AREN'T YOU?  YOU RANKED ONLY " + (k - k1) + " IN THE\n");
                        print(k + " SUCCESSFUL JUMPS BEFORE YOURS.\n");
                    } else if (k - k1 <= 0.9 * k) {
                        print("HUMPH!  DON'T YOU HAVE ANY SPORTING BLOOD?  THERE WERE\n");
                        print(k + " SUCCESSFUL JUMPS BEFORE YOURS AND YOU CAME IN " + k1 + "JUMPS\n");
                        print("BETTER THAN THE WORST.  SHAPE UP!!!\n");
                    } else {
                        print("HEY!  YOU PULLED THE RIP CORD MUCH TOO SOON.  " + k + " SUCCESSFUL\n");
                        print("JUMPS BEFORE YOURS AND YOU CAME IN NUMBER " + (k - k1) + "!  GET WITH IT!\n");
                    }
                }
            } else {
                switch (Math.floor(1 + 10 * Math.random())) {
                    case 1:
                        print("REQUIESCAT IN PACE.\n");
                        break;
                    case 2:
                        print("MAY THE ANGEL OF HEAVEN LEAD YOU INTO PARADISE.\n");
                        break;
                    case 3:
                        print("REST IN PEACE.\n");
                        break;
                    case 4:
                        print("SON-OF-A-GUN.\n");
                        break;
                    case 5:
                        print("#%&&%!$\n");
                        break;
                    case 6:
                        print("A KICK IN THE PANTS IS A BOOST IF YOU'RE HEADED RIGHT.\n");
                        break;
                    case 7:
                        print("HMMM. SHOULD HAVE PICKED A SHORTER TIME.\n");
                        break;
                    case 8:
                        print("MUTTER. MUTTER. MUTTER.\n");
                        break;
                    case 9:
                        print("PUSHING UP DAISIES.\n");
                        break;
                    case 10:
                        print("EASY COME, EASY GO.\n");
                        break;
                }
                print("I'LL GIVE YOU ANOTHER CHANCE.\n");
            }
            while (1) {
                print("DO YOU WANT TO PLAY AGAIN");
                str = await input();
                if (str == "YES" || str == "NO")
                    break;
                print("YES OR NO\n");
            }
            if (str == "YES")
                continue;
            print("PLEASE");
            while (1) {
                str = await input();
                if (str == "YES" || str == "NO")
                    break;
                print("YES OR NO");
            }
            if (str == "YES")
                continue;
            break;
        }
        print("SSSSSSSSSS.\n");
        print("\n");
    }
    
    main();
    
    
    ================================================
    FILE: 81_Splat/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 81_Splat/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 81_Splat/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 81_Splat/perl/splat.pl
    ================================================
    #!/usr/bin/env perl
    
    use 5.010;      # To get 'state' and 'say'
    
    use strict;     # Require explicit declaration of variables
    use warnings;   # Enable optional compiler warnings
    
    use English;    # Use more friendly names for Perl's magic variables
    use List::Util qw{ shuffle };   # Shuffle an array.
    use Scalar::Util qw{ looks_like_number };
    use Term::ReadLine;     # Prompt and return user input
    
    our $VERSION = '0.000_01';
    
    use constant ROW_TPLT => ( '%4d' x 8 ) . "\n";
    
    print <<'EOD';
                                     SPLAT
                   Creative Computing  Morristown, New Jersey
    
    
    
    Welcome to 'Splat' -- the game that simulates a parachute
    jump.  Try to open your chute at the last possible
    moment without going splat.
    EOD
    
    while ( 1 ) {
        say '';
        my $initial_altitude = int( 9001 * rand() + 1000 );
    
        my $nominal_terminal_velocity;
        if ( get_yes_no( 'Select your own terminal velocity' ) ) {
            $nominal_terminal_velocity = get_input(
                'What terminal velocity (mi/hr)? ',
                sub { looks_like_number( $ARG ) && $ARG > 0 },
                'Please enter a positive number',
            );
            # Convert miles per hour to feet per second
            $nominal_terminal_velocity = $nominal_terminal_velocity * 5280 / 3600;
        } else {
            $nominal_terminal_velocity = int( 1000 * rand() );
            say "OK.  Terminal velocity = $nominal_terminal_velocity mi/hr"
        }
        my $terminal_velocity = dither( $nominal_terminal_velocity );
    
        my $nominal_gravity; # Acceleration due to gravity
        if ( get_yes_no( 'Want to select acceleration due to gravity' ) ) {
        } else {
            state $body = [
                [ q,
                    12.2 ],
                [ q,
                    28.3 ],
                [ q,
                    32.16 ],
                [ q,
                    5.15 ],
                [ q,
                    12.5 ],
                [ q,
                    85.2 ],
                [ q,
                    37.6 ],
                [ q,
                    33.8  ],
                [ q,
                    39.6 ],
                [ q,
                    896 ],
            ];
            my $pick = $body->[ rand scalar @{ $body } ];
            say $pick->[0];
            $nominal_gravity = $pick->[1];
        }
        my $gravity = dither( $nominal_gravity );
    
        print <<"EOD";
    
        Altitude        = $initial_altitude ft
        Term. velocity  = $nominal_terminal_velocity ft/sec +/- 5%
        Acceleration    = $nominal_gravity ft/sec/sec +/- 5%
    Set the timer for your freefall
    EOD
    
        my $drop_time = get_input(
            'How many seconds? ',
            sub { m/ \A [0-9]+ \z /smx },
            "Please enter an unsigned integer\n",
        );
    
        print <<'EOD';
    Here we go.
    
    Time (sec)      Dist to fall (ft)
    ==========      =================
    EOD
    
        if ( defined( my $altitude = make_jump(
                    $initial_altitude,
                    $gravity,
                    $terminal_velocity,
                    $drop_time ) )
        ) {
            # Successful jump
            state $succesful = [];
            state $ordinal = [ qw{ 1st 2nd 3rd } ];
            if ( defined( my $ord = $ordinal->[ @{ $succesful } ] ) ) {
                say "Amazing!!! Not nad for your $ord successful jump!!!";
            } else {
                my $jumps = @{ $succesful };
                my $worse = grep { $_ > $altitude } @{ $succesful };
                my $fractile = 1 - $worse / $jumps;
                my $better = $jumps - $worse;
                if ( $fractile <= 0.1 ) {
                    print <<"EOD";
    Wow!  That's some jumping.  Of the $jumps successful jumps
    before yours, only $better opened their chutes lower than
    you did.
    EOD
                } elsif ( $fractile <= 0.25 ) {
                    print <<"EOD";
    Pretty good! $jumps successful jumps preceded yours and only
    $better of them got lower than you did before their chutes
    opened.
    EOD
                } elsif ( $fractile <= 0.5 ) {
                    print <<"EOD";
    Not bad.  There have been $jumps successful jumps before yours.
    You were beaten out by $better of them.
    EOD
                } elsif ( $fractile <= 0.75 ) {
                    print <<"EOD";
    Conservative, aren't you?  You ranked only $better in the
    $jumps successful jumps before yours.
    EOD
                } elsif ( $fractile <= 0.9 ) {
                    print <<"EOD";
    Humph!  Don't you have any sporting blood?  There were
    $jumps successful jumps before yours and you came in $worse jumps
    better than the worst.  Shape up!!!
    EOD
                } else {
                    print <<"EOD";
    Hey!  You pulled the rip cord much too soon.  $jumps successful
    jumps before yours and you came in number $better!  Get with it!
    EOD
                }
            }
            push @{ $succesful }, $altitude;
        } else {
            # Splat
    
            say q;
        }
    
        next if get_yes_no( 'Do you want to play again' );
        next if get_yes_no( 'Please' );
    
        print <<'EOD';
    Ssssssssss.
    
    EOD
        last;
    
    }
    
    # Return the first argument modified by up to plus or minus some
    # fraction specified by the second argument (default 0.05)
    sub dither {
        my ( $arg, $fract ) = @_;
        $fract //= 1 / 20;
        return $arg + ( $arg * rand() * $fract ) - ( $arg * rand() * $fract );
    }
    
    use constant FORMAT_FALL    => "%10.1f      %10d\n";
    use constant FORMAT_SPLAT   => "%10.1f      %s\n";
    sub make_jump {
        my ( $initial_altitude, $gravity, $terminal_velocity, $drop_time ) = @_;
        my $altitude;
        foreach my $step ( 0 .. 8 ) {
            my $time = $step * $drop_time / 8;
            if ( $time > $terminal_velocity / $gravity ) {
                # Terminal velocity reached
                printf "Terminal velocity reached at T plus %.2f seconds.\n",
                    $terminal_velocity / $gravity;
                for my $step ( $step .. 8 ) {
                    my $time = $step * $drop_time / 8;
                    $altitude = $initial_altitude - (
                        $terminal_velocity * $terminal_velocity /
                        ( 2 * $gravity ) + $terminal_velocity * (
                            $time - $terminal_velocity / $gravity ) );
                    if ( $altitude > 0 ) {
                        printf FORMAT_FALL, $time, $altitude;
                    } else {
                        splat(
                            $terminal_velocity / $gravity + (
                                $initial_altitude -
                                $terminal_velocity * $terminal_velocity /
                                ( 2 * $gravity ) ) / $terminal_velocity,
                        );
                        return;
                    }
                }
                last;
            } else {
                $altitude = $initial_altitude - $gravity / 2 * $time * $time;
                if ( $altitude > 0 ) {
                    printf FORMAT_FALL, $time, $altitude;
                } else {
                    splat( sqrt( 2 * $initial_altitude / $gravity ) );
                    return;
                }
            }
        }
    
        say 'Chute open.';
        return $altitude;
    }
    
    sub splat {
        my ( $time ) = @_;
        printf FORMAT_SPLAT, $time, 'Splat!';
        state $rip = [
            q,
            q,
            q,
            q,
            q<#$%&&%!$>,
            q
    , q, q, q, q, ]; say $rip->[ rand scalar @{ $rip } ]; return; } # Get input from the user. The arguments are: # * The prompt # * A reference to validation code. This code receives the response in # $ARG and returns true for a valid response. # * A warning to print if the response is not valid. This must end in a # return. # The first valid response is returned. An end-of-file terminates the # script. sub get_input { my ( $prompt, $validate, $warning ) = @ARG; # If no validator is passed, default to one that always returns # true. $validate ||= sub { 1 }; # Create the readline object. The 'state' causes the variable to be # initialized only once, no matter how many times this subroutine is # called. The do { ... } is a compound statement used because we # need to tweak the created object before we store it. state $term = do { my $obj = Term::ReadLine->new( 'reverse' ); $obj->ornaments( 0 ); $obj; }; while ( 1 ) { # Iterate indefinitely # Read the input into the topic variable, localized to prevent # Spooky Action at a Distance. We exit on undef, which signals # end-of-file. exit unless defined( local $ARG = $term->readline( $prompt ) ); # Return the input if it is valid. return $ARG if $validate->(); # Issue the warning, and go around the merry-go-round again. warn $warning; } } # Get a yes-or-no answer. The argument is the prompt, which will have # '? [y/n]: ' appended. The donkey work is done by get_input(), which is # requested to validate the response as beginning with 'y' or 'n', # case-insensitive. The return is a true value for 'y' and a false value # for 'n'. sub get_yes_no { my ( $prompt ) = @ARG; state $map_answer = { n => 0, y => 1, }; my $resp = lc get_input( "$prompt? [y/n]: ", sub { m/ \A [yn] /smxi }, "Please respond 'y' or 'n'\n", ); return $map_answer->{ substr $resp, 0, 1 }; } __END__ =head1 TITLE splat.pl - Play the game 'splat' from Basic Computer Games =head1 SYNOPSIS splat.pl =head1 DETAILS This Perl script is a port of C, which is the 73rd entry in Basic Computer Games. This is a very basic port. All I really did was untangle the spaghetti. =head1 PORTED BY Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2022 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the Artistic License 1.0 at L, and/or the Gnu GPL at L. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set expandtab tabstop=4 textwidth=72 : ================================================ FILE: 81_Splat/python/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Python](https://www.python.org/about/) ================================================ FILE: 81_Splat/python/splat.py ================================================ """ SPLAT Splat similates a parachute jump in which you try to open your parachute at the last possible moment without going splat! You may select your own terminal velocity or let the computer do it for you. You may also select the acceleration due to gravity or, again, let the computer do it in which case you might wind up on any one of the eight planets (out to Neptune), the moon, or the sun. The computer then tells you the height you're jumping from and asks for the seconds of free fall. It then divides your free fall time into eight intervals and gives you progress reports on the way down. The computer also keeps track of all prior jumps and lets you know how you compared with previous successful jumps. If you want to recall information from previous runs, then you should store the array `successful_jumps` on disk and read it before each run. John Yegge created this program while at the Oak Ridge Associated Universities. Ported in 2021 by Jonas Nockert / @lemonad """ from math import sqrt from random import choice, random, uniform from typing import List, Tuple PAGE_WIDTH = 72 def numeric_input(question, default=0) -> float: """Ask user for a numeric value.""" while True: answer_str = input(f"{question} [{default}]: ").strip() or default try: return float(answer_str) except ValueError: pass def yes_no_input(question: str, default="YES") -> bool: """Ask user a yes/no question and returns True if yes, otherwise False.""" answer = input(f"{question} (YES OR NO) [{default}]: ").strip() or default while answer.lower() not in ["n", "no", "y", "yes"]: answer = input(f"YES OR NO [{default}]: ").strip() or default return answer.lower() in ["y", "yes"] def get_terminal_velocity() -> float: """Terminal velocity by user or picked by computer.""" if yes_no_input("SELECT YOUR OWN TERMINAL VELOCITY", default="NO"): v1 = numeric_input("WHAT TERMINAL VELOCITY (MI/HR)", default=100) else: # Computer picks 0-1000 terminal velocity. v1 = int(1000 * random()) print(f"OK. TERMINAL VELOCITY = {v1} MI/HR") # Convert miles/h to feet/s. return v1 * (5280 / 3600) def get_acceleration() -> float: """Acceleration due to gravity by user or picked by computer.""" if yes_no_input("WANT TO SELECT ACCELERATION DUE TO GRAVITY", default="NO"): a2 = numeric_input("WHAT ACCELERATION (FT/SEC/SEC)", default=32.16) else: body, a2 = pick_random_celestial_body() print(f"FINE. YOU'RE ON {body}. ACCELERATION={a2} FT/SEC/SEC.") return a2 def get_freefall_time() -> float: """User-guessed freefall time. The idea of the game is to pick a freefall time, given initial altitude, terminal velocity and acceleration, so the parachute as close to the ground as possible without going splat. """ t_freefall: float = 0 # A zero or negative freefall time is not handled by the motion # equations during the jump. while t_freefall <= 0: t_freefall = numeric_input("HOW MANY SECONDS", default=10) return t_freefall def jump() -> float: """Simulate a jump and returns the altitude where the chute opened. The idea is to open the chute as late as possible -- but not too late. """ v: float = 0 # Terminal velocity. a: float = 0 # Acceleration. initial_altitude = int(9001 * random() + 1000) v1 = get_terminal_velocity() # Actual terminal velocity is +/-5% of v1. v = v1 * uniform(0.95, 1.05) a2 = get_acceleration() # Actual acceleration is +/-5% of a2. a = a2 * uniform(0.95, 1.05) print( "\n" f" ALTITUDE = {initial_altitude} FT\n" f" TERM. VELOCITY = {v1:.2f} FT/SEC +/-5%\n" f" ACCELERATION = {a2:.2f} FT/SEC/SEC +/-5%\n" "SET THE TIMER FOR YOUR FREEFALL." ) t_freefall = get_freefall_time() print( "HERE WE GO.\n\n" "TIME (SEC)\tDIST TO FALL (FT)\n" "==========\t=================" ) terminal_velocity_reached = False is_splat = False for i in range(9): # Divide time for freefall into 8 intervals. t = i * (t_freefall / 8) # From the first equation of motion, v = v_0 + a * delta_t, with # initial velocity v_0 = 0, we can get the time when terminal velocity # is reached: delta_t = v / a. if t > v / a: if not terminal_velocity_reached: print(f"TERMINAL VELOCITY REACHED AT T PLUS {v / a:.2f} SECONDS.") terminal_velocity_reached = True # After having reached terminal velocity, the displacement is # composed of two parts: # 1. Displacement up to reaching terminal velocity: # From the third equation of motion, v^2 = v_0^2 + 2 * a * d, # with v_0 = 0, we can get the displacement using # d1 = v^2 / (2 * a). # 2. Displacement beyond having reached terminal velocity: # here, the displacement is just a function of the terminal # velocity and the time passed after having reached terminal # velocity: d2 = v * (t - t_reached_term_vel) d1 = (v**2) / (2 * a) d2 = v * (t - (v / a)) altitude = initial_altitude - (d1 + d2) if altitude <= 0: # Time taken for an object to fall to the ground given # an initial altitude is composed of two parts after having # reached terminal velocity: # 1. time up to reaching terminal velocity: t1 = v / a # 2. time beyond having reached terminal velocity: # here, the altitude that remains after having reached # terminal velocity can just be divided by the constant # terminal velocity to get the time it takes to reach the # ground: t2 = altitude_remaining / v t1 = v / a t2 = (initial_altitude - d1) / v print_splat(t1 + t2) is_splat = True break else: # 1. Displacement before reaching terminal velocity: # From the second equation of motion, # d = v_0 * t + 0.5 * a * t^2, with v_0 = 0, we can get # the displacement using d1 = a / 2 * t^2 d1 = (a / 2) * (t**2) altitude = initial_altitude - d1 if altitude <= 0: # Time taken for an object to fall to the ground given that # it never reaches terminal velocity can be calculated by # using the second equation of motion: # d = v_0 * t + 0.5 * a * t^2, with v_0 = 0, which # when solved for t becomes # t1 = sqrt(2 * d / a). t1 = sqrt(2 * initial_altitude / a) print_splat(t1) is_splat = True break print(f"{t:.2f}\t\t{altitude:.1f}") if not is_splat: print("CHUTE OPEN") return altitude def pick_random_celestial_body() -> Tuple[str, float]: """Pick a random planet, the moon, or the sun with associated gravity.""" return choice( [ ("MERCURY", 12.2), ("VENUS", 28.3), ("EARTH", 32.16), ("THE MOON", 5.15), ("MARS", 12.5), ("JUPITER", 85.2), ("SATURN", 37.6), ("URANUS", 33.8), ("NEPTUNE", 39.6), ("THE SUN", 896.0), ] ) def jump_stats(previous_jumps, chute_altitude) -> Tuple[int, int]: """Compare altitude when chute opened with previous successful jumps. Return the number of previous jumps and the number of times the current jump is better. """ n_previous_jumps = len(previous_jumps) n_better = sum(1 for pj in previous_jumps if chute_altitude < pj) return n_previous_jumps, n_better def print_splat(time_on_impact) -> None: """Parachute opened too late!""" print(f"{time_on_impact:.2f}\t\tSPLAT") print( choice( [ "REQUIESCAT IN PACE.", "MAY THE ANGEL OF HEAVEN LEAD YOU INTO PARADISE.", "REST IN PEACE.", "SON-OF-A-GUN.", "#$%&&%!$", "A KICK IN THE PANTS IS A BOOST IF YOU'RE HEADED RIGHT.", "HMMM. SHOULD HAVE PICKED A SHORTER TIME.", "MUTTER. MUTTER. MUTTER.", "PUSHING UP DAISIES.", "EASY COME, EASY GO.", ] ) ) def print_results(n_previous_jumps, n_better) -> None: """Compare current jump to previous successful jumps.""" k = n_previous_jumps k1 = n_better n_jumps = k + 1 if n_jumps <= 3: order = ["1ST", "2ND", "3RD"] nth = order[n_jumps - 1] print(f"AMAZING!!! NOT BAD FOR YOUR {nth} SUCCESSFUL JUMP!!!") elif k - k1 <= 0.1 * k: print( f"WOW! THAT'S SOME JUMPING. OF THE {k} SUCCESSFUL JUMPS\n" f"BEFORE YOURS, ONLY {k - k1} OPENED THEIR CHUTES LOWER THAN\n" "YOU DID." ) elif k - k1 <= 0.25 * k: print( f"PRETTY GOOD! {k} SUCCESSFUL JUMPS PRECEDED YOURS AND ONLY\n" f"{k - k1} OF THEM GOT LOWER THAN YOU DID BEFORE THEIR CHUTES\n" "OPENED." ) elif k - k1 <= 0.5 * k: print( f"NOT BAD. THERE HAVE BEEN {k} SUCCESSFUL JUMPS BEFORE YOURS.\n" f"YOU WERE BEATEN OUT BY {k - k1} OF THEM." ) elif k - k1 <= 0.75 * k: print( f"CONSERVATIVE, AREN'T YOU? YOU RANKED ONLY {k - k1} IN THE\n" f"{k} SUCCESSFUL JUMPS BEFORE YOURS." ) elif k - k1 <= 0.9 * k: print( "HUMPH! DON'T YOU HAVE ANY SPORTING BLOOD? THERE WERE\n" f"{k} SUCCESSFUL JUMPS BEFORE YOURS AND YOU CAME IN {k1} JUMPS\n" "BETTER THAN THE WORST. SHAPE UP!!!" ) else: print( f"HEY! YOU PULLED THE RIP CORD MUCH TOO SOON. {k} SUCCESSFUL\n" f"JUMPS BEFORE YOURS AND YOU CAME IN NUMBER {k - k1}!" " GET WITH IT!" ) def print_centered(msg: str) -> None: """Print centered text.""" spaces = " " * ((PAGE_WIDTH - len(msg)) // 2) print(spaces + msg) def print_header() -> None: print_centered("SPLAT") print_centered("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") print( "\n\n\n" "WELCOME TO 'SPLAT' -- THE GAME THAT SIMULATES A PARACHUTE\n" "JUMP. TRY TO OPEN YOUR CHUTE AT THE LAST POSSIBLE\n" "MOMENT WITHOUT GOING SPLAT.\n\n" ) def main() -> None: print_header() successful_jumps: List[float] = [] while True: chute_altitude = jump() if chute_altitude > 0: # We want the statistics on previous jumps (i.e. not including the # current jump.) n_previous_jumps, n_better = jump_stats(successful_jumps, chute_altitude) successful_jumps.append(chute_altitude) print_results(n_previous_jumps, n_better) else: # Splat! print("I'LL GIVE YOU ANOTHER CHANCE.") 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 __name__ == "__main__": main() ================================================ FILE: 81_Splat/ruby/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Ruby](https://www.ruby-lang.org/en/) ================================================ FILE: 81_Splat/rust/Cargo.toml ================================================ [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" ================================================ FILE: 81_Splat/rust/src/celestial_body.rs ================================================ use rand::{prelude::SliceRandom, Rng}; #[derive(Debug)] pub enum CelestialBody { MERCURY, VENUS, EARTH, MOON, MARS, JUPITER, SATURN, URANUS, NEPTUNE, SUN, } impl CelestialBody { pub fn get_acceleration(&self) -> f32 { use CelestialBody::*; match self { MERCURY => 12.2, VENUS => 28.3, EARTH => 32.16, MOON => 5.12, MARS => 12.5, JUPITER => 85.2, SATURN => 37.6, URANUS => 33.8, NEPTUNE => 39.6, SUN => 896., } } pub fn print_acceleration_message(&self) { let messages: [&str; 3] = ["Fine,", "All right,", "Then"]; let m = messages.choose(&mut rand::thread_rng()).unwrap(); println!( "{} YOU'RE ON {:?}. ACCELERATION = {} FT/SEC/SEC.", m.to_uppercase(), self, self.get_acceleration() ); } } pub fn random_celestial_body() -> Option { use CelestialBody::*; match rand::thread_rng().gen_range(0..10) { 0 => Some(MERCURY), 1 => Some(VENUS), 2 => Some(EARTH), 3 => Some(MOON), 4 => Some(MARS), 5 => Some(JUPITER), 6 => Some(SATURN), 7 => Some(URANUS), 8 => Some(NEPTUNE), 9 => Some(SUN), _ => None, } } ================================================ FILE: 81_Splat/rust/src/game.rs ================================================ use crate::utility; pub struct Game { altitude: f32, terminal_velocity: f32, acceleration: f32, interval: f32, } impl Game { pub fn new() -> Game { let altitude = utility::get_altitude(); let terminal_velocity = utility::get_terminal_velocity( "SELECT YOUR OWN TERMINAL VELOCITY", "WHAT TERMINAL VELOCITY (MI/HR)?", ); let acceleration = utility::get_acceleration( "WANT TO SELECT ACCELERATION DUE TO GRAVITY", "WHAT ACCELERATION (FT/SEC/SEC)?", ); println!(""); println!(" ALTITUDE = {} FT", altitude); println!(" TERM. VELOCITY = {} FT/SEC +-5%", terminal_velocity); println!(" ACCELERATION = {} FT/SEC/SEC +-5%", acceleration); let seconds = utility::prompt_numeric("\nSET THE TIMER FOR YOUR FREEFALL.\nHOW MANY SECONDS?"); println!("\nHERE WE GO.\n"); println!("TIME (SEC)\tDIST TO FALL (FT)"); println!("==========\t================="); Game { altitude, terminal_velocity, acceleration, interval: seconds / 8., } } pub fn tick(&mut self) -> f32 { let mut splat = false; let mut terminal_velocity_reached = false; let (v, a) = (self.terminal_velocity, self.acceleration); let terminal_velocity_time = v / a; let initial_altitude = self.altitude; for i in 0..=8 { let dt = i as f32 * self.interval; if dt >= terminal_velocity_time { if !terminal_velocity_reached { println!( "TERMINAL VELOCITY REACHED AT T PLUS {} SECONDS.", terminal_velocity_time ); terminal_velocity_reached = true; } let d1 = v.powi(2) / (2. * a); let d2 = v * (dt - (terminal_velocity_time)); self.altitude = initial_altitude - (d1 + d2); if self.altitude <= 0. { let t = (initial_altitude - d1) / v; utility::print_splat(t + terminal_velocity_time); splat = true; break; } } else { let d1 = (a * 0.5) * (dt.powi(2)); self.altitude = initial_altitude - d1; if self.altitude <= 0. { let t = (2. * initial_altitude / a).sqrt(); utility::print_splat(t); splat = true; break; } } println!("{}\t\t{}", dt, self.altitude); std::thread::sleep(std::time::Duration::from_secs(1)); } let mut a = -1.; if !splat { println!("\nCHUTE OPEN\n"); a = self.altitude; } a } } ================================================ FILE: 81_Splat/rust/src/main.rs ================================================ use crate::{game::Game, stats::Stats}; mod celestial_body; mod game; mod stats; mod utility; fn main() { println!("\n\n\n SPLAT"); println!(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n"); println!("WELCOME TO 'SPLAT' -- THE GAME THAT SIMULATES"); println!("A PARACHUTE JUMP. TRY OPEN YOUR CHUTE AT THE"); println!("LAST POSSIBLE MOMENT WITHOUT GOING SPLAT.\n"); let mut stats = Stats::new(); loop { let mut game = Game::new(); let latest_altitude = game.tick(); if latest_altitude > 0. { if let Some(s) = &mut stats { s.add_altitude(latest_altitude); } } use utility::prompt_bool; if !prompt_bool("DO YOU WANT TO PLAY AGAIN?", true) { if !prompt_bool("PLEASE?", false) { if !prompt_bool("YES OR NO PLEASE?", false) { println!("SSSSSSSSSS."); break; } } } } } ================================================ FILE: 81_Splat/rust/src/stats.rs ================================================ use std::{ fs::{self, File}, io::Write, }; use crate::utility; pub struct Stats { altitudes: Vec, } impl Stats { pub fn new() -> Option { if utility::prompt_bool("WOULD YOU LIKE TO LOAD PREVIOUS GAME DATA?", false) { let path = "src/stats.txt"; let mut altitudes = Vec::new(); if let Ok(stats) = fs::read_to_string(path) { if stats.is_empty() { return Some(Stats { altitudes: Vec::new(), }); } let stats: Vec<&str> = stats.trim().split(",").collect(); for s in stats { if s.is_empty() { continue; } let s = s.parse::().expect("Corrupt stats file!"); altitudes.push(s); } return Some(Stats { altitudes }); } else { println!("PREVIOUS GAME DATA NOT FOUND!"); if !utility::prompt_bool("WOULD YOU LIKE TO CREATE ONE?", false) { return None; } else { let mut file = File::create(path).expect("Invalid file path!"); file.write_all("".as_bytes()) .expect("Could not create file!"); return Some(Stats { altitudes: Vec::new(), }); } } } println!("\nRESULTS OF THIS SESSION WILL NOT BE SAVED."); None } pub fn add_altitude(&mut self, a: f32) { let all_jumps = self.altitudes.len() + 1; let mut placement = all_jumps; for (i, altitude) in self.altitudes.iter().enumerate() { if a <= *altitude { placement = i + 1; break; } } utility::print_win(all_jumps, placement); self.altitudes.push(a); self.altitudes.sort_by(|a, b| a.partial_cmp(b).unwrap()); self.write(); } fn write(&self) { let mut file = File::create("src/stats.txt").expect("Error loading stats data!"); let mut altitudes = String::new(); for a in &self.altitudes { altitudes.push_str(format!("{},", a).as_str()); } write!(&mut file, "{}", altitudes.trim()).expect("ERROR WRITING Stats FILE!"); } } ================================================ FILE: 81_Splat/rust/src/utility.rs ================================================ use crate::celestial_body; use rand::Rng; use std::io; const DEATH_MESSAGES: [&str; 10] = [ "REQUIESCAT IN PACE.", "MAY THE ANGEL OF HEAVEN LEAD YOU INTO PARADISE.", "REST IN PEACE.", "SON-OF-A-GUN.", "#$%&&%!$", "A KICK IN THE PANTS IS A BOOST IF YOU'RE HEADED RIGHT.", "HMMM. SHOULD HAVE PICKED A SHORTER TIME.", "MUTTER. MUTTER. MUTTER.", "PUSHING UP DAISIES.", "EASY COME, EASY GO.", ]; pub fn read_line() -> String { let mut input = String::new(); io::stdin() .read_line(&mut input) .expect("Failed to read line."); input.trim().to_uppercase() } pub fn prompt_bool(msg: &str, template: bool) -> bool { if template { println!("{} (YES OR NO)?", msg); } else { println!("{}", msg); } loop { let response = read_line(); match response.as_str() { "YES" => return true, "NO" => return false, _ => println!("PLEASE ENTER YES OR NO."), } } } pub fn prompt_numeric(msg: &str) -> f32 { println!("{}", msg); loop { let response = read_line(); if let Some(_) = response.chars().find(|c| !c.is_numeric()) { println!("PLEASE ENTER A NUMBER."); } else { return response.parse::().unwrap(); } } } pub fn get_altitude() -> f32 { 9001. * rand::random::() + 1000. } pub fn get_terminal_velocity(bool_msg: &str, num_msg: &str) -> f32 { let mut _num = 0.0; if prompt_bool(bool_msg, true) { _num = prompt_numeric(num_msg); } else { _num = get_random_float(0., 1000.); println!("OK. TERMINAL VELOCTY = {} MI/HR", _num); } (_num * ((5280 / 3600) as f32)) * get_random_float(0.95, 1.05) } pub fn get_acceleration(bool_msg: &str, num_msg: &str) -> f32 { let mut _num = 0.0; if prompt_bool(bool_msg, true) { _num = prompt_numeric(num_msg); } else { let b = celestial_body::random_celestial_body().expect("Fatal Error: Invalid Celestial Body!"); _num = b.get_acceleration(); b.print_acceleration_message(); } _num * get_random_float(0.95, 1.05) } fn get_random_float(min: f32, max: f32) -> f32 { rand::thread_rng().gen_range(min..=max) } pub fn print_splat(t: f32) { print_random(format!("{}\t\tSPLAT!", t).as_str(), &DEATH_MESSAGES); print!("I'LL GIVE YOU ANOTHER CHANCE.\n"); } fn print_random(msg: &str, choices: &[&str]) { use rand::seq::SliceRandom; use rand::thread_rng; let mut rng = thread_rng(); println!("{}", msg); println!("\n{}\n", choices.choose(&mut rng).unwrap()); } pub fn print_win(total: usize, placement: usize) { if total <= 3 { let order; match total { 1 => order = "1ST", 2 => order = "2ND", 3 => order = "3RD", _ => order = "#INVALID#", } println!("AMAZING!!! NOT BAD FOR YOUR {} SUCCESSFUL JUMP!!!", order); return; } let (total, placement) = (total as f32, placement as f32); let betters = placement - 1.; let p: f32 = (total - betters) / total; println!("{}", p); println!( "placement: {}, total jumps: {}, percent is: {}", placement, total, p ); if p < 0.1 { println!( "HEY! YOU PULLED THE RIP CORD MUCH TOO SOON. {} SUCCESSFUL\nJUMPS BEFORE YOURS AND YOU CAME IN NUMBER {}! GET WITH IT!", total, placement ); } else if p < 0.25 { println!( "HUMPH! DON'T YOU HAVE ANY SPORTING BLOOD? THERE WERE\n{} SUCCESSFUL JUMPS BEFORE YOURS AND YOU CAME IN {} JUMPS\nBETTER THAN THE WORST. SHAPE UP!!!", total, placement ); } else if p < 0.5 { println!( "CONSERVATIVE, AREN'T YOU? YOU RANKED ONLY {} IN THE\n{} SUCCESSFUL JUMPS BEFORE YOURS.", placement, total ); } else if p < 0.75 { println!( "NOT BAD. THERE HAVE BEEN {} SUCCESSFUL JUMPS BEFORE YOURS.\nYOU WERE BEATEN OUT BY {} OF THEM.", total, betters ); } else if p < 0.9 { println!( "PRETTY GOOD! {} SUCCESSFUL JUMPS PRECEDED YOURS AND ONLY\n{} OF THEM GOT LOWER THAN YOU DID BEFORE THEIR CHUTES\nOPENED.", total, betters ) } else { println!( "WOW! THAT'S SOME JUMPING. OF THE {} SUCCESSFUL JUMPS\nBEFORE YOURS, ONLY {} OPENED THEIR CHUTES LOWER THAN\nYOU DID.", total, betters ) } } ================================================ FILE: 81_Splat/splat.bas ================================================ 10 PRINT TAB(33);"SPLAT" 20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 40 PRINT:PRINT:PRINT 50 DIM A(42) 95 PRINT "WELCOME TO 'SPLAT' -- THE GAME THAT SIMULATES A PARACHUTE" 96 PRINT "JUMP. TRY TO OPEN YOUR CHUTE AT THE LAST POSSIBLE" 97 PRINT "MOMENT WITHOUT GOING SPLAT." 118 PRINT:PRINT:D1=0:V=0:A=0:N=0:M=0:D1=INT(9001*RND(1)+1000) 119 PRINT "SELECT YOUR OWN TERMINAL VELOCITY (YES OR NO)";:INPUT A1$ 120 IF A1$="NO" THEN 128 121 IF A1$<>"YES" THEN PRINT "YES OR NO";:INPUT A1$:GOTO 120 123 PRINT "WHAT TERMINAL VELOCITY (MI/HR)";:INPUT V1 125 V1=V1*(5280/3600):V=V1+((V1*RND(1))/20)-((V1*RND(1))/20):GOTO 135 128 V1=INT(1000*RND(1)) 130 PRINT "OK. TERMINAL VELOCITY ="V1"MI/HR" 131 V1=V1*(5280/3600):V=V1+((V1*RND(1))/20)-((V1*RND(1))/20) 135 PRINT "WANT TO SELECT ACCELERATION DUE TO GRAVITY (YES OR NO)"; 136 INPUT B1$ 140 IF B1$="NO" THEN 150 141 IF B1$<>"YES" THEN PRINT "YES OR NO";:INPUT B1$:GOTO 140 143 PRINT "WHAT ACCELERATION (FT/SEC/SEC)";:INPUT A2 145 A=A2+((A2*RND(1))/20)-((A2*RND(1))/20):GOTO 205 150 ON INT(1+(10*RND(1)))GOTO 151,152,153,154,155,156,157,158,159,160 151 PRINT"FINE. YOU'RE ON MERCURY. ACCELERATION=12.2 FT/SEC/SEC.":GOTO 161 152 PRINT"ALL RIGHT. YOU'RE ON VENUS. ACCELERATION=28.3 FT/SEC/SEC.":GOTO 162 153 PRINT "THEN YOU'RE ON EARTH. ACCELERATION=32.16 FT/SEC/SEC.":GOTO 163 154 PRINT"FINE. YOU'RE ON THE MOON. ACCELERATION=5.15 FT/SEC/SEC.":GOTO 164 155 PRINT"ALL RIGHT. YOU'RE ON MARS. ACCELERATION=12.5 FT/SEC/SEC.":GOTO 165 156 PRINT"THEN YOU'RE ON JUPITER. ACCELERATION=85.2 FT/SEC/SEC.":GOTO 166 157 PRINT"FINE. YOU'RE ON SATURN. ACCELERATION=37.6 FT/SEC/SEC.":GOTO 167 158 PRINT"ALL RIGHT. YOU'RE ON URANUS. ACCELERATION=33.8 FT/SEC/SEC.":GOTO 168 159 PRINT"THEN YOU'RE ON NEPTUNE. ACCELERATION=39.6 FT/SEC/SEC.":GOTO 169 160 PRINT"FINE. YOU'RE ON THE SUN. ACCELERATION=896 FT/SEC/SEC.":GOTO 170 161 A2=12.2:GOTO 145 162 A2=28.3:GOTO 145 163 A2=32.16:GOTO 145 164 A2=5.15:GOTO 145 165 A2=12.5:GOTO 145 166 A2=85.2:GOTO 145 167 A2=37.6:GOTO 145 168 A2=33.8 :GOTO 145 169 A2=39.6:GOTO 145 170 A2=896:GOTO 145 205 PRINT 206 PRINT " ALTITUDE ="D1"FT" 207 PRINT " TERM. VELOCITY ="V1"FT/SEC +/-5%" 208 PRINT " ACCELERATION ="A2"FT/SEC/SEC +/-5%" 210 PRINT "SET THE TIMER FOR YOUR FREEFALL." 211 PRINT "HOW MANY SECONDS";:INPUT T 215 PRINT "HERE WE GO." 217 PRINT 218 PRINT "TIME (SEC)","DIST TO FALL (FT)" 219 PRINT "==========","=================" 300 FOR I=0 TO T STEP (T/8) 310 IF I>V/A THEN 400 320 D=D1-((A/2)*I^2) 330 IF D<=0 THEN 1000 340 PRINT I,D 350 NEXT I 360 GOTO 500 400 PRINT "TERMINAL VELOCITY REACHED AT T PLUS"V/A"SECONDS." 405 FOR I=I TO T STEP (T/8) 410 D=D1-((V^2/(2*A))+(V*(I-(V/A)))) 420 IF D<=0 THEN 1010 430 PRINT I,D 440 NEXT I 500 PRINT "CHUTE OPEN" 510 K=0:K1=0 550 FOR J=0 TO 42 555 IF A(J)=0 THEN 620 560 K=K+1 570 IF D>=A(J) THEN 600 580 K1=K1+1 600 NEXT J 610 GOTO 540 620 A(J)=D 630 IF J>2 THEN 650 635 PRINT "AMAZING!!! NOT BAD FOR YOUR "; 636 IF J=0 THEN PRINT "1ST "; 637 IF J=1 THEN PRINT "2ND "; 638 IF J=2 THEN PRINT "3RD "; 639 PRINT "SUCCESSFUL JUMP!!!":GOTO 2000 650 IF K-K1<=.1*K THEN 700 660 IF K-K1<=.25*K THEN 710 670 IF K-K1<=.5*K THEN 720 680 IF K-K1<=.75*K THEN 730 690 IF K-K1<=.9*K THEN 740 695 GOTO 750 700 PRINT "WOW! THAT'S SOME JUMPING. OF THE"K"SUCCESSFUL JUMPS" 701 PRINT "BEFORE YOURS, ONLY"K-K1"OPENED THEIR CHUTES LOWER THAN" 702 PRINT "YOU DID." 703 GOTO 2000 710 PRINT "PRETTY GOOD! " K"SUCCESSFUL JUMPS PRECEDED YOURS AND ONLY" 711 PRINT K-K1" OF THEM GOT LOWER THAN YOU DID BEFORE THEIR CHUTES" 712 PRINT "OPENED." :GOTO 2000 720 PRINT "NOT BAD. THERE HAVE BEEN"K"SUCCESSFUL JUMPS BEFORE YOURS." 721 PRINT"YOU WERE BEATEN OUT BY"K-K1"OF THEM.":GOTO 2000 730 PRINT "CONSERVATIVE, AREN'T YOU? YOU RANKED ONLY"K-K1"IN THE" 731 PRINT K"SUCCESSFUL JUMPS BEFORE YOURS.":GOTO 2000 740 PRINT "HUMPH! DON'T YOU HAVE ANY SPORTING BLOOD? THERE WERE" 741 PRINT K"SUCCESSFUL JUMPS BEFORE YOURS AND YOU CAME IN"K1"JUMPS" 742 PRINT "BETTER THAN THE WORST. SHAPE UP!!!":GOTO 2000 750 PRINT "HEY! YOU PULLED THE RIP CORD MUCH TOO SOON. "K"SUCCESSFUL" 751 PRINT "JUMPS BEFORE YOURS AND YOU CAME IN NUMBER"K-K1"! GET WITH IT!" 752 GOTO 2000 800 PRINT "REQUIESCAT IN PACE.":GOTO 1950 801 PRINT "MAY THE ANGEL OF HEAVEN LEAD YOU INTO PARADISE.":GOTO 1950 802 PRINT "REST IN PEACE.":GOTO 1950 803 PRINT "SON-OF-A-GUN.":GOTO 1950 804 PRINT "#$%&&%!$":GOTO 1950 805 PRINT "A KICK IN THE PANTS IS A BOOST IF YOU'RE HEADED RIGHT.":GOTO 1950 806 PRINT "HMMM. SHOULD HAVE PICKED A SHORTER TIME.":GOTO 1950 807 PRINT "MUTTER. MUTTER. MUTTER.":GOTO 1950 808 PRINT "PUSHING UP DAISIES.":GOTO 1950 809 PRINT "EASY COME, EASY GO.":GOTO 1950 1000 PRINT SQR(2*D1/A),"SPLAT" 1005 ON INT(1+(10*RND(1)))GOTO 800,801,802,803,804,805,806,807,808,809 1010 PRINT (V/A)+((D1-(V^2/(2*A)))/V),"SPLAT" 1020 GOTO 1005 1950 PRINT "I'LL GIVE YOU ANOTHER CHANCE.":GOTO 2000 2000 PRINT "DO YOU WANT TO PLAY AGAIN";:INPUT Z$ 2001 IF Z$="YES" THEN 118 2002 IF Z$="NO" THEN 2005 2003 PRINT "YES OR NO":GOTO 2000 2005 PRINT "PLEASE";:INPUT Z$:IF Z$="YES" THEN 118 2006 IF Z$<>"NO" THEN PRINT "YES OR NO ";:GOTO 2005 2007 PRINT "SSSSSSSSSS.":PRINT:GOTO 2046 2046 END ================================================ FILE: 81_Splat/vbnet/README.md ================================================ Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET) ================================================ FILE: 81_Splat/vbnet/Splat.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30114.105 MinimumVisualStudioVersion = 10.0.40219.1 Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Splat", "Splat.vbproj", "{62FD7167-97B2-4EA7-8BC8-CBC5765DF721}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {62FD7167-97B2-4EA7-8BC8-CBC5765DF721}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {62FD7167-97B2-4EA7-8BC8-CBC5765DF721}.Debug|Any CPU.Build.0 = Debug|Any CPU {62FD7167-97B2-4EA7-8BC8-CBC5765DF721}.Release|Any CPU.ActiveCfg = Release|Any CPU {62FD7167-97B2-4EA7-8BC8-CBC5765DF721}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal ================================================ FILE: 81_Splat/vbnet/Splat.vbproj ================================================ Exe Splat net6.0 16.9 ================================================ FILE: 82_Stars/README.md ================================================ ### Stars In this game, the computer selects a random number from 1 to 100 (or any value you set). You try to guess the number and the computer gives you clues to tell you how close you’re getting. One star (\*) means you’re far away from the number; seven stars (\*\*\*\*\*\*\*) means you’re really close. You get 7 guesses. On the surface this game is similar to GUESS; however, the guessing strategy is quite different. See if you can come up with one or more approaches to finding the mystery number. Bob Albrecht of People’s Computer Company created this game. --- As published in Basic Computer Games (1978): - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=153) - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=166) Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html #### Porting Notes (please note any difficulties or challenges in porting here) ================================================ FILE: 82_Stars/csharp/Game.cs ================================================ using System; using Games.Common.IO; using Games.Common.Randomness; using Stars.Resources; namespace Stars; internal class Game { private readonly TextIO _io; private readonly IRandom _random; private readonly int _maxNumber; private readonly int _maxGuessCount; public Game(TextIO io, IRandom random, int maxNumber, int maxGuessCount) { _io = io; _random = random; _maxNumber = maxNumber; _maxGuessCount = maxGuessCount; } internal void Play(Func playAgain) { DisplayIntroduction(); do { Play(); } while (playAgain.Invoke()); } private void DisplayIntroduction() { _io.Write(Resource.Streams.Title); if (_io.ReadString("Do you want instructions").Equals("N", StringComparison.InvariantCultureIgnoreCase)) { return; } _io.WriteLine(Resource.Formats.Instructions, _maxNumber, _maxGuessCount); } private void Play() { _io.WriteLine(); _io.WriteLine(); var target = _random.Next(_maxNumber) + 1; _io.WriteLine("Ok, I am thinking of a number. Start guessing."); AcceptGuesses(target); } private void AcceptGuesses(int target) { for (int guessCount = 1; guessCount <= _maxGuessCount; guessCount++) { _io.WriteLine(); var guess = _io.ReadNumber("Your guess"); if (guess == target) { DisplayWin(guessCount); return; } DisplayStars(target, guess); } DisplayLoss(target); } private void DisplayStars(int target, float guess) { var stars = Math.Abs(guess - target) switch { >= 64 => "*", >= 32 => "**", >= 16 => "***", >= 8 => "****", >= 4 => "*****", >= 2 => "******", _ => "*******" }; _io.WriteLine(stars); } private void DisplayWin(int guessCount) { _io.WriteLine(); _io.WriteLine(new string('*', 79)); _io.WriteLine(); _io.WriteLine($"You got it in {guessCount} guesses!!! Let's play again..."); } private void DisplayLoss(int target) { _io.WriteLine(); _io.WriteLine($"Sorry, that's {_maxGuessCount} guesses. The number was {target}."); } } ================================================ FILE: 82_Stars/csharp/Program.cs ================================================ using Games.Common.IO; using Games.Common.Randomness; using Stars; var game = new Game(new ConsoleIO(), new RandomNumberGenerator(), maxNumber: 100, maxGuessCount: 7); game.Play(() => true); ================================================ FILE: 82_Stars/csharp/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/) ================================================ FILE: 82_Stars/csharp/Resources/Instructions.txt ================================================ I am thinking of a number between 1 and {0}. Try to guess my number. After you guess, I will type one or more stars (*). The more stars I type, the closer you are to my number. One star (*) means far away, seven stars (*******) means really close! You get {1} guesses. ================================================ FILE: 82_Stars/csharp/Resources/Resource.cs ================================================ using System.IO; using System.Reflection; using System.Runtime.CompilerServices; namespace Stars.Resources; internal static class Resource { internal static class Streams { public static Stream Title => GetStream(); } internal static class Formats { public static string Instructions => GetString(); } 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($"Stars.Resources.{name}.txt"); } ================================================ FILE: 82_Stars/csharp/Resources/Title.txt ================================================ Stars Creative Computing Morristown, New Jersey ================================================ FILE: 82_Stars/csharp/Stars.csproj ================================================ Exe net6.0 ================================================ FILE: 82_Stars/csharp/Stars.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.0.32014.148 MinimumVisualStudioVersion = 15.0.26124.0 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Stars", "Stars.csproj", "{F03C19DA-0F5D-45F4-B85E-E3ABCA197D4B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {F03C19DA-0F5D-45F4-B85E-E3ABCA197D4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F03C19DA-0F5D-45F4-B85E-E3ABCA197D4B}.Debug|Any CPU.Build.0 = Debug|Any CPU {F03C19DA-0F5D-45F4-B85E-E3ABCA197D4B}.Release|Any CPU.ActiveCfg = Release|Any CPU {F03C19DA-0F5D-45F4-B85E-E3ABCA197D4B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {0752CA22-85F0-43AE-8B79-7A611531CAF7} EndGlobalSection EndGlobal ================================================ FILE: 82_Stars/java/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Oracle Java](https://openjdk.java.net/) ================================================ FILE: 82_Stars/java/src/Stars.java ================================================ import java.util.Arrays; import java.util.Scanner; /** * Game of Stars * * Based on the Basic game of Stars here * https://github.com/coding-horror/basic-computer-games/blob/main/82%20Stars/stars.bas * * Note: The idea was to create a version of the 1970's Basic game in Java, without introducing * new features - no additional text, error checking, etc has been added. */ public class Stars { public static final int HIGH_NUMBER_RANGE = 100; public static final int MAX_GUESSES = 7; private enum GAME_STATE { STARTING, INSTRUCTIONS, START_GAME, GUESSING, WON, LOST, GAME_OVER } // Used for keyboard input private final Scanner kbScanner; // Current game state private GAME_STATE gameState; // Players guess count; private int playerTotalGuesses; // Players current guess private int playerCurrentGuess; // Computers random number private int computersNumber; public Stars() { gameState = GAME_STATE.STARTING; // Initialise kb scanner kbScanner = new Scanner(System.in); } /** * Main game loop * */ public void play() { do { switch (gameState) { // Show an introduction the first time the game is played. case STARTING: intro(); gameState = GAME_STATE.INSTRUCTIONS; break; // Ask if instructions are needed and display if yes case INSTRUCTIONS: if(yesEntered(displayTextAndGetInput("DO YOU WANT INSTRUCTIONS? "))) { instructions(); } gameState = GAME_STATE.START_GAME; break; // Generate computers number for player to guess, etc. case START_GAME: init(); System.out.println("OK, I AM THINKING OF A NUMBER, START GUESSING."); gameState = GAME_STATE.GUESSING; break; // Player guesses the number until they get it or run out of guesses case GUESSING: playerCurrentGuess = playerGuess(); // Check if the player guessed the number if(playerCurrentGuess == computersNumber) { gameState = GAME_STATE.WON; } else { // incorrect guess showStars(); playerTotalGuesses++; // Ran out of guesses? if (playerTotalGuesses > MAX_GUESSES) { gameState = GAME_STATE.LOST; } } break; // Won game. case WON: System.out.println(stars(79)); System.out.println("YOU GOT IT IN " + playerTotalGuesses + " GUESSES!!! LET'S PLAY AGAIN..."); gameState = GAME_STATE.START_GAME; break; // Lost game by running out of guesses case LOST: System.out.println("SORRY, THAT'S " + MAX_GUESSES + " GUESSES. THE NUMBER WAS " + computersNumber); gameState = GAME_STATE.START_GAME; break; } // Endless loop since the original code did not allow the player to exit } while (gameState != GAME_STATE.GAME_OVER); } /** * Shows how close a players guess is to the computers number by * showing a series of stars - the more stars the closer to the * number. * */ private void showStars() { int d = Math.abs(playerCurrentGuess - computersNumber); int starsToShow; if(d >=64) { starsToShow = 1; } else if(d >=32) { starsToShow = 2; } else if (d >= 16) { starsToShow = 3; } else if (d >=8) { starsToShow = 4; } else if( d>= 4) { starsToShow = 5; } else if(d>= 2) { starsToShow = 6; } else { starsToShow = 7; } System.out.println(stars(starsToShow)); } /** * Show a number of stars (asterisks) * @param number the number of stars needed * @return the string encoded with the number of required stars */ private String stars(int number) { char[] stars = new char[number]; Arrays.fill(stars, '*'); return new String(stars); } /** * Initialise variables before each new game * */ private void init() { playerTotalGuesses = 1; computersNumber = randomNumber(); } public void instructions() { System.out.println("I AM THINKING OF A WHOLE NUMBER FROM 1 TO " + HIGH_NUMBER_RANGE); System.out.println("TRY TO GUESS MY NUMBER. AFTER YOU GUESS, I"); System.out.println("WILL TYPE ONE OR MORE STARS (*). THE MORE"); System.out.println("STARS I TYPE, THE CLOSER YOU ARE TO MY NUMBER."); System.out.println("ONE STAR (*) MEANS FAR AWAY, SEVEN STARS (*******)"); System.out.println("MEANS REALLY CLOSE! YOU GET " + MAX_GUESSES + " GUESSES."); } public void intro() { System.out.println("STARS"); System.out.println("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println(); } /** * Get players guess from kb * * @return players guess as an int */ private int playerGuess() { return Integer.parseInt((displayTextAndGetInput("YOUR GUESS? "))); } /** * Checks whether player entered Y or YES to a question. * * @param text player string from kb * @return true of Y or YES was entered, otherwise false */ private boolean yesEntered(String text) { return stringIsAnyValue(text, "Y", "YES"); } /** * Check whether a string equals one of a variable number of values * Useful to check for Y or YES for example * Comparison is case insensitive. * * @param text source string * @param values a range of values to compare against the source string * @return true if a comparison was found in one of the variable number of strings passed */ private boolean stringIsAnyValue(String text, String... values) { // Cycle through the variable number of values and test each for(String val:values) { if(text.equalsIgnoreCase(val)) { return true; } } // no matches return false; } /* * Print a message on the screen, then accept input from Keyboard. * * @param text message to be displayed on screen. * @return what was typed by the player. */ private String displayTextAndGetInput(String text) { System.out.print(text); return kbScanner.next(); } /** * Generate random number * * @return random number */ private int randomNumber() { return (int) (Math.random() * (HIGH_NUMBER_RANGE) + 1); } } ================================================ FILE: 82_Stars/java/src/StarsGame.java ================================================ import java.lang.reflect.AnnotatedType; public class StarsGame { public static void main(String[] args) { Stars stars = new Stars(); stars.play(); } } ================================================ FILE: 82_Stars/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 82_Stars/javascript/stars.html ================================================ STARS
    
    
    
    
    
    
    ================================================
    FILE: 82_Stars/javascript/stars.js
    ================================================
    // STARS
    //
    // Converted from BASIC to Javascript by Qursch
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    var guesses = 7;
    var limit = 100;
    
    // Main program
    async function main()
    {
        print(tab(33) + "STARS\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n\n\n");
    
        // Instructions
        print("DO YOU WANT INSTRUCTIONS? (Y/N)");
        var instructions = await input();
        if(instructions.toLowerCase()[0] == "y") {
            print(`I AM THINKING OF A WHOLE NUMBER FROM 1 TO ${limit}\n`);
            print("TRY TO GUESS MY NUMBER.  AFTER YOU GUESS, I\n");
            print("WILL TYPE ONE OR MORE STARS (*).  THE MORE\n");
            print("STARS I TYPE, THE CLOSER YOU ARE TO MY NUMBER.\n");
            print("ONE STAR (*) MEANS FAR AWAY, SEVEN STARS (*******)\n");
            print(`MEANS REALLY CLOSE!  YOU GET ${guesses} GUESSES.\n\n\n`);
        }
    
        // Game loop
        while (true) {
    
            var randomNum = Math.floor(Math.random() * limit) + 1;
            var loss = true;
    
            print("\nOK, I AM THINKING OF A NUMBER, START GUESSING.\n\n");
    
            for(var guessNum=1; guessNum <= guesses; guessNum++) {
    
                // Input guess
                print("YOUR GUESS");
                var guess = parseInt(await input());
    
                // Check if guess is correct
                if(guess == randomNum) {
                    loss = false;
                    print("\n\n" + "*".repeat(50) + "!!!\n");
                    print(`YOU GOT IT IN ${guessNum} GUESSES!!! LET'S PLAY AGAIN...\n`);
                    break;
                }
    
                // Output distance in stars
                var dist = Math.abs(guess - randomNum);
                if(isNaN(dist)) print("*");
                else if(dist >= 64) print("*");
                else if(dist >= 32) print("**");
                else if(dist >= 16) print("***");
                else if(dist >= 8) print("****");
                else if(dist >= 4) print("*****");
                else if(dist >= 2) print("******");
                else print("*******")
                print("\n\n")
            }
    
            if(loss) {
                print(`SORRY, THAT'S ${guesses} GUESSES. THE NUMBER WAS ${randomNum}\n`);
            }
        }
    }
    
    main();
    
    
    ================================================
    FILE: 82_Stars/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 82_Stars/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 82_Stars/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 82_Stars/perl/stars.pl
    ================================================
    #!/usr/bin/perl
    
    use v5.11; # for say and use strict
    use warnings;
    
    my $MAX_NUMBER = 100;
    my $MAX_GUESSES = 7;
    
    print<<__END_OF_INTRO;
                                      Stars
                   Creative Computing  Morristown, New Jersey
    
    
    
    __END_OF_INTRO
    
    print "Do you want instructions? ";
    chomp( my $answer = <> );
    if ( $answer !~ /^N/i ) {
      print<<__END_OF_INSTRUCTIONS;
    I am thinking of a whole number from 1 to $MAX_NUMBER
    Try to guess my number.  After you guess, I
    will type one or more stars (*).  The more
    stars I type, the closer you are to my number.
    One star (*) means far away, seven stars (*******)
    means really close!  You get $MAX_GUESSES guesses.
    __END_OF_INSTRUCTIONS
    }
    
    
    while (1) {
      my $number_to_guess = int(rand($MAX_NUMBER) + 1);
      say "\n\nOK, I am thinking of a number, start guessing.";
    
      my $guess_number = 1;
      while ( $guess_number <= $MAX_GUESSES ) {
        print "\nYour Guess? ";
        chomp( my $guess = <> );
        last if $guess == $number_to_guess;
        $guess_number++;
        my $difference = abs $guess - $number_to_guess;
        print '*' if $difference < 2;
        print '*' if $difference < 4;
        print '*' if $difference < 8;
        print '*' if $difference < 16;
        print '*' if $difference < 32;
        print '*' if $difference < 64;
        print "*\n";
      }
      if ( $guess_number > $MAX_GUESSES ) { # didn't guess
        say "\nSorry, that's $MAX_GUESSES guesses, number was $number_to_guess";
      } else { # winner!
        say '*' x 50, '!!!';
        say "You got it in $guess_number guesses!!!  Let's play again...";
      }
    }
    
    
    ================================================
    FILE: 82_Stars/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 82_Stars/python/stars.py
    ================================================
    """
    Stars
    
    From: BASIC Computer Games (1978)
          Edited by David H. Ahl
    
    "In this game, the computer selects a random number from 1 to 100
     (or any value you set [for MAX_NUM]).  You try to guess the number
     and the computer gives you clues to tell you how close you're
     getting.  One star (*) means you're far away from the number; seven
     stars (*******) means you're really close.  You get 7  guesses.
    
    "On the surface this game is very similar to GUESS; however, the
     guessing strategy is quite different.  See if you can come up with
     one or more approaches to finding the mystery number.
    
    "Bob Albrecht of People's Computer Company created this game."
    
    
    Python port by Jeff Jetton, 2019
    """
    
    
    import random
    
    # Some contants
    MAX_NUM = 100
    MAX_GUESSES = 7
    
    
    def print_instructions() -> None:
        """Instructions on how to play"""
        print("I am thinking of a whole number from 1 to %d" % MAX_NUM)
        print("Try to guess my number.  After you guess, I")
        print("will type 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 %d guesses." % MAX_GUESSES)
    
    
    def print_stars(secret_number, guess) -> None:
        diff = abs(guess - secret_number)
        stars = "".join("*" for i in range(8) if diff < 2**i)
        print(stars)
    
    
    def get_guess(prompt: str) -> int:
        while True:
            guess_str = input(prompt)
            if guess_str.isdigit():
                return int(guess_str)
    
    
    def main() -> None:
        # Display intro text
        print("\n                   Stars")
        print("Creative Computing  Morristown, New Jersey")
        print("\n\n")
        # "*** Stars - People's Computer Center, MenloPark, CA"
    
        response = input("Do you want instructions? ")
        if response.upper()[0] == "Y":
            print_instructions()
    
        still_playing = True
        while still_playing:
    
            # "*** Computer thinks of a number"
            secret_number = random.randint(1, MAX_NUM)
            print("\n\nOK, I am thinking of a number, start guessing.")
    
            # Init/start guess loop
            guess_number = 0
            player_has_won = False
            while (guess_number < MAX_GUESSES) and not player_has_won:
    
                print()
                guess = get_guess("Your guess? ")
                guess_number += 1
    
                if guess == secret_number:
                    # "*** We have a winner"
                    player_has_won = True
                    print("**************************************************!!!")
                    print(f"You got it in {guess_number} guesses!!!")
    
                else:
                    print_stars(secret_number, guess)
    
                # End of guess loop
    
            # "*** Did not guess in [MAX_GUESS] guesses"
            if not player_has_won:
                print(f"\nSorry, that's {guess_number} guesses, number was {secret_number}")
    
            # Keep playing?
            response = input("\nPlay again? ")
            if response.upper()[0] != "Y":
                still_playing = False
    
    
    if __name__ == "__main__":
        main()
    
    ######################################################################
    #
    # Porting Notes
    #
    #   The original program never exited--it just kept playing rounds
    #   over and over.  This version asks to continue each time.
    #
    #
    # Ideas for Modifications
    #
    #   Let the player know how many guesses they have remaining after
    #   each incorrect guess.
    #
    #   Ask the player to select a skill level at the start of the game,
    #   which will affect the values of MAX_NUM and MAX_GUESSES.
    #   For example:
    #
    #       Easy   = 8 guesses, 1 to 50
    #       Medium = 7 guesses, 1 to 100
    #       Hard   = 6 guesses, 1 to 200
    #
    ######################################################################
    
    
    ================================================
    FILE: 82_Stars/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 82_Stars/ruby/stars.rb
    ================================================
    class Stars
      MAX_NUM = 100
      MAX_GUESSES = 7
    
      def start
        print "Do you want instructions? (Y/N) "
        response = gets.chomp!
    
        if response.upcase[0] == "Y"
          print_instructions()
        end
    
        still_playing = true
        while still_playing
          secret_number = rand(1..MAX_NUM)
          puts "\n\nOK, I am thinking of a number, start guessing."
    
          guess_number = 0
          player_has_won = false
    
          while (guess_number < MAX_GUESSES) and not player_has_won
            puts "\n"
            guess = get_guess()
            guess_number += 1
    
            if guess == secret_number
              player_has_won = true
                puts "**************************************************!!!"
                puts "You got it in #{guess_number} guesses!!!\n\n"
            else
              print_stars(secret_number, guess)
            end
          end
    
          if not player_has_won
            puts "\nSorry, that's #{guess_number} guesses, number was #{secret_number}\n"
          end
    
          print "Play again? (Y/N) "
          response = gets.chomp!
    
          if response.upcase[0] != "Y"
            still_playing = false
          end
          
        end
      end
    
      private
        def print_instructions
          puts "I am thinking of a whole number from 1 to #{MAX_NUM}"
          puts "Try to guess my number.  After you guess, I"
          puts "will type one or more stars (*).  The more"
          puts "stars I type, the closer you are to my number."
          puts "one star (*) means far away, seven stars (*******)"
          puts "means really close!  You get #{MAX_GUESSES} guesses."
        end
    
        def get_guess
          valid_response = false
          while not valid_response
            print "Your guess? "
            guess = gets.chomp!
    
            if guess.match?(/[[:digit:]]/)
              valid_response = true
              guess = guess.to_i
            end
          end
    
          return guess
        end
    
        def print_stars secret_number, guess
          diff = (guess - secret_number).abs
          stars = ""
          for i in 0..7
            if diff < 2**i
              stars += "*"
            end
          end
          print(stars)
        end
    end
    
    
    if __FILE__ == $0
      stars = Stars.new
      stars.start()
    end
    
    ================================================
    FILE: 82_Stars/rust/Cargo.toml
    ================================================
    [package]
    name = "stars"
    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"
    
    
    ================================================
    FILE: 82_Stars/rust/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Rust](https://www.rust-lang.org/)
    
    
    ================================================
    FILE: 82_Stars/rust/src/main.rs
    ================================================
    use rand::Rng;
    use std::io;
    
    fn main() {
        println!(
            "{: >39}\n{: >57}\n\n\n",
            "STARS", "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
        );
        // STARS - PEOPLE'S COMPUTER CENTER, MENLO PARK, CA
        // A IS LIMIT ON NUMBER, M IS NUMBER OF GUESSES
        let a: u32 = 101;
        let m: u32 = 7;
        let mut need_instrut = String::new();
    
        println!("DO YOU WANT INSTRUCTIONS?");
        io::stdin()
            .read_line(&mut need_instrut)
            .expect("Failed to get input");
    
        if need_instrut[..1].to_ascii_lowercase().eq("y") {
            println!("I AM THINKING OF A WHOLE NUMBER FROM 1 TO {}", a - 1);
            println!("TRY TO GUESS MY NUMBER.  AFTER YOU GUESS, I");
            println!("WILL TYPE ONE OR MORE STARS (*).  THE MORE");
            println!("STARS I TYPE, THE CLOSER YOU ARE TO MY NUMBER.");
            println!("ONE STAR (*) MEANS FAR AWAY, SEVEN STARS (*******)");
            println!("MEANS REALLY CLOSE!  YOU GET {} GUESSES.\n\n", m);
        }
    
        loop {
            println!("\nOK, I AM THINKING OF A NUMBER, START GUESSING.\n");
            let rand_number: i32 = rand::thread_rng().gen_range(1..a) as i32; // generates a random number between 1 and 100
    
            // GUESSING BEGINS, HUMAN GETS M GUESSES
            for i in 0..m {
                let mut guess = String::new();
                println!("YOUR GUESS?");
                io::stdin()
                    .read_line(&mut guess)
                    .expect("Failed to get input");
                let guess: i32 = match guess.trim().parse() {
                    Ok(num) => num,
                    Err(_) => {
                        println!("PLEASE ENTER A NUMBER VALUE.\n");
                        continue;
                    }
                };
                if guess == rand_number {
                    print!("");
                    for _i in 0..50 {
                        print!("*");
                    }
                    println!("!!!");
                    println!("YOU GOT IT IN {} GUESSES!!!  LET'S PLAY AGAIN...\n", i + 1);
                    break;
                } else {
                    match_guess(rand_number - guess);
                }
    
                if i == 6 {
                    println!(
                        "SORRY, THAT'S {} GUESSES. THE NUMBER WAS {}",
                        m, rand_number
                    );
                }
            }
        }
    }
    
    fn match_guess(diff: i32) {
        if diff.abs() >= 64 {
            println!("*\n");
        } else if diff.abs() >= 32 {
            println!("**\n");
        } else if diff.abs() >= 16 {
            println!("***\n");
        } else if diff.abs() >= 8 {
            println!("****\n");
        } else if diff.abs() >= 4 {
            println!("*****\n");
        } else if diff.abs() >= 2 {
            println!("******\n");
        } else {
            println!("*******\n");
        }
    }
    
    
    ================================================
    FILE: 82_Stars/rust_JWB/Cargo.toml
    ================================================
    [package]
    <<<<<<< HEAD
    name = "rust"
    =======
    name = "stars"
    >>>>>>> 3e27c70ca800f5efbe6bc1a7d180211decf55b7d
    version = "0.1.0"
    edition = "2021"
    
    # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
    
    [dependencies]
    <<<<<<< HEAD
    rand = "0.8.5"
    =======
    rand = "0.8.3"
    >>>>>>> 3e27c70ca800f5efbe6bc1a7d180211decf55b7d
    
    
    ================================================
    FILE: 82_Stars/rust_JWB/README.md
    ================================================
    <<<<<<< HEAD
    #STARS
    
    From: BASIC Computer Games (1978), edited by David H. Ahl
    
    In this game, the computer selects a random number from 1 to 100
    (or any value you set [for MAX_NUM]).  You try to guess the number
    and the computer gives you clues to tell you how close you're
    getting.  One star (*) means you're far away from the number; seven
    stars (*******) means you're really close.  You get 7  guesses.
    
    On the surface this game is very similar to GUESS; however, the
    guessing strategy is quite different.  See if you can come up with
    one or more approaches to finding the mystery number.
    
    Bob Albrecht of People's Computer Company created this game.
    
    ## NOTES
    
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Rust](https://www.rust-lang.org/) by JW Bruce
    
    thanks to Jeff Jetton for his Python port which provide inspiration
    =======
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Rust](https://www.rust-lang.org/)
    >>>>>>> 3e27c70ca800f5efbe6bc1a7d180211decf55b7d
    
    
    ================================================
    FILE: 82_Stars/rust_JWB/src/main.rs
    ================================================
    <<<<<<< HEAD
    //
    // Stars
    //
    // From: BASIC Computer Games (1978), edited by David H. Ahl
    //
    // In this game, the computer selects a random number from 1 to 100
    // (or any value you set [for MAX_NUM]).  You try to guess the number
    // and the computer gives you clues to tell you how close you're
    // getting.  One star (*) means you're far away from the number; seven
    // stars (*******) means you're really close.  You get 7  guesses.
    //
    // On the surface this game is very similar to GUESS; however, the
    // guessing strategy is quite different.  See if you can come up with
    // one or more approaches to finding the mystery number.
    //
    // Bob Albrecht of People's Computer Company created this game.
    //
    // rust port by JW BRUCE 2022
    //
    // ********************************************************************
    //
    // Porting Notes (taken for Jeff Jetton's Python version)
    //
    //   The original program never exited--it just kept playing rounds
    //   over and over.  This version asks to continue each time.
    //
    // Ideas for Modifications
    //
    //   Let the player know how many guesses they have remaining after
    //   each incorrect guess.
    //
    //   Ask the player to select a skill level at the start of the game,
    //   which will affect the values of MAX_NUM and MAX_GUESSES.
    //   For example:
    //
    //       Easy   = 8 guesses, 1 to 50
    //       Medium = 7 guesses, 1 to 100
    //       Hard   = 6 guesses, 1 to 200
    //
    // *********************************************************************
    
    // I M P O R T S
    use std::io;
    use std::io::stdin;
    //use std::io::{stdin, stdout, Write};
    use rand::Rng;
    
    const   MAX_NUM: u8 = 100;
    const   MAX_GUESSES: u8 = 7;
    
    fn main() -> io::Result<()> {
        print_header();
        if !read_lowercase_input()?.starts_with('n') {
            print_rules();
        }
        loop {
            let secret_number : u8 = rand::thread_rng().gen_range(1..101);
            let mut guess_count = 0;
            let mut player_won: bool = false;
            
            println!("\n\nOK, I am thinking of a number, start guessing.");
            while guess_count < MAX_GUESSES && !player_won {
                
                guess_count += 1;        
    
                println!("Your guess? ");
                let mut guess = String::new();
                io::stdin()
                    .read_line(&mut guess)
                    .expect("Failed to read line");
    
                let guess: u8 = match guess.trim().parse() {
                    Ok(num) => num,
                    Err(_) => continue,
                };
                
                // USE THIS STATEMENT FOR DEBUG PURPOSES
                // println!("Guess #{} is {}. secret number is {}",guess_count, guess, secret_number);
                
                if guess == secret_number {
                    // winner winner chicken dinner
                    player_won = true;
                    println!("**************************************************!!!");
                    println!("You got it in {guess_count} guesses!!!");
                } else {
                    print_stars( guess, secret_number) ;
                }      
            }
            
            // player exhausted their number of guesses and did not win.
            if !player_won {
                println!("Sorry, that's {guess_count} guesses, number was {secret_number}");
            }
    
            println!("\nPlay again (yes or no)?");
            if !read_lowercase_input()?.starts_with('y') {
                return Ok(());
    =======
    use rand::Rng;
    use std::io;
    
    fn main() {
        println!(
            "{: >39}\n{: >57}\n\n\n",
            "STARS", "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
        );
        // STARS - PEOPLE'S COMPUTER CENTER, MENLO PARK, CA
        // A IS LIMIT ON NUMBER, M IS NUMBER OF GUESSES
        let a: u32 = 101;
        let m: u32 = 7;
        let mut need_instrut = String::new();
    
        println!("DO YOU WANT INSTRUCTIONS?");
        io::stdin()
            .read_line(&mut need_instrut)
            .expect("Failed to get input");
    
        if need_instrut[..1].to_ascii_lowercase().eq("y") {
            println!("I AM THINKING OF A WHOLE NUMBER FROM 1 TO {}", a - 1);
            println!("TRY TO GUESS MY NUMBER.  AFTER YOU GUESS, I");
            println!("WILL TYPE ONE OR MORE STARS (*).  THE MORE");
            println!("STARS I TYPE, THE CLOSER YOU ARE TO MY NUMBER.");
            println!("ONE STAR (*) MEANS FAR AWAY, SEVEN STARS (*******)");
            println!("MEANS REALLY CLOSE!  YOU GET {} GUESSES.\n\n", m);
        }
    
        loop {
            println!("\nOK, I AM THINKING OF A NUMBER, START GUESSING.\n");
            let rand_number: i32 = rand::thread_rng().gen_range(1..a) as i32; // generates a random number between 1 and 100
    
            // GUESSING BEGINS, HUMAN GETS M GUESSES
            for i in 0..m {
                let mut guess = String::new();
                println!("YOUR GUESS?");
                io::stdin()
                    .read_line(&mut guess)
                    .expect("Failed to get input");
                let guess: i32 = match guess.trim().parse() {
                    Ok(num) => num,
                    Err(_) => {
                        println!("PLEASE ENTER A NUMBER VALUE.\n");
                        continue;
                    }
                };
                if guess == rand_number {
                    print!("");
                    for _i in 0..50 {
                        print!("*");
                    }
                    println!("!!!");
                    println!("YOU GOT IT IN {} GUESSES!!!  LET'S PLAY AGAIN...\n", i + 1);
                    break;
                } else {
                    match_guess(rand_number - guess);
                }
    
                if i == 6 {
                    println!(
                        "SORRY, THAT'S {} GUESSES. THE NUMBER WAS {}",
                        m, rand_number
                    );
                }
    >>>>>>> 3e27c70ca800f5efbe6bc1a7d180211decf55b7d
            }
        }
    }
    
    <<<<<<< HEAD
    // guess is wrong, so print stars to show how far away they are
    fn print_stars( guess: u8, target: u8) {
        // choose to use u8 in main, but currently (1.59.0) does not
        //   have abs() defined for u8.  abs() is defined for i16, so
        //   this provide an opportunity to demonstrate casting in rust
        let diff : i16 = ((guess as i16)-(target as i16)).abs();
        
        // Since we only print 1-7 stars, this finite set of choices is
        //   small enough that we can use rust's match keyword.
        // The match "arms" here use the inclusive range notation.
        //   The exlusive range notation is not an approved feature of
        //   rust, yet.
        match diff {
            1..=2 => println!("*******"),
            3..=4 => println!("******"),
            5..=8 => println!("*****"),
            9..=16 => println!("****"),
            17..=32 => println!("***"),
            33..=64 => println!("**"),
            _ => println!("*"),
        }
    }
    
    //
    fn read_lowercase_input() -> io::Result {
        let mut input = String::new();
        stdin().read_line(&mut input)?;
        Ok(input.trim().to_lowercase())
    }
    
    // Text to print at the start of the game
    fn print_header() {
        println!("\n                   Stars");
        println!("Creative-Computing  Morristown, New Jersey");
        println!("\n\n");
        println!("Do you want instructions? ");
    }
    
    // Instructions on how to play
    fn print_rules() {
        println!();
        println!("I am thinking of a whole number from 1 to {}", MAX_NUM);
        println!("Try to guess my number.  After you guess, I");
        println!("will type one or more stars (*).  The more");
        println!("stars I type, the closer you are to my number.");
        println!("one star (*) means far away, seven stars (*******)");
        println!("means really close!  You get {} guesses.", MAX_GUESSES);
    }
    =======
    fn match_guess(diff: i32) {
        if diff.abs() >= 64 {
            println!("*\n");
        } else if diff.abs() >= 32 {
            println!("**\n");
        } else if diff.abs() >= 16 {
            println!("***\n");
        } else if diff.abs() >= 8 {
            println!("****\n");
        } else if diff.abs() >= 4 {
            println!("*****\n");
        } else if diff.abs() >= 2 {
            println!("******\n");
        } else {
            println!("*******\n");
        }
    }
    >>>>>>> 3e27c70ca800f5efbe6bc1a7d180211decf55b7d
    
    
    ================================================
    FILE: 82_Stars/stars.bas
    ================================================
    10 PRINT TAB(34);"STARS"
    20 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    30 PRINT:PRINT:PRINT
    100 REM *** STARS - PEOPLE'S COMPUTER CENTER, MENLO PARK, CA
    140 REM *** A IS LIMIT ON NUMBER, M IS NUMBER OF GUESSES
    150 A=100:M=7
    170 INPUT "DO YOU WANT INSTRUCTIONS";A$
    190 IF LEFT$(A$,1)="N" THEN 280
    200 REM *** INSTRUCTIONS ON HOW TO PLAY
    210 PRINT "I AM THINKING OF A WHOLE NUMBER FROM 1 TO";A
    220 PRINT "TRY TO GUESS MY NUMBER.  AFTER YOU GUESS, I"
    230 PRINT "WILL TYPE ONE OR MORE STARS (*).  THE MORE"
    240 PRINT "STARS I TYPE, THE CLOSER YOU ARE TO MY NUMBER."
    250 PRINT "ONE STAR (*) MEANS FAR AWAY, SEVEN STARS (*******)"
    260 PRINT "MEANS REALLY CLOSE!  YOU GET";M;"GUESSES."
    270 REM *** COMPUTER THINKS OF A NUMBER
    280 PRINT
    290 PRINT
    300 X=INT(A*RND(1)+1)
    310 PRINT "OK, I AM THINKING OF A NUMBER, START GUESSING."
    320 REM *** GUESSING BEGINS, HUMAN GETS M GUESSES
    330 FOR K=1 TO M
    340 PRINT
    350 PRINT "YOUR GUESS";
    360 INPUT G
    370 IF G=X THEN 600
    380 D=ABS(G-X)
    390 IF D>=64 THEN 510
    400 IF D>=32 THEN 500
    410 IF D>=16 THEN 490
    420 IF D>=8 THEN 480
    430 IF D>=4 THEN 470
    440 IF D>=2 THEN 460
    450 PRINT "*";
    460 PRINT "*";
    470 PRINT "*";
    480 PRINT "*";
    490 PRINT "*";
    500 PRINT "*";
    510 PRINT "*";
    520 PRINT
    530 NEXT K
    540 REM *** DID NOT GUESS IN M GUESSES
    550 PRINT
    560 PRINT "SORRY, THAT'S";M;"GUESSES. THE NUMBER WAS";X
    580 GOTO 650
    590 REM *** WE HAVE A WINNER
    600 PRINT:FOR N=1 TO 79
    610 PRINT "*";
    620 NEXT N
    630 PRINT:PRINT
    640 PRINT "YOU GOT IT IN";K;"GUESSES!!!  LET'S PLAY AGAIN..."
    650 GOTO 280
    660 END
    
    
    ================================================
    FILE: 82_Stars/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 82_Stars/vbnet/Stars.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Stars", "Stars.vbproj", "{181B70F5-C2CB-4A73-9982-30C16DE89240}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{181B70F5-C2CB-4A73-9982-30C16DE89240}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{181B70F5-C2CB-4A73-9982-30C16DE89240}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{181B70F5-C2CB-4A73-9982-30C16DE89240}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{181B70F5-C2CB-4A73-9982-30C16DE89240}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 82_Stars/vbnet/Stars.vbproj
    ================================================
    
      
        Exe
        Stars
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 83_Stock_Market/README.md
    ================================================
    ### Stock Market
    
    This program “plays” the stock market. You will be given $10,000 and may buy or sell stocks. Stock prices and trends are generated randomly; therefore, this model does not represent exactly what happens on the exchange. (Depending upon your point of view, you may feel this is quite a good representation!)
    
    Every trading day, a table of stocks, their prices, and number of shares in your portfolio is printed. Following this, the initials of each stock are printed followed by a question mark. You indicate your transaction in number of shares — a positive number to buy, negative to sell, or 0 to do no trading. A brokerage fee of 1% is charges on all transactions (a bargain!). Note: Even if the value of a stock drops to zero, it may rebound again — then again, it may not.
    
    This program was created by D. Pessel, L. Braun, and C. Losik of the Huntington Computer Project at SUNY, Stony Brook, N.Y.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=154)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=166)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 83_Stock_Market/csharp/Assets.cs
    ================================================
    using System.Collections.Immutable;
    
    namespace Game
    {
        /// 
        /// Stores the player's assets.
        /// 
        public record Assets
        {
            /// 
            /// Gets the player's amount of cash.
            /// 
            public double Cash { get; init; }
    
            /// 
            /// Gets the number of stocks owned of each company.
            /// 
            public ImmutableArray Portfolio { get; init; }
        }
    }
    
    
    ================================================
    FILE: 83_Stock_Market/csharp/Broker.cs
    ================================================
    using System;
    using System.Collections.Generic;
    using System.Collections.Immutable;
    using System.Linq;
    
    namespace Game
    {
        /// 
        /// Contains functions for exchanging assets.
        /// 
        public static class Broker
        {
            /// 
            /// Applies the given set of transactions to the given set of assets.
            /// 
            /// 
            /// The assets to update.
            /// 
            /// 
            /// The set of stocks to purchase or sell.  Positive values indicate
            /// purchaes and negative values indicate sales.
            /// 
            /// 
            /// The collection of companies.
            /// 
            /// 
            /// Returns the sellers new assets and a code indicating the result
            /// of the transaction.
            /// 
            public static (Assets newAssets, TransactionResult result) Apply(Assets assets, IEnumerable transactions, IEnumerable companies)
            {
                var (netCost, transactionSize) = Enumerable.Zip(
                        transactions,
                        companies,
                        (amount, company) => (amount * company.SharePrice))
                    .Aggregate(
                        (netCost: 0.0, transactionSize: 0.0),
                        (accumulated, amount) => (accumulated.netCost + amount, accumulated.transactionSize + Math.Abs(amount)));
    
                var brokerageFee = 0.01 * transactionSize;
    
                var newAssets = assets with
                {
                    Cash      = assets.Cash - netCost - brokerageFee,
                    Portfolio = ImmutableArray.CreateRange(Enumerable.Zip(
                        assets.Portfolio,
                        transactions,
                        (sharesOwned, delta) => sharesOwned + delta))
                };
    
                if (newAssets.Portfolio.Any(amount => amount < 0))
                    return (newAssets, TransactionResult.Oversold);
                else
                if (newAssets.Cash < 0)
                    return (newAssets, TransactionResult.Overspent);
                else
                    return (newAssets, TransactionResult.Ok);
            }
        }
    }
    
    
    ================================================
    FILE: 83_Stock_Market/csharp/Company.cs
    ================================================
    namespace Game
    {
        /// 
        /// Represents a company.
        /// 
        public record Company
        {
            /// 
            /// Gets the company's name.
            /// 
            public string Name { get; }
    
            /// 
            /// Gets the company's three letter stock symbol.
            /// 
            public string StockSymbol { get; }
    
            /// 
            /// Gets the company's current share price.
            /// 
            public double SharePrice { get; init; }
    
            /// 
            /// Initializes a new Company record.
            /// 
            public Company(string name, string stockSymbol, double sharePrice) =>
                (Name, StockSymbol, SharePrice) = (name, stockSymbol, sharePrice);
        }
    }
    
    
    ================================================
    FILE: 83_Stock_Market/csharp/Controller.cs
    ================================================
    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace Game
    {
        public static class Controller
        {
            /// 
            /// Manages the initial interaction with the user.
            /// 
            public static void StartGame()
            {
                View.ShowBanner();
    
                var showInstructions = GetYesOrNo(View.PromptShowInstructions);
                View.ShowSeparator();
                if (showInstructions)
                    View.ShowInstructions();
    
                View.ShowSeparator();
            }
    
            /// 
            /// Gets a yes or no answer from the user.
            /// 
            /// 
            /// Displays the prompt.
            /// 
            /// 
            /// True if the user answered yes and false if he or she answered no.
            /// 
            public static bool GetYesOrNo(Action prompt)
            {
                prompt();
    
                var response = default(char);
                do
                {
                    response = Console.ReadKey(intercept: true).KeyChar;
                }
                while (response != '0' && response != '1');
    
                View.ShowChar(response);
                return response == '1';
            }
    
            /// 
            /// Gets a transaction amount for each company in the given collection
            /// of companies and returns the updated assets.
            /// 
            /// 
            /// The assets to update.
            /// 
            /// 
            /// The collection of companies.
            /// 
            /// 
            /// The updated assets.
            /// 
            public static Assets UpdateAssets(Assets assets, IEnumerable companies)
            {
                while (true)
                {
                    View.PromptEnterTransactions();
    
                    var result = Broker.Apply (
                        assets,
                        companies.Select(GetTransactionAmount).ToList(),
                        companies);
    
                    switch (result)
                    {
                        case (Assets newAssets, TransactionResult.Ok):
                            return newAssets;
                        case (_, TransactionResult.Oversold):
                            View.ShowOversold();
                            break;
                        case (Assets newAssets, TransactionResult.Overspent):
                            View.ShowOverspent(-newAssets.Cash);
                            break;
                    }
                }
            }
    
            /// 
            /// Gets a transaction amount for the given company.
            /// 
            /// 
            /// The company to buy or sell.
            /// 
            /// 
            /// The number of shares to buy or sell.
            /// 
            public static int GetTransactionAmount(Company company)
            {
                while (true)
                {
                    View.PromptBuySellCompany(company);
    
                    var input = Console.ReadLine();
                    if (input is null)
                        Environment.Exit(0);
                    else
                    if (!Int32.TryParse(input, out var amount))
                        View.PromptValidInteger();
                    else
                        return amount;
                }
            }
        }
    }
    
    
    ================================================
    FILE: 83_Stock_Market/csharp/Extensions/EnumerableExtensions.cs
    ================================================
    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace Game.Extensions
    {
        /// 
        /// Provides additional methods for the 
        /// interface.
        /// 
        public static class EnumerableExtensions
        {
            /// 
            /// Simultaneously projects each element of a sequence and applies
            /// the result of the previous projection.
            /// 
            /// 
            /// The type of elements in the source sequence.
            /// 
            /// 
            /// The type of elements in the result sequence.
            /// 
            /// 
            /// The source sequence.
            /// 
            /// 
            /// The seed value for the aggregation component.  This value is
            /// passed to the first call to .
            /// 
            /// 
            /// The projection function.  This function is supplied with a value
            /// from the source sequence and the result of the projection on the
            /// previous value in the source sequence.
            /// 
            /// 
            /// The resulting sequence.
            /// 
            public static IEnumerable SelectAndAggregate(
                this IEnumerable source,
                TResult seed,
                Func selector)
            {
                foreach (var element in source)
                {
                    seed = selector(element, seed);
                    yield return seed;
                }
            }
    
            /// 
            /// Combines the results of three distinct sequences into a single
            /// sequence.
            /// 
            /// 
            /// The element type of the first sequence.
            /// 
            /// 
            /// The element type of the second sequence.
            /// 
            /// 
            /// The element type of the third sequence.
            /// 
            /// 
            /// The element type of the resulting sequence.
            /// 
            /// 
            /// The first source sequence.
            /// 
            /// 
            /// The second source sequence.
            /// 
            /// 
            /// The third source sequence.
            /// 
            /// 
            /// Function that combines results from each source sequence into a
            /// final result.
            /// 
            /// 
            /// A sequence of combined values.
            /// 
            /// 
            /// 
            /// This function works identically to Enumerable.Zip except that it
            /// combines three sequences instead of two.
            /// 
            /// 
            /// We have defined this as an extension method for consistency with
            /// the similar LINQ methods in the  class.
            /// However, since there is nothing special about the first sequence,
            /// it is often more clear to call this as a regular function.  For
            /// example:
            /// 
            /// 
            /// EnumerableExtensions.Zip(
            ///     sequence1,
            ///     sequence2,
            ///     sequence3,
            ///     (a, b, c) => GetResult (a, b, c));
            /// 
            /// 
            public static IEnumerable Zip(
                this IEnumerable first,
                IEnumerable second,
                IEnumerable third,
                Func resultSelector)
            {
                using var enumerator1 = first.GetEnumerator();
                using var enumerator2 = second.GetEnumerator();
                using var enumerator3 = third.GetEnumerator();
    
                while (enumerator1.MoveNext() && enumerator2.MoveNext() && enumerator3.MoveNext())
                    yield return resultSelector(enumerator1.Current, enumerator2.Current, enumerator3.Current);
            }
        }
    }
    
    
    ================================================
    FILE: 83_Stock_Market/csharp/Extensions/ImmutableArrayExtensions.cs
    ================================================
    using System;
    using System.Collections.Immutable;
    
    namespace Game.Extensions
    {
        /// 
        /// Provides additional methods for the  class.
        /// 
        public static class ImmutableArrayExtensions
        {
            /// 
            /// Maps each element in an immutable array to a new value.
            /// 
            /// 
            /// The type of elements in the source array.
            /// 
            /// 
            /// The type of elements in the resulting array.
            /// 
            /// 
            /// The source array.
            /// 
            /// 
            /// Function which receives an element from the source array and its
            /// index and returns the resulting element.
            /// 
            public static ImmutableArray Map(this ImmutableArray source, Func selector)
            {
                var builder = ImmutableArray.CreateBuilder(source.Length);
    
                for (var i = 0; i < source.Length; ++i)
                    builder.Add(selector(source[i], i));
    
                return builder.MoveToImmutable();
            }
        }
    }
    
    
    ================================================
    FILE: 83_Stock_Market/csharp/Extensions/RandomExtensions.cs
    ================================================
    using System;
    using System.Collections.Generic;
    
    namespace Game.Extensions
    {
        /// 
        /// Provides additional methods for the  class.
        /// 
        public static class RandomExtensions
        {
            /// 
            /// Generates an infinite sequence of random numbers.
            /// 
            /// 
            /// The random number generator.
            /// 
            /// 
            /// The inclusive lower bound of the range to generate.
            /// 
            /// 
            /// The exclusive upper bound of the range to generate.
            /// 
            /// 
            /// An infinite sequence of random integers in the range [min, max).
            /// 
            /// 
            /// 
            /// We use an exclusive upper bound, even though it's a little
            /// confusing, for the sake of consistency with Random.Next.
            /// 
            /// 
            /// Since the sequence is infinite, a typical usage would be to cap
            /// the results with a function like Enumerable.Take.  For example,
            /// to sum the results of rolling three six sided dice, we could do:
            /// 
            /// 
            /// random.Integers(1, 7).Take(3).Sum()
            /// 
            /// 
            public static IEnumerable Integers(this Random random, int min, int max)
            {
                while (true)
                    yield return random.Next(min, max);
            }
        }
    }
    
    
    ================================================
    FILE: 83_Stock_Market/csharp/Program.cs
    ================================================
    using System;
    using System.Collections.Immutable;
    using System.Linq;
    
    namespace Game
    {
        class Program
        {
            /// 
            /// Defines the set of companies that will be simulated in the game.
            /// 
            private readonly static ImmutableArray Companies = ImmutableArray.CreateRange(new[]
            {
                new Company("INT. BALLISTIC MISSILES",     "IBM", sharePrice:100),
                new Company("RED CROSS OF AMERICA",        "RCA", sharePrice:85 ),
                new Company("LICHTENSTEIN, BUMRAP & JOKE", "LBJ", sharePrice:150),
                new Company("AMERICAN BANKRUPT CO.",       "ABC", sharePrice:140),
                new Company("CENSURED BOOKS STORE",        "CBS", sharePrice:110)
            });
    
            static void Main()
            {
                var assets = new Assets
                {
                    Cash      = 10000.0,
                    Portfolio = ImmutableArray.CreateRange(Enumerable.Repeat(0, Companies.Length))
                };
    
                var previousDay = default(TradingDay);
    
                Controller.StartGame();
    
                foreach (var day in StockMarket.Simulate(Companies))
                {
                    if (previousDay is null)
                        View.ShowCompanies(day.Companies);
                    else
                        View.ShowTradeResults(day, previousDay, assets);
    
                    View.ShowAssets(assets, day.Companies);
    
                    if (previousDay is not null && !Controller.GetYesOrNo(View.PromptContinue))
                        break;
    
                    assets      = Controller.UpdateAssets(assets, day.Companies);
                    previousDay = day;
                }
    
                View.ShowFarewell();
            }
        }
    }
    
    
    ================================================
    FILE: 83_Stock_Market/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 83_Stock_Market/csharp/StockMarket.cs
    ================================================
    using System;
    using System.Collections.Generic;
    using System.Collections.Immutable;
    using System.Linq;
    using Game.Extensions;
    
    namespace Game
    {
        /// 
        /// Provides a method for simulating a stock market.
        /// 
        public static class StockMarket
        {
            /// 
            /// Simulates changes in the stock market over time.
            /// 
            /// 
            /// The collection of companies that will participate in the market.
            /// 
            /// 
            /// An infinite sequence of trading days.  Each day represents the
            /// state of the stock market at the start of that day.
            /// 
            public static IEnumerable Simulate(ImmutableArray companies)
            {
                var random = new Random();
    
                var cyclicParameters = EnumerableExtensions.Zip(
                    Trends(random, 1, 5),
                    PriceSpikes(random, companies.Length, 1, 5),
                    PriceSpikes(random, companies.Length, 1, 5),
                    (trend, company1, company2) => (trend, positiveSpike: company1, negativeSpike: company2));
    
                return cyclicParameters.SelectAndAggregate(
                    new TradingDay
                    {
                        Companies = companies
                    },
                    (parameters, previousDay) => previousDay with
                    {
                        Companies = previousDay.Companies.Map(
                            (company, index) => AdjustSharePrice(
                                random,
                                company,
                                parameters.trend,
                                parameters.positiveSpike == index,
                                parameters.negativeSpike == index))
                    });
            }
    
            /// 
            /// Creates a copy of a company with a randomly adjusted share price,
            /// based on the given parameters.
            /// 
            /// 
            /// The random number generator.
            /// 
            /// 
            /// The company to adjust.
            /// 
            /// 
            /// The slope of the overall market price trend.
            /// 
            /// 
            /// True if the function should simulate a positive spike in the
            /// company's share price.
            /// 
            /// 
            /// True if the function should simulate a negative spike in the
            /// company's share price.
            /// 
            /// 
            /// The adjusted company.
            /// 
            private static Company AdjustSharePrice(Random random, Company company, double trend, bool positiveSpike, bool negativeSpike)
            {
                var boost = random.Next(4) * 0.25;
    
                var spikeAmount = 0.0;
    
                if (positiveSpike)
                    spikeAmount = 10;
    
                if (negativeSpike)
                    spikeAmount = spikeAmount - 10;
    
                var priceChange = (int)(trend * company.SharePrice) + boost + (int)(3.5 - (6 * random.NextDouble())) + spikeAmount;
    
                var newPrice = company.SharePrice + priceChange;
                if (newPrice < 0)
                    newPrice = 0;
    
                return company with { SharePrice = newPrice };
            }
    
            /// 
            /// Generates an infinite sequence of market trends.
            /// 
            /// 
            /// The random number generator.
            /// 
            /// 
            /// The minimum number of days each trend should last.
            /// 
            /// 
            /// The maximum number of days each trend should last.
            /// 
            public static IEnumerable Trends(Random random, int minDays, int maxDays) =>
                random.Integers(minDays, maxDays + 1).SelectMany(daysInCycle => Enumerable.Repeat(GenerateTrend(random), daysInCycle));
    
            /// 
            /// Generates a random value for the market trend.
            /// 
            /// 
            /// The random number generator.
            /// 
            /// 
            /// A trend value in the range [-0.1, 0.1].
            /// 
            private static double GenerateTrend(Random random) =>
                ((int)(random.NextDouble() * 10 + 0.5) / 100.0) * (random.Next(2) == 0 ? 1 : -1) ;
    
            /// 
            /// Generates an infinite sequence of price spikes.
            /// 
            /// 
            /// The random number generator.
            /// 
            /// 
            /// The number of companies.
            /// 
            /// 
            /// The minimum number of days in between price spikes.
            /// 
            /// 
            /// The maximum number of days in between price spikes.
            /// 
            /// 
            /// An infinite sequence of random company indexes and null values.
            /// A non-null value means that the corresponding company should
            /// experience a price spike.
            /// 
            private static IEnumerable PriceSpikes(Random random, int companyCount, int minDays, int maxDays) =>
                random.Integers(minDays, maxDays + 1)
                    .SelectMany(
                        daysInCycle => Enumerable.Range(0, daysInCycle),
                        (daysInCycle, dayNumber) => dayNumber == 0 ? random.Next(companyCount) : default(int?));
        }
    }
    
    
    ================================================
    FILE: 83_Stock_Market/csharp/StockMarket.csproj
    ================================================
    
      
        Exe
        net5.0
        enable
      
    
    
    
    ================================================
    FILE: 83_Stock_Market/csharp/StockMarket.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 17
    VisualStudioVersion = 17.0.32014.148
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StockMarket", "StockMarket.csproj", "{B4C15C31-EB2D-4AAF-8380-5F2EBC0DD9D0}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{B4C15C31-EB2D-4AAF-8380-5F2EBC0DD9D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{B4C15C31-EB2D-4AAF-8380-5F2EBC0DD9D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{B4C15C31-EB2D-4AAF-8380-5F2EBC0DD9D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{B4C15C31-EB2D-4AAF-8380-5F2EBC0DD9D0}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {C78DBA4A-87E2-4B31-A261-4AEF5E4C3B12}
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 83_Stock_Market/csharp/TradingDay.cs
    ================================================
    using System.Collections.Immutable;
    using System.Linq;
    
    namespace Game
    {
        /// 
        /// Represents a single trading day.
        /// 
        public record TradingDay
        {
            /// 
            /// Gets the average share price of all companies in the market this
            /// day.
            /// 
            public double AverageSharePrice =>
                Companies.Average (company => company.SharePrice);
    
            /// 
            /// Gets the collection of public listed companies in the stock market
            /// this day.
            /// 
            public ImmutableArray Companies { get; init; }
        }
    }
    
    
    ================================================
    FILE: 83_Stock_Market/csharp/TransactionResult.cs
    ================================================
    namespace Game
    {
        /// 
        /// Enumerates the different possible outcomes of applying a transaction.
        /// 
        public enum TransactionResult
        {
            /// 
            /// The transaction was successful.
            /// 
            Ok,
    
            /// 
            /// The transaction failed because the seller tried to sell more shares
            /// than he or she owns.
            /// 
            Oversold,
    
            /// 
            /// The transaction failed because the net cost was greater than the
            /// seller's available cash.
            /// 
            Overspent
        }
    }
    
    
    ================================================
    FILE: 83_Stock_Market/csharp/View.cs
    ================================================
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Game.Extensions;
    
    namespace Game
    {
        /// 
        /// Contains functions for displaying information to the user.
        /// 
        public static class View
        {
            public static void ShowBanner()
            {
                Console.WriteLine("                             STOCK MARKET");
                Console.WriteLine("               CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine();
            }
    
            public static void ShowInstructions()
            {
                Console.WriteLine("THIS PROGRAM PLAYS THE STOCK MARKET.  YOU WILL BE GIVEN");
                Console.WriteLine("$10,000 AND MAY BUY OR SELL STOCKS.  THE STOCK PRICES WILL");
                Console.WriteLine("BE GENERATED RANDOMLY AND THEREFORE THIS MODEL DOES NOT");
                Console.WriteLine("REPRESENT EXACTLY WHAT HAPPENS ON THE EXCHANGE.  A TABLE");
                Console.WriteLine("OF AVAILABLE STOCKS, THEIR PRICES, AND THE NUMBER OF SHARES");
                Console.WriteLine("IN YOUR PORTFOLIO WILL BE PRINTED.  FOLLOWING THIS, THE");
                Console.WriteLine("INITIALS OF EACH STOCK WILL BE PRINTED WITH A QUESTION");
                Console.WriteLine("MARK.  HERE YOU INDICATE A TRANSACTION.  TO BUY A STOCK");
                Console.WriteLine("TYPE +NNN, TO SELL A STOCK TYPE -NNN, WHERE NNN IS THE");
                Console.WriteLine("NUMBER OF SHARES.  A BROKERAGE FEE OF 1% WILL BE CHARGED");
                Console.WriteLine("ON ALL TRANSACTIONS.  NOTE THAT IF A STOCK'S VALUE DROPS");
                Console.WriteLine("TO ZERO IT MAY REBOUND TO A POSITIVE VALUE AGAIN.  YOU");
                Console.WriteLine("HAVE $10,000 TO INVEST.  USE INTEGERS FOR ALL YOUR INPUTS.");
                Console.WriteLine("(NOTE:  TO GET A 'FEEL' FOR THE MARKET RUN FOR AT LEAST");
                Console.WriteLine("10 DAYS)");
                Console.WriteLine("-----GOOD LUCK!-----");
            }
    
            public static void ShowCompanies(IEnumerable companies)
            {
                var maxNameLength = companies.Max(company => company.Name.Length);
    
                Console.WriteLine($"{"STOCK".PadRight(maxNameLength)} INITIALS      PRICE/SHARE");
                foreach (var company in companies)
                    Console.WriteLine($"{company.Name.PadRight(maxNameLength)}   {company.StockSymbol}          {company.SharePrice:0.00}");
    
                Console.WriteLine();
                Console.WriteLine($"NEW YORK STOCK EXCHANGE AVERAGE: {companies.Average(company => company.SharePrice):0.00}");
                Console.WriteLine();
            }
    
            public static void ShowTradeResults(TradingDay day, TradingDay previousDay, Assets assets)
            {
                var results = EnumerableExtensions.Zip(
                    day.Companies,
                    previousDay.Companies,
                    assets.Portfolio,
                    (company, previous, shares) =>
                    (
                        stockSymbol: company.StockSymbol,
                        price: company.SharePrice,
                        shares,
                        value: shares * company.SharePrice,
                        change: company.SharePrice - previous.SharePrice
                    )).ToList();
    
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine("**********     END OF DAY'S TRADING     **********");
                Console.WriteLine();
                Console.WriteLine();
    
                Console.WriteLine("STOCK\tPRICE/SHARE\tHOLDINGS\tVALUE\tNET PRICE CHANGE");
                foreach (var result in results)
                    Console.WriteLine($"{result.stockSymbol}\t{result.price}\t\t{result.shares}\t\t{result.value:0.00}\t\t{result.change:0.00}");
    
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine();
    
                var averagePrice = day.AverageSharePrice;
                var averagePriceChange = averagePrice - previousDay.AverageSharePrice;
    
                Console.WriteLine($"NEW YORK STOCK EXCHANGE AVERAGE: {averagePrice:0.00} NET CHANGE {averagePriceChange:0.00}");
                Console.WriteLine();
            }
    
            public static void ShowAssets(Assets assets, IEnumerable companies)
            {
                var totalStockValue = Enumerable.Zip(
                    assets.Portfolio,
                    companies,
                    (shares, company) => shares * company.SharePrice).Sum();
    
                Console.WriteLine($"TOTAL STOCK ASSETS ARE   ${totalStockValue:0.00}");
                Console.WriteLine($"TOTAL CASH ASSETS ARE    ${assets.Cash:0.00}");
                Console.WriteLine($"TOTAL ASSETS ARE         ${totalStockValue + assets.Cash:0.00}");
                Console.WriteLine();
            }
    
            public static void ShowOversold()
            {
                Console.WriteLine();
                Console.WriteLine("YOU HAVE OVERSOLD A STOCK; TRY AGAIN.");
            }
    
            public static void ShowOverspent(double amount)
            {
                Console.WriteLine();
                Console.WriteLine($"YOU HAVE USED ${amount:0.00} MORE THAN YOU HAVE.");
            }
    
            public static void ShowFarewell()
            {
                Console.WriteLine("HOPE YOU HAD FUN!!");
            }
    
            public static void ShowSeparator()
            {
                Console.WriteLine();
                Console.WriteLine();
            }
    
            public static void ShowChar(char c)
            {
                Console.WriteLine(c);
            }
    
            public static void PromptShowInstructions()
            {
                Console.Write("DO YOU WANT THE INSTRUCTIONS (YES-TYPE 1, NO-TYPE 0)? ");
            }
    
            public static void PromptContinue()
            {
                Console.Write("DO YOU WISH TO CONTINUE (YES-TYPE 1, NO-TYPE 0)? ");
            }
    
            public static void PromptEnterTransactions()
            {
                Console.WriteLine("WHAT IS YOUR TRANSACTION IN");
            }
    
            public static void PromptBuySellCompany(Company company)
            {
                Console.Write($"{company.StockSymbol}? ");
            }
    
            public static void PromptValidInteger()
            {
                Console.WriteLine("PLEASE ENTER A VALID INTEGER");
            }
        }
    }
    
    
    ================================================
    FILE: 83_Stock_Market/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 83_Stock_Market/java/StockMarket.java
    ================================================
    import java.util.ArrayList;
    import java.util.InputMismatchException;
    import java.util.List;
    import java.util.Random;
    import java.util.Scanner;
    
    /**
     * Stock Market Simulation
     *
     * Some of the original program's variables' documentation and their equivalent in this program:
     * A-MRKT TRND SLP;             marketTrendSlope
     * B5-BRKRGE FEE;               brokerageFee
     * C-TTL CSH ASSTS;             cashAssets
     * C5-TTL CSH ASSTS (TEMP);     tmpCashAssets
     * C(I)-CHNG IN STK VAL;        changeStockValue
     * D-TTL ASSTS;                 assets
     * E1,E2-LRG CHNG MISC;         largeChange1, largeChange2
     * I1,I2-STCKS W LRG CHNG;      randomStockIndex1, randomStockIndex2
     * N1,N2-LRG CHNG DAY CNTS;     largeChangeNumberDays1, largeChangeNumberDays2
     * P5-TTL DAYS PRCHSS;          totalDaysPurchases
     * P(I)-PRTFL CNTNTS;           portfolioContents
     * Q9-NEW CYCL?;                newCycle
     * S4-SGN OF A;                 slopeSign
     * S5-TTL DYS SLS;              totalDaysSales
     * S(I)-VALUE/SHR;              stockValue
     * T-TTL STCK ASSTS;            totalStockAssets
     * T5-TTL VAL OF TRNSCTNS;      totalValueOfTransactions
     * W3-LRG CHNG;                 bigChange
     * X1-SMLL CHNG(<$1);           smallChange
     * Z4,Z5,Z6-NYSE AVE.;          tmpNyseAverage, nyseAverage, nyseAverageChange
     * Z(I)-TRNSCT                  transactionQuantity
     *
     * new price = old price + (trend x old price) + (small random price
     * change) + (possible large price change)
     *
     * Converted from BASIC to Java by Aldrin Misquitta (@aldrinm)
     */
    public class StockMarket {
    
    	private static final Random random = new Random();
    
    	public static void main(String[] args) {
    
    		Scanner scan = new Scanner(System.in);
    
    		printIntro();
    		printGameHelp(scan);
    
    		final List stocks = initStocks();
    
    		double marketTrendSlope = Math.floor((random.nextFloat() / 10) * 100 + 0.5)/100f;
    		double totalValueOfTransactions;
    		int largeChangeNumberDays1 = 0;
    		int largeChangeNumberDays2 = 0;
    
    		//DAYS FOR FIRST TREND SLOPE (A)
    		var t8 = randomNumber(1, 6);
    
    		//RANDOMIZE SIGN OF FIRST TREND SLOPE (A)
    		if (random.nextFloat() <= 0.5) {
    			marketTrendSlope = -marketTrendSlope;
    		}
    
    		// INITIALIZE CASH ASSETS:C
    		double cashAssets = 10000;
    		boolean largeChange1 = false;
    		boolean largeChange2 = false;
    		double tmpNyseAverage;
    		double nyseAverage = 0;
    		boolean inProgress = true;
    		var firstRound = true;
    
    		while (inProgress) {
    
    			/* Original documentation:
    			RANDOMLY PRODUCE NEW STOCK VALUES BASED ON PREVIOUS DAY'S VALUES
    			N1,N2 ARE RANDOM NUMBERS OF DAYS WHICH RESPECTIVELY
    			DETERMINE WHEN STOCK I1 WILL INCREASE 10 PTS. AND STOCK
    			I2 WILL DECREASE 10 PTS.
    			IF N1 DAYS HAVE PASSED, PICK AN I1, SET E1, DETERMINE NEW N1
    			*/
    			int randomStockIndex1 = 0;
    			int randomStockIndex2 = 0;
    
    			if (largeChangeNumberDays1 <= 0) {
    				randomStockIndex1 = randomNumber(0, stocks.size());
    				largeChangeNumberDays1 = randomNumber(1, 6);
    				largeChange1 = true;
    			}
    			if (largeChangeNumberDays2 <= 0) {
    				randomStockIndex2 = randomNumber(0, stocks.size());
    				largeChangeNumberDays2 = randomNumber(1, 6);
    				largeChange2 = true;
    			}
    			adjustAllStockValues(stocks, largeChange1, largeChange2, marketTrendSlope, stocks.get(randomStockIndex1), stocks.get(randomStockIndex2));
    
    			//reset largeChange flags
    			largeChange1 = false;
    			largeChange2 = false;
    			largeChangeNumberDays1--;
    			largeChangeNumberDays2--;
    
    			//AFTER T8 DAYS RANDOMLY CHANGE TREND SIGN AND SLOPE
    			t8 = t8 - 1;
    			if (t8 < 1) {
    				marketTrendSlope = newMarketTrendSlope();
    				t8 = randomNumber(1, 6);
    			}
    
    			//PRINT PORTFOLIO
    			printPortfolio(firstRound, stocks);
    
    			tmpNyseAverage = nyseAverage;
    			nyseAverage = 0;
    			double totalStockAssets = 0;
    			for (Stock stock : stocks) {
    				nyseAverage = nyseAverage + stock.getStockValue();
    				totalStockAssets = totalStockAssets + stock.getStockValue() * stock.getPortfolioContents();
    			}
    			nyseAverage = Math.floor(100 * (nyseAverage / 5) + .5) / 100f;
    			double nyseAverageChange = Math.floor((nyseAverage - tmpNyseAverage) * 100 + .5) / 100f;
    
    			// TOTAL ASSETS:D
    			double assets = totalStockAssets + cashAssets;
    			if (firstRound) {
    				System.out.printf("\n\nNEW YORK STOCK EXCHANGE AVERAGE: %.2f", nyseAverage);
    			} else {
    				System.out.printf("\n\nNEW YORK STOCK EXCHANGE AVERAGE: %.2f NET CHANGE %.2f", nyseAverage, nyseAverageChange);
    			}
    
    			totalStockAssets = Math.floor(100 * totalStockAssets + 0.5) / 100d;
    			System.out.printf("\n\nTOTAL STOCK ASSETS ARE   $ %.2f", totalStockAssets);
           		cashAssets = Math.floor(100 * cashAssets + 0.5) / 100d;
    			System.out.printf("\nTOTAL CASH ASSETS ARE    $ %.2f", cashAssets);
    			assets = Math.floor(100 * assets + .5) / 100d;
    			System.out.printf("\nTOTAL ASSETS ARE         $ %.2f\n", assets);
    
    			if (!firstRound) {
    				System.out.print("\nDO YOU WISH TO CONTINUE (YES-TYPE 1, NO-TYPE 0)? ");
    				var newCycle = readANumber(scan);
    				if (newCycle < 1) {
    					System.out.println("HOPE YOU HAD FUN!!");
    					inProgress = false;
    				}
    			}
    
    			if (inProgress) {
    				boolean validTransaction = false;
    				//    TOTAL DAY'S PURCHASES IN $:P5
    				double totalDaysPurchases = 0;
    				//    TOTAL DAY'S SALES IN $:S5
    				double totalDaysSales = 0;
    				double tmpCashAssets;
    				while (!validTransaction) {
    					//INPUT TRANSACTIONS
    					readStockTransactions(stocks, scan);
    					totalDaysPurchases = 0;
    					totalDaysSales = 0;
    
    					validTransaction = true;
    					for (Stock stock : stocks) {
    						stock.setTransactionQuantity(Math.floor(stock.getTransactionQuantity() + 0.5));
    						if (stock.getTransactionQuantity() > 0) {
    							totalDaysPurchases = totalDaysPurchases + stock.getTransactionQuantity() * stock.getStockValue();
    						} else {
    							totalDaysSales = totalDaysSales - stock.getTransactionQuantity() * stock.getStockValue();
    							if (-stock.getTransactionQuantity() > stock.getPortfolioContents()) {
    								System.out.println("YOU HAVE OVERSOLD A STOCK; TRY AGAIN.");
    								validTransaction = false;
    								break;
    							}
    						}
    					}
    
    					//TOTAL VALUE OF TRANSACTIONS:T5
    					totalValueOfTransactions = totalDaysPurchases + totalDaysSales;
    					// BROKERAGE FEE:B5
    					var brokerageFee = Math.floor(0.01 * totalValueOfTransactions * 100 + .5) / 100d;
    					// CASH ASSETS=OLD CASH ASSETS-TOTAL PURCHASES
    					//-BROKERAGE FEES+TOTAL SALES:C5
    					tmpCashAssets = cashAssets - totalDaysPurchases - brokerageFee + totalDaysSales;
    					if (tmpCashAssets < 0) {
    						System.out.printf("\nYOU HAVE USED $%.2f MORE THAN YOU HAVE.", -tmpCashAssets);
    						validTransaction = false;
    					} else {
    						cashAssets = tmpCashAssets;
    					}
    				}
    
    				// CALCULATE NEW PORTFOLIO
    				for (Stock stock : stocks) {
    					stock.setPortfolioContents(stock.getPortfolioContents() + stock.getTransactionQuantity());
    				}
    
    				firstRound = false;
    			}
    
    		}
    	}
    
    	/**
    	 * Random int between lowerBound(inclusive) and upperBound(exclusive)
    	 */
    	private static int randomNumber(int lowerBound, int upperBound) {
    		return random.nextInt((upperBound - lowerBound)) + lowerBound;
    	}
    
    	private static double newMarketTrendSlope() {
    		return randomlyChangeTrendSignAndSlopeAndDuration();
    	}
    
    	private static void printPortfolio(boolean firstRound, List stocks) {
    		//BELL RINGING-DIFFERENT ON MANY COMPUTERS
    		if (firstRound) {
    			System.out.printf("%n%-30s\t%12s\t%12s", "STOCK", "INITIALS", "PRICE/SHARE");
    			for (Stock stock : stocks) {
    				System.out.printf("%n%-30s\t%12s\t%12.2f ------ %12.2f", stock.getStockName(), stock.getStockCode(),
    						stock.getStockValue(), stock.getChangeStockValue());
    			}
    			System.out.println("");
    		} else {
    			System.out.println("\n**********     END OF DAY'S TRADING     **********\n\n");
    			System.out.printf("%n%-12s\t%-12s\t%-12s\t%-12s\t%-20s", "STOCK", "PRICE/SHARE",
    					"HOLDINGS", "VALUE", "NET PRICE CHANGE");
    			for (Stock stock : stocks) {
    				System.out.printf("%n%-12s\t%-12.2f\t%-12.0f\t%-12.2f\t%-20.2f",
    						stock.getStockCode(), stock.getStockValue(), stock.getPortfolioContents(),
    						stock.getStockValue() * stock.getPortfolioContents(), stock.getChangeStockValue());
    			}
    		}
    	}
    
    	private static void readStockTransactions(List stocks, Scanner scan) {
    		System.out.println("\n\nWHAT IS YOUR TRANSACTION IN");
    		for (Stock stock : stocks) {
    			System.out.printf("%s? ", stock.getStockCode());
    
    			stock.setTransactionQuantity(readANumber(scan));
    		}
    	}
    
    	private static int readANumber(Scanner scan) {
    		int choice = 0;
    
    		boolean validInput = false;
    		while (!validInput) {
    			try {
    				choice = scan.nextInt();
    				validInput = true;
    			} catch (InputMismatchException ex) {
    				System.out.println("!NUMBER EXPECTED - RETRY INPUT LINE");
    			} finally {
    				scan.nextLine();
    			}
    		}
    
    		return choice;
    	}
    
    	private static void adjustAllStockValues(List stocks, boolean largeChange1,
    			boolean largeChange2,
    			double marketTrendSlope,
    			Stock stockForLargeChange1, Stock stockForLargeChange2
    	) {
    		//LOOP THROUGH ALL STOCKS
    		for (Stock stock : stocks) {
    			double smallChange = random.nextFloat();
    
    			if (smallChange <= 0.25) {
    				smallChange = 0.25;
    			} else if (smallChange <= 0.5) {
    				smallChange = 0.5;
    			} else if (smallChange <= 0.75) {
    				smallChange = 0.75;
    			} else {
    				smallChange = 0;
    			}
    
    			//BIG CHANGE CONSTANT:W3  (SET TO ZERO INITIALLY)
    			var bigChange = 0;
    			if (largeChange1) {
    				if (stock.getStockCode().equals(stockForLargeChange1.getStockCode())) {
    					//ADD 10 PTS. TO THIS STOCK;  RESET E1
    					bigChange = 10;
    				}
    			}
    
    			if (largeChange2) {
    				if (stock.getStockCode().equals(stockForLargeChange2.getStockCode())) {
    					//SUBTRACT 10 PTS. FROM THIS STOCK;  RESET E2
    					bigChange = bigChange - 10;
    				}
    			}
    
    			stock.setChangeStockValue(Math.floor(marketTrendSlope * stock.stockValue) + smallChange +
    					Math.floor(3 - 6 * random.nextFloat() + .5) + bigChange);
    			stock.setChangeStockValue(Math.floor(100 * stock.getChangeStockValue() + .5) / 100d);
    			stock.stockValue += stock.getChangeStockValue();
    
    			if (stock.stockValue > 0) {
    				stock.stockValue = Math.floor(100 * stock.stockValue + 0.5) / 100d;
    			} else {
    				stock.setChangeStockValue(0);
    				stock.stockValue = 0;
    			}
    		}
    	}
    
    	private static double randomlyChangeTrendSignAndSlopeAndDuration() {
    		// RANDOMLY CHANGE TREND SIGN AND SLOPE (A), AND DURATION
    		var newTrend = Math.floor((random.nextFloat() / 10) * 100 + .5) / 100d;
    		var slopeSign = random.nextFloat();
    		if (slopeSign > 0.5) {
    			newTrend = -newTrend;
    		}
    		return newTrend;
    	}
    
    	private static List initStocks() {
    		List stocks = new ArrayList<>();
    		stocks.add(new Stock(100, "INT. BALLISTIC MISSILES", "IBM"));
    		stocks.add(new Stock(85, "RED CROSS OF AMERICA", "RCA"));
    		stocks.add(new Stock(150, "LICHTENSTEIN, BUMRAP & JOKE", "LBJ"));
    		stocks.add(new Stock(140, "AMERICAN BANKRUPT CO.", "ABC"));
    		stocks.add(new Stock(110, "CENSURED BOOKS STORE", "CBS"));
    		return stocks;
    	}
    
    	private static void printGameHelp(Scanner scan) {
    		System.out.print("DO YOU WANT THE INSTRUCTIONS (YES-TYPE 1, NO-TYPE 0) ? ");
    		int choice = scan.nextInt();
    		if (choice >= 1) {
    			System.out.println("");
    			System.out.println("THIS PROGRAM PLAYS THE STOCK MARKET.  YOU WILL BE GIVEN");
    			System.out.println("$10,000 AND MAY BUY OR SELL STOCKS.  THE STOCK PRICES WILL");
    			System.out.println("BE GENERATED RANDOMLY AND THEREFORE THIS MODEL DOES NOT");
    			System.out.println("REPRESENT EXACTLY WHAT HAPPENS ON THE EXCHANGE.  A TABLE");
    			System.out.println("OF AVAILABLE STOCKS, THEIR PRICES, AND THE NUMBER OF SHARES");
    			System.out.println("IN YOUR PORTFOLIO WILL BE PRINTED.  FOLLOWING THIS, THE");
    			System.out.println("INITIALS OF EACH STOCK WILL BE PRINTED WITH A QUESTION");
    			System.out.println("MARK.  HERE YOU INDICATE A TRANSACTION.  TO BUY A STOCK");
    			System.out.println("TYPE +NNN, TO SELL A STOCK TYPE -NNN, WHERE NNN IS THE");
    			System.out.println("NUMBER OF SHARES.  A BROKERAGE FEE OF 1% WILL BE CHARGED");
    			System.out.println("ON ALL TRANSACTIONS.  NOTE THAT IF A STOCK'S VALUE DROPS");
    			System.out.println("TO ZERO IT MAY REBOUND TO A POSITIVE VALUE AGAIN.  YOU");
    			System.out.println("HAVE $10,000 TO INVEST.  USE INTEGERS FOR ALL YOUR INPUTS.");
    			System.out.println("(NOTE:  TO GET A 'FEEL' FOR THE MARKET RUN FOR AT LEAST");
    			System.out.println("10 DAYS)");
    			System.out.println("-----GOOD LUCK!-----");
    		}
    		System.out.println("\n\n");
    	}
    
    	private static void printIntro() {
    		System.out.println("                                STOCK MARKET");
    		System.out.println("              CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
    		System.out.println("\n\n");
    	}
    
    	/**
    	 * Stock class also storing the stock information and other related information for simplicity
    	 */
    	private static class Stock {
    
    		private final String stockName;
    		private final String stockCode;
    		private double stockValue;
    		private double portfolioContents = 0;
    		private double transactionQuantity = 0;
    		private double changeStockValue = 0;
    
    		public Stock(double stockValue, String stockName, String stockCode) {
    			this.stockValue = stockValue;
    			this.stockName = stockName;
    			this.stockCode = stockCode;
    		}
    
    		public String getStockName() {
    			return stockName;
    		}
    
    		public String getStockCode() {
    			return stockCode;
    		}
    
    		public double getStockValue() {
    			return stockValue;
    		}
    
    		public double getPortfolioContents() {
    			return portfolioContents;
    		}
    
    		public void setPortfolioContents(double portfolioContents) {
    			this.portfolioContents = portfolioContents;
    		}
    
    		public double getTransactionQuantity() {
    			return transactionQuantity;
    		}
    
    		public void setTransactionQuantity(double transactionQuantity) {
    			this.transactionQuantity = transactionQuantity;
    		}
    
    		public double getChangeStockValue() {
    			return changeStockValue;
    		}
    
    		public void setChangeStockValue(double changeStockValue) {
    			this.changeStockValue = changeStockValue;
    		}
    
    		@Override
    		public String toString() {
    			return "Stock{" +
    					"stockValue=" + stockValue +
    					", stockCode='" + stockCode + '\'' +
    					", portfolioContents=" + portfolioContents +
    					", transactionQuantity=" + transactionQuantity +
    					", changeStockValue=" + changeStockValue +
    					'}';
    		}
    	}
    
    }
    
    
    ================================================
    FILE: 83_Stock_Market/javascript/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells)
    
    
    ================================================
    FILE: 83_Stock_Market/javascript/stockmarket.html
    ================================================
    
    
    STOCKMARKET
    
    
    
    
    
    
    
    
    
    
    ================================================
    FILE: 83_Stock_Market/javascript/stockmarket.js
    ================================================
    // STOCKMARKET
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    var sa = [];
    var pa = [];
    var za = [];
    var ca = [];
    var i1;
    var n1;
    var e1;
    var i2;
    var n2;
    var e2;
    var x1;
    var w3;
    var t8;
    var a;
    var s4;
    
    // New stock values - subroutine
    function randomize_initial()
    {
        // RANDOMLY PRODUCE NEW STOCK VALUES BASED ON PREVIOUS
        // DAY'S VALUES
        // N1,N2 ARE RANDOM NUMBERS OF DAYS WHICH RESPECTIVELY
        // DETERMINE WHEN STOCK I1 WILL INCREASE 10 PTS. AND STOCK
        // I2 WILL DECREASE 10 PTS.
        // IF N1 DAYS HAVE PASSED, PICK AN I1, SET E1, DETERMINE NEW N1
        if (n1 <= 0) {
            i1 = Math.floor(4.99 * Math.random() + 1);
            n1 = Math.floor(4.99 * Math.random() + 1);
            e1 = 1;
        }
        // IF N2 DAYS HAVE PASSED, PICK AN I2, SET E2, DETERMINE NEW N2
        if (n2 <= 0) {
            i2 = Math.floor(4.99 * Math.random() + 1);
            n2 = Math.floor(4.99 * Math.random() + 1);
            e2 = 1;
        }
        // DEDUCT ONE DAY FROM N1 AND N2
        n1--;
        n2--;
        // LOOP THROUGH ALL STOCKS
        for (i = 1; i <= 5; i++) {
            x1 = Math.random();
            if (x1 < 0.25) {
                x1 = 0.25;
            } else if (x1 < 0.5) {
                x1 = 0.5;
            } else if (x1 < 0.75) {
                x1 = 0.75;
            } else {
                x1 = 0.0;
            }
            // BIG CHANGE CONSTANT:W3  (SET TO ZERO INITIALLY)
            w3 = 0;
            if (e1 >= 1 && Math.floor(i1 + 0.5) == Math.floor(i + 0.5)) {
                // ADD 10 PTS. TO THIS STOCK;  RESET E1
                w3 = 10;
                e1 = 0;
            }
            if (e2 >= 1 && Math.floor(i2 + 0.5) == Math.floor(i + 0.5)) {
                // SUBTRACT 10 PTS. FROM THIS STOCK;  RESET E2
                w3 -= 10;
                e2 = 0;
            }
            // C(I) IS CHANGE IN STOCK VALUE
            ca[i] = Math.floor(a * sa[i]) + x1 + Math.floor(3 - 6 * Math.random() + 0.5) + w3;
            ca[i] = Math.floor(100 * ca[i] + 0.5) / 100;
            sa[i] += ca[i];
            if (sa[i] <= 0) {
                ca[i] = 0;
                sa[i] = 0;
            } else {
                sa[i] = Math.floor(100 * sa[i] + 0.5) / 100;
            }
        }
        // AFTER T8 DAYS RANDOMLY CHANGE TREND SIGN AND SLOPE
        if (--t8 < 1) {
            // RANDOMLY CHANGE TREND SIGN AND SLOPE (A), AND DURATION
            // OF TREND (T8)
            t8 = Math.floor(4.99 * Math.random() + 1);
            a = Math.floor((Math.random() / 10) * 100 + 0.5) / 100;
            s4 = Math.random();
            if (s4 > 0.5)
                a = -a;
        }
    }
    
    // Main program
    async function main()
    {
        print(tab(30) + "STOCK MARKET\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        // STOCK MARKET SIMULATION     -STOCK-
        // REVISED 8/18/70 (D. PESSEL, L. BRAUN, C. LOSIK)
        // IMP VRBLS: A-MRKT TRND SLP; B5-BRKRGE FEE; C-TTL CSH ASSTS;
        // C5-TTL CSH ASSTS (TEMP); C(I)-CHNG IN STK VAL; D-TTL ASSTS;
        // E1,E2-LRG CHNG MISC; I-STCK #; I1,I2-STCKS W LRG CHNG;
        // N1,N2-LRG CHNG DAY CNTS; P5-TTL DAYS PRCHSS; P(I)-PRTFL CNTNTS;
        // Q9-NEW CYCL?; S4-SGN OF A; S5-TTL DYS SLS; S(I)-VALUE/SHR;
        // T-TTL STCK ASSTS; T5-TTL VAL OF TRNSCTNS;
        // W3-LRG CHNG; X1-SMLL CHNG(<$1); Z4,Z5,Z6-NYSE AVE.; Z(I)-TRNSCT
        // SLOPE OF MARKET TREND:A  (SAME FOR ALL STOCKS)
        x = 1;
        a = Math.floor(Math.random() / 10 * 100 + 0.5) / 100;
        t5 = 0;
        x9 = 0;
        n1 = 0;
        n2 = 0;
        e1 = 0;
        e2 = 0;
        // INTRODUCTION
        print("DO YOU WANT THE INSTRUCTIONS (YES-TYPE 1, NO-TYPE 0)");
        z9 = parseInt(await input());
        print("\n");
        print("\n");
        if (z9 >= 1) {
            print("THIS PROGRAM PLAYS THE STOCK MARKET.  YOU WILL BE GIVEN\n");
            print("$10,000 AND MAY BUY OR SELL STOCKS.  THE STOCK PRICES WILL\n");
            print("BE GENERATED RANDOMLY AND THEREFORE THIS MODEL DOES NOT\n");
            print("REPRESENT EXACTLY WHAT HAPPENS ON THE EXCHANGE.  A TABLE\n");
            print("OF AVAILABLE STOCKS, THEIR PRICES, AND THE NUMBER OF SHARES\n");
            print("IN YOUR PORTFOLIO WILL BE PRINTED.  FOLLOWING THIS, THE\n");
            print("INITIALS OF EACH STOCK WILL BE PRINTED WITH A QUESTION\n");
            print("MARK.  HERE YOU INDICATE A TRANSACTION.  TO BUY A STOCK\n");
            print("TYPE +NNN, TO SELL A STOCK TYPE -NNN, WHERE NNN IS THE\n");
            print("NUMBER OF SHARES.  A BROKERAGE FEE OF 1% WILL BE CHARGED\n");
            print("ON ALL TRANSACTIONS.  NOTE THAT IF A STOCK'S VALUE DROPS\n");
            print("TO ZERO IT MAY REBOUND TO A POSITIVE VALUE AGAIN.  YOU\n");
            print("HAVE $10,000 TO INVEST.  USE INTEGERS FOR ALL YOUR INPUTS.\n");
            print("(NOTE:  TO GET A 'FEEL' FOR THE MARKET RUN FOR AT LEAST\n");
            print("10 DAYS)\n");
            print("-----GOOD LUCK!-----\n");
        }
        // GENERATION OF STOCK TABLE: INPUT REQUESTS
        // INITIAL STOCK VALUES
        sa[1] = 100;
        sa[2] = 85;
        sa[3] = 150;
        sa[4] = 140;
        sa[5] = 110;
        // INITIAL T8 - # DAYS FOR FIRST TREND SLOPE (A)
        t8 = Math.floor(4.99 * Math.random() + 1);
        // RANDOMIZE SIGN OF FIRST TREND SLOPE (A)
        if (Math.random() <= 0.5)
            a -= a;
        // RANDOMIZE INITIAL VALUES
        randomize_initial();
        // INITIAL PORTFOLIO CONTENTS
        for (i = 1; i <= 5; i++) {
            pa[i] = 0;
            za[i] = 0;
        }
        print("\n");
        print("\n");
        // INITIALIZE CASH ASSETS:C
        c = 10000;
        z5 = 0;
        // PRINT INITIAL PORTFOLIO
        print("STOCK\t \t\t\tINITIALS\tPRICE/SHARE\n");
        print("INT. BALLISTIC MISSILES\t\t  IBM\t\t" + sa[1] + "\n");
        print("RED CROSS OF AMERICA\t\t  RCA\t\t" + sa[2] + "\n");
        print("LICHTENSTEIN, BUMRAP & JOKE\t  LBJ\t\t" + sa[3] + "\n");
        print("AMERICAN BANKRUPT CO.\t\t  ABC\t\t" + sa[4] + "\n");
        print("CENSURED BOOKS STORE\t\t  CBS\t\t" + sa[5] + "\n");
        while (1) {
            print("\n");
            // NYSE AVERAGE:Z5; TEMP. VALUE:Z4; NET CHANGE:Z6
            z4 = z5;
            z5 = 0;
            t = 0;
            for (i = 1; i <= 5; i++) {
                z5 += sa[i];
                t += sa[i] * pa[i];
            }
            z5 = Math.floor(100 * (z5 / 5) + 0.5) / 100;
            z6 = Math.floor((z5 - z4) * 100 + 0.5) / 100;
            // TOTAL ASSETS:D
            d = t + c;
            if (x9 <= 0) {
                print("NEW YORK STOCK EXCHANGE AVERAGE: " + z5 + "\n");
            } else {
                print("NEW YORK STOCK EXCHANGE AVERAGE: " + z5 + " NET CHANGE " + z6 + "\n");
            }
            print("\n");
            t = Math.floor(100 * t + 0.5) / 100;
            print("TOTAL STOCK ASSETS ARE   $" + t + "\n");
            c = Math.floor(100 * c + 0.5) / 100;
            print("TOTAL CASH ASSETS ARE    $" + c + "\n");
            d = Math.floor(100 * d + 0.5) / 100;
            print("TOTAL ASSETS ARE         $" + d + "\n");
            print("\n");
            if (x9 != 0) {
                print("DO YOU WISH TO CONTINUE (YES-TYPE 1, NO-TYPE 0)");
                q9 = parseInt(await input());
                if (q9 < 1) {
                    print("HOPE YOU HAD FUN!!\n");
                    return;
                }
            }
            // INPUT TRANSACTIONS
            while (1) {
                print("WHAT IS YOUR TRANSACTION IN\n");
                print("IBM");
                za[1] = parseInt(await input());
                print("RCA");
                za[2] = parseInt(await input());
                print("LBJ");
                za[3] = parseInt(await input());
                print("ABC");
                za[4] = parseInt(await input());
                print("CBS");
                za[5] = parseInt(await input());
                print("\n");
                // TOTAL DAY'S PURCHASES IN $:P5
                p5 = 0;
                // TOTAL DAY'S SALES IN $:S5
                s5 = 0;
                for (i = 1; i <= 5; i++) {
                    za[i] = Math.floor(za[i] + 0.5);
                    if (za[i] > 0) {
                        p5 += za[i] * sa[i];
                    } else {
                        s5 -= za[i] * sa[i];
                        if (-za[i] > pa[i]) {
                            print("YOU HAVE OVERSOLD A STOCK; TRY AGAIN.\n");
                            break;
                        }
                    }
                }
                if (i <= 5)
                    contine;
                // TOTAL VALUE OF TRANSACTIONS:T5
                t5 = p5 + s5;
                // BROKERAGE FEE:B5
                b5 = Math.floor(0.01 * t5 * 100 + 0.5) / 100;
                // CASH ASSETS=OLD CASH ASSETS-TOTAL PURCHASES
                // -BROKERAGE FEES+TOTAL SALES:C5
                c5 = c - p5 - b5 + s5;
                if (c5 < 0) {
                    print("YOU HAVE USED $" + (-c5) + " MORE THAN YOU HAVE.\n");
                    continue;
                }
                break;
            }
            c = c5;
            // CALCULATE NEW PORTFOLIO
            for (i = 1; i <= 5; i++) {
                pa[i] += za[i];
            }
            // CALCULATE NEW STOCK VALUES
            randomize_initial();
            // PRINT PORTFOLIO
            // BELL RINGING-DIFFERENT ON MANY COMPUTERS
            print("\n");
            print("**********     END OF DAY'S TRADING     **********\n");
            print("\n");
            print("\n");
            if (x9 >= 1) ;
            print("STOCK\tPRICE/SHARE\tHOLDINGS\tVALUE\tNET PRICE CHANGE\n");
            print("IBM\t" + sa[1] + "\t\t" + pa[1] + "\t\t" + sa[1] * pa[1] + "\t" + ca[1] + "\n");
            print("RCA\t" + sa[2] + "\t\t" + pa[2] + "\t\t" + sa[2] * pa[2] + "\t" + ca[2] + "\n");
            print("LBJ\t" + sa[3] + "\t\t" + pa[3] + "\t\t" + sa[3] * pa[3] + "\t" + ca[3] + "\n");
            print("ABC\t" + sa[4] + "\t\t" + pa[4] + "\t\t" + sa[4] * pa[4] + "\t" + ca[4] + "\n");
            print("CBS\t" + sa[5] + "\t\t" + pa[5] + "\t\t" + sa[5] * pa[5] + "\t" + ca[5] + "\n");
            x9 = 1;
            print("\n");
            print("\n");
        }
    }
    
    main();
    
    
    ================================================
    FILE: 83_Stock_Market/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 83_Stock_Market/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 83_Stock_Market/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 83_Stock_Market/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 83_Stock_Market/python/Stock_Market.py
    ================================================
    import random
    from typing import Any, Dict, List
    
    
    class Stock_Market:
        def __init__(self) -> None:
            # Hard Coded Names
            short_names = ["IBM", "RCA", "LBJ", "ABC", "CBS"]
            full_names = [
                "INT. BALLISTIC MISSLES",
                "RED CROSS OF AMERICA",
                "LICHTENSTEIN, BUMRAP & JOKE",
                "AMERICAN BANKRUPT CO.",
                "CENSURED BOOKS STORE",
            ]
    
            # Initializing Dictionary to hold all the information systematically
            self.data: Dict[str, Any] = {}
            for sn, fn in zip(short_names, full_names):
                # A dictionary for each stock
                temp = {"Name": fn, "Price": None, "Holdings": 0}
                # Nested outer dictionary for all stocks
                self.data[sn] = temp
    
            # Initializing Randomly generated initial prices
            for stock in self.data.values():
                stock["Price"] = round(random.uniform(80, 120), 2)  # Price b/w 60 and 120
    
            # Initialize Assets
            self.cash_assets = 10000
            self.stock_assets = 0
    
        def total_assets(self) -> float:
            return self.cash_assets + self.stock_assets
    
        def _generate_day_change(self) -> None:
            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()
            for stock, change in zip(self.data.values(), self.changes):
                stock["Price"] = round(stock["Price"] + (change / 100) * stock["Price"], 2)
    
        def print_exchange_average(self) -> None:
    
            sum = 0
            for stock in self.data.values():
                sum += stock["Price"]
    
            print(f"\nNEW YORK STOCK EXCHANGE AVERAGE: ${sum / 5:.2f}")
    
        def get_average_change(self) -> float:
            sum: float = 0
            for change in self.changes:
                sum += change
    
            return round(sum / 5, 2)
    
        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(f'{data["Name"]}\t\t\t{stock}\t\t{data["Price"]}')
                else:
                    print(f'{data["Name"]}\t\t{stock}\t\t{data["Price"]}')
    
            self.print_exchange_average()
            self.print_assets()
    
        def take_inputs(self) -> List[str]:
            print("\nWHAT IS YOUR TRANSACTION IN")
            flag = False
            while not flag:
                new_holdings = []
                for stock in self.data.keys():
                    try:
                        new_holdings.append(int(input(f"{stock}? ")))
                    except Exception:
                        print("\nINVALID ENTRY, TRY AGAIN\n")
                        break
                if len(new_holdings) == 5:
                    flag = self._check_transaction(new_holdings)
    
            return new_holdings  # type: ignore
    
        def print_trading_day(self) -> None:
    
            print("STOCK\tPRICE/SHARE\tHOLDINGS\tNET. Value\tPRICE CHANGE")
            for stock, data, change in zip(
                self.data.keys(), self.data.values(), self.changes
            ):
                value = data["Price"] * data["Holdings"]
                print(
                    "{}\t{}\t\t{}\t\t{:.2f}\t\t{}".format(
                        stock, data["Price"], data["Holdings"], value, change
                    )
                )
    
        def update_cash_assets(self, new_holdings) -> None:
            sell = 0
            buy = 0
            for stock, holding in zip(self.data.values(), new_holdings):
                if holding > 0:
                    buy += stock["Price"] * holding
    
                elif holding < 0:
                    sell += stock["Price"] * abs(holding)
    
            self.cash_assets = self.cash_assets + sell - buy
    
        def update_stock_assets(self) -> None:
            sum = 0
            for data in self.data.values():
                sum += data["Price"] * data["Holdings"]
    
            self.stock_assets = round(sum, 2)
    
        def print_assets(self) -> None:
            print(f"\nTOTAL STOCK ASSETS ARE: ${self.stock_assets:.2f}")
            print(f"TOTAL CASH ASSETS ARE: ${self.cash_assets:.2f}")
            print(f"TOTAL ASSETS ARE: ${self.total_assets():.2f}")
    
        def _check_transaction(self, new_holdings) -> bool:
            sum = 0
            for stock, holding in zip(self.data.values(), new_holdings):
                if holding > 0:
                    sum += stock["Price"] * holding
    
                elif holding < 0:
                    if abs(holding) > stock["Holdings"]:
                        print("\nYOU HAVE OVERSOLD SOME STOCKS, TRY AGAIN\n")
                        return False
    
            if sum > self.cash_assets:
                print(
                    "\nYOU HAVE USED ${:.2f} MORE THAN YOU HAVE, TRY AGAIN\n".format(
                        sum - self.cash_assets
                    )
                )
                return False
    
            return True
    
        def update_holdings(self, new_holdings) -> None:
            for stock, new_holding in zip(self.data.values(), new_holdings):
                stock["Holdings"] += new_holding
    
    
    def print_instruction() -> None:
    
        print(
            """
    THIS PROGRAM PLAYS THE STOCK MARKET.  YOU WILL BE GIVEN
    $10,000 AND MAY BUY OR SELL STOCKS.  THE STOCK PRICES WILL
    BE GENERATED RANDOMLY AND THEREFORE THIS MODEL DOES NOT
    REPRESENT EXACTLY WHAT HAPPENS ON THE EXCHANGE.  A TABLE
    OF AVAILABLE STOCKS, THEIR PRICES, AND THE NUMBER OF SHARES
    IN YOUR PORTFOLIO WILL BE PRINTED.  FOLLOWING THIS, THE
    INITIALS OF EACH STOCK WILL BE PRINTED WITH A QUESTION
    MARK.  HERE YOU INDICATE A TRANSACTION.  TO BUY A STOCK
    TYPE +NNN, TO SELL A STOCK TYPE -NNN, WHERE NNN IS THE
    NUMBER OF SHARES.  A BROKERAGE FEE OF 1% WILL BE CHARGED
    ON ALL TRANSACTIONS.  NOTE THAT IF A STOCK'S VALUE DROPS
    TO ZERO IT MAY REBOUND TO A POSITIVE VALUE AGAIN.  YOU
    HAVE $10,000 TO INVEST.  USE INTEGERS FOR ALL YOUR INPUTS.
    (NOTE:  TO GET A 'FEEL' FOR THE MARKET RUN FOR AT LEAST
    10 DAYS)
              ------------GOOD LUCK!------------\n
        """
        )
    
    
    def main() -> None:
        print("\t\t      STOCK MARKET")
        help = input("\nDO YOU WANT INSTRUCTIONS(YES OR NO)? ")
    
        # Printing Instruction
        if help.lower() == "yes":
            print_instruction()
    
        # Initialize Game
        Game = Stock_Market()
    
        # Do first day
        Game.print_first_day()
        new_holdings = Game.take_inputs()
        Game.update_holdings(new_holdings)
        Game.update_cash_assets(new_holdings)
        print("\n------------END OF TRADING DAY--------------\n")
    
        response = 1
        while response == 1:
    
            # Simulate a DAY
            Game.update_prices()
            Game.print_trading_day()
            Game.print_exchange_average()
            Game.update_stock_assets()
            Game.print_assets()
    
            response = int(input("\nDO YOU WISH TO CONTINUE (YES-TYPE 1, NO-TYPE 0)? "))
            if response == 0:
                break
    
            new_holdings = Game.take_inputs()
            Game.update_holdings(new_holdings)
            Game.update_cash_assets(new_holdings)
            print("\n------------END OF TRADING DAY--------------\n")
    
        print("\nHOPE YOU HAD FUN!!!!")
        input()
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 83_Stock_Market/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 83_Stock_Market/stockmarket.bas
    ================================================
    10 PRINT TAB(30);"STOCK MARKET"
    20 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    30 PRINT: PRINT: PRINT
    100 REM STOCK MARKET SIMULATION     -STOCK-
    101 REM REVISED 8/18/70 (D. PESSEL, L. BRAUN, C. LOSIK)
    102 REM IMP VRBLS: A-MRKT TRND SLP; B5-BRKRGE FEE; C-TTL CSH ASSTS;
    103 REM C5-TTL CSH ASSTS (TEMP); C(I)-CHNG IN STK VAL; D-TTL ASSTS;
    104 REM E1,E2-LRG CHNG MISC; I-STCK #; I1,I2-STCKS W LRG CHNG;
    105 REM N1,N2-LRG CHNG DAY CNTS; P5-TTL DAYS PRCHSS; P(I)-PRTFL CNTNTS;
    106 REM Q9-NEW CYCL?; S4-SGN OF A; S5-TTL DYS SLS; S(I)-VALUE/SHR;
    107 REM T-TTL STCK ASSTS; T5-TTL VAL OF TRNSCTNS;
    108 REM W3-LRG CHNG; X1-SMLL CHNG(<$1); Z4,Z5,Z6-NYSE AVE.; Z(I)-TRNSCT
    110 DIM S(5),P(5),Z(5),C(5)
    112 REM SLOPE OF MARKET TREND:A  (SAME FOR ALL STOCKS)
    113 LET X=1
    114 LET A=INT((RND(X)/10)*100+.5)/100
    115 LET T5=0
    116 LET X9=0
    117 LET N1=0
    118 LET N2=0
    119 LET E1=0
    120 LET E2=0
    121 REM INTRODUCTION
    122 PRINT "DO YOU WANT THE INSTRUCTIONS (YES-TYPE 1, NO-TYPE 0)";
    123 INPUT Z9
    124 PRINT
    125 PRINT
    126 IF Z9<1 THEN 200
    130 PRINT "THIS PROGRAM PLAYS THE STOCK MARKET.  YOU WILL BE GIVEN"
    132 PRINT "$10,000 AND MAY BUY OR SELL STOCKS.  THE STOCK PRICES WILL"
    134 PRINT "BE GENERATED RANDOMLY AND THEREFORE THIS MODEL DOES NOT"
    135 PRINT "REPRESENT EXACTLY WHAT HAPPENS ON THE EXCHANGE.  A TABLE"
    136 PRINT "OF AVAILABLE STOCKS, THEIR PRICES, AND THE NUMBER OF SHARES"
    137 PRINT "IN YOUR PORTFOLIO WILL BE PRINTED.  FOLLOWING THIS, THE"
    138 PRINT "INITIALS OF EACH STOCK WILL BE PRINTED WITH A QUESTION"
    139 PRINT "MARK.  HERE YOU INDICATE A TRANSACTION.  TO BUY A STOCK"
    140 PRINT "TYPE +NNN, TO SELL A STOCK TYPE -NNN, WHERE NNN IS THE"
    141 PRINT "NUMBER OF SHARES.  A BROKERAGE FEE OF 1% WILL BE CHARGED"
    142 PRINT "ON ALL TRANSACTIONS.  NOTE THAT IF A STOCK'S VALUE DROPS"
    143 PRINT "TO ZERO IT MAY REBOUND TO A POSITIVE VALUE AGAIN.  YOU"
    144 PRINT "HAVE $10,000 TO INVEST.  USE INTEGERS FOR ALL YOUR INPUTS."
    145 PRINT "(NOTE:  TO GET A 'FEEL' FOR THE MARKET RUN FOR AT LEAST"
    146 PRINT "10 DAYS)"
    147 PRINT "-----GOOD LUCK!-----"
    200 REM GENERATION OF STOCK TABLE; INPUT REQUESTS
    210 REM INITIAL STOCK VALUES
    220 LET S(1)=100
    230 LET S(2)=85
    240 LET S(3)=150
    250 LET S(4)=140
    260 LET S(5)=110
    265 REM INITIAL T8 - # DAYS FOR FIRST TREND SLOPE (A)
    266 LET T8=INT(4.99*RND(X)+1)
    267 REM RANDOMIZE SIGN OF FIRST TREND SLOPE (A)
    268 IF RND(X)>.5 THEN 270
    269 LET A=-A
    270 REM RANDOMIZE INITIAL VALUES
    280 GOSUB 830
    285 REM INITIAL PORTFOLIO CONTENTS
    290 FOR I=1 TO 5
    300 LET P(I)=0
    305 LET Z(I)=0
    310 NEXT I
    320 PRINT
    330 PRINT
    333 REM INITIALIZE CASH ASSETS:C
    335 LET C=10000
    338 REM PRINT INITIAL PORTFOLIO
    340 PRINT "STOCK"," ","INITIALS","PRICE/SHARE"
    350 PRINT "INT. BALLISTIC MISSILES","  IBM",S(1)
    352 PRINT "RED CROSS OF AMERICA","  RCA",S(2)
    354 PRINT "LICHTENSTEIN, BUMRAP & JOKE","  LBJ",S(3)
    356 PRINT "AMERICAN BANKRUPT CO.","  ABC",S(4)
    358 PRINT "CENSURED BOOKS STORE","  CBS",S(5)
    360 PRINT
    361 REM NYSE AVERAGE:Z5; TEMP. VALUE:Z4; NET CHANGE:Z6
    363 LET Z4=Z5
    364 LET Z5=0
    365 LET T=0
    370 FOR I=1 TO 5
    375 LET Z5=Z5+S(I)
    380 LET T=T+S(I)*P(I)
    390 NEXT I
    391 LET Z5=INT(100*(Z5/5)+.5)/100
    392 LET Z6=INT((Z5-Z4)*100+.5)/100
    393 REM TOTAL ASSETS:D
    394 LET D=T+C
    395 IF X9>0 THEN 398
    396 PRINT "NEW YORK STOCK EXCHANGE AVERAGE: "Z5
    397 GOTO 399
    398 PRINT "NEW YORK STOCK EXCHANGE AVERAGE: "Z5"NET CHANGE"Z6
    399 PRINT
    400 LET T=INT(100*T+.5)/100
    401 PRINT "TOTAL STOCK ASSETS ARE   $";T
    403 LET C=INT(100*C+.5)/100
    405 PRINT "TOTAL CASH ASSETS ARE    $";C
    407 LET D=INT(100*D+.5)/100
    408 PRINT "TOTAL ASSETS ARE         $";D
    410 PRINT
    411 IF X9=0 THEN 416
    412 PRINT "DO YOU WISH TO CONTINUE (YES-TYPE 1, NO-TYPE 0)";
    413 INPUT Q9
    414 IF Q9<1 THEN 998
    416 REM INPUT TRANSACTIONS
    420 PRINT "WHAT IS YOUR TRANSACTION IN"
    430 PRINT "IBM";
    440 INPUT Z(1)
    450 PRINT "RCA";
    460 INPUT Z(2)
    470 PRINT "LBJ";
    480 INPUT Z(3)
    490 PRINT "ABC";
    500 INPUT Z(4)
    510 PRINT "CBS";
    520 INPUT Z(5)
    525 PRINT
    530 REM TOTAL DAY'S PURCHASES IN $:P5
    540 LET P5=0
    550 REM TOTAL DAY'S SALES IN $:S5
    560 LET S5=0
    570 FOR I=1 TO 5
    575 LET Z(I)=INT(Z(I)+.5)
    580 IF Z(I)<=0 THEN 610
    590 LET P5=P5+Z(I)*S(I)
    600 GOTO 620
    610 LET S5=S5-Z(I)*S(I)
    612 IF -Z(I)<=P(I) THEN 620
    614 PRINT "YOU HAVE OVERSOLD A STOCK; TRY AGAIN."
    616 GOTO 420
    620 NEXT I
    622 REM TOTAL VALUE OF TRANSACTIONS:T5
    625 LET T5=P5+S5
    630 REM BROKERAGE FEE:B5
    640 LET B5=INT(.01*T5*100+.5)/100
    650 REM CASH ASSETS=OLD CASH ASSETS-TOTAL PURCHASES
    652 REM -BROKERAGE FEES+TOTAL SALES:C5
    654 LET C5=C-P5-B5+S5
    656 IF C5>=0 THEN 674
    658 PRINT "YOU HAVE USED $"-C5" MORE THAN YOU HAVE."
    660 GOTO 420
    674 LET C=C5
    675 REM CALCULATE NEW PORTFOLIO
    680 FOR I=1 TO 5
    690 LET P(I)=P(I)+Z(I)
    700 NEXT I
    710 REM CALCULATE NEW STOCK VALUES
    720 GOSUB 830
    750 REM PRINT PORTFOLIO
    751 REM BELL RINGING-DIFFERENT ON MANY COMPUTERS
    755 PRINT
    756 PRINT "**********     END OF DAY'S TRADING     **********"
    757 PRINT
    758 PRINT
    759 IF X9<1 THEN 769
    769 PRINT "STOCK","PRICE/SHARE","HOLDINGS", "VALUE", "NET PRICE CHANGE"
    770 PRINT "IBM", S(1), P(1), S(1)*P(1), C(1)
    771 PRINT "RCA", S(2), P(2), S(2)*P(2), C(2)
    772 PRINT "LBJ", S(3), P(3), S(3)*P(3), C(3)
    773 PRINT "ABC", S(4), P(4), S(4)*P(4), C(4)
    774 PRINT "CBS", S(5), P(5), S(5)*P(5), C(5)
    775 LET X9=1
    780 PRINT
    790 PRINT
    810 GOTO 360
    829 REM NEW STOCK VALUES - SUBROUTINE
    830 REM RANDOMLY PRODUCE NEW STOCK VALUES BASED ON PREVIOUS
    831 REM DAY'S VALUES
    832 REM N1,N2 ARE RANDOM NUMBERS OF DAYS WHICH RESPECTIVELY
    833 REM DETERMINE WHEN STOCK I1 WILL INCREASE 10 PTS. AND STOCK
    834 REM I2 WILL DECREASE 10 PTS.
    840 REM IF N1 DAYS HAVE PASSED, PICK AN I1, SET E1, DETERMINE NEW N1
    841 IF N1>0 THEN 850
    845 LET I1=INT(4.99*RND(X)+1)
    846 LET N1=INT(4.99*RND(X)+1)
    847 LET E1=1
    850 REM IF N2 DAYS HAVE PASSED, PICK AN I2, SET E2, DETERMINE NEW N2
    851 IF N2>0 THEN 860
    855 LET I2=INT(4.99*RND(X)+1)
    856 LET N2=INT(4.99*RND(X)+1)
    857 LET E2=1
    860 REM DEDUCT ONE DAY FROM N1 AND N2
    861 LET N1=N1-1
    862 LET N2=N2-1
    890 REM LOOP THROUGH ALL STOCKS
    900 FOR I=1 TO 5
    910 LET X1=RND(X)
    915 IF X1>.25 THEN 920
    916 LET X1=.25
    917 GOTO 935
    920 IF X1>.5 THEN 925
    921 LET X1=.5
    922 GOTO 935
    925 IF X1>.75 THEN 930
    926 LET X1=.75
    927 GOTO 935
    930 LET X1=0.0
    931 REM BIG CHANGE CONSTANT:W3  (SET TO ZERO INITIALLY)
    935 LET W3=0
    936 IF E1<1 THEN 945
    937 IF INT(I1+.5)<>INT(I+.5) THEN 945
    938 REM ADD 10 PTS. TO THIS STOCK;  RESET E1
    939 LET W3=10
    943 LET E1=0
    945 IF E2<1 THEN 955
    947 IF INT(I2+.5)<>INT(I+.5) THEN 955
    948 REM SUBTRACT 10 PTS. FROM THIS STOCK;  RESET E2
    949 LET W3=W3-10
    953 LET E2=0
    954 REM C(I) IS CHANGE IN STOCK VALUE
    955 LET C(I)=INT(A*S(I))+X1+INT(3-6*RND(X)+.5)+W3
    956 LET C(I)=INT(100*C(I)+.5)/100
    957 LET S(I)=S(I)+C(I)
    960 IF S(I)>0 THEN 967
    964 LET C(I)=0
    965 LET S(I)=0
    966 GOTO 970
    967 LET S(I)=INT(100*S(I)+.5)/100
    970 NEXT I
    972 REM AFTER T8 DAYS RANDOMLY CHANGE TREND SIGN AND SLOPE
    973 LET T8=T8-1
    974 IF T8<1 THEN 985
    980 RETURN
    985 REM RANDOMLY CHANGE TREND SIGN AND SLOPE (A), AND DURATION
    986 REM OF TREND (T8)
    990 LET T8=INT(4.99*RND(X)+1)
    992 LET A=INT((RND(X)/10)*100+.5)/100
    993 LET S4=RND(X)
    994 IF S4<=.5 THEN 997
    995 LET A=-A
    997 RETURN
    998 PRINT "HOPE YOU HAD FUN!!"
    999 END
    
    
    ================================================
    FILE: 83_Stock_Market/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 83_Stock_Market/vbnet/StockMarket.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "StockMarket", "StockMarket.vbproj", "{BF9F62B1-4F0E-40F0-AE76-D1055F0A2F31}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{BF9F62B1-4F0E-40F0-AE76-D1055F0A2F31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{BF9F62B1-4F0E-40F0-AE76-D1055F0A2F31}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{BF9F62B1-4F0E-40F0-AE76-D1055F0A2F31}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{BF9F62B1-4F0E-40F0-AE76-D1055F0A2F31}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 83_Stock_Market/vbnet/StockMarket.vbproj
    ================================================
    
      
        Exe
        StockMarket
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 84_Super_Star_Trek/README.md
    ================================================
    ### Super Star Trek
    
    #### 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 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 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.”
    
    The relation between the Historical and Standard nomenclatures is shown in the simplified CG map below.
    
    |   | 1            | 2  | 3   | 4  | 5          | 6  | 7   | 8  |
    |---|--------------|----|-----|----|------------|----|-----|----|
    | 1 |    ANTARES   |    |     |    |   SIRIUS   |    |     |    |
    |   | I            | II | III | IV | I          | II | III | IV |
    | 2 |     RIGEL    |    |     |    |    DENEB   |    |     |    |
    |   | I            | II | III | IV | I          | II | III | IV |
    | 3 |    PROCYON   |    |     |    |   CAPELLA  |    |     |    |
    |   | I            | II | III | IV | I          | II | III | IV |
    | 4 | VEGA         |    |     |    | BETELGUESE |    |     |    |
    |   | I            | II | III | IV | I          | II | III | IV |
    | 5 |    CANOPUS   |    |     |    |  ALDEBARA  |    |     |    |
    |   | I            | II | III | IV | I          | II | III | IV |
    | 6 |    ALTAIR    |    |     |    |   REGULUS  |    |     |    |
    |   | I            | II | III | IV | I          | II | III | IV |
    | 7 | SAGITTARIOUS |    |     |    |  ARCTURUS  |    |     |    |
    |   | I            | II | III | IV | I          | II | III | IV |
    | 8 |    POLLUX    |    |     |    |    SPICA   |    |     |    |
    |   | I            | II | III | IV | I          | II | III | IV |
    
    #### Super Star Trek† Rules and Notes
    1. OBJECTIVE: You are Captain of the starship “Enterprise”† with a mission to seek and destroy a fleet of Klingon† warships (usually about 17) which are menacing the United Federation of Planets.† You have a specified number of stardates in which to complete your mission. You also have two or three Federation Starbases† for resupplying your ship.
    
    2. You will be assigned a starting position somewhere in the galaxy. The galaxy is divided into an 8 x 8 quadrant grid. The astronomical name of a quadrant is called out upon entry into a new region. (See “Quadrant Nomenclature.”) Each quadrant is further divided into an 8 x 8 section grid.
    
    3. On a section diagram, the following symbols are used:
        - `<*>` Enterprise
        - `†††` Klingon
        - `>!<` Starbase
        - `*`   Star
    
    4. You have eight commands available to you (A detailed description of each command is given in the program instructions.)
        - `NAV` Navigate the Starship by setting course and warp engine speed.
        - `SRS` Short-range sensor scan (one quadrant)
        - `LRS` Long-range sensor scan (9 quadrants)
        - `PHA` Phaser† control (energy gun)
        - `TOR` Photon torpedo control
        - `SHE` Shield control (protects against phaser fire)
        - `DAM` Damage and state-of-repair report
        - `COM` Call library computer
    
    5. Library computer options are as follows (more complete descriptions are in program instructions):
        - `0` Cumulative galactic report
        - `1` Status report
        - `2` Photon torpedo course data
        - `3` Starbase navigation data
        - `4` Direction/distance calculator
        - `5` Quadrant nomenclature map
    
    6. Certain reports on the ship’s status are made by officers of the Enterprise who appears on the original TV Show—Spock,† Scott,† Uhura,† Chekov,† etc.
    
    7. Klingons are non-stationary within their quadrants. If you try to maneuver on them, they will move and fire on you.
    
    8. Firing and damage notes:
        - Phaser fire diminishes with increased distance between combatants.
        - If a Klingon zaps you hard enough (relative to your shield strength) he will generally cause damage to some part of your ship with an appropriate “Damage Control” report resulting.
        - If you don’t zap a Klingon hard enough (relative to his shield strength) you won’t damage him at all. Your sensors will tell the story.
        - Damage control will let you know when out-of-commission devices have been completely repaired.
    
    9. Your engines will automatically shut down if you should attempt to leave the galaxy, or if you should try to maneuver through a star, or Starbase, or—heaven help you—a Klingon warship.
    
    10. In a pinch, or if you should miscalculate slightly, some shield control energy will be automatically diverted to warp engine control (if your shield are operational!).
    
    11. While you’re docked at a Starbase, a team of technicians can repair your ship (if you’re willing for them to spend the time required—and the repairmen _always_ underestimate…)
    
    12. If, to same maneuvering time toward the end of the game, you should cold-bloodedly destroy a Starbase, you get a nasty note from Starfleet Command. If you destroy your _last_ Starbase, you lose the game! (For those who think this is too a harsh penalty, delete line 5360-5390, and you’ll just get a “you dumdum!”-type message on all future status reports.)
    
    13. End game logic has been “cleaned up” in several spots, and it is possible to get a new command after successfully completing your mission (or, after resigning your old one).
    
    14. For those of you with certain types of CRT/keyboards setups (e.g. Westinghouse 1600), a “bell” character is inserted at appropriate spots to cause the following items to flash on and off on the screen:
        - The Phrase “\*RED\*” (as in Condition: Red)
        - The character representing your present quadrant in the cumulative galactic record printout.
    
    15. This version of Star Trek was created for a Data General Nova 800 system with 32K or core. So that it would fit, the instructions are separated from the main program via a CHAIN. For conversion to DEC BASIC-PLUS, Statement 160 (Randomize) should be moved after the return from the chained instructions, say to Statement 245. For Altair BASIC, Randomize and the chain instructions should be eliminated.
    
    † 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=172)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    Instructions in this directory at
    instructions.txt
    
    #### Porting Notes
    
    Many of the programs in this book and this collection have bugs in the original code.
    
    @jkboyce has done a great job of discovering and fixing a number of bugs in the [original code](superstartrek.bas), as part of his [python implementation](python/superstartrek.py), which should be noted by other implementers:
    
    - line `4410` : `D(7)` should be `D(6)`
    - 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
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Commands/Command.cs
    ================================================
    using System.ComponentModel;
    
    namespace SuperStarTrek.Commands;
    
    internal enum Command
    {
        [Description("To set course")]
        NAV,
    
        [Description("For short range sensor scan")]
        SRS,
    
        [Description("For long range sensor scan")]
        LRS,
    
        [Description("To fire phasers")]
        PHA,
    
        [Description("To fire photon torpedoes")]
        TOR,
    
        [Description("To raise or lower shields")]
        SHE,
    
        [Description("For damage control reports")]
        DAM,
    
        [Description("To call on library-computer")]
        COM,
    
        [Description("To resign your command")]
        XXX
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Commands/CommandExtensions.cs
    ================================================
    using System.Reflection;
    using System.ComponentModel;
    
    namespace SuperStarTrek.Commands;
    
    internal static class CommandExtensions
    {
        internal static string GetDescription(this Command command) =>
            typeof(Command)
                .GetField(command.ToString())
                .GetCustomAttribute()
                .Description;
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Commands/CommandResult.cs
    ================================================
    namespace SuperStarTrek.Commands;
    
    internal class CommandResult
    {
        public static readonly CommandResult Ok = new(false);
        public static readonly CommandResult GameOver = new(true);
    
        private CommandResult(bool isGameOver)
        {
            IsGameOver = isGameOver;
        }
    
        private CommandResult(float timeElapsed)
        {
            TimeElapsed = timeElapsed;
        }
    
        public bool IsGameOver { get; }
        public float TimeElapsed { get; }
    
        public static CommandResult Elapsed(float timeElapsed) => new(timeElapsed);
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Game.cs
    ================================================
    using System;
    using Games.Common.IO;
    using Games.Common.Randomness;
    using SuperStarTrek.Objects;
    using SuperStarTrek.Resources;
    using SuperStarTrek.Space;
    using SuperStarTrek.Systems;
    using SuperStarTrek.Systems.ComputerFunctions;
    
    namespace SuperStarTrek;
    
    internal class Game
    {
        private readonly TextIO _io;
        private readonly IRandom _random;
    
        private int _initialStardate;
        private int _finalStarDate;
        private float _currentStardate;
        private Coordinates _currentQuadrant;
        private Galaxy _galaxy;
        private int _initialKlingonCount;
        private Enterprise _enterprise;
    
        internal Game(TextIO io, IRandom random)
        {
            _io = io;
            _random = random;
        }
    
        internal float Stardate => _currentStardate;
    
        internal float StardatesRemaining => _finalStarDate - _currentStardate;
    
        internal void DoIntroduction()
        {
            _io.Write(Strings.Title);
    
            if (_io.GetYesNo("Do you need instructions", IReadWriteExtensions.YesNoMode.FalseOnN))
            {
                _io.Write(Strings.Instructions);
    
                _io.WaitForAnyKeyButEnter("to continue");
            }
        }
    
        internal void Play()
        {
            Initialise();
            var gameOver = false;
    
            while (!gameOver)
            {
                var command = _io.ReadCommand();
    
                var result = _enterprise.Execute(command);
    
                gameOver = result.IsGameOver || CheckIfStranded();
                _currentStardate += result.TimeElapsed;
                gameOver |= _currentStardate > _finalStarDate;
            }
    
            if (_galaxy.KlingonCount > 0)
            {
                _io.Write(Strings.EndOfMission, _currentStardate, _galaxy.KlingonCount);
            }
            else
            {
                _io.Write(Strings.Congratulations, CalculateEfficiency());
            }
        }
    
        private void Initialise()
        {
            _currentStardate = _initialStardate = _random.Next(20, 40) * 100;
            _finalStarDate = _initialStardate + _random.Next(25, 35);
    
            _currentQuadrant = _random.NextCoordinate();
    
            _galaxy = new Galaxy(_random);
            _initialKlingonCount = _galaxy.KlingonCount;
    
            _enterprise = new Enterprise(3000, _random.NextCoordinate(), _io, _random);
            _enterprise
                .Add(new WarpEngines(_enterprise, _io))
                .Add(new ShortRangeSensors(_enterprise, _galaxy, this, _io))
                .Add(new LongRangeSensors(_galaxy, _io))
                .Add(new PhaserControl(_enterprise, _io, _random))
                .Add(new PhotonTubes(10, _enterprise, _io))
                .Add(new ShieldControl(_enterprise, _io))
                .Add(new DamageControl(_enterprise, _io))
                .Add(new LibraryComputer(
                    _io,
                    new CumulativeGalacticRecord(_io, _galaxy),
                    new StatusReport(this, _galaxy, _enterprise, _io),
                    new TorpedoDataCalculator(_enterprise, _io),
                    new StarbaseDataCalculator(_enterprise, _io),
                    new DirectionDistanceCalculator(_enterprise, _io),
                    new GalaxyRegionMap(_io, _galaxy)));
    
            _io.Write(Strings.Enterprise);
            _io.Write(
                Strings.Orders,
                _galaxy.KlingonCount,
                _finalStarDate,
                _finalStarDate - _initialStardate,
                _galaxy.StarbaseCount > 1 ? "are" : "is",
                _galaxy.StarbaseCount,
                _galaxy.StarbaseCount > 1 ? "s" : "");
    
            _io.WaitForAnyKeyButEnter("when ready to accept command");
    
            _enterprise.StartIn(BuildCurrentQuadrant());
        }
    
        private Quadrant BuildCurrentQuadrant() => new(_galaxy[_currentQuadrant], _enterprise, _random, _galaxy, _io);
    
        internal bool Replay() => _galaxy.StarbaseCount > 0 && _io.ReadExpectedString(Strings.ReplayPrompt, "Aye");
    
        private bool CheckIfStranded()
        {
            if (_enterprise.IsStranded) { _io.Write(Strings.Stranded); }
            return _enterprise.IsStranded;
        }
    
        private float CalculateEfficiency() =>
            1000 * (float)Math.Pow(_initialKlingonCount / (_currentStardate - _initialStardate), 2);
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/IRandomExtensions.cs
    ================================================
    using Games.Common.Randomness;
    using SuperStarTrek.Space;
    
    namespace SuperStarTrek;
    
    internal static class IRandomExtensions
    {
        internal static Coordinates NextCoordinate(this IRandom random) =>
            new Coordinates(random.Next1To8Inclusive() - 1, random.Next1To8Inclusive() - 1);
    
        // Duplicates the algorithm used in the original code to get an integer value from 1 to 8, inclusive:
        //     475 DEF FNR(R)=INT(RND(R)*7.98+1.01)
        // Returns a value from 1 to 8, inclusive.
        // Note there's a slight bias away from the extreme values, 1 and 8.
        internal static int Next1To8Inclusive(this IRandom random) => (int)(random.NextFloat() * 7.98 + 1.01);
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/IReadWriteExtensions.cs
    ================================================
    using System;
    using System.Linq;
    using Games.Common.IO;
    using SuperStarTrek.Commands;
    using SuperStarTrek.Space;
    using static System.StringComparison;
    
    namespace SuperStarTrek;
    
    internal static class IReadWriteExtensions
    {
        internal static void WaitForAnyKeyButEnter(this IReadWrite io, string prompt)
        {
            io.Write($"Hit any key but Enter {prompt} ");
            while (io.ReadCharacter() == '\r');
        }
    
        internal static (float X, float Y) GetCoordinates(this IReadWrite io, string prompt) =>
            io.Read2Numbers($"{prompt} (X,Y)");
    
        internal static bool TryReadNumberInRange(
            this IReadWrite io,
            string prompt,
            float minValue,
            float maxValue,
            out float value)
        {
            value = io.ReadNumber($"{prompt} ({minValue}-{maxValue})");
    
            return value >= minValue && value <= maxValue;
        }
    
        internal static bool ReadExpectedString(this IReadWrite io, string prompt, string trueValue) =>
            io.ReadString(prompt).Equals(trueValue, InvariantCultureIgnoreCase);
    
        internal static Command ReadCommand(this IReadWrite io)
        {
            while(true)
            {
                var response = io.ReadString("Command");
    
                if (response.Length >= 3 &&
                    Enum.TryParse(response.Substring(0, 3), ignoreCase: true, out Command parsedCommand))
                {
                    return parsedCommand;
                }
    
                io.WriteLine("Enter one of the following:");
                foreach (var command in Enum.GetValues(typeof(Command)).OfType())
                {
                    io.WriteLine($"  {command}  ({command.GetDescription()})");
                }
                io.WriteLine();
            }
        }
    
        internal static bool TryReadCourse(this IReadWrite io, string prompt, string officer, out Course course)
        {
            if (!io.TryReadNumberInRange(prompt, 1, 9, out var direction))
            {
                io.WriteLine($"{officer} reports, 'Incorrect course data, sir!'");
                course = default;
                return false;
            }
    
            course = new Course(direction);
            return true;
        }
    
        internal static bool GetYesNo(this IReadWrite io, string prompt, YesNoMode mode)
        {
            var response = io.ReadString($"{prompt} (Y/N)").ToUpperInvariant();
    
            return (mode, response) switch
            {
                (YesNoMode.FalseOnN, "N") => false,
                (YesNoMode.FalseOnN, _) => true,
                (YesNoMode.TrueOnY, "Y") => true,
                (YesNoMode.TrueOnY, _) => false,
                _ => throw new ArgumentOutOfRangeException(nameof(mode), mode, "Invalid value")
            };
        }
    
        internal enum YesNoMode
        {
            TrueOnY,
            FalseOnN
        }
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Objects/Enterprise.cs
    ================================================
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Games.Common.IO;
    using Games.Common.Randomness;
    using SuperStarTrek.Commands;
    using SuperStarTrek.Resources;
    using SuperStarTrek.Space;
    using SuperStarTrek.Systems;
    
    namespace SuperStarTrek.Objects;
    
    internal class Enterprise
    {
        private readonly int _maxEnergy;
        private readonly IReadWrite _io;
        private readonly List _systems;
        private readonly Dictionary _commandExecutors;
        private readonly IRandom _random;
        private Quadrant _quadrant;
    
        public Enterprise(int maxEnergy, Coordinates sector, IReadWrite io, IRandom random)
        {
            SectorCoordinates = sector;
            TotalEnergy = _maxEnergy = maxEnergy;
    
            _systems = new List();
            _commandExecutors = new Dictionary();
            _io = io;
            _random = random;
        }
    
        internal Quadrant Quadrant => _quadrant;
    
        internal Coordinates QuadrantCoordinates => _quadrant.Coordinates;
    
        internal Coordinates SectorCoordinates { get; private set; }
    
        internal string Condition => GetCondition();
    
        internal LibraryComputer Computer => (LibraryComputer)_commandExecutors[Command.COM];
    
        internal ShieldControl ShieldControl => (ShieldControl)_commandExecutors[Command.SHE];
    
        internal float Energy => TotalEnergy - ShieldControl.ShieldEnergy;
    
        internal float TotalEnergy { get; private set; }
    
        internal int DamagedSystemCount => _systems.Count(s => s.IsDamaged);
    
        internal IEnumerable Systems => _systems;
    
        internal PhotonTubes PhotonTubes => (PhotonTubes)_commandExecutors[Command.TOR];
    
        internal bool IsDocked => _quadrant.EnterpriseIsNextToStarbase;
    
        internal bool IsStranded => TotalEnergy < 10 || Energy < 10 && ShieldControl.IsDamaged;
    
        internal Enterprise Add(Subsystem system)
        {
            _systems.Add(system);
            _commandExecutors[system.Command] = system;
    
            return this;
        }
    
        internal void StartIn(Quadrant quadrant)
        {
            _quadrant = quadrant;
            quadrant.Display(Strings.StartText);
        }
    
        private string GetCondition() =>
            IsDocked switch
            {
                true => "Docked",
                false when _quadrant.HasKlingons => "*Red*",
                false when Energy / _maxEnergy < 0.1f => "Yellow",
                false => "Green"
            };
    
        internal CommandResult Execute(Command command)
        {
            if (command == Command.XXX) { return CommandResult.GameOver; }
    
            return _commandExecutors[command].ExecuteCommand(_quadrant);
        }
    
        internal void Refuel() => TotalEnergy = _maxEnergy;
    
        public override string ToString() => "<*>";
    
        internal void UseEnergy(float amountUsed)
        {
            TotalEnergy -= amountUsed;
        }
    
        internal CommandResult TakeHit(Coordinates sector, int hitStrength)
        {
            _io.WriteLine($"{hitStrength} unit hit on Enterprise from sector {sector}");
            ShieldControl.AbsorbHit(hitStrength);
    
            if (ShieldControl.ShieldEnergy <= 0)
            {
                _io.WriteLine(Strings.Destroyed);
                return CommandResult.GameOver;
            }
    
            _io.WriteLine($"      ");
    
            if (hitStrength >= 20)
            {
                TakeDamage(hitStrength);
            }
    
            return CommandResult.Ok;
        }
    
        private void TakeDamage(float hitStrength)
        {
            var hitShieldRatio = hitStrength / ShieldControl.ShieldEnergy;
            if (_random.NextFloat() > 0.6 || hitShieldRatio <= 0.02f)
            {
                return;
            }
    
            var system = _systems[_random.Next1To8Inclusive() - 1];
            system.TakeDamage(hitShieldRatio + 0.5f * _random.NextFloat());
            _io.WriteLine($"Damage Control reports, '{system.Name} damaged by the hit.'");
        }
    
        internal void RepairSystems(float repairWorkDone)
        {
            var repairedSystems = new List();
    
            foreach (var system in _systems.Where(s => s.IsDamaged))
            {
                if (system.Repair(repairWorkDone))
                {
                    repairedSystems.Add(system.Name);
                }
            }
    
            if (repairedSystems.Any())
            {
                _io.WriteLine("Damage Control report:");
                foreach (var systemName in repairedSystems)
                {
                    _io.WriteLine($"        {systemName} repair completed.");
                }
            }
        }
    
        internal void VaryConditionOfRandomSystem()
        {
            if (_random.NextFloat() > 0.2f) { return; }
    
            var system = _systems[_random.Next1To8Inclusive() - 1];
            _io.Write($"Damage Control report:  {system.Name} ");
            if (_random.NextFloat() >= 0.6)
            {
                system.Repair(_random.NextFloat() * 3 + 1);
                _io.WriteLine("state of repair improved");
            }
            else
            {
                system.TakeDamage(_random.NextFloat() * 5 + 1);
                _io.WriteLine("damaged");
            }
        }
    
        internal float Move(Course course, float warpFactor, int distance)
        {
            var (quadrant, sector) = MoveWithinQuadrant(course, distance) ?? MoveBeyondQuadrant(course, distance);
    
            if (quadrant != _quadrant.Coordinates)
            {
                _quadrant = new Quadrant(_quadrant.Galaxy[quadrant], this, _random, _quadrant.Galaxy, _io);
            }
            _quadrant.SetEnterpriseSector(sector);
            SectorCoordinates = sector;
    
            TotalEnergy -= distance + 10;
            if (Energy < 0)
            {
                _io.WriteLine("Shield Control supplies energy to complete the maneuver.");
                ShieldControl.ShieldEnergy = Math.Max(0, TotalEnergy);
            }
    
            return GetTimeElapsed(quadrant, warpFactor);
        }
    
        private (Coordinates, Coordinates)? MoveWithinQuadrant(Course course, int distance)
        {
            var currentSector = SectorCoordinates;
            foreach (var (sector, index) in course.GetSectorsFrom(SectorCoordinates).Select((s, i) => (s, i)))
            {
                if (distance == 0) { break; }
    
                if (_quadrant.HasObjectAt(sector))
                {
                    _io.WriteLine($"Warp engines shut down at sector {currentSector} dues to bad navigation");
                    distance = 0;
                    break;
                }
    
                currentSector = sector;
                distance -= 1;
            }
    
            return distance == 0 ? (_quadrant.Coordinates, currentSector) : null;
        }
    
        private (Coordinates, Coordinates) MoveBeyondQuadrant(Course course, int distance)
        {
            var (complete, quadrant, sector) = course.GetDestination(QuadrantCoordinates, SectorCoordinates, distance);
    
            if (!complete)
            {
                _io.Write(Strings.PermissionDenied, sector, quadrant);
            }
    
            return (quadrant, sector);
        }
    
        private float GetTimeElapsed(Coordinates finalQuadrant, float warpFactor) =>
            finalQuadrant == _quadrant.Coordinates
                ? Math.Min(1, (float)Math.Round(warpFactor, 1, MidpointRounding.ToZero))
                : 1;
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Objects/Klingon.cs
    ================================================
    using Games.Common.Randomness;
    using SuperStarTrek.Commands;
    using SuperStarTrek.Space;
    
    namespace SuperStarTrek.Objects;
    
    internal class Klingon
    {
        private readonly IRandom _random;
    
        internal Klingon(Coordinates sector, IRandom random)
        {
            Sector = sector;
            _random = random;
            Energy = _random.NextFloat(100, 300);
        }
    
        internal float Energy { get; private set; }
    
        internal Coordinates Sector { get; private set; }
    
        public override string ToString() => "+K+";
    
        internal CommandResult FireOn(Enterprise enterprise)
        {
            var attackStrength = _random.NextFloat();
            var distanceToEnterprise = Sector.GetDistanceTo(enterprise.SectorCoordinates);
            var hitStrength = (int)(Energy * (2 + attackStrength) / distanceToEnterprise);
            Energy /= 3 + attackStrength;
    
            return enterprise.TakeHit(Sector, hitStrength);
        }
    
        internal bool TakeHit(int hitStrength)
        {
            if (hitStrength < 0.15 * Energy) { return false; }
    
            Energy -= hitStrength;
            return true;
        }
    
        internal void MoveTo(Coordinates newSector) => Sector = newSector;
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Objects/Star.cs
    ================================================
    namespace SuperStarTrek.Objects;
    
    internal class Star
    {
        public override string ToString() => " * ";
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Objects/Starbase.cs
    ================================================
    using Games.Common.IO;
    using Games.Common.Randomness;
    using SuperStarTrek.Resources;
    using SuperStarTrek.Space;
    
    namespace SuperStarTrek.Objects;
    
    internal class Starbase
    {
        private readonly IReadWrite _io;
        private readonly float _repairDelay;
    
        internal Starbase(Coordinates sector, IRandom random, IReadWrite io)
        {
            Sector = sector;
            _repairDelay = random.NextFloat(0.5f);
            _io = io;
        }
    
        internal Coordinates Sector { get; }
    
        public override string ToString() => ">!<";
    
        internal bool TryRepair(Enterprise enterprise, out float repairTime)
        {
            repairTime = enterprise.DamagedSystemCount * 0.1f + _repairDelay;
            if (repairTime >= 1) { repairTime = 0.9f; }
    
            _io.Write(Strings.RepairEstimate, repairTime);
            if (_io.GetYesNo(Strings.RepairPrompt, IReadWriteExtensions.YesNoMode.TrueOnY))
            {
                foreach (var system in enterprise.Systems)
                {
                    system.Repair();
                }
                return true;
            }
    
            repairTime = 0;
            return false;
        }
    
        internal void ProtectEnterprise() => _io.WriteLine(Strings.Protected);
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Program.cs
    ================================================
    // 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
    // ****  LINE NUMBERS FROM VERSION STREK7 OF 1/12/75 PRESERVED AS
    // ****  MUCH AS POSSIBLE WHILE USING MULTIPLE STATEMENTS PER LINE
    // ****  SOME LINES ARE LONGER THAN 72 CHARACTERS; THIS WAS DONE
    // ****  BY USING "?" INSTEAD OF "PRINT" WHEN ENTERING LINES
    // ****
    // ****  CONVERTED TO MICROSOFT C# 2/20/21 BY ANDREW COOPER
    // ****
    
    using Games.Common.IO;
    using Games.Common.Randomness;
    using SuperStarTrek;
    
    var io = new ConsoleIO();
    var random = new RandomNumberGenerator();
    
    var game = new Game(io, random);
    
    game.DoIntroduction();
    
    do
    {
        game.Play();
    } while (game.Replay());
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Resources/CombatArea.txt
    ================================================
    COMBAT AREA      CONDITION RED
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Resources/Congratulations.txt
    ================================================
    Congratulations, Captain!  The last Klingon battle cruiser
    menacing the Federation has been destroyed.
    
    Your efficiency rating is {0}.
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Resources/CourtMartial.txt
    ================================================
    
    Starfleet Command reviewing your record to consider
    court martial!
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Resources/Destroyed.txt
    ================================================
    The Enterprise has been destroyed.  The Federation will be conquered.
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Resources/EndOfMission.txt
    ================================================
    Is is stardate {0}.
    There were {1} Klingon battle cruisers left at
    the end of your mission.
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Resources/Enterprise.txt
    ================================================
    
    
    
    
    
    
    
    
    
    
    
                                        ,------*------,
                        ,-------------   '---  ------'
                         '-------- --'      / /
                             ,---' '-------/ /--,
                              '----------------'
    
                        THE USS ENTERPRISE --- NCC-1701
    
    
    
    
    
    
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Resources/Instructions.txt
    ================================================
    
          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.
    
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Resources/LowShields.txt
    ================================================
       SHIELDS DANGEROUSLY LOW
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Resources/NoEnemyShips.txt
    ================================================
    Science Officer Spock reports, 'Sensors show no enemy ships
                                    in this quadrant'
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Resources/NoStarbase.txt
    ================================================
    Mr. Spock reports, 'Sensors show no starbases in this quadrant.'
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Resources/NowEntering.txt
    ================================================
    
    Now entering {0} quadrant . . .
    
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Resources/Orders.txt
    ================================================
    Your orders are as follows:
        Destroy the {0} Klingon warships which have invaded
      the galaxy before they can attack federation headquarters
      on stardate {1}. This gives you {2} days. There {3}
      {4} starbase{5} in the galaxy for resupplying your ship.
    
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Resources/PermissionDenied.txt
    ================================================
    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 {0} of quadrant {1}.'
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Resources/Protected.txt
    ================================================
    Starbase shields protect the Enterprise
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Resources/RegionNames.txt
    ================================================
          Antares                  Sirius
           Rigel                   Deneb
          Procyon                 Capella
            Vega                 Betelgeuse
          Canopus                Aldebaran
           Altair                 Regulus
        Sagittarius               Arcturus
           Pollux                  Spica
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Resources/RelievedOfCommand.txt
    ================================================
    
    That does it, Captain!!  You are hereby relieved of command
    and sentenced to 99 stardates at hard labor on Cygnus 12!!
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Resources/RepairEstimate.txt
    ================================================
    Technicians standing by to effect repairs to your ship;
    Estimated time to repair: {0} stardates.
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Resources/RepairPrompt.txt
    ================================================
    Will you authorize the repair order (Y/N)
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Resources/ReplayPrompt.txt
    ================================================
    
    
    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'
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Resources/ShieldsDropped.txt
    ================================================
    Shields dropped for docking purposes
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Resources/ShieldsSet.txt
    ================================================
    Deflector control room report:
      'Shields now at {0} units per your command.'
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Resources/ShortRangeSensorsOut.txt
    ================================================
    *** Short Range Sensors are out ***
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Resources/StartText.txt
    ================================================
    
    
    Your mission begins with your starship located
    in the galactic quadrant, '{0}'.
    
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Resources/Stranded.txt
    ================================================
    ** 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!!
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Resources/Strings.cs
    ================================================
    using System.IO;
    using System.Reflection;
    using System.Runtime.CompilerServices;
    
    namespace SuperStarTrek.Resources
    {
        internal static class Strings
        {
            internal static string CombatArea => GetResource();
    
            internal static string Congratulations => GetResource();
    
            internal static string CourtMartial => GetResource();
    
            internal static string Destroyed => GetResource();
    
            internal static string EndOfMission => GetResource();
    
            internal static string Enterprise => GetResource();
    
            internal static string Instructions => GetResource();
    
            internal static string LowShields => GetResource();
    
            internal static string NoEnemyShips => GetResource();
    
            internal static string NoStarbase => GetResource();
    
            internal static string NowEntering => GetResource();
    
            internal static string Orders => GetResource();
    
            internal static string PermissionDenied => GetResource();
    
            internal static string Protected => GetResource();
    
            internal static string RegionNames => GetResource();
    
            internal static string RelievedOfCommand => GetResource();
    
            internal static string RepairEstimate => GetResource();
    
            internal static string RepairPrompt => GetResource();
    
            internal static string ReplayPrompt => GetResource();
    
            internal static string ShieldsDropped => GetResource();
    
            internal static string ShieldsSet => GetResource();
    
            internal static string ShortRangeSensorsOut => GetResource();
    
            internal static string StartText => GetResource();
    
            internal static string Stranded => GetResource();
    
            internal static string Title => GetResource();
    
            private static string GetResource([CallerMemberName] string name = "")
            {
                var streamName = $"SuperStarTrek.Resources.{name}.txt";
                using var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(streamName);
                using var reader = new StreamReader(stream);
    
                return reader.ReadToEnd();
            }
        }
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Resources/Title.txt
    ================================================
    
    
    
    
    
    
    
    
    
    
    
    
              *************************************
              *                                   *
              *                                   *
              *      * * SUPER STAR TREK * *      *
              *                                   *
              *                                   *
              *************************************
    
    
    
    
    
    
    
    
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Space/Coordinates.cs
    ================================================
    using System;
    using SuperStarTrek.Utils;
    
    namespace SuperStarTrek.Space;
    
    // Represents the corrdintate of a quadrant in the galaxy, or a sector in a quadrant.
    // Note that the origin is top-left, x increase downwards, and y increases to the right.
    internal record Coordinates
    {
        internal Coordinates(int x, int y)
        {
            X = Validated(x, nameof(x));
            Y = Validated(y, nameof(y));
    
            RegionIndex = (X << 1) + (Y >> 2);
            SubRegionIndex = Y % 4;
        }
    
        internal int X { get; }
    
        internal int Y { get; }
    
        internal int RegionIndex { get; }
    
        internal int SubRegionIndex { get; }
    
        private static int Validated(int value, string argumentName)
        {
            if (value >= 0 && value <= 7) { return value; }
    
            throw new ArgumentOutOfRangeException(argumentName, value, "Must be 0 to 7 inclusive");
        }
    
        private static bool IsValid(int value) => value >= 0 && value <= 7;
    
        public override string ToString() => $"{X+1} , {Y+1}";
    
        internal void Deconstruct(out int x, out int y)
        {
            x = X;
            y = Y;
        }
    
        internal static bool TryCreate(float x, float y, out Coordinates coordinates)
        {
            var roundedX = Round(x);
            var roundedY = Round(y);
    
            if (IsValid(roundedX) && IsValid(roundedY))
            {
                coordinates = new Coordinates(roundedX, roundedY);
                return true;
            }
    
            coordinates = default;
            return false;
    
            static int Round(float value) => (int)Math.Round(value, MidpointRounding.AwayFromZero);
        }
    
        internal (float Direction, float Distance) GetDirectionAndDistanceTo(Coordinates destination) =>
            DirectionAndDistance.From(this).To(destination);
    
        internal float GetDistanceTo(Coordinates destination)
        {
            var (_, distance) = GetDirectionAndDistanceTo(destination);
            return distance;
        }
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Space/Course.cs
    ================================================
    using System;
    using System.Collections.Generic;
    
    namespace SuperStarTrek.Space;
    
    // Implements the course calculations from the original code:
    //     530 FORI=1TO9:C(I,1)=0:C(I,2)=0:NEXTI
    //     540 C(3,1)=-1:C(2,1)=-1:C(4,1)=-1:C(4,2)=-1:C(5,2)=-1:C(6,2)=-1
    //     600 C(1,2)=1:C(2,2)=1:C(6,1)=1:C(7,1)=1:C(8,1)=1:C(8,2)=1:C(9,2)=1
    //
    //     3110 X1=C(C1,1)+(C(C1+1,1)-C(C1,1))*(C1-INT(C1))
    //     3140 X2=C(C1,2)+(C(C1+1,2)-C(C1,2))*(C1-INT(C1))
    internal class Course
    {
        private static readonly (int DeltaX, int DeltaY)[] cardinals = new[]
        {
            (0, 1),
            (-1, 1),
            (-1, 0),
            (-1, -1),
            (0, -1),
            (1, -1),
            (1, 0),
            (1, 1),
            (0, 1)
        };
    
        internal Course(float direction)
        {
            if (direction < 1 || direction > 9)
            {
                throw new ArgumentOutOfRangeException(
                    nameof(direction),
                    direction,
                    "Must be between 1 and 9, inclusive.");
            }
    
            var cardinalDirection = (int)(direction - 1) % 8;
            var fractionalDirection = direction - (int)direction;
    
            var baseCardinal = cardinals[cardinalDirection];
            var nextCardinal = cardinals[cardinalDirection + 1];
    
            DeltaX = baseCardinal.DeltaX + (nextCardinal.DeltaX - baseCardinal.DeltaX) * fractionalDirection;
            DeltaY = baseCardinal.DeltaY + (nextCardinal.DeltaY - baseCardinal.DeltaY) * fractionalDirection;
        }
    
        internal float DeltaX { get; }
    
        internal float DeltaY { get; }
    
        internal IEnumerable GetSectorsFrom(Coordinates start)
        {
            (float x, float y) = start;
    
            while(true)
            {
                x += DeltaX;
                y += DeltaY;
    
                if (!Coordinates.TryCreate(x, y, out var coordinates))
                {
                    yield break;
                }
    
                yield return coordinates;
            }
        }
    
        internal (bool, Coordinates, Coordinates) GetDestination(Coordinates quadrant, Coordinates sector, int distance)
        {
            var (xComplete, quadrantX, sectorX) = GetNewCoordinate(quadrant.X, sector.X, DeltaX * distance);
            var (yComplete, quadrantY, sectorY) = GetNewCoordinate(quadrant.Y, sector.Y, DeltaY * distance);
    
            return (xComplete && yComplete, new Coordinates(quadrantX, quadrantY), new Coordinates(sectorX, sectorY));
        }
    
        private static (bool, int, int) GetNewCoordinate(int quadrant, int sector, float sectorsTravelled)
        {
            var galacticCoordinate = quadrant * 8 + sector + sectorsTravelled;
            var newQuadrant = (int)(galacticCoordinate / 8);
            var newSector = (int)(galacticCoordinate - newQuadrant * 8);
    
            if (newSector < 0)
            {
                newQuadrant -= 1;
                newSector += 8;
            }
    
            return newQuadrant switch
            {
                < 0 => (false, 0, 0),
                > 7 => (false, 7, 7),
                _ => (true, newQuadrant, newSector)
            };
        }
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Space/Galaxy.cs
    ================================================
    using System.Collections.Generic;
    using System.Linq;
    using Games.Common.Randomness;
    using SuperStarTrek.Resources;
    
    using static System.StringSplitOptions;
    
    namespace SuperStarTrek.Space;
    
    internal class Galaxy
    {
        private static readonly string[] _regionNames;
        private static readonly string[] _subRegionIdentifiers;
        private readonly QuadrantInfo[][] _quadrants;
    
        static Galaxy()
        {
            _regionNames = Strings.RegionNames.Split(new[] { ' ', '\n' }, RemoveEmptyEntries | TrimEntries);
            _subRegionIdentifiers = new[] { "I", "II", "III", "IV" };
        }
    
        internal Galaxy(IRandom random)
        {
            _quadrants = Enumerable
                .Range(0, 8)
                .Select(x => Enumerable
                    .Range(0, 8)
                    .Select(y => new Coordinates(x, y))
                    .Select(c => QuadrantInfo.Create(c, GetQuadrantName(c), random))
                    .ToArray())
                .ToArray();
    
            if (StarbaseCount == 0)
            {
                var randomQuadrant = this[random.NextCoordinate()];
                randomQuadrant.AddStarbase();
    
                if (randomQuadrant.KlingonCount < 2)
                {
                    randomQuadrant.AddKlingon();
                }
            }
        }
    
        internal QuadrantInfo this[Coordinates coordinate] => _quadrants[coordinate.X][coordinate.Y];
    
        internal int KlingonCount => _quadrants.SelectMany(q => q).Sum(q => q.KlingonCount);
    
        internal int StarbaseCount => _quadrants.SelectMany(q => q).Count(q => q.HasStarbase);
    
        internal IEnumerable> Quadrants => _quadrants;
    
        private static string GetQuadrantName(Coordinates coordinates) =>
            $"{_regionNames[coordinates.RegionIndex]} {_subRegionIdentifiers[coordinates.SubRegionIndex]}";
    
        internal IEnumerable> GetNeighborhood(Quadrant quadrant) =>
            Enumerable.Range(-1, 3)
                .Select(dx => dx + quadrant.Coordinates.X)
                .Select(x => GetNeighborhoodRow(quadrant, x));
        private IEnumerable GetNeighborhoodRow(Quadrant quadrant, int x) =>
            Enumerable.Range(-1, 3)
                .Select(dy => dy + quadrant.Coordinates.Y)
                .Select(y => y < 0 || y > 7 || x < 0 || x > 7 ? null : _quadrants[x][y]);
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Space/Quadrant.cs
    ================================================
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Games.Common.IO;
    using Games.Common.Randomness;
    using SuperStarTrek.Commands;
    using SuperStarTrek.Objects;
    using SuperStarTrek.Resources;
    
    namespace SuperStarTrek.Space;
    
    internal class Quadrant
    {
        private readonly QuadrantInfo _info;
        private readonly IRandom _random;
        private readonly Dictionary _sectors;
        private readonly Enterprise _enterprise;
        private readonly IReadWrite _io;
        private bool _entered = false;
    
        internal Quadrant(
            QuadrantInfo info,
            Enterprise enterprise,
            IRandom random,
            Galaxy galaxy,
            IReadWrite io)
        {
            _info = info;
            _random = random;
            _io = io;
            Galaxy = galaxy;
    
            info.MarkAsKnown();
            _sectors = new() { [enterprise.SectorCoordinates] = _enterprise = enterprise };
            PositionObject(sector => new Klingon(sector, _random), _info.KlingonCount);
            if (_info.HasStarbase)
            {
                Starbase = PositionObject(sector => new Starbase(sector, _random, io));
            }
            PositionObject(_ => new Star(), _info.StarCount);
        }
    
        internal Coordinates Coordinates => _info.Coordinates;
    
        internal bool HasKlingons => _info.KlingonCount > 0;
    
        internal int KlingonCount => _info.KlingonCount;
    
        internal bool HasStarbase => _info.HasStarbase;
    
        internal Starbase Starbase { get; }
    
        internal Galaxy Galaxy { get; }
    
        internal bool EnterpriseIsNextToStarbase =>
            _info.HasStarbase &&
            Math.Abs(_enterprise.SectorCoordinates.X - Starbase.Sector.X) <= 1 &&
            Math.Abs(_enterprise.SectorCoordinates.Y - Starbase.Sector.Y) <= 1;
    
        internal IEnumerable Klingons => _sectors.Values.OfType();
    
        public override string ToString() => _info.Name;
    
        private T PositionObject(Func objectFactory)
        {
            var sector = GetRandomEmptySector();
            _sectors[sector] = objectFactory.Invoke(sector);
            return (T)_sectors[sector];
        }
    
        private void PositionObject(Func objectFactory, int count)
        {
            for (int i = 0; i < count; i++)
            {
                PositionObject(objectFactory);
            }
        }
    
        internal void Display(string textFormat)
        {
            if (!_entered)
            {
                _io.Write(textFormat, this);
                _entered = true;
            }
    
            if (_info.KlingonCount > 0)
            {
                _io.Write(Strings.CombatArea);
                if (_enterprise.ShieldControl.ShieldEnergy <= 200) { _io.Write(Strings.LowShields); }
            }
    
            _enterprise.Execute(Command.SRS);
        }
    
        internal bool HasObjectAt(Coordinates coordinates) => _sectors.ContainsKey(coordinates);
    
        internal bool TorpedoCollisionAt(Coordinates coordinates, out string message, out bool gameOver)
        {
            gameOver = false;
            message = default;
    
            switch (_sectors.GetValueOrDefault(coordinates))
            {
                case Klingon klingon:
                    message = Remove(klingon);
                    gameOver = Galaxy.KlingonCount == 0;
                    return true;
    
                case Star _:
                    message = $"Star at {coordinates} absorbed torpedo energy.";
                    return true;
    
                case Starbase _:
                    _sectors.Remove(coordinates);
                    _info.RemoveStarbase();
                    message = "*** Starbase destroyed ***" +
                        (Galaxy.StarbaseCount > 0 ? Strings.CourtMartial : Strings.RelievedOfCommand);
                    gameOver = Galaxy.StarbaseCount == 0;
                    return true;
    
                default:
                    return false;
            }
        }
    
        internal string Remove(Klingon klingon)
        {
            _sectors.Remove(klingon.Sector);
            _info.RemoveKlingon();
            return "*** Klingon destroyed ***";
        }
    
        internal CommandResult KlingonsMoveAndFire()
        {
            foreach (var klingon in Klingons.ToList())
            {
                var newSector = GetRandomEmptySector();
                _sectors.Remove(klingon.Sector);
                _sectors[newSector] = klingon;
                klingon.MoveTo(newSector);
            }
    
            return KlingonsFireOnEnterprise();
        }
    
        internal CommandResult KlingonsFireOnEnterprise()
        {
            if (EnterpriseIsNextToStarbase && Klingons.Any())
            {
                Starbase.ProtectEnterprise();
                return CommandResult.Ok;
            }
    
            foreach (var klingon in Klingons)
            {
                var result = klingon.FireOn(_enterprise);
                if (result.IsGameOver) { return result; }
            }
    
            return CommandResult.Ok;
        }
    
        private Coordinates GetRandomEmptySector()
        {
            while (true)
            {
                var sector = _random.NextCoordinate();
                if (!_sectors.ContainsKey(sector))
                {
                    return sector;
                }
            }
        }
    
        internal IEnumerable GetDisplayLines() => Enumerable.Range(0, 8).Select(x => GetDisplayLine(x));
    
        private string GetDisplayLine(int x) =>
            string.Join(
                " ",
                Enumerable
                    .Range(0, 8)
                    .Select(y => new Coordinates(x, y))
                    .Select(c => _sectors.GetValueOrDefault(c))
                    .Select(o => o?.ToString() ?? "   "));
    
        internal void SetEnterpriseSector(Coordinates sector)
        {
            _sectors.Remove(_enterprise.SectorCoordinates);
            _sectors[sector] = _enterprise;
        }
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Space/QuadrantInfo.cs
    ================================================
    using Games.Common.Randomness;
    
    namespace SuperStarTrek.Space;
    
    internal class QuadrantInfo
    {
        private bool _isKnown;
    
        private QuadrantInfo(Coordinates coordinates, string name, int klingonCount, int starCount, bool hasStarbase)
        {
            Coordinates = coordinates;
            Name = name;
            KlingonCount = klingonCount;
            StarCount = starCount;
            HasStarbase = hasStarbase;
        }
    
        internal Coordinates Coordinates { get; }
    
        internal string Name { get; }
    
        internal int KlingonCount { get; private set; }
    
        internal bool HasStarbase { get; private set; }
    
        internal int StarCount { get; }
    
        internal static QuadrantInfo Create(Coordinates coordinates, string name, IRandom random)
        {
            var klingonCount = random.NextFloat() switch
            {
                > 0.98f => 3,
                > 0.95f => 2,
                > 0.80f => 1,
                _ => 0
            };
            var hasStarbase = random.NextFloat() > 0.96f;
            var starCount = random.Next1To8Inclusive();
    
            return new QuadrantInfo(coordinates, name, klingonCount, starCount, hasStarbase);
        }
    
        internal void AddKlingon() => KlingonCount += 1;
    
        internal void AddStarbase() => HasStarbase = true;
    
        internal void MarkAsKnown() => _isKnown = true;
    
        internal string Scan()
        {
            _isKnown = true;
            return ToString();
        }
    
        public override string ToString() => _isKnown ? $"{KlingonCount}{(HasStarbase ? 1 : 0)}{StarCount}" : "***";
    
        internal void RemoveKlingon()
        {
            if (KlingonCount > 0)
            {
                KlingonCount -= 1;
            }
        }
    
        internal void RemoveStarbase() => HasStarbase = false;
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/StringExtensions.cs
    ================================================
    namespace SuperStarTrek;
    
    internal static class StringExtensions
    {
        internal static string Pluralize(this string singular, int quantity) => singular + (quantity > 1 ? "s" : "");
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/SuperStarTrek.csproj
    ================================================
    
    
      
        Exe
        net6.0
      
    
      
        
      
    
      
        
      
    
    
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/SuperStarTrek.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio 15
    VisualStudioVersion = 15.0.26124.0
    MinimumVisualStudioVersion = 15.0.26124.0
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SuperStarTrek", "SuperStarTrek.csproj", "{ACB0A7F5-A4DC-4A2F-8D3D-104E83A0417F}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Debug|x64 = Debug|x64
    		Debug|x86 = Debug|x86
    		Release|Any CPU = Release|Any CPU
    		Release|x64 = Release|x64
    		Release|x86 = Release|x86
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{ACB0A7F5-A4DC-4A2F-8D3D-104E83A0417F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{ACB0A7F5-A4DC-4A2F-8D3D-104E83A0417F}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{ACB0A7F5-A4DC-4A2F-8D3D-104E83A0417F}.Debug|x64.ActiveCfg = Debug|Any CPU
    		{ACB0A7F5-A4DC-4A2F-8D3D-104E83A0417F}.Debug|x64.Build.0 = Debug|Any CPU
    		{ACB0A7F5-A4DC-4A2F-8D3D-104E83A0417F}.Debug|x86.ActiveCfg = Debug|Any CPU
    		{ACB0A7F5-A4DC-4A2F-8D3D-104E83A0417F}.Debug|x86.Build.0 = Debug|Any CPU
    		{ACB0A7F5-A4DC-4A2F-8D3D-104E83A0417F}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{ACB0A7F5-A4DC-4A2F-8D3D-104E83A0417F}.Release|Any CPU.Build.0 = Release|Any CPU
    		{ACB0A7F5-A4DC-4A2F-8D3D-104E83A0417F}.Release|x64.ActiveCfg = Release|Any CPU
    		{ACB0A7F5-A4DC-4A2F-8D3D-104E83A0417F}.Release|x64.Build.0 = Release|Any CPU
    		{ACB0A7F5-A4DC-4A2F-8D3D-104E83A0417F}.Release|x86.ActiveCfg = Release|Any CPU
    		{ACB0A7F5-A4DC-4A2F-8D3D-104E83A0417F}.Release|x86.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Systems/ComputerFunctions/ComputerFunction.cs
    ================================================
    using Games.Common.IO;
    using SuperStarTrek.Space;
    
    namespace SuperStarTrek.Systems.ComputerFunctions;
    
    internal abstract class ComputerFunction
    {
        protected ComputerFunction(string description, IReadWrite io)
        {
            Description = description;
            IO = io;
        }
    
        internal string Description { get; }
    
        protected IReadWrite IO { get; }
    
        internal abstract void Execute(Quadrant quadrant);
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Systems/ComputerFunctions/CumulativeGalacticRecord.cs
    ================================================
    using System.Collections.Generic;
    using System.Linq;
    using Games.Common.IO;
    using SuperStarTrek.Space;
    
    namespace SuperStarTrek.Systems.ComputerFunctions;
    
    internal class CumulativeGalacticRecord : GalacticReport
    {
        internal CumulativeGalacticRecord(IReadWrite io, Galaxy galaxy)
            : base("Cumulative galactic record", io, galaxy)
        {
        }
    
        protected override void WriteHeader(Quadrant quadrant)
        {
            IO.WriteLine();
            IO.WriteLine($"Computer record of galaxy for quadrant {quadrant.Coordinates}");
            IO.WriteLine();
        }
    
        protected override IEnumerable GetRowData() =>
            Galaxy.Quadrants.Select(row => " " + string.Join("   ", row));
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Systems/ComputerFunctions/DirectionDistanceCalculator.cs
    ================================================
    using Games.Common.IO;
    using SuperStarTrek.Objects;
    using SuperStarTrek.Space;
    
    namespace SuperStarTrek.Systems.ComputerFunctions;
    
    internal class DirectionDistanceCalculator : NavigationCalculator
    {
        private readonly Enterprise _enterprise;
        private readonly IReadWrite _io;
    
        internal DirectionDistanceCalculator(Enterprise enterprise, IReadWrite io)
            : base("Direction/distance calculator", io)
        {
            _enterprise = enterprise;
            _io = io;
        }
    
        internal override void Execute(Quadrant quadrant)
        {
            IO.WriteLine("Direction/distance calculator:");
            IO.Write($"You are at quadrant {_enterprise.QuadrantCoordinates}");
            IO.WriteLine($" sector {_enterprise.SectorCoordinates}");
            IO.WriteLine("Please enter");
    
            WriteDirectionAndDistance(
                _io.GetCoordinates("  Initial coordinates"),
                _io.GetCoordinates("  Final coordinates"));
        }
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Systems/ComputerFunctions/GalacticReport.cs
    ================================================
    using System.Collections.Generic;
    using System.Linq;
    using Games.Common.IO;
    using SuperStarTrek.Space;
    
    namespace SuperStarTrek.Systems.ComputerFunctions;
    
    internal abstract class GalacticReport : ComputerFunction
    {
        internal GalacticReport(string description, IReadWrite io, Galaxy galaxy)
            : base(description, io)
        {
            Galaxy = galaxy;
        }
    
        protected Galaxy Galaxy { get; }
    
        protected abstract void WriteHeader(Quadrant quadrant);
    
        protected abstract IEnumerable GetRowData();
    
        internal sealed override void Execute(Quadrant quadrant)
        {
            WriteHeader(quadrant);
            IO.WriteLine("       1     2     3     4     5     6     7     8");
            IO.WriteLine("     ----- ----- ----- ----- ----- ----- ----- -----");
    
            foreach (var (row, index) in GetRowData().Select((r, i) => (r, i)))
            {
                IO.WriteLine($" {index+1}   {row}");
                IO.WriteLine("     ----- ----- ----- ----- ----- ----- ----- -----");
            }
        }
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Systems/ComputerFunctions/GalaxyRegionMap.cs
    ================================================
    using System.Collections.Generic;
    using System.Linq;
    using Games.Common.IO;
    using SuperStarTrek.Resources;
    using SuperStarTrek.Space;
    
    namespace SuperStarTrek.Systems.ComputerFunctions;
    
    internal class GalaxyRegionMap : GalacticReport
    {
        internal GalaxyRegionMap(IReadWrite io, Galaxy galaxy)
            : base("Galaxy 'region name' map", io, galaxy)
        {
        }
    
        protected override void WriteHeader(Quadrant quadrant) =>
            IO.WriteLine("                        The Galaxy");
    
        protected override IEnumerable GetRowData() =>
            Strings.RegionNames.Split('\n').Select(n => n.TrimEnd('\r'));
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Systems/ComputerFunctions/NavigationCalculator.cs
    ================================================
    using Games.Common.IO;
    using SuperStarTrek.Space;
    using SuperStarTrek.Utils;
    
    namespace SuperStarTrek.Systems.ComputerFunctions;
    
    internal abstract class NavigationCalculator : ComputerFunction
    {
        protected NavigationCalculator(string description, IReadWrite io)
            : base(description, io)
        {
        }
    
        protected void WriteDirectionAndDistance(Coordinates from, Coordinates to)
        {
            var (direction, distance) = from.GetDirectionAndDistanceTo(to);
            Write(direction, distance);
        }
    
        protected void WriteDirectionAndDistance((float X, float Y) from, (float X, float Y) to)
        {
            var (direction, distance) = DirectionAndDistance.From(from.X, from.Y).To(to.X, to.Y);
            Write(direction, distance);
        }
    
        private void Write(float direction, float distance)
        {
            IO.WriteLine($"Direction = {direction}");
            IO.WriteLine($"Distance = {distance}");
        }
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Systems/ComputerFunctions/StarbaseDataCalculator.cs
    ================================================
    using Games.Common.IO;
    using SuperStarTrek.Objects;
    using SuperStarTrek.Resources;
    using SuperStarTrek.Space;
    
    namespace SuperStarTrek.Systems.ComputerFunctions;
    
    internal class StarbaseDataCalculator : NavigationCalculator
    {
        private readonly Enterprise _enterprise;
    
        internal StarbaseDataCalculator(Enterprise enterprise, IReadWrite io)
            : base("Starbase nav data", io)
        {
            _enterprise = enterprise;
        }
    
        internal override void Execute(Quadrant quadrant)
        {
            if (!quadrant.HasStarbase)
            {
                IO.WriteLine(Strings.NoStarbase);
                return;
            }
    
            IO.WriteLine("From Enterprise to Starbase:");
    
            WriteDirectionAndDistance(_enterprise.SectorCoordinates, quadrant.Starbase.Sector);
        }
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Systems/ComputerFunctions/StatusReport.cs
    ================================================
    using Games.Common.IO;
    using SuperStarTrek.Commands;
    using SuperStarTrek.Objects;
    using SuperStarTrek.Space;
    
    namespace SuperStarTrek.Systems.ComputerFunctions;
    
    internal class StatusReport : ComputerFunction
    {
        private readonly Game _game;
        private readonly Galaxy _galaxy;
        private readonly Enterprise _enterprise;
    
        internal StatusReport(Game game, Galaxy galaxy, Enterprise enterprise, IReadWrite io)
            : base("Status report", io)
        {
            _game = game;
            _galaxy = galaxy;
            _enterprise = enterprise;
        }
    
        internal override void Execute(Quadrant quadrant)
        {
            IO.WriteLine("   Status report:");
            IO.Write("Klingon".Pluralize(_galaxy.KlingonCount));
            IO.WriteLine($" left:  {_galaxy.KlingonCount}");
            IO.WriteLine($"Mission must be completed in {_game.StardatesRemaining:0.#} stardates.");
    
            if (_galaxy.StarbaseCount > 0)
            {
                IO.Write($"The Federation is maintaining {_galaxy.StarbaseCount} ");
                IO.Write("starbase".Pluralize(_galaxy.StarbaseCount));
                IO.WriteLine(" in the galaxy.");
            }
            else
            {
                IO.WriteLine("Your stupidity has left you on your own in");
                IO.WriteLine("  the galaxy -- you have no starbases left!");
            }
    
            _enterprise.Execute(Command.DAM);
        }
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Systems/ComputerFunctions/TorpedoDataCalculator.cs
    ================================================
    using Games.Common.IO;
    using SuperStarTrek.Objects;
    using SuperStarTrek.Resources;
    using SuperStarTrek.Space;
    
    namespace SuperStarTrek.Systems.ComputerFunctions;
    
    internal class TorpedoDataCalculator : NavigationCalculator
    {
        private readonly Enterprise _enterprise;
    
        internal TorpedoDataCalculator(Enterprise enterprise, IReadWrite io)
            : base("Photon torpedo data", io)
        {
            _enterprise = enterprise;
        }
    
        internal override void Execute(Quadrant quadrant)
        {
            if (!quadrant.HasKlingons)
            {
                IO.WriteLine(Strings.NoEnemyShips);
                return;
            }
    
            IO.WriteLine("From Enterprise to Klingon battle cruiser".Pluralize(quadrant.KlingonCount));
    
            foreach (var klingon in quadrant.Klingons)
            {
                WriteDirectionAndDistance(_enterprise.SectorCoordinates, klingon.Sector);
            }
        }
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Systems/DamageControl.cs
    ================================================
    using Games.Common.IO;
    using SuperStarTrek.Commands;
    using SuperStarTrek.Objects;
    using SuperStarTrek.Space;
    
    namespace SuperStarTrek.Systems;
    
    internal class DamageControl : Subsystem
    {
        private readonly Enterprise _enterprise;
        private readonly IReadWrite _io;
    
        internal DamageControl(Enterprise enterprise, IReadWrite io)
            : base("Damage Control", Command.DAM, io)
        {
            _enterprise = enterprise;
            _io = io;
        }
    
        protected override CommandResult ExecuteCommandCore(Quadrant quadrant)
        {
            if (IsDamaged)
            {
                _io.WriteLine("Damage Control report not available");
            }
            else
            {
                _io.WriteLine();
                WriteDamageReport();
            }
    
            if (_enterprise.DamagedSystemCount > 0 && _enterprise.IsDocked)
            {
                if (quadrant.Starbase.TryRepair(_enterprise, out var repairTime))
                {
                    WriteDamageReport();
                    return CommandResult.Elapsed(repairTime);
                }
            }
    
            return CommandResult.Ok;
        }
    
        internal void WriteDamageReport()
        {
            _io.WriteLine();
            _io.WriteLine("Device             State of Repair");
            foreach (var system in _enterprise.Systems)
            {
                _io.Write(system.Name.PadRight(25));
                _io.WriteLine((int)(system.Condition * 100) * 0.01F);
            }
            _io.WriteLine();
        }
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Systems/LibraryComputer.cs
    ================================================
    using Games.Common.IO;
    using SuperStarTrek.Commands;
    using SuperStarTrek.Space;
    using SuperStarTrek.Systems.ComputerFunctions;
    
    namespace SuperStarTrek.Systems;
    
    internal class LibraryComputer : Subsystem
    {
        private readonly IReadWrite _io;
        private readonly ComputerFunction[] _functions;
    
        internal LibraryComputer(IReadWrite io, params ComputerFunction[] functions)
            : base("Library-Computer", Command.COM, io)
        {
            _io = io;
            _functions = functions;
        }
    
        protected override bool CanExecuteCommand() => IsOperational("Computer disabled");
    
        protected override CommandResult ExecuteCommandCore(Quadrant quadrant)
        {
            var index = GetFunctionIndex();
            _io.WriteLine();
    
            _functions[index].Execute(quadrant);
    
            return CommandResult.Ok;
        }
    
        private int GetFunctionIndex()
        {
            while (true)
            {
                var index = (int)_io.ReadNumber("Computer active and waiting command");
                if (index >= 0 && index <= 5) { return index; }
    
                for (int i = 0; i < _functions.Length; i++)
                {
                    _io.WriteLine($"   {i} = {_functions[i].Description}");
                }
            }
        }
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Systems/LongRangeSensors.cs
    ================================================
    using System.Linq;
    using Games.Common.IO;
    using SuperStarTrek.Commands;
    using SuperStarTrek.Space;
    
    namespace SuperStarTrek.Systems;
    
    internal class LongRangeSensors : Subsystem
    {
        private readonly Galaxy _galaxy;
        private readonly IReadWrite _io;
    
        internal LongRangeSensors(Galaxy galaxy, IReadWrite io)
            : base("Long Range Sensors", Command.LRS, io)
        {
            _galaxy = galaxy;
            _io = io;
        }
    
        protected override bool CanExecuteCommand() => IsOperational("{name} are inoperable");
    
        protected override CommandResult ExecuteCommandCore(Quadrant quadrant)
        {
            _io.WriteLine($"Long range scan for quadrant {quadrant.Coordinates}");
            _io.WriteLine("-------------------");
            foreach (var quadrants in _galaxy.GetNeighborhood(quadrant))
            {
                _io.WriteLine(": " + string.Join(" : ", quadrants.Select(q => q?.Scan() ?? "***")) + " :");
                _io.WriteLine("-------------------");
            }
    
            return CommandResult.Ok;
        }
    }
    
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Systems/PhaserControl.cs
    ================================================
    using System.Linq;
    using Games.Common.IO;
    using Games.Common.Randomness;
    using SuperStarTrek.Commands;
    using SuperStarTrek.Objects;
    using SuperStarTrek.Resources;
    using SuperStarTrek.Space;
    
    namespace SuperStarTrek.Systems;
    
    internal class PhaserControl : Subsystem
    {
        private readonly Enterprise _enterprise;
        private readonly IReadWrite _io;
        private readonly IRandom _random;
    
        internal PhaserControl(Enterprise enterprise, IReadWrite io, IRandom random)
            : base("Phaser Control", Command.PHA, io)
        {
            _enterprise = enterprise;
            _io = io;
            _random = random;
        }
    
        protected override bool CanExecuteCommand() => IsOperational("Phasers inoperative");
    
        protected override CommandResult ExecuteCommandCore(Quadrant quadrant)
        {
            if (!quadrant.HasKlingons)
            {
                _io.WriteLine(Strings.NoEnemyShips);
                return CommandResult.Ok;
            }
    
            if (_enterprise.Computer.IsDamaged)
            {
                _io.WriteLine("Computer failure hampers accuracy");
            }
    
            _io.Write($"Phasers locked on target;  ");
    
            var phaserStrength = GetPhaserStrength();
            if (phaserStrength < 0) { return CommandResult.Ok; }
    
            _enterprise.UseEnergy(phaserStrength);
    
            var perEnemyStrength = GetPerTargetPhaserStrength(phaserStrength, quadrant.KlingonCount);
    
            foreach (var klingon in quadrant.Klingons.ToList())
            {
                ResolveHitOn(klingon, perEnemyStrength, quadrant);
            }
    
            return quadrant.KlingonsFireOnEnterprise();
        }
    
        private float GetPhaserStrength()
        {
            while (true)
            {
                _io.WriteLine($"Energy available = {_enterprise.Energy} units");
                var phaserStrength = _io.ReadNumber("Number of units to fire");
    
                if (phaserStrength <= _enterprise.Energy) { return phaserStrength; }
            }
        }
    
        private float GetPerTargetPhaserStrength(float phaserStrength, int targetCount)
        {
            if (_enterprise.Computer.IsDamaged)
            {
                phaserStrength *= _random.NextFloat();
            }
    
            return phaserStrength / targetCount;
        }
    
        private void ResolveHitOn(Klingon klingon, float perEnemyStrength, Quadrant quadrant)
        {
            var distance = _enterprise.SectorCoordinates.GetDistanceTo(klingon.Sector);
            var hitStrength = (int)(perEnemyStrength / distance * (2 + _random.NextFloat()));
    
            if (klingon.TakeHit(hitStrength))
            {
                _io.WriteLine($"{hitStrength} unit hit on Klingon at sector {klingon.Sector}");
                _io.WriteLine(
                    klingon.Energy <= 0
                        ? quadrant.Remove(klingon)
                        : $"   (sensors show {klingon.Energy} units remaining)");
            }
            else
            {
                _io.WriteLine($"Sensors show no damage to enemy at {klingon.Sector}");
            }
        }
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Systems/PhotonTubes.cs
    ================================================
    using Games.Common.IO;
    using SuperStarTrek.Commands;
    using SuperStarTrek.Objects;
    using SuperStarTrek.Space;
    
    namespace SuperStarTrek.Systems;
    
    internal class PhotonTubes : Subsystem
    {
        private readonly int _tubeCount;
        private readonly Enterprise _enterprise;
        private readonly IReadWrite _io;
    
        internal PhotonTubes(int tubeCount, Enterprise enterprise, IReadWrite io)
            : base("Photon Tubes", Command.TOR, io)
        {
            TorpedoCount = _tubeCount = tubeCount;
            _enterprise = enterprise;
            _io = io;
        }
    
        internal int TorpedoCount { get; private set; }
    
        protected override bool CanExecuteCommand() => HasTorpedoes() && IsOperational("{name} are not operational");
    
        private bool HasTorpedoes()
        {
            if (TorpedoCount > 0) { return true; }
    
            _io.WriteLine("All photon torpedoes expended");
            return false;
        }
    
        protected override CommandResult ExecuteCommandCore(Quadrant quadrant)
        {
            if (!_io.TryReadCourse("Photon torpedo course", "Ensign Chekov", out var course))
            {
                return CommandResult.Ok;
            }
    
            TorpedoCount -= 1;
    
            var isHit = false;
            _io.WriteLine("Torpedo track:");
            foreach (var sector in course.GetSectorsFrom(_enterprise.SectorCoordinates))
            {
                _io.WriteLine($"                {sector}");
    
                if (quadrant.TorpedoCollisionAt(sector, out var message, out var gameOver))
                {
                    _io.WriteLine(message);
                    isHit = true;
                    if (gameOver) { return CommandResult.GameOver; }
                    break;
                }
            }
    
            if (!isHit) { _io.WriteLine("Torpedo missed!"); }
    
            return quadrant.KlingonsFireOnEnterprise();
        }
    
        internal void ReplenishTorpedoes() => TorpedoCount = _tubeCount;
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Systems/ShieldControl.cs
    ================================================
    using Games.Common.IO;
    using SuperStarTrek.Commands;
    using SuperStarTrek.Objects;
    using SuperStarTrek.Resources;
    using SuperStarTrek.Space;
    
    namespace SuperStarTrek.Systems;
    
    internal class ShieldControl : Subsystem
    {
        private readonly Enterprise _enterprise;
        private readonly IReadWrite _io;
    
        internal ShieldControl(Enterprise enterprise, IReadWrite io)
            : base("Shield Control", Command.SHE, io)
        {
            _enterprise = enterprise;
            _io = io;
        }
    
        internal float ShieldEnergy { get; set; }
    
        protected override bool CanExecuteCommand() => IsOperational("{name} inoperable");
    
        protected override CommandResult ExecuteCommandCore(Quadrant quadrant)
        {
            _io.WriteLine($"Energy available = {_enterprise.TotalEnergy}");
            var requested = _io.ReadNumber($"Number of units to shields");
    
            if (Validate(requested))
            {
                ShieldEnergy = requested;
                _io.Write(Strings.ShieldsSet, requested);
            }
            else
            {
                _io.WriteLine("");
            }
    
            return CommandResult.Ok;
        }
    
        private bool Validate(float requested)
        {
            if (requested > _enterprise.TotalEnergy)
            {
                _io.WriteLine("Shield Control reports, 'This is not the Federation Treasury.'");
                return false;
            }
    
            return requested >= 0 && requested != ShieldEnergy;
        }
    
        internal void AbsorbHit(int hitStrength) => ShieldEnergy -= hitStrength;
    
        internal void DropShields() => ShieldEnergy = 0;
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Systems/ShortRangeSensors.cs
    ================================================
    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Games.Common.IO;
    using SuperStarTrek.Commands;
    using SuperStarTrek.Objects;
    using SuperStarTrek.Resources;
    using SuperStarTrek.Space;
    
    namespace SuperStarTrek.Systems;
    
    internal class ShortRangeSensors : Subsystem
    {
        private readonly Enterprise _enterprise;
        private readonly Galaxy _galaxy;
        private readonly Game _game;
        private readonly IReadWrite _io;
    
        internal ShortRangeSensors(Enterprise enterprise, Galaxy galaxy, Game game, IReadWrite io)
            : base("Short Range Sensors", Command.SRS, io)
        {
            _enterprise = enterprise;
            _galaxy = galaxy;
            _game = game;
            _io = io;
        }
    
        protected override CommandResult ExecuteCommandCore(Quadrant quadrant)
        {
            if (_enterprise.IsDocked)
            {
                _io.WriteLine(Strings.ShieldsDropped);
            }
    
            if (Condition < 0)
            {
                _io.WriteLine(Strings.ShortRangeSensorsOut);
            }
    
            _io.WriteLine("---------------------------------");
            quadrant.GetDisplayLines()
                .Zip(GetStatusLines(), (sectors, status) => $" {sectors}         {status}")
                .ToList()
                .ForEach(l => _io.WriteLine(l));
            _io.WriteLine("---------------------------------");
    
            return CommandResult.Ok;
        }
    
        internal IEnumerable GetStatusLines()
        {
            yield return $"Stardate           {_game.Stardate}";
            yield return $"Condition          {_enterprise.Condition}";
            yield return $"Quadrant           {_enterprise.QuadrantCoordinates}";
            yield return $"Sector             {_enterprise.SectorCoordinates}";
            yield return $"Photon torpedoes   {_enterprise.PhotonTubes.TorpedoCount}";
            yield return $"Total energy       {Math.Ceiling(_enterprise.TotalEnergy)}";
            yield return $"Shields            {(int)_enterprise.ShieldControl.ShieldEnergy}";
            yield return $"Klingons remaining {_galaxy.KlingonCount}";
        }
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Systems/Subsystem.cs
    ================================================
    using Games.Common.IO;
    using SuperStarTrek.Commands;
    using SuperStarTrek.Space;
    
    namespace SuperStarTrek.Systems;
    
    internal abstract class Subsystem
    {
        private readonly IReadWrite _io;
    
        protected Subsystem(string name, Command command, IReadWrite io)
        {
            Name = name;
            Command = command;
            Condition = 0;
            _io = io;
        }
    
        internal string Name { get; }
    
        internal float Condition { get; private set; }
    
        internal bool IsDamaged => Condition < 0;
    
        internal Command Command { get; }
    
        protected virtual bool CanExecuteCommand() => true;
    
        protected bool IsOperational(string notOperationalMessage)
        {
            if (IsDamaged)
            {
                _io.WriteLine(notOperationalMessage.Replace("{name}", Name));
                return false;
            }
    
            return true;
        }
    
        internal CommandResult ExecuteCommand(Quadrant quadrant)
            => CanExecuteCommand() ? ExecuteCommandCore(quadrant) : CommandResult.Ok;
    
        protected abstract CommandResult ExecuteCommandCore(Quadrant quadrant);
    
        internal virtual void Repair()
        {
            if (IsDamaged)
            {
                Condition = 0;
            }
        }
    
        internal virtual bool Repair(float repairWorkDone)
        {
            if (IsDamaged)
            {
                Condition += repairWorkDone;
                if (Condition > -0.1f && Condition < 0)
                {
                    Condition = -0.1f;
                }
            }
    
            return !IsDamaged;
        }
    
        internal void TakeDamage(float damage) => Condition -= damage;
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Systems/WarpEngines.cs
    ================================================
    using System;
    using Games.Common.IO;
    using SuperStarTrek.Commands;
    using SuperStarTrek.Objects;
    using SuperStarTrek.Resources;
    using SuperStarTrek.Space;
    
    namespace SuperStarTrek.Systems
    {
        internal class WarpEngines : Subsystem
        {
            private readonly Enterprise _enterprise;
            private readonly IReadWrite _io;
    
            internal WarpEngines(Enterprise enterprise, IReadWrite io)
                : base("Warp Engines", Command.NAV, io)
            {
                _enterprise = enterprise;
                _io = io;
            }
    
            protected override CommandResult ExecuteCommandCore(Quadrant quadrant)
            {
                if (_io.TryReadCourse("Course", "   Lt. Sulu", out var course) &&
                    TryGetWarpFactor(out var warpFactor) &&
                    TryGetDistanceToMove(warpFactor, out var distanceToMove))
                {
                    var result = quadrant.KlingonsMoveAndFire();
                    if (result.IsGameOver) { return result; }
    
                    _enterprise.RepairSystems(warpFactor);
                    _enterprise.VaryConditionOfRandomSystem();
                    var timeElapsed = _enterprise.Move(course, warpFactor, distanceToMove);
    
                    if (_enterprise.IsDocked)
                    {
                        _enterprise.ShieldControl.DropShields();
                        _enterprise.Refuel();
                        _enterprise.PhotonTubes.ReplenishTorpedoes();
                    }
    
                    _enterprise.Quadrant.Display(Strings.NowEntering);
    
                    return CommandResult.Elapsed(timeElapsed);
                }
    
                return CommandResult.Ok;
            }
    
            private bool TryGetWarpFactor(out float warpFactor)
            {
                var maximumWarp = IsDamaged ? 0.2f : 8;
                if (_io.TryReadNumberInRange("Warp Factor", 0, maximumWarp, out warpFactor))
                {
                    return warpFactor > 0;
                }
    
                _io.WriteLine(
                    IsDamaged && warpFactor > maximumWarp
                        ? "Warp engines are damaged.  Maximum speed = warp 0.2"
                        : $"  Chief Engineer Scott reports, 'The engines won't take warp {warpFactor} !'");
    
                return false;
            }
    
            private bool TryGetDistanceToMove(float warpFactor, out int distanceToTravel)
            {
                distanceToTravel = (int)Math.Round(warpFactor * 8, MidpointRounding.AwayFromZero);
                if (distanceToTravel <= _enterprise.Energy) { return true; }
    
                _io.WriteLine("Engineering reports, 'Insufficient energy available");
                _io.WriteLine($"                      for maneuvering at warp {warpFactor} !'");
    
                if (distanceToTravel <= _enterprise.TotalEnergy && !_enterprise.ShieldControl.IsDamaged)
                {
                    _io.Write($"Deflector control room acknowledges {_enterprise.ShieldControl.ShieldEnergy} ");
                    _io.WriteLine("units of energy");
                    _io.WriteLine("                         presently deployed to shields.");
                }
    
                return false;
            }
        }
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/csharp/Utils/DirectionAndDistance.cs
    ================================================
    using System;
    using SuperStarTrek.Space;
    
    namespace SuperStarTrek.Utils
    {
        internal class DirectionAndDistance
        {
            private readonly float _fromX;
            private readonly float _fromY;
    
            private DirectionAndDistance(float fromX, float fromY)
            {
                _fromX = fromX;
                _fromY = fromY;
            }
    
            internal static DirectionAndDistance From(Coordinates coordinates) => From(coordinates.X, coordinates.Y);
    
            internal static DirectionAndDistance From(float x, float y) => new DirectionAndDistance(x, y);
    
            internal (float Direction, float Distance) To(Coordinates coordinates) => To(coordinates.X, coordinates.Y);
    
            internal (float Direction, float Distance) To(float x, float y)
            {
                var deltaX = x - _fromX;
                var deltaY = y - _fromY;
    
                return (GetDirection(deltaX, deltaY), GetDistance(deltaX, deltaY));
            }
    
            // The algorithm here is mathematically equivalent to the following code in the original,
            // where X is deltaY and A is deltaX
            //     8220 X=X-A:A=C1-W1:IFX<0THEN8350
            //     8250 IFA<0THEN8410
            //     8260 IFX>0THEN8280
            //     8270 IFA=0THENC1=5:GOTO8290
            //     8280 C1=1
            //     8290 IFABS(A)<=ABS(X)THEN8330
            //     8310 PRINT"DIRECTION =";C1+(((ABS(A)-ABS(X))+ABS(A))/ABS(A)):GOTO8460
            //     8330 PRINT"DIRECTION =";C1+(ABS(A)/ABS(X)):GOTO8460
            //     8350 IFA>0THENC1=3:GOTO8420
            //     8360 IFX<>0THENC1=5:GOTO8290
            //     8410 C1=7
            //     8420 IFABS(A)>=ABS(X)THEN8450
            //     8430 PRINT"DIRECTION =";C1+(((ABS(X)-ABS(A))+ABS(X))/ABS(X)):GOTO8460
            //     8450 PRINT"DIRECTION =";C1+(ABS(X)/ABS(A))
            //     8460 PRINT"DISTANCE =";SQR(X^2+A^2):IFH8=1THEN1990
            private static float GetDirection(float deltaX, float deltaY)
            {
                var deltaXDominant = Math.Abs(deltaX) > Math.Abs(deltaY);
                var fractionalPart = deltaXDominant ? deltaY / deltaX : -deltaX / deltaY;
                var nearestCardinal = deltaXDominant switch
                {
                    true => deltaX > 0 ? 7 : 3,
                    false => deltaY > 0 ? 1 : 5
                };
    
                var direction = nearestCardinal + fractionalPart;
                return direction < 1 ? direction + 8 : direction;
            }
    
            private static float GetDistance(float deltaX, float deltaY) =>
                (float)Math.Sqrt(Math.Pow(deltaX, 2) + Math.Pow(deltaY, 2));
        }
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/instructions.txt
    ================================================
    
    
    
    
    
    
    
    
    
    
    *************************************
    *                                   *
    *                                   *
    *      * * SUPER STAR TREK * *      *
    *                                   *
    *                                   *
    *************************************
    
    
    
    
    
    
    
    
          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), THAN 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 COMANDER OF THE STARSHIP
    \ENTERPRISE\; YOUR MISSION: TO SEEK AND DESTROY THE FLEET OF
    KLINGON WARWHIPS 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 QUADTANT.  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 TH UNITS DIGIT
         IS THE NUMBER OF STARS, THE TENS DIGIT IS THE NUMBER OF
         STARBASES, AND THE HUNDRESDS 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
         THAN THE STATUS DISPLAY TOTAL ENERGY INCLUDES SHIELD ENERGY
    
    \DAM\ COMMAND = DAMMAGE 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 SHOWES 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.
    
    
    ================================================
    FILE: 84_Super_Star_Trek/java/Enterprise.java
    ================================================
    import java.util.stream.IntStream;
    
    /**
     * The starship Enterprise.
     */
    public class Enterprise {
    
        public static final int COORD_X = 0;
        public static final int COORD_Y = 1;
    
        // devices
        static final int DEVICE_WARP_ENGINES = 1;
        static final int DEVICE_SHORT_RANGE_SENSORS = 2;
        static final int DEVICE_LONG_RANGE_SENSORS = 3;
        static final int DEVICE_PHASER_CONTROL = 4;
        static final int DEVICE_PHOTON_TUBES = 5;
        static final int DEVICE_DAMAGE_CONTROL = 6;
        static final int DEVICE_SHIELD_CONTROL = 7;
        static final int DEVICE_LIBRARY_COMPUTER = 8;
        final double[] deviceStatus = new double[9];   // 8  device damage stats
    
        // position
        final int[][] cardinalDirections = new int[10][3];   // 9x2 vectors in cardinal directions
        int quadrantX;
        int quadrantY;
        int sectorX;
        int sectorY;
    
        // ship status
        boolean docked = false;
        int energy = 3000;
        int torpedoes = 10;
        int shields = 0;
        double repairCost;
    
        final int initialEnergy = energy;
        final int initialTorpedoes = torpedoes;
    
        public Enterprise() {
            // random initial position
            this.setQuadrant(new int[]{ Util.fnr(), Util.fnr() });
            this.setSector(new int[]{ Util.fnr(), Util.fnr() });
            // init cardinal directions
            IntStream.range(1, 9).forEach(i -> {
                cardinalDirections[i][1] = 0;
                cardinalDirections[i][2] = 0;
            });
            cardinalDirections[3][1] = -1;
            cardinalDirections[2][1] = -1;
            cardinalDirections[4][1] = -1;
            cardinalDirections[4][2] = -1;
            cardinalDirections[5][2] = -1;
            cardinalDirections[6][2] = -1;
            cardinalDirections[1][2] = 1;
            cardinalDirections[2][2] = 1;
            cardinalDirections[6][1] = 1;
            cardinalDirections[7][1] = 1;
            cardinalDirections[8][1] = 1;
            cardinalDirections[8][2] = 1;
            cardinalDirections[9][2] = 1;
            // init devices
            IntStream.range(1, 8).forEach(i -> deviceStatus[i] = 0);
        }
    
        public int getShields() {
            return shields;
        }
    
        /**
         * Enterprise is hit by enemies.
         * @param hits the number of hit points
         */
        public void sufferHitPoints(int hits) {
            this.shields = shields - hits;
        }
    
        public int getEnergy() {
            return energy;
        }
    
        public void replenishSupplies() {
            this.energy = this.initialEnergy;
            this.torpedoes = this.initialTorpedoes;
        }
    
        public void decreaseEnergy(final double amount) {
            this.energy -= amount;
        }
    
        public void decreaseTorpedoes(final int amount) {
            torpedoes -= amount;
        }
    
        public void dropShields() {
            this.shields = 0;
        }
    
        public int getTotalEnergy() {
            return (shields + energy);
        }
    
        public int getInitialEnergy() {
            return initialEnergy;
        }
    
        public int getTorpedoes() {
            return torpedoes;
        }
    
        public double[] getDeviceStatus() {
            return deviceStatus;
        }
    
        public int[][] getCardinalDirections() {
            return cardinalDirections;
        }
    
        public void setDeviceStatus(final int device, final double status) {
            this.deviceStatus[device] = status;
        }
    
        public boolean isDocked() {
            return docked;
        }
    
        public void setDocked(boolean docked) {
            this.docked = docked;
        }
    
        public int[] getQuadrant() {
            return new int[] {quadrantX, quadrantY};
        }
    
        public void setQuadrant(final int[] quadrant) {
            this.quadrantX = quadrant[COORD_X];
            this.quadrantY = quadrant[COORD_Y];
        }
    
        public int[] getSector() {
            return new int[] {sectorX, sectorY};
        }
    
        public void setSector(final int[] sector) {
            this.sectorX = sector[COORD_X];
            this.sectorY = sector[COORD_Y];
        }
    
        public int[] moveShip(final float course, final int n, final String quadrantMap, final double stardate, final double initialStardate, final int missionDuration, final GameCallback callback) {
            int ic1 = Util.toInt(course);
            float x1 = cardinalDirections[ic1][1] + (cardinalDirections[ic1 + 1][1] - cardinalDirections[ic1][1]) * (course - ic1);
            float x = sectorX;
            float y = sectorY;
            float x2 = cardinalDirections[ic1][2] + (cardinalDirections[ic1 + 1][2] - cardinalDirections[ic1][2]) * (course - ic1);
            final int initialQuadrantX = quadrantX;
            final int initialQuadrantY = quadrantY;
            for (int i = 1; i <= n; i++) {
                sectorX += x1;
                sectorY += x2;
                if (sectorX < 1 || sectorX >= 9 || sectorY < 1 || sectorY >= 9) {
                    // exceeded quadrant limits
                    x = 8 * quadrantX + x + n * x1;
                    y = 8 * quadrantY + y + n * x2;
                    quadrantX = Util.toInt(x / 8);
                    quadrantY = Util.toInt(y / 8);
                    sectorX = Util.toInt(x - quadrantX * 8);
                    sectorY = Util.toInt(y - quadrantY * 8);
                    if (sectorX == 0) {
                        quadrantX = quadrantX - 1;
                        sectorX = 8;
                    }
                    if (sectorY == 0) {
                        quadrantY = quadrantY - 1;
                        sectorY = 8;
                    }
                    boolean hitEdge = false;
                    if (quadrantX < 1) {
                        hitEdge = true;
                        quadrantX = 1;
                        sectorX = 1;
                    }
                    if (quadrantX > 8) {
                        hitEdge = true;
                        quadrantX = 8;
                        sectorX = 8;
                    }
                    if (quadrantY < 1) {
                        hitEdge = true;
                        quadrantY = 8;
                        sectorY = 8;
                    }
                    if (quadrantY > 8) {
                        hitEdge = true;
                        quadrantY = 8;
                        sectorY = 8;
                    }
                    if (hitEdge) {
                        Util.println("LT. UHURA REPORTS MESSAGE FROM STARFLEET COMMAND:");
                        Util.println("  'PERMISSION TO ATTEMPT CROSSING OF GALACTIC PERIMETER");
                        Util.println("  IS HEREBY *DENIED*.  SHUT DOWN YOUR ENGINES.'");
                        Util.println("CHIEF ENGINEER SCOTT REPORTS  'WARP ENGINES SHUT DOWN");
                        Util.println("  AT SECTOR " + sectorX + "," + sectorY + " OF QUADRANT " + quadrantX + "," + quadrantY + ".'");
                        if (stardate > initialStardate + missionDuration) callback.endGameFail(false);
                    }
                    if (8 * quadrantX + quadrantY == 8 * initialQuadrantX + initialQuadrantY) {
                        break;
                    }
                    callback.incrementStardate(1);
                    maneuverEnergySR(n);
                    callback.enterNewQuadrant();
                    return this.getSector();
                } else {
                    int S8 = Util.toInt(sectorX) * 24 + Util.toInt(sectorY) * 3 - 26; // S8 = pos
                    if (!("  ".equals(Util.midStr(quadrantMap, S8, 2)))) {
                        sectorX = Util.toInt(sectorX - x1);
                        sectorY = Util.toInt(sectorY - x2);
                        Util.println("WARP ENGINES SHUT DOWN AT ");
                        Util.println("SECTOR " + sectorX + "," + sectorY + " DUE TO BAD NAVIGATION");
                        break;
                    }
                }
            }
            sectorX = Util.toInt(sectorX);
            sectorY = Util.toInt(sectorY);
            return this.getSector();
        }
    
        void randomRepairCost() {
            repairCost = .5 * Util.random();
        }
    
        public void repairDamagedDevices(final float warp) {
            // repair damaged devices and print damage report
            for (int i = 1; i <= 8; i++) {
                if (deviceStatus[i] < 0) {
                    deviceStatus[i] += Math.min(warp, 1);
                    if ((deviceStatus[i] > -.1) && (deviceStatus[i] < 0)) {
                        deviceStatus[i] = -.1;
                        break;
                    } else if (deviceStatus[i] >= 0) {
                        Util.println("DAMAGE CONTROL REPORT:  ");
                        Util.println(Util.tab(8) + printDeviceName(i) + " REPAIR COMPLETED.");
                    }
                }
            }
        }
    
        public void maneuverEnergySR(final int N) {
            energy = energy - N - 10;
            if (energy >= 0) return;
            Util.println("SHIELD CONTROL SUPPLIES ENERGY TO COMPLETE THE MANEUVER.");
            shields = shields + energy;
            energy = 0;
            if (shields <= 0) shields = 0;
        }
    
        void shieldControl() {
            if (deviceStatus[DEVICE_SHIELD_CONTROL] < 0) {
                Util.println("SHIELD CONTROL INOPERABLE");
                return;
            }
            Util.println("ENERGY AVAILABLE = " + (energy + shields));
            int energyToShields = Util.toInt(Util.inputFloat("NUMBER OF UNITS TO SHIELDS"));
            if (energyToShields < 0 || shields == energyToShields) {
                Util.println("");
                return;
            }
            if (energyToShields > energy + energyToShields) {
                Util.println("SHIELD CONTROL REPORTS  'THIS IS NOT THE FEDERATION TREASURY.'");
                Util.println("");
                return;
            }
            energy = energy + shields - energyToShields;
            shields = energyToShields;
            Util.println("DEFLECTOR CONTROL ROOM REPORT:");
            Util.println("  'SHIELDS NOW AT " + Util.toInt(shields) + " UNITS PER YOUR COMMAND.'");
        }
    
        void damageControl(GameCallback callback) {
            if (deviceStatus[DEVICE_DAMAGE_CONTROL] < 0) {
                Util.println("DAMAGE CONTROL REPORT NOT AVAILABLE");
            } else {
                Util.println("\nDEVICE             STATE OF REPAIR");
                for (int deviceNr = 1; deviceNr <= 8; deviceNr++) {
                    Util.print(printDeviceName(deviceNr) + Util.leftStr(GalaxyMap.QUADRANT_ROW, 25 - Util.strlen(printDeviceName(deviceNr))) + " " + Util.toInt(deviceStatus[deviceNr] * 100) * .01 + "\n");
                }
            }
            if (!docked) return;
    
            double deltaToRepair = 0;
            for (int i = 1; i <= 8; i++) {
                if (deviceStatus[i] < 0) deltaToRepair += .1;
            }
            if (deltaToRepair > 0) {
                deltaToRepair += repairCost;
                if (deltaToRepair >= 1) deltaToRepair = .9;
                Util.println("TECHNICIANS STANDING BY TO EFFECT REPAIRS TO YOUR SHIP;");
                Util.println("ESTIMATED TIME TO REPAIR:'" + .01 * Util.toInt(100 * deltaToRepair) + " STARDATES");
                final String reply = Util.inputStr("WILL YOU AUTHORIZE THE REPAIR ORDER (Y/N)");
                if ("Y".equals(reply)) {
                    for (int deviceNr = 1; deviceNr <= 8; deviceNr++) {
                        if (deviceStatus[deviceNr] < 0) deviceStatus[deviceNr] = 0;
                    }
                    callback.incrementStardate(deltaToRepair + .1);
                }
            }
        }
    
        public static String printDeviceName(final int deviceNumber) {  // 8790
            // PRINTS DEVICE NAME
            switch (deviceNumber) {
                case DEVICE_WARP_ENGINES:
                    return "WARP ENGINES";
                case DEVICE_SHORT_RANGE_SENSORS:
                    return "SHORT RANGE SENSORS";
                case DEVICE_LONG_RANGE_SENSORS:
                    return "LONG RANGE SENSORS";
                case DEVICE_PHASER_CONTROL:
                    return "PHASER CONTROL";
                case DEVICE_PHOTON_TUBES:
                    return "PHOTON TUBES";
                case DEVICE_DAMAGE_CONTROL:
                    return "DAMAGE CONTROL";
                case DEVICE_SHIELD_CONTROL:
                    return "SHIELD CONTROL";
                case DEVICE_LIBRARY_COMPUTER:
                    return "LIBRARY-COMPUTER";
            }
            return "";
        }
    
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/java/GalaxyMap.java
    ================================================
    import java.util.stream.IntStream;
    
    /**
     * Map of the galaxy divided in Quadrants and Sectors,
     * populated with stars, starbases, klingons, and the Enterprise.
     */
    public class GalaxyMap {
    
        // markers
        static final String MARKER_EMPTY = "   ";
        static final String MARKER_ENTERPRISE = "<*>";
        static final String MARKER_KLINGON = "+K+";
        static final String MARKER_STARBASE = ">!<";
        static final String MARKER_STAR = " * ";
    
        static final int AVG_KLINGON_SHIELD_ENERGY = 200;
    
        // galaxy map
        public static final String QUADRANT_ROW = "                         ";
        String quadrantMap = QUADRANT_ROW + QUADRANT_ROW + QUADRANT_ROW + QUADRANT_ROW + QUADRANT_ROW + QUADRANT_ROW + QUADRANT_ROW + Util.leftStr(QUADRANT_ROW, 17);       // current quadrant map
        final int[][] galaxy = new int[9][9];    // 8x8 galaxy map G
        final int[][] klingonQuadrants = new int[4][4];    // 3x3 position of klingons K
        final int[][] chartedGalaxy = new int[9][9];    // 8x8 charted galaxy map Z
    
        // galaxy state
        int basesInGalaxy = 0;
        int remainingKlingons;
        int klingonsInGalaxy = 0;
        final Enterprise enterprise = new Enterprise();
    
        // quadrant state
        int klingons = 0;
        int starbases = 0;
        int stars = 0;
        int starbaseX = 0; // X coordinate of starbase
        int starbaseY = 0; // Y coord of starbase
    
        public Enterprise getEnterprise() {
            return enterprise;
        }
    
        public int getBasesInGalaxy() {
            return basesInGalaxy;
        }
    
        public int getRemainingKlingons() {
            return remainingKlingons;
        }
    
        public int getKlingonsInGalaxy() {
            return klingonsInGalaxy;
        }
    
        double fnd(int i) {
            return Math.sqrt((klingonQuadrants[i][1] - enterprise.getSector()[Enterprise.COORD_X]) ^ 2 + (klingonQuadrants[i][2] - enterprise.getSector()[Enterprise.COORD_Y]) ^ 2);
        }
    
        public GalaxyMap() {
            int quadrantX = enterprise.getQuadrant()[Enterprise.COORD_X];
            int quadrantY = enterprise.getQuadrant()[Enterprise.COORD_Y];
            // populate Klingons, Starbases, Stars
            IntStream.range(1, 8).forEach(x -> {
                IntStream.range(1, 8).forEach(y -> {
                    klingons = 0;
                    chartedGalaxy[x][y] = 0;
                    float random = Util.random();
                    if (random > .98) {
                        klingons = 3;
                        klingonsInGalaxy += 3;
                    } else if (random > .95) {
                        klingons = 2;
                        klingonsInGalaxy += 2;
                    } else if (random > .80) {
                        klingons = 1;
                        klingonsInGalaxy += 1;
                    }
                    starbases = 0;
                    if (Util.random() > .96) {
                        starbases = 1;
                        basesInGalaxy = +1;
                    }
                    galaxy[x][y] = klingons * 100 + starbases * 10 + Util.fnr();
                });
            });
            if (basesInGalaxy == 0) {
                if (galaxy[quadrantX][quadrantY] < 200) {
                    galaxy[quadrantX][quadrantY] = galaxy[quadrantX][quadrantY] + 120;
                    klingonsInGalaxy = +1;
                }
                basesInGalaxy = 1;
                galaxy[quadrantX][quadrantY] = +10;
                enterprise.setQuadrant(new int[]{ Util.fnr(), Util.fnr() });
            }
            remainingKlingons = klingonsInGalaxy;
        }
    
        void newQuadrant(final double stardate, final double initialStardate) {   // 1320
            final int quadrantX = enterprise.getQuadrant()[Enterprise.COORD_X];
            final int quadrantY = enterprise.getQuadrant()[Enterprise.COORD_Y];
            klingons = 0;
            starbases = 0;
            stars = 0;
            enterprise.randomRepairCost();
            chartedGalaxy[quadrantX][quadrantY] = galaxy[quadrantX][quadrantY];
            if (!(quadrantX < 1 || quadrantX > 8 || quadrantY < 1 || quadrantY > 8)) {
                final String quadrantName = getQuadrantName(false, quadrantX, quadrantY);
                if (initialStardate == stardate) {
                    Util.println("YOUR MISSION BEGINS WITH YOUR STARSHIP LOCATED\n" +
                            "IN THE GALACTIC QUADRANT, '" + quadrantName + "'.");
                } else {
                    Util.println("NOW ENTERING " + quadrantName + " QUADRANT . . .");
                }
                Util.println("");
                klingons = (int) Math.round(galaxy[quadrantX][quadrantY] * .01);
                starbases = (int) Math.round(galaxy[quadrantX][quadrantY] * .1) - 10 * klingons;
                stars = galaxy[quadrantX][quadrantY] - 100 * klingons - 10 * starbases;
                if (klingons != 0) {
                    Util.println("COMBAT AREA      CONDITION RED");
                    if (enterprise.getShields() <= 200) {
                        Util.println("   SHIELDS DANGEROUSLY LOW");
                    }
                }
                IntStream.range(1, 3).forEach(i -> {
                    klingonQuadrants[i][1] = 0;
                    klingonQuadrants[i][2] = 0;
                });
            }
            IntStream.range(1, 3).forEach(i -> {
                klingonQuadrants[i][3] = 0;
            });
            // position enterprise in quadrant
            insertMarker(MARKER_ENTERPRISE, enterprise.getSector()[Enterprise.COORD_X], enterprise.getSector()[Enterprise.COORD_Y]);
            // position klingons
            if (klingons >= 1) {
                for (int i = 1; i <= klingons; i++) {
                    final int[] emptyCoordinate = findEmptyPlaceInQuadrant(quadrantMap);
                    insertMarker(MARKER_KLINGON, emptyCoordinate[0], emptyCoordinate[1]);
                    klingonQuadrants[i][1] = emptyCoordinate[0];
                    klingonQuadrants[i][2] = emptyCoordinate[1];
                    klingonQuadrants[i][3] = (int) Math.round(AVG_KLINGON_SHIELD_ENERGY * (0.5 + Util.random()));
                }
            }
            // position bases
            if (starbases >= 1) {
                final int[] emptyCoordinate = findEmptyPlaceInQuadrant(quadrantMap);
                starbaseX = emptyCoordinate[0];
                starbaseY = emptyCoordinate[1];
                insertMarker(MARKER_STARBASE, emptyCoordinate[0], emptyCoordinate[1]);
            }
            // position stars
            for (int i = 1; i <= stars; i++) {
                final int[] emptyCoordinate = findEmptyPlaceInQuadrant(quadrantMap);
                insertMarker(MARKER_STAR, emptyCoordinate[0], emptyCoordinate[1]);
            }
        }
    
        public void klingonsMoveAndFire(GameCallback callback) {
            for (int i = 1; i <= klingons; i++) {
                if (klingonQuadrants[i][3] == 0) continue;
                insertMarker(MARKER_EMPTY, klingonQuadrants[i][1], klingonQuadrants[i][2]);
                final int[] newCoords = findEmptyPlaceInQuadrant(quadrantMap);
                klingonQuadrants[i][1] = newCoords[0];
                klingonQuadrants[i][2] = newCoords[1];
                insertMarker(MARKER_KLINGON, klingonQuadrants[i][1], klingonQuadrants[i][2]);
            }
            klingonsShoot(callback);
        }
    
        void klingonsShoot(GameCallback callback) {
            if (klingons <= 0) return; // no klingons
            if (enterprise.isDocked()) {
                Util.println("STARBASE SHIELDS PROTECT THE ENTERPRISE");
                return;
            }
            for (int i = 1; i <= 3; i++) {
                if (klingonQuadrants[i][3] <= 0) continue;
                int hits = Util.toInt((klingonQuadrants[i][3] / fnd(1)) * (2 + Util.random()));
                enterprise.sufferHitPoints(hits);
                klingonQuadrants[i][3] = Util.toInt(klingonQuadrants[i][3] / (3 + Util.random()));
                Util.println(hits + " UNIT HIT ON ENTERPRISE FROM SECTOR " + klingonQuadrants[i][1] + "," + klingonQuadrants[i][2]);
                if (enterprise.getShields() <= 0) callback.endGameFail(true);
                Util.println("      ");
                if (hits < 20) continue;
                if ((Util.random() > .6) || (hits / enterprise.getShields() <= .02)) continue;
                int randomDevice = Util.fnr();
                enterprise.setDeviceStatus(randomDevice, enterprise.getDeviceStatus()[randomDevice]- hits / enterprise.getShields() - .5 * Util.random());
                Util.println("DAMAGE CONTROL REPORTS " + Enterprise.printDeviceName(randomDevice) + " DAMAGED BY THE HIT'");
            }
        }
    
        public void moveEnterprise(final float course, final float warp, final int n, final double stardate, final double initialStardate, final int missionDuration, final GameCallback callback) {
            insertMarker(MARKER_EMPTY, Util.toInt(enterprise.getSector()[Enterprise.COORD_X]), Util.toInt(enterprise.getSector()[Enterprise.COORD_Y]));
            final int[] sector = enterprise.moveShip(course, n, quadrantMap, stardate, initialStardate, missionDuration, callback);
            int sectorX = sector[Enterprise.COORD_X];
            int sectorY = sector[Enterprise.COORD_Y];
            insertMarker(MARKER_ENTERPRISE, Util.toInt(sectorX), Util.toInt(sectorY));
            enterprise.maneuverEnergySR(n);
            double stardateDelta = 1;
            if (warp < 1) stardateDelta = .1 * Util.toInt(10 * warp);
            callback.incrementStardate(stardateDelta);
            if (stardate > initialStardate + missionDuration) callback.endGameFail(false);
        }
    
        void shortRangeSensorScan(final double stardate) {
            final int sectorX = enterprise.getSector()[Enterprise.COORD_X];
            final int sectorY = enterprise.getSector()[Enterprise.COORD_Y];
            boolean docked = false;
            String shipCondition; // ship condition (docked, red, yellow, green)
            for (int i = sectorX - 1; i <= sectorX + 1; i++) {
                for (int j = sectorY - 1; j <= sectorY + 1; j++) {
                    if ((Util.toInt(i) >= 1) && (Util.toInt(i) <= 8) && (Util.toInt(j) >= 1) && (Util.toInt(j) <= 8)) {
                        if (compareMarker(quadrantMap, MARKER_STARBASE, i, j)) {
                            docked = true;
                        }
                    }
                }
            }
            if (!docked) {
                enterprise.setDocked(false);
                if (klingons > 0) {
                    shipCondition = "*RED*";
                } else {
                    shipCondition = "GREEN";
                    if (enterprise.getEnergy() < enterprise.getInitialEnergy() * .1) {
                        shipCondition = "YELLOW";
                    }
                }
            } else {
                enterprise.setDocked(true);
                shipCondition = "DOCKED";
                enterprise.replenishSupplies();
                Util.println("SHIELDS DROPPED FOR DOCKING PURPOSES");
                enterprise.dropShields();
            }
            if (enterprise.getDeviceStatus()[Enterprise.DEVICE_SHORT_RANGE_SENSORS] < 0) { // are short range sensors out?
                Util.println("\n*** SHORT RANGE SENSORS ARE OUT ***\n");
                return;
            }
            final String row = "---------------------------------";
            Util.println(row);
            for (int i = 1; i <= 8; i++) {
                String sectorMapRow = "";
                for (int j = (i - 1) * 24 + 1; j <= (i - 1) * 24 + 22; j += 3) {
                    sectorMapRow += " " + Util.midStr(quadrantMap, j, 3);
                }
                switch (i) {
                    case 1:
                        Util.println(sectorMapRow + "        STARDATE           " + Util.toInt(stardate * 10) * .1);
                        break;
                    case 2:
                        Util.println(sectorMapRow + "        CONDITION          " + shipCondition);
                        break;
                    case 3:
                        Util.println(sectorMapRow + "        QUADRANT           " + enterprise.getQuadrant()[Enterprise.COORD_X] + "," + enterprise.getQuadrant()[Enterprise.COORD_Y]);
                        break;
                    case 4:
                        Util.println(sectorMapRow + "        SECTOR             " + sectorX + "," + sectorY);
                        break;
                    case 5:
                        Util.println(sectorMapRow + "        PHOTON TORPEDOES   " + Util.toInt(enterprise.getTorpedoes()));
                        break;
                    case 6:
                        Util.println(sectorMapRow + "        TOTAL ENERGY       " + Util.toInt(enterprise.getTotalEnergy()));
                        break;
                    case 7:
                        Util.println(sectorMapRow + "        SHIELDS            " + Util.toInt(enterprise.getShields()));
                        break;
                    case 8:
                        Util.println(sectorMapRow + "        KLINGONS REMAINING " + Util.toInt(klingonsInGalaxy));
                }
            }
            Util.println(row);
        }
    
        void longRangeSensorScan() {
            final int quadrantX = enterprise.getQuadrant()[Enterprise.COORD_X];
            final int quadrantY = enterprise.getQuadrant()[Enterprise.COORD_Y];
            if (enterprise.getDeviceStatus()[Enterprise.DEVICE_LONG_RANGE_SENSORS] < 0) {
                Util.println("LONG RANGE SENSORS ARE INOPERABLE");
                return;
            }
            Util.println("LONG RANGE SCAN FOR QUADRANT " + quadrantX + "," + quadrantY);
            final String rowStr = "-------------------";
            Util.println(rowStr);
            final int[] n = new int[4];
            for (int i = quadrantX - 1; i <= quadrantX + 1; i++) {
                n[1] = -1;
                n[2] = -2;
                n[3] = -3;
                for (int j = quadrantY - 1; j <= quadrantY + 1; j++) {
                    if (i > 0 && i < 9 && j > 0 && j < 9) {
                        n[j - quadrantY + 2] = galaxy[i][j];
                        chartedGalaxy[i][j] = galaxy[i][j];
                    }
                }
                for (int l = 1; l <= 3; l++) {
                    Util.print(": ");
                    if (n[l] < 0) {
                        Util.print("*** ");
                        continue;
                    }
                    Util.print(Util.rightStr(Integer.toString(n[l] + 1000), 3) + " ");
                }
                Util.println(": \n" + rowStr);
            }
        }
    
        void firePhasers(GameCallback callback) {
            final double[] deviceStatus = enterprise.getDeviceStatus();
            final int quadrantX = enterprise.getQuadrant()[Enterprise.COORD_X];
            final int quadrantY = enterprise.getQuadrant()[Enterprise.COORD_Y];
            if (deviceStatus[Enterprise.DEVICE_PHASER_CONTROL] < 0) {
                Util.println("PHASERS INOPERATIVE");
                return;
            }
            if (klingons <= 0) {
                printNoEnemyShipsMessage();
                return;
            }
            if (deviceStatus[Enterprise.DEVICE_LIBRARY_COMPUTER] < 0) Util.println("COMPUTER FAILURE HAMPERS ACCURACY");
            Util.println("PHASERS LOCKED ON TARGET;  ");
            int nrUnitsToFire;
            while (true) {
                Util.println("ENERGY AVAILABLE = " + enterprise.getEnergy() + " UNITS");
                nrUnitsToFire = Util.toInt(Util.inputFloat("NUMBER OF UNITS TO FIRE"));
                if (nrUnitsToFire <= 0) return;
                if (enterprise.getEnergy() - nrUnitsToFire >= 0) break;
            }
            enterprise.decreaseEnergy(nrUnitsToFire);
            if (deviceStatus[Enterprise.DEVICE_SHIELD_CONTROL] < 0) nrUnitsToFire = Util.toInt(nrUnitsToFire * Util.random());
            int h1 = Util.toInt(nrUnitsToFire / klingons);
            for (int i = 1; i <= 3; i++) {
                if (klingonQuadrants[i][3] <= 0) break;
                int hitPoints = Util.toInt((h1 / fnd(0)) * (Util.random() + 2));
                if (hitPoints <= .15 * klingonQuadrants[i][3]) {
                    Util.println("SENSORS SHOW NO DAMAGE TO ENEMY AT " + klingonQuadrants[i][1] + "," + klingonQuadrants[i][2]);
                    continue;
                }
                klingonQuadrants[i][3] = klingonQuadrants[i][3] - hitPoints;
                Util.println(hitPoints + " UNIT HIT ON KLINGON AT SECTOR " + klingonQuadrants[i][1] + "," + klingonQuadrants[i][2]);
                if (klingonQuadrants[i][3] <= 0) {
                    Util.println("*** KLINGON DESTROYED ***");
                    klingons -= 1;
                    klingonsInGalaxy -= 1;
                    insertMarker(MARKER_EMPTY, klingonQuadrants[i][1], klingonQuadrants[i][2]);
                    klingonQuadrants[i][3] = 0;
                    galaxy[quadrantX][quadrantY] -= 100;
                    chartedGalaxy[quadrantX][quadrantY] = galaxy[quadrantX][quadrantY];
                    if (klingonsInGalaxy <= 0) callback.endGameSuccess();
                } else {
                    Util.println("   (SENSORS SHOW " + klingonQuadrants[i][3] + " UNITS REMAINING)");
                }
            }
            klingonsShoot(callback);
        }
    
        void firePhotonTorpedo(final double stardate, final double initialStardate, final double missionDuration, GameCallback callback) {
            if (enterprise.getTorpedoes() <= 0) {
                Util.println("ALL PHOTON TORPEDOES EXPENDED");
                return;
            }
            if (enterprise.getDeviceStatus()[Enterprise.DEVICE_PHOTON_TUBES] < 0) {
                Util.println("PHOTON TUBES ARE NOT OPERATIONAL");
            }
            float c1 = Util.inputFloat("PHOTON TORPEDO COURSE (1-9)");
            if (c1 == 9) c1 = 1;
            if (c1 < 1 && c1 >= 9) {
                Util.println("ENSIGN CHEKOV REPORTS,  'INCORRECT COURSE DATA, SIR!'");
                return;
            }
            int ic1 = Util.toInt(c1);
            final int[][] cardinalDirections = enterprise.getCardinalDirections();
            float x1 = cardinalDirections[ic1][1] + (cardinalDirections[ic1 + 1][1] - cardinalDirections[ic1][1]) * (c1 - ic1);
            enterprise.decreaseEnergy(2);
            enterprise.decreaseTorpedoes(1);
            float x2 = cardinalDirections[ic1][2] + (cardinalDirections[ic1 + 1][2] - cardinalDirections[ic1][2]) * (c1 - ic1);
            float x = enterprise.getSector()[Enterprise.COORD_X];
            float y = enterprise.getSector()[Enterprise.COORD_Y];
            Util.println("TORPEDO TRACK:");
            while (true) {
                x = x + x1;
                y = y + x2;
                int x3 = Math.round(x);
                int y3 = Math.round(y);
                if (x3 < 1 || x3 > 8 || y3 < 1 || y3 > 8) {
                    Util.println("TORPEDO MISSED"); // 5490
                    klingonsShoot(callback);
                    return;
                }
                Util.println("               " + x3 + "," + y3);
                if (compareMarker(quadrantMap, MARKER_EMPTY, Util.toInt(x), Util.toInt(y)))  {
                    continue;
                } else if (compareMarker(quadrantMap, MARKER_KLINGON, Util.toInt(x), Util.toInt(y))) {
                    Util.println("*** KLINGON DESTROYED ***");
                    klingons = klingons - 1;
                    klingonsInGalaxy = klingonsInGalaxy - 1;
                    if (klingonsInGalaxy <= 0) callback.endGameSuccess();
                    for (int i = 1; i <= 3; i++) {
                        if (x3 == klingonQuadrants[i][1] && y3 == klingonQuadrants[i][2]) break;
                    }
                    int i = 3;
                    klingonQuadrants[i][3] = 0;
                } else if (compareMarker(quadrantMap, MARKER_STAR, Util.toInt(x), Util.toInt(y))) {
                    Util.println("STAR AT " + x3 + "," + y3 + " ABSORBED TORPEDO ENERGY.");
                    klingonsShoot(callback);
                    return;
                } else if (compareMarker(quadrantMap, MARKER_STARBASE, Util.toInt(x), Util.toInt(y))) {
                    Util.println("*** STARBASE DESTROYED ***");
                    starbases = starbases - 1;
                    basesInGalaxy = basesInGalaxy - 1;
                    if (basesInGalaxy == 0 && klingonsInGalaxy <= stardate - initialStardate - missionDuration) {
                        Util.println("THAT DOES IT, CAPTAIN!!  YOU ARE HEREBY RELIEVED OF COMMAND");
                        Util.println("AND SENTENCED TO 99 STARDATES AT HARD LABOR ON CYGNUS 12!!");
                        callback.endGameFail(false);
                    } else {
                        Util.println("STARFLEET COMMAND REVIEWING YOUR RECORD TO CONSIDER");
                        Util.println("COURT MARTIAL!");
                        enterprise.setDocked(false);
                    }
                }
                insertMarker(MARKER_EMPTY, Util.toInt(x), Util.toInt(y));
                final int quadrantX = enterprise.getQuadrant()[Enterprise.COORD_X];
                final int quadrantY = enterprise.getQuadrant()[Enterprise.COORD_Y];
                galaxy[quadrantX][quadrantY] = klingons * 100 + starbases * 10 + stars;
                chartedGalaxy[quadrantX][quadrantY] = galaxy[quadrantX][quadrantY];
                klingonsShoot(callback);
            }
        }
    
        public void cumulativeGalacticRecord(final boolean cumulativeReport) {
            final int quadrantX = enterprise.getQuadrant()[Enterprise.COORD_X];
            final int quadrantY = enterprise.getQuadrant()[Enterprise.COORD_Y];
            if (cumulativeReport) {
                Util.println("");
                Util.println("        ");
                Util.println("COMPUTER RECORD OF GALAXY FOR QUADRANT " + quadrantX + "," + quadrantY);
                Util.println("");
            } else {
                Util.println("                        THE GALAXY");
            }
            Util.println("       1     2     3     4     5     6     7     8");
            final String rowDivider = "     ----- ----- ----- ----- ----- ----- ----- -----";
            Util.println(rowDivider);
            for (int i = 1; i <= 8; i++) {
                Util.print(i + "  ");
                if (cumulativeReport) {
                    int y = 1;
                    String quadrantName = getQuadrantName(false, i, y);
                    int tabLen = Util.toInt(15 - .5 * Util.strlen(quadrantName));
                    Util.println(Util.tab(tabLen) + quadrantName);
                    y = 5;
                    quadrantName = getQuadrantName(false, i, y);
                    tabLen = Util.toInt(39 - .5 * Util.strlen(quadrantName));
                    Util.println(Util.tab(tabLen) + quadrantName);
                } else {
                    for (int j = 1; j <= 8; j++) {
                        Util.print("   ");
                        if (chartedGalaxy[i][j] == 0) {
                            Util.print("***");
                        } else {
                            Util.print(Util.rightStr(Integer.toString(chartedGalaxy[i][j] + 1000), 3));
                        }
                    }
                }
                Util.println("");
                Util.println(rowDivider);
            }
            Util.println("");
        }
    
        public void photonTorpedoData() {
            int sectorX = enterprise.getSector()[Enterprise.COORD_X];
            int sectorY = enterprise.getSector()[Enterprise.COORD_Y];
            if (klingons <= 0) {
                printNoEnemyShipsMessage();
                return;
            }
            Util.println("FROM ENTERPRISE TO KLINGON BATTLE CRUISER" + ((klingons > 1)? "S" : ""));
            for (int i = 1; i <= 3; i++) {
                if (klingonQuadrants[i][3] > 0) {
                    printDirection(sectorX, sectorY, klingonQuadrants[i][1], klingonQuadrants[i][2]);
                }
            }
        }
    
        void directionDistanceCalculator() {
            int quadrantX = enterprise.getQuadrant()[Enterprise.COORD_X];
            int quadrantY = enterprise.getQuadrant()[Enterprise.COORD_Y];
            int sectorX = enterprise.getSector()[Enterprise.COORD_X];
            int sectorY = enterprise.getSector()[Enterprise.COORD_Y];
            Util.println("DIRECTION/DISTANCE CALCULATOR:");
            Util.println("YOU ARE AT QUADRANT " + quadrantX + "," + quadrantY + " SECTOR " + sectorX + "," + sectorY);
            Util.print("PLEASE ENTER ");
            int[] initialCoords = Util.inputCoords("  INITIAL COORDINATES (X,Y)");
            int[] finalCoords = Util.inputCoords("  FINAL COORDINATES (X,Y)");
            printDirection(initialCoords[0], initialCoords[1], finalCoords[0], finalCoords[1]);
        }
    
        void printDirection(int from_x, int from_y, int to_x, int to_y) {
            to_y = to_y - from_y;  // delta 2
            from_y = from_x - to_x;    // delta 1
            if (to_y > 0) {
                if (from_y < 0) {
                    from_x = 7;
                } else {
                    from_x = 1;
                    int tempA = from_y;
                    from_y = to_y;
                    to_y = tempA;
                }
            } else {
                if (from_y > 0) {
                    from_x = 3;
                } else {
                    from_x = 5;
                    int tempA = from_y;
                    from_y = to_y;
                    to_y = tempA;
                }
            }
    
            from_y = Math.abs(from_y);
            to_y = Math.abs(to_y);
    
            if (from_y > 0 || to_y > 0) {
                if (from_y >= to_y) {
                    Util.println("DIRECTION = " + (from_x + to_y / from_y));
                } else {
                    Util.println("DIRECTION = " + (from_x + 2 - to_y / from_y));
                }
            }
            Util.println("DISTANCE = " + Util.round(Math.sqrt(to_y ^ 2 + from_y ^ 2), 6));
        }
    
        void starbaseNavData() {
            int sectorX = enterprise.getSector()[Enterprise.COORD_X];
            int sectorY = enterprise.getSector()[Enterprise.COORD_Y];
            if (starbases != 0) {
                Util.println("FROM ENTERPRISE TO STARBASE:");
                printDirection(sectorX, sectorY, starbaseX, starbaseY);
            } else {
                Util.println("MR. SPOCK REPORTS,  'SENSORS SHOW NO STARBASES IN THIS");
                Util.println(" QUADRANT.'");
            }
        }
    
        void printNoEnemyShipsMessage() {
            Util.println("SCIENCE OFFICER SPOCK REPORTS  'SENSORS SHOW NO ENEMY SHIPS");
            Util.println("                                IN THIS QUADRANT'");
        }
    
        String getRegionName(final boolean regionNameOnly, final int y) {
            if (!regionNameOnly) {
                switch (y % 4) {
                    case 0:
                        return " I";
                    case 1:
                        return " II";
                    case 2:
                        return " III";
                    case 3:
                        return " IV";
                }
            }
            return "";
        }
    
        String getQuadrantName(final boolean regionNameOnly, final int x, final int y) {
            if (y <= 4) {
                switch (x) {
                    case 1:
                        return "ANTARES" + getRegionName(regionNameOnly, y);
                    case 2:
                        return "RIGEL" + getRegionName(regionNameOnly, y);
                    case 3:
                        return "PROCYON" + getRegionName(regionNameOnly, y);
                    case 4:
                        return "VEGA" + getRegionName(regionNameOnly, y);
                    case 5:
                        return "CANOPUS" + getRegionName(regionNameOnly, y);
                    case 6:
                        return "ALTAIR" + getRegionName(regionNameOnly, y);
                    case 7:
                        return "SAGITTARIUS" + getRegionName(regionNameOnly, y);
                    case 8:
                        return "POLLUX" + getRegionName(regionNameOnly, y);
                }
            } else {
                switch (x) {
                    case 1:
                        return "SIRIUS" + getRegionName(regionNameOnly, y);
                    case 2:
                        return "DENEB" + getRegionName(regionNameOnly, y);
                    case 3:
                        return "CAPELLA" + getRegionName(regionNameOnly, y);
                    case 4:
                        return "BETELGEUSE" + getRegionName(regionNameOnly, y);
                    case 5:
                        return "ALDEBARAN" + getRegionName(regionNameOnly, y);
                    case 6:
                        return "REGULUS" + getRegionName(regionNameOnly, y);
                    case 7:
                        return "ARCTURUS" + getRegionName(regionNameOnly, y);
                    case 8:
                        return "SPICA" + getRegionName(regionNameOnly, y);
                }
            }
            return "UNKNOWN - ERROR";
        }
    
        void insertMarker(final String marker, final int x, final int y) {
            final int pos = Util.toInt(y) * 3 + Util.toInt(x) * 24 + 1;
            if (marker.length() != 3) {
                System.err.println("ERROR");
                System.exit(-1);
            }
            if (pos == 1) {
                quadrantMap = marker + Util.rightStr(quadrantMap, 189);
            }
            if (pos == 190) {
                quadrantMap = Util.leftStr(quadrantMap, 189) + marker;
            }
            quadrantMap = Util.leftStr(quadrantMap, (pos - 1)) + marker + Util.rightStr(quadrantMap, (190 - pos));
        }
    
        /**
         * Finds random empty coordinates in a quadrant.
         *
         * @param quadrantString
         * @return an array with a pair of coordinates x, y
         */
        int[] findEmptyPlaceInQuadrant(final String quadrantString) {
            final int x = Util.fnr();
            final int y = Util.fnr();
            if (!compareMarker(quadrantString, MARKER_EMPTY, x, y)) {
                return findEmptyPlaceInQuadrant(quadrantString);
            }
            return new int[]{x, y};
        }
    
        boolean compareMarker(final String quadrantString, final String marker, final int x, final int y) {
            final int markerRegion = (y - 1) * 3 + (x - 1) * 24 + 1;
            if (Util.midStr(quadrantString, markerRegion, 3).equals(marker)) {
                return true;
            }
            return false;
        }
    
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/java/GameCallback.java
    ================================================
    /**
     * Interface for decoupling inversion of control from GalaxyMap and Enterprise towards the game class.
     */
    public interface GameCallback {
        void enterNewQuadrant();
        void incrementStardate(double increment);
        void endGameSuccess();
        void endGameFail(boolean enterpriseDestroyed);
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/) by [Taciano Dreckmann Perez](https://github.com/taciano-perez).
    
    Overview of Java classes:
    - SuperStarTrekInstructions: displays game instructions
    - SuperStarTrekGame: main game class
    - GalaxyMap: map of the galaxy divided in quadrants and sectors, containing stars, bases, klingons, and the Enterprise
    - Enterprise: the starship Enterprise
    - GameCallback: interface allowing other classes to interact with the game class without circular dependencies 
    - Util: utility methods
    
    [This video](https://www.youtube.com/watch?v=cU3NKOnRNCI) describes the approach and the different steps followed to translate the game.
    
    ================================================
    FILE: 84_Super_Star_Trek/java/SuperStarTrekGame.java
    ================================================
    import java.util.stream.IntStream;
    
    /**
     * SUPER STARTREK - MAY 16,1978
     * ****        **** STAR TREK ****        ****
     * **** SIMULATION OF A MISSION OF THE STARSHIP ENTERPRISE,
     * **** AS SEEN ON THE STAR TREK TV SHOW.
     * **** ORIGINAL 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 . . .
     *
     * Ported to Java in Jan-Mar 2022 by
     * Taciano Dreckmann Perez (taciano.perez@gmail.com)
     */
    public class SuperStarTrekGame implements GameCallback {
    
        // commands
        static final int COMMAND_NAV = 1;
        static final int COMMAND_SRS = 2;
        static final int COMMAND_LRS = 3;
        static final int COMMAND_PHA = 4;
        static final int COMMAND_TOR = 5;
        static final int COMMAND_SHE = 6;
        static final int COMMAND_DAM = 7;
        static final int COMMAND_COM = 8;
        static final int COMMAND_XXX = 9;
    
        // computer commands
        static final int COMPUTER_COMMAND_CUMULATIVE_GALACTIC_RECORD = 1;
        static final int COMPUTER_COMMAND_STATUS_REPORT = 2;
        static final int COMPUTER_COMMAND_PHOTON_TORPEDO_DATA = 3;
        static final int COMPUTER_COMMAND_STARBASE_NAV_DATA = 4;
        static final int COMPUTER_COMMAND_DIR_DIST_CALC = 5;
        static final int COMPUTER_COMMAND_GALAXY_MAP = 6;
    
        // other constants
        static final String COMMANDS = "NAVSRSLRSPHATORSHEDAMCOMXXX";
    
        // game state
        final GalaxyMap galaxyMap = new GalaxyMap();
        double stardate = Util.toInt(Util.random() * 20 + 20);
        int missionDuration = Math.max((25 + Util.toInt(Util.random() * 10)), galaxyMap.getKlingonsInGalaxy()+1);    // T9 (mission duration in stardates)
        boolean restart = false;
    
        // initial values
        final double initialStardate = stardate;
    
        public static void main(String[] args) {
            final SuperStarTrekGame game = new SuperStarTrekGame();
            printBanner();
            while (true) {
                game.orders();
                game.enterNewQuadrant();
                game.restart = false;
                game.commandLoop();
            }
        }
    
        static void printBanner() {
            IntStream.range(1, 10).forEach(i -> {
                Util.println("");
            });
            Util.println(
                    """
                                                                ,------*------,
                                                ,-------------   '---  ------'
                                                 '-------- --'      / /
                                                     ,---' '-------/ /--,
                                                      '----------------'
    
                                                THE USS ENTERPRISE --- NCC-1701"
    
                            """
            );
        }
    
        void orders() {
            Util.println("YOUR ORDERS ARE AS FOLLOWS:\n" +
                    "     DESTROY THE " + galaxyMap.getKlingonsInGalaxy() + " KLINGON WARSHIP" + ((galaxyMap.getKlingonsInGalaxy() == 1) ? "" : "S") + " WHICH HAVE INVADED\n" +
                    "   THE GALAXY BEFORE THEY CAN ATTACK FEDERATION HEADQUARTERS\n" +
                    "   ON STARDATE " + initialStardate + missionDuration + "  THIS GIVES YOU " + missionDuration + " DAYS.  THERE " + ((galaxyMap.getBasesInGalaxy() == 1) ? "IS" : "ARE") + "\n" +
                    "  " + galaxyMap.getBasesInGalaxy() + " STARBASE" + ((galaxyMap.getBasesInGalaxy() == 1) ? "" : "S") + " IN THE GALAXY FOR RESUPPLYING YOUR SHIP");
        }
    
        public void enterNewQuadrant() {
            galaxyMap.newQuadrant(stardate, initialStardate);
            shortRangeSensorScan();
        }
    
        void commandLoop() {
            while (!this.restart) {
                checkShipEnergy();
                String cmdStr = "";
                while ("".equals(cmdStr)) cmdStr = Util.inputStr("COMMAND");
                boolean foundCommand = false;
                for (int i = 1; i <= 9; i++) {
                    if (Util.leftStr(cmdStr, 3).equals(Util.midStr(COMMANDS, 3 * i - 2, 3))) {
                        switch (i) {
                            case COMMAND_NAV:
                                navigation();
                                foundCommand = true;
                                break;
                            case COMMAND_SRS:
                                shortRangeSensorScan();
                                foundCommand = true;
                                break;
                            case COMMAND_LRS:
                                longRangeSensorScan();
                                foundCommand = true;
                                break;
                            case COMMAND_PHA:
                                firePhasers();
                                foundCommand = true;
                                break;
                            case COMMAND_TOR:
                                firePhotonTorpedo();
                                foundCommand = true;
                                break;
                            case COMMAND_SHE:
                                shieldControl();
                                foundCommand = true;
                                break;
                            case COMMAND_DAM:
                                galaxyMap.getEnterprise().damageControl(this);
                                foundCommand = true;
                                break;
                            case COMMAND_COM:
                                libraryComputer();
                                foundCommand = true;
                                break;
                            case COMMAND_XXX:
                                endGameFail(false);
                                foundCommand = true;
                                break;
                            default:
                                printCommandOptions();
                                foundCommand = true;
                        }
                    }
                }
                if (!foundCommand) printCommandOptions();
            }
        }
    
        void checkShipEnergy() {
            final Enterprise enterprise = galaxyMap.getEnterprise();
            if (enterprise.getTotalEnergy() < 10 && (enterprise.getEnergy() <= 10 || enterprise.getDeviceStatus()[Enterprise.DEVICE_SHIELD_CONTROL] != 0)) {
                Util.println("\n** FATAL ERROR **   YOU'VE JUST STRANDED YOUR SHIP IN ");
                Util.println("SPACE");
                Util.println("YOU HAVE INSUFFICIENT MANEUVERING ENERGY,");
                Util.println(" AND SHIELD CONTROL");
                Util.println("IS PRESENTLY INCAPABLE OF CROSS");
                Util.println("-CIRCUITING TO ENGINE ROOM!!");
                endGameFail(false);
            }
        }
    
        void printCommandOptions() {
            Util.println("ENTER ONE OF THE FOLLOWING:");
            Util.println("  NAV  (TO SET COURSE)");
            Util.println("  SRS  (FOR SHORT RANGE SENSOR SCAN)");
            Util.println("  LRS  (FOR LONG RANGE SENSOR SCAN)");
            Util.println("  PHA  (TO FIRE PHASERS)");
            Util.println("  TOR  (TO FIRE PHOTON TORPEDOES)");
            Util.println("  SHE  (TO RAISE OR LOWER SHIELDS)");
            Util.println("  DAM  (FOR DAMAGE CONTROL REPORTS)");
            Util.println("  COM  (TO CALL ON LIBRARY-COMPUTER)");
            Util.println("  XXX  (TO RESIGN YOUR COMMAND)\n");
        }
    
        void navigation() {
            float course = Util.toInt(Util.inputFloat("COURSE (0-9)"));
            if (course == 9) course = 1;
            if (course < 1 || course >= 9) {
                Util.println("   LT. SULU REPORTS, 'INCORRECT COURSE DATA, SIR!'");
                return;
            }
            final Enterprise enterprise = galaxyMap.getEnterprise();
            final double[] deviceStatus = enterprise.getDeviceStatus();
            Util.println("WARP FACTOR (0-" + ((deviceStatus[Enterprise.DEVICE_WARP_ENGINES] < 0) ? "0.2" : "8") + ")");
            float warp = Util.inputFloat("");
            if (deviceStatus[Enterprise.DEVICE_WARP_ENGINES] < 0 && warp > .2) {
                Util.println("WARP ENGINES ARE DAMAGED.  MAXIMUM SPEED = WARP 0.2");
                return;
            }
            if (warp == 0) return;
            if (warp > 0 && warp <= 8) {
                int n = Util.toInt(warp * 8);
                if (enterprise.getEnergy() - n >= 0) {
                    galaxyMap.klingonsMoveAndFire(this);
                    repairDamagedDevices(course, warp, n);
                    galaxyMap.moveEnterprise(course, warp, n, stardate, initialStardate, missionDuration, this);
                } else {
                    Util.println("ENGINEERING REPORTS   'INSUFFICIENT ENERGY AVAILABLE");
                    Util.println("                       FOR MANEUVERING AT WARP " + warp + "!'");
                    if (enterprise.getShields() < n - enterprise.getEnergy() || deviceStatus[Enterprise.DEVICE_SHIELD_CONTROL] < 0) return;
                    Util.println("DEFLECTOR CONTROL ROOM ACKNOWLEDGES " + enterprise.getShields() + " UNITS OF ENERGY");
                    Util.println("                         PRESENTLY DEPLOYED TO SHIELDS.");
                }
            } else {
                Util.println("   CHIEF ENGINEER SCOTT REPORTS 'THE ENGINES WON'T TAKE");
                Util.println(" WARP " + warp + "!'");
            }
        }
    
        void repairDamagedDevices(final float course, final float warp, final int N) {
            final Enterprise enterprise = galaxyMap.getEnterprise();
            // repair damaged devices and print damage report
            enterprise.repairDamagedDevices(warp);
            if (Util.random() > .2) return;  // 80% chance no damage nor repair
            int randomDevice = Util.fnr();    // random device
            final double[] deviceStatus = enterprise.getDeviceStatus();
            if (Util.random() >= .6) {   // 40% chance of repair of random device
                enterprise.setDeviceStatus(randomDevice, deviceStatus[randomDevice] + Util.random() * 3 + 1);
                Util.println("DAMAGE CONTROL REPORT:  " + Enterprise.printDeviceName(randomDevice) + " STATE OF REPAIR IMPROVED\n");
            } else {            // 60% chance of damage of random device
                enterprise.setDeviceStatus(randomDevice, deviceStatus[randomDevice] - (Util.random() * 5 + 1));
                Util.println("DAMAGE CONTROL REPORT:  " + Enterprise.printDeviceName(randomDevice) + " DAMAGED");
            }
        }
    
        void longRangeSensorScan() {
            // LONG RANGE SENSOR SCAN CODE
            galaxyMap.longRangeSensorScan();
        }
    
        void firePhasers() {
            galaxyMap.firePhasers(this);
        }
    
        void firePhotonTorpedo() {
            galaxyMap.firePhotonTorpedo(stardate, initialStardate, missionDuration, this);
        }
    
        void shieldControl() {
            galaxyMap.getEnterprise().shieldControl();
        }
    
        void shortRangeSensorScan() {
            // SHORT RANGE SENSOR SCAN & STARTUP SUBROUTINE
            galaxyMap.shortRangeSensorScan(stardate);
        }
    
        void libraryComputer() {
            // REM LIBRARY COMPUTER CODE
            if (galaxyMap.getEnterprise().getDeviceStatus()[Enterprise.DEVICE_LIBRARY_COMPUTER] < 0) {
                Util.println("COMPUTER DISABLED");
                return;
            }
            while (true) {
                final float commandInput = Util.inputFloat("COMPUTER ACTIVE AND AWAITING COMMAND");
                if (commandInput < 0) return;
                Util.println("");
                int command = Util.toInt(commandInput) + 1;
                if (command >= COMPUTER_COMMAND_CUMULATIVE_GALACTIC_RECORD && command <= COMPUTER_COMMAND_GALAXY_MAP) {
                    switch (command) {
                        case COMPUTER_COMMAND_CUMULATIVE_GALACTIC_RECORD:
                            galaxyMap.cumulativeGalacticRecord(true);
                            return;
                        case COMPUTER_COMMAND_STATUS_REPORT:
                            statusReport();
                            return;
                        case COMPUTER_COMMAND_PHOTON_TORPEDO_DATA:
                            galaxyMap.photonTorpedoData();
                            return;
                        case COMPUTER_COMMAND_STARBASE_NAV_DATA:
                            galaxyMap.starbaseNavData();
                            return;
                        case COMPUTER_COMMAND_DIR_DIST_CALC:
                            galaxyMap.directionDistanceCalculator();
                            return;
                        case COMPUTER_COMMAND_GALAXY_MAP:
                            galaxyMap.cumulativeGalacticRecord(false);
                            return;
                    }
                } else {
                    // invalid command
                    Util.println("FUNCTIONS AVAILABLE FROM LIBRARY-COMPUTER:");
                    Util.println("   0 = CUMULATIVE GALACTIC RECORD");
                    Util.println("   1 = STATUS REPORT");
                    Util.println("   2 = PHOTON TORPEDO DATA");
                    Util.println("   3 = STARBASE NAV DATA");
                    Util.println("   4 = DIRECTION/DISTANCE CALCULATOR");
                    Util.println("   5 = GALAXY 'REGION NAME' MAP");
                    Util.println("");
                }
            }
        }
    
        void statusReport() {
            Util.println("   STATUS REPORT:");
            Util.println("KLINGON" + ((galaxyMap.getKlingonsInGalaxy() > 1)? "S" : "")  + " LEFT: " + galaxyMap.getKlingonsInGalaxy());
            Util.println("MISSION MUST BE COMPLETED IN " + .1 * Util.toInt((initialStardate + missionDuration - stardate) * 10) + " STARDATES");
            if (galaxyMap.getBasesInGalaxy() >= 1) {
                Util.println("THE FEDERATION IS MAINTAINING " + galaxyMap.getBasesInGalaxy() + " STARBASE" + ((galaxyMap.getBasesInGalaxy() > 1)? "S" : "") + " IN THE GALAXY");
            } else {
                Util.println("YOUR STUPIDITY HAS LEFT YOU ON YOUR OWN IN");
                Util.println("  THE GALAXY -- YOU HAVE NO STARBASES LEFT!");
            }
            galaxyMap.getEnterprise().damageControl(this);
        }
    
        public void incrementStardate(double increment) {
            this.stardate += increment;
        }
    
        public void endGameFail(final boolean enterpriseDestroyed) {    // 6220
            if (enterpriseDestroyed) {
                Util.println("\nTHE ENTERPRISE HAS BEEN DESTROYED.  THEN FEDERATION ");
                Util.println("WILL BE CONQUERED");
            }
            Util.println("\nIT IS STARDATE " + stardate);
            Util.println("THERE WERE " + galaxyMap.getKlingonsInGalaxy() + " KLINGON BATTLE CRUISERS LEFT AT");
            Util.println("THE END OF YOUR MISSION.");
            repeatGame();
        }
    
        public void endGameSuccess() {
            Util.println("CONGRATULATION, CAPTAIN!  THE LAST KLINGON BATTLE CRUISER");
            Util.println("MENACING THE FEDERATION HAS BEEN DESTROYED.\n");
            Util.println("YOUR EFFICIENCY RATING IS " + (Math.sqrt(1000 * (galaxyMap.getRemainingKlingons() / (stardate - initialStardate)))));
            repeatGame();
        }
    
        void repeatGame() {
            Util.println("\n");
            if (galaxyMap.getBasesInGalaxy() != 0) {
                Util.println("THE FEDERATION IS IN NEED OF A NEW STARSHIP COMMANDER");
                Util.println("FOR A SIMILAR MISSION -- IF THERE IS A VOLUNTEER,");
                final String reply = Util.inputStr("LET HIM STEP FORWARD AND ENTER 'AYE'");
                if ("AYE".equals(reply)) {
                    this.restart = true;
                } else {
                    System.exit(0);
                }
            }
        }
    
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/java/SuperStarTrekInstructions.java
    ================================================
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.util.stream.Collectors;
    import java.util.stream.IntStream;
    
    /**
     * SUPER STARTREK INSTRUCTIONS
     * MAR 5, 1978
     * Just the instructions for SUPERSTARTREK
     *
     * Ported to Java in Jan-Feb 2022 by
     * Taciano Dreckmann Perez (taciano.perez@gmail.com)
     */
    public class SuperStarTrekInstructions {
    
        public static void main(String[] args) {
            printBanner();
            final String reply = inputStr("DO YOU NEED INSTRUCTIONS (Y/N)? ");
            if ("Y".equals(reply)) {
                printInstructions();
            }
        }
    
        static void printBanner() {
            print(tab(10)+"*************************************");
            print(tab(10)+"*                                   *");
            print(tab(10)+"*                                   *");
            print(tab(10)+"*      * * SUPER STAR TREK * *      *");
            print(tab(10)+"*                                   *");
            print(tab(10)+"*                                   *");
            print(tab(10)+"*************************************");
        }
    
        static void printInstructions() {
            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), THAN 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.");
            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 QUADTANT.  THEREFORE, TO GET");
            print("     FROM QUADRANT 6,5 TO 5,5, YOU WOULD");
            print("     USE COURSE 3, WARP FACTOR 1.");
            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.");
            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 TH 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.");
            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)");
            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("     THAN 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.");
            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.");
        }
    
        static void print(final String s) {
            System.out.println(s);
        }
    
        static String tab(final int n) {
            return IntStream.range(1, n).mapToObj(num -> " ").collect(Collectors.joining());
        }
    
        static String inputStr(final String message) {
            System.out.print(message + "? ");
            final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            try {
                return reader.readLine();
            } catch (IOException ioe) {
                ioe.printStackTrace();
                return "";
            }
        }
    
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/java/Util.java
    ================================================
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.math.BigDecimal;
    import java.math.RoundingMode;
    import java.util.Random;
    import java.util.stream.Collectors;
    import java.util.stream.IntStream;
    
    /**
     * Convenience utility methods for the Super Star Trek game.
     */
    public class Util {
    
        static final Random random = new Random();
    
        public static float random() {
            return random.nextFloat();
        }
    
        public static int fnr() {    // 475
            // Generate a random integer from 1 to 8 inclusive.
            return toInt(random() * 7 + 1);
        }
    
        public static int toInt(final double num) {
            int x = (int) Math.floor(num);
            if (x < 0) x *= -1;
            return x;
        }
    
        public static void println(final String s) {
            System.out.println(s);
        }
    
        public static void print(final String s) {
            System.out.print(s);
        }
    
        public static String tab(final int n) {
            return IntStream.range(1, n).mapToObj(num -> " ").collect(Collectors.joining());
        }
    
        public static int strlen(final String s) {
            return s.length();
        }
    
        public static String inputStr(final String message) {
            System.out.print(message + "? ");
            final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            try {
                return reader.readLine();
            } catch (IOException ioe) {
                ioe.printStackTrace();
                return "";
            }
        }
    
        public static int[] inputCoords(final String message) {
            while (true) {
                final String input = inputStr(message);
                try {
                    final String[] splitInput = input.split(",");
                    if (splitInput.length == 2) {
                        int x = Integer.parseInt(splitInput[0]);
                        int y = Integer.parseInt(splitInput[0]);
                        return new int[]{x, y};
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    
        public static float inputFloat(final String message) {
            while (true) {
                System.out.print(message + "? ");
                final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
                try {
                    final String input = reader.readLine();
                    if (input.length() > 0) {
                        return Float.parseFloat(input);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    
        public static String leftStr(final String input, final int len) {
            if (input == null || input.length() < len) return input;
            return input.substring(0, len);
        }
    
        public static String midStr(final String input, final int start, final int len) {
            if (input == null || input.length() < ((start - 1) + len)) return input;
            return input.substring(start - 1, (start - 1) + len);
        }
    
        public static String rightStr(final String input, final int len) {
            if (input == null || input.length() < len) return "";
            return input.substring(input.length() - len);
        }
    
        public static double round(double value, int places) {
            if (places < 0) throw new IllegalArgumentException();
            BigDecimal bd = new BigDecimal(Double.toString(value));
            bd = bd.setScale(places, RoundingMode.HALF_UP);
            return bd.doubleValue();
        }
    
    
    }
    
    
    ================================================
    FILE: 84_Super_Star_Trek/javascript/README.md
    ================================================
    Original source downloaded [from Vintage Basic][def]
    
    Conversion to [****J****avaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells)
    
    
    [def]: http://www.vintage-basic.net/games.html
    
    ================================================
    FILE: 84_Super_Star_Trek/javascript/cli.mjs
    ================================================
    import {
      onExit,
      onPrint,
      onInput,
      gameMain,
    } from "./superstartrek.mjs";
    
    import readline from "readline";
    
    onExit(function exit() {
      process.exit();
    });
    
    onPrint(function print(...messages) {
      console.log(messages.join(""));
    });
    
    onInput(async function input(prompt) {
      const rl = readline.createInterface({
        input: process.stdin,
        output: process.stdout,
        terminal: false,
      });
      return new Promise((resolve, reject) => {
        rl.question(`${prompt}? `, (response) => {
          rl.close();
          resolve(response);
        });
      });
    });
    
    gameMain().then(process.exit).catch(console.log);
    
    
    ================================================
    FILE: 84_Super_Star_Trek/javascript/index.html
    ================================================
    
      
        Super Star Trek
        
        
        
      
      
        

    Super Star Trek

    Originally found in David Ahl's BASIC Computer Games (1978)

    Converted to JavaScript in February 2021 by Les Orchard <me@lmorchard.com>

          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), THAN 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 COMANDER OF THE STARSHIP
    \ENTERPRISE\; YOUR MISSION: TO SEEK AND DESTROY THE FLEET OF
    KLINGON WARWHIPS 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 QUADTANT.  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 TH UNITS DIGIT
         IS THE NUMBER OF STARS, THE TENS DIGIT IS THE NUMBER OF
         STARBASES, AND THE HUNDRESDS 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.  (REMBER, 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
         THAN THE STATUS DISPLAY TOTAL ENERGY INCLUDES SHIELD ENERGY
    
    \DAM\ COMMAND = DAMMAGE 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 SHOWES 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.
        
    ================================================ FILE: 84_Super_Star_Trek/javascript/package.json ================================================ { "name": "super-star-trek", "version": "1.0.0", "description": "As published in Basic Computer Games (1978) https://www.atariarchives.org/basicgames/showpage.php?page=157", "main": "superstartrek.mjs", "scripts": { "start": "node cli.mjs" }, "author": "", "license": "ISC", "devDependencies": { "eslint": "^7.20.0" } } ================================================ FILE: 84_Super_Star_Trek/javascript/superstartrek.mjs ================================================ /** * 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 * LINE NUMBERS FROM VERSION STREK7 OF 1/12/75 PRESERVED AS * MUCH AS POSSIBLE WHILE USING MULTIPLE STATEMENTS PER LINE * SOME LINES ARE LONGER THAN 72 CHARACTERS; THIS WAS DONE * BY USING "?" INSTEAD OF "PRINT" WHEN ENTERING LINES * * Translated and reworked into JavaScript in February 2021 * by Les Orchard */ export const setGameOptions = (options = {}) => Object.assign(gameOptions, options); export const getGameState = () => ({ ...gameState }); export const onPrint = (fn) => (print = fn); export const onInput = (fn) => (input = fn); export const onExit = (fn) => (exit = fn); export async function gameMain() { await gameReset(); await gameLoop(); await exit(); } let gameState = {}; let print = () => {}; let input = () => {}; let exit = () => {}; export const gameOptions = { stardateStart: Math.floor(Math.random() * 20 + 20) * 100, timeLimit: 25 + Math.floor(Math.random() * 10), energyMax: 3000, photonTorpedoesMax: 10, starbaseSpawnChance: 0.96, enemyMaxShield: 200, enemySpawnChance: [0.8, 0.85, 0.98], maxStarsPerSector: 8, sectorWidth: 8, sectorHeight: 8, galaxyWidth: 8, galaxyHeight: 8, systemDamageChanceOnHit: 0.6, systemDamageHitThroughShields: 0.02, systemChanceAffectedInWarp: 0.2, systemChanceDamageInWarp: 0.6, nameEnemy: "KLINGON", nameEnemies: "KLINGONS", nameScienceOfficer: "SPOCK", nameNavigationOfficer: "LT. SULU", nameWeaponsOfficer: "ENSIGN CHEKOV", nameCommunicationsOfficer: "LT. UHURA", nameChiefEngineer: "SCOTT", sectorMapSymbols: { empty: " ", star: " * ", base: ">!<", hero: "<*>", enemy: "+K+", }, shipSystems: [ "WARP ENGINES", "SHORT RANGE SENSORS", "LONG RANGE SENSORS", "PHASER CONTROL", "PHOTON TUBES", "DAMAGE CONTROL", "SHIELD CONTROL", "LIBRARY-COMPUTER", ], quadrantNames: [ [ "ANTARES", "RIGEL", "PROCYON", "VEGA", "CANOPUS", "ALTAIR", "SAGITTARIUS", "POLLUX", ], [ "SIRIUS", "DENEB", "CAPELLA", "BETELGEUSE", "ALDEBARAN", "REGULUS", "ARCTURUS", "SPICA", ], ], quadrantNumbers: ["I", "II", "III", "IV"], }; let SYSTEM_WARP_ENGINES, SYSTEM_SHORT_RANGE_SENSORS, SYSTEM_LONG_RANGE_SENSORS, SYSTEM_PHASER_CONTROL, SYSTEM_PHOTON_TUBES, SYSTEM_DAMAGE_CONTROL, SYSTEM_SHIELD_CONTROL, SYSTEM_LIBRARY_COMPUTER; async function gameIntro() { print("\n".repeat(10)); print(" ,------*------,"); print(" ,------------- '--- ------'"); print(" '-------- --' / /"); print(" ,---' '-------/ /--,"); print(" '----------------'"); print(""); print(" THE USS ENTERPRISE --- NCC-1701"); print("\n".repeat(4)); print("YOUR ORDERS ARE AS FOLLOWS:"); print(); print( ` DESTROY THE ${gameState.enemiesRemaining} ${gameOptions.nameEnemy} WARSHIPS WHICH HAVE INVADED` ); print(" THE GALAXY BEFORE THEY CAN ATTACK FEDERATION HEADQUARTERS"); print( ` ON STARDATE ${formatStardate( gameOptions.stardateStart + gameOptions.timeLimit )} THIS GIVES YOU ${gameOptions.timeLimit} DAYS. THERE${ gameState.starbasesRemaining > 1 ? " ARE " : " IS " }` ); print( ` ${gameState.starbasesRemaining} STARBASE${ gameState.starbasesRemaining > 1 ? "S" : " ARE" } IN THE GALAXY FOR RESUPPLYING YOUR SHIP` ); } async function gameReset() { [ SYSTEM_WARP_ENGINES, SYSTEM_SHORT_RANGE_SENSORS, SYSTEM_LONG_RANGE_SENSORS, SYSTEM_PHASER_CONTROL, SYSTEM_PHOTON_TUBES, SYSTEM_DAMAGE_CONTROL, SYSTEM_SHIELD_CONTROL, SYSTEM_LIBRARY_COMPUTER, ] = gameOptions.shipSystems; gameState = { gameOver: false, gameWon: false, gameQuit: false, destroyed: false, shouldRestart: false, sectorMap: "", alertCondition: "", stardateCurrent: gameOptions.stardateStart, isDocked: false, energyRemaining: gameOptions.energyMax, photonTorpedoesRemaining: gameOptions.photonTorpedoesMax, shieldsCurrent: 0, starbasesRemaining: 0, enemiesRemaining: 0, quadrantPositionY: randomInt(gameOptions.galaxyHeight, 1), quadrantPositionX: randomInt(gameOptions.galaxyWidth, 1), sectorPositionY: randomInt(gameOptions.sectorHeight, 1), sectorPositionX: randomInt(gameOptions.sectorWidth, 1), sectorEnemiesCount: 0, sectorStarbasesCount: 0, sectorStarsCount: 0, galacticMap: [], galacticMapDiscovered: [], }; gameState.systemsDamage = {}; for (const systemName of gameOptions.shipSystems) { gameState.systemsDamage[systemName] = 0; } for (let mapY = 1; mapY <= gameOptions.galaxyHeight; mapY++) { gameState.galacticMap[mapY] = []; gameState.galacticMapDiscovered[mapY] = []; for (let mapX = 1; mapX <= gameOptions.galaxyWidth; mapX++) { gameState.galacticMapDiscovered[mapY][mapX] = 0; gameState.sectorEnemiesCount = 0; const enemySpawnRoll = Math.random(); if (enemySpawnRoll > gameOptions.enemySpawnChance[2]) { gameState.sectorEnemiesCount = 3; gameState.enemiesRemaining = gameState.enemiesRemaining + 3; } else if (enemySpawnRoll > gameOptions.enemySpawnChance[1]) { gameState.sectorEnemiesCount = 2; gameState.enemiesRemaining = gameState.enemiesRemaining + 2; } else if (enemySpawnRoll > gameOptions.enemySpawnChance[0]) { gameState.sectorEnemiesCount = 1; gameState.enemiesRemaining = gameState.enemiesRemaining + 1; } gameState.sectorStarbasesCount = 0; if (Math.random() > gameOptions.starbaseSpawnChance) { gameState.sectorStarbasesCount = 1; gameState.starbasesRemaining = gameState.starbasesRemaining + 1; } // 1040 gameState.galacticMap[mapY][mapX] = gameState.sectorEnemiesCount * 100 + gameState.sectorStarbasesCount * 10 + randomInt(gameOptions.maxStarsPerSector, 1); } } if (gameState.enemiesRemaining > gameOptions.timeLimit) { // Ensure the player has at least one more stardate than the number of enemies gameOptions.timeLimit = gameState.enemiesRemaining + 1; } if (gameState.starbasesRemaining === 0) { if ( gameState.galacticMap[gameState.quadrantPositionY][ gameState.quadrantPositionX ] < 200 ) { gameState.galacticMap[gameState.quadrantPositionY][ gameState.quadrantPositionX ] = gameState.galacticMap[gameState.quadrantPositionY][ gameState.quadrantPositionX ] + 120; } gameState.enemiesRemaining = gameState.enemiesRemaining + 1; gameState.starbasesRemaining = 1; gameState.galacticMap[gameState.quadrantPositionY][ gameState.quadrantPositionX ] = gameState.galacticMap[gameState.quadrantPositionY][ gameState.quadrantPositionX ] + 10; gameState.quadrantPositionY = randomInt(gameOptions.galaxyHeight, 1); gameState.quadrantPositionX = randomInt(gameOptions.galaxyWidth, 1); } gameState.enemiesInitialCount = gameState.enemiesRemaining; await gameIntro(); await newQuadrantEntered(); } async function newQuadrantEntered() { gameState.sectorEnemiesCount = 0; gameState.sectorStarbasesCount = 0; gameState.sectorStarsCount = 0; gameState.starbaseRepairDelay = 0.5 * Math.random(); // Add this sector to the known map gameState.galacticMapDiscovered[gameState.quadrantPositionY][ gameState.quadrantPositionX ] = gameState.galacticMap[gameState.quadrantPositionY][ gameState.quadrantPositionX ]; // Initialize a sector enemy for each that had a chance to spawn gameState.sectorEnemies = gameOptions.enemySpawnChance.map((c) => ({ health: 0, posY: 0, posX: 0, })); if ( gameState.quadrantPositionY >= 1 && gameState.quadrantPositionY <= gameOptions.galaxyHeight && gameState.quadrantPositionX >= 1 && gameState.quadrantPositionX <= gameOptions.galaxyWidth ) { let currentQuadrantName = buildQuadrantName( gameState.quadrantPositionY, gameState.quadrantPositionX ); print(); if (gameOptions.stardateStart == gameState.stardateCurrent) { print("YOUR MISSION BEGINS WITH YOUR STARSHIP LOCATED"); print(`IN THE GALACTIC QUADRANT, '${currentQuadrantName}'.`); } else { print(`NOW ENTERING ${currentQuadrantName} QUADRANT . . .`); } print(); gameState.sectorEnemiesCount = Math.floor( gameState.galacticMap[gameState.quadrantPositionY][ gameState.quadrantPositionX ] * 0.01 ); gameState.sectorStarbasesCount = Math.floor( gameState.galacticMap[gameState.quadrantPositionY][ gameState.quadrantPositionX ] * 0.1 ) - 10 * gameState.sectorEnemiesCount; gameState.sectorStarsCount = gameState.galacticMap[gameState.quadrantPositionY][ gameState.quadrantPositionX ] - 100 * gameState.sectorEnemiesCount - 10 * gameState.sectorStarbasesCount; if (gameState.sectorEnemiesCount != 0) { print("COMBAT AREA CONDITION RED"); if (gameState.shieldsCurrent <= 200) { print(" SHIELDS DANGEROUSLY LOW"); } } for ( let enemyIdx = 0; enemyIdx < gameOptions.enemySpawnChance.length; enemyIdx++ ) { gameState.sectorEnemies[enemyIdx].posY = 0; gameState.sectorEnemies[enemyIdx].posX = 0; } } for ( let enemyIdx = 0; enemyIdx < gameOptions.enemySpawnChance.length; enemyIdx++ ) { gameState.sectorEnemies[enemyIdx].health = 0; } gameState.sectorMap = " ".repeat( gameOptions.sectorMapSymbols.empty.length * gameOptions.sectorHeight * gameOptions.sectorWidth ); insertInSectorMap( gameOptions.sectorMapSymbols.hero, gameState.sectorPositionY, gameState.sectorPositionX ); if (gameState.sectorEnemiesCount >= 1) { // 1720 for ( let enemyIdx = 0; enemyIdx < gameState.sectorEnemiesCount; enemyIdx++ ) { const [posY, posX] = findSpaceInSectorMap(); insertInSectorMap(gameOptions.sectorMapSymbols.enemy, posY, posX); gameState.sectorEnemies[enemyIdx] = { posY, posX, health: gameOptions.enemyMaxShield * (0.5 + Math.random()), }; } } if (gameState.sectorStarbasesCount >= 1) { const [R1, R2] = findSpaceInSectorMap(); gameState.sectorStarbaseY = R1; gameState.sectorStarbaseX = R2; insertInSectorMap(gameOptions.sectorMapSymbols.base, R1, R2); } for (let i = 1; i <= gameState.sectorStarsCount; i++) { insertInSectorMap( gameOptions.sectorMapSymbols.star, ...findSpaceInSectorMap() ); } return shortRangeSensorScanAndStartup(); } async function gameLoop() { while (!gameState.gameOver) { await acceptCommand(); if (gameState.gameOver) { await endOfGame(); } if (gameState.shouldRestart) { await gameReset(); } } } async function acceptCommand() { if ( gameState.shieldsCurrent + gameState.energyRemaining <= 10 || (gameState.energyRemaining < 10 && gameState.systemsDamage[SYSTEM_SHIELD_CONTROL] != 0) ) { 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!!"); print(); gameState.gameOver = true; return; } const commandInput = (await input("COMMAND")).trim().toUpperCase(); const command = COMMANDS[commandInput] || commandHelp; await command(); } /************************************************************************/ const COMMANDS = { NAV: commandCourseControl, SRS: shortRangeSensorScanAndStartup, LRS: commandLongRangeScan, PHA: commandPhaserControl, TOR: commandPhotonTorpedo, SHE: commandShieldControl, DAM: commandDamageControl, COM: commandLibraryComputer, XXX: () => { // todo more confirmation here? gameState.gameOver = true; gameState.gameQuit = true; }, DUMP: () => { console.log(JSON.stringify(gameState)); }, }; async function commandHelp() { print("ENTER ONE OF THE FOLLOWING:"); print(" NAV (TO SET COURSE)"); print(" SRS (FOR SHORT RANGE SENSOR SCAN)"); print(" LRS (FOR LONG RANGE SENSOR SCAN)"); print(" PHA (TO FIRE PHASERS)"); print(" TOR (TO FIRE PHOTON TORPEDOES)"); print(" SHE (TO RAISE OR LOWER SHIELDS)"); print(" DAM (FOR DAMAGE CONTROL REPORTS)"); print(" COM (TO CALL ON LIBRARY-COMPUTER)"); print(" XXX (TO RESIGN YOUR COMMAND)"); print(); } async function shortRangeSensorScanAndStartup() { checkIfDocked(); if (gameState.isDocked) { gameState.alertCondition = "DOCKED"; gameState.energyRemaining = gameOptions.energyMax; gameState.photonTorpedoesRemaining = gameOptions.photonTorpedoesMax; print("SHIELDS DROPPED FOR DOCKING PURPOSES"); gameState.shieldsCurrent = 0; } else { gameState.alertCondition = "GREEN"; if (gameState.energyRemaining < gameOptions.energyMax * 0.1) gameState.alertCondition = "YELLOW"; if (gameState.sectorEnemiesCount > 0) gameState.alertCondition = "RED"; } if (gameState.systemsDamage[SYSTEM_SHORT_RANGE_SENSORS] < 0) { print(); print("*** SHORT RANGE SENSORS ARE OUT ***"); print(); return; } const statusLines = [ `STARDATE ${formatStardate(gameState.stardateCurrent)}`, `CONDITION ${gameState.alertCondition}`, `QUADRANT ${gameState.quadrantPositionY} , ${gameState.quadrantPositionX}`, `SECTOR ${gameState.sectorPositionY} , ${gameState.sectorPositionX}`, `PHOTON TORPEDOES ${gameState.photonTorpedoesRemaining}`, `TOTAL ENERGY ${ gameState.energyRemaining + gameState.shieldsCurrent }`, `SHIELDS ${gameState.shieldsCurrent}`, `${gameOptions.nameEnemies} REMAINING ${gameState.enemiesRemaining}`, ]; const lineSplit = new RegExp( `.{${gameOptions.sectorMapSymbols.empty.length * gameOptions.sectorWidth}}`, "g" ); const cellSplit = new RegExp( `.{${gameOptions.sectorMapSymbols.empty.length}}`, "g" ); const borderLine = "-".repeat( (gameOptions.sectorMapSymbols.empty.length + 1) * gameOptions.sectorWidth ); print(borderLine); print( gameState.sectorMap // Split the map into lines of 24 chars .match(lineSplit) // Split each line into cells of 3 chars .map((line) => line.match(cellSplit)) // Format each line with Y coord, spaced out cells, and a line of status .map((line, idx) => line.join(" ") + " ".repeat(4) + statusLines[idx]) // Finally, join all the lines with returns .join("\n") ); print(borderLine); print(); } function checkIfDocked() { const { sectorPositionY: sY, sectorPositionX: sX } = gameState; for (let posY = sY - 1; posY <= sY + 1; posY++) { for (let posX = sX - 1; posX <= sX + 1; posX++) { if ( posY >= 1 || posY <= gameOptions.sectorHeight || posX >= 1 || posX <= gameOptions.sectorWidth ) { if (findInsectorMap(gameOptions.sectorMapSymbols.base, posY, posX)) { gameState.isDocked = true; return; } } } } gameState.isDocked = false; } async function commandCourseControl() { let courseInput = parseFloat(await input("COURSE (0-9)")); if (courseInput == 9) courseInput = 1; if (isNaN(courseInput) || courseInput < 1 || courseInput > 9) { print( ` ${gameOptions.nameNavigationOfficer} REPORTS, 'INCORRECT COURSE DATA, SIR!'` ); return; } const warpFactorInput = parseFloat( await input( `WARP FACTOR (0-${ gameState.systemsDamage[SYSTEM_WARP_ENGINES] < 0 ? "0.2" : "8" })` ) ); if (warpFactorInput == 0 || isNaN(warpFactorInput)) return; if ( gameState.systemsDamage[SYSTEM_WARP_ENGINES] < 0 && warpFactorInput > 0.2 ) { return print("WARP ENGINES ARE DAMAGED. MAXIMUM SPEED = WARP 0.2"); } if (warpFactorInput < 0 && warpFactorInput > 8) { return print( ` CHIEF ENGINEER ${gameOptions.nameChiefEngineer} REPORTS 'THE ENGINES WON'T TAKE WARP ${warpFactorInput}!'` ); } // FIXME: This seems to depend on square sectors - which we have, but could be changed in config const sectorsToWarp = Math.floor(warpFactorInput * gameOptions.sectorWidth + 0.5); if (gameState.energyRemaining - sectorsToWarp < 0) { print("ENGINEERING REPORTS 'INSUFFICIENT ENERGY AVAILABLE"); print( " FOR MANEUVERING AT WARP ", warpFactorInput, " !'" ); if ( gameState.shieldsCurrent > sectorsToWarp - gameState.energyRemaining && gameState.systemsDamage[SYSTEM_SHIELD_CONTROL] > 0 ) { print( "DEFLECTOR CONTROL ROOM ACKNOWLEDGES ", gameState.shieldsCurrent, " UNITS OF ENERGY" ); print(" PRESENTLY DEPLOYED TO SHIELDS."); } } for ( let enemyIdx = 0; enemyIdx < gameOptions.enemySpawnChance.length; enemyIdx++ ) { if (gameState.sectorEnemies[enemyIdx].health > 0) { insertInSectorMap( gameOptions.sectorMapSymbols.empty, gameState.sectorEnemies[enemyIdx].posY, gameState.sectorEnemies[enemyIdx].posX ); const [rY, rX] = findSpaceInSectorMap(); gameState.sectorEnemies[enemyIdx].posY = rY; gameState.sectorEnemies[enemyIdx].posX = rX; insertInSectorMap( gameOptions.sectorMapSymbols.enemy, gameState.sectorEnemies[enemyIdx].posY, gameState.sectorEnemies[enemyIdx].posX ); } } enemiesShoot(); let damageControlHeaderPrinted = false; const printDamageReport = (msg) => { if (!damageControlHeaderPrinted) { damageControlHeaderPrinted = true; print("DAMAGE CONTROL REPORT:"); } print(msg); }; let repairFactorDuringWarp = Math.min(1, warpFactorInput); // Continually repair damaged systems during warp for (const systemName of gameOptions.shipSystems) { if (gameState.systemsDamage[systemName] >= 0) continue; gameState.systemsDamage[systemName] = gameState.systemsDamage[systemName] + repairFactorDuringWarp; if ( gameState.systemsDamage[systemName] > -0.1 && gameState.systemsDamage[systemName] < 0 ) { gameState.systemsDamage[systemName] = -0.1; continue; } if (gameState.systemsDamage[systemName] < 0) continue; printDamageReport(` ${systemName} REPAIR COMPLETED.`); } // 20% chance of a random system being damaged, repaired, or improved in warp if (Math.random() < gameOptions.systemChanceAffectedInWarp) { const systemIdx = randomInt(gameOptions.shipSystems.length); const systemName = gameOptions.shipSystems[systemIdx]; if (Math.random() < gameOptions.systemChanceDamageInWarp) { // 60% chance of random system damage gameState.systemsDamage[systemName] = gameState.systemsDamage[systemName] - (Math.random() * 5 + 1); printDamageReport(` ${systemName} DAMAGED`); } else { // 40% chance of random system repair or improvement gameState.systemsDamage[systemName] = gameState.systemsDamage[systemName] + Math.random() * 3 + 1; printDamageReport(` ${systemName} STATE OF REPAIR IMPROVED`); } print(); } // 3060 REM BEGIN MOVING STARSHIP insertInSectorMap( gameOptions.sectorMapSymbols.empty, Math.floor(gameState.sectorPositionY), Math.floor(gameState.sectorPositionX) ); const [courseDeltaY, courseDeltaX] = courseToDeltaXY(courseInput); let currentSectorPositionY = gameState.sectorPositionY; let currentSectorPositionX = gameState.sectorPositionX; let currentQuadrantPosY = gameState.quadrantPositionY; let currentQuadrantPosX = gameState.quadrantPositionX; for (let sectorsWarped = 1; sectorsWarped < sectorsToWarp; sectorsWarped++) { gameState.sectorPositionY = gameState.sectorPositionY + courseDeltaY; gameState.sectorPositionX = gameState.sectorPositionX + courseDeltaX; if ( gameState.sectorPositionY < 1 || gameState.sectorPositionY >= 9 || gameState.sectorPositionX < 1 || gameState.sectorPositionX >= 9 ) { // 3490 REM EXCEEDED QUADRANT LIMITS currentSectorPositionY = gameOptions.sectorHeight * gameState.quadrantPositionY + currentSectorPositionY + sectorsToWarp * courseDeltaY; currentSectorPositionX = gameOptions.sectorWidth * gameState.quadrantPositionX + currentSectorPositionX + sectorsToWarp * courseDeltaX; gameState.quadrantPositionY = Math.floor(currentSectorPositionY / 8); gameState.quadrantPositionX = Math.floor(currentSectorPositionX / 8); gameState.sectorPositionY = Math.floor( currentSectorPositionY - gameState.quadrantPositionY * 8 ); gameState.sectorPositionX = Math.floor( currentSectorPositionX - gameState.quadrantPositionX * 8 ); if (gameState.sectorPositionY == 0) { gameState.quadrantPositionY = gameState.quadrantPositionY - 1; gameState.sectorPositionY = 8; } if (gameState.sectorPositionX == 0) { gameState.quadrantPositionX = gameState.quadrantPositionX - 1; gameState.sectorPositionX = 8; } let galacticPerimeterHit = false; if (gameState.quadrantPositionY < 1) { galacticPerimeterHit = true; gameState.quadrantPositionY = 1; gameState.sectorPositionY = 1; } if (gameState.quadrantPositionY > 8) { galacticPerimeterHit = true; gameState.quadrantPositionY = 8; gameState.sectorPositionY = 8; } if (gameState.quadrantPositionX < 1) { galacticPerimeterHit = true; gameState.quadrantPositionX = 1; gameState.sectorPositionX = 1; } if (gameState.quadrantPositionX > 8) { galacticPerimeterHit = true; gameState.quadrantPositionX = 8; gameState.sectorPositionX = 8; } if (galacticPerimeterHit) { print( `${gameOptions.nameCommunicationsOfficer} REPORTS MESSAGE FROM STARFLEET COMMAND:` ); print(" 'PERMISSION TO ATTEMPT CROSSING OF GALACTIC PERIMETER"); print(" IS HEREBY *DENIED*. SHUT DOWN YOUR ENGINES.'"); print( `CHIEF ENGINEER ${gameOptions.nameChiefEngineer} REPORTS 'WARP ENGINES SHUT DOWN` ); print( ` AT SECTOR ${gameState.sectorPositionY} , ${gameState.sectorPositionX} OF QUADRANT ${gameState.quadrantPositionY} , ${gameState.quadrantPositionX}.'` ); if (checkIfTimeExpired()) { return; } } if ( gameOptions.sectorHeight * gameState.quadrantPositionY + gameState.quadrantPositionX == gameOptions.sectorHeight * currentQuadrantPosY + currentQuadrantPosX ) { break; } gameState.stardateCurrent = gameState.stardateCurrent + 1; consumeEnergyForWarp(sectorsToWarp); return newQuadrantEntered(); } if ( !findInsectorMap( gameOptions.sectorMapSymbols.empty, gameState.sectorPositionY, gameState.sectorPositionX ) ) { // Undo this step of warp travel if the space isn't empty gameState.sectorPositionY = Math.floor( gameState.sectorPositionY - courseDeltaY ); gameState.sectorPositionX = Math.floor( gameState.sectorPositionX - courseDeltaX ); print( `WARP ENGINES SHUT DOWN AT SECTOR ${gameState.sectorPositionY} , ${gameState.sectorPositionX} DUE TO BAD NAVAGATION` ); break; } } gameState.sectorPositionY = Math.floor(gameState.sectorPositionY); gameState.sectorPositionX = Math.floor(gameState.sectorPositionX); insertInSectorMap( gameOptions.sectorMapSymbols.hero, Math.floor(gameState.sectorPositionY), Math.floor(gameState.sectorPositionX) ); consumeEnergyForWarp(sectorsToWarp); let timeElapsedDuringWarp = 1; if (warpFactorInput < 1) { timeElapsedDuringWarp = 0.1 * Math.floor(10 * warpFactorInput); } gameState.stardateCurrent = gameState.stardateCurrent + timeElapsedDuringWarp; if (checkIfTimeExpired()) { return; } await shortRangeSensorScanAndStartup(); } function checkIfTimeExpired() { if ( gameState.stardateCurrent > gameOptions.stardateStart + gameOptions.timeLimit ) { gameState.gameOver = true; } return gameState.gameOver; } function consumeEnergyForWarp(sectorsToWarp) { // 3900 REM MANEUVER ENERGY S/R ** gameState.energyRemaining = gameState.energyRemaining - sectorsToWarp - 10; if (gameState.energyRemaining >= 0) { return; } print("SHIELD CONTROL SUPPLIES ENERGY TO COMPLETE THE MANEUVER."); gameState.shieldsCurrent = gameState.shieldsCurrent + gameState.energyRemaining; gameState.energyRemaining = 0; if (gameState.shieldsCurrent <= 0) { gameState.shieldsCurrent = 0; } } async function commandLongRangeScan() { // 3990 REM LONG RANGE SENSOR SCAN CODE if (gameState.systemsDamage[SYSTEM_LONG_RANGE_SENSORS] < 0) { print("LONG RANGE SENSORS ARE INOPERABLE"); return; } print( "LONG RANGE SCAN FOR QUADRANT ", gameState.quadrantPositionY, " , ", gameState.quadrantPositionX ); const separatorLine = "-------------------"; print(separatorLine); for ( let posY = gameState.quadrantPositionY - 1; posY <= gameState.quadrantPositionY + 1; posY++ ) { // Scan a line of sectors const lineSectors = [null, null, null]; for ( let posX = gameState.quadrantPositionX - 1; posX <= gameState.quadrantPositionX + 1; posX++ ) { if (posY > 0 && posY < 9 && posX > 0 && posX < 9) { // Add the scanned cell to the current scan output lineSectors[posX - gameState.quadrantPositionX + 1] = gameState.galacticMap[posY][posX]; // Add the scanned cell to the discovered map gameState.galacticMapDiscovered[posY][posX] = gameState.galacticMap[posY][posX]; } } // Print a formatted line of the scan - e.g. ": 004 : 205 : 004 :" print( ": " + lineSectors .map((sector) => sector === null ? "***" : sector.toString().padStart(3, "0") ) .join(" : ") + " :" ); print(separatorLine); } } async function commandPhaserControl() { // 4250 REM PHASER CONTROL CODE BEGINS HERE if (gameState.systemsDamage[SYSTEM_PHASER_CONTROL] < 0) { print("PHASERS INOPERATIVE"); return; } if (gameState.sectorEnemiesCount <= 0) { print( `SCIENCE OFFICER ${gameOptions.nameScienceOfficer} REPORTS 'SENSORS SHOW NO ENEMY SHIPS` ); print(" IN THIS QUADRANT'"); return; } if (gameState.systemsDamage[SYSTEM_LIBRARY_COMPUTER] < 0) { print("COMPUTER FAILURE HAMPERS ACCURACY"); } print( "PHASERS LOCKED ON TARGET; ENERGY AVAILABLE = ", gameState.energyRemaining, " UNITS" ); let phaserUnitsToFire; const continueCommandLoop = true; while (continueCommandLoop) { phaserUnitsToFire = parseFloat(await input("NUMBER OF UNITS TO FIRE")); if (phaserUnitsToFire <= 0) return; if (gameState.energyRemaining - phaserUnitsToFire >= 0) { break; } print(`ENERGY AVAILABLE = ${gameState.energyRemaining} UNITS`); } gameState.energyRemaining = gameState.energyRemaining - phaserUnitsToFire; // FIXED: in the original, this was shield system. Changed to phaser system. if (gameState.systemsDamage[SYSTEM_PHASER_CONTROL] < 0) { phaserUnitsToFire = phaserUnitsToFire * Math.random(); } // Spread phaser fire between all enemies let phaserUnitsPerEnemy = Math.floor( phaserUnitsToFire / gameState.sectorEnemiesCount ); for ( let enemyIdx = 0; enemyIdx < gameOptions.enemySpawnChance.length; enemyIdx++ ) { if (gameState.sectorEnemies[enemyIdx].health <= 0) { // Skip dead enemies continue; } print(); // Phaser damage falls off based on distance and a bit of chance let phaserDamage = Math.floor( (phaserUnitsPerEnemy / distanceFromEnemy(enemyIdx)) * (Math.random() + 2) ); if (phaserDamage <= 0.15 * gameState.sectorEnemies[enemyIdx].health) { print( "SENSORS SHOW NO DAMAGE TO ENEMY AT ", gameState.sectorEnemies[enemyIdx].posY, " , ", gameState.sectorEnemies[enemyIdx].posX ); continue; } gameState.sectorEnemies[enemyIdx].health -= phaserDamage; print( `${phaserDamage} UNIT HIT ON ${gameOptions.nameEnemy} AT SECTOR ${gameState.sectorEnemies[enemyIdx].posY} , ${gameState.sectorEnemies[enemyIdx].posX}` ); if (gameState.sectorEnemies[enemyIdx].health > 0) { print( ` (SENSORS SHOW ${gameState.sectorEnemies[enemyIdx].health} UNITS REMAINING)` ); print(); } else { print(`*** ${gameOptions.nameEnemy} DESTROYED ***`); print(); gameState.sectorEnemiesCount = gameState.sectorEnemiesCount - 1; gameState.enemiesRemaining = gameState.enemiesRemaining - 1; // Remove enemy from display insertInSectorMap( gameOptions.sectorMapSymbols.empty, gameState.sectorEnemies[enemyIdx].posY, gameState.sectorEnemies[enemyIdx].posX ); // Set enemy health at exactly zero gameState.sectorEnemies[enemyIdx].health = 0; // Update the galactic map with one fewer enemy gameState.galacticMap[gameState.quadrantPositionY][ gameState.quadrantPositionX ] -= 100; // Copy updated galactic map sector to discovered map. gameState.galacticMapDiscovered[gameState.quadrantPositionY][ gameState.quadrantPositionX ] = gameState.galacticMap[gameState.quadrantPositionY][ gameState.quadrantPositionX ]; if (gameState.enemiesRemaining <= 0) { // If that was the last enemy, we've won! gameState.gameOver = true; gameState.gameWon = true; return; } } } enemiesShoot(); } async function commandPhotonTorpedo() { // 4690 REM PHOTON TORPEDO CODE BEGINS HERE // 4700 if (gameState.photonTorpedoesRemaining <= 0) { return print("ALL PHOTON TORPEDOES EXPENDED"); } if (gameState.systemsDamage[SYSTEM_PHOTON_TUBES] < 0) { return print("PHOTON TUBES ARE NOT OPERATIONAL"); } let torpedoCourse = parseFloat(await input("PHOTON TORPEDO COURSE (1-9)")); if (torpedoCourse == 9) torpedoCourse = 1; if (torpedoCourse < 1 || torpedoCourse > 9) { print( `${gameOptions.nameWeaponsOfficer} REPORTS, 'INCORRECT COURSE DATA, SIR!'` ); } const [courseDeltaY, courseDeltaX] = courseToDeltaXY(torpedoCourse); gameState.energyRemaining = gameState.energyRemaining - 2; gameState.photonTorpedoesRemaining = gameState.photonTorpedoesRemaining - 1; let currPosY = gameState.sectorPositionY; let currPosX = gameState.sectorPositionX; print("TORPEDO TRACK:"); // Fly the torpedo along its course... let quantizedPosY, quantizedPosX; const forever = true; while (forever) { currPosY = currPosY + courseDeltaY; currPosX = currPosX + courseDeltaX; // The course will move in decimals, quantize to whole numbers quantizedPosY = Math.floor(currPosY + 0.5); quantizedPosX = Math.floor(currPosX + 0.5); // Exiting the sector means the torpedo missed if ( quantizedPosY < 1 || quantizedPosY > gameOptions.sectorHeight || quantizedPosX < 1 || quantizedPosX > gameOptions.sectorWidth ) { print("TORPEDO MISSED"); return enemiesShoot(); } print(` ${quantizedPosY} , ${quantizedPosX}`); if ( !findInsectorMap( gameOptions.sectorMapSymbols.empty, quantizedPosY, quantizedPosX ) ) { // Torpedo hit something solid, so stop flying. break; } } // Did the torpedo hit an enemy? if ( findInsectorMap( gameOptions.sectorMapSymbols.enemy, quantizedPosY, quantizedPosX ) ) { print(`*** ${gameOptions.nameEnemy} DESTROYED ***`); gameState.sectorEnemiesCount = gameState.sectorEnemiesCount - 1; gameState.enemiesRemaining = gameState.enemiesRemaining - 1; if (gameState.enemiesRemaining <= 0) { // If that was the last enemy, then we've won! gameState.gameOver = true; gameState.gameWon = true; return; } // Find which enemy was hit and set health to zero for ( let enemyIdx = 0; enemyIdx < gameOptions.enemySpawnChance.length; enemyIdx++ ) { if ( quantizedPosY == gameState.sectorEnemies[enemyIdx].posY && quantizedPosX == gameState.sectorEnemies[enemyIdx].posX ) { gameState.sectorEnemies[enemyIdx].health = 0; break; } } } // Did the torpedo hit a star? if ( findInsectorMap( gameOptions.sectorMapSymbols.star, quantizedPosY, quantizedPosX ) ) { print( `STAR AT ${quantizedPosY} , ${quantizedPosX} ABSORBED TORPEDO ENERGY.` ); return enemiesShoot(); } // Did the torpedo hit a starbase? if ( findInsectorMap( gameOptions.sectorMapSymbols.base, quantizedPosY, quantizedPosX ) ) { print("*** STARBASE DESTROYED ***"); gameState.sectorStarbasesCount = gameState.sectorStarbasesCount - 1; gameState.starbasesRemaining = gameState.starbasesRemaining - 1; if ( gameState.starbasesRemaining <= 0 || gameState.enemiesRemaining <= gameState.stardateCurrent - gameOptions.stardateStart - gameOptions.timeLimit ) { print("THAT DOES IT, CAPTAIN!! YOU ARE HEREBY RELIEVED OF COMMAND"); print("AND SENTENCED TO 99 STARDATES AT HARD LABOR ON CYGNUS 12!!"); gameState.gameOver = true; return; } else { print("STARFLEET COMMAND REVIEWING YOUR RECORD TO CONSIDER"); print("COURT MARTIAL!"); gameState.isDocked = false; } } // If we hit an enemy or a starbase, update the sector and galaxy map to // remove the thing destroyed insertInSectorMap( gameOptions.sectorMapSymbols.empty, quantizedPosY, quantizedPosX ); gameState.galacticMap[gameState.quadrantPositionY][ gameState.quadrantPositionX ] = gameState.sectorEnemiesCount * 100 + gameState.sectorStarbasesCount * 10 + gameState.sectorStarsCount; gameState.galacticMapDiscovered[gameState.quadrantPositionY][ gameState.quadrantPositionX ] = gameState.galacticMap[gameState.quadrantPositionY][ gameState.quadrantPositionX ]; return enemiesShoot(); } async function enemiesShoot() { if (gameState.sectorEnemiesCount <= 0) { return; } if (gameState.isDocked) { print("STARBASE SHIELDS PROTECT THE ENTERPRISE"); return; } for ( let enemyIdx = 0; enemyIdx < gameOptions.enemySpawnChance.length; enemyIdx++ ) { if (gameState.sectorEnemies[enemyIdx].health <= 0) { continue; } // Enemy damage based on health with drop-off for distance and chance const enemyWeaponDamage = Math.floor( (gameState.sectorEnemies[enemyIdx].health / distanceFromEnemy(enemyIdx)) * (2 + Math.random()) ); gameState.shieldsCurrent = gameState.shieldsCurrent - enemyWeaponDamage; // Consume enemy health for firing weapon gameState.sectorEnemies[enemyIdx].health = Math.floor( gameState.sectorEnemies[enemyIdx].health / (3 + Math.random()) ); print( `${enemyWeaponDamage} UNIT HIT ON ENTERPRISE FROM SECTOR ${gameState.sectorEnemies[enemyIdx].posY} , ${gameState.sectorEnemies[enemyIdx].posX}` ); if (gameState.shieldsCurrent <= 0) { // If we're out of shields, we're out of luck gameState.gameOver = true; gameState.destroyed = true; return; } print(` `); if (enemyWeaponDamage < 20) { continue; } // Systems damage with 60% chance or a hit of more than 2% of shields if ( Math.random() > gameOptions.systemDamageChanceOnHit || enemyWeaponDamage / gameState.shieldsCurrent <= gameOptions.systemDamageHitThroughShields ) { continue; } // Random system damaged proportional to enemy damage and current shields const systemIdx = randomInt(gameOptions.shipSystems.length); const systemName = gameOptions.shipSystems[systemIdx]; gameState.systemsDamage[systemName] = gameState.systemsDamage[systemName] - enemyWeaponDamage / gameState.shieldsCurrent - 0.5 * Math.random(); print(`DAMAGE CONTROL REPORTS ${systemName} DAMAGED BY THE HIT`); } } async function commandShieldControl() { // 5520 REM SHIELD CONTROL if (gameState.systemsDamage[SYSTEM_SHIELD_CONTROL] < 0) { print("SHIELD CONTROL INOPERABLE"); return; } print( "ENERGY AVAILABLE = ", gameState.energyRemaining + gameState.shieldsCurrent ); const shieldUnits = parseFloat(await input("NUMBER OF UNITS TO SHIELDS")); if (shieldUnits < 0 || gameState.shieldsCurrent == shieldUnits) { print(""); return; } if (shieldUnits > gameState.energyRemaining + gameState.shieldsCurrent) { print("SHIELD CONTROL REPORTS 'THIS IS NOT THE FEDERATION TREASURY.'"); print(""); return; } gameState.energyRemaining = gameState.energyRemaining + gameState.shieldsCurrent - shieldUnits; gameState.shieldsCurrent = shieldUnits; print("DEFLECTOR CONTROL ROOM REPORT:"); print( ` 'SHIELDS NOW AT ${Math.floor( gameState.shieldsCurrent )} UNITS PER YOUR COMMAND.` ); } async function commandDamageControl() { // 5680 REM DAMAGE CONTROL // 5690 // FIXME: Seems like damage control should work while docked? if (gameState.systemsDamage[SYSTEM_DAMAGE_CONTROL] < 0) { print("DAMAGE CONTROL REPORT NOT AVAILABLE"); return; } // 5910 print(); print("DEVICE STATE OF REPAIR"); for (const systemName of gameOptions.shipSystems) { print( systemName.padEnd(25, " "), Math.floor(gameState.systemsDamage[systemName] * 100) * 0.01 ); } print(); if (gameState.isDocked) { let repairTimeEstimate = 0; for (const systemName of gameOptions.shipSystems) { if (gameState.systemsDamage[systemName] < 0) { repairTimeEstimate = repairTimeEstimate + 0.1; } } if (repairTimeEstimate == 0) { return; } print(); repairTimeEstimate = repairTimeEstimate + gameState.starbaseRepairDelay; if (repairTimeEstimate >= 1) { repairTimeEstimate = 0.9; } print("TECHNICIANS STANDING BY TO EFFECT REPAIRS TO YOUR SHIP;"); print( `ESTIMATED TIME TO REPAIR: ${ 0.01 * Math.floor(100 * repairTimeEstimate) } STARDATES` ); const authorizeRepairInput = await input( "WILL YOU AUTHORIZE THE REPAIR ORDER (Y/N)" ); if (authorizeRepairInput.toUpperCase() != "Y") { return; } for (const systemName of gameOptions.shipSystems) { gameState.systemsDamage[systemName] = 0; } gameState.stardateCurrent = gameState.stardateCurrent + repairTimeEstimate + 0.1; } } async function commandLibraryComputer() { // 7280 REM LIBRARY COMPUTER CODE // 7290 if (gameState.systemsDamage[SYSTEM_LIBRARY_COMPUTER] < 0) { print("COMPUTER DISABLED"); return; } const commandInput = parseInt( await input("COMPUTER ACTIVE AND AWAITING COMMAND") ); if (commandInput < 0) return; const command = COMMANDS_COMPUTER[commandInput] || computerHelp; print(); await command(); } const COMMANDS_COMPUTER = [ computerCumulativeRecord, computerStatusReport, computerPhotonData, computerStarbaseData, computerDirectionData, computerGalaxyMap, ]; async function computerHelp() { 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"); print(); } async function computerPhotonData() { if (gameState.sectorEnemiesCount <= 0) { print( `SCIENCE OFFICER ${gameOptions.nameScienceOfficer} REPORTS 'SENSORS SHOW NO ENEMY SHIPS` ); print(" IN THIS QUADRANT'"); return; } print( `FROM ENTERPRISE TO ${gameOptions.nameEnemy} BATTLE CRUISER${ gameState.sectorEnemiesCount > 1 ? "S" : "" }` ); for ( let enemyIdx = 0; enemyIdx < gameOptions.enemySpawnChance.length; enemyIdx++ ) { if (gameState.sectorEnemies[enemyIdx].health <= 0) continue; computerDirectionCommon({ fromY: gameState.sectorPositionY, fromX: gameState.sectorPositionX, toY: gameState.sectorEnemies[enemyIdx].posY, toX: gameState.sectorEnemies[enemyIdx].posX, }); } } async function computerStarbaseData() { if (gameState.sectorStarbasesCount == 0) { print( `MR. ${gameOptions.nameScienceOfficer} REPORTS, 'SENSORS SHOW NO STARBASES IN THIS QUADRANT.'` ); return; } print("FROM ENTERPRISE TO STARBASE:"); computerDirectionCommon({ fromY: gameState.sectorPositionY, fromX: gameState.sectorPositionX, toY: gameState.sectorStarbaseY, toX: gameState.sectorStarbaseX, }); } const inputCoords = async (prompt) => (await input(prompt)).split(",").map((s) => parseInt(s.trim())); async function computerDirectionData() { print("DIRECTION/DISTANCE CALCULATOR:"); print( `YOU ARE AT QUADRANT ${gameState.quadrantPositionY} , ${gameState.quadrantPositionX} SECTOR ${gameState.sectorPositionY} , ${gameState.sectorPositionX}` ); print("PLEASE ENTER"); const [fromY, fromX] = await inputCoords(" INITIAL COORDINATES (Y,X)"); const [toY, toX] = await inputCoords(" FINAL COORDINATES (Y,X)"); computerDirectionCommon({ fromX, fromY, toX, toY }); } async function computerDirectionCommon({ fromX, fromY, toX, toY }) { const distance = Math.sqrt( Math.pow(toX - fromX, 2) + Math.pow(toY - fromY, 2) ); const direction = 1 + (8 / (Math.PI * 2)) * ((Math.atan2(0 - fromY - (0 - toY), fromX - toX) + Math.PI) % (Math.PI * 2)); print(`DIRECTION = ${direction}`); print(`DISTANCE = ${distance}`); } async function computerStatusReport() { print("STATUS REPORT:"); print(); print( `${ gameState.enemiesRemaining > 1 ? gameOptions.nameEnemies : gameOptions.nameEnemy } LEFT: ${gameState.enemiesRemaining}` ); print( `MISSION MUST BE COMPLETED IN ${ 0.1 * Math.floor( (gameOptions.stardateStart + gameOptions.timeLimit - gameState.stardateCurrent) * 10 ) } STARDATES` ); if (gameState.starbasesRemaining < 1) { print("YOUR STUPIDITY HAS LEFT YOU ON YOUR ON IN"); print(" THE GALAXY -- YOU HAVE NO STARBASES LEFT!"); } else { print( `THE FEDERATION IS MAINTAINING ${gameState.starbasesRemaining} STARBASE${ gameState.starbasesRemaining < 2 ? "" : "S" } IN THE GALAXY` ); } commandDamageControl(); } async function computerGalaxyMap() { print(" THE GALAXY"); computerCommonMap(false); } async function computerCumulativeRecord() { print(); print( ` COMPUTER RECORD OF GALAXY FOR QUADRANT ${gameState.quadrantPositionY} , ${gameState.quadrantPositionX}` ); print(); computerCommonMap(); } async function computerCommonMap(showMapCells = true) { // Print the X column number header based on width of first galaxy row print( " " + gameState.galacticMap[1] .map((_, idx) => idx.toString().padStart(3, " ")) .join(" ") ); // Assemble X column separator based on width of first galaxy row const separator = " " + gameState.galacticMap[1].map((_, idx) => "----- ").join(""); print(separator); for (let mapY = 1; mapY <= gameOptions.galaxyHeight; mapY++) { let out = mapY.toString().padStart(3, " "); if (showMapCells) { // 7630 for (let mapX = 1; mapX <= gameOptions.galaxyWidth; mapX++) { out += ` ${ gameState.galacticMapDiscovered[mapY][mapX] == 0 ? "***" : ("" + gameState.galacticMapDiscovered[mapY][mapX]).padStart( 3, "0" ) }`; } } else { let quadrantName = buildQuadrantName(mapY, 1, true); let centerSpacing = Math.floor(12 - 0.5 * quadrantName.length); out += ` ${" ".repeat(centerSpacing)}${quadrantName}${" ".repeat( centerSpacing )}`; quadrantName = buildQuadrantName(mapY, 5, true); centerSpacing = Math.floor(12 - 0.5 * quadrantName.length); out += `${" ".repeat(centerSpacing)}${quadrantName}`; } print(out); print(separator); } } async function endOfGame() { if (gameState.destroyed) { print(); print( "THE ENTERPRISE HAS BEEN DESTROYED. THEN FEDERATION WILL BE CONQUERED" ); } print(`IT IS STARDATE ${formatStardate(gameState.stardateCurrent)}`); if (!gameState.gameWon) { print( `THERE WERE ${gameState.enemiesRemaining} ${gameOptions.nameEnemy} BATTLE CRUISERS LEFT AT` ); print("THE END OF YOUR MISSION."); } else { print( `CONGRULATION, CAPTAIN! THEN LAST ${gameOptions.nameEnemy} BATTLE CRUISER` ); print("MENACING THE FEDERATION HAS BEEN DESTROYED."); print(); print( "YOUR EFFICIENCY RATING IS ", (1000 * (gameState.enemiesInitialCount / (gameState.stardateCurrent - gameOptions.stardateStart))) ^ 2 ); } print(); print(); if (gameState.starbasesRemaining > 0) { print("THE FEDERATION IS IN NEED OF A NEW STARSHIP COMMANDER"); print("FOR A SIMILAR MISSION -- IF THERE IS A VOLUNTEER,"); const playAgainInput = await input("LET HIM STEP FORWARD AND ENTER 'AYE'"); if (playAgainInput.toUpperCase() == "AYE") { gameState.shouldRestart = true; return; } } } const COURSE_TO_XY = [ [0, 1], [-1, 1], [-1, 0], [-1, -1], [0, -1], [1, -1], [1, 0], [1, 1], [0, 1], ]; function courseToDeltaXY(course) { const courseIdx = Math.floor(course) - 1; //3110 X1=C(C1,1)+(C(C1+1,1)-C(C1,1))*(C1-INT(C1)):X=S1:Y=S2 //3140 X2=C(C1,2)+(C(C1+1,2)-C(C1,2))*(C1-INT(C1)):Q4=Q1:Q5=Q2 const courseDeltaY = COURSE_TO_XY[courseIdx][0] + (COURSE_TO_XY[courseIdx + 1][0] - COURSE_TO_XY[courseIdx][0]) * (course - Math.floor(course)); const courseDeltaX = COURSE_TO_XY[courseIdx][1] + (COURSE_TO_XY[courseIdx + 1][1] - COURSE_TO_XY[courseIdx][1]) * (course - Math.floor(course)); return [courseDeltaY, courseDeltaX]; } function findSpaceInSectorMap() { let posY, posX, foundEmptyPlace = false; while (!foundEmptyPlace) { posY = randomInt(8, 1); posX = randomInt(8, 1); foundEmptyPlace = findInsectorMap( gameOptions.sectorMapSymbols.empty, posY, posX ); } return [posY, posX]; } function findInsectorMap(str, y, x) { const idx = (x - 1) * 3 + (y - 1) * 24; return gameState.sectorMap.substring(idx, idx + 3) == str; } // 8660 REM INSERT IN STRING ARRAY FOR QUADRANT function insertInSectorMap(str, y, x) { // 8670 const strPos = (x - 1) * 3 + (y - 1) * 24; if (str.length != 3) { throw "ERROR"; } gameState.sectorMap = gameState.sectorMap.slice(0, strPos) + str + gameState.sectorMap.slice(strPos + 3); } function buildQuadrantName(y, x, regionNameOnly = false) { const xIdx = x - 1; const yIdx = y - 1; const name = gameOptions.quadrantNames[xIdx < 4 ? 0 : 1][yIdx]; return `${name}${ regionNameOnly ? "" : ` ${gameOptions.quadrantNumbers[xIdx % 4]}` }`; } const randomInt = (max, min = 0) => Math.floor(min + Math.random() * (max - min)); const formatStardate = (stardate) => Math.floor(stardate * 10) / 10; const distanceFromEnemy = (sectorEnemyIndex) => Math.sqrt( Math.pow( gameState.sectorEnemies[sectorEnemyIndex].posY - gameState.sectorPositionY, 2 ) + Math.pow( gameState.sectorEnemies[sectorEnemyIndex].posX - gameState.sectorPositionX, 2 ) ); ================================================ FILE: 84_Super_Star_Trek/kotlin/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Kotlin](https://kotlinlang.org/) ================================================ FILE: 84_Super_Star_Trek/lua/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Lua](https://www.lua.org/) ================================================ FILE: 84_Super_Star_Trek/perl/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Perl](https://www.perl.org/) ================================================ FILE: 84_Super_Star_Trek/python/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Python](https://www.python.org/about/) ================================================ FILE: 84_Super_Star_Trek/python/superstartrek.py ================================================ """ **** **** STAR TREK **** **** **** SIMULATION OF A MISSION OF THE STARSHIP ENTERPRISE, **** AS SEEN ON THE STAR TREK TV SHOW. **** ORIGINAL 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 . . . Output is identical to BASIC version except for a few fixes (as noted, search `bug`) and minor cleanup. """ import random import sys from dataclasses import dataclass from enum import Enum from math import sqrt from typing import Callable, Dict, Final, List, Optional, Tuple def get_user_float(prompt: str) -> float: """Get input from user and return it.""" while True: answer = input(prompt) try: return float(answer) except ValueError: pass class Entity(Enum): klingon = "+K+" ship = "<*>" empty = "***" starbase = ">!<" star = " * " void = " " @dataclass class Point: x: int y: int def __str__(self) -> str: return f"{self.x + 1} , {self.y + 1}" @dataclass class Position: """ Every quadrant has 8 sectors Hence the position could also be represented as: x = quadrant.x * 8 + sector.x y = quadrant.y * 8 + sector.y """ quadrant: Point sector: Point @dataclass class QuadrantData: klingons: int bases: int stars: int def num(self) -> int: return 100 * self.klingons + 10 * self.bases + self.stars @dataclass class KlingonShip: sector: Point shield: float class Ship: energy_capacity: int = 3000 torpedo_capacity: int = 10 def __init__(self) -> None: self.position = Position(Point(fnr(), fnr()), Point(fnr(), fnr())) self.energy: int = Ship.energy_capacity self.devices: Tuple[str, ...] = ( "WARP ENGINES", "SHORT RANGE SENSORS", "LONG RANGE SENSORS", "PHASER CONTROL", "PHOTON TUBES", "DAMAGE CONTROL", "SHIELD CONTROL", "LIBRARY-COMPUTER", ) self.damage_stats: List[float] = [0] * len(self.devices) self.shields = 0 self.torpedoes = Ship.torpedo_capacity self.docked: bool = False # true when docked at starbase def refill(self) -> None: self.energy = Ship.energy_capacity self.torpedoes = Ship.torpedo_capacity def maneuver_energy(self, n: int) -> None: """Deduct the energy for navigation from energy/shields.""" self.energy -= n + 10 if self.energy <= 0: print("SHIELD CONTROL SUPPLIES ENERGY TO COMPLETE THE MANEUVER.") self.shields += self.energy self.energy = 0 self.shields = max(0, self.shields) def shield_control(self) -> None: """Raise or lower the shields.""" if self.damage_stats[6] < 0: print("SHIELD CONTROL INOPERABLE") return while True: energy_to_shield = input( f"ENERGY AVAILABLE = {self.energy + self.shields} NUMBER OF UNITS TO SHIELDS? " ) if len(energy_to_shield) > 0: x = int(energy_to_shield) break if x < 0 or self.shields == x: print("") return if x > self.energy + self.shields: print( "SHIELD CONTROL REPORTS 'THIS IS NOT THE FEDERATION " "TREASURY.'\n" "" ) return self.energy += self.shields - x self.shields = x print("DEFLECTOR CONTROL ROOM REPORT:") print(f" 'SHIELDS NOW AT {self.shields} UNITS PER YOUR COMMAND.'") class Quadrant: def __init__( self, point: Point, # position of the quadrant population: QuadrantData, ship_position: Position, ) -> None: """Populate quadrant map""" assert 0 <= point.x <= 7 and 0 <= point.y <= 7 self.name = Quadrant.quadrant_name(point.x, point.y, False) self.nb_klingons = population.klingons self.nb_bases = population.bases self.nb_stars = population.stars # extra delay in repairs at base self.delay_in_repairs_at_base: float = 0.5 * random.random() # Klingons in current quadrant self.klingon_ships: List[KlingonShip] = [] # Initialize empty: save what is at which position self.data = [[Entity.void for _ in range(8)] for _ in range(8)] self.populate_quadrant(ship_position) @classmethod def quadrant_name(cls, row: int, col: int, region_only: bool = False) -> str: """Return quadrant name visible on scans, etc.""" region1 = [ "ANTARES", "RIGEL", "PROCYON", "VEGA", "CANOPUS", "ALTAIR", "SAGITTARIUS", "POLLUX", ] region2 = [ "SIRIUS", "DENEB", "CAPELLA", "BETELGEUSE", "ALDEBARAN", "REGULUS", "ARCTURUS", "SPICA", ] modifier = ["I", "II", "III", "IV"] quadrant = region1[row] if col < 4 else region2[row] if not region_only: quadrant += f" {modifier[col % 4]}" return quadrant def set_value(self, x: float, y: float, entity: Entity) -> None: self.data[round(x)][round(y)] = entity def get_value(self, x: float, y: float) -> Entity: return self.data[round(x)][round(y)] def find_empty_place(self) -> Tuple[int, int]: """Find an empty location in the current quadrant.""" while True: row, col = fnr(), fnr() if self.get_value(row, col) == Entity.void: return row, col def populate_quadrant(self, ship_position: Position) -> None: self.set_value(ship_position.sector.x, ship_position.sector.y, Entity.ship) for _ in range(self.nb_klingons): x, y = self.find_empty_place() self.set_value(x, y, Entity.klingon) self.klingon_ships.append( KlingonShip( Point(x, y), klingon_shield_strength * (0.5 + random.random()) ) ) if self.nb_bases > 0: # Position of starbase in current sector starbase_x, starbase_y = self.find_empty_place() self.starbase = Point(starbase_x, starbase_y) self.set_value(starbase_x, starbase_y, Entity.starbase) for _ in range(self.nb_stars): x, y = self.find_empty_place() self.set_value(x, y, Entity.star) def __str__(self) -> str: quadrant_string = "" for row in self.data: for entity in row: quadrant_string += entity.value return quadrant_string class World: def __init__( self, total_klingons: int = 0, # Klingons at start of game bases_in_galaxy: int = 0, ) -> None: self.ship = Ship() self.initial_stardate = 100 * random.randint(20, 39) self.stardate: float = self.initial_stardate self.mission_duration = random.randint(25, 34) # Enemy self.remaining_klingons = total_klingons # Player starbases self.bases_in_galaxy = bases_in_galaxy self.galaxy_map: List[List[QuadrantData]] = [ [QuadrantData(0, 0, 0) for _ in range(8)] for _ in range(8) ] self.charted_galaxy_map: List[List[QuadrantData]] = [ [QuadrantData(0, 0, 0) for _ in range(8)] for _ in range(8) ] # initialize contents of galaxy for x in range(8): for y in range(8): r1 = random.random() if r1 > 0.98: quadrant_klingons = 3 elif r1 > 0.95: quadrant_klingons = 2 elif r1 > 0.80: quadrant_klingons = 1 else: quadrant_klingons = 0 self.remaining_klingons += quadrant_klingons quadrant_bases = 0 if random.random() > 0.96: quadrant_bases = 1 self.bases_in_galaxy += 1 self.galaxy_map[x][y] = QuadrantData( quadrant_klingons, quadrant_bases, 1 + fnr() ) if self.remaining_klingons > self.mission_duration: self.mission_duration = self.remaining_klingons + 1 if self.bases_in_galaxy == 0: # original has buggy extra code here self.bases_in_galaxy = 1 self.galaxy_map[self.ship.position.quadrant.x][ self.ship.position.quadrant.y ].bases += 1 curr = self.ship.position.quadrant self.quadrant = Quadrant( self.ship.position.quadrant, self.galaxy_map[curr.x][curr.y], self.ship.position, ) def remaining_time(self) -> float: return self.initial_stardate + self.mission_duration - self.stardate def has_mission_ended(self) -> bool: return self.remaining_time() < 0 class Game: """Handle user actions""" def __init__(self) -> None: self.restart = False self.world = World() def startup(self) -> None: """Initialize the game variables and map, and print startup messages.""" print( "\n\n\n\n\n\n\n\n\n\n\n" " ,------*------,\n" " ,------------- '--- ------'\n" " '-------- --' / /\n" " ,---' '-------/ /--,\n" " '----------------'\n\n" " THE USS ENTERPRISE --- NCC-1701\n" "\n\n\n\n" ) world = self.world print( "YOUR ORDERS ARE AS FOLLOWS:\n" f" DESTROY THE {world.remaining_klingons} KLINGON WARSHIPS WHICH HAVE INVADED\n" " THE GALAXY BEFORE THEY CAN ATTACK FEDERATION HEADQUARTERS\n" f" ON STARDATE {world.initial_stardate+world.mission_duration}. " f" THIS GIVES YOU {world.mission_duration} DAYS. THERE " f"{'IS' if world.bases_in_galaxy == 1 else 'ARE'}\n" f" {world.bases_in_galaxy} " f"STARBASE{'' if world.bases_in_galaxy == 1 else 'S'} IN THE GALAXY FOR " "RESUPPLYING YOUR SHIP.\n" ) def new_quadrant(self) -> None: """Enter a new quadrant: populate map and print a short range scan.""" world = self.world ship = world.ship q = ship.position.quadrant world.quadrant = Quadrant( q, world.galaxy_map[q.x][q.y], ship.position, ) world.charted_galaxy_map[q.x][q.y] = world.galaxy_map[q.x][q.y] if world.stardate == world.initial_stardate: print("\nYOUR MISSION BEGINS WITH YOUR STARSHIP LOCATED") print(f"IN THE GALACTIC QUADRANT, '{world.quadrant.name}'.\n") else: print(f"\nNOW ENTERING {world.quadrant.name} QUADRANT . . .\n") if world.quadrant.nb_klingons != 0: print("COMBAT AREA CONDITION RED") if ship.shields <= 200: print(" SHIELDS DANGEROUSLY LOW") self.short_range_scan() def fnd(self, i: int) -> float: """Find distance between Enterprise and i'th Klingon warship.""" ship = self.world.ship.position.sector klingons = self.world.quadrant.klingon_ships[i].sector return sqrt((klingons.x - ship.x) ** 2 + (klingons.y - ship.y) ** 2) def klingons_fire(self) -> None: """Process nearby Klingons firing on Enterprise.""" ship = self.world.ship if self.world.quadrant.nb_klingons <= 0: return if ship.docked: print("STARBASE SHIELDS PROTECT THE ENTERPRISE") return for i, klingon_ship in enumerate(self.world.quadrant.klingon_ships): if klingon_ship.shield <= 0: continue h = int((klingon_ship.shield / self.fnd(i)) * (random.random() + 2)) ship.shields -= h klingon_ship.shield /= random.random() + 3 print(f" {h} UNIT HIT ON ENTERPRISE FROM SECTOR {klingon_ship.sector} ") if ship.shields <= 0: self.end_game(won=False, quit=False, enterprise_killed=True) return print(f" ") if h >= 20 and random.random() < 0.60 and h / ship.shields > 0.02: device = fnr() ship.damage_stats[device] -= h / ship.shields + 0.5 * random.random() print( f"DAMAGE CONTROL REPORTS '{ship.devices[device]} DAMAGED BY THE HIT'" ) def phaser_control(self) -> None: """Take phaser control input and fire phasers.""" world = self.world klingon_ships = world.quadrant.klingon_ships ship = world.ship if ship.damage_stats[3] < 0: print("PHASERS INOPERATIVE") return if self.world.quadrant.nb_klingons <= 0: print("SCIENCE OFFICER SPOCK REPORTS 'SENSORS SHOW NO ENEMY SHIPS") print(" IN THIS QUADRANT'") return if ship.damage_stats[7] < 0: print("COMPUTER FAILURE HAMPERS ACCURACY") print(f"PHASERS LOCKED ON TARGET; ENERGY AVAILABLE = {ship.energy} UNITS") phaser_firepower: float = 0 while True: while True: units_to_fire = input("NUMBER OF UNITS TO FIRE? ") if len(units_to_fire) > 0: phaser_firepower = int(units_to_fire) break if phaser_firepower <= 0: return if ship.energy >= phaser_firepower: break print(f"ENERGY AVAILABLE = {ship.energy} UNITS") ship.energy -= phaser_firepower if ship.damage_stats[7] < 0: # bug in original, was d[6] phaser_firepower *= random.random() phaser_per_klingon = int(phaser_firepower / self.world.quadrant.nb_klingons) for i, klingon_ship in enumerate(klingon_ships): if klingon_ship.shield <= 0: continue h = int((phaser_per_klingon / self.fnd(i)) * (random.random() + 2)) if h <= 0.15 * klingon_ship.shield: print(f"SENSORS SHOW NO DAMAGE TO ENEMY AT {klingon_ship.sector}") else: klingon_ship.shield -= h print(f" {h} UNIT HIT ON KLINGON AT SECTOR {klingon_ship.sector}") if klingon_ship.shield <= 0: print("*** KLINGON DESTROYED ***") self.world.quadrant.nb_klingons -= 1 world.remaining_klingons -= 1 world.quadrant.set_value( klingon_ship.sector.x, klingon_ship.sector.y, Entity.void ) klingon_ship.shield = 0 world.galaxy_map[ship.position.quadrant.x][ ship.position.quadrant.y ].klingons -= 1 world.charted_galaxy_map[ship.position.quadrant.x][ ship.position.quadrant.y ] = world.galaxy_map[ship.position.quadrant.x][ ship.position.quadrant.y ] if world.remaining_klingons <= 0: self.end_game(won=True, quit=False) return else: print( f" (SENSORS SHOW {round(klingon_ship.shield,6)} UNITS REMAINING)" ) self.klingons_fire() def photon_torpedoes(self) -> None: """Take photon torpedo input and process firing of torpedoes.""" world = self.world klingon_ships = world.quadrant.klingon_ships ship = world.ship if ship.torpedoes <= 0: print("ALL PHOTON TORPEDOES EXPENDED") return if ship.damage_stats[4] < 0: print("PHOTON TUBES ARE NOT OPERATIONAL") return cd = get_user_float("PHOTON TORPEDO COURSE (1-9)? ") if cd == 9: cd = 1 if cd < 1 or cd >= 9: print("ENSIGN CHEKOV REPORTS, 'INCORRECT COURSE DATA, SIR!'") return cdi = int(cd) # Interpolate direction: dx = dirs[cdi - 1][0] + (dirs[cdi][0] - dirs[cdi - 1][0]) * (cd - cdi) dy = dirs[cdi - 1][1] + (dirs[cdi][1] - dirs[cdi - 1][1]) * (cd - cdi) ship.energy -= 2 ship.torpedoes -= 1 # Exact position x: float = ship.position.sector.x y: float = ship.position.sector.y # Rounded position (to coordinates) torpedo_x, torpedo_y = x, y print("TORPEDO TRACK:") while True: x += dx y += dy torpedo_x, torpedo_y = round(x), round(y) if torpedo_x < 0 or torpedo_x > 7 or torpedo_y < 0 or torpedo_y > 7: print("TORPEDO MISSED") self.klingons_fire() return print(f" {torpedo_x + 1} , {torpedo_y + 1}") if world.quadrant.get_value(torpedo_x, torpedo_y) != Entity.void: break if world.quadrant.get_value(torpedo_x, torpedo_y) == Entity.klingon: print("*** KLINGON DESTROYED ***") self.world.quadrant.nb_klingons -= 1 world.remaining_klingons -= 1 if world.remaining_klingons <= 0: self.end_game(won=True, quit=False) return for klingon_ship in klingon_ships: if ( torpedo_x == klingon_ship.sector.x and torpedo_y == klingon_ship.sector.y ): klingon_ship.shield = 0 elif world.quadrant.get_value(torpedo_x, torpedo_y) == Entity.star: print(f"STAR AT {torpedo_x + 1} , {torpedo_y + 1} ABSORBED TORPEDO ENERGY.") self.klingons_fire() return elif world.quadrant.get_value(torpedo_x, torpedo_y) == Entity.starbase: print("*** STARBASE DESTROYED ***") self.world.quadrant.nb_bases -= 1 world.bases_in_galaxy -= 1 if ( world.bases_in_galaxy == 0 and world.remaining_klingons <= world.stardate - world.initial_stardate - world.mission_duration ): print("THAT DOES IT, CAPTAIN!! YOU ARE HEREBY RELIEVED OF COMMAND") print("AND SENTENCED TO 99 STARDATES AT HARD LABOR ON CYGNUS 12!!") self.end_game(won=False) return print("STARFLEET COMMAND REVIEWING YOUR RECORD TO CONSIDER") print("COURT MARTIAL!") ship.docked = False world.quadrant.set_value(torpedo_x, torpedo_y, Entity.void) world.galaxy_map[ship.position.quadrant.x][ ship.position.quadrant.y ] = QuadrantData( self.world.quadrant.nb_klingons, self.world.quadrant.nb_bases, self.world.quadrant.nb_stars, ) world.charted_galaxy_map[ship.position.quadrant.x][ ship.position.quadrant.y ] = world.galaxy_map[ship.position.quadrant.x][ship.position.quadrant.y] self.klingons_fire() def short_range_scan(self) -> None: """Print a short range scan.""" self.world.ship.docked = False ship = self.world.ship for x in ( ship.position.sector.x - 1, ship.position.sector.x, ship.position.sector.x + 1, ): for y in ( ship.position.sector.y - 1, ship.position.sector.y, ship.position.sector.y + 1, ): if ( 0 <= x <= 7 and 0 <= y <= 7 and self.world.quadrant.get_value(x, y) == Entity.starbase ): ship.docked = True cs = "DOCKED" ship.refill() print("SHIELDS DROPPED FOR DOCKING PURPOSES") ship.shields = 0 break else: continue break else: if self.world.quadrant.nb_klingons > 0: cs = "*RED*" elif ship.energy < Ship.energy_capacity * 0.1: cs = "YELLOW" else: cs = "GREEN" if ship.damage_stats[1] < 0: print("\n*** SHORT RANGE SENSORS ARE OUT ***\n") return sep = "---------------------------------" print(sep) for x in range(8): line = "" for y in range(8): 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)}" elif x == 1: line += f" CONDITION {cs}" elif x == 2: line += f" QUADRANT {ship.position.quadrant}" elif x == 3: line += f" SECTOR {ship.position.sector}" elif x == 4: line += f" PHOTON TORPEDOES {int(ship.torpedoes)}" elif x == 5: line += f" TOTAL ENERGY {int(ship.energy + ship.shields)}" elif x == 6: line += f" SHIELDS {int(ship.shields)}" else: line += f" KLINGONS REMAINING {self.world.remaining_klingons}" print(line) print(sep) def long_range_scan(self) -> None: """Print a long range scan.""" if self.world.ship.damage_stats[2] < 0: print("LONG RANGE SENSORS ARE INOPERABLE") return print(f"LONG RANGE SCAN FOR QUADRANT {self.world.ship.position.quadrant}") print_scan_results( self.world.ship.position.quadrant, self.world.galaxy_map, self.world.charted_galaxy_map, ) def navigation(self) -> None: """ Take navigation input and move the Enterprise. 1/8 warp goes 1 sector in the direction dirs[course] """ world = self.world ship = world.ship cd = get_user_float("COURSE (1-9)? ") - 1 # Convert to 0-8 if cd == len(dirs) - 1: cd = 0 if cd < 0 or cd >= len(dirs): print(" LT. SULU REPORTS, 'INCORRECT COURSE DATA, SIR!'") return warp = get_user_float( f"WARP FACTOR (0-{'0.2' if ship.damage_stats[0] < 0 else '8'})? " ) if ship.damage_stats[0] < 0 and warp > 0.2: print("WARP ENGINES ARE DAMAGED. MAXIMUM SPEED = WARP 0.2") return if warp == 0: return if warp < 0 or warp > 8: print( f" CHIEF ENGINEER SCOTT REPORTS 'THE ENGINES WON'T TAKE WARP {warp}!'" ) return warp_rounds = round(warp * 8) if ship.energy < warp_rounds: print("ENGINEERING REPORTS 'INSUFFICIENT ENERGY AVAILABLE") print(f" FOR MANEUVERING AT WARP {warp}!'") if ship.shields >= warp_rounds - ship.energy and ship.damage_stats[6] >= 0: print( f"DEFLECTOR CONTROL ROOM ACKNOWLEDGES {ship.shields} UNITS OF ENERGY" ) print(" PRESENTLY DEPLOYED TO SHIELDS.") return # klingons move and fire for klingon_ship in self.world.quadrant.klingon_ships: if klingon_ship.shield != 0: world.quadrant.set_value( klingon_ship.sector.x, klingon_ship.sector.y, Entity.void ) ( klingon_ship.sector.x, klingon_ship.sector.y, ) = world.quadrant.find_empty_place() world.quadrant.set_value( klingon_ship.sector.x, klingon_ship.sector.y, Entity.klingon ) self.klingons_fire() # repair damaged devices and print damage report line = "" for i in range(8): if ship.damage_stats[i] < 0: ship.damage_stats[i] += min(warp, 1) if -0.1 < ship.damage_stats[i] < 0: ship.damage_stats[i] = -0.1 elif ship.damage_stats[i] >= 0: if len(line) == 0: line = "DAMAGE CONTROL REPORT:" line += f" {ship.devices[i]} REPAIR COMPLETED\n" if len(line) > 0: print(line) if random.random() <= 0.2: device = fnr() if random.random() < 0.6: ship.damage_stats[device] -= random.random() * 5 + 1 print(f"DAMAGE CONTROL REPORT: {ship.devices[device]} DAMAGED\n") else: ship.damage_stats[device] += random.random() * 3 + 1 print( f"DAMAGE CONTROL REPORT: {ship.devices[device]} STATE OF REPAIR IMPROVED\n" ) self.move_ship(warp_rounds, cd) world.stardate += 0.1 * int(10 * warp) if warp < 1 else 1 if world.has_mission_ended(): self.end_game(won=False, quit=False) return self.short_range_scan() def move_ship(self, warp_rounds: int, cd: float) -> None: assert cd >= 0 assert cd < len(dirs) - 1 # cd is the course data which points to 'dirs' world = self.world ship = self.world.ship world.quadrant.set_value( int(ship.position.sector.x), int(ship.position.sector.y), Entity.void ) cdi = int(cd) # Interpolate direction: dx = dirs[cdi][0] + (dirs[cdi + 1][0] - dirs[cdi][0]) * (cd - cdi) dy = dirs[cdi][1] + (dirs[cdi + 1][1] - dirs[cdi][1]) * (cd - cdi) start_quadrant = Point(ship.position.quadrant.x, ship.position.quadrant.y) sector_start_x: float = ship.position.sector.x sector_start_y: float = ship.position.sector.y for _ in range(warp_rounds): ship.position.sector.x += dx # type: ignore ship.position.sector.y += dy # type: ignore if ( ship.position.sector.x < 0 or ship.position.sector.x > 7 or ship.position.sector.y < 0 or ship.position.sector.y > 7 ): # exceeded quadrant limits; calculate final position sector_start_x += ship.position.quadrant.x * 8 + warp_rounds * dx sector_start_y += ship.position.quadrant.y * 8 + warp_rounds * dy ship.position.quadrant.x = int(sector_start_x / 8) ship.position.quadrant.y = int(sector_start_y / 8) ship.position.sector.x = int( sector_start_x - ship.position.quadrant.x * 8 ) ship.position.sector.y = int( sector_start_y - ship.position.quadrant.y * 8 ) if ship.position.sector.x < 0: ship.position.quadrant.x -= 1 ship.position.sector.x = 7 if ship.position.sector.y < 0: ship.position.quadrant.y -= 1 ship.position.sector.y = 7 hit_edge = False if ship.position.quadrant.x < 0: hit_edge = True ship.position.quadrant.x = ship.position.sector.x = 0 if ship.position.quadrant.x > 7: hit_edge = True ship.position.quadrant.x = ship.position.sector.x = 7 if ship.position.quadrant.y < 0: hit_edge = True ship.position.quadrant.y = ship.position.sector.y = 0 if ship.position.quadrant.y > 7: hit_edge = True ship.position.quadrant.y = ship.position.sector.y = 7 if hit_edge: 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( f" AT SECTOR {ship.position.sector} OF " f"QUADRANT {ship.position.quadrant}.'" ) if world.has_mission_ended(): self.end_game(won=False, quit=False) return stayed_in_quadrant = ( ship.position.quadrant.x == start_quadrant.x and ship.position.quadrant.y == start_quadrant.y ) if stayed_in_quadrant: break world.stardate += 1 ship.maneuver_energy(warp_rounds) self.new_quadrant() return ship_sector = self.world.ship.position.sector ship_x = int(ship_sector.x) ship_y = int(ship_sector.y) if self.world.quadrant.data[ship_x][ship_y] != Entity.void: ship_sector.x = int(ship_sector.x - dx) ship_sector.y = int(ship_sector.y - dy) print( "WARP ENGINES SHUT DOWN AT SECTOR " f"{ship_sector} DUE TO BAD NAVIGATION" ) break else: ship.position.sector.x, ship.position.sector.y = int( ship.position.sector.x ), int(ship.position.sector.y) world.quadrant.set_value( int(ship.position.sector.x), int(ship.position.sector.y), Entity.ship ) ship.maneuver_energy(warp_rounds) def damage_control(self) -> None: """Print a damage control report.""" ship = self.world.ship if ship.damage_stats[5] < 0: print("DAMAGE CONTROL REPORT NOT AVAILABLE") else: print("\nDEVICE STATE OF REPAIR") for r1 in range(8): print( f"{ship.devices[r1].ljust(26, ' ')}{int(ship.damage_stats[r1] * 100) * 0.01:g}" ) print() if not ship.docked: return damage_sum = sum(0.1 for i in range(8) if ship.damage_stats[i] < 0) if damage_sum == 0: return damage_sum += self.world.quadrant.delay_in_repairs_at_base if damage_sum >= 1: damage_sum = 0.9 print("\nTECHNICIANS STANDING BY TO EFFECT REPAIRS TO YOUR SHIP;") print( f"ESTIMATED TIME TO REPAIR: {round(0.01 * int(100 * damage_sum), 2)} STARDATES" ) if input("WILL YOU AUTHORIZE THE REPAIR ORDER (Y/N)? ").upper().strip() != "Y": return for i in range(8): ship.damage_stats[i] = max(ship.damage_stats[i], 0) self.world.stardate += damage_sum + 0.1 def computer(self) -> None: """Perform the various functions of the library computer.""" world = self.world ship = world.ship if ship.damage_stats[7] < 0: print("COMPUTER DISABLED") return while True: command = input("COMPUTER ACTIVE AND AWAITING COMMAND? ") if len(command) == 0: com = 6 else: try: com = int(command) except ValueError: com = 6 if com < 0: return print() if com in {0, 5}: if com == 5: print(" THE GALAXY") else: print( "\n COMPUTER RECORD OF GALAXY FOR " f"QUADRANT {ship.position.quadrant}\n" ) print(" 1 2 3 4 5 6 7 8") sep = " ----- ----- ----- ----- ----- ----- ----- -----" print(sep) for i in range(8): line = f" {str(i + 1)} " if com == 5: g2s = Quadrant.quadrant_name(i, 0, True) line += (" " * int(12 - 0.5 * len(g2s))) + g2s g2s = Quadrant.quadrant_name(i, 4, True) line += (" " * int(39 - 0.5 * len(g2s) - len(line))) + g2s else: for j in range(8): line += " " if world.charted_galaxy_map[i][j].num() == 0: line += "***" else: line += str( world.charted_galaxy_map[i][j].num() + 1000 )[-3:] print(line) print(sep) print() elif com == 1: print(" STATUS REPORT:") print( f"KLINGON{'S' if world.remaining_klingons > 1 else ''} LEFT: {world.remaining_klingons}" ) print( "MISSION MUST BE COMPLETED IN " f"{round(0.1 * int(world.remaining_time() * 10), 1)} STARDATES" ) if world.bases_in_galaxy == 0: print("YOUR STUPIDITY HAS LEFT YOU ON YOUR OWN IN") print(" THE GALAXY -- YOU HAVE NO STARBASES LEFT!") else: print( f"THE FEDERATION IS MAINTAINING {world.bases_in_galaxy} " f"STARBASE{'S' if world.bases_in_galaxy > 1 else ''} IN THE GALAXY" ) self.damage_control() elif com == 2: if self.world.quadrant.nb_klingons <= 0: print( "SCIENCE OFFICER SPOCK REPORTS 'SENSORS SHOW NO ENEMY " "SHIPS\n" " IN THIS QUADRANT'" ) return print( f"FROM ENTERPRISE TO KLINGON BATTLE CRUISER{'S' if self.world.quadrant.nb_klingons > 1 else ''}" ) for klingon_ship in self.world.quadrant.klingon_ships: if klingon_ship.shield > 0: print_direction( Point(ship.position.sector.x, ship.position.sector.y), Point( int(klingon_ship.sector.x), int(klingon_ship.sector.y), ), ) elif com == 3: if self.world.quadrant.nb_bases == 0: print( "MR. SPOCK REPORTS, 'SENSORS SHOW NO STARBASES IN THIS " "QUADRANT.'" ) return print("FROM ENTERPRISE TO STARBASE:") print_direction( Point(ship.position.sector.x, ship.position.sector.y), self.world.quadrant.starbase, ) elif com == 4: print("DIRECTION/DISTANCE CALCULATOR:") print( f"YOU ARE AT QUADRANT {ship.position.quadrant} " f"SECTOR {ship.position.sector}" ) print("PLEASE ENTER") while True: coordinates = input(" INITIAL COORDINATES (X,Y)? ").split(",") if len(coordinates) == 2: from1, from2 = int(coordinates[0]) - 1, int(coordinates[1]) - 1 if 0 <= from1 <= 7 and 0 <= from2 <= 7: break while True: coordinates = input(" FINAL COORDINATES (X,Y)? ").split(",") if len(coordinates) == 2: to1, to2 = int(coordinates[0]) - 1, int(coordinates[1]) - 1 if 0 <= to1 <= 7 and 0 <= to2 <= 7: break print_direction(Point(from1, from2), Point(to1, to2)) else: print( "FUNCTIONS AVAILABLE FROM LIBRARY-COMPUTER:\n" " 0 = CUMULATIVE GALACTIC RECORD\n" " 1 = STATUS REPORT\n" " 2 = PHOTON TORPEDO DATA\n" " 3 = STARBASE NAV DATA\n" " 4 = DIRECTION/DISTANCE CALCULATOR\n" " 5 = GALAXY 'REGION NAME' MAP\n" ) def end_game( self, won: bool = False, quit: bool = True, enterprise_killed: bool = False ) -> None: """Handle end-of-game situations.""" if won: print("CONGRATULATIONS, CAPTAIN! THE LAST KLINGON BATTLE CRUISER") print("MENACING THE FEDERATION HAS BEEN DESTROYED.\n") print( f"YOUR EFFICIENCY RATING IS {round(1000 * (self.world.remaining_klingons / (self.world.stardate - self.world.initial_stardate))**2, 4)}\n\n" ) else: if not quit: if enterprise_killed: print( "\nTHE ENTERPRISE HAS BEEN DESTROYED. THE FEDERATION " "WILL BE CONQUERED." ) print(f"IT IS STARDATE {round(self.world.stardate, 1)}") print( f"THERE WERE {self.world.remaining_klingons} KLINGON BATTLE CRUISERS LEFT AT" ) print("THE END OF YOUR MISSION.\n\n") if self.world.bases_in_galaxy == 0: sys.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'? ").upper().strip() != "AYE": sys.exit() self.restart = True klingon_shield_strength: Final = 200 # 8 sectors = 1 quadrant dirs: Final = [ # (down-up, left,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) ] # vectors in cardinal directions def fnr() -> int: """Generate a random integer from 0 to 7 inclusive.""" return random.randint(0, 7) def print_scan_results( quadrant: Point, galaxy_map: List[List[QuadrantData]], charted_galaxy_map: List[List[QuadrantData]], ) -> None: sep = "-------------------" print(sep) for x in (quadrant.x - 1, quadrant.x, quadrant.x + 1): n: List[Optional[int]] = [None, None, None] # Reveal parts of the current map for y in (quadrant.y - 1, quadrant.y, quadrant.y + 1): if 0 <= x <= 7 and 0 <= y <= 7: n[y - quadrant.y + 1] = galaxy_map[x][y].num() charted_galaxy_map[x][y] = galaxy_map[x][y] line = ": " for line_col in n: if line_col is None: line += "*** : " else: line += str(line_col + 1000).rjust(4, " ")[-3:] + " : " print(line) print(sep) def print_direction(source: Point, to: Point) -> None: """Print direction and distance between two locations in the grid.""" delta1 = -(to.x - source.x) # flip so positive is up (heading = 3) delta2 = to.y - source.y if delta2 > 0: if delta1 < 0: base = 7 else: base = 1 delta1, delta2 = delta2, delta1 elif delta1 > 0: base = 3 else: base = 5 delta1, delta2 = delta2, delta1 delta1, delta2 = abs(delta1), abs(delta2) if delta1 > 0 or delta2 > 0: # bug in original; no check for divide by 0 if delta1 >= delta2: print(f"DIRECTION = {round(base + delta2 / delta1, 6)}") else: print(f"DIRECTION = {round(base + 2 - delta1 / delta2, 6)}") print(f"DISTANCE = {round(sqrt(delta1 ** 2 + delta2 ** 2), 6)}") def main() -> None: game = Game() world = game.world ship = world.ship f: Dict[str, Callable[[], None]] = { "NAV": game.navigation, "SRS": game.short_range_scan, "LRS": game.long_range_scan, "PHA": game.phaser_control, "TOR": game.photon_torpedoes, "SHE": ship.shield_control, "DAM": game.damage_control, "COM": game.computer, "XXX": game.end_game, } while True: game.startup() game.new_quadrant() restart = False while not restart: if ship.shields + ship.energy <= 10 or ( ship.energy <= 10 and ship.damage_stats[6] != 0 ): print( "\n** FATAL ERROR ** YOU'VE JUST STRANDED YOUR SHIP " "IN SPACE.\nYOU HAVE INSUFFICIENT MANEUVERING ENERGY, " "AND SHIELD CONTROL\nIS PRESENTLY INCAPABLE OF CROSS-" "CIRCUITING TO ENGINE ROOM!!" ) command = input("COMMAND? ").upper().strip() if command in f: f[command]() else: print( "ENTER ONE OF THE FOLLOWING:\n" " NAV (TO SET COURSE)\n" " SRS (FOR SHORT RANGE SENSOR SCAN)\n" " LRS (FOR LONG RANGE SENSOR SCAN)\n" " PHA (TO FIRE PHASERS)\n" " TOR (TO FIRE PHOTON TORPEDOES)\n" " SHE (TO RAISE OR LOWER SHIELDS)\n" " DAM (FOR DAMAGE CONTROL REPORTS)\n" " COM (TO CALL ON LIBRARY-COMPUTER)\n" " XXX (TO RESIGN YOUR COMMAND)\n" ) if __name__ == "__main__": main() ================================================ FILE: 84_Super_Star_Trek/python/superstartrekins.py ================================================ """ SUPER STARTREK INSTRUCTIONS MAR 5, 1978 Just the instructions for SUPERSTARTREK Ported by Dave LeCompte """ def get_yes_no(prompt: str) -> bool: response = input(prompt).upper() return response[0] != "N" def print_header() -> None: for _ in range(12): print() t10 = " " * 10 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() def print_instructions() -> None: # Back in the 70s, at this point, the user would be prompted to # turn on their (printing) TTY to capture the output to hard copy. 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), THAN 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.") 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 QUADTANT. THEREFORE, TO GET") print(" FROM QUADRANT 6,5 TO 5,5, YOU WOULD") print(" USE COURSE 3, WARP FACTOR 1.") 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.") 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 TH 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.") 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)") 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(" THAN 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.") 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.") def main() -> None: print_header() if not get_yes_no("DO YOU NEED INSTRUCTIONS (Y/N)? "): return print_instructions() if __name__ == "__main__": main() ================================================ FILE: 84_Super_Star_Trek/ruby/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Ruby](https://www.ruby-lang.org/en/) ================================================ FILE: 84_Super_Star_Trek/rust/Cargo.toml ================================================ [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" ================================================ FILE: 84_Super_Star_Trek/rust/readme.md ================================================ # 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. ================================================ FILE: 84_Super_Star_Trek/rust/src/commands.rs ================================================ 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(); loop { let nx = (last_sector.0 as f32 + dx) as i8; let ny = (last_sector.1 as f32 + dy) as i8; if nx < 0 || ny < 0 || nx >= 8 || ny >= 8 { break; } last_sector = Pos(nx as u8, ny as u8); path.push(last_sector); } path } ================================================ FILE: 84_Super_Star_Trek/rust/src/input.rs ================================================ 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 } } ================================================ FILE: 84_Super_Star_Trek/rust/src/main.rs ================================================ 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; } } } ================================================ FILE: 84_Super_Star_Trek/rust/src/model.rs ================================================ 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 } } ================================================ FILE: 84_Super_Star_Trek/rust/src/view.rs ================================================ 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) } ================================================ FILE: 84_Super_Star_Trek/superstartrek.bas ================================================ 10 REM SUPER STARTREK - MAY 16,1978 - REQUIRES 24K MEMORY 30 REM 40 REM **** **** STAR TREK **** **** 50 REM **** SIMULATION OF A MISSION OF THE STARSHIP ENTERPRISE, 60 REM **** AS SEEN ON THE STAR TREK TV SHOW. 70 REM **** ORIGIONAL PROGRAM BY MIKE MAYFIELD, MODIFIED VERSION 80 REM **** PUBLISHED IN DEC'S "101 BASIC GAMES", BY DAVE AHL. 90 REM **** MODIFICATIONS TO THE LATTER (PLUS DEBUGGING) BY BOB 100 REM *** LEEDOM - APRIL & DECEMBER 1974, 110 REM *** WITH A LITTLE HELP FROM HIS FRIENDS . . . 120 REM *** COMMENTS, EPITHETS, AND SUGGESTIONS SOLICITED -- 130 REM *** SEND TO: R. C. LEEDOM 140 REM *** WESTINGHOUSE DEFENSE & ELECTRONICS SYSTEMS CNTR. 150 REM *** BOX 746, M.S. 338 160 REM *** BALTIMORE, MD 21203 170 REM *** 180 REM *** CONVERTED TO MICROSOFT 8 K BASIC 3/16/78 BY JOHN GORDERS 190 REM *** LINE NUMBERS FROM VERSION STREK7 OF 1/12/75 PRESERVED AS 200 REM *** MUCH AS POSSIBLE WHILE USING MULTIPLE STATEMENTS PER LINE 205 REM *** SOME LINES ARE LONGER THAN 72 CHARACTERS; THIS WAS DONE 210 REM *** BY USING "?" INSTEAD OF "PRINT" WHEN ENTERING LINES 215 REM *** 220 PRINT:PRINT:PRINT:PRINT:PRINT:PRINT:PRINT:PRINT:PRINT:PRINT:PRINT 221 PRINT " ,------*------," 222 PRINT " ,------------- '--- ------'" 223 PRINT " '-------- --' / /" 224 PRINT " ,---' '-------/ /--," 225 PRINT " '----------------'":PRINT 226 PRINT " THE USS ENTERPRISE --- NCC-1701" 227 PRINT:PRINT:PRINT:PRINT:PRINT 260 REM CLEAR 600 270 Z$=" " 330 DIM G(8,8),C(9,2),K(3,3),N(3),Z(8,8),D(8) 370 T=INT(RND(1)*20+20)*100:T0=T:T9=25+INT(RND(1)*10):D0=0:E=3000:E0=E 440 P=10:P0=P:S9=200:S=0:B9=2:K9=0:X$="":X0$=" IS " 470 DEF FND(D)=SQR((K(I,1)-S1)^2+(K(I,2)-S2)^2) 475 DEF FNR(R)=INT(RND(R)*7.98+1.01) 480 REM INITIALIZE ENTERPRIZE'S POSITION 490 Q1=FNR(1):Q2=FNR(1):S1=FNR(1):S2=FNR(1) 530 FOR I=1 TO 9:C(I,1)=0:C(I,2)=0:NEXT I 540 C(3,1)=-1:C(2,1)=-1:C(4,1)=-1:C(4,2)=-1:C(5,2)=-1:C(6,2)=-1 600 C(1,2)=1:C(2,2)=1:C(6,1)=1:C(7,1)=1:C(8,1)=1:C(8,2)=1:C(9,2)=1 670 FOR I=1 TO 8:D(I)=0:NEXT I 710 A1$="NAVSRSLRSPHATORSHEDAMCOMXXX" 810 REM SETUP WHAT EXHISTS IN GALAXY . . . 815 REM K3= # KLINGONS B3= # STARBASES S3 = # STARS 820 FOR I=1 TO 8:FOR J=1 TO 8:K3=0:Z(I,J)=0:R1=RND(1) 850 IF R1>.98 THEN K3=3:K9=K9+3:GOTO 980 860 IF R1>.95 THEN K3=2:K9=K9+2:GOTO 980 870 IF R1>.80 THEN K3=1:K9=K9+1 980 B3=0:IF RND(1)>.96 THEN B3=1:B9=B9+1 1040 G(I,J)=K3*100+B3*10+FNR(1):NEXT J:NEXT I:IF K9>T9 THEN T9=K9+1 1100 IF B9<>0 THEN 1200 1150 IF G(Q1,Q2)<200 THEN G(Q1,Q2)=G(Q1,Q2)+120:K9=K9+1 1160 B9=1:G(Q1,Q2)=G(Q1,Q2)+10:Q1=FNR(1):Q2=FNR(1) 1200 K7=K9:IF B9<>1 THEN X$="S":X0$=" ARE " 1230 PRINT "YOUR ORDERS ARE AS FOLLOWS:" 1240 PRINT " DESTROY THE";K9;"KLINGON WARSHIPS WHICH HAVE INVADED" 1252 PRINT " THE GALAXY BEFORE THEY CAN ATTACK FEDERATION HEADQUARTERS" 1260 PRINT " ON STARDATE";T0+T9;" THIS GIVES YOU";T9;"DAYS. THERE";X0$ 1272 PRINT " ";B9;"STARBASE";X$;" IN THE GALAXY FOR RESUPPLYING YOUR SHIP" 1280 PRINT:REM PRINT "HIT ANY KEY EXCEPT RETURN WHEN READY TO ACCEPT COMMAND" 1300 I=RND(1):REM IF INP(1)=13 THEN 1300 1310 REM HERE ANY TIME NEW QUADRANT ENTERED 1320 Z4=Q1:Z5=Q2:K3=0:B3=0:S3=0:G5=0:D4=.5*RND(1):Z(Q1,Q2)=G(Q1,Q2) 1390 IF Q1<1 OR Q1>8 OR Q2<1 OR Q2>8 THEN 1600 1430 GOSUB 9030:PRINT:IF T0<>T THEN 1490 1460 PRINT "YOUR MISSION BEGINS WITH YOUR STARSHIP LOCATED" 1470 PRINT "IN THE GALACTIC QUADRANT, '";G2$;"'.":GOTO 1500 1490 PRINT "NOW ENTERING ";G2$;" QUADRANT . . ." 1500 PRINT:K3=INT(G(Q1,Q2)*.01):B3=INT(G(Q1,Q2)*.1)-10*K3 1540 S3=G(Q1,Q2)-100*K3-10*B3:IF K3=0 THEN 1590 1560 PRINT "COMBAT AREA CONDITION RED":IF S>200 THEN 1590 1580 PRINT " SHIELDS DANGEROUSLY LOW" 1590 FOR I=1 TO 3:K(I,1)=0:K(I,2)=0:NEXT I 1600 FOR I=1 TO 3:K(I,3)=0:NEXT I:Q$=Z$+Z$+Z$+Z$+Z$+Z$+Z$+LEFT$(Z$,17) 1660 REM POSITION ENTERPRISE IN QUADRANT, THEN PLACE "K3" KLINGONS, & 1670 REM "B3" STARBASES, & "S3" STARS ELSEWHERE. 1680 A$="<*>":Z1=S1:Z2=S2:GOSUB 8670:IF K3<1 THEN 1820 1720 FOR I=1 TO K3:GOSUB 8590:A$="+K+":Z1=R1:Z2=R2 1780 GOSUB 8670:K(I,1)=R1:K(I,2)=R2:K(I,3)=S9*(0.5+RND(1)):NEXT I 1820 IF B3<1 THEN 1910 1880 GOSUB 8590:A$=">!<":Z1=R1:B4=R1:Z2=R2:B5=R2:GOSUB 8670 1910 FOR I=1 TO S3:GOSUB 8590:A$=" * ":Z1=R1:Z2=R2:GOSUB 8670:NEXT I 1980 GOSUB 6430 1990 IF S+E>10 THEN IF E>10 OR D(7)=0 THEN 2060 2020 PRINT:PRINT "** FATAL ERROR ** YOU'VE JUST STRANDED YOUR SHIP IN " 2030 PRINT "SPACE":PRINT "YOU HAVE INSUFFICIENT MANEUVERING ENERGY,"; 2040 PRINT " AND SHIELD CONTROL":PRINT "IS PRESENTLY INCAPABLE OF CROSS"; 2050 PRINT "-CIRCUITING TO ENGINE ROOM!!":GOTO 6220 2060 INPUT"COMMAND";A$ 2080 FOR I=1 TO 9:IF LEFT$(A$,3)<>MID$(A1$,3*I-2,3) THEN 2160 2140 ON I GOTO 2300,1980,4000,4260,4700,5530,5690,7290,6270 2160 NEXT I:PRINT "ENTER ONE OF THE FOLLOWING:" 2180 PRINT " NAV (TO SET COURSE)" 2190 PRINT " SRS (FOR SHORT RANGE SENSOR SCAN)" 2200 PRINT " LRS (FOR LONG RANGE SENSOR SCAN)" 2210 PRINT " PHA (TO FIRE PHASERS)" 2220 PRINT " TOR (TO FIRE PHOTON TORPEDOES)" 2230 PRINT " SHE (TO RAISE OR LOWER SHIELDS)" 2240 PRINT " DAM (FOR DAMAGE CONTROL REPORTS)" 2250 PRINT " COM (TO CALL ON LIBRARY-COMPUTER)" 2260 PRINT " XXX (TO RESIGN YOUR COMMAND)":PRINT:GOTO 1990 2290 REM COURSE CONTROL BEGINS HERE 2300 INPUT"COURSE (0-9)";C1:IF C1=9 THEN C1=1 2310 IF C1>=1 AND C1<9 THEN 2350 2330 PRINT " LT. SULU REPORTS, 'INCORRECT COURSE DATA, SIR!'":GOTO 1990 2350 X$="8":IF D(1)<0 THEN X$="0.2" 2360 PRINT "WARP FACTOR (0-";X$;")";:INPUT W1:IF D(1)<0 AND W1>.2 THEN 2470 2380 IF W1>0 AND W1<=8 THEN 2490 2390 IF W1=0 THEN 1990 2420 PRINT " CHIEF ENGINEER SCOTT REPORTS 'THE ENGINES WON'T TAKE"; 2430 PRINT " WARP ";W1;"!'":GOTO 1990 2470 PRINT "WARP ENGINES ARE DAMAGED. MAXIUM SPEED = WARP 0.2":GOTO 1990 2490 N=INT(W1*8+.5):IF E-N>=0 THEN 2590 2500 PRINT "ENGINEERING REPORTS 'INSUFFICIENT ENERGY AVAILABLE" 2510 PRINT " FOR MANEUVERING AT WARP";W1;"!'" 2530 IF S=1 THEN D6=1 2770 FOR I=1 TO 8:IF D(I)>=0 THEN 2880 2790 D(I)=D(I)+D6:IF D(I)>-.1 AND D(I)<0 THEN D(I)=-.1:GOTO 2880 2800 IF D(I)<0 THEN 2880 2810 IF D1<>1 THEN D1=1:PRINT "DAMAGE CONTROL REPORT: "; 2840 PRINT TAB(8);:R1=I:GOSUB 8790:PRINT G2$;" REPAIR COMPLETED." 2880 NEXT I:IF RND(1)>.2 THEN 3070 2910 R1=FNR(1):IF RND(1)>=.6 THEN 3000 2930 D(R1)=D(R1)-(RND(1)*5+1):PRINT "DAMAGE CONTROL REPORT: "; 2960 GOSUB 8790:PRINT G2$;" DAMAGED":PRINT:GOTO 3070 3000 D(R1)=D(R1)+RND(1)*3+1:PRINT "DAMAGE CONTROL REPORT: "; 3030 GOSUB 8790:PRINT G2$;" STATE OF REPAIR IMPROVED":PRINT 3060 REM BEGIN MOVING STARSHIP 3070 A$=" ":Z1=INT(S1):Z2=INT(S2):GOSUB 8670 3110 X1=C(C1,1)+(C(C1+1,1)-C(C1,1))*(C1-INT(C1)):X=S1:Y=S2 3140 X2=C(C1,2)+(C(C1+1,2)-C(C1,2))*(C1-INT(C1)):Q4=Q1:Q5=Q2 3170 FOR I=1 TO N:S1=S1+X1:S2=S2+X2:IF S1<1 OR S1>=9 OR S2<1 OR S2>=9 THEN 3500 3240 S8=INT(S1)*24+INT(S2)*3-26:IF MID$(Q$,S8,2)=" " THEN 3360 3320 S1=INT(S1-X1):S2=INT(S2-X2):PRINT "WARP ENGINES SHUT DOWN AT "; 3350 PRINT "SECTOR";S1;",";S2;"DUE TO BAD NAVAGATION":GOTO 3370 3360 NEXT I:S1=INT(S1):S2=INT(S2) 3370 A$="<*>":Z1=INT(S1):Z2=INT(S2):GOSUB 8670:GOSUB 3910:T8=1 3430 IF W1<1 THEN T8=.1*INT(10*W1) 3450 T=T+T8:IF T>T0+T9 THEN 6220 3470 REM SEE IF DOCKED, THEN GET COMMAND 3480 GOTO 1980 3490 REM EXCEEDED QUADRANT LIMITS 3500 X=8*Q1+X+N*X1:Y=8*Q2+Y+N*X2:Q1=INT(X/8):Q2=INT(Y/8):S1=INT(X-Q1*8) 3550 S2=INT(Y-Q2*8):IF S1=0 THEN Q1=Q1-1:S1=8 3590 IF S2=0 THEN Q2=Q2-1:S2=8 3620 X5=0:IF Q1<1 THEN X5=1:Q1=1:S1=1 3670 IF Q1>8 THEN X5=1:Q1=8:S1=8 3710 IF Q2<1 THEN X5=1:Q2=1:S2=1 3750 IF Q2>8 THEN X5=1:Q2=8:S2=8 3790 IF X5=0 THEN 3860 3800 PRINT "LT. UHURA REPORTS MESSAGE FROM STARFLEET COMMAND:" 3810 PRINT " 'PERMISSION TO ATTEMPT CROSSING OF GALACTIC PERIMETER" 3820 PRINT " IS HEREBY *DENIED*. SHUT DOWN YOUR ENGINES.'" 3830 PRINT "CHIEF ENGINEER SCOTT REPORTS 'WARP ENGINES SHUT DOWN" 3840 PRINT " AT SECTOR";S1;",";S2;"OF QUADRANT";Q1;",";Q2;".'" 3850 IF T>T0+T9 THEN 6220 3860 IF 8*Q1+Q2=8*Q4+Q5 THEN 3370 3870 T=T+1:GOSUB 3910:GOTO 1320 3900 REM MANEUVER ENERGY S/R ** 3910 E=E-N-10:IF E>=0 THEN RETURN 3930 PRINT "SHIELD CONTROL SUPPLIES ENERGY TO COMPLETE THE MANEUVER." 3940 S=S+E:E=0:IF S<=0 THEN S=0 3980 RETURN 3990 REM LONG RANGE SENSOR SCAN CODE 4000 IF D(3)<0 THEN PRINT "LONG RANGE SENSORS ARE INOPERABLE":GOTO 1990 4030 PRINT "LONG RANGE SCAN FOR QUADRANT";Q1;",";Q2 4040 O1$="-------------------":PRINT O1$ 4060 FOR I=Q1-1TOQ1+1:N(1)=-1:N(2)=-2:N(3)=-3:FOR J=Q2-1TOQ2+1 4120 IF I>0 AND I<9 AND J>0 AND J<9 THEN N(J-Q2+2)=G(I,J):Z(I,J)=G(I,J) 4180 NEXT J:FOR L=1 TO 3:PRINT ": ";:IF N(L)<0 THEN PRINT "*** ";:GOTO 4230 4210 PRINT RIGHT$(STR$(N(L)+1000),3);" "; 4230 NEXT L:PRINT ":":PRINT O1$:NEXT I:GOTO 1990 4250 REM PHASER CONTROL CODE BEGINS HERE 4260 IF D(4)<0 THEN PRINT "PHASERS INOPERATIVE":GOTO 1990 4265 IF K3>0 THEN 4330 4270 PRINT "SCIENCE OFFICER SPOCK REPORTS 'SENSORS SHOW NO ENEMY SHIPS" 4280 PRINT " IN THIS QUADRANT'":GOTO 1990 4330 IF D(8)<0 THEN PRINT "COMPUTER FAILURE HAMPERS ACCURACY" 4350 PRINT "PHASERS LOCKED ON TARGET; "; 4360 PRINT "ENERGY AVAILABLE =";E;"UNITS" 4370 INPUT"NUMBER OF UNITS TO FIRE";X:IF X<=0 THEN 1990 4400 IF E-X<0 THEN 4360 4410 E=E-X:IF D(7)<0 THEN X=X*RND(1) 4450 H1=INT(X/K3):FOR I=1TO3:IF K(I,3)<=0 THEN 4670 4480 H=INT((H1/FND(0))*(RND(1)+2)):IF H>.15*K(I,3) THEN 4530 4500 PRINT "SENSORS SHOW NO DAMAGE TO ENEMY AT ";K(I,1);",";K(I,2):GOTO 4670 4530 K(I,3)=K(I,3)-H:PRINT H;"UNIT HIT ON KLINGON AT SECTOR";K(I,1);","; 4550 PRINT K(I,2):IF K(I,3)<=0 THEN PRINT "*** KLINGON DESTROYED ***":GOTO 4580 4560 PRINT " (SENSORS SHOW";K(I,3);"UNITS REMAINING)":GOTO 4670 4580 K3=K3-1:K9=K9-1:Z1=K(I,1):Z2=K(I,2):A$=" ":GOSUB 8670 4650 K(I,3)=0:G(Q1,Q2)=G(Q1,Q2)-100:Z(Q1,Q2)=G(Q1,Q2):IF K9<=0 THEN 6370 4670 NEXT I:GOSUB 6000:GOTO 1990 4690 REM PHOTON TORPEDO CODE BEGINS HERE 4700 IF P<=0 THEN PRINT "ALL PHOTON TORPEDOES EXPENDED":GOTO 1990 4730 IF D(5)<0 THEN PRINT "PHOTON TUBES ARE NOT OPERATIONAL":GOTO 1990 4760 INPUT"PHOTON TORPEDO COURSE (1-9)";C1:IF C1=9 THEN C1=1 4780 IF C1>=1ANDC1<9 THEN 4850 4790 PRINT "ENSIGN CHEKOV REPORTS, 'INCORRECT COURSE DATA, SIR!'" 4800 GOTO 1990 4850 X1=C(C1,1)+(C(C1+1,1)-C(C1,1))*(C1-INT(C1)):E=E-2:P=P-1 4860 X2=C(C1,2)+(C(C1+1,2)-C(C1,2))*(C1-INT(C1)):X=S1:Y=S2 4910 PRINT "TORPEDO TRACK:" 4920 X=X+X1:Y=Y+X2:X3=INT(X+.5):Y3=INT(Y+.5) 4960 IF X3<1 OR X3>8 OR Y3<1 OR Y3>8 THEN 5490 5000 PRINT " ";X3;",";Y3:A$=" ":Z1=X:Z2=Y:GOSUB 8830 5050 IF Z3<>0 THEN 4920 5060 A$="+K+":Z1=X:Z2=Y:GOSUB 8830:IF Z3=0 THEN 5210 5110 PRINT "*** KLINGON DESTROYED ***":K3=K3-1:K9=K9-1:IF K9<=0 THEN 6370 5150 FOR I=1TO3:IF X3=K(I,1) AND Y3=K(I,2) THEN 5190 5180 NEXT I:I=3 5190 K(I,3)=0:GOTO 5430 5210 A$=" * ":Z1=X:Z2=Y:GOSUB 8830:IF Z3=0 THEN 5280 5260 PRINT "STAR AT";X3;",";Y3;"ABSORBED TORPEDO ENERGY.":GOSUB 6000:GOTO 1990 5280 A$=">!<":Z1=X:Z2=Y:GOSUB 8830:IF Z3=0 THEN 4760 5330 PRINT "*** STARBASE DESTROYED ***":B3=B3-1:B9=B9-1 5360 IF B9>0 OR K9>T-T0-T9 THEN 5400 5370 PRINT "THAT DOES IT, CAPTAIN!! YOU ARE HEREBY RELIEVED OF COMMAND" 5380 PRINT "AND SENTENCED TO 99 STARDATES AT HARD LABOR ON CYGNUS 12!!" 5390 GOTO 6270 5400 PRINT "STARFLEET COMMAND REVIEWING YOUR RECORD TO CONSIDER" 5410 PRINT "COURT MARTIAL!":D0=0 5430 Z1=X:Z2=Y:A$=" ":GOSUB 8670 5470 G(Q1,Q2)=K3*100+B3*10+S3:Z(Q1,Q2)=G(Q1,Q2):GOSUB 6000:GOTO 1990 5490 PRINT "TORPEDO MISSED":GOSUB 6000:GOTO 1990 5520 REM SHIELD CONTROL 5530 IF D(7)<0 THEN PRINT "SHIELD CONTROL INOPERABLE":GOTO 1990 5560 PRINT "ENERGY AVAILABLE =";E+S;:INPUT"NUMBER OF UNITS TO SHIELDS";X 5580 IF X<0 OR S=X THEN PRINT "":GOTO 1990 5590 IF X<=E+S THEN 5630 5600 PRINT "SHIELD CONTROL REPORTS 'THIS IS NOT THE FEDERATION TREASURY.'" 5610 PRINT "":GOTO 1990 5630 E=E+S-X:S=X:PRINT "DEFLECTOR CONTROL ROOM REPORT:" 5660 PRINT " 'SHIELDS NOW AT";INT(S);"UNITS PER YOUR COMMAND.'":GOTO 1990 5680 REM DAMAGE CONTROL 5690 IF D(6)>=0 THEN 5910 5700 PRINT "DAMAGE CONTROL REPORT NOT AVAILABLE":IF D0=0 THEN 1990 5720 D3=0:FOR I=1TO8:IF D(I)<0 THEN D3=D3+.1 5760 NEXT I:IF D3=0 THEN 1990 5780 PRINT:D3=D3+D4:IF D3>=1 THEN D3=.9 5810 PRINT "TECHNICIANS STANDING BY TO EFFECT REPAIRS TO YOUR SHIP;" 5820 PRINT "ESTIMATED TIME TO REPAIR:";.01*INT(100*D3);"STARDATES" 5840 INPUT "WILL YOU AUTHORIZE THE REPAIR ORDER (Y/N)";A$ 5860 IF A$<>"Y" THEN 1990 5870 FOR I=1TO8:IF D(I)<0 THEN D(I)=0 5890 NEXT I:T=T+D3+.1 5910 PRINT:PRINT "DEVICE STATE OF REPAIR":FOR R1=1TO8 5920 GOSUB 8790:PRINT G2$;LEFT$(Z$,25-LEN(G2$));INT(D(R1)*100)*.01 5950 NEXT R1:PRINT:IF D0<>0 THEN 5720 5980 GOTO 1990 5990 REM KLINGONS SHOOTING 6000 IF K3<=0 THEN RETURN 6010 IF D0<>0 THEN PRINT "STARBASE SHIELDS PROTECT THE ENTERPRISE":RETURN 6040 FOR I=1TO3:IF K(I,3)<=0 THEN 6200 6060 H=INT((K(I,3)/FND(1))*(2+RND(1))):S=S-H:K(I,3)=K(I,3)/(3+RND(0)) 6080 PRINT H;"UNIT HIT ON ENTERPRISE FROM SECTOR";K(I,1);",";K(I,2) 6090 IF S<=0 THEN 6240 6100 PRINT " ":IF H<20 THEN 6200 6120 IF RND(1)>.6 OR H/S<=.02 THEN 6200 6140 R1=FNR(1):D(R1)=D(R1)-H/S-.5*RND(1):GOSUB 8790 6170 PRINT "DAMAGE CONTROL REPORTS ";G2$;" DAMAGED BY THE HIT'" 6200 NEXT I:RETURN 6210 REM END OF GAME 6220 PRINT "IT IS STARDATE";T:GOTO 6270 6240 PRINT:PRINT "THE ENTERPRISE HAS BEEN DESTROYED. THEN FEDERATION "; 6250 PRINT "WILL BE CONQUERED":GOTO 6220 6270 PRINT "THERE WERE";K9;"KLINGON BATTLE CRUISERS LEFT AT" 6280 PRINT "THE END OF YOUR MISSION." 6290 PRINT:PRINT:IF B9=0 THEN 6360 6310 PRINT "THE FEDERATION IS IN NEED OF A NEW STARSHIP COMMANDER" 6320 PRINT "FOR A SIMILAR MISSION -- IF THERE IS A VOLUNTEER," 6330 INPUT"LET HIM STEP FORWARD AND ENTER 'AYE'";A$:IF A$="AYE" THEN 10 6360 END 6370 PRINT "CONGRULATION, CAPTAIN! THEN LAST KLINGON BATTLE CRUISER" 6380 PRINT "MENACING THE FDERATION HAS BEEN DESTROYED.":PRINT 6400 PRINT "YOUR EFFICIENCY RATING IS";1000*(K7/(T-T0))^2:GOTO 6290 6420 REM SHORT RANGE SENSOR SCAN & STARTUP SUBROUTINE 6430 FOR I=S1-1TOS1+1:FOR J=S2-1 TO S2+1 6450 IF INT(I+.5)<1 OR INT(I+.5)>8 OR INT(J+.5)<1 OR INT(J+.5)>8 THEN 6540 6490 A$=">!<":Z1=I:Z2=J:GOSUB 8830:IF Z3=1 THEN 6580 6540 NEXT J:NEXT I:D0=0:GOTO 6650 6580 D0=1:C$="DOCKED":E=E0:P=P0 6620 PRINT "SHIELDS DROPPED FOR DOCKING PURPOSES":S=0:GOTO 6720 6650 IF K3>0 THEN C$="*RED*":GOTO 6720 6660 C$="GREEN":IF E=0 THEN 6770 6730 PRINT:PRINT "*** SHORT RANGE SENSORS ARE OUT ***":PRINT:RETURN 6770 O1$="---------------------------------":PRINT O1$:FOR I=1 TO 8 6820 FOR J=(I-1)*24+1 TO (I-1)*24+22STEP3:PRINT " ";MID$(Q$,J,3);:NEXT J 6830 ON I GOTO 6850,6900,6960,7020,7070,7120,7180,7240 6850 PRINT " STARDATE ";INT(T*10)*.1:GOTO 7260 6900 PRINT " CONDITION ";C$:GOTO 7260 6960 PRINT " QUADRANT ";Q1;",";Q2:GOTO 7260 7020 PRINT " SECTOR ";S1;",";S2:GOTO 7260 7070 PRINT " PHOTON TORPEDOES ";INT(P):GOTO 7260 7120 PRINT " TOTAL ENERGY ";INT(E+S):GOTO 7260 7180 PRINT " SHIELDS ";INT(S):GOTO 7260 7240 PRINT " KLINGONS REMAINING";INT(K9) 7260 NEXT I:PRINT O1$:RETURN 7280 REM LIBRARY COMPUTER CODE 7290 IF D(8)<0 THEN PRINT "COMPUTER DISABLED":GOTO 1990 7320 INPUT"COMPUTER ACTIVE AND AWAITING COMMAND";A:IF A<0 THEN 1990 7350 PRINT:H8=1:ON A+1 GOTO 7540,7900,8070,8500,8150,7400 7360 PRINT "FUNCTIONS AVAILABLE FROM LIBRARY-COMPUTER:" 7370 PRINT " 0 = CUMULATIVE GALACTIC RECORD" 7372 PRINT " 1 = STATUS REPORT" 7374 PRINT " 2 = PHOTON TORPEDO DATA" 7376 PRINT " 3 = STARBASE NAV DATA" 7378 PRINT " 4 = DIRECTION/DISTANCE CALCULATOR" 7380 PRINT " 5 = GALAXY 'REGION NAME' MAP":PRINT:GOTO 7320 7390 REM SETUP TO CHANGE CUM GAL RECORD TO GALAXY MAP 7400 H8=0:G5=1:PRINT " THE GALAXY":GOTO 7550 7530 REM CUM GALACTIC RECORD 7540 REM INPUT"DO YOU WANT A HARDCOPY? IS THE TTY ON (Y/N)";A$ 7542 REM IF A$="Y" THEN POKE1229,2:POKE1237,3:NULL1 7543 PRINT:PRINT " "; 7544 PRINT "COMPUTER RECORD OF GALAXY FOR QUADRANT";Q1;",";Q2 7546 PRINT 7550 PRINT " 1 2 3 4 5 6 7 8" 7560 O1$=" ----- ----- ----- ----- ----- ----- ----- -----" 7570 PRINT O1$:FOR I=1 TO 8:PRINT I;:IF H8=0 THEN 7740 7630 FOR J=1 TO 8:PRINT " ";:IF Z(I,J)=0 THEN PRINT "***";:GOTO 7720 7700 PRINT RIGHT$(STR$(Z(I,J)+1000),3); 7720 NEXT J:GOTO 7850 7740 Z4=I:Z5=1:GOSUB 9030:J0=INT(15-.5*LEN(G2$)):PRINT TAB(J0);G2$; 7800 Z5=5:GOSUB 9030:J0=INT(39-.5*LEN(G2$)):PRINT TAB(J0);G2$; 7850 PRINT:PRINT O1$:NEXT I:PRINT:GOTO 1990 7890 REM STATUS REPORT 7900 PRINT " STATUS REPORT:":X$="":IF K9>1 THEN X$="S" 7940 PRINT "KLINGON";X$;" LEFT: ";K9 7960 PRINT "MISSION MUST BE COMPLETED IN";.1*INT((T0+T9-T)*10);"STARDATES" 7970 X$="S":IF B9<2 THEN X$="":IF B9<1 THEN 8010 7980 PRINT "THE FEDERATION IS MAINTAINING";B9;"STARBASE";X$;" IN THE GALAXY" 7990 GOTO 5690 8010 PRINT "YOUR STUPIDITY HAS LEFT YOU ON YOUR ON IN" 8020 PRINT " THE GALAXY -- YOU HAVE NO STARBASES LEFT!":GOTO 5690 8060 REM TORPEDO, BASE NAV, D/D CALCULATOR 8070 IF K3<=0 THEN 4270 8080 X$="":IF K3>1 THEN X$="S" 8090 PRINT "FROM ENTERPRISE TO KLINGON BATTLE CRUSER";X$ 8100 H8=0:FOR I=1 TO 3:IF K(I,3)<=0 THEN 8480 8110 W1=K(I,1):X=K(I,2) 8120 C1=S1:A=S2:GOTO 8220 8150 PRINT "DIRECTION/DISTANCE CALCULATOR:" 8160 PRINT "YOU ARE AT QUADRANT ";Q1;",";Q2;" SECTOR ";S1;",";S2 8170 PRINT "PLEASE ENTER":INPUT" INITIAL COORDINATES (X,Y)";C1,A 8200 INPUT" FINAL COORDINATES (X,Y)";W1,X 8220 X=X-A:A=C1-W1:IF X<0 THEN 8350 8250 IF A<0 THEN 8410 8260 IF X>0 THEN 8280 8270 IF A=0 THEN C1=5:GOTO 8290 8280 C1=1 8290 IF ABS(A)<=ABS(X) THEN 8330 8310 PRINT "DIRECTION =";C1+(((ABS(A)-ABS(X))+ABS(A))/ABS(A)):GOTO 8460 8330 PRINT "DIRECTION =";C1+(ABS(A)/ABS(X)):GOTO 8460 8350 IF A>0 THEN C1=3:GOTO 8420 8360 IF X<>0 THEN C1=5:GOTO 8290 8410 C1=7 8420 IF ABS(A)>=ABS(X) THEN 8450 8430 PRINT "DIRECTION =";C1+(((ABS(X)-ABS(A))+ABS(X))/ABS(X)):GOTO 8460 8450 PRINT "DIRECTION =";C1+(ABS(X)/ABS(A)) 8460 PRINT "DISTANCE =";SQR(X^2+A^2):IF H8=1 THEN 1990 8480 NEXT I:GOTO 1990 8500 IF B3<>0 THEN PRINT "FROM ENTERPRISE TO STARBASE:":W1=B4:X=B5:GOTO 8120 8510 PRINT "MR. SPOCK REPORTS, 'SENSORS SHOW NO STARBASES IN THIS"; 8520 PRINT " QUADRANT.'":GOTO 1990 8580 REM FIND EMPTY PLACE IN QUADRANT (FOR THINGS) 8590 R1=FNR(1):R2=FNR(1):A$=" ":Z1=R1:Z2=R2:GOSUB 8830:IF Z3=0 THEN 8590 8600 RETURN 8660 REM INSERT IN STRING ARRAY FOR QUADRANT 8670 S8=INT(Z2-.5)*3+INT(Z1-.5)*24+1 8675 IF LEN(A$)<>3 THEN PRINT "ERROR":STOP 8680 IF S8=1 THEN Q$=A$+RIGHT$(Q$,189):RETURN 8690 IF S8=190 THEN Q$=LEFT$(Q$,189)+A$:RETURN 8700 Q$=LEFT$(Q$,S8-1)+A$+RIGHT$(Q$,190-S8):RETURN 8780 REM PRINTS DEVICE NAME 8790 ON R1 GOTO 8792,8794,8796,8798,8800,8802,8804,8806 8792 G2$="WARP ENGINES":RETURN 8794 G2$="SHORT RANGE SENSORS":RETURN 8796 G2$="LONG RANGE SENSORS":RETURN 8798 G2$="PHASER CONTROL":RETURN 8800 G2$="PHOTON TUBES":RETURN 8802 G2$="DAMAGE CONTROL":RETURN 8804 G2$="SHIELD CONTROL":RETURN 8806 G2$="LIBRARY-COMPUTER":RETURN 8820 REM STRING COMPARISON IN QUADRANT ARRAY 8830 Z1=INT(Z1+.5):Z2=INT(Z2+.5):S8=(Z2-1)*3+(Z1-1)*24+1:Z3=0 8890 IF MID$(Q$,S8,3)<>A$ THEN RETURN 8900 Z3=1:RETURN 9010 REM QUADRANT NAME IN G2$ FROM Z4,Z5 (=Q1,Q2) 9020 REM CALL WITH G5=1 TO GET REGION NAME ONLY 9030 IF Z5<=4 THEN ON Z4 GOTO 9040,9050,9060,9070,9080,9090,9100,9110 9035 GOTO 9120 9040 G2$="ANTARES":GOTO 9210 9050 G2$="RIGEL":GOTO 9210 9060 G2$="PROCYON":GOTO 9210 9070 G2$="VEGA":GOTO 9210 9080 G2$="CANOPUS":GOTO 9210 9090 G2$="ALTAIR":GOTO 9210 9100 G2$="SAGITTARIUS":GOTO 9210 9110 G2$="POLLUX":GOTO 9210 9120 ON Z4 GOTO 9130,9140,9150,9160,9170,9180,9190,9200 9130 G2$="SIRIUS":GOTO 9210 9140 G2$="DENEB":GOTO 9210 9150 G2$="CAPELLA":GOTO 9210 9160 G2$="BETELGEUSE":GOTO 9210 9170 G2$="ALDEBARAN":GOTO 9210 9180 G2$="REGULUS":GOTO 9210 9190 G2$="ARCTURUS":GOTO 9210 9200 G2$="SPICA" 9210 IF G5<>1 THEN ON Z5 GOTO 9230,9240,9250,9260,9230,9240,9250,9260 9220 RETURN 9230 G2$=G2$+" I":RETURN 9240 G2$=G2$+" II":RETURN 9250 G2$=G2$+" III":RETURN 9260 G2$=G2$+" IV":RETURN ================================================ FILE: 84_Super_Star_Trek/superstartrekins.bas ================================================ 10 REM INSTRUCTIONS FOR "SUPER STARTREK" MAR 5, 1978 20 FOR I=1 TO 12:PRINT:NEXT I 21 PRINT TAB(10);"*************************************" 22 PRINT TAB(10);"* *" 23 PRINT TAB(10);"* *" 30 PRINT TAB(10);"* * * SUPER STAR TREK * * *" 31 PRINT TAB(10);"* *" 32 PRINT TAB(10);"* *" 35 PRINT TAB(10);"*************************************" 36 FOR I=1 TO 8:PRINT:NEXT I 40 INPUT "DO YOU NEED INSTRUCTIONS (Y/N)";K$:IF K$="N" THEN 2000 44 PRINT 45 REM PRINT "TURN THE TTY ON-LINE AND HIT ANY KEY EXCEPT RETURN" 46 REM IF INP(1)=13 THEN 46 50 REM POKE 1229,2:POKE 1237,3:NULL 1 90 PRINT" INSTRUCTIONS FOR 'SUPER STAR TREK'" 100 PRINT 110 PRINT"1. WHEN YOU SEE \COMMAND ?\ PRINTED, ENTER ONE OF THE LEGAL" 120 PRINT" COMMANDS (NAV,SRS,LRS,PHA,TOR,SHE,DAM,COM, OR XXX)." 130 PRINT"2. IF YOU SHOULD TYPE IN AN ILLEGAL COMMAND, YOU'LL GET A SHORT" 140 PRINT" LIST OF THE LEGAL COMMANDS PRINTED OUT." 150 PRINT"3. SOME COMMANDS REQUIRE YOU TO ENTER DATA (FOR EXAMPLE, THE" 160 PRINT" 'NAV' COMMAND COMES BACK WITH 'COURSE (1-9) ?'.) IF YOU" 170 PRINT" TYPE IN ILLEGAL DATA (LIKE NEGATIVE NUMBERS), THAN COMMAND" 180 PRINT" WILL BE ABORTED" 190 PRINT 270 PRINT" THE GALAXY IS DIVIDED INTO AN 8 X 8 QUADRANT GRID," 280 PRINT"AND EACH QUADRANT IS FURTHER DIVIDED INTO AN 8 X 8 SECTOR GRID." 290 PRINT 300 PRINT" YOU WILL BE ASSIGNED A STARTING POINT SOMEWHERE IN THE" 310 PRINT"GALAXY TO BEGIN A TOUR OF DUTY AS COMANDER OF THE STARSHIP" 320 PRINT"\ENTERPRISE\; YOUR MISSION: TO SEEK AND DESTROY THE FLEET OF" 330 PRINT"KLINGON WARWHIPS WHICH ARE MENACING THE UNITED FEDERATION OF" 340 PRINT"PLANETS." 360 PRINT 370 PRINT" YOU HAVE THE FOLLOWING COMMANDS AVAILABLE TO YOU AS CAPTAIN" 380 PRINT"OF THE STARSHIP ENTERPRISE:" 385 PRINT 390 PRINT"\NAV\ COMMAND = WARP ENGINE CONTROL --" 400 PRINT" COURSE IS IN A CIRCULAR NUMERICAL 4 3 2" 410 PRINT" VECTOR ARRANGEMENT AS SHOWN . . ." 420 PRINT" INTEGER AND REAL VALUES MAY BE ..." 430 PRINT" USED. (THUS COURSE 1.5 IS HALF- 5 ---*--- 1" 440 PRINT" WAY BETWEEN 1 AND 2 ..." 450 PRINT" . . ." 460 PRINT" VALUES MAY APPROACH 9.0, WHICH 6 7 8" 470 PRINT" ITSELF IS EQUIVALENT TO 1.0" 480 PRINT" COURSE" 490 PRINT" ONE WARP FACTOR IS THE SIZE OF " 500 PRINT" ONE QUADTANT. THEREFORE, TO GET" 510 PRINT" FROM QUADRANT 6,5 TO 5,5, YOU WOULD" 520 PRINT" USE COURSE 3, WARP FACTOR 1." 530 PRINT 540 PRINT"\SRS\ COMMAND = SHORT RANGE SENSOR SCAN" 550 PRINT" SHOWS YOU A SCAN OF YOUR PRESENT QUADRANT." 555 PRINT 560 PRINT" SYMBOLOGY ON YOUR SENSOR SCREEN IS AS FOLLOWS:" 570 PRINT" <*> = YOUR STARSHIP'S POSITION" 580 PRINT" +K+ = KLINGON BATTLE CRUISER" 590 PRINT" >!< = FEDERATION STARBASE (REFUEL/REPAIR/RE-ARM HERE!)" 600 PRINT" * = STAR" 605 PRINT 610 PRINT" A CONDENSED 'STATUS REPORT' WILL ALSO BE PRESENTED." 620 PRINT 640 PRINT"\LRS\ COMMAND = LONG RANGE SENSOR SCAN" 650 PRINT" SHOWS CONDITIONS IN SPACE FOR ONE QUADRANT ON EACH SIDE" 660 PRINT" OF THE ENTERPRISE (WHICH IS IN THE MIDDLE OF THE SCAN)" 670 PRINT" THE SCAN IS CODED IN THE FORM \###\, WHERE TH UNITS DIGIT" 680 PRINT" IS THE NUMBER OF STARS, THE TENS DIGIT IS THE NUMBER OF" 690 PRINT" STARBASES, AND THE HUNDRESDS DIGIT IS THE NUMBER OF" 700 PRINT" KLINGONS." 705 PRINT 706 PRINT" EXAMPLE - 207 = 2 KLINGONS, NO STARBASES, & 7 STARS." 710 PRINT 720 PRINT"\PHA\ COMMAND = PHASER CONTROL." 730 PRINT" ALLOWS YOU TO DESTROY THE KLINGON BATTLE CRUISERS BY " 740 PRINT" ZAPPING THEM WITH SUITABLY LARGE UNITS OF ENERGY TO" 750 PRINT" DEPLETE THEIR SHIELD POWER. (REMEMBER, KLINGONS HAVE" 760 PRINT" PHASERS TOO!)" 770 PRINT 780 PRINT"\TOR\ COMMAND = PHOTON TORPEDO CONTROL" 790 PRINT" TORPEDO COURSE IS THE SAME AS USED IN WARP ENGINE CONTROL" 800 PRINT" IF YOU HIT THE KLINGON VESSEL, HE IS DESTROYED AND" 810 PRINT" CANNOT FIRE BACK AT YOU. IF YOU MISS, YOU ARE SUBJECT TO" 820 PRINT" HIS PHASER FIRE. IN EITHER CASE, YOU ARE ALSO SUBJECT TO " 825 PRINT" THE PHASER FIRE OF ALL OTHER KLINGONS IN THE QUADRANT." 830 PRINT 835 PRINT" THE LIBRARY-COMPUTER (\COM\ COMMAND) HAS AN OPTION TO " 840 PRINT" COMPUTE TORPEDO TRAJECTORY FOR YOU (OPTION 2)" 850 PRINT 860 PRINT"\SHE\ COMMAND = SHIELD CONTROL" 870 PRINT" DEFINES THE NUMBER OF ENERGY UNITS TO BE ASSIGNED TO THE" 880 PRINT" SHIELDS. ENERGY IS TAKEN FROM TOTAL SHIP'S ENERGY. NOTE" 890 PRINT" THAN THE STATUS DISPLAY TOTAL ENERGY INCLUDES SHIELD ENERGY" 900 PRINT 910 PRINT"\DAM\ COMMAND = DAMMAGE CONTROL REPORT" 920 PRINT" GIVES THE STATE OF REPAIR OF ALL DEVICES. WHERE A NEGATIVE" 930 PRINT" 'STATE OF REPAIR' SHOWS THAT THE DEVICE IS TEMPORARILY" 940 PRINT" DAMAGED." 950 PRINT 960 PRINT"\COM\ COMMAND = LIBRARY-COMPUTER" 970 PRINT" THE LIBRARY-COMPUTER CONTAINS SIX OPTIONS:" 980 PRINT" OPTION 0 = CUMULATIVE GALACTIC RECORD" 990 PRINT" THIS OPTION SHOWES COMPUTER MEMORY OF THE RESULTS OF ALL" 1000 PRINT" PREVIOUS SHORT AND LONG RANGE SENSOR SCANS" 1010 PRINT" OPTION 1 = STATUS REPORT" 1020 PRINT" THIS OPTION SHOWS THE NUMBER OF KLINGONS, STARDATES," 1030 PRINT" AND STARBASES REMAINING IN THE GAME." 1040 PRINT" OPTION 2 = PHOTON TORPEDO DATA" 1050 PRINT" WHICH GIVES DIRECTIONS AND DISTANCE FROM THE ENTERPRISE" 1060 PRINT" TO ALL KLINGONS IN YOUR QUADRANT" 1070 PRINT" OPTION 3 = STARBASE NAV DATA" 1080 PRINT" THIS OPTION GIVES DIRECTION AND DISTANCE TO ANY " 1090 PRINT" STARBASE WITHIN YOUR QUADRANT" 1100 PRINT" OPTION 4 = DIRECTION/DISTANCE CALCULATOR" 1110 PRINT" THIS OPTION ALLOWS YOU TO ENTER COORDINATES FOR" 1120 PRINT" DIRECTION/DISTANCE CALCULATIONS" 1130 PRINT" OPTION 5 = GALACTIC /REGION NAME/ MAP" 1140 PRINT" THIS OPTION PRINTS THE NAMES OF THE SIXTEEN MAJOR " 1150 PRINT" GALACTIC REGIONS REFERRED TO IN THE GAME." 1990 REM POKE 1229,0:POKE 1237,1:NULL 0 2000 REM PRINT:PRINT:PRINT 2010 REM PRINT "TURN CASSETTE PLAYER ON AND HIT ANY KEY EXCEPT RETURN" 2020 REM IF INP(1)=13 THEN 2020 2030 REM PRINT 2040 REM PRINT "TURN CASSETTE PLAYER OFF AND " 2050 REM PRINT "TYPE 'RUN' WHEN COMPUTER PRINTS 'OK'" ================================================ FILE: 84_Super_Star_Trek/vbnet/README.md ================================================ Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET) ================================================ FILE: 84_Super_Star_Trek/vbnet/SuperStarTrek.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30114.105 MinimumVisualStudioVersion = 10.0.40219.1 Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "SuperStarTrek", "SuperStarTrek.vbproj", "{92825C31-5966-4E6A-9CB0-5AEEE3D6A2A4}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {92825C31-5966-4E6A-9CB0-5AEEE3D6A2A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {92825C31-5966-4E6A-9CB0-5AEEE3D6A2A4}.Debug|Any CPU.Build.0 = Debug|Any CPU {92825C31-5966-4E6A-9CB0-5AEEE3D6A2A4}.Release|Any CPU.ActiveCfg = Release|Any CPU {92825C31-5966-4E6A-9CB0-5AEEE3D6A2A4}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal ================================================ FILE: 84_Super_Star_Trek/vbnet/SuperStarTrek.vbproj ================================================ Exe SuperStarTrek net6.0 16.9 ================================================ FILE: 85_Synonym/README.md ================================================ ### Synonym A synonym of a word is another word (in the English language) which has the same, or very nearly the same, meaning. This program tests your knowledge of synonyms of a few common words. The computer chooses a word and asks you for a synonym. The computer then tells you whether you’re right or wrong. If you can’t think of a synonym, type “HELP” which causes a synonym to be printed. You may put in words of your choice in the data statements. The number following DATA in Statement 500 is the total number of data statements. In each data statement, the first number is the number of words in that statement. Can you think of a way to make this into a more general kind of CAI program for any subject? Walt Koetke of Lexington High School, Massachusetts created this program. --- As published in Basic Computer Games (1978): - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=164) - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=179) Downloaded from Vintage Basic at http://www.vintage-basic.net/games.html #### Porting Notes - Each time the player asks for HELP, one of the synonyms is shown and discarded. There is no protection against the player using up all of the help. - The player can ask for HELP and then submit that answer. Is it meant to be a clue, or just giving a correct answer to the player? ================================================ FILE: 85_Synonym/csharp/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/) ================================================ FILE: 85_Synonym/csharp/Synonym.cs ================================================ using System.Text; namespace Synonym { class Synonym { Random rand = new Random(); // Initialize list of corrent responses private string[] Affirmations = { "Right", "Correct", "Fine", "Good!", "Check" }; // Initialize list of words and their synonyms private string[][] Words = { new string[] {"first", "start", "beginning", "onset", "initial"}, new string[] {"similar", "alike", "same", "like", "resembling"}, new string[] {"model", "pattern", "prototype", "standard", "criterion"}, new string[] {"small", "insignificant", "little", "tiny", "minute"}, new string[] {"stop", "halt", "stay", "arrest", "check", "standstill"}, new string[] {"house", "dwelling", "residence", "domicile", "lodging", "habitation"}, new string[] {"pit", "hole", "hollow", "well", "gulf", "chasm", "abyss"}, new string[] {"push", "shove", "thrust", "prod", "poke", "butt", "press"}, new string[] {"red", "rouge", "scarlet", "crimson", "flame", "ruby"}, new string[] {"pain", "suffering", "hurt", "misery", "distress", "ache", "discomfort"} }; private void DisplayIntro() { Console.WriteLine(""); Console.WriteLine("SYNONYM".PadLeft(23)); Console.WriteLine("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); Console.WriteLine(""); Console.WriteLine("A synonym of a word means another word in the English"); Console.WriteLine("language which has the same or very nearly the same meaning."); Console.WriteLine("I choose a word -- you type a synonym."); Console.WriteLine("If you can't think of a synonym, type the word 'help'"); Console.WriteLine("and I will tell you a synonym."); Console.WriteLine(""); } private void DisplayOutro() { Console.WriteLine("Synonym drill completed."); } private void RandomizeTheList() { // Randomize the list of Words to pick from int[] Order = new int[Words.Length]; foreach (int i in Order) { Order[i] = rand.Next(); } Array.Sort(Order, Words); } private string GetAnAffirmation() { return Affirmations[rand.Next(Affirmations.Length)]; } private bool CheckTheResponse(string WordName, int WordIndex, string LineInput, string[] WordList) { if (LineInput.Equals("help")) { // Choose a random correct synonym response that doesn't equal the current word given int HelpIndex = rand.Next(WordList.Length); while (HelpIndex == WordIndex) { HelpIndex = rand.Next(0, WordList.Length); } Console.WriteLine("**** A synonym of {0} is {1}.", WordName, WordList[HelpIndex]); return false; } else { // Check to see if the response is one of the listed synonyms and not the current word prompt if (WordList.Contains(LineInput) && LineInput != WordName) { // Randomly display one of the five correct answer exclamations Console.WriteLine(GetAnAffirmation()); return true; } else { // Incorrect response. Try again. Console.WriteLine(" Try again.".PadLeft(5)); return false; } } } private string PromptForSynonym(string WordName) { Console.Write(" What is a synonym of {0}? ", WordName); string LineInput = Console.ReadLine().Trim().ToLower(); return LineInput; } private void AskForSynonyms() { Random rand = new Random(); // Loop through the now randomized list of Words and display a random word from each to prompt for a synonym foreach (string[] WordList in Words) { int WordIndex = rand.Next(WordList.Length); // random word position in the current list of words string WordName = WordList[WordIndex]; // what is that actual word bool Success = false; while (!Success) { // Ask for the synonym of the current word string LineInput = PromptForSynonym(WordName); // Check the response Success = CheckTheResponse(WordName, WordIndex, LineInput, WordList); // Add extra line space for formatting Console.WriteLine(""); } } } public void PlayTheGame() { RandomizeTheList(); DisplayIntro(); AskForSynonyms(); DisplayOutro(); } } class Program { static void Main(string[] args) { new Synonym().PlayTheGame(); } } } ================================================ FILE: 85_Synonym/csharp/Synonym.csproj ================================================  Exe net6.0 10 enable enable Synonym.Program ================================================ FILE: 85_Synonym/csharp/Synonym.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30114.105 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Synonym", "Synonym.csproj", "{9CCC22AC-EDF3-4137-8EF7-EBB2F8677CE4}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {9CCC22AC-EDF3-4137-8EF7-EBB2F8677CE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9CCC22AC-EDF3-4137-8EF7-EBB2F8677CE4}.Debug|Any CPU.Build.0 = Debug|Any CPU {9CCC22AC-EDF3-4137-8EF7-EBB2F8677CE4}.Release|Any CPU.ActiveCfg = Release|Any CPU {9CCC22AC-EDF3-4137-8EF7-EBB2F8677CE4}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal ================================================ FILE: 85_Synonym/java/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Oracle Java](https://openjdk.java.net/) ================================================ FILE: 85_Synonym/java/src/Synonym.java ================================================ import java.util.ArrayList; import java.util.Arrays; import java.util.Scanner; /** * Game of Synonym *

    * Based on the Basic game of Synonym here * https://github.com/coding-horror/basic-computer-games/blob/main/85%20Synonym/synonym.bas *

    * Note: The idea was to create a version of the 1970's Basic game in Java, without introducing * new features - no additional text, error checking, etc has been added. */ public class Synonym { public static final String[] RANDOM_ANSWERS = {"RIGHT", "CORRECT", "FINE", "GOOD!", "CHECK"}; // Used for keyboard input private final Scanner kbScanner; // List of words and synonyms private final ArrayList synonyms; private enum GAME_STATE { INIT, PLAY, GAME_OVER } // Current game state private GAME_STATE gameState; private int currentQuestion; public Synonym() { kbScanner = new Scanner(System.in); synonyms = new ArrayList<>(); gameState = GAME_STATE.INIT; } /** * Main game loop */ public void play() { do { switch (gameState) { case INIT: intro(); currentQuestion = 0; // Load data synonyms.add(new SynonymList("FIRST", new String[]{"START", "BEGINNING", "ONSET", "INITIAL"})); synonyms.add(new SynonymList("SIMILAR", new String[]{"SAME", "LIKE", "RESEMBLING"})); synonyms.add(new SynonymList("MODEL", new String[]{"PATTERN", "PROTOTYPE", "STANDARD", "CRITERION"})); synonyms.add(new SynonymList("SMALL", new String[]{"INSIGNIFICANT", "LITTLE", "TINY", "MINUTE"})); synonyms.add(new SynonymList("STOP", new String[]{"HALT", "STAY", "ARREST", "CHECK", "STANDSTILL"})); synonyms.add(new SynonymList("HOUSE", new String[]{"DWELLING", "RESIDENCE", "DOMICILE", "LODGING", "HABITATION"})); synonyms.add(new SynonymList("PIT", new String[]{"HOLE", "HOLLOW", "WELL", "GULF", "CHASM", "ABYSS"})); synonyms.add(new SynonymList("PUSH", new String[]{"SHOVE", "THRUST", "PROD", "POKE", "BUTT", "PRESS"})); synonyms.add(new SynonymList("RED", new String[]{"ROUGE", "SCARLET", "CRIMSON", "FLAME", "RUBY"})); synonyms.add(new SynonymList("PAIN", new String[]{"SUFFERING", "HURT", "MISERY", "DISTRESS", "ACHE", "DISCOMFORT"})); gameState = GAME_STATE.PLAY; break; case PLAY: // Get the word and synonyms to ask a question about SynonymList synonym = synonyms.get(currentQuestion); String getAnswer = displayTextAndGetInput(" WHAT IS A SYNONYM OF " + synonym.getWord() + " ? "); // HELP is used to give a random synonym for the current word if (getAnswer.equals("HELP")) { int randomSynonym = (int) (Math.random() * synonym.size()); System.out.println("**** A SYNONYM OF " + synonym.getWord() + " IS " + synonym.getSynonyms()[randomSynonym] + "."); } else { // Check if the entered word is in the synonym list if (synonym.exists(getAnswer)) { // If it is, give a random "correct" response System.out.println(RANDOM_ANSWERS[(int) (Math.random() * RANDOM_ANSWERS.length)]); currentQuestion++; // Have we reached the final word/synonyms on file? if (currentQuestion == synonyms.size()) { // We have so end game. System.out.println("SYNONYM DRILL COMPLETED."); gameState = GAME_STATE.GAME_OVER; } } else { // Word does not exist in the synonym list System.out.println("TRY AGAIN."); } } } } while (gameState != GAME_STATE.GAME_OVER); } private void intro() { System.out.println(simulateTabs(33) + "SYNONYM"); System.out.println(simulateTabs(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println(); System.out.println("A SYNONYM OF A WORD MEANS ANOTHER WORD IN THE ENGLISH"); System.out.println("LANGUAGE WHICH HAS THE SAME OR VERY NEARLY THE SAME"); System.out.println(" MEANING."); System.out.println("I CHOOSE A WORD -- YOU TYPE A SYNONYM."); System.out.println("IF YOU CAN'T THINK OF A SYNONYM, TYPE THE WORD 'HELP'"); System.out.println("AND I WILL TELL YOU A SYNONYM."); System.out.println(); } /* * Print a message on the screen, then accept input from Keyboard. * Converts input to uppercase. * * @param text message to be displayed on screen. * @return what was typed by the player. */ private String displayTextAndGetInput(String text) { System.out.print(text); return kbScanner.next().toUpperCase(); } /** * Simulate the old basic tab(xx) command which indented text by xx spaces. * * @param spaces number of spaces required * @return String with number of spaces */ private String simulateTabs(int spaces) { char[] spacesTemp = new char[spaces]; Arrays.fill(spacesTemp, ' '); return new String(spacesTemp); } } ================================================ FILE: 85_Synonym/java/src/SynonymGame.java ================================================ public class SynonymGame { public static void main(String[] args) { Synonym synonym = new Synonym(); synonym.play(); } } ================================================ FILE: 85_Synonym/java/src/SynonymList.java ================================================ import java.util.ArrayList; import java.util.Arrays; /** * Stores a word and a list of synonyms for that word */ public class SynonymList { private final String word; private final ArrayList synonyms; public SynonymList(String word, String[] synonyms) { this.word = word; this.synonyms = new ArrayList<>(Arrays.asList(synonyms)); } /** * Check if the word passed to this method exists in the list of synonyms * N.B. Case insensitive * * @param word word to search for * @return true if found, otherwise false */ public boolean exists(String word) { return synonyms.stream().anyMatch(str -> str.equalsIgnoreCase(word)); } public String getWord() { return word; } public int size() { return synonyms.size(); } /** * Returns all synonyms for this word in string array format * * @return */ public String[] getSynonyms() { // Parameter to toArray method determines type of the resultant array return synonyms.toArray(new String[0]); } } ================================================ FILE: 85_Synonym/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 85_Synonym/javascript/synonym.html ================================================ SYNONYM

    
    
    
    
    
    
    ================================================
    FILE: 85_Synonym/javascript/synonym.js
    ================================================
    // SYNONYM
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    var ra = [, "RIGHT", "CORRECT", "FINE", "GOOD!", "CHECK"];
    var la = [];
    var tried = [];
    
    var synonym = [[5,"FIRST","START","BEGINNING","ONSET","INITIAL"],
                   [5,"SIMILAR","ALIKE","SAME","LIKE","RESEMBLING"],
                   [5,"MODEL","PATTERN","PROTOTYPE","STANDARD","CRITERION"],
                   [5,"SMALL","INSIGNIFICANT","LITTLE","TINY","MINUTE"],
                   [6,"STOP","HALT","STAY","ARREST","CHECK","STANDSTILL"],
                   [6,"HOUSE","DWELLING","RESIDENCE","DOMICILE","LODGING","HABITATION"],
                   [7,"PIT","HOLE","HOLLOW","WELL","GULF","CHASM","ABYSS"],
                   [7,"PUSH","SHOVE","THRUST","PROD","POKE","BUTT","PRESS"],
                   [6,"RED","ROUGE","SCARLET","CRIMSON","FLAME","RUBY"],
                   [7,"PAIN","SUFFERING","HURT","MISERY","DISTRESS","ACHE","DISCOMFORT"]
                   ];
    
    // Main program
    async function main()
    {
        print(tab(33) + "SYNONYM\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        for (c = 0; c <= synonym.length; c++)
            tried[c] = false;
        print("A SYNONYM OF A WORD MEANS ANOTHER WORD IN THE ENGLISH\n");
        print("LANGUAGE WHICH HAS THE SAME OR VERY NEARLY THE SAME");
        print(" MEANING.\n");
        print("I CHOOSE A WORD -- YOU TYPE A SYNONYM.\n");
        print("IF YOU CAN'T THINK OF A SYNONYM, TYPE THE WORD 'HELP'\n");
        print("AND I WILL TELL YOU A SYNONYM.\n");
        print("\n");
        c = 0;
        while (c < synonym.length) {
            c++;
            do {
                n1 = Math.floor(Math.random() * synonym.length + 1);
            } while (tried[n1]) ;
            tried[n1] = true;
            n2 = synonym[n1][0];    // Length of synonym list
            // This array keeps a list of words not shown
            for (j = 1; j <= n2; j++)
                la[j] = j;
            la[0] = n2;
            g = 1;  // Always show first word
            print("\n");
            la[g] = la[la[0]];  // Replace first word with last word
            la[0] = n2 - 1; // Reduce size of list by one.
            print("\n");
            while (1) {
                print("     WHAT IS A SYNONYM OF " + synonym[n1][g]);
                str = await input();
                if (str == "HELP") {
                    g1 = Math.floor(Math.random() * la[0] + 1);
                    print("**** A SYNONYM OF " + synonym[n1][g] + " IS " + synonym[n1][la[g1]] + ".\n");
                    print("\n");
                    la[g1] = la[la[0]];
                    la[0]--;
                    continue;
                }
                for (k = 1; k <= n2; k++) {
                    if (g == k)
                        continue;
                    if (str == synonym[n1][k])
                        break;
                }
                if (k > n2) {
                    print("     TRY AGAIN.\n");
                } else {
                    print(synonym[n1][Math.floor(Math.random() * 5 + 1)] + "\n");
                    break;
                }
            }
        }
        print("\n");
        print("SYNONYM DRILL COMPLETED.\n");
    }
    
    main();
    
    
    ================================================
    FILE: 85_Synonym/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 85_Synonym/kotlin/Synonym.kt
    ================================================
    /**
     * Game of Synonym
     *
     *
     * Based on the Basic game of Synonym here
     * https://github.com/coding-horror/basic-computer-games/blob/main/85%20Synonym/synonym.bas
     *
     *
     * Note:  The idea was to create a version of the 1970's Basic game in Java, without introducing
     * new features - no additional text, error checking, etc has been added.
     */
    
    fun main() {
        println(introText)
        synonyms.forEach {
            it.testUser()
        }
        println("SYNONYM DRILL COMPLETED.")
    }
    // We could put this inside of SynonymList, but this keeps the core implementation
    // right here at the top
    private fun SynonymList.testUser() {
        do {
            val answer = ask("     WHAT IS A SYNONYM OF $word ? ")
            when {
                answer == "HELP" ->
                    println("""**** A SYNONYM OF $word IS ${synonyms.random()}.""")
                synonyms.contains(answer) ->
                    println(AFFIRMATIONS.random())
                else ->
                    println("TRY AGAIN.")
            }
        } while (!synonyms.contains(answer))
    }
    
    val introText = """
    ${tab(33)}SYNONYM
    ${tab(15)}CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY
    A SYNONYM OF A WORD MEANS ANOTHER WORD IN THE ENGLISH
    LANGUAGE WHICH HAS THE SAME OR VERY NEARLY THE SAME
     MEANING.
    I CHOOSE A WORD -- YOU TYPE A SYNONYM.
    IF YOU CAN'T THINK OF A SYNONYM, TYPE THE WORD 'HELP'
    AND I WILL TELL YOU A SYNONYM.
    
        """
    
    // prints a question and reads a string (and converts to uppercase)
    private fun ask(text: String): String {
        print(text)
        return readln().uppercase()
    }
    
    // Just like TAB in BASIC
    private fun tab(spaces: Int): String = " ".repeat(spaces)
    
    val AFFIRMATIONS = arrayOf("RIGHT", "CORRECT", "FINE", "GOOD!", "CHECK")
    
    // List of words and synonyms
    private val synonyms = listOf(
        SynonymList("FIRST", listOf("START", "BEGINNING", "ONSET", "INITIAL")),
        SynonymList("SIMILAR", listOf("SAME", "LIKE", "RESEMBLING")),
        SynonymList("MODEL", listOf("PATTERN", "PROTOTYPE", "STANDARD", "CRITERION")),
        SynonymList("SMALL", listOf("INSIGNIFICANT", "LITTLE", "TINY", "MINUTE")),
        SynonymList("STOP", listOf("HALT", "STAY", "ARREST", "CHECK", "STANDSTILL")),
        SynonymList("HOUSE", listOf("DWELLING", "RESIDENCE", "DOMICILE", "LODGING", "HABITATION")),
        SynonymList("PIT", listOf("HOLE", "HOLLOW", "WELL", "GULF", "CHASM", "ABYSS")),
        SynonymList("PUSH", listOf("SHOVE", "THRUST", "PROD", "POKE", "BUTT", "PRESS")),
        SynonymList("RED", listOf("ROUGE", "SCARLET", "CRIMSON", "FLAME", "RUBY")),
        SynonymList("PAIN", listOf("SUFFERING", "HURT", "MISERY", "DISTRESS", "ACHE", "DISCOMFORT"))
    )
    
    class SynonymList(val word: String, val synonyms: List)
    
    
    ================================================
    FILE: 85_Synonym/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 85_Synonym/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    I used List::Util to do all the heavy work to show that perl can handle all the various
    array functions.  It would be interesting to see a version that handled all of this
    manually as there ended up being very little code left in this program.
    
    
    ================================================
    FILE: 85_Synonym/perl/synonym.pl
    ================================================
    #!/usr/bin/perl
    
    use v5.32; # for sample from List::Util, also includes 'use strict'
    use warnings; # always a good idea
    
    use List::Util qw/ any sample shuffle /; # Rather than write our own utilities, use the built in ones
    
    my @correct = qw/ Right Correct Fine Good! Check /;
    
    # lowercase all words here
    my @synonyms = (
      [ qw/ first start beginning onset initial / ],
      [ qw/ similar alike same like resembling / ],
      [ qw/ model pattern prototype standard criterion /],
      [ qw/ small insignificant little tiny minute /],
      [ qw/ stop halt stay arrest check standstill /],
      [ qw/ house dwelling residense domicile lodging habitation /],
      [ qw/ pit hole hollow well gulf chasm abyss /],
      [ qw/ push shove thrust prod poke butt press /],
      [ qw/ red rouge scarlet crimson flame ruby /],
      [ qw/ pain suffering hurt misery distress ache discomfort /],
    );
    
    print <<__END_OF_INTRO;
                                     Synonym
                    Creative Computing  Morristown, New Jersey
    
    
    
    A synonym of a word means another word in the English
    language which has the same or very nearly the same meaning
    I choose a word -- you type a synonym.
    If you can't think of a synonym, type the word 'HELP'
    and I will tell you a synonym.
    
    __END_OF_INTRO
    
    foreach my $drill ( shuffle @synonyms ) {
      my $word = $drill->[0];
      my @answers = $drill->@[1 .. $drill->$#*];
      print "     What is a synonym of $word? ";
      my $response = <>;
      chomp $response;
      $response = lc $response;
    
      if ( $response eq 'help' ) {
        say "**** A synonym of $word is ", sample(1, @answers);
        redo;
      } elsif ( not any { $response eq $_ } @answers ) {
        say '     Try again.';
        redo;
      } else {
        say sample 1, @correct;
      }
    }
    
    say "\nSynonym drill completed.";
    
    
    ================================================
    FILE: 85_Synonym/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 85_Synonym/python/synonym.py
    ================================================
    """
    SYNONYM
    
    Vocabulary quiz
    
    Ported by Dave LeCompte
    """
    
    import random
    
    PAGE_WIDTH = 64
    
    
    def print_centered(msg: str) -> None:
        spaces = " " * ((PAGE_WIDTH - len(msg)) // 2)
        print(spaces + msg)
    
    
    def print_header(title: str) -> None:
        print_centered(title)
        print_centered("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY")
        print()
        print()
        print()
    
    
    def print_instructions() -> None:
        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 OF A SYNONYM, TYPE THE WORD 'HELP'")
        print("AND I WILL TELL YOU A SYNONYM.")
        print()
    
    
    right_words = ["RIGHT", "CORRECT", "FINE", "GOOD!", "CHECK"]
    
    synonym_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"],
    ]
    
    
    def print_right() -> None:
        print(random.choice(right_words))
    
    
    def ask_question(question_number: int) -> None:
        words = synonym_words[question_number]
        clues = words[:]
        base_word = clues.pop(0)
    
        while True:
            question = f"     WHAT IS A SYNONYM OF {base_word}? "
            response = input(question).upper()
    
            if response == "HELP":
                clue = random.choice(clues)
                print(f"**** A SYNONYM OF {base_word} IS {clue}.")
                print()
    
                # remove the clue from available clues
                clues.remove(clue)
                continue
    
            if (response != base_word) and (response in words):
                print_right()
                return
    
    
    def finish() -> None:
        print()
        print("SYNONYM DRILL COMPLETED.")
    
    
    def main() -> None:
        print_header("SYNONYM")
        print_instructions()
    
        num_questions = len(synonym_words)
        word_indices = list(range(num_questions))
        random.shuffle(word_indices)
    
        for word_number in word_indices:
            ask_question(word_number)
    
        finish()
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 85_Synonym/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 85_Synonym/ruby/synonim.rb
    ================================================
    ########################################################
    #
    # Synonym
    #
    # From Basic Computer Games (1978)
    #
    # A synonym of a word is another word (in the English language) which has the same,
    # or very nearly the same, meaning. This program tests your knowledge of synonyms
    # of a few common words.
    #
    # The computer chooses a word and asks you for a synonym. The computer then tells
    # you whether you’re right or wrong. If you can’t think of a synonym, type “HELP”
    # which causes a synonym to be printed.
    # You may put in words of your choice in the data statements.
    # The number following DATA in Statement 500 is the total number of data statements.
    # In each data statement, the first number is the number of words in that statement.
    #
    # Can you think of a way to make this into a more general kind of CAI program for any subject?
    # Walt Koetke of Lexington High School, Massachusetts created this program.
    #
    #
    ########################################################
    
    puts <<~INSTRUCTIONS
                                     SYNONYM
                     CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY
    
    A SYNONYM OF A WORD MEANS ANOTHER WORD IN THE ENGLISH
    LANGUAGE WHICH HAS THE SAME OR VERY NEARLY THE SAME MEANING.
    I CHOOSE A WORD -- YOU TYPE A SYNONYM.
    IF YOU CAN'T THINK OF A SYNONYM, TYPE THE WORD 'HELP'
    AND I WILL TELL YOU A SYNONYM.
    
    
    INSTRUCTIONS
    
    right_words = ["RIGHT", "CORRECT", "FINE", "GOOD!", "CHECK"]
    
    synonym_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"],
    ]
    
    synonym_words.shuffle.each {|words_ar|
    
    }
    
    
    synonym_words.each {|words_ar|
        answer = false
        keyword = words_ar.shift
    
        while not answer and words_ar.length != 0
            puts "     WHAT IS A SYNONYM OF #{keyword}? "
            inp = gets.chomp.upcase
    
            if inp  == "HELP"
                clue = words_ar.sample
                puts "**** A SYNONYM OF #{keyword} IS #{clue}."
                words_ar.delete(clue)
            elsif words_ar.include? inp
                puts right_words.sample
                answer = true
            else
                puts "TRY AGAIN."
            end
    
        end
    
    }
    
    puts "SYNONYM DRILL COMPLETED"
    
    
    ######################################################################
    #
    # Porting notes
    #
    #  There is a bug in the original program where if you keep asking for
    # synoyms of a given word it ends up running out of synonyms
    # in the array and the program crashes.
    #  The bug has been fixed in this version and now when
    # it runs out of words it continues with the next
    # array.
    #
    ######################################################################
    
    
    ================================================
    FILE: 85_Synonym/rust/Cargo.toml
    ================================================
    [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"
    
    
    ================================================
    FILE: 85_Synonym/rust/README.md
    ================================================
    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)
    
    
    ================================================
    FILE: 85_Synonym/rust/src/main.rs
    ================================================
    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
    ////////////////////////////////////////////////////////////
    
    
    ================================================
    FILE: 85_Synonym/synonym.bas
    ================================================
    2 PRINT TAB(33);"SYNONYM"
    4 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    6 PRINT: PRINT: PRINT
    10 DIM R$(5),W$(10),L(30),R(30)
    20 R$(1)="RIGHT": R$(2)="CORRECT": R$(3)="FINE": R$(4)="GOOD!"
    30 R$(5)="CHECK"
    70 C=0
    90 PRINT "A SYNONYM OF A WORD MEANS ANOTHER WORD IN THE ENGLISH"
    100 PRINT "LANGUAGE WHICH HAS THE SAME OR VERY NEARLY THE SAME";
    110 PRINT " MEANING."
    130 PRINT "I CHOOSE A WORD -- YOU TYPE A SYNONYM."
    140 PRINT "IF YOU CAN'T THINK OF A SYNONYM, TYPE THE WORD 'HELP'"
    145 PRINT "AND I WILL TELL YOU A SYNONYM.": PRINT
    150 RESTORE: C=C+1: READ N
    160 IF C>N THEN 420
    170 N1=INT(RND(1)*N+1)
    174 IF R(N1)=1 THEN 170
    176 R(N1)=1
    180 FOR I=1 TO N1
    190 READ N2
    200 FOR J=1 TO N2
    210 READ W$(J)
    220 NEXT J
    230 NEXT I
    232 FOR J=1 TO N2: L(J)=J: NEXT J
    235 L(0)=N2: G=1: PRINT
    237 L(G)=L(L(0)): L(0)=N2-1: PRINT
    240 PRINT "     WHAT IS A SYNONYM OF ";W$(G);: INPUT A$
    250 IF A$="HELP" THEN 340
    260 FOR K=1 TO N2
    270 IF G=K THEN 290
    280 IF A$=W$(K) THEN 320
    290 NEXT K
    300 PRINT "     TRY AGAIN.": GOTO 240
    320 PRINT R$(INT(RND(1)*5+1)): GOTO 150
    340 G1=INT(RND(1)*L(0)+1)
    360 PRINT "**** A SYNONYM OF ";W$(G);" IS ";W$(L(G1));".": PRINT
    370 L(G1)=L(L(0)): L(0)=L(0)-1: GOTO 240
    420 PRINT: PRINT "SYNONYM DRILL COMPLETED.": GOTO 999
    500 DATA 10
    510 DATA 5,"FIRST","START","BEGINNING","ONSET","INITIAL"
    520 DATA 5,"SIMILAR","ALIKE","SAME","LIKE","RESEMBLING"
    530 DATA 5,"MODEL","PATTERN","PROTOTYPE","STANDARD","CRITERION"
    540 DATA 5,"SMALL","INSIGNIFICANT","LITTLE","TINY","MINUTE"
    550 DATA 6,"STOP","HALT","STAY","ARREST","CHECK","STANDSTILL"
    560 DATA 6,"HOUSE","DWELLING","RESIDENCE","DOMICILE","LODGING"
    565 DATA "HABITATION"
    570 DATA 7,"PIT","HOLE","HOLLOW","WELL","GULF","CHASM","ABYSS"
    580 DATA 7,"PUSH","SHOVE","THRUST","PROD","POKE","BUTT","PRESS"
    590 DATA 6,"RED","ROUGE","SCARLET","CRIMSON","FLAME","RUBY"
    600 DATA 7,"PAIN","SUFFERING","HURT","MISERY","DISTRESS","ACHE"
    605 DATA "DISCOMFORT"
    999 END
    
    
    ================================================
    FILE: 85_Synonym/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 85_Synonym/vbnet/Synonym.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Synonym", "Synonym.vbproj", "{E6BEB53F-F6A3-4AA9-8EB9-68D8238DFAA6}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{E6BEB53F-F6A3-4AA9-8EB9-68D8238DFAA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{E6BEB53F-F6A3-4AA9-8EB9-68D8238DFAA6}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{E6BEB53F-F6A3-4AA9-8EB9-68D8238DFAA6}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{E6BEB53F-F6A3-4AA9-8EB9-68D8238DFAA6}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 85_Synonym/vbnet/Synonym.vbproj
    ================================================
    
      
        Exe
        Synonym
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 86_Target/README.md
    ================================================
    ### Target
    
    In this program, you are firing a weapon from a spaceship in 3-dimensional space. Your ship, the Starship Enterprise, is located at the origin (0,0,0) of a set of x,y,z coordinates. You will be told the approximate location of the target in 3-dimensional rectangular coordinates, the approximate angular deviation from the x and z axes in both radians and degrees, and the approximate distance to the target.
    
    Given this information, you then proceed to shoot at the target. A shot within 20 kilometers of the target destroys it. After each shot, you are given information as to the position of the explosion of your shot and a somewhat improved estimate of the location of the target. Fortunately, this is just practice and the target doesn’t shoot back. After you have attained proficiency, you ought to be able to destroy a target in 3 or 4 shots. However, attaining proficiency might take a while!
    
    The author is H. David Crockett of Fort Worth, Texas.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=165)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=180)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 86_Target/csharp/Angle.cs
    ================================================
    namespace Target
    {
        internal class Angle
        {
            // Use same precision for constants as original code
            private const float PI = 3.14159f;
            private const float DegreesPerRadian = 57.296f;
    
            private readonly float _radians;
    
            private Angle(float radians) => _radians = radians;
    
            public static Angle InDegrees(float degrees) => new (degrees / DegreesPerRadian);
            public static Angle InRotations(float rotations) => new (2 * PI * rotations);
    
            public static implicit operator float(Angle angle) => angle._radians;
        }
    }
    
    
    ================================================
    FILE: 86_Target/csharp/Explosion.cs
    ================================================
    namespace Target
    {
        internal class Explosion
        {
            private readonly Point _position;
    
            public Explosion(Point position, Offset targetOffset)
            {
                _position = position;
                FromTarget = targetOffset;
                DistanceToTarget = targetOffset.Distance;
            }
    
            public Point Position => _position;
            public Offset FromTarget { get; }
            public float DistanceToTarget { get; }
            public string GetBearing() => _position.GetBearing();
    
            public bool IsHit => DistanceToTarget <= 20;
            public bool IsTooClose => _position.Distance < 20;
        }
    }
    
    
    ================================================
    FILE: 86_Target/csharp/FiringRange.cs
    ================================================
    using Games.Common.Randomness;
    
    namespace Target
    {
        internal class FiringRange
        {
            private readonly IRandom _random;
            private Point _targetPosition;
    
            public FiringRange(IRandom random)
            {
                _random = random;
            }
    
            public Point NextTarget() =>  _targetPosition = _random.NextPosition();
    
            public Explosion Fire(Angle angleFromX, Angle angleFromZ, float distance)
            {
                var explosionPosition = new Point(angleFromX, angleFromZ, distance);
                var targetOffset = explosionPosition - _targetPosition;
                return new (explosionPosition, targetOffset);
            }
        }
    }
    
    
    ================================================
    FILE: 86_Target/csharp/Game.cs
    ================================================
    using System;
    using Games.Common.IO;
    
    namespace Target
    {
        internal class Game
        {
            private readonly IReadWrite _io;
            private readonly FiringRange _firingRange;
            private int _shotCount;
    
            public Game(IReadWrite io, FiringRange firingRange)
            {
                _io = io;
                _firingRange = firingRange;
            }
    
            public void Play()
            {
                _shotCount = 0;
                var target = _firingRange.NextTarget();
                _io.WriteLine(target.GetBearing());
                _io.WriteLine($"Target sighted: approximate coordinates:  {target}");
    
                while (true)
                {
                    _io.WriteLine($"     Estimated distance: {target.EstimateDistance()}");
                    _io.WriteLine();
    
                    var explosion = Shoot();
    
                    if (explosion.IsTooClose)
                    {
                        _io.WriteLine("You blew yourself up!!");
                        return;
                    }
    
                    _io.WriteLine(explosion.GetBearing());
    
                    if (explosion.IsHit)
                    {
                        ReportHit(explosion.DistanceToTarget);
                        return;
                    }
    
                    ReportMiss(explosion);
                }
            }
    
            private Explosion Shoot()
            {
                var (xDeviation, zDeviation, distance) = _io.Read3Numbers(
                    "Input angle deviation from X, angle deviation from Z, distance");
                _shotCount++;
                _io.WriteLine();
    
                return _firingRange.Fire(Angle.InDegrees(xDeviation), Angle.InDegrees(zDeviation), distance);
            }
    
            private void ReportHit(float distance)
            {
                _io.WriteLine();
                _io.WriteLine($" * * * HIT * * *   Target is non-functional");
                _io.WriteLine();
                _io.WriteLine($"Distance of explosion from target was {distance} kilometers.");
                _io.WriteLine();
                _io.WriteLine($"Mission accomplished in {_shotCount} shots.");
            }
    
            private void ReportMiss(Explosion explosion)
            {
                ReportMiss(explosion.FromTarget);
                _io.WriteLine($"Approx position of explosion:  {explosion.Position}");
                _io.WriteLine($"     Distance from target = {explosion.DistanceToTarget}");
                _io.WriteLine();
                _io.WriteLine();
                _io.WriteLine();
            }
    
            private void ReportMiss(Offset targetOffset)
            {
                ReportMiss(targetOffset.DeltaX, "in front of", "behind");
                ReportMiss(targetOffset.DeltaY, "to left of", "to right of");
                ReportMiss(targetOffset.DeltaZ, "above", "below");
            }
    
            private void ReportMiss(float delta, string positiveText, string negativeText) =>
                _io.WriteLine(delta >= 0 ? GetOffsetText(positiveText, delta) : GetOffsetText(negativeText, -delta));
    
            private static string GetOffsetText(string text, float distance) => $"Shot {text} target {distance} kilometers.";
        }
    }
    
    
    ================================================
    FILE: 86_Target/csharp/Offset.cs
    ================================================
    using System;
    
    namespace Target
    {
        internal class Offset
        {
            public Offset(float deltaX, float deltaY, float deltaZ)
            {
                DeltaX = deltaX;
                DeltaY = deltaY;
                DeltaZ = deltaZ;
    
                Distance = (float)Math.Sqrt(deltaX * deltaX + deltaY * deltaY + deltaZ + deltaZ);
            }
    
            public float DeltaX { get; }
            public float DeltaY { get; }
            public float DeltaZ { get; }
            public float Distance { get; }
        }
    }
    
    
    ================================================
    FILE: 86_Target/csharp/Point.cs
    ================================================
    using System;
    
    namespace Target
    {
        internal class Point
        {
            private readonly float _angleFromX;
            private readonly float _angleFromZ;
    
            private readonly float _x;
            private readonly float _y;
            private readonly float _z;
    
            private int _estimateCount;
    
            public Point(Angle angleFromX, Angle angleFromZ, float distance)
            {
                _angleFromX = angleFromX;
                _angleFromZ = angleFromZ;
                Distance = distance;
    
                _x = distance * (float)Math.Sin(_angleFromZ) * (float)Math.Cos(_angleFromX);
                _y = distance * (float)Math.Sin(_angleFromZ) * (float)Math.Sin(_angleFromX);
                _z = distance * (float)Math.Cos(_angleFromZ);
            }
    
            public float Distance { get; }
    
            public float EstimateDistance() =>
                ++_estimateCount switch
                {
                    1 => EstimateDistance(20),
                    2 => EstimateDistance(10),
                    3 => EstimateDistance(5),
                    4 => EstimateDistance(1),
                    _ => Distance
                };
    
            public float EstimateDistance(int precision) => (float)Math.Floor(Distance / precision) * precision;
    
            public string GetBearing() => $"Radians from X axis = {_angleFromX}   from Z axis = {_angleFromZ}";
    
            public override string ToString() => $"X= {_x}   Y = {_y}   Z= {_z}";
    
            public static Offset operator -(Point p1, Point p2) => new (p1._x - p2._x, p1._y - p2._y, p1._z - p2._z);
        }
    }
    
    
    ================================================
    FILE: 86_Target/csharp/Program.cs
    ================================================
    using System;
    using System.Reflection;
    using Games.Common.IO;
    using Games.Common.Randomness;
    
    namespace Target
    {
        class Program
        {
            static void Main()
            {
                var io = new ConsoleIO();
                var game = new Game(io, new FiringRange(new RandomNumberGenerator()));
    
                Play(game, io, () => true);
            }
    
            public static void Play(Game game, TextIO io, Func playAgain)
            {
                DisplayTitleAndInstructions(io);
    
                while (playAgain())
                {
                    game.Play();
    
                    io.WriteLine();
                    io.WriteLine();
                    io.WriteLine();
                    io.WriteLine();
                    io.WriteLine();
                    io.WriteLine("Next target...");
                    io.WriteLine();
                }
            }
    
            private static void DisplayTitleAndInstructions(TextIO io)
            {
                using var stream = Assembly.GetExecutingAssembly()
                    .GetManifestResourceStream("Target.Strings.TitleAndInstructions.txt");
                io.Write(stream);
            }
        }
    }
    
    
    ================================================
    FILE: 86_Target/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 86_Target/csharp/RandomExtensions.cs
    ================================================
    using Games.Common.Randomness;
    
    namespace Target
    {
        internal static class RandomExtensions
        {
            public static Point NextPosition(this IRandom rnd) => new (
                Angle.InRotations(rnd.NextFloat()),
                Angle.InRotations(rnd.NextFloat()),
                100000 * rnd.NextFloat() + rnd.NextFloat());
        }
    }
    
    
    ================================================
    FILE: 86_Target/csharp/Strings/TitleAndInstructions.txt
    ================================================
                                     Target
                   Creative Computing  Morristown, New Jersey
    
    
    
    You are the weapons officer on the Starship Enterprise
    and this is a test to see how accurate a shot you
    are in a three-dimensional range.  You will be told
    the radian offset for the X and Z axes, the location
    of the target in three dimensional rectangular coordinates,
    the approximate number of degrees from the X and Z
    axes, and the approximate distance to the target.
    You will then proceed to shoot at the target until it is
    destroyed!
    
    Good luck!!
    
    
    ================================================
    FILE: 86_Target/csharp/Target.csproj
    ================================================
    
    
      
        Exe
        net6.0
      
    
      
        
      
    
      
        
      
    
    
    
    
    ================================================
    FILE: 86_Target/csharp/Target.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 17
    VisualStudioVersion = 17.0.32014.148
    MinimumVisualStudioVersion = 15.0.26124.0
    Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Target", "Target.csproj", "{DF03CFDB-2857-4416-A07A-80D84874F46E}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{DF03CFDB-2857-4416-A07A-80D84874F46E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{DF03CFDB-2857-4416-A07A-80D84874F46E}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{DF03CFDB-2857-4416-A07A-80D84874F46E}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{DF03CFDB-2857-4416-A07A-80D84874F46E}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {B49DF932-4DF9-44C3-B63F-FD9443B4DDD2}
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 86_Target/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 86_Target/java/Target.java
    ================================================
    import java.util.Scanner;
    
    /**
     * TARGET
     * 

    * Converted from BASIC to Java by Aldrin Misquitta (@aldrinm) */ public class Target { private static final double RADIAN = 180 / Math.PI; public static void main(String[] args) { Scanner scan = new Scanner(System.in); printIntro(); //continue till the user aborts while (true) { int numberShots = 0; final double xAxisInRadians = Math.random() * 2 * Math.PI; final double yAxisInRadians = Math.random() * 2 * Math.PI; System.out.printf("RADIANS FROM X AXIS = %.7f FROM Z AXIS = %.7f\n", xAxisInRadians, yAxisInRadians); final double p1 = 100000 * Math.random() + Math.random(); final double x = Math.sin(yAxisInRadians) * Math.cos(xAxisInRadians) * p1; final double y = Math.sin(yAxisInRadians) * Math.sin(xAxisInRadians) * p1; final double z = Math.cos(yAxisInRadians) * p1; System.out.printf("TARGET SIGHTED: APPROXIMATE COORDINATES: X=%.3f Y=%.3f Z=%.3f\n", x, y, z); boolean targetOrSelfDestroyed = false; while (!targetOrSelfDestroyed) { numberShots++; int estimatedDistance = 0; switch (numberShots) { case 1: estimatedDistance = (int) (p1 * .05) * 20; break; case 2: estimatedDistance = (int) (p1 * .1) * 10; break; case 3: estimatedDistance = (int) (p1 * .5) * 2; break; case 4: case 5: estimatedDistance = (int) (p1); break; } System.out.printf(" ESTIMATED DISTANCE: %s\n\n", estimatedDistance); final TargetAttempt targetAttempt = readInput(scan); if (targetAttempt.distance < 20) { System.out.println("YOU BLEW YOURSELF UP!!"); targetOrSelfDestroyed = true; } else { final double a1 = targetAttempt.xDeviation / RADIAN; final double b1 = targetAttempt.zDeviation / RADIAN; System.out.printf("RADIANS FROM X AXIS = %.7f FROM Z AXIS = %.7f\n", a1, b1); final double x1 = targetAttempt.distance * Math.sin(b1) * Math.cos(a1); final double y1 = targetAttempt.distance * Math.sin(b1) * Math.sin(a1); final double z1 = targetAttempt.distance * Math.cos(b1); double distance = Math.sqrt((x1 - x) * (x1 - x) + (y1 - y) * (y1 - y) + (z1 - z) * (z1 - z)); if (distance > 20) { double X2 = x1 - x; double Y2 = y1 - y; double Z2 = z1 - z; if (X2 < 0) { System.out.printf("SHOT BEHIND TARGET %.7f KILOMETERS.\n", -X2); } else { System.out.printf("SHOT IN FRONT OF TARGET %.7f KILOMETERS.\n", X2); } if (Y2 < 0) { System.out.printf("SHOT TO RIGHT OF TARGET %.7f KILOMETERS.\n", -Y2); } else { System.out.printf("SHOT TO LEFT OF TARGET %.7f KILOMETERS.\n", Y2); } if (Z2 < 0) { System.out.printf("SHOT BELOW TARGET %.7f KILOMETERS.\n", -Z2); } else { System.out.printf("SHOT ABOVE TARGET %.7f KILOMETERS.\n", Z2); } System.out.printf("APPROX POSITION OF EXPLOSION: X=%.7f Y=%.7f Z=%.7f\n", x1, y1, z1); System.out.printf(" DISTANCE FROM TARGET =%.7f\n\n\n\n", distance); } else { System.out.println(" * * * HIT * * * TARGET IS NON-FUNCTIONAL"); System.out.printf("DISTANCE OF EXPLOSION FROM TARGET WAS %.5f KILOMETERS.\n", distance); System.out.printf("MISSION ACCOMPLISHED IN %s SHOTS.\n", numberShots); targetOrSelfDestroyed = true; } } } System.out.println("\n\n\n\n\nNEXT TARGET...\n"); } } private static TargetAttempt readInput(Scanner scan) { System.out.println("INPUT ANGLE DEVIATION FROM X, DEVIATION FROM Z, DISTANCE "); boolean validInput = false; TargetAttempt targetAttempt = new TargetAttempt(); while (!validInput) { String input = scan.nextLine(); final String[] split = input.split(","); try { targetAttempt.xDeviation = Float.parseFloat(split[0]); targetAttempt.zDeviation = Float.parseFloat(split[1]); targetAttempt.distance = Float.parseFloat(split[2]); validInput = true; } catch (NumberFormatException nfe) { System.out.println("!NUMBER EXPECTED - RETRY INPUT LINE\n? "); } } return targetAttempt; } private static void printIntro() { System.out.println(" TARGET"); System.out.println(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println("\n\n"); System.out.println("YOU ARE THE WEAPONS OFFICER ON THE STARSHIP ENTERPRISE"); System.out.println("AND THIS IS A TEST TO SEE HOW ACCURATE A SHOT YOU"); System.out.println("ARE IN A THREE-DIMENSIONAL RANGE. YOU WILL BE TOLD"); System.out.println("THE RADIAN OFFSET FOR THE X AND Z AXES, THE LOCATION"); System.out.println("OF THE TARGET IN THREE DIMENSIONAL RECTANGULAR COORDINATES,"); System.out.println("THE APPROXIMATE NUMBER OF DEGREES FROM THE X AND Z"); System.out.println("AXES, AND THE APPROXIMATE DISTANCE TO THE TARGET."); System.out.println("YOU WILL THEN PROCEED TO SHOOT AT THE TARGET UNTIL IT IS"); System.out.println("DESTROYED!"); System.out.println("\nGOOD LUCK!!\n\n"); } /** * Represents the user input */ private static class TargetAttempt { double xDeviation; double zDeviation; double distance; } } ================================================ FILE: 86_Target/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 86_Target/javascript/target.html ================================================ TARGET

    
    
    
    
    
    
    ================================================
    FILE: 86_Target/javascript/target.js
    ================================================
    // TARGET
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    // Main program
    async function main()
    {
        print(tab(33) + "TARGET\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        r = 0;  // 1 in original
        r1 = 57.296;
        p = Math.PI;
        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");
        print("\n");
        print("GOOD LUCK!!\n");
        print("\n");
        print("\n");
        while (1) {
            a = Math.random() * 2 * p;
            b = Math.random() * 2 * p;
            q = Math.floor(a * r1);
            w = Math.floor(b * r1);
            print("RADIANS FROM X AXIS = " + a + "   FROM Z AXIS = " + b + "\n");
            p1 = 100000 * Math.random() + Math.random();
            x = Math.sin(b) * Math.cos(a) * p1;
            y = Math.sin(b) * Math.sin(a) * p1;
            z = Math.cos(b) * p1;
            print("TARGET SIGHTED: APPROXIMATE COORDINATES:  X=" + x + "  Y=" + y + "  Z=" + z + "\n");
            while (1) {
                r++;
                switch (r) {
                    case 1:
                        p3 = Math.floor(p1 * 0.05) * 20;
                        break;
                    case 2:
                        p3 = Math.floor(p1 * 0.1) * 10;
                        break;
                    case 3:
                        p3 = Math.floor(p1 * 0.5) * 2;
                        break;
                    case 4:
                        p3 = Math.floor(p1);
                        break;
                    case 5:
                        p3 = p1;
                        break;
                }
                print("     ESTIMATED DISTANCE: " + p3 + "\n");
                print("\n");
                print("INPUT ANGLE DEVIATION FROM X, DEVIATION FROM Z, DISTANCE");
                str = await input();
                a1 = parseInt(str);
                b1 = parseInt(str.substr(str.indexOf(",") + 1));
                p2 = parseInt(str.substr(str.lastIndexOf(",") + 1));
                print("\n");
                if (p2 < 20) {
                    print("YOU BLEW YOURSELF UP!!\n");
                    break;
                }
                a1 /= r1;
                b1 /= r1;
                print("RADIANS FROM X AXIS = " + a1 + "  ");
                print("FROM Z AXIS = " + b1 + "\n");
                x1 = p2 * Math.sin(b1) * Math.cos(a1);
                y1 = p2 * Math.sin(b1) * Math.sin(a1);
                z1 = p2 * Math.cos(b1);
                d = Math.sqrt((x1 - x) * (x1 - x) + (y1 - y) * (y1 - y) + (z1 - z) * (z1 - z));
                if (d <= 20) {
                    print("\n");
                    print(" * * * HIT * * *   TARGET IS NON-FUNCTIONAL\n");
                    print("\n");
                    print("DISTANCE OF EXPLOSION FROM TARGET WAS " + d + " KILOMETERS.");
                    print("\n");
                    print("MISSION ACCOMPLISHED IN " + r + " SHOTS.\n");
                    r = 0;
                    for (i = 1; i <= 5; i++)
                        print("\n");
                    print("NEXT TARGET...\n");
                    print("\n");
                    break;
                }
                x2 = x1 - x;
                y2 = y1 - y;
                z2 = z1 - z;
                if (x2 >= 0)
                    print("SHOT IN FRONT OF TARGET " + x2 + " KILOMETERS.\n");
                else
                    print("SHOT BEHIND TARGET " + -x2 + " KILOMETERS.\n");
                if (y2 >= 0)
                    print("SHOT TO LEFT OF TARGET " + y2 + " KILOMETERS.\n");
                else
                    print("SHOT TO RIGHT OF TARGET " + -y2 + " KILOMETERS.\n");
                if (z2 >= 0)
                    print("SHOT ABOVE TARGET " + z2 + " KILOMETERS.\n");
                else
                    print("SHOT BELOW TARGET " + -z2 + " KILOMETERS.\n");
                print("APPROX POSITION OF EXPLOSION:  X=" + x1 + "   Y=" + y1 + "   Z=" + z1 + "\n");
                print("     DISTANCE FROM TARGET = " + d + "\n");
                print("\n");
                print("\n");
                print("\n");
            }
        }
    }
    
    main();
    
    
    ================================================
    FILE: 86_Target/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 86_Target/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 86_Target/perl/README.md
    ================================================
    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.
    
    
    ================================================
    FILE: 86_Target/perl/target.pl
    ================================================
    #!/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";
    }
    
    
    ================================================
    FILE: 86_Target/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 86_Target/python/target.py
    ================================================
    """
    TARGET
    
    Weapon targeting simulation / 3d trigonometry practice
    
    Ported by Dave LeCompte
    """
    
    import math
    import random
    from typing import List
    
    PAGE_WIDTH = 64
    
    
    def print_centered(msg: str) -> None:
        spaces = " " * ((PAGE_WIDTH - len(msg)) // 2)
        print(spaces + msg)
    
    
    def print_header(title: str) -> None:
        print_centered(title)
        print_centered("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY")
        print()
        print()
        print()
    
    
    def print_instructions() -> None:
        print("YOU ARE THE WEAPONS OFFICER ON THE STARSHIP ENTERPRISE")
        print("AND THIS IS A TEST TO SEE HOW ACCURATE A SHOT YOU")
        print("ARE IN A THREE-DIMENSIONAL RANGE.  YOU WILL BE TOLD")
        print("THE RADIAN OFFSET FOR THE X AND Z AXES, THE LOCATION")
        print("OF THE TARGET IN THREE 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 PROCEEED TO SHOOT AT THE TARGET UNTIL IT IS")
        print("DESTROYED!")
        print()
        print("GOOD LUCK!!")
        print()
        print()
    
    
    def prompt() -> List[float]:
        while True:
            response = input("INPUT ANGLE DEVIATION FROM X, DEVIATION FROM Z, DISTANCE? ")
            if "," not in response:
                continue
    
            terms = response.split(",")
            if len(terms) != 3:
                continue
    
            return [float(t) for t in terms]
    
    
    def next_target() -> None:
        for _ in range(5):
            print()
        print("NEXT TARGET...")
        print()
    
    
    def describe_miss(x, y, z, x1, y1, z1, d) -> None:
        x2 = x1 - x
        y2 = y1 - y
        z2 = z1 - z
    
        if x2 < 0:
            print(f"SHOT BEHIND TARGET {-x2:.2f} KILOMETERS.")
        else:
            print(f"SHOT IN FRONT OF TARGET {x2:.2f} KILOMETERS.")
    
        if y2 < 0:
            print(f"SHOT TO RIGHT OF TARGET {-y2:.2f} KILOMETERS.")
        else:
            print(f"SHOT TO LEFT OF TARGET {y2:.2f} KILOMETERS.")
    
        if z2 < 0:
            print(f"SHOT BELOW TARGET {-z2:.2f} KILOMETERS.")
        else:
            print(f"SHOT ABOVE TARGET {z2:.2f} KILOMETERS.")
    
        print(f"APPROX POSITION OF EXPLOSION:  X={x1:.4f}   Y={y1:.4f}   Z={z1:.4f}")
        print(f"     DISTANCE FROM TARGET = {d:.2f}")
        print()
        print()
        print()
    
    
    def do_shot_loop(p1, x, y, z) -> None:
        shot_count = 0
        while True:
            shot_count += 1
            if shot_count == 1:
                p3 = int(p1 * 0.05) * 20
            elif shot_count == 2:
                p3 = int(p1 * 0.1) * 10
            elif shot_count == 3:
                p3 = int(p1 * 0.5) * 2
            elif shot_count == 4:
                p3 = int(p1)
            else:
                p3 = p1
    
            if p3 == int(p3):
                print(f"     ESTIMATED DISTANCE: {p3}")
            else:
                print(f"     ESTIMATED DISTANCE: {p3:.2f}")
            print()
            a1, b1, p2 = prompt()
    
            if p2 < 20:
                print("YOU BLEW YOURSELF UP!!")
                return
    
            a1 = math.radians(a1)
            b1 = math.radians(b1)
            show_radians(a1, b1)
    
            x1 = p2 * math.sin(b1) * math.cos(a1)
            y1 = p2 * math.sin(b1) * math.sin(a1)
            z1 = p2 * math.cos(b1)
    
            distance = math.sqrt((x1 - x) ** 2 + (y1 - y) ** 2 + (z1 - z) ** 2)
    
            if distance <= 20:
                print()
                print(" * * * HIT * * *   TARGET IS NON FUNCTIONAL")
                print()
                print(f"DISTANCE OF EXPLOSION FROM TARGET WAS {distance:.4f} KILOMETERS")
                print()
                print(f"MISSION ACCOMPLISHED IN {shot_count} SHOTS.")
    
                return
            else:
                describe_miss(x, y, z, x1, y1, z1, distance)
    
    
    def show_radians(a, b) -> None:
        print(f"RADIANS FROM X AXIS = {a:.4f}   FROM Z AXIS = {b:.4f}")
    
    
    def play_game() -> None:
        while True:
            a = random.uniform(0, 2 * math.pi)  # random angle
            b = random.uniform(0, 2 * math.pi)  # random angle
    
            show_radians(a, b)
    
            p1 = random.uniform(0, 100000) + random.uniform(0, 1)
            x = math.sin(b) * math.cos(a) * p1
            y = math.sin(b) * math.sin(a) * p1
            z = math.cos(b) * p1
            print(
                f"TARGET SIGHTED: APPROXIMATE COORDINATES:  X={x:.1f}  Y={y:.1f}  Z={z:.1f}"
            )
    
            do_shot_loop(p1, x, y, z)
            next_target()
    
    
    def main() -> None:
        print_header("TARGET")
        print_instructions()
    
        play_game()
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 86_Target/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 86_Target/target.bas
    ================================================
    10 PRINT TAB(33);"TARGET"
    20 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    30 PRINT: PRINT: PRINT
    100 R=1: R1=57.296: P=3.14159
    110 PRINT "YOU ARE THE WEAPONS OFFICER ON THE STARSHIP ENTERPRISE"
    120 PRINT "AND THIS IS A TEST TO SEE HOW ACCURATE A SHOT YOU"
    130 PRINT "ARE IN A THREE-DIMENSIONAL RANGE.  YOU WILL BE TOLD"
    140 PRINT "THE RADIAN OFFSET FOR THE X AND Z AXES, THE LOCATION"
    150 PRINT "OF THE TARGET IN THREE DIMENSIONAL RECTANGULAR COORDINATES,"
    160 PRINT "THE APPROXIMATE NUMBER OF DEGREES FROM THE X AND Z"
    170 PRINT "AXES, AND THE APPROXIMATE DISTANCE TO THE TARGET."
    180 PRINT "YOU WILL THEN PROCEEED TO SHOOT AT THE TARGET UNTIL IT IS"
    190 PRINT "DESTROYED!": PRINT: PRINT "GOOD LUCK!!":PRINT: PRINT
    220 A=RND(1)*2*P: B=RND(1)*2*P: Q=INT(A*R1): W=INT(B*R1)
    260 PRINT "RADIANS FROM X AXIS =";A;"   FROM Z AXIS =";B
    280 P1=100000*RND(1)+RND(1): X=SIN(B)*COS(A)*P1: Y=SIN(B)*SIN(A)*P1
    290 Z=COS(B)*P1
    340 PRINT "TARGET SIGHTED: APPROXIMATE COORDINATES:  X=";X;"  Y=";Y;"  Z=";Z
    345 R=R+1: IF R>5 THEN 390
    350 ON R GOTO 355,360,365,370,375
    355 P3=INT(P1*.05)*20: GOTO 390
    360 P3=INT(P1*.1)*10: GOTO 390
    365 P3=INT(P1*.5)*2: GOTO 390
    370 P3=INT(P1): GOTO 390
    375 P3=P1
    390 PRINT "     ESTIMATED DISTANCE:";P3
    400 PRINT:PRINT "INPUT ANGLE DEVIATION FROM X, DEVIATION FROM Z, DISTANCE";
    405 INPUT A1,B1,P2
    410 PRINT: IF P2<20 THEN PRINT "YOU BLEW YOURSELF UP!!": GOTO 580
    420 A1=A1/R1: B1=B1/R1: PRINT "RADIANS FROM X AXIS =";A1;"  ";
    425 PRINT "FROM Z AXIS =";B1
    480 X1=P2*SIN(B1)*COS(A1): Y1=P2*SIN(B1)*SIN(A1): Z1=P2*COS(B1)
    510 D=((X1-X)^2+(Y1-Y)^2+(Z1-Z)^2)^(1/2)
    520 IF D>20 THEN 670
    530 PRINT: PRINT " * * * HIT * * *   TARGET IS NON-FUNCTIONAL": PRINT
    550 PRINT "DISTANCE OF EXPLOSION FROM TARGET WAS";D;"KILOMETERS."
    570 PRINT: PRINT "MISSION ACCOMPLISHED IN ";R;" SHOTS."
    580 R=0: FOR I=1 TO 5: PRINT: NEXT I: PRINT "NEXT TARGET...": PRINT
    590 GOTO 220
    670 X2=X1-X: Y2=Y1-Y: Z2=Z1-Z: IF X2<0 THEN 730
    710 PRINT "SHOT IN FRONT OF TARGET";X2;"KILOMETERS.": GOTO 740
    730 PRINT "SHOT BEHIND TARGET";-X2;"KILOMETERS."
    740 IF Y2<0 THEN 770
    750 PRINT "SHOT TO LEFT OF TARGET";Y2;"KILOMETERS.": GOTO 780
    770 PRINT "SHOT TO RIGHT OF TARGET";-Y2;"KILOMETERS."
    780 IF Z2<0 THEN 810
    790 PRINT "SHOT ABOVE TARGET";Z2;"KILOMETERS.": GOTO 820
    810 PRINT "SHOT BELOW TARGET";-Z2;"KILOMETERS."
    820 PRINT "APPROX POSITION OF EXPLOSION:  X=";X1;"   Y=";Y1;"   Z=";Z1
    830 PRINT "     DISTANCE FROM TARGET =";D: PRINT: PRINT: PRINT: GOTO 345
    999 END
    
    
    ================================================
    FILE: 86_Target/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 86_Target/vbnet/Target.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Target", "Target.vbproj", "{48A62242-16CC-4BFC-B7DB-C2DAE3C13B5A}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{48A62242-16CC-4BFC-B7DB-C2DAE3C13B5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{48A62242-16CC-4BFC-B7DB-C2DAE3C13B5A}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{48A62242-16CC-4BFC-B7DB-C2DAE3C13B5A}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{48A62242-16CC-4BFC-B7DB-C2DAE3C13B5A}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 86_Target/vbnet/Target.vbproj
    ================================================
    
      
        Exe
        Target
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 87_3-D_Plot/3dplot.bas
    ================================================
    1 PRINT TAB(32);"3D PLOT"
    2 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    3 PRINT:PRINT:PRINT
    5 DEF FNA(Z)=30*EXP(-Z*Z/100)
    100 PRINT
    110 FOR X=-30 TO 30 STEP 1.5
    120 L=0
    130 Y1=5*INT(SQR(900-X*X)/5)
    140 FOR Y=Y1 TO -Y1 STEP -5
    150 Z=INT(25+FNA(SQR(X*X+Y*Y))-.7*Y)
    160 IF Z<=L THEN 190
    170 L=Z
    180 PRINT TAB(Z);"*";
    190 NEXT Y
    200 PRINT
    210 NEXT X
    300 END
    
    
    ================================================
    FILE: 87_3-D_Plot/README.md
    ================================================
    ### 3-D Plot
    
    3-D PLOT will plot the family of curves of any function. The function Z is plotted as “rising” out of the x-y plane with x and y inside a circle of radius 30. The resultant plot looks almost 3-dimensional.
    
    You set the function you want plotted in line 5. As with any mathematical plot, some functions come out “prettier” than others.
    
    The author of this amazingly clever program is Mark Bramhall of DEC.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=167)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=182)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 87_3-D_Plot/csharp/Function.cs
    ================================================
    using System;
    using System.Collections.Generic;
    
    namespace Plot
    {
        internal static class Function
        {
            internal static IEnumerable> GetRows()
            {
                for (var x = -30f; x <= 30f; x += 1.5f)
                {
                    yield return GetValues(x);
                }
            }
    
            private static IEnumerable GetValues(float x)
            {
                var zPrevious = 0;
                var yLimit = 5 * (int)(Math.Sqrt(900 - x * x) / 5);
    
                for (var y = yLimit; y >= -yLimit; y -= 5)
                {
                    var z = GetValue(x, y);
    
                    if (z > zPrevious)
                    {
                        zPrevious = z;
                        yield return z;
                    }
                }
            }
    
            private static int GetValue(float x, float y)
            {
                var r = (float)Math.Sqrt(x * x + y * y);
                return (int)(25 + 30 * Math.Exp(-r * r / 100) - 0.7f * y);
            }
        }
    }
    
    
    ================================================
    FILE: 87_3-D_Plot/csharp/Plot.csproj
    ================================================
    
    
      
        Exe
        net5.0
      
    
    
    
    
    ================================================
    FILE: 87_3-D_Plot/csharp/Plot.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 17
    VisualStudioVersion = 17.0.32014.148
    MinimumVisualStudioVersion = 15.0.26124.0
    Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Plot", "Plot.csproj", "{8000A3CF-612D-4FB7-B53D-885BB6E5492B}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{8000A3CF-612D-4FB7-B53D-885BB6E5492B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{8000A3CF-612D-4FB7-B53D-885BB6E5492B}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{8000A3CF-612D-4FB7-B53D-885BB6E5492B}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{8000A3CF-612D-4FB7-B53D-885BB6E5492B}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {5DE8E572-67C9-4DE3-B851-447B78FE983A}
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 87_3-D_Plot/csharp/Program.cs
    ================================================
    using System;
    
    namespace Plot
    {
        class Program
        {
            static void Main(string[] args)
            {
                PrintTitle();
    
                foreach (var row in Function.GetRows())
                {
                    foreach (var z in row)
                    {
                        Plot(z);
                    }
                    Console.WriteLine();
                }
            }
    
            private static void PrintTitle()
            {
                Console.WriteLine("                                3D Plot");
                Console.WriteLine("               Creative Computing  Morristown, New Jersey");
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine();
                Console.WriteLine();
            }
    
            private static void Plot(int z)
            {
                var x = Console.GetCursorPosition().Top;
                Console.SetCursorPosition(z, x);
                Console.Write("*");
            }
        }
    }
    
    
    ================================================
    FILE: 87_3-D_Plot/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 87_3-D_Plot/d/.gitignore
    ================================================
    *.exe
    *.obj
    
    
    ================================================
    FILE: 87_3-D_Plot/d/README.md
    ================================================
    Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Converted to [D](https://dlang.org/) by [Bastiaan Veelo](https://github.com/veelo).
    
    ## Running the code
    
    Assuming the reference [dmd](https://dlang.org/download.html#dmd) compiler:
    ```shell
    dmd -dip1000 -run threedeeplot.d
    ```
    
    [Other compilers](https://dlang.org/download.html) also exist.
    
    ## On rounding floating point values to integer values
    
    The D equivalent of Basic `INT` is [`floor`](https://dlang.org/phobos/std_math_rounding.html#.floor),
    which rounds towards negative infinity. If you change occurrences of `floor` to
    [`lrint`](https://dlang.org/phobos/std_math_rounding.html#.lrint), you'll see that the plots show a bit more detail,
    as is done in the bonus below.
    
    ## Bonus: Self-writing programs
    
    With a small modification to the source, the program can be extended to **plot a random function**, and **print its formula**.
    
    ```shell
    rdmd -dip1000 threedeeplot_random.d
    ```
    (`rdmd` caches the executable, which results in speedy execution when the source does not change.)
    
    ### Example output
    ```
                                        3D Plot
                  (After Creative Computing  Morristown, New Jersey)
    
    
                               f(z) = 30 * sin(z / 10.0)
    
                                 *
                          *      *    * *
                    *         *      *    * *
                        *         *      *    * *
                *           *        *       *   *  *
                   *           *         *      *   *  *
                      *           *         *     *    * **
           *             *           *        *      *   *  *
             *              *           *       *     *   *  **
                *              *          *       *    *   *  * *
                  *              *          *      *   *   *  *  *
                    *              *          *     *  *  *   *   **
                      *              *         *    * *  *   *    * *
       *                *             *        *    ** *    *     * *
        *                *             *        *  **     *      *   *
         *                 *            *       * *     *       *    *
          *                 *            *      * *   *         *    *
           *                *             *     ** *           *     **
            *                *            *     **            *      **
            *                *            *     *            *       **
            *                *            *     *            *       **
            *                *            *     *            *       **
            *                *            *     **            *      **
           *                *             *     ** *           *     **
          *                 *            *      * *   *         *    *
         *                 *            *       * *     *       *    *
        *                *             *        *  **     *      *   *
       *                *             *        *    ** *    *     * *
                      *              *         *    * *  *   *    * *
                    *              *          *     *  *  *   *   **
                  *              *          *      *   *   *  *  *
                *              *          *       *    *   *  * *
             *              *           *       *     *   *  **
           *             *           *        *      *   *  *
                      *           *         *     *    * **
                   *           *         *      *   *  *
                *           *        *       *   *  *
                        *         *      *    * *
                    *         *      *    * *
                          *      *    * *
                                 *
    ```
    
    ### Breakdown of differences
    
    Have a look at the relevant differences between `threedeeplot.d` and `threedeeplot_random.d`.
    This is the original function with the single expression that is evaluated for the plot:
    ```d
        static float fna(float z)
        {
            return 30.0 * exp(-z * z / 100.0);
        }
    ```
    Here `static` means that the nested function does not need acces to its enclosing scope.
    
    Now, by inserting the following:
    ```d
        enum functions = ["30.0 * exp(-z * z / 100.0)",
                          "sqrt(900.01 - z * z) * .9 - 2",
                          "30 * (cos(z / 16.0) + .5)",
                          "30 - 30 * sin(z / 18.0)",
                          "30 * exp(-cos(z / 16.0)) - 30",
                          "30 * sin(z / 10.0)"];
    
        size_t index = uniform(0, functions.length);
        writeln(center("f(z) = " ~ functions[index], width), "\n");
    ```
    and changing the implementation of `fna` to
    ```d
        float fna(float z)
        {
            final switch (index)
            {
                static foreach (i, f; functions)
                    case i:
                        mixin("return " ~ f ~ ";");
            }
        }
    ```
    we unlock some very special abilities of D. Let's break it down:
    
    ```d
        enum functions = ["30.0 * exp(-z * z / 100.0)", /*...*/];
    ```
    This defines an array of strings, each containing a mathematical expression. Due to the `enum` keyword, this is an
    array that really only exists at compile-time.
    
    ```d
        size_t index = uniform(0, functions.length);
    ```
    This defines a random index into the array. `functions.length` is evaluated at compile-time, due to D's compile-time
    function evaluation (CTFE).
    
    ```d
        writeln(center("f(z) = " ~ functions[index], width), "\n");
    ```
    Unmistakenly, this prints the formula centered on a line. What happens behind the scenes is that `functions` (which
    only existed at compile-time before now) is pasted in, so that an instance of that array actually exists at run-time
    at this spot, and is instantly indexed.
    
    ```d
        float fna(float z)
        {
            final switch (index)
            {
                // ...
            }
        }
    ```
    `static` has been dropped from the nested function because we want to evaluate `index` inside it. The function contains
    an ordinary `switch`, with `final` providing some extra robustness. It disallows a `default` case and produces an error
    when the switch doesn't handle all cases. The `switch` body is where the magic happens and consists of these three
    lines:
    ```d
                static foreach (i, f; functions)
                    case i:
                        mixin("return " ~ f ~ ";");
    ```
    The `static foreach` iterates over `functions` at compile-time, producing one `case` for every element in `functions`.
    `mixin` takes a string, which is constructed at compile-time, and pastes it right into the source.
    
    In effect, the implementation of `float fna(float z)` unrolls itself into
    ```d
        float fna(float z)
        {
            final switch (index)
            {
                case 0:
                    return 30.0 * exp(-z * z / 100.0);
                case 1:
                    return sqrt(900.01 - z * z) * .9 - 2;
                case 2:
                    return 30 * (cos(z / 16.0) + .5);
                case 3:
                    return 30 - 30 * sin(z / 18.0);
                case 4:
                    return 30 * exp(-cos(z / 16.0)) - 30;
                case 5:
                    return 30 * sin(z / 10.0)";
            }
        }
    ```
    
    So if you feel like adding another function, all you need to do is append it to the `functions` array, and the rest of
    the program *rewrites itself...*
    
    
    ================================================
    FILE: 87_3-D_Plot/d/threedeeplot.d
    ================================================
    @safe: // Make @safe the default for this file, enforcing memory-safety.
    import std.stdio, std.string, std.math, std.range, std.conv, std.algorithm;
    
    void main()
    {
        enum width = 80;
        writeln(center("3D Plot", width));
        writeln(center("(After Creative Computing  Morristown, New Jersey)\n\n\n", width));
    
        static float fna(float z)
        {
            return 30.0 * exp(-z * z / 100.0);
        }
    
        char[] row;
    
        for (float x = -30.0; x <= 30.0; x += 1.5)
        {
            size_t max_z = 0L;
            auto y1 = 5 * floor((sqrt(900 - x * x)) / 5.0);
            for (float y = y1; y >= -y1; y -= 5)
            {
                auto z = to!size_t(max(0, floor(25 + fna(sqrt(x * x + y * y)) - .7 * y)));
                if (z > max_z) // Visible
                {
                    max_z = z;
                    if (z + 1 > row.length) // row needs to grow
                        row ~= ' '.repeat(z + 1 - row.length).array;
                    row[z] = '*';
                }
            }
            writeln(row);
            row = null;
        }
    }
    
    
    ================================================
    FILE: 87_3-D_Plot/d/threedeeplot_random.d
    ================================================
    @safe: // Make @safe the default for this file, enforcing memory-safety.
    import std.stdio, std.string, std.math, std.range, std.conv, std.random, std.algorithm;
    
    void main()
    {
        enum width = 80;
        writeln(center("3D Plot", width));
        writeln(center("(After Creative Computing  Morristown, New Jersey)\n\n", width));
    
        enum functions = ["30.0 * exp(-z * z / 100.0)",
                          "sqrt(900.01 - z * z) * .9 - 2",
                          "30 * (cos(z / 16.0) + .5)",
                          "30 - 30 * sin(z / 18.0)",
                          "30 * exp(-cos(z / 16.0)) - 30",
                          "30 * sin(z / 10.0)"];
    
        size_t index = uniform(0, functions.length);
        writeln(center("f(z) = " ~ functions[index], width), "\n");
    
        float fna(float z)
        {
            final switch (index)
            {
                static foreach (i, f; functions)
                    case i:
                        mixin("return " ~ f ~ ";");
            }
        }
    
        char[] row;
    
        for (float x = -30.0; x <= 30.0; x += 1.5)
        {
            size_t max_z = 0L;
            auto y1 = 5 * lrint((sqrt(900 - x * x)) / 5.0);
            for (float y = y1; y >= -y1; y -= 5)
            {
                auto z = to!size_t(max(0, lrint(25 + fna(sqrt(x * x + y * y)) - .7 * y)));
                if (z > max_z) // Visible
                {
                    max_z = z;
                    if (z + 1 > row.length) // row needs to grow
                        row ~= ' '.repeat(z + 1 - row.length).array;
                    row[z] = '*';
                }
            }
            writeln(row);
            row = null;
        }
    }
    
    
    ================================================
    FILE: 87_3-D_Plot/java/Plot3D.java
    ================================================
    import java.lang.Math;
    
    /**
     * Game of 3-D Plot
     * 

    * Based on the BASIC game of 3-D Plot here * https://github.com/coding-horror/basic-computer-games/blob/main/87%203-D%20Plot/3dplot.bas *

    * Note: The idea was to create a version of the 1970's BASIC game in Java, without introducing * new features - no additional text, error checking, etc has been added. * * Converted from BASIC to Java by Darren Cardenas. */ // Java class names cannot begin with a letter, so class name 3dplot cannot be used public class Plot3D { public void play() { showIntro(); startGame(); } // End of method play private void showIntro() { System.out.println(" ".repeat(31) + "3D PLOT"); System.out.println(" ".repeat(14) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println("\n\n\n"); } // End of method showIntro private void startGame() { float row = 0; int column = 0; int limit = 0; int plotVal = 0; int root = 0; String lineContent = ""; // Begin loop through all rows for (row = -30; row <= 30; row += 1.5) { limit = 0; root = 5 * (int) Math.floor((Math.sqrt(900 - row * row) / 5)); // Begin loop through all columns for (column = root; column >= -root; column += -5) { plotVal = 25 + (int) Math.floor(func(Math.sqrt(row * row + column * column)) - 0.7 * column); if (plotVal > limit) { limit = plotVal; // Add whitespace while (lineContent.length() < (plotVal-1)) { lineContent += " "; } lineContent += "*"; } } // End loop through all columns System.out.println(lineContent); lineContent = ""; } // End loop through all rows } // End of method startGame // Function to be plotted public double func(double inputVal) { return (30 * Math.exp(-inputVal * inputVal / 100)); } public static void main(String[] args) { Plot3D plot = new Plot3D(); plot.play(); } // End of method main } // End of class Plot3D ================================================ FILE: 87_3-D_Plot/java/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Oracle Java](https://openjdk.java.net/) ================================================ FILE: 87_3-D_Plot/javascript/3dplot.html ================================================ 3D PLOT

    
    
    
    
    
    
    ================================================
    FILE: 87_3-D_Plot/javascript/3dplot.js
    ================================================
    // 3D PLOT
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    function print(str)
    {
    	document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function tab(space)
    {
    	var str = "";
    	while (space-- > 0)
    		str += " ";
    	return str;
    }
    
    function equation(input)
    {
    	return 30 * Math.exp(-input * input / 100);
    }
    
    print(tab(32) + "3D PLOT\n");
    print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
    
    for (x = -30; x <= 30; x += 1.5) {
    	l = 0;
    	y1 = 5 * Math.floor(Math.sqrt(900 - x * x) / 5);
    	str = "";
    	for (y = y1; y >= -y1; y -= 5) {
    		z = Math.floor(25 + equation(Math.sqrt(x * x + y * y)) - .7 * y);
    		if (z > l) {
    			l = z;
    			while (str.length < z)
    				str += " ";
    			str += "*";
    		}
    	}
    	print(str + "\n");
    }
    
    
    ================================================
    FILE: 87_3-D_Plot/javascript/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells)
    
    
    ================================================
    FILE: 87_3-D_Plot/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 87_3-D_Plot/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 87_3-D_Plot/perl/3dplot.pl
    ================================================
    #!/usr/bin/perl
    use strict;
    use warnings;
    
    print ' 'x32 ."3D PLOT\n";
    print ' 'x15 ."CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n";
    print "\n\n\n";
    
    sub FNA {
    	my ($Z)= @_;
    	return 30*exp(-$Z*$Z/100);
    	}
    
    print "\n";
    
    for (my $X=-30; $X<=30; $X+=1.5) {
    	my $L=0;
    	my $Line=" "x80; #Empty buffer string;
    	my $Y1=5*int(sqrt(900-$X*$X)/5);
    	for (my $Y=$Y1; $Y>=-$Y1; $Y-=5) {
    		my $Z=int(25+&FNA(sqrt($X*$X+$Y*$Y))-.7*$Y);
    		if ($Z<=$L) { next; }
    		$L= $Z;
    		substr $Line, $Z, 1, "*"; #Plot on the line by sustitution.
    		}
    	print "$Line\n"; #Now print the line.
    	}
    
    
    ================================================
    FILE: 87_3-D_Plot/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 87_3-D_Plot/python/3dplot.py
    ================================================
    #!/usr/bin/env python3
    
    # 3D PLOT
    #
    # Converted from BASIC to Python by Trevor Hobson
    
    from math import exp, floor, sqrt
    
    
    def equation(x: float) -> float:
        return 30 * exp(-x * x / 100)
    
    
    def main() -> None:
        print(" " * 32 + "3D PLOT")
        print(" " * 15 + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n\n\n\n")
    
        for x in range(-300, 315, 15):
            x1 = x / 10
            max_column = 0
            y1 = 5 * floor(sqrt(900 - x1 * x1) / 5)
            y_plot = [" "] * 80
    
            for y in range(y1, -(y1 + 5), -5):
                column = floor(25 + equation(sqrt(x1 * x1 + y * y)) - 0.7 * y)
                if column > max_column:
                    max_column = column
                    y_plot[column] = "*"
            print("".join(y_plot))
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 87_3-D_Plot/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 87_3-D_Plot/ruby/3dplot.rb
    ================================================
    def intro
      puts "                                3D PLOT
                   CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n\n\n\n\n"
    end
    
    def fna(z) = 30 * Math.exp(-z * z / 100)
    
    def render
      (-30..30).step(1.5).each do |x|
        l = 0
        y1 = 5 * (Math.sqrt(900 - x * x) / 5).to_i
        y_plot = " " * 80
        (y1..-y1).step(-5).each do |y|
          z = (25 + fna(Math.sqrt(x * x + y * y)) - 0.7 * y).to_i
          next if z <= l
          l = z
          y_plot[z] = '*'
        end
        puts y_plot
      end
    end
    
    def main
      intro
      render
    end
    
    main
    
    
    ================================================
    FILE: 87_3-D_Plot/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 87_3-D_Plot/rust/Cargo.toml
    ================================================
    [package]
    name = "rust"
    version = "0.1.0"
    edition = "2021"
    
    [dependencies]
    
    
    ================================================
    FILE: 87_3-D_Plot/rust/src/main.rs
    ================================================
    /** 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
    }
    
    ================================================
    FILE: 87_3-D_Plot/vbnet/Plot.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "Plot", "Plot.vbproj", "{534355CC-A2C1-4138-9EB5-09D658DD4504}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{534355CC-A2C1-4138-9EB5-09D658DD4504}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{534355CC-A2C1-4138-9EB5-09D658DD4504}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{534355CC-A2C1-4138-9EB5-09D658DD4504}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{534355CC-A2C1-4138-9EB5-09D658DD4504}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {CAD09CCC-4522-438E-A8C6-514A866D90DE}
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 87_3-D_Plot/vbnet/Plot.vbproj
    ================================================
    
      
        Exe
        Plot
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 87_3-D_Plot/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 88_3-D_Tic-Tac-Toe/README.md
    ================================================
    ### 3-D Tic-Tac-Toe
    
    3-D TIC-TAC-TOE is a game of tic-tac-toe in a 4x4x4 cube. You must get 4 markers in a row or diagonal along any 3-dimensional plane in order to win.
    
    Each move is indicated by a 3-digit number (digits not separated by commas), with each digit between 1 and 4 inclusive. The digits indicate the level, column, and row, respectively, of the move. You can win if you play correctly; although, it is considerably more difficult than standard, two-dimensional 3x3 tic-tac-toe.
    
    This version of 3-D TIC-TAC-TOE is from Dartmouth College.
    
    ### Conversion notes
    
    The AI code for TicTacToe2 depends quite heavily on the non-structured GOTO (I can almost hear Dijkstra now) and translation is quite challenging. This code relies very heavily on GOTOs that bind the code tightly together. Comments explain where that happens in the original.
    
    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):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=168)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=183)
    
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 88_3-D_Tic-Tac-Toe/csharp/Program.cs
    ================================================
    namespace ThreeDTicTacToe
    {
        class Program
        {
            static void Main()
            {
                new Qubic().Run();
            }
        }
    }
    
    
    ================================================
    FILE: 88_3-D_Tic-Tac-Toe/csharp/Qubic.cs
    ================================================
    using System.Text;
    
    namespace ThreeDTicTacToe
    {
        /// 
        /// Qubic is a 3D Tic-Tac-Toe game played on a 4x4x4 cube. This code allows
        ///  a player to compete against a deterministic AI that is surprisingly
        ///  difficult to beat.
        /// 
        internal class Qubic
        {
            // The Y variable in the original BASIC.
            private static readonly int[] CornersAndCenters = QubicData.CornersAndCenters;
            // The M variable in the original BASIC.
            private static readonly int[,] RowsByPlane = QubicData.RowsByPlane;
    
            // Board spaces are filled in with numeric values. A space could be:
            //
            //  - EMPTY: no one has moved here yet.
            //  - PLAYER: the player moved here.
            //  - MACHINE: the machine moved here.
            //  - POTENTIAL: the machine, in the middle of its move,
            //      might fill a space with a potential move marker, which
            //      prioritizes the space once it finally chooses where to move.
            //
            // The numeric values allow the program to determine what moves have
            //  been made in a row by summing the values in a row. In theory, the
            //  individual values could be any positive numbers that satisfy the
            //  following:
            //
            //  - EMPTY = 0
            //  - POTENTIAL * 4 < PLAYER
            //  - PLAYER * 4 < MACHINE
            private const double PLAYER = 1.0;
            private const double MACHINE = 5.0;
            private const double POTENTIAL = 0.125;
            private const double EMPTY = 0.0;
    
            // The X variable in the original BASIC. This is the Qubic board,
            //  flattened into a 1D array.
            private readonly double[] Board = new double[64];
    
            // The L variable in the original BASIC. There are 76 unique winning rows
            //  in the board, so each gets an entry in RowSums. A row sum can be used
            //  to check what moves have been made to that row in the board.
            //
            // Example: if RowSums[i] == PLAYER * 4, the player won with row i!
            private readonly double[] RowSums = new double[76];
    
            public Qubic() { }
    
            /// 
            /// Run the Qubic game.
            ///
            /// Show the title, prompt for instructions, then begin the game loop.
            /// 
            public void Run()
            {
                Title();
                Instructions();
                Loop();
            }
    
            /***********************************************************************
            /* Terminal Text/Prompts
            /**********************************************************************/
            #region TerminalText
    
            /// 
            /// Display title and attribution.
            ///
            /// Original BASIC: 50-120
            /// 
            private static void Title()
            {
                Console.WriteLine(
                    "\n" +
                    "                                 QUBIC\n\n" +
                    "               CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n\n\n"
                );
            }
    
            /// 
            /// Prompt user for game instructions.
            ///
            /// Original BASIC: 210-313
            /// 
            private static void Instructions()
            {
                Console.Write("DO YOU WANT INSTRUCTIONS? ");
                var yes = ReadYesNo();
    
                if (yes)
                {
                    Console.WriteLine(
                        "\n" +
                        "THE GAME IS TIC-TAC-TOE IN A 4 X 4 X 4 CUBE.\n" +
                        "EACH MOVE IS INDICATED BY A 3 DIGIT NUMBER, WITH EACH\n" +
                        "DIGIT BETWEEN 1 AND 4 INCLUSIVE.  THE DIGITS INDICATE THE\n" +
                        "LEVEL, ROW, AND COLUMN, RESPECTIVELY, OF THE OCCUPIED\n" +
                        "PLACE.\n" +
                        "\n" +
                        "TO PRINT THE PLAYING BOARD, TYPE 0 (ZERO) AS YOUR MOVE.\n" +
                        "THE PROGRAM WILL PRINT THE BOARD WITH YOUR MOVES INDI-\n" +
                        "CATED WITH A (Y), THE MACHINE'S MOVES WITH AN (M), AND\n" +
                        "UNUSED SQUARES WITH A ( ).  OUTPUT IS ON PAPER.\n" +
                        "\n" +
                        "TO STOP THE PROGRAM RUN, TYPE 1 AS YOUR MOVE.\n\n"
                    );
                }
            }
    
            /// 
            /// Prompt player for whether they would like to move first, or allow
            ///  the machine to make the first move.
            ///
            /// Original BASIC: 440-490
            /// 
            /// true if the player wants to move first
            private static bool PlayerMovePreference()
            {
                Console.Write("DO YOU WANT TO MOVE FIRST? ");
                var result = ReadYesNo();
                Console.WriteLine();
                return result;
            }
    
            /// 
            /// Run the Qubic program loop.
            /// 
            private void Loop()
            {
                // The "retry" loop; ends if player quits or chooses not to retry
                // after game ends.
                while (true)
                {
                    ClearBoard();
                    var playerNext = PlayerMovePreference();
    
                    // The "game" loop; ends if player quits, player/machine wins,
                    // or game ends in draw.
                    while (true)
                    {
                        if (playerNext)
                        {
                            // Player makes a move.
                            var playerAction = PlayerMove();
                            if (playerAction == PlayerAction.Move)
                            {
                                playerNext = !playerNext;
                            }
                            else
                            {
                                return;
                            }
                        }
                        else
                        {
                            // Check for wins, if any.
                            RefreshRowSums();
                            if (CheckPlayerWin() || CheckMachineWin())
                            {
                                break;
                            }
    
                            // Machine makes a move.
                            var machineAction = MachineMove();
                            if (machineAction == MachineAction.Move)
                            {
                                playerNext = !playerNext;
                            }
                            else if (machineAction == MachineAction.End)
                            {
                                break;
                            }
                            else
                            {
                                throw new Exception("unreachable; machine should always move or end game in game loop");
                            }
                        }
                    }
    
                    var retry = RetryPrompt();
    
                    if (!retry)
                    {
                        return;
                    }
                }
            }
    
            /// 
            /// Prompt the user to try another game.
            ///
            /// Original BASIC: 1490-1560
            /// 
            /// true if the user wants to play again
            private static bool RetryPrompt()
            {
                Console.Write("DO YOU WANT TO TRY ANOTHER GAME? ");
                return ReadYesNo();
            }
    
            /// 
            /// Read a yes/no from the terminal. This method accepts anything that
            ///  starts with N/n as no and Y/y as yes.
            /// 
            /// true if the player answered yes
            private static bool ReadYesNo()
            {
                while (true)
                {
                    var response = Console.ReadLine() ?? " ";
                    if (response.ToLower().StartsWith("y"))
                    {
                        return true;
                    }
                    else if (response.ToLower().StartsWith("n"))
                    {
                        return false;
                    }
                    else
                    {
                        Console.Write("INCORRECT ANSWER.  PLEASE TYPE 'YES' OR 'NO'. ");
                    }
                }
            }
    
            #endregion
    
            /***********************************************************************
            /* Player Move
            /**********************************************************************/
            #region PlayerMove
    
            /// 
            /// Possible actions player has taken after ending their move. This
            ///  replaces the `GOTO` logic that allowed the player to jump out of
            ///  the game loop and quit.
            /// 
            private enum PlayerAction
            {
                /// 
                /// The player ends the game prematurely.
                /// 
                Quit,
                /// 
                /// The player makes a move on the board.
                /// 
                Move,
            }
    
            /// 
            /// Make the player's move based on their input.
            ///
            /// Original BASIC: 500-620
            /// 
            /// Whether the player moved or quit the program.
            private PlayerAction PlayerMove()
            {
                // Loop until a valid move is inputted.
                while (true)
                {
                    var move = ReadMove();
                    if (move == 1)
                    {
                        return PlayerAction.Quit;
                    }
                    else if (move == 0)
                    {
                        ShowBoard();
                    }
                    else
                    {
                        ClearPotentialMoves();
                        if (TryCoordToIndex(move, out int moveIndex))
                        {
                            if (Board[moveIndex] == EMPTY)
                            {
                                Board[moveIndex] = PLAYER;
                                return PlayerAction.Move;
                            }
                            else
                            {
                                Console.WriteLine("THAT SQUARE IS USED, TRY AGAIN.");
                            }
                        }
                        else
                        {
                            Console.WriteLine("INCORRECT MOVE, TRY AGAIN.");
                        }
                    }
                }
            }
    
            /// 
            /// Read a player move from the terminal. Move can be any integer.
            ///
            /// Original BASIC: 510-520
            /// 
            /// the move inputted
            private static int ReadMove()
            {
                Console.Write("YOUR MOVE? ");
                return ReadInteger();
            }
    
            /// 
            /// Read an integer from the terminal.
            ///
            /// Original BASIC: 520
            ///
            /// Unlike the basic, this code will not accept any string that starts
            ///  with a number; only full number strings are allowed.
            /// 
            /// the integer inputted
            private static int ReadInteger()
            {
                while (true)
                {
                    var response = Console.ReadLine() ?? " ";
    
                    if (int.TryParse(response, out var move))
                    {
                        return move;
    
                    }
                    else
                    {
                        Console.Write("!NUMBER EXPECTED - RETRY INPUT LINE--? ");
                    }
                }
            }
    
            /// 
            /// Display the board to the player. Spaces taken by the player are
            ///  marked with "Y", while machine spaces are marked with "M".
            ///
            /// Original BASIC: 2550-2740
            /// 
            private void ShowBoard()
            {
                var s = new StringBuilder(new string('\n', 9));
    
                for (int i = 1; i <= 4; i++)
                {
                    for (int j = 1; j <= 4; j++)
                    {
                        s.Append(' ', 3 * (j + 1));
                        for (int k = 1; k <= 4; k++)
                        {
                            int q = (16 * i) + (4 * j) + k - 21;
                            s.Append(Board[q] switch
                            {
                                EMPTY or POTENTIAL => "( )      ",
                                PLAYER => "(Y)      ",
                                MACHINE => "(M)      ",
                                _ => throw new Exception($"invalid space value {Board[q]}"),
                            });
                        }
                        s.Append("\n\n");
                    }
                    s.Append("\n\n");
                }
    
                Console.WriteLine(s.ToString());
            }
    
            #endregion
    
            /***********************************************************************
            /* Machine Move
            /**********************************************************************/
            #region MachineMove
    
            /// 
            /// Check all rows for a player win.
            ///
            /// A row indicates a player win if its sum = PLAYER * 4.
            ///
            /// Original BASIC: 720-780
            /// 
            /// whether the player won in any row
            private bool CheckPlayerWin()
            {
                for (int row = 0; row < 76; row++)
                {
                    if (RowSums[row] == (PLAYER * 4))
                    {
                        // Found player win!
                        Console.WriteLine("YOU WIN AS FOLLOWS");
                        DisplayRow(row);
                        return true;
                    }
                }
    
                // No player win found.
                return false;
            }
    
            /// 
            /// Check all rows for a row that the machine could move to to win
            ///  immediately.
            ///
            /// A row indicates a player could win immediately if it has three
            ///  machine moves already; that is, sum = MACHINE * 3.
            ///
            /// Original Basic: 790-920
            /// 
            /// 
            private bool CheckMachineWin()
            {
                for (int row = 0; row < 76; row++)
                {
                    if (RowSums[row] == (MACHINE * 3))
                    {
                        // Found a winning row!
                        for (int space = 0; space < 4; space++)
                        {
                            int move = RowsByPlane[row, space];
                            if (Board[move] == EMPTY)
                            {
                                // Found empty space in winning row; move there.
                                Board[move] = MACHINE;
                                Console.WriteLine($"MACHINE MOVES TO {IndexToCoord(move)} , AND WINS AS FOLLOWS");
                                DisplayRow(row);
                                return true;
                            }
                        }
                    }
                }
    
                // No winning row available.
                return false;
            }
    
            /// 
            /// Display the coordinates of a winning row.
            /// 
            /// index into RowsByPlane data
            private void DisplayRow(int row)
            {
                for (int space = 0; space < 4; space++)
                {
                    Console.Write($" {IndexToCoord(RowsByPlane[row, space])} ");
                }
                Console.WriteLine();
            }
    
            /// 
            /// Possible actions machine can take in a move. This helps replace the
            ///  complex GOTO logic from the original BASIC, which allowed the
            ///  program to jump from the machine's action to the end of the game.
            /// 
            private enum MachineAction
            {
                /// 
                /// Machine did not take any action.
                /// 
                None,
                /// 
                /// Machine made a move.
                /// 
                Move,
                /// 
                /// Machine either won, conceded, or found a draw.
                /// 
                End,
            }
    
            /// 
            /// Machine decides where to move on the board, and ends the game if
            ///  appropriate.
            ///
            /// The machine's AI tries to take the following actions (in order):
            ///
            ///  1. If the player has a row that will get them the win on their
            ///     next turn, block that row.
            ///  2. If the machine can trap the player (create two different rows
            ///     with three machine moves each that cannot be blocked by only a
            ///     single player move, create such a trap.
            ///  3. If the player can create a similar trap for the machine on
            ///     their next move, block the space where that trap would be
            ///     created.
            ///  4. Find a plane in the board that is well-populated by player
            ///     moves, and take a space in the first such plane.
            ///  5. Find the first open corner or center and move there.
            ///  6. Find the first open space and move there.
            ///
            /// If none of these actions are possible, then the board is entirely
            ///  full, and the game results in a draw.
            ///
            /// Original BASIC: start at 930
            /// 
            /// the action the machine took
            private MachineAction MachineMove()
            {
                // The actions the machine attempts to take, in order.
                var actions = new Func[]
                {
                    BlockPlayer,
                    MakePlayerTrap,
                    BlockMachineTrap,
                    MoveByPlane,
                    MoveCornerOrCenter,
                    MoveAnyOpenSpace,
                };
    
                foreach (var action in actions)
                {
                    // Try each action, moving to the next if nothing happens.
                    var actionResult = action();
                    if (actionResult != MachineAction.None)
                    {
                        // Not in original BASIC: check for draw after each machine
                        // move.
                        if (CheckDraw())
                        {
                            return DrawGame();
                        }
                        return actionResult;
                    }
                }
    
                // If we got here, all spaces are taken. Draw the game.
                return DrawGame();
            }
    
            /// 
            /// Block a row with three spaces already taken by the player.
            ///
            /// Original BASIC: 930-1010
            /// 
            /// 
            /// Move if the machine blocked,
            /// None otherwise
            /// 
            private MachineAction BlockPlayer()
            {
                for (int row = 0; row < 76; row++)
                {
                    if (RowSums[row] == (PLAYER * 3))
                    {
                        // Found a row to block on!
                        for (int space = 0; space < 4; space++)
                        {
                            if (Board[RowsByPlane[row, space]] == EMPTY)
                            {
                                // Take the remaining empty space.
                                Board[RowsByPlane[row, space]] = MACHINE;
                                Console.WriteLine($"NICE TRY. MACHINE MOVES TO {IndexToCoord(RowsByPlane[row, space])}");
                                return MachineAction.Move;
                            }
                        }
                    }
                }
    
                // Didn't find a row to block on.
                return MachineAction.None;
            }
    
            /// 
            /// Create a trap for the player if possible. A trap can be created if
            ///  moving to a space on the board results in two different rows having
            ///  three MACHINE spaces, with the remaining space not shared between
            ///  the two rows. The player can only block one of these traps, so the
            ///  machine will win.
            ///
            /// If a player trap is not possible, but a row is found that is
            ///  particularly advantageous for the machine to move to, the machine
            ///  will try and move to a plane edge in that row.
            ///
            /// Original BASIC: 1300-1480
            ///
            /// Lines 1440/50 of the BASIC call 2360 (MovePlaneEdge). Because it
            ///  goes to this code only after it has found an open space marked as
            ///  potential, it cannot reach line 2440 of that code, as that is only
            ///  reached if an open space failed to be found in the row on which
            ///  that code was called.
            /// 
            /// 
            /// Move if a trap was created,
            /// End if the machine conceded,
            /// None otherwise
            /// 
            private MachineAction MakePlayerTrap()
            {
                for (int row = 0; row < 76; row++)
                {
                    // Refresh row sum, since new POTENTIALs might have changed it.
                    var rowSum = RefreshRowSum(row);
    
                    // Machine has moved in this row twice, and player has not moved
                    // in this row.
                    if (rowSum >= (MACHINE * 2) && rowSum < (MACHINE * 2) + 1)
                    {
                        // Machine has no potential moves yet in this row.
                        if (rowSum == (MACHINE * 2))
                        {
                            for (int space = 0; space < 4; space++)
                            {
                                // Empty space can potentially be used to create a
                                // trap.
                                if (Board[RowsByPlane[row, space]] == EMPTY)
                                {
                                    Board[RowsByPlane[row, space]] = POTENTIAL;
                                }
                            }
                        }
                        // Machine has already found a potential move in this row,
                        // so a trap can be created with another row.
                        else
                        {
                            return MakeOrBlockTrap(row);
                        }
                    }
                }
    
                // No player traps can be made.
                RefreshRowSums();
    
                for (int row = 0; row < 76; row++)
                {
                    // A row may be particularly advantageous for the machine to
                    // move to at this point; this is the case if a row is entirely
                    // filled with POTENTIAL or has one MACHINE and others
                    // POTENTIAL. Such rows may help set up trapping opportunities.
                    if (RowSums[row] == (POTENTIAL * 4) || RowSums[row] == MACHINE + (POTENTIAL * 3))
                    {
                        // Try moving to a plane edge in an advantageous row.
                        return MovePlaneEdge(row, POTENTIAL);
                    }
                }
    
                // No spaces found that are particularly advantageous to machine.
                ClearPotentialMoves();
                return MachineAction.None;
            }
    
            /// 
            /// Block a trap that the player could create for the machine on their
            ///  next turn.
            ///
            /// If there are no player traps to block, but a row is found that is
            ///  particularly advantageous for the player to move to, the machine
            ///  will try and move to a plane edge in that row.
            ///
            /// Original BASIC: 1030-1190
            ///
            /// Lines 1160/1170 of the BASIC call 2360 (MovePlaneEdge). As with
            ///  MakePlayerTrap, because it goes to this code only after it has
            ///  found an open space marked as potential, it cannot reach line 2440
            ///  of that code, as that is only reached if an open space failed to be
            ///  found in the row on which that code was called.
            /// 
            /// 
            /// Move if a trap was created,
            /// End if the machine conceded,
            /// None otherwise
            /// 
            private MachineAction BlockMachineTrap()
            {
                for (int i = 0; i < 76; i++)
                {
                    // Refresh row sum, since new POTENTIALs might have changed it.
                    var rowSum = RefreshRowSum(i);
    
                    // Player has moved in this row twice, and machine has not moved
                    // in this row.
                    if (rowSum >= (PLAYER * 2) && rowSum < (PLAYER * 2) + 1)
                    {
                        // Machine has no potential moves yet in this row.
                        if (rowSum == (PLAYER * 2))
                        {
                            for (int j = 0; j < 4; j++)
                            {
                                if (Board[RowsByPlane[i, j]] == EMPTY)
                                {
                                    Board[RowsByPlane[i, j]] = POTENTIAL;
                                }
                            }
                        }
                        // Machine has already found a potential move in this row,
                        // so a trap can be created with another row by the player.
                        // Move to block.
                        else
                        {
                            return MakeOrBlockTrap(i);
                        }
                    }
                }
    
                // No player traps to block found.
                RefreshRowSums();
    
                for (int row = 0; row < 76; row++)
                {
                    // A row may be particularly advantageous for the player to move
                    // to at this point, indicated by a row containing all POTENTIAL
                    // moves or one PLAYER and rest POTENTIAL. Such rows may aid in
                    // in the later creation of traps.
                    if (RowSums[row] == (POTENTIAL * 4) || RowSums[row] == PLAYER + (POTENTIAL * 3))
                    {
                        // Try moving to a plane edge in an advantageous row.
                        return MovePlaneEdge(row, POTENTIAL);
                    }
                }
    
                // No spaces found that are particularly advantageous to the player.
                return MachineAction.None;
            }
    
            /// 
            /// Either make a trap for the player or block a trap the player could
            ///  create on their next turn.
            ///
            /// Unclear how this method could possibly end with a concession; it
            ///  seems it can only be called if the row contains a potential move.
            ///
            /// Original BASIC: 2230-2350
            /// 
            /// the row containing the space to move to
            /// 
            /// Move if the machine moved,
            /// End if the machine conceded
            /// 
            private MachineAction MakeOrBlockTrap(int row)
            {
                for (int space = 0; space < 4; space++)
                {
                    if (Board[RowsByPlane[row, space]] == POTENTIAL)
                    {
                        Board[RowsByPlane[row, space]] = MACHINE;
    
                        // Row sum indicates we're blocking a player trap.
                        if (RowSums[row] < MACHINE)
                        {
                            Console.Write("YOU FOX.  JUST IN THE NICK OF TIME, ");
                        }
                        // Row sum indicates we're completing a machine trap.
                        else
                        {
                            Console.Write("LET'S SEE YOU GET OUT OF THIS:  ");
                        }
    
                        Console.WriteLine($"MACHINE MOVES TO {IndexToCoord(RowsByPlane[row, space])}");
    
                        return MachineAction.Move;
                    }
                }
    
                // Unclear how this can be reached.
                Console.WriteLine("MACHINE CONCEDES THIS GAME.");
                return MachineAction.End;
            }
    
            /// 
            /// Find a satisfactory plane on the board and move to one if that
            ///  plane's plane edges.
            ///
            /// A plane on the board is satisfactory if it meets the following
            ///  conditions:
            ///     1. Player has made exactly 4 moves on the plane.
            ///     2. Machine has made either 0 or one moves on the plane.
            ///  Such a plane is one that the player could likely use to form traps.
            ///
            /// Original BASIC: 1830-2020
            ///
            /// Line 1990 of the original basic calls 2370 (MovePlaneEdge). Only on
            ///  this call to MovePlaneEdge can line 2440 of that method be reached,
            ///  which surves to help this method iterate through the rows of a
            ///  plane.
            /// 
            /// 
            /// Move if a move in a plane was found,
            /// None otherwise
            /// 
            private MachineAction MoveByPlane()
            {
                // For each plane in the cube...
                for (int plane = 1; plane <= 18; plane++)
                {
                    double planeSum = PlaneSum(plane);
    
                    // Check that plane sum satisfies condition.
                    const double P4 = PLAYER * 4;
                    const double P4_M1 = (PLAYER * 4) + MACHINE;
                    if (
                        (planeSum >= P4 && planeSum < P4 + 1) ||
                        (planeSum >= P4_M1 && planeSum < P4_M1 + 1)
                    )
                    {
                        // Try to move to plane edges in each row of plane
                        // First, check for plane edges marked as POTENTIAL.
                        for (int row = (4 * plane) - 4; row < (4 * plane); row++)
                        {
                            var moveResult = MovePlaneEdge(row, POTENTIAL);
                            if (moveResult != MachineAction.None)
                            {
                                return moveResult;
                            }
                        }
    
                        // If no POTENTIAL plane edge found, look for an EMPTY one.
                        for (int row = (4 * plane) - 4; row < (4 * plane); row++)
                        {
                            var moveResult = MovePlaneEdge(row, EMPTY);
                            if (moveResult != MachineAction.None)
                            {
                                return moveResult;
                            }
                        }
                    }
                }
    
                // No satisfactory planes with open plane edges found.
                ClearPotentialMoves();
                return MachineAction.None;
            }
    
            /// 
            /// Given a row, move to the first space in that row that:
            ///  1. is a plane edge, and
            ///  2. has the given value in Board
            ///
            /// Plane edges are any spaces on a plane with one face exposed. The AI
            ///  prefers to move to these spaces before others, presumably
            ///  because they are powerful moves: a plane edge is contained on 3-4
            ///  winning rows of the cube.
            ///
            /// Original BASIC: 2360-2490
            ///
            /// In the original BASIC, this code is pointed to from three different
            ///  locations by GOTOs:
            ///  - 1440/50, or MakePlayerTrap;
            ///  - 1160/70, or BlockMachineTrap; and
            ///  - 1990, or MoveByPlane.
            /// At line 2440, this code jumps back to line 2000, which is in
            ///  MoveByPlane. This makes it appear as though calling MakePlayerTrap
            ///  or BlockPlayerTrap in the BASIC could jump into the middle of the
            ///  MoveByPlane method; were this to happen, not all of MoveByPlane's
            ///  variables would be defined! However, the program logic prevents
            ///  this from ever occurring; see each method's description for why
            ///  this is the case.
            /// 
            /// the row to try to move to
            /// 
            /// what value the space to move to should have in Board
            /// 
            /// 
            /// Move if a plane edge piece in the row with the given spaceValue was
            /// found,
            /// None otherwise
            /// 
            private MachineAction MovePlaneEdge(int row, double spaceValue)
            {
                // Given a row, we want to find the plane edge pieces in that row.
                // We know that each row is part of a plane, and that the first
                // and last rows of the plane are on the plane edge, while the
                // other two rows are in the middle. If we know whether a row is an
                // edge or middle, we can determine which spaces in that row are
                // plane edges.
                //
                // Below is a birds-eye view of a plane in the cube, with rows
                // oriented horizontally:
                //
                //   row 0: ( ) (1) (2) ( )
                //   row 1: (0) ( ) ( ) (3)
                //   row 2: (0) ( ) ( ) (3)
                //   row 3: ( ) (1) (2) ( )
                //
                // The plane edge pieces have their row indices marked. The pattern
                // above shows that:
                //
                //  if row == 0 | 3, plane edge spaces = [1, 2]
                //  if row == 1 | 2, plane edge spaces = [0, 3]
    
                // The below condition replaces the following BASIC code (2370):
                //
                //  I-(INT(I/4)*4)>1
                //
                // which in C# would be:
                //
                //
                // int a = i - (i / 4) * 4 <= 1)
                //     ? 1
                //     : 2;
                //
                // In the above, i is the one-indexed row in RowsByPlane.
                //
                // This condition selects a different a value based on whether the
                // given row is on the edge or middle of its plane.
                int a = (row % 4) switch
                {
                    0 or 3 => 1,  // row is on edge of plane
                    1 or 2 => 2,  // row is in middle of plane
                    _ => throw new Exception($"unreachable ({row % 4})"),
                };
    
                // Iterate through plane edge pieces of the row.
                //
                //  if a = 1 (row is edge), iterate through [0, 3]
                //  if a = 2 (row is middle), iterate through [1, 2]
                for (int space = a - 1; space <= 4 - a; space += 5 - (2 * a))
                {
                    if (Board[RowsByPlane[row, space]] == spaceValue)
                    {
                        // Found a plane edge to take!
                        Board[RowsByPlane[row, space]] = MACHINE;
                        Console.WriteLine($"MACHINE TAKES {IndexToCoord(RowsByPlane[row, space])}");
                        return MachineAction.Move;
                    }
                }
    
                // No valid corner edge to take.
                return MachineAction.None;
            }
    
            /// 
            /// Find the first open corner or center in the board and move there.
            ///
            /// Original BASIC: 1200-1290
            ///
            /// This is the only place where the Z variable from the BASIC code is
            ///  used; here it is implied in the for loop.
            /// 
            /// 
            /// Move if an open corner/center was found and moved to,
            /// None otherwise
            /// 
            private MachineAction MoveCornerOrCenter()
            {
                foreach (int space in CornersAndCenters)
                {
                    if (Board[space] == EMPTY)
                    {
                        Board[space] = MACHINE;
                        Console.WriteLine($"MACHINE MOVES TO {IndexToCoord(space)}");
                        return MachineAction.Move;
                    }
                }
    
                return MachineAction.None;
            }
    
            /// 
            /// Find the first open space in the board and move there.
            ///
            /// Original BASIC: 1720-1800
            /// 
            /// 
            /// Move if an open space was found and moved to,
            /// None otherwise
            /// 
            private MachineAction MoveAnyOpenSpace()
            {
                for (int row = 0; row < 64; row++)
                {
                    if (Board[row] == EMPTY)
                    {
                        Board[row] = MACHINE;
                        Console.WriteLine($"MACHINE LIKES {IndexToCoord(row)}");
                        return MachineAction.Move;
                    }
                }
                return MachineAction.None;
            }
    
            /// 
            /// Draw the game in the event that there are no open spaces.
            ///
            /// Original BASIC: 1810-1820
            /// 
            /// End
            private MachineAction DrawGame()
            {
                Console.WriteLine("THIS GAME IS A DRAW.");
                return MachineAction.End;
            }
    
            #endregion
    
            /***********************************************************************
            /* Helpers
            /**********************************************************************/
            #region Helpers
    
            /// 
            /// Attempt to transform a cube coordinate to an index into Board.
            ///
            /// A valid cube coordinate is a three-digit number, where each digit
            ///  of the number X satisfies 1 <= X <= 4.
            ///
            /// Examples:
            ///  111 -> 0
            ///  444 -> 63
            ///  232 -> 35
            ///
            /// If the coord provided is not valid, the transformation fails.
            ///
            /// The conversion from coordinate to index is essentially a conversion
            ///  between base 4 and base 10.
            ///
            /// Original BASIC: 525-580
            ///
            /// This method fixes a bug in the original BASIC (525-526), which only
            ///  checked whether the given coord satisfied 111 <= coord <= 444. This
            ///  allows invalid coordinates such as 199 and 437, whose individual
            ///  digits are out of range.
            /// 
            /// cube coordinate (e.g. "111", "342")
            /// trasnformation output
            /// 
            /// true if the transformation was successful, false otherwise
            /// 
            private static bool TryCoordToIndex(int coord, out int index)
            {
                // parse individual digits, subtract 1 to get base 4 number
                var hundreds = (coord / 100) - 1;
                var tens = ((coord % 100) / 10) - 1;
                var ones = (coord % 10) - 1;
    
                // bounds check for each digit
                foreach (int digit in new int[] { hundreds, tens, ones })
                {
                    if (digit < 0 || digit > 3)
                    {
                        index = -1;
                        return false;
                    }
                }
    
                // conversion from base 4 to base 10
                index = (16 * hundreds) + (4 * tens) + ones;
                return true;
            }
    
            /// 
            /// Transform a Board index into a valid cube coordinate.
            ///
            /// Examples:
            ///  0 -> 111
            ///  63 -> 444
            ///  35 -> 232
            ///
            /// The conversion from index to coordinate is essentially a conversion
            ///  between base 10 and base 4.
            ///
            /// Original BASIC: 1570-1610
            /// 
            /// Board index
            /// the corresponding cube coordinate
            private static int IndexToCoord(int index)
            {
                // check that index is valid
                if (index < 0 || index > 63)
                {
                    // runtime exception; all uses of this method are with
                    // indices provided by the program, so this should never fail
                    throw new Exception($"index {index} is out of range");
                }
    
                // convert to base 4, add 1 to get cube coordinate
                var hundreds = (index / 16) + 1;
                var tens = ((index % 16) / 4) + 1;
                var ones = (index % 4) + 1;
    
                // concatenate digits
                int coord = (hundreds * 100) + (tens * 10) + ones;
                return coord;
            }
    
            /// 
            /// Refresh the values in RowSums to account for any changes.
            ///
            /// Original BASIC: 1640-1710
            /// 
            private void RefreshRowSums()
            {
                for (var row = 0; row < 76; row++)
                {
                    RefreshRowSum(row);
                }
            }
    
            /// 
            /// Refresh a row in RowSums to reflect changes.
            /// 
            /// row in RowSums to refresh
            /// row sum after refresh
            private double RefreshRowSum(int row)
            {
                double rowSum = 0;
                for (int space = 0; space < 4; space++)
                {
                    rowSum += Board[RowsByPlane[row, space]];
                }
                RowSums[row] = rowSum;
                return rowSum;
            }
    
            /// 
            /// Calculate the sum of spaces in one of the 18 cube planes in RowSums.
            ///
            /// Original BASIC: 1840-1890
            /// 
            /// the desired plane
            /// sum of spaces in plane
            private double PlaneSum(int plane)
            {
                double planeSum = 0;
                for (int row = (4 * (plane - 1)); row < (4 * plane); row++)
                {
                    for (int space = 0; space < 4; space++)
                    {
                        planeSum += Board[RowsByPlane[row, space]];
                    }
                }
                return planeSum;
            }
    
            /// 
            /// Check whether the board is in a draw state, that is all spaces are
            ///  full and neither the player nor the machine has won.
            ///
            /// The original BASIC contains a bug that if the player moves first, a
            ///  draw will go undetected. An example series of player inputs
            ///  resulting in such a draw (assuming player goes first):
            ///
            ///  114, 414, 144, 444, 122, 221, 112, 121,
            ///  424, 332, 324, 421, 231, 232, 244, 311,
            ///  333, 423, 331, 134, 241, 243, 143, 413,
            ///  142, 212, 314, 341, 432, 412, 431, 442
            /// 
            /// whether the game is a draw
            private bool CheckDraw()
            {
                for (var i = 0; i < 64; i++)
                {
                    if (Board[i] != PLAYER && Board[i] != MACHINE)
                    {
                        return false;
                    }
                }
    
                RefreshRowSums();
    
                for (int row = 0; row < 76; row++)
                {
                    var rowSum = RowSums[row];
                    if (rowSum == PLAYER * 4 || rowSum == MACHINE * 4)
                    {
                        return false;
                    }
                }
    
    
                return true;
            }
    
            /// 
            /// Reset POTENTIAL spaces in Board to EMPTY.
            ///
            /// Original BASIC: 2500-2540
            /// 
            private void ClearPotentialMoves()
            {
                for (var i = 0; i < 64; i++)
                {
                    if (Board[i] == POTENTIAL)
                    {
                        Board[i] = EMPTY;
                    }
                }
            }
    
            /// 
            /// Reset all spaces in Board to EMPTY.
            ///
            /// Original BASIC: 400-420
            /// 
            private void ClearBoard()
            {
                for (var i = 0; i < 64; i++)
                {
                    Board[i] = EMPTY;
                }
            }
    
            #endregion
        }
    }
    
    
    ================================================
    FILE: 88_3-D_Tic-Tac-Toe/csharp/QubicData.cs
    ================================================
    namespace ThreeDTicTacToe
    {
        /// 
        /// Data in this class was originally given by the following DATA section in
        /// the BASIC program:
        ///
        /// 2030 DATA 1,49,52,4,13,61,64,16,22,39,23,38,26,42,27,43
        /// 2040 DATA 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20
        /// 2050 DATA 21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38
        /// 2060 DATA 39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56
        /// 2070 DATA 57,58,59,60,61,62,63,64
        /// 2080 DATA 1,17,33,49,5,21,37,53,9,25,41,57,13,29,45,61
        /// 2090 DATA 2,18,34,50,6,22,38,54,10,26,42,58,14,30,46,62
        /// 2100 DATA 3,19,35,51,7,23,39,55,11,27,43,59,15,31,47,63
        /// 2110 DATA 4,20,36,52,8,24,40,56,12,28,44,60,16,32,48,64
        /// 2120 DATA 1,5,9,13,17,21,25,29,33,37,41,45,49,53,57,61
        /// 2130 DATA 2,6,10,14,18,22,26,30,34,38,42,46,50,54,58,62
        /// 2140 DATA 3,7,11,15,19,23,27,31,35,39,43,47,51,55,59,63
        /// 2150 DATA 4,8,12,16,20,24,28,32,36,40,44,48,52,56,60,64
        /// 2160 DATA 1,6,11,16,17,22,27,32,33,38,43,48,49,54,59,64
        /// 2170 DATA 13,10,7,4,29,26,23,20,45,42,39,36,61,58,55,52
        /// 2180 DATA 1,21,41,61,2,22,42,62,3,23,43,63,4,24,44,64
        /// 2190 DATA 49,37,25,13,50,38,26,14,51,39,27,15,52,40,28,16
        /// 2200 DATA 1,18,35,52,5,22,39,56,9,26,43,60,13,30,47,64
        /// 2210 DATA 49,34,19,4,53,38,23,8,57,42,27,12,61,46,31,16
        /// 2220 DATA 1,22,43,64,16,27,38,49,4,23,42,61,13,26,39,52
        ///
        /// In short, each number is an index into the board. The data in this class
        /// is zero-indexed, as opposed to the original data which was one-indexed.
        /// 
        internal static class QubicData
        {
            /// 
            /// The corners and centers of the Qubic board. They correspond to the
            ///  following coordinates:
            ///
            /// [
            ///     111, 411, 414, 114, 141, 441, 444, 144,
            ///     222, 323, 223, 322, 232, 332, 233, 333
            /// ]
            /// 
            public static readonly int[] CornersAndCenters = new int[16]
            {
               //     (X)      ( )      ( )      (X)
               //         ( )      ( )      ( )      ( )
               //             ( )      ( )      ( )      ( )
               //                 (X)      ( )      ( )      (X)
    
               //     ( )      ( )      ( )      ( )
               //         ( )      (X)      (X)      ( )
               //             ( )      (X)      (X)      ( )
               //                 ( )      ( )      ( )      ( )
    
               //     ( )      ( )      ( )      ( )
               //         ( )      (X)      (X)      ( )
               //             ( )      (X)      (X)      ( )
               //                 ( )      ( )      ( )      ( )
    
               //     (X)      ( )      ( )      (X)
               //         ( )      ( )      ( )      ( )
               //             ( )      ( )      ( )      ( )
               //                 (X)      ( )      ( )      (X)
    
                0,48,51,3,12,60,63,15,21,38,22,37,25,41,26,42
            };
    
            /// 
            /// A list of all "winning" rows in the Qubic board; that is, sets of
            ///  four spaces that, if filled entirely by the player (or machine),
            ///  would result in a win.
            ///
            /// Each group of four rows in the list corresponds to a plane in the
            ///  cube, and each plane is organized so that the first and last rows
            ///  are on the plane's edges, while the second and third rows are in
            ///  the middle of the plane. The only exception is the last group of
            ///  rows, which contains the corners and centers rather than a plane.
            ///
            /// The order of the rows in this list is key to how the Qubic AI
            ///  decides its next move.
            /// 
            public static readonly int[,] RowsByPlane = new int[76, 4]
            {
               //     (1)      (1)      (1)      (1)
               //         (2)      (2)      (2)      (2)
               //             (3)      (3)      (3)      (3)
               //                 (4)      (4)      (4)      (4)
    
               //     ( )      ( )      ( )      ( )
               //         ( )      ( )      ( )      ( )
               //             ( )      ( )      ( )      ( )
               //                 ( )      ( )      ( )      ( )
    
               //     ( )      ( )      ( )      ( )
               //         ( )      ( )      ( )      ( )
               //             ( )      ( )      ( )      ( )
               //                 ( )      ( )      ( )      ( )
    
               //     ( )      ( )      ( )      ( )
               //         ( )      ( )      ( )      ( )
               //             ( )      ( )      ( )      ( )
               //                 ( )      ( )      ( )      ( )
    
               { 0, 1, 2, 3,  },
               { 4, 5, 6, 7,  },
               { 8, 9, 10,11, },
               { 12,13,14,15, },
    
               //     ( )      ( )      ( )      ( )
               //         ( )      ( )      ( )      ( )
               //             ( )      ( )      ( )      ( )
               //                 ( )      ( )      ( )      ( )
    
               //     (1)      (1)      (1)      (1)
               //         (2)      (2)      (2)      (2)
               //             (3)      (3)      (3)      (3)
               //                 (4)      (4)      (4)      (4)
    
               //     ( )      ( )      ( )      ( )
               //         ( )      ( )      ( )      ( )
               //             ( )      ( )      ( )      ( )
               //                 ( )      ( )      ( )      ( )
    
               //     ( )      ( )      ( )      ( )
               //         ( )      ( )      ( )      ( )
               //             ( )      ( )      ( )      ( )
               //                 ( )      ( )      ( )      ( )
    
               { 16,17,18,19, },
               { 20,21,22,23, },
               { 24,25,26,27, },
               { 28,29,30,31, },
    
               //     ( )      ( )      ( )      ( )
               //         ( )      ( )      ( )      ( )
               //             ( )      ( )      ( )      ( )
               //                 ( )      ( )      ( )      ( )
    
               //     ( )      ( )      ( )      ( )
               //         ( )      ( )      ( )      ( )
               //             ( )      ( )      ( )      ( )
               //                 ( )      ( )      ( )      ( )
    
               //     (1)      (1)      (1)      (1)
               //         (2)      (2)      (2)      (2)
               //             (3)      (3)      (3)      (3)
               //                 (4)      (4)      (4)      (4)
    
               //     ( )      ( )      ( )      ( )
               //         ( )      ( )      ( )      ( )
               //             ( )      ( )      ( )      ( )
               //                 ( )      ( )      ( )      ( )
    
               { 32,33,34,35, },
               { 36,37,38,39, },
               { 40,41,42,43, },
               { 44,45,46,47, },
    
               //     ( )      ( )      ( )      ( )
               //         ( )      ( )      ( )      ( )
               //             ( )      ( )      ( )      ( )
               //                 ( )      ( )      ( )      ( )
    
               //     ( )      ( )      ( )      ( )
               //         ( )      ( )      ( )      ( )
               //             ( )      ( )      ( )      ( )
               //                 ( )      ( )      ( )      ( )
    
               //     ( )      ( )      ( )      ( )
               //         ( )      ( )      ( )      ( )
               //             ( )      ( )      ( )      ( )
               //                 ( )      ( )      ( )      ( )
    
               //     (1)      (1)      (1)      (1)
               //         (2)      (2)      (2)      (2)
               //             (3)      (3)      (3)      (3)
               //                 (4)      (4)      (4)      (4)
    
               { 48,49,50,51, },
               { 52,53,54,55, },
               { 56,57,58,59, },
               { 60,61,62,63, },
    
               //     (1)      ( )      ( )      ( )
               //         (2)      ( )      ( )      ( )
               //             (3)      ( )      ( )      ( )
               //                 (4)      ( )      ( )      ( )
    
               //     (1)      ( )      ( )      ( )
               //         (2)      ( )      ( )      ( )
               //             (3)      ( )      ( )      ( )
               //                 (4)      ( )      ( )      ( )
    
               //     (1)      ( )      ( )      ( )
               //         (2)      ( )      ( )      ( )
               //             (3)      ( )      ( )      ( )
               //                 (4)      ( )      ( )      ( )
    
               //     (1)      ( )      ( )      ( )
               //         (2)      ( )      ( )      ( )
               //             (3)      ( )      ( )      ( )
               //                 (4)      ( )      ( )      ( )
    
               { 0, 16,32,48, },
               { 4, 20,36,52, },
               { 8, 24,40,56, },
               { 12,28,44,60, },
    
               //     ( )      (1)      ( )      ( )
               //         ( )      (2)      ( )      ( )
               //             ( )      (3)      ( )      ( )
               //                 ( )      (4)      ( )      ( )
    
               //     ( )      (1)      ( )      ( )
               //         ( )      (2)      ( )      ( )
               //             ( )      (3)      ( )      ( )
               //                 ( )      (4)      ( )      ( )
    
               //     ( )      (1)      ( )      ( )
               //         ( )      (2)      ( )      ( )
               //             ( )      (3)      ( )      ( )
               //                 ( )      (4)      ( )      ( )
    
               //     ( )      (1)      ( )      ( )
               //         ( )      (2)      ( )      ( )
               //             ( )      (3)      ( )      ( )
               //                 ( )      (4)      ( )      ( )
    
               { 1, 17,33,49, },
               { 5, 21,37,53, },
               { 9, 25,41,57, },
               { 13,29,45,61, },
    
               //     ( )      ( )      (1)      ( )
               //         ( )      ( )      (2)      ( )
               //             ( )      ( )      (3)      ( )
               //                 ( )      ( )      (4)      ( )
    
               //     ( )      ( )      (1)      ( )
               //         ( )      ( )      (2)      ( )
               //             ( )      ( )      (3)      ( )
               //                 ( )      ( )      (4)      ( )
    
               //     ( )      ( )      (1)      ( )
               //         ( )      ( )      (2)      ( )
               //             ( )      ( )      (3)      ( )
               //                 ( )      ( )      (4)      ( )
    
               //     ( )      ( )      (1)      ( )
               //         ( )      ( )      (2)      ( )
               //             ( )      ( )      (3)      ( )
               //                 ( )      ( )      (4)      ( )
    
               { 2, 18,34,50, },
               { 6, 22,38,54, },
               { 10,26,42,58, },
               { 14,30,46,62, },
    
               //     ( )      ( )      ( )      (1)
               //         ( )      ( )      ( )      (2)
               //             ( )      ( )      ( )      (3)
               //                 ( )      ( )      ( )      (4)
    
               //     ( )      ( )      ( )      (1)
               //         ( )      ( )      ( )      (2)
               //             ( )      ( )      ( )      (3)
               //                 ( )      ( )      ( )      (4)
    
               //     ( )      ( )      ( )      (1)
               //         ( )      ( )      ( )      (2)
               //             ( )      ( )      ( )      (3)
               //                 ( )      ( )      ( )      (4)
    
               //     ( )      ( )      ( )      (1)
               //         ( )      ( )      ( )      (2)
               //             ( )      ( )      ( )      (3)
               //                 ( )      ( )      ( )      (4)
    
               { 3, 19,35,51, },
               { 7, 23,39,55, },
               { 11,27,43,59, },
               { 15,31,47,63, },
    
               //     (1)      ( )      ( )      ( )
               //         (1)      ( )      ( )      ( )
               //             (1)      ( )      ( )      ( )
               //                 (1)      ( )      ( )      ( )
    
               //     (2)      ( )      ( )      ( )
               //         (2)      ( )      ( )      ( )
               //             (2)      ( )      ( )      ( )
               //                 (2)      ( )      ( )      ( )
    
               //     (3)      ( )      ( )      ( )
               //         (3)      ( )      ( )      ( )
               //             (3)      ( )      ( )      ( )
               //                 (3)      ( )      ( )      ( )
    
               //     (4)      ( )      ( )      ( )
               //         (4)      ( )      ( )      ( )
               //             (4)      ( )      ( )      ( )
               //                 (4)      ( )      ( )      ( )
    
               { 0, 4, 8, 12, },
               { 16,20,24,28, },
               { 32,36,40,44, },
               { 48,52,56,60, },
    
               //     ( )      (1)      ( )      ( )
               //         ( )      (1)      ( )      ( )
               //             ( )      (1)      ( )      ( )
               //                 ( )      (1)      ( )      ( )
    
               //     ( )      (2)      ( )      ( )
               //         ( )      (2)      ( )      ( )
               //             ( )      (2)      ( )      ( )
               //                 ( )      (2)      ( )      ( )
    
               //     ( )      (3)      ( )      ( )
               //         ( )      (3)      ( )      ( )
               //             ( )      (3)      ( )      ( )
               //                 ( )      (3)      ( )      ( )
    
               //     ( )      (4)      ( )      ( )
               //         ( )      (4)      ( )      ( )
               //             ( )      (4)      ( )      ( )
               //                 ( )      (4)      ( )      ( )
    
               { 1, 5, 9, 13, },
               { 17,21,25,29, },
               { 33,37,41,45, },
               { 49,53,57,61, },
    
               //     ( )      ( )      (1)      ( )
               //         ( )      ( )      (1)      ( )
               //             ( )      ( )      (1)      ( )
               //                 ( )      ( )      (1)      ( )
    
               //     ( )      ( )      (2)      ( )
               //         ( )      ( )      (2)      ( )
               //             ( )      ( )      (2)      ( )
               //                 ( )      ( )      (2)      ( )
    
               //     ( )      ( )      (3)      ( )
               //         ( )      ( )      (3)      ( )
               //             ( )      ( )      (3)      ( )
               //                 ( )      ( )      (3)      ( )
    
               //     ( )      ( )      (4)      ( )
               //         ( )      ( )      (4)      ( )
               //             ( )      ( )      (4)      ( )
               //                 ( )      ( )      (4)      ( )
    
               { 2, 6, 10,14, },
               { 18,22,26,30, },
               { 34,38,42,46, },
               { 50,54,58,62, },
    
               //     ( )      ( )      ( )      (1)
               //         ( )      ( )      ( )      (1)
               //             ( )      ( )      ( )      (1)
               //                 ( )      ( )      ( )      (1)
    
               //     ( )      ( )      ( )      (2)
               //         ( )      ( )      ( )      (2)
               //             ( )      ( )      ( )      (2)
               //                 ( )      ( )      ( )      (2)
    
               //     ( )      ( )      ( )      (3)
               //         ( )      ( )      ( )      (3)
               //             ( )      ( )      ( )      (3)
               //                 ( )      ( )      ( )      (3)
    
               //     ( )      ( )      ( )      (4)
               //         ( )      ( )      ( )      (4)
               //             ( )      ( )      ( )      (4)
               //                 ( )      ( )      ( )      (4)
    
               { 3, 7, 11,15, },
               { 19,23,27,31, },
               { 35,39,43,47, },
               { 51,55,59,63, },
    
               //     (1)      ( )      ( )      ( )
               //         ( )      (1)      ( )      ( )
               //             ( )      ( )      (1)      ( )
               //                 ( )      ( )      ( )      (1)
    
               //     (2)      ( )      ( )      ( )
               //         ( )      (2)      ( )      ( )
               //             ( )      ( )      (2)      ( )
               //                 ( )      ( )      ( )      (2)
    
               //     (3)      ( )      ( )      ( )
               //         ( )      (3)      ( )      ( )
               //             ( )      ( )      (3)      ( )
               //                 ( )      ( )      ( )      (3)
    
               //     (4)      ( )      ( )      ( )
               //         ( )      (4)      ( )      ( )
               //             ( )      ( )      (4)      ( )
               //                 ( )      ( )      ( )      (4)
    
               { 0, 5, 10,15, },
               { 16,21,26,31, },
               { 32,37,42,47, },
               { 48,53,58,63, },
    
               //     ( )      ( )      ( )      (1)
               //         ( )      ( )      (1)      ( )
               //             ( )      (1)      ( )      ( )
               //                 (1)      ( )      ( )      ( )
    
               //     ( )      ( )      ( )      (2)
               //         ( )      ( )      (2)      ( )
               //             ( )      (2)      ( )      ( )
               //                 (2)      ( )      ( )      ( )
    
               //     ( )      ( )      ( )      (3)
               //         ( )      ( )      (3)      ( )
               //             ( )      (3)      ( )      ( )
               //                 (3)      ( )      ( )      ( )
    
               //     ( )      ( )      ( )      (4)
               //         ( )      ( )      (4)      ( )
               //             ( )      (4)      ( )      ( )
               //                 (4)      ( )      ( )      ( )
    
               { 12,9, 6, 3,  },
               { 28,25,22,19, },
               { 44,41,38,35, },
               { 60,57,54,51, },
    
               //     (1)      (2)      (3)      (4)
               //         ( )      ( )      ( )      ( )
               //             ( )      ( )      ( )      ( )
               //                 ( )      ( )      ( )      ( )
    
               //     ( )      ( )      ( )      ( )
               //         (1)      (2)      (3)      (4)
               //             ( )      ( )      ( )      ( )
               //                 ( )      ( )      ( )      ( )
    
               //     ( )      ( )      ( )      ( )
               //         ( )      ( )      ( )      ( )
               //             (1)      (2)      (3)      (4)
               //                 ( )      ( )      ( )      ( )
    
               //     ( )      ( )      ( )      ( )
               //         ( )      ( )      ( )      ( )
               //             ( )      ( )      ( )      ( )
               //                 (1)      (2)      (3)      (4)
    
               { 0, 20,40,60, },
               { 1, 21,41,61, },
               { 2, 22,42,62, },
               { 3, 23,43,63, },
    
               //     ( )      ( )      ( )      ( )
               //         ( )      ( )      ( )      ( )
               //             ( )      ( )      ( )      ( )
               //                 (1)      (2)      (3)      (4)
    
               //     ( )      ( )      ( )      ( )
               //         ( )      ( )      ( )      ( )
               //             (1)      (2)      (3)      (4)
               //                 ( )      ( )      ( )      ( )
    
               //     ( )      ( )      ( )      ( )
               //         (1)      (2)      (3)      (4)
               //             ( )      ( )      ( )      ( )
               //                 ( )      ( )      ( )      ( )
    
               //     (1)      (2)      (3)      (4)
               //         ( )      ( )      ( )      ( )
               //             ( )      ( )      ( )      ( )
               //                 ( )      ( )      ( )      ( )
    
               { 48,36,24,12, },
               { 49,37,25,13, },
               { 50,38,26,14, },
               { 51,39,27,15, },
    
               //     (1)      ( )      ( )      ( )
               //         (2)      ( )      ( )      ( )
               //             (3)      ( )      ( )      ( )
               //                 (4)      ( )      ( )      ( )
    
               //     ( )      (1)      ( )      ( )
               //         ( )      (2)      ( )      ( )
               //             ( )      (3)      ( )      ( )
               //                 ( )      (4)      ( )      ( )
    
               //     ( )      ( )      (1)      ( )
               //         ( )      ( )      (2)      ( )
               //             ( )      ( )      (3)      ( )
               //                 ( )      ( )      (4)      ( )
    
               //     ( )      ( )      ( )      (1)
               //         ( )      ( )      ( )      (2)
               //             ( )      ( )      ( )      (3)
               //                 ( )      ( )      ( )      (4)
    
               { 0, 17,34,51, },
               { 4, 21,38,55, },
               { 8, 25,42,59, },
               { 12,29,46,63, },
    
               //     ( )      ( )      ( )      (1)
               //         ( )      ( )      ( )      (2)
               //             ( )      ( )      ( )      (3)
               //                 ( )      ( )      ( )      (4)
    
               //     ( )      ( )      (1)      ( )
               //         ( )      ( )      (2)      ( )
               //             ( )      ( )      (3)      ( )
               //                 ( )      ( )      (4)      ( )
    
               //     ( )      (1)      ( )      ( )
               //         ( )      (2)      ( )      ( )
               //             ( )      (3)      ( )      ( )
               //                 ( )      (4)      ( )      ( )
    
               //     (1)      ( )      ( )      ( )
               //         (2)      ( )      ( )      ( )
               //             (3)      ( )      ( )      ( )
               //                 (4)      ( )      ( )      ( )
    
               { 48,33,18,3,  },
               { 52,37,22,7,  },
               { 56,41,26,11, },
               { 60,45,30,15, },
    
               //     (1)      ( )      ( )      (3)
               //         ( )      ( )      ( )      ( )
               //             ( )      ( )      ( )      ( )
               //                 (4)      ( )      ( )      (2)
    
               //     ( )      ( )      ( )      ( )
               //         ( )      (1)      (3)      ( )
               //             ( )      (4)      (2)      ( )
               //                 ( )      ( )      ( )      ( )
    
               //     ( )      ( )      ( )      ( )
               //         ( )      (2)      (4)      ( )
               //             ( )      (3)      (1)      ( )
               //                 ( )      ( )      ( )      ( )
    
               //     (2)      ( )      ( )      (4)
               //         ( )      ( )      ( )      ( )
               //             ( )      ( )      ( )      ( )
               //                 (3)      ( )      ( )      (1)
    
               { 0, 21,42,63, },
               { 15,26,37,48, },
               { 3, 22,41,60, },
               { 12,25,38,51, },
            };
        }
    }
    
    
    ================================================
    FILE: 88_3-D_Tic-Tac-Toe/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 88_3-D_Tic-Tac-Toe/csharp/ThreeDTicTacToe.csproj
    ================================================
    
      
        Exe
        net6.0
        10
        enable
        enable
      
    
    
    
    ================================================
    FILE: 88_3-D_Tic-Tac-Toe/csharp/ThreeDTicTacToe.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThreeDTicTacToe", "ThreeDTicTacToe.csproj", "{6001B6FB-DF8A-4D59-8E22-BA126C8DA274}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{6001B6FB-DF8A-4D59-8E22-BA126C8DA274}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{6001B6FB-DF8A-4D59-8E22-BA126C8DA274}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{6001B6FB-DF8A-4D59-8E22-BA126C8DA274}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{6001B6FB-DF8A-4D59-8E22-BA126C8DA274}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 88_3-D_Tic-Tac-Toe/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 88_3-D_Tic-Tac-Toe/javascript/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells)
    
    
    ================================================
    FILE: 88_3-D_Tic-Tac-Toe/javascript/qubit.html
    ================================================
    
    
    QUBIT
    
    
    
    
    
    
    
    
    
    
    ================================================
    FILE: 88_3-D_Tic-Tac-Toe/javascript/qubit.js
    ================================================
    // QUBIT
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    var xa = [];
    var la = [];
    var ma = [[],
              [,1,2,3,4],    // 1
              [,5,6,7,8],    // 2
              [,9,10,11,12], // 3
              [,13,14,15,16],    // 4
              [,17,18,19,20],    // 5
              [,21,22,23,24],    // 6
              [,25,26,27,28],    // 7
              [,29,30,31,32],    // 8
              [,33,34,35,36],    // 9
              [,37,38,39,40],    // 10
              [,41,42,43,44],    // 11
              [,45,46,47,48],    // 12
              [,49,50,51,52],    // 13
              [,53,54,55,56],    // 14
              [,57,58,59,60],    // 15
              [,61,62,63,64],    // 16
              [,1,17,33,49], // 17
              [,5,21,37,53],    // 18
              [,9,25,41,57],   // 19
              [,13,29,45,61], // 20
              [,2,18,34,50], // 21
              [,6,22,38,54],    // 22
              [,10,26,42,58],  // 23
              [,14,30,46,62],   // 24
              [,3,19,35,51], // 25
              [,7,23,39,55],    // 26
              [,11,27,43,59],  // 27
              [,15,31,47,63], // 28
              [,4,20,36,52], // 29
              [,8,24,40,56], // 30
              [,12,28,44,60],    // 31
              [,16,32,48,64],    // 32
              [,1,5,9,13],   // 33
              [,17,21,25,29],    // 34
              [,33,37,41,45],    // 35
              [,49,53,57,61],    // 36
              [,2,6,10,14],  // 37
              [,18,22,26,30],    // 38
              [,34,38,42,46],    // 39
              [,50,54,58,62],    // 40
              [,3,7,11,15],  // 41
              [,19,23,27,31],    // 42
              [,35,39,43,47],    // 43
              [,51,55,59,63],    // 44
              [,4,8,12,16],  // 45
              [,20,24,28,32],    // 46
              [,36,40,44,48],    // 47
              [,52,56,60,64],    // 48
              [,1,6,11,16],  // 49
              [,17,22,27,32],    // 50
              [,33,38,43,48],    // 51
              [,49,54,59,64],    // 52
              [,13,10,7,4],  // 53
              [,29,26,23,20],    // 54
              [,45,42,39,36],    // 55
              [,61,58,55,52],    // 56
              [,1,21,41,61], // 57
              [,2,22,42,62], // 58
              [,3,23,43,63], // 59
              [,4,24,44,64], // 60
              [,49,37,25,13],    // 61
              [,50,38,26,14],    // 62
              [,51,39,27,15],    // 63
              [,52,40,28,16],    // 64
              [,1,18,35,52], // 65
              [,5,22,39,56], // 66
              [,9,26,43,60], // 67
              [,13,30,47,64],    // 68
              [,49,34,19,4], // 69
              [,53,38,23,8], // 70
              [,57,42,27,12],    // 71
              [,61,46,31,16],    // 72
              [,1,22,43,64], // 73
              [,16,27,38,49],    // 74
              [,4,23,42,61], // 75
              [,13,26,39,52] // 76
              ];
    var ya = [,1,49,52,4,13,61,64,16,22,39,23,38,26,42,27,43];
    
    function show_board()
    {
        for (xx = 1; xx <= 9; xx++)
            print("\n");
        for (i = 1; i <= 4; i++) {
            for (j = 1; j <= 4; j++) {
                str = "";
                for (i1 = 1; i1 <= j; i1++)
                    str += "   ";
                for (k = 1; k <= 4; k++) {
                    q = 16 * i + 4 * j + k - 20;
                    if (xa[q] == 0)
                        str += "( )      ";
                    if (xa[q] == 5)
                        str += "(M)      ";
                    if (xa[q] == 1)
                        str += "(Y)      ";
                    if (xa[q] == 1 / 8)
                        str += "( )      ";
                }
                print(str + "\n");
                print("\n");
            }
            print("\n");
            print("\n");
        }
    }
    
    function process_board()
    {
        for (i = 1; i <= 64; i++) {
            if (xa[i] == 1 / 8)
                xa[i] = 0;
        }
    }
    
    function check_for_lines()
    {
        for (s = 1; s <= 76; s++) {
            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];
        }
    }
    
    function show_square(m)
    {
        k1 = Math.floor((m - 1) / 16) + 1;
        j2 = m - 16 * (k1 - 1);
        k2 = Math.floor((j2 - 1) / 4) + 1;
        k3 = m - (k1 - 1) * 16 - (k2 - 1) * 4;
        m = k1 * 100 + k2 * 10 + k3;
        print(" " + m + " ");
    }
    
    function select_move() {
        if (i % 4 <= 1) {
            a = 1;
        } else {
            a = 2;
        }
        for (j = a; j <= 5 - a; j += 5 - 2 * a) {
            if (xa[ma[i][j]] == s)
                break;
        }
        if (j > 5 - a)
            return false;
        xa[ma[i][j]] = 5;
        m = ma[i][j];
        print("MACHINE TAKES");
        show_square(m);
        return true;
    }
    
    // Main control section
    async function main()
    {
        print(tab(33) + "QUBIC\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        while (1) {
            print("DO YOU WANT INSTRUCTIONS");
            str = await input();
            str = str.substr(0, 1);
            if (str == "Y" || str == "N")
                break;
            print("INCORRECT ANSWER.  PLEASE TYPE 'YES' OR 'NO'");
        }
        if (str == "Y") {
            print("\n");
            print("THE GAME IS TIC-TAC-TOE IN A 4 X 4 X 4 CUBE.\n");
            print("EACH MOVE IS INDICATED BY A 3 DIGIT NUMBER, WITH EACH\n");
            print("DIGIT BETWEEN 1 AND 4 INCLUSIVE.  THE DIGITS INDICATE THE\n");
            print("LEVEL, ROW, AND COLUMN, RESPECTIVELY, OF THE OCCUPIED\n");
            print("PLACE.  \n");
            print("\n");
            print("TO PRINT THE PLAYING BOARD, TYPE 0 (ZERO) AS YOUR MOVE.\n");
            print("THE PROGRAM WILL PRINT THE BOARD WITH YOUR MOVES INDI-\n");
            print("CATED WITH A (Y), THE MACHINE'S MOVES WITH AN (M), AND\n");
            print("UNUSED SQUARES WITH A ( ).  OUTPUT IS ON PAPER.\n");
            print("\n");
            print("TO STOP THE PROGRAM RUN, TYPE 1 AS YOUR MOVE.\n");
            print("\n");
            print("\n");
        }
        while (1) {
            for (i = 1; i <= 64; i++)
                xa[i] = 0;
            z = 1;
            print("DO YOU WANT TO MOVE FIRST");
            while (1) {
                str = await input();
                str = str.substr(0, 1);
                if (str == "Y" || str == "N")
                    break;
                print("INCORRECT ANSWER.  PLEASE TYPE 'YES' OR 'NO'");
            }
            while (1) {
                while (1) {
                    print(" \n");
                    print("YOUR MOVE");
                    j1 = parseInt(await input());
                    if (j1 == 0) {
                        show_board();
                        continue;
                    }
                    if (j1 == 1)
                        return;
                    k1 = Math.floor(j1 / 100);
                    j2 = j1 - k1 * 100;
                    k2 = Math.floor(j2 / 10);
                    k3 = j2 - k2 * 10;
                    m = 16 * k1 + 4 * k2 + k3 - 20;
                    if (k1 < 1 || k2 < 1 || k3 < 1 || k1 > 4 || k2 > 4 || k3 >> 4) {
                        print("INCORRECT MOVE, RETYPE IT--");
                    } else {
                        process_board();
                        if (xa[m] != 0) {
                            print("THAT SQUARE IS USED, TRY AGAIN.\n");
                        } else {
                            break;
                        }
                    }
                }
                xa[m] = 1;
                check_for_lines();
                status = 0;
                for (j = 1; j <= 3; j++) {
                    for (i = 1; i <= 76; i++) {
                        if (j == 1) {
                            if (la[i] != 4)
                                continue;
                            print("YOU WIN AS FOLLOWS");
                            for (j = 1; j <= 4; j++) {
                                m = ma[i][j];
                                show_square(m);
                            }
                            status = 1;
                            break;
                        }
                        if (j == 2) {
                            if (la[i] != 15)
                                continue;
                            for (j = 1; j <= 4; j++) {
                                m = ma[i][j];
                                if (xa[m] != 0)
                                    continue;
                                xa[m] = 5;
                                print("MACHINE MOVES TO ");
                                show_square(m);
                            }
                            print(", AND WINS AS FOLLOWS");
                            for (j = 1; j <= 4; j++) {
                                m = ma[i][j];
                                show_square(m);
                            }
                            status = 1;
                            break;
                        }
                        if (j == 3) {
                            if (la[i] != 3)
                                continue;
                            print("NICE TRY, MACHINE MOVES TO");
                            for (j = 1; j <= 4; j++) {
                                m = ma[i][j];
                                if (xa[m] != 0)
                                    continue;
                                xa[m] = 5;
                                show_square(m);
                                status = 2;
                            }
                            break;
                        }
                    }
                    if (i <= 76)
                        break;
                }
                if (status == 2)
                    continue;
                if (status == 1)
                    break;
                // x = x; non-useful in original
                i = 1;
                do {
                    la[i] = xa[ma[i][1]] + xa[ma[i][2]] + xa[ma[i][3]] + xa[ma[i][4]];
                    l = la[i];
                    if (l == 10) {
                        for (j = 1; j <= 4; j++) {
                            if (xa[ma[i][j]] == 0)
                                xa[ma[i][j]] = 1 / 8;
                        }
                    }
                } while (++i <= 76) ;
                check_for_lines();
                i = 1;
                do {
                    if (la[i] == 0.5) {
                        s = 1 / 8;
                        select_move();
                        break;
                    }
                    if (la[i] == 5 + 3 / 8) {
                        s = 1 / 8;
                        select_move();
                        break;
                    }
                } while (++i <= 76) ;
                if (i <= 76)
                    continue;
    
                process_board();
    
                i = 1;
                do {
                    la[i] = xa[ma[i][1]] + xa[ma[i][2]] + xa[ma[i][3]] + xa[ma[i][4]];
                    l = la[i];
                    if (l == 2) {
                        for (j = 1; j <= 4; j++) {
                            if (xa[ma[i][j]] == 0)
                                xa[ma[i][j]] = 1 / 8;
                        }
                    }
                } while (++i <= 76) ;
                check_for_lines();
                i = 1;
                do {
                    if (la[i] == 0.5) {
                        s = 1 / 8;
                        select_move();
                        break;
                    }
                    if (la[i] == 1 + 3 / 8) {
                        s = 1 / 8;
                        select_move();
                        break;
                    }
                } while (++i <= 76) ;
                if (i <= 76)
                    continue;
    
                for (k = 1; k <= 18; k++) {
                    p = 0;
                    for (i = 4 * k - 3; i <= 4 * k; i++) {
                        for (j = 1; j <= 4; j++)
                            p += xa[ma[i][j]];
                    }
                    if (p == 4 || p == 9) {
                        s = 1 / 8;
                        for (i = 4 * k - 3; i <= 4 * k; i++) {
                            if (select_move())
                                break;
                        }
                        s = 0;
                    }
                }
                if (k <= 18)
                    continue
                process_board();
                z = 1;
                do {
                    if (xa[ya[z]] == 0)
                        break;
                } while (++z < 17) ;
                if (z >= 17) {
                    for (i = 1; i <= 64; i++) {
                        if (xa[i] == 0) {
                            xa[i] = 5;
                            m = i;
                            print("MACHINE LIKES");
                            break;
                        }
                    }
                    if (i > 64) {
                        print("THE GAME IS A DRAW.\n");
                        break;
                    }
                } else {
                    m = ya[z];
                    xa[m] = 5;
                    print("MACHINE MOVES TO");
                }
                show_square(m);
            }
            print(" \n");
            print("DO YOU WANT TO TRY ANOTHER GAME");
            while (1) {
                str = await input();
                str = str.substr(0, 1);
                if (str == "Y" || str == "N")
                    break;
                print("INCORRECT ANSWER. PLEASE TYPE 'YES' OR 'NO'");
            }
            if (str == "N")
                break;
        }
    }
    
    main();
    
    
    ================================================
    FILE: 88_3-D_Tic-Tac-Toe/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 88_3-D_Tic-Tac-Toe/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 88_3-D_Tic-Tac-Toe/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 88_3-D_Tic-Tac-Toe/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 88_3-D_Tic-Tac-Toe/python/qubit.py
    ================================================
    #!/usr/bin/env python3
    
    # Ported from the BASIC source for 3D Tic Tac Toe
    # in BASIC Computer Games, by David H. Ahl
    # The code originated from Dartmouth College
    
    from enum import Enum
    from typing import Optional, Tuple, Union
    
    
    class Move(Enum):
        """Game status and types of machine move"""
    
        HUMAN_WIN = 0
        MACHINE_WIN = 1
        DRAW = 2
        MOVES = 3
        LIKES = 4
        TAKES = 5
        GET_OUT = 6
        YOU_FOX = 7
        NICE_TRY = 8
        CONCEDES = 9
    
    
    class Player(Enum):
        EMPTY = 0
        HUMAN = 1
        MACHINE = 2
    
    
    class TicTacToe3D:
        """The game logic for 3D Tic Tac Toe and the machine opponent"""
    
        def __init__(self) -> None:
            # 4x4x4 board keeps track of which player occupies each place
            # and used by machine to work out its strategy
            self.board = [0] * 64
    
            # starting move
            self.corners = [0, 48, 51, 3, 12, 60, 63, 15, 21, 38, 22, 37, 25, 41, 26, 42]
    
            # lines to check for end game
            self.lines = [
                [0, 1, 2, 3],
                [4, 5, 6, 7],
                [8, 9, 10, 11],
                [12, 13, 14, 15],
                [16, 17, 18, 19],
                [20, 21, 22, 23],
                [24, 25, 26, 27],
                [28, 29, 30, 31],
                [32, 33, 34, 35],
                [36, 37, 38, 39],
                [40, 41, 42, 43],
                [44, 45, 46, 47],
                [48, 49, 50, 51],
                [52, 53, 54, 55],
                [56, 57, 58, 59],
                [60, 61, 62, 63],
                [0, 16, 32, 48],
                [4, 20, 36, 52],
                [8, 24, 40, 56],
                [12, 28, 44, 60],
                [1, 17, 33, 49],
                [5, 21, 37, 53],
                [9, 25, 41, 57],
                [13, 29, 45, 61],
                [2, 18, 34, 50],
                [6, 22, 38, 54],
                [10, 26, 42, 58],
                [14, 30, 46, 62],
                [3, 19, 35, 51],
                [7, 23, 39, 55],
                [11, 27, 43, 59],
                [15, 31, 47, 63],
                [0, 4, 8, 12],
                [16, 20, 24, 28],
                [32, 36, 40, 44],
                [48, 52, 56, 60],
                [1, 5, 9, 13],
                [17, 21, 25, 29],
                [33, 37, 41, 45],
                [49, 53, 57, 61],
                [2, 6, 10, 14],
                [18, 22, 26, 30],
                [34, 38, 42, 46],
                [50, 54, 58, 62],
                [3, 7, 11, 15],
                [19, 23, 27, 31],
                [35, 39, 43, 47],
                [51, 55, 59, 63],
                [0, 5, 10, 15],
                [16, 21, 26, 31],
                [32, 37, 42, 47],
                [48, 53, 58, 63],
                [12, 9, 6, 3],
                [28, 25, 22, 19],
                [44, 41, 38, 35],
                [60, 57, 54, 51],
                [0, 20, 40, 60],
                [1, 21, 41, 61],
                [2, 22, 42, 62],
                [3, 23, 43, 63],
                [48, 36, 24, 12],
                [49, 37, 25, 13],
                [50, 38, 26, 14],
                [51, 39, 27, 15],
                [0, 17, 34, 51],
                [4, 21, 38, 55],
                [8, 25, 42, 59],
                [12, 29, 46, 63],
                [48, 33, 18, 3],
                [52, 37, 22, 7],
                [56, 41, 26, 11],
                [60, 45, 30, 15],
                [0, 21, 42, 63],
                [15, 26, 37, 48],
                [3, 22, 41, 60],
                [12, 25, 38, 51],
            ]
    
        def get(self, x, y, z) -> Player:
            m = self.board[4 * (4 * z + y) + x]
            if m == 40:
                return Player.MACHINE
            elif m == 8:
                return Player.HUMAN
            else:
                return Player.EMPTY
    
        def move_3d(self, x, y, z, player) -> bool:
            m = 4 * (4 * z + y) + x
            return self.move(m, player)
    
        def move(self, m, player) -> bool:
            if self.board[m] > 1:
                return False
    
            self.board[m] = 40 if player == Player.MACHINE else 8
            return True
    
        def get_3d_position(self, m) -> Tuple[int, int, int]:
            x = m % 4
            y = (m // 4) % 4
            z = m // 16
            return x, y, z
    
        def evaluate_lines(self) -> None:
            self.lineValues = [0] * 76
            for j in range(76):
                value = sum(self.board[self.lines[j][k]] for k in range(4))
                self.lineValues[j] = value
    
        def strategy_mark_line(self, i) -> None:
            for j in range(4):
                m = self.lines[i][j]
                if self.board[m] == 0:
                    self.board[m] = 1
    
        def clear_strategy_marks(self) -> None:
            for i in range(64):
                if self.board[i] == 1:
                    self.board[i] = 0
    
        def mark_and_move(self, vlow, vhigh, vmove) -> Optional[Tuple[Move, int]]:
            """
            mark lines that can potentially win the game for the human
            or the machine and choose best place to play
            """
            for i in range(76):
                value = sum(self.board[self.lines[i][j]] for j in range(4))
                self.lineValues[i] = value
                if vlow <= value < vhigh:
                    if value > vlow:
                        return self.move_triple(i)
                    self.strategy_mark_line(i)
            self.evaluate_lines()
    
            for i in range(76):
                value = self.lineValues[i]
                if value in [4, vmove]:
                    return self.move_diagonals(i, 1)
            return None
    
        def machine_move(self) -> Union[None, Tuple[Move, int], Tuple[Move, int, int]]:
            """machine works out what move to play"""
            self.clear_strategy_marks()
    
            self.evaluate_lines()
            for value, event in [
                (32, self.human_win),
                (120, self.machine_win),
                (24, self.block_human_win),
            ]:
                for i in range(76):
                    if self.lineValues[i] == value:
                        return event(i)
    
            m = self.mark_and_move(80, 88, 43)
            if m is not None:
                return m
    
            self.clear_strategy_marks()
    
            m = self.mark_and_move(16, 24, 11)
            if m is not None:
                return m
    
            for k in range(18):
                value = 0
                for i in range(4 * k, 4 * k + 4):
                    for j in range(4):
                        value += self.board[self.lines[i][j]]
                if (32 <= value < 40) or (72 <= value < 80):
                    for s in [1, 0]:
                        for i in range(4 * k, 4 * k + 4):
                            m = self.move_diagonals(i, s)
                            if m is not None:
                                return m
    
            self.clear_strategy_marks()
    
            for y in self.corners:
                if self.board[y] == 0:
                    return (Move.MOVES, y)
    
            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)
    
        def machine_win(self, i) -> Optional[Tuple[Move, int, int]]:
            for j in range(4):
                m = self.lines[i][j]
                if self.board[m] == 0:
                    return (Move.MACHINE_WIN, m, i)
            return None
    
        def block_human_win(self, i) -> Optional[Tuple[Move, int]]:
            for j in range(4):
                m = self.lines[i][j]
                if self.board[m] == 0:
                    return (Move.NICE_TRY, m)
            return None
    
        def move_triple(self, i) -> Tuple[Move, int]:
            """make two lines-of-3 or prevent human from doing this"""
            for j in range(4):
                m = self.lines[i][j]
                if self.board[m] == 1:
                    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]]:
            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:
                    return (Move.TAKES, m)
            return None
    
    
    class Qubit:
        def move_code(self, board, m) -> str:
            x, y, z = board.get_3d_position(m)
            return f"{z + 1:d}{y + 1:d}{x + 1:d}"
    
        def show_win(self, board, i) -> None:
            for m in board.lines[i]:
                print(self.move_code(board, m))
    
        def show_board(self, board) -> None:
            c = " YM"
            for z in range(4):
                for y in range(4):
                    print("   " * y, end="")
                    for x in range(4):
                        p = board.get(x, y, z)
                        print(f"({c[p.value]})      ", end="")
                    print("\n")
                print("\n")
    
        def human_move(self, board) -> bool:
            print()
            c = "1234"
            while True:
                h = input("Your move?\n")
                if h == "1":
                    return False
                if h == "0":
                    self.show_board(board)
                    continue
                if (len(h) == 3) and (h[0] in c) and (h[1] in c) and (h[2] in c):
                    x = c.find(h[2])
                    y = c.find(h[1])
                    z = c.find(h[0])
                    if board.move_3d(x, y, z, Player.HUMAN):
                        break
    
                    print("That square is used. Try again.")
                else:
                    print("Incorrect move. Retype it--")
    
            return True
    
        def play(self) -> None:
            print("Qubic\n")
            print("Create Computing Morristown, New Jersey\n\n\n")
            while True:
                c = input("Do you want instructions?\n")
                if len(c) >= 1 and (c[0] in "ynYN"):
                    break
                print("Incorrect answer. Please type 'yes' or 'no.")
    
            c = c.lower()
            if c[0] == "y":
                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.\n")
    
                print("To print the playing board, type 0 (zero) as your move.")
                print("The program will print the board with your moves indicated")
                print("with a (Y), the machine's moves with an (M), and")
                print("unused squares with a ( ).\n")
    
                print("To stop the program run, type 1 as your move.\n\n")
    
            play_again = True
            while play_again:
                board = TicTacToe3D()
    
                while True:
                    s = input("Do you want to move first?\n")
                    if len(s) >= 1 and (s[0] in "ynYN"):
                        break
                    print("Incorrect answer. Please type 'yes' or 'no'.")
    
                skip_human = s[0] in "nN"
    
                move_text = [
                    "Machine moves to",
                    "Machine likes",
                    "Machine takes",
                    "Let's see you get out of this:  Machine moves to",
                    "You fox.  Just in the nick of time, machine moves to",
                    "Nice try. Machine moves to",
                ]
    
                while True:
                    if not skip_human and not self.human_move(board):
                        break
                    skip_human = False
    
                    m = board.machine_move()
                    assert m is not None
                    if m[0] == Move.HUMAN_WIN:
                        print("You win as follows,")
                        self.show_win(board, m[2])  # type: ignore
                        break
                    elif m[0] == Move.MACHINE_WIN:
                        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:
                        print("The game is a draw.")
                        break
                    elif m[0] == Move.CONCEDES:
                        print("Machine concedes this game.")
                        break
                    else:
                        print(move_text[m[0].value - Move.MOVES.value])
                        print(self.move_code(board, m[1]))
                        board.move(m[1], Player.MACHINE)
    
                    self.show_board(board)
    
                print(" ")
                while True:
                    x = input("Do you want to try another game\n")
                    if len(x) >= 1 and x[0] in "ynYN":
                        break
                    print("Incorrect answer. Please Type 'yes' or 'no'.")
    
                play_again = x[0] in "yY"
    
    
    if __name__ == "__main__":
        game = Qubit()
        game.play()
    
    
    ================================================
    FILE: 88_3-D_Tic-Tac-Toe/qubit.bas
    ================================================
    50 PRINT CHR$(26):REM WIDTH 80
    100 PRINT TAB(33);"QUBIC":PRINT
    110 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    120 PRINT:PRINT:PRINT
    210 PRINT "DO YOU WANT INSTRUCTIONS";
    220 INPUT C$
    230 IF LEFT$(C$,1)="N" THEN 315
    240 IF LEFT$(C$,1)="Y" THEN 265
    250 PRINT "INCORRECT ANSWER.  PLEASE TYPE 'YES' OR 'NO'";
    260 GOTO 220
    265 PRINT
    270 PRINT "THE GAME IS TIC-TAC-TOE IN A 4 X 4 X 4 CUBE."
    280 PRINT "EACH MOVE IS INDICATED BY A 3 DIGIT NUMBER, WITH EACH"
    290 PRINT "DIGIT BETWEEN 1 AND 4 INCLUSIVE.  THE DIGITS INDICATE THE"
    300 PRINT "LEVEL, ROW, AND COLUMN, RESPECTIVELY, OF THE OCCUPIED"
    305 PRINT "PLACE.  "
    306 PRINT
    307 PRINT "TO PRINT THE PLAYING BOARD, TYPE 0 (ZERO) AS YOUR MOVE."
    308 PRINT "THE PROGRAM WILL PRINT THE BOARD WITH YOUR MOVES INDI-"
    309 PRINT "CATED WITH A (Y), THE MACHINE'S MOVES WITH AN (M), AND"
    310 PRINT "UNUSED SQUARES WITH A ( ).  OUTPUT IS ON PAPER."
    311 PRINT
    312 PRINT "TO STOP THE PROGRAM RUN, TYPE 1 AS YOUR MOVE."
    313 PRINT:PRINT
    315 DIM X(64),L(76),M(76,4),Y(16)
    320 FOR I = 1 TO 16
    330 READ Y(I)
    340 NEXT I
    350 FOR I=1 TO 76
    360 FOR J = 1 TO 4
    370 READ M(I,J)
    380 NEXT J
    390 NEXT I
    400 FOR I = 1 TO 64
    410 LET X (I) =0
    420 NEXT I
    430 LET Z=1
    440 PRINT "DO YOU WANT TO MOVE FIRST";
    450 INPUT S$
    460 IF LEFT$(S$,1)="N" THEN 630
    470 IF LEFT$(S$,1)="Y" THEN 500
    480 PRINT "INCORRECT ANSWER.  PLEASE TYPE 'YES' OR 'NO'.";
    490 GOTO 450
    500 PRINT " "
    510 PRINT "YOUR MOVE";
    520 INPUT J1
    521 IF J1=1 THEN 2770
    522 IF J1<>0 THEN 525
    523 GOSUB 2550
    524 GOTO 500
    525 IF J1<111 THEN 2750
    526 IF J1>444 THEN 2750
    530 GOSUB 2500
    540 LET K1=INT(J1/100)
    550 LET J2=(J1-K1*100)
    560 LET K2=INT(J2/10)
    570 LET K3= J1 - K1*100 -K2*10
    580 LET M=16*K1+4*K2+K3-20
    590 IF X(M)=0 THEN 620
    600 PRINT "THAT SQUARE IS USED, TRY AGAIN."
    610 GOTO 500
    620 LET X(M)=1
    630 GOSUB 1640
    640 J=1
    650 I=1
    660 IF J=1 THEN 720
    670 IF J=2 THEN 790
    680 IF J=3 THEN 930
    690 I=I+1: IF I<=76 THEN 660
    700 J=J+1: IF J<=3 THEN 650
    710 GOTO 1300
    720 IF L(I)<>4 THEN 690
    730 PRINT "YOU WIN AS FOLLOWS";
    740 FOR J=1 TO 4
    750 LET M=M(I,J)
    760 GOSUB 1570
    770 NEXT J
    780 GOTO 1490
    790 IF L(I)<>15 THEN 690
    800 FOR J=1 TO 4
    810 LET M=M(I,J)
    820 IF X(M)<>0 THEN 860
    830 LET X(M)=5
    840 PRINT "MACHINE MOVES TO";
    850 GOSUB 1570
    860 NEXT J
    870 PRINT ", AND WINS AS FOLLOWS"
    880 FOR J=1 TO 4
    890 LET M=M(I,J)
    900 GOSUB 1570
    910 NEXT J
    920 GOTO 1490
    930 IF L(I)<>3 THEN 690
    940 PRINT "NICE TRY. MACHINE MOVES TO";
    950 FOR J=1 TO 4
    960 LET M=M(I,J)
    970 IF X(M)<>0 THEN 1010
    980 LET X(M)=5
    990 GOSUB 1570
    1000 GOTO 500
    1010 NEXT J
    1020 GOTO 1300
    1030 I=1
    1040 LET L(I)=X(M(I,1))+X(M(I,2))+X(M(I,3))+X(M(I,4))
    1050 LET L = L(I)
    1060 IF L <2 THEN 1130
    1070 IF L>=3 THEN 1130
    1080 IF L>2 THEN 2230
    1090 FOR J = 1 TO 4
    1100 IF X(M(I,J))<>0 THEN 1120
    1110 LET X(M(I,J))=1/8
    1120 NEXT J
    1130 I=I+1: IF I<=76 THEN 1040
    1140 GOSUB 1640
    1150 I=1
    1160 IF L(I)=1/2 THEN 2360
    1170 IF L(I)=1+3/8 THEN 2360
    1180 I=I+1: IF I<=76 THEN 1160
    1190 GOTO 1830
    1200 LET Z = 1
    1210 IF X(Y(Z))=0 THEN 1250
    1220 LET Z=Z+1
    1230 IF Z<>17 THEN 1210
    1240 GOTO 1720
    1250 LET M=Y(Z)
    1260 LET X(M)=5
    1270 PRINT "MACHINE MOVES TO";
    1280 GOSUB 1570
    1290 GOTO 500
    1300 LET X=X
    1310 I=1
    1320 LET L(I)=X(M(I,1))+X(M(I,2))+X(M(I,3))+X(M(I,4))
    1330 LET L=L(I)
    1340 IF L<10 THEN 1410
    1350 IF L>=11 THEN 1410
    1360 IF L>10 THEN 2230
    1370 FOR J=1 TO 4
    1380 IF X(M(I,J))<>0 THEN 1400
    1390 LET X(M(I,J))=1/8
    1400 NEXT J
    1410 I=I+1: IF I<=76 THEN 1320
    1420 GOSUB 1640
    1430 I=1
    1440 IF L(I)=.5 THEN 2360
    1450 IF L(I)=5+3/8 THEN 2360
    1460 I=I+1: IF I<=76 THEN 1440
    1470 GOSUB 2500
    1480 GOTO 1030
    1490 PRINT " "
    1500 PRINT "DO YOU WANT TO TRY ANOTHER GAME";
    1510 INPUT X$
    1520 IF LEFT$(X$,1)="Y" THEN 400
    1530 IF LEFT$(X$,1)="N" THEN 1560
    1540 PRINT "INCORRECT ANSWER. PLEASE TYPE 'YES' OR 'NO'";
    1550 GOTO 1510
    1560 END
    1570 LET K1=INT((M-1)/16)+1
    1580 LET J2=M-16*(K1-1)
    1590 LET K2=INT((J2-1)/4)+1
    1600 LET K3=M-(K1-1)*16-(K2-1)*4
    1610 LET M=K1*100+K2*10+K3
    1620 PRINT M;
    1630 RETURN
    1640 FOR S=1 TO 76
    1650 LET J1 = M(S,1)
    1660 LET J2=M(S,2)
    1670 LET J3=M(S,3)
    1680 LET J4=M(S,4)
    1690 LET L(S)=X(J1)+X(J2)+X(J3)+X(J4)
    1700 NEXT S
    1710 RETURN
    1720 FOR I=1 TO 64
    1730 IF X(I)<>0 THEN 1800
    1740 LET X(I)=5
    1750 LET M=I
    1760 PRINT "MACHINE LIKES";
    1770 GOSUB 1570
    1780 PRINT " "
    1790 GOTO 500
    1800 NEXT I
    1810 PRINT "THE GAME IS A DRAW."
    1820 GOTO 1490
    1830 FOR K=1 TO 18
    1840 LET P=0
    1850 FOR I=4*K-3 TO 4*K
    1860 FOR J=1 TO 4
    1870 LET P=P+X(M(I,J))
    1880 NEXT J
    1890 NEXT I
    1900 IF P<4 THEN 1940
    1910 IF P<5 THEN 1970
    1920 IF P<9 THEN 1940
    1930 IF P<10 THEN 1970
    1940 NEXT K
    1950 GOSUB 2500
    1960 GOTO 1200
    1970 LET S=1/8
    1980 FOR I=4*K-3 TO 4*K
    1990 GOTO 2370
    2000 NEXT I
    2010 LET S=0
    2020 GOTO 1980
    2030 DATA 1,49,52,4,13,61,64,16,22,39,23,38,26,42,27,43
    2040 DATA 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20
    2050 DATA 21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38
    2060 DATA 39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56
    2070 DATA 57,58,59,60,61,62,63,64
    2080 DATA 1,17,33,49,5,21,37,53,9,25,41,57,13,29,45,61
    2090 DATA 2,18,34,50,6,22,38,54,10,26,42,58,14,30,46,62
    2100 DATA 3,19,35,51,7,23,39,55,11,27,43,59,15,31,47,63
    2110 DATA 4,20,36,52,8,24,40,56,12,28,44,60,16,32,48,64
    2120 DATA 1,5,9,13,17,21,25,29,33,37,41,45,49,53,57,61
    2130 DATA 2,6,10,14,18,22,26,30,34,38,42,46,50,54,58,62
    2140 DATA 3,7,11,15,19,23,27,31,35,39,43,47,51,55,59,63
    2150 DATA 4,8,12,16,20,24,28,32,36,40,44,48,52,56,60,64
    2160 DATA 1,6,11,16,17,22,27,32,33,38,43,48,49,54,59,64
    2170 DATA 13,10,7,4,29,26,23,20,45,42,39,36,61,58,55,52
    2180 DATA 1,21,41,61,2,22,42,62,3,23,43,63,4,24,44,64
    2190 DATA 49,37,25,13,50,38,26,14,51,39,27,15,52,40,28,16
    2200 DATA 1,18,35,52,5,22,39,56,9,26,43,60,13,30,47,64
    2210 DATA 49,34,19,4,53,38,23,8,57,42,27,12,61,46,31,16
    2220 DATA 1,22,43,64,16,27,38,49,4,23,42,61,13,26,39,52
    2230 FOR J=1 TO 4
    2240 IF X(M(I,J))<>1/8 THEN 2330
    2250 LET X(M(I,J))=5
    2260 IF L(I)<5 THEN 2290
    2270 PRINT "LET'S SEE YOU GET OUT OF THIS:  MACHINE MOVES TO";
    2280 GOTO 2300
    2290 PRINT "YOU FOX.  JUST IN THE NICK OF TIME, MACHINE MOVES TO";
    2300 LET M=M(I,J)
    2310 GOSUB 1570
    2320 GOTO 500
    2330 NEXT J
    2340 PRINT "MACHINE CONCEDES THIS GAME."
    2350 GOTO 1490
    2360 LET S=1/8
    2370 IF I-INT(I/4)*4>1 THEN 2400
    2380 LET A=1
    2390 GOTO 2410
    2400 LET A=2
    2410 FOR J=A TO 5-A STEP 5-2*A
    2420 IF X(M(I,J))=S THEN 2450
    2430 NEXT J
    2440 GOTO 2000
    2450 LET X(M(I,J))=5
    2460 LET M=M(I,J)
    2470 PRINT "MACHINE TAKES";
    2480 GOSUB 1570
    2490 GOTO 500
    2500 FOR I=1 TO 64
    2510 IF X(I)<>1/8 THEN 2530
    2520 LET X(I)=0
    2530 NEXT I
    2540 RETURN
    2550 FOR XX=1 TO 9:PRINT:NEXT:FOR I=1 TO 4
    2560 FOR J=1 TO 4
    2562 FOR I1=1 TO J
    2564 PRINT"   ";
    2566 NEXT I1
    2570 FOR K=1 TO 4
    2600 LET Q=16*I+4*J+K-20
    2610 IF X(Q)<>O THEN 2630
    2620 PRINT"( )      ";
    2630 IF X(Q)<>5 THEN 2650
    2640 PRINT"(M)      ";
    2650 IF X(Q)<>1 THEN 2660
    2655 PRINT"(Y)      ";
    2660 IF X(Q)<>1/8 THEN 2670
    2665 PRINT"( )      ";
    2670 NEXT K
    2680 PRINT
    2690 PRINT
    2700 NEXT J
    2710 PRINT
    2720 PRINT
    2730 NEXT I
    2735 REM PRINT CHR$(12)
    2740 RETURN
    2750 PRINT"INCORRECT MOVE, RETYPE IT--";
    2760 GOTO 520
    2770 END
    
    
    ================================================
    FILE: 88_3-D_Tic-Tac-Toe/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 88_3-D_Tic-Tac-Toe/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 88_3-D_Tic-Tac-Toe/vbnet/ThreeDTicTacToe.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "ThreeDTicTacToe", "ThreeDTicTacToe.vbproj", "{AD17E9E0-4795-4533-A215-09DD99F7D696}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{AD17E9E0-4795-4533-A215-09DD99F7D696}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{AD17E9E0-4795-4533-A215-09DD99F7D696}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{AD17E9E0-4795-4533-A215-09DD99F7D696}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{AD17E9E0-4795-4533-A215-09DD99F7D696}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 88_3-D_Tic-Tac-Toe/vbnet/ThreeDTicTacToe.vbproj
    ================================================
    
      
        Exe
        ThreeDTicTacToe
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 89_Tic-Tac-Toe/README.md
    ================================================
    ### Tic-Tac-Toe
    
    The game of tic-tac-toe hardly needs any introduction. In this one, you play versus the computer. Moves are entered by number:
    ```
    1   2   3
    
    4   5   6
    
    7   8   9
    ```
    
    If you make any bad moves, the computer will win; if the computer makes a bad move, you can win; otherwise, the game ends in a tie.
    
    A second version of the game is included which prints out the board after each move. This is ideally suited to a CRT terminal, particularly if you modify it to not print out a new board after each move, but rather use the cursor to make the move.
    
    The first program was written by Tom Koos while a student researcher at the Oregon Museum of Science and Industry; it was extensively modified by Steve North of Creative Computing. The author of the second game is Curt Flick of Akron, Ohio.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=171)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=186)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 89_Tic-Tac-Toe/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 89_Tic-Tac-Toe/csharp/TicTacToe.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "tictactoe1", "tictactoe1\tictactoe1.csproj", "{E0AF55BF-4C2B-41C6-B556-E8EC8C1BE647}"
    EndProject
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "tictactoe2", "tictactoe2\tictactoe2.csproj", "{B98682C9-132E-44F1-B288-8BE04CF53BCF}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{E0AF55BF-4C2B-41C6-B556-E8EC8C1BE647}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{E0AF55BF-4C2B-41C6-B556-E8EC8C1BE647}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{E0AF55BF-4C2B-41C6-B556-E8EC8C1BE647}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{E0AF55BF-4C2B-41C6-B556-E8EC8C1BE647}.Release|Any CPU.Build.0 = Release|Any CPU
    		{B98682C9-132E-44F1-B288-8BE04CF53BCF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{B98682C9-132E-44F1-B288-8BE04CF53BCF}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{B98682C9-132E-44F1-B288-8BE04CF53BCF}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{B98682C9-132E-44F1-B288-8BE04CF53BCF}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 89_Tic-Tac-Toe/csharp/tictactoe1/Program.cs
    ================================================
    // See https://aka.ms/new-console-template for more information
    // Print text on the screen with 30 spaces before text
    Console.WriteLine("TIC TAC TOE".PadLeft(30));
    // Print text on screen with 15 spaces before text
    Console.WriteLine("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY".PadLeft(15));
    // Print three blank lines on screen
    Console.WriteLine("\n\n\n");
    // THIS PROGRAM PLAYS TIC TAC TOE
    // THE MACHINE GOES FIRST
    Console.WriteLine("THE GAME BOARD IS NUMBERED:\n");
    Console.WriteLine("1  2  3");
    Console.WriteLine("8  9  4");
    Console.WriteLine("7  6  5");
    
    // Main program
    while(true) {
    	int a, b, c, d, e;
    	int p, q, r, s;
    	a = 9;
    	Console.WriteLine("\n\n");
    	computerMoves(a);
    	p = readYourMove();
    	b = move(p + 1);
    	computerMoves(b);
    	q = readYourMove();
    	if (q == move(b + 4)) {
    		c = move(b + 2);
    		computerMoves(c);
    		r = readYourMove();
    		if (r == move(c + 4)) {
    			if (p % 2 != 0) {
    				d = move(c + 3);
    				computerMoves(d);
    				s = readYourMove();
    				if (s == move(d + 4)) {
    					e = move(d + 6);
    					computerMoves(e);
    					Console.WriteLine("THE GAME IS A DRAW.");
    				} else {
    					e = move(d + 4);
    					computerMoves(e);
    					Console.WriteLine("AND WINS ********");
    				}
    			} else {
    				d = move(c + 7);
    				computerMoves(d);
    				Console.WriteLine("AND WINS ********");
    			}
    		} else {
    			d = move(c + 4);
    			computerMoves(d);
    			Console.WriteLine("AND WINS ********");
    		}
    	} else {
    		c = move(b + 4);
    		computerMoves(c);
    		Console.WriteLine("AND WINS ********");
    	}
    }
    
    void computerMoves(int move) {
    		Console.WriteLine("COMPUTER MOVES " + move);
    }
    int readYourMove() {
    	while(true) {
    		Console.Write("YOUR MOVE?");
    		string input = Console.ReadLine();
    		if (int.TryParse(input, out int number)) {
    			return number;
    		}
    	}
    }
    
    int move(int number) {
    	return number - 8 * (int)((number - 1) / 8);
    }
    
    
    ================================================
    FILE: 89_Tic-Tac-Toe/csharp/tictactoe1/tictactoe1.csproj
    ================================================
    
    
      
        Exe
        net6.0
        enable
        enable
      
    
    
    
    
    ================================================
    FILE: 89_Tic-Tac-Toe/csharp/tictactoe2/Program.cs
    ================================================
    // See https://aka.ms/new-console-template for more information
    char[] board = new char[10];
    char human;
    char computer;
    int move = 0;
    char result;
    for(;;){
        // Print text on the screen with 30 spaces before text
        Console.WriteLine("TIC TAC TOE".PadLeft(30));
        // Print text on screen with 15 spaces before text
        Console.WriteLine("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY".PadLeft(15));
        // THIS PROGRAM PLAYS TIC TAC TOE
        Console.WriteLine("THE BOARD IS NUMBERED:");
        Console.WriteLine("1  2  3");
        Console.WriteLine("4  5  6");
        Console.WriteLine("7  8  9");
        Console.Write("\n\nDO YOU WANT 'X' OR 'O'");
        var key = Console.ReadKey();
    	Console.WriteLine();
    	// cleanup the board
    	for(int i=0; i < 10; i++) {
    		board[i]=' ';
    	}
    	// X GOES FIRST
        if (key.Key == ConsoleKey.X) {
    		human = 'X';
    		computer = 'O';
    		move = readYourMove();
    		board[move] = human;
    		printBoard();
        } else {
    		human = 'O';
    		computer = 'X';
        }
    	for(;;){
    		Console.WriteLine("THE COMPUTER MOVES TO...");
    		move = computerMove(move);
    		board[move] = computer;
    		result = printBoard();
    		printResult(result);
    		move = readYourMove();
    		board[move] = human;
    		result = printBoard();
    		printResult(result);
    	}
    }
    
    void printResult(int result) {
    	if (result == '\0') {
    		Console.WriteLine("IT'S A DRAW. THANK YOU.");
    		Environment.Exit(0);
    	} else if (result == computer) {
    		Console.WriteLine("I WIN, TURKEY!!!");
    		Environment.Exit(0);
    	} else if (result == human) {
    		Console.WriteLine("YOU BEAT ME!! GOOD GAME.");
    		Environment.Exit(0);
    	}
    }
    char printBoard() {
    	for (int i=1; i < 10; i++){
    		Console.Write($" {board[i]} ");
    		if (i % 3 == 0) {
    			if (i < 9) {
    				Console.Write("\n---+---+---\n");
    			} else {
    				Console.Write("\n");
    			}
    		} else {
    			Console.Write("!");
    		}
    	}
    	// horizontal check
    	for (int i = 1; i <= 9; i += 3) {
    		if (board[i] != ' ' && (board[i] == board[i+1]) && (board[i+1] == board[i+2])) {
    			return board[i];
    		}
    	}
    	// vertical check
    	for (int i = 1; i <= 3; i++) {
    		if (board[i] != ' ' && (board[i] == board[i+3]) && (board[i] == board[i+6])) {
    			return board[i];
    		}
    	}
    	// cross
    	if (board[5] != ' ') {
    		if ((board[1] == board[5] && board[9] == board[5]) || (board[3] == board[5] && board[7] == board[5])) {
    			return board[5];
    		}
    	}
    	// draw check
    	for (int i = 1; i <= 9; i++) {
    		if (board[i] == ' ') {
    			return ' ';
    		}
    	}
    	return '\0';
    }
    
    int readYourMove()  {
    	int number = 0;
        for(;;) {
    		Console.Write("\n\nWHERE DO YOU MOVE? ");
    		var key = Console.ReadKey();
            Console.WriteLine();
    		if (key.Key == ConsoleKey.D0) {
    			Console.WriteLine("THANKS FOR THE GAME.");
                Environment.Exit(0);
            }
            if (key.Key >= ConsoleKey.D1 && key.Key <= ConsoleKey.D9) {
                number = key.Key - ConsoleKey.D0;
                if (number > 9 || board[number] != ' ') {
                    Console.WriteLine("THAT SQUARE IS OCCUPIED.\n");
                    continue;
                }
            }
    		return number;
    	}
    }
    
    int getIndex(int number) {
    	return ((number - 1) % 8) + 1; //number - 8 * (int)((number - 1) / 8);
    }
    int computerMove(int lastMove) {
    	int[] boardMap = new int[] {0, 1, 2, 3, 6, 9, 8, 7, 4, 5};
    	int index = Array.IndexOf(boardMap, lastMove);
    	if (lastMove == 0 || board[5] == ' '){
    		return 5;
    	}
    	if (lastMove == 5) {
    		return 1;
    	}
    	if (board[5] == human) {
    		// check possible win
    		if (board[1] == computer && board[2] == ' ' && board[3] == computer) {
    			return 2;
    		}
    		if (board[7] == computer && board[8] == ' ' && board[9] == computer) {
    			return 8;
    		}
    		if (board[1] == computer && board[4] == ' ' && board[7] == computer) {
    			return 4;
    		}
    		if (board[3] == computer && board[6] == ' ' && board[7] == computer) {
    			return 6;
    		}
    		// check cross
    		int crossIndex = boardMap[getIndex(index + 4)];
    		if (board[crossIndex] == ' ') {
    			return crossIndex;
    		}
    		int stepForward2 = boardMap[getIndex(index + 2)];
    		if (board[stepForward2] == ' ') {
    			return stepForward2;
    		}
    		int stepBackward2 = boardMap[getIndex(index + 6)];
    		if (board[stepBackward2] == ' ') {
    			return stepBackward2;
    		}
    		int stepForward1 = boardMap[getIndex(index + 1)];
    		if (board[stepForward1] == ' ') {
    			return stepForward1;
    		}
    		int stepBackward1 = boardMap[getIndex(index + 7)];
    		if (board[stepBackward1] == ' ') {
    			return stepBackward1;
    		}
    		int stepForward3 = boardMap[getIndex(index + 3)];
    		if (board[stepForward3] == ' ') {
    			return stepForward3;
    		}
    		int stepBackward3 = boardMap[getIndex(index + 5)];
    		if (board[stepBackward3] == ' ') {
    			return stepBackward3;
    		}
    	} else {
    		// check possible win
    		if (board[1] == computer && board[9] == ' ') {
    			return 9;
    		}
    		if (board[9] == computer && board[1] == ' ') {
    			return 1;
    		}
    		if (board[3] == computer && board[7] == ' ') {
    			return 7;
    		}
    		if (board[7] == computer && board[3] == ' ') {
    			return 3;
    		}
    		// if corner
    		if (index % 2 == 1) {
    			int stepForward2 = boardMap[getIndex(index + 2)];
    			if (board[stepForward2] == ' ') {
    				return stepForward2;
    			}
    			int stepBackward2 = boardMap[getIndex(index + 6)];
    			if (board[stepBackward2] == ' ') {
    				return stepBackward2;
    			}
    		} else {
    			int stepForward1 = boardMap[getIndex(index + 1)];
    			if (board[stepForward1] == ' ') {
    				return stepForward1;
    			}
    			int stepBackward1 = boardMap[getIndex(index + 7)];
    			if (board[stepBackward1] == ' ') {
    				return stepBackward1;
    			}
    			int stepForward3 = boardMap[getIndex(index + 3)];
    			if (board[stepForward3] == ' ') {
    				return stepForward3;
    			}
    			int stepBackward3 = boardMap[getIndex(index + 5)];
    			if (board[stepBackward3] == ' ') {
    				return stepBackward3;
    			}
    					int crossIndex = boardMap[getIndex(index + 4)];
    			if (board[crossIndex] == ' ') {
    				return crossIndex;
    			}
    			int stepForward2 = boardMap[getIndex(index + 2)];
    			if (board[stepForward2] == ' ') {
    				return stepForward2;
    			}
    			int stepBackward2 = boardMap[getIndex(index + 6)];
    			if (board[stepBackward2] == ' ') {
    				return stepBackward2;
    			}
    		}
    	}
    	return 0;
    }
    
    
    ================================================
    FILE: 89_Tic-Tac-Toe/csharp/tictactoe2/tictactoe2.csproj
    ================================================
    
    
      
        Exe
        net6.0
        enable
        enable
      
    
    
    
    
    ================================================
    FILE: 89_Tic-Tac-Toe/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 89_Tic-Tac-Toe/java/src/Board.java
    ================================================
    /**
     * @author Ollie Hensman-Crook
     */
    public class Board {
        private char arr[];
    
        public Board() {
            this.arr = new char[9];
            for (int x = 1; x <= 9; x++) {
                this.arr[x - 1] = ' ';
            }
        }
    
    
        /**
         * Place 'X' or 'O' on the board position passed
         * @param position
         * @param player
         */
        public void setArr(int position, char player) {
            if (player == 'X') {
                this.arr[position-1] = 'X';
            } else {
                this.arr[position -1] = 'O';
            }
        }
    
        public void printBoard() {
            System.out.format("%-3c ! %-3c ! %-3c\n----+----+----\n%-3c ! %-3c ! %-3c\n----+----+----\n%-3c ! %-3c ! %-3c\n",
            this.arr[0], this.arr[1], this.arr[2], this.arr[3], this.arr[4], this.arr[5], this.arr[6], this.arr[7], this.arr[8]
            );
        }
    
    
        /**
         * @param x
         * @return the value of the char at a given position
         */
        public char getBoardValue(int x) {
            return arr[x-1];
        }
    
    
        /**
         * Go through the board and check for win (horizontal, diagonal, vertical)
         * @param player
         * @return whether a win has occured
         */
        public boolean checkWin(char player) {
            if(this.arr[0] == player && this.arr[1] == player && this.arr[2] == player)
                return true;
    
    
            if(this.arr[3] == player && this.arr[4] == player && this.arr[5] == player)
                return true;
    
    
            if(this.arr[6] == player && this.arr[7] == player && this.arr[8] == player)
                return true;
    
            if(this.arr[0] == player && this.arr[4] == player && this.arr[8] == player)
                return true;
    
            if(this.arr[2] == player && this.arr[4] == player && this.arr[6] == player)
                return true;
    
            if(this.arr[0] == player && this.arr[3] == player && this.arr[6] == player)
                return true;
    
            if(this.arr[1] == player && this.arr[4] == player && this.arr[7] == player)
                return true;
    
            if(this.arr[2] == player && this.arr[5] == player && this.arr[8] == player)
                return true;
    
            return false;
        }
        public boolean checkDraw() {
            if(this.checkWin('X') == false && this.checkWin('O') == false) {
                if(this.getBoardValue(1) != ' ' && this.getBoardValue(2) != ' ' && this.getBoardValue(3) != ' ' && this.getBoardValue(4) != ' ' && this.getBoardValue(5) != ' ' && this.getBoardValue(6) != ' ' && this.getBoardValue(7) != ' ' && this.getBoardValue(8) != ' ' && this.getBoardValue(9) != ' ' ) {
                    return true;
                }
            }
    
            return false;
        }
        /**
         * Reset the board
         */
        public void clear() {
            for (int x = 1; x <= 9; x++) {
                this.arr[x - 1] = ' ';
            }
        }
    
    }
    
    
    ================================================
    FILE: 89_Tic-Tac-Toe/java/src/TicTacToe2.java
    ================================================
    import java.util.Scanner;
    import java.util.Random;
    
    /**
     * @author Ollie Hensman-Crook
     */
    public class TicTacToe2 {
        public static void main(String[] args) {
            Board gameBoard = new Board();
            Random compChoice = new Random();
            char yourChar;
            char compChar;
            Scanner in = new Scanner(System.in);
    
            System.out.println("              TIC-TAC-TOE");
            System.out.println("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
            System.out.println("\nTHE BOARD IS NUMBERED: ");
            System.out.println(" 1  2  3\n 4  5  6\n 7  8  9\n");
    
            while (true) {
                // ask if the player wants to be X or O and if their input is valid set their
                // play piece as such
                System.out.println("DO YOU WANT 'X' OR 'O'");
                while (true) {
                    try {
                        char input;
                        input = in.next().charAt(0);
    
                        if (input == 'X' || input == 'x') {
                            yourChar = 'X';
                            compChar = 'O';
                            break;
                        } else if (input == 'O' || input == 'o') {
                            yourChar = 'O';
                            compChar = 'X';
                            break;
                        } else {
                            System.out.println("THATS NOT 'X' OR 'O', TRY AGAIN");
                            in.nextLine();
                        }
    
                    } catch (Exception e) {
                        System.out.println("THATS NOT 'X' OR 'O', TRY AGAIN");
                        in.nextLine();
                    }
                }
    
                while (true) {
                    System.out.println("WHERE DO YOU MOVE");
    
                    // check the user can move where they want to and if so move them there
                    while (true) {
                        int input;
                        try {
                            input = in.nextInt();
                            if (gameBoard.getBoardValue(input) == ' ') {
                                gameBoard.setArr(input, yourChar);
                                break;
                            } else {
                                System.out.println("INVALID INPUT, TRY AGAIN");
                            }
                            in.nextLine();
                        } catch (Exception e) {
                            System.out.println("INVALID INPUT, TRY AGAIN");
                            in.nextLine();
                        }
                    }
    
                    gameBoard.printBoard();
                    System.out.println("THE COMPUTER MOVES TO");
    
                    while (true) {
                        int position = 1 + compChoice.nextInt(9);
                        if (gameBoard.getBoardValue(position) == ' ') {
                            gameBoard.setArr(position, compChar);
                            break;
                        }
                    }
    
                    gameBoard.printBoard();
    
                    // if there is a win print if player won or the computer won and ask if they
                    // want to play again
                    if (gameBoard.checkWin(yourChar)) {
                        System.out.println("YOU WIN, PLAY AGAIN? (Y/N)");
                        gameBoard.clear();
                        while (true) {
                            try {
                                char input;
                                input = in.next().charAt(0);
    
                                if (input == 'Y' || input == 'y') {
                                    break;
                                } else if (input == 'N' || input == 'n') {
                                    System.exit(0);
                                } else {
                                    System.out.println("THATS NOT 'Y' OR 'N', TRY AGAIN");
                                    in.nextLine();
                                }
    
                            } catch (Exception e) {
                                System.out.println("THATS NOT 'Y' OR 'N', TRY AGAIN");
                                in.nextLine();
                            }
                        }
                        break;
                    } else if (gameBoard.checkWin(compChar)) {
                        System.out.println("YOU LOSE, PLAY AGAIN? (Y/N)");
                        gameBoard.clear();
                        while (true) {
                            try {
                                char input;
                                input = in.next().charAt(0);
    
                                if (input == 'Y' || input == 'y') {
                                    break;
                                } else if (input == 'N' || input == 'n') {
                                    System.exit(0);
                                } else {
                                    System.out.println("THATS NOT 'Y' OR 'N', TRY AGAIN");
                                    in.nextLine();
                                }
    
                            } catch (Exception e) {
                                System.out.println("THATS NOT 'Y' OR 'N', TRY AGAIN");
                                in.nextLine();
                            }
                        }
                        break;
                    } else if (gameBoard.checkDraw()) {
                        System.out.println("DRAW, PLAY AGAIN? (Y/N)");
                        gameBoard.clear();
                        while (true) {
                            try {
                                char input;
                                input = in.next().charAt(0);
    
                                if (input == 'Y' || input == 'y') {
                                    break;
                                } else if (input == 'N' || input == 'n') {
                                    System.exit(0);
                                } else {
                                    System.out.println("THATS NOT 'Y' OR 'N', TRY AGAIN");
                                    in.nextLine();
                                }
    
                            } catch (Exception e) {
                                System.out.println("THATS NOT 'Y' OR 'N', TRY AGAIN");
                                in.nextLine();
                            }
                        }
                        break;
                    }
    
                }
            }
        }
    }
    
    
    ================================================
    FILE: 89_Tic-Tac-Toe/javascript/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells)
    
    
    ================================================
    FILE: 89_Tic-Tac-Toe/javascript/tictactoe1.html
    ================================================
    
    
    TIC TAC TOE 1
    
    
    
    
    
    
    
    
    
    
    ================================================
    FILE: 89_Tic-Tac-Toe/javascript/tictactoe1.js
    ================================================
    // TIC TAC TOE 1
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    function mf(x)
    {
        return x - 8 * Math.floor((x - 1) / 8);
    }
    
    function computer_moves()
    {
        print("COMPUTER MOVES " + m + "\n");
    }
    
    var m;
    
    // Main control section
    async function main()
    {
        print(tab(30) + "TIC TAC TOE\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        //
        // This program plays Tic Tac Toe
        // The machine goes first
        print("THE GAME BOARD IS NUMBERED:\n");
        print("\n");
        print("1  2  3\n");
        print("8  9  4\n");
        print("7  6  5\n");
        print("\n");
        //
        // Main program
        while (1) {
            print("\n");
            print("\n");
            a = 9;
            m = a;
    
            computer_moves();
            print("YOUR MOVE");
            m = parseInt(await input());
    
            p = m;
            b = mf(p + 1);
            m = b;
    
            computer_moves();
            print("YOUR MOVE");
            m = parseInt(await input());
    
            q = m;
            if (q != mf(b + 4)) {
                c = mf(b + 4);
                m = c;
                computer_moves();
                print("AND WINS ********\n");
                continue;
            }
    
            c = mf(b + 2);
            m = c;
    
            computer_moves();
            print("YOUR MOVE");
            m = parseInt(await input());
    
            r = m;
            if (r != mf(c + 4)) {
                d = mf(c + 4);
                m = d;
                computer_moves();
                print("AND WINS ********\n");
                continue;
            }
    
            if (p % 2 == 0) {
                d = mf(c + 7);
                m = d;
                computer_moves();
                print("AND WINS ********\n");
                continue;
            }
    
            d = mf(c + 3);
            m = d;
    
            computer_moves();
            print("YOUR MOVE");
            m = parseInt(await input());
    
            s = m;
            if (s != mf(d + 4)) {
                e = mf(d + 4);
                m = e;
                computer_moves();
            }
            e = mf(d + 6);
            m = e;
            computer_moves();
            print("THE GAME IS A DRAW.\n");
        }
    }
    
    main();
    
    
    ================================================
    FILE: 89_Tic-Tac-Toe/javascript/tictactoe2.html
    ================================================
    
    
    TIC TAC TOE 2
    
    
    
    
    
    
    
    
    
    
    ================================================
    FILE: 89_Tic-Tac-Toe/javascript/tictactoe2.js
    ================================================
    // TIC TAC TOE 2
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    var s = [];
    
    function who_win(piece)
    {
        if (piece == -1) {
            print("I WIN, TURKEY!!!\n");
        } else if (piece == 1) {
            print("YOU BEAT ME!! GOOD GAME.\n");
        }
    }
    
    function show_board()
    {
        print("\n");
        for (i = 1; i <= 9; i++) {
            print(" ");
            if (s[i] == -1) {
                print(qs + " ");
            } else if (s[i] == 0) {
                print("  ");
            } else {
                print(ps + " ");
            }
            if (i == 3 || i == 6) {
                print("\n");
                print("---+---+---\n");
            } else if (i != 9) {
                print("!");
            }
        }
        print("\n");
        print("\n");
        print("\n");
        for (i = 1; i <= 7; i += 3) {
            if (s[i] && s[i] == s[i + 1] && s[i] == s[i + 2]) {
                who_win(s[i]);
                return true;
            }
        }
        for (i = 1; i <= 3; i++) {
            if (s[i] && s[i] == s[i + 3] && s[i] == s[i + 6]) {
                who_win(s[i]);
                return true;
            }
        }
        if (s[1] && s[1] == s[5] && s[1] == s[9]) {
            who_win(s[1]);
            return true;
        }
        if (s[3] && s[3] == s[5] && s[3] == s[7]) {
            who_win(s[3]);
            return true;
        }
        for (i = 1; i <= 9; i++) {
            if (s[i] == 0)
                break;
        }
        if (i > 9) {
            print("IT'S A DRAW. THANK YOU.\n");
            return true;
        }
        return false;
    }
    
    // Main control section
    async function main()
    {
        print(tab(30) + "TIC-TAC-TOE\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        for (i = 1; i <= 9; i++)
            s[i] = 0;
        print("THE BOARD IS NUMBERED:\n");
        print(" 1  2  3\n");
        print(" 4  5  6\n");
        print(" 7  8  9\n");
        print("\n");
        print("\n");
        print("\n");
        print("DO YOU WANT 'X' OR 'O'");
        str = await input();
        if (str == "X") {
            ps = "X";
            qs = "O";
            first_time = true;
        } else {
            ps = "O";
            qs = "X";
            first_time = false;
        }
        while (1) {
            if (!first_time) {
                g = -1;
                h = 1;
                if (s[5] == 0) {
                    s[5] = -1;
                } else if (s[5] == 1 && s[1] == 0) {
                    s[1] = -1;
                } else if (s[5] != 1 && s[2] == 1 && s[1] == 0 || s[5] != 1 && s[4] == 1 && s[1] == 0) {
                    s[1] = -1;
                } else if (s[5] != 1 && s[6] == 1 && s[9] == 0 || s[5] != 1 && s[8] == 1 && s[9] == 0) {
                    s[9] = -1;
                } else {
                    while (1) {
                        played = false;
                        if (g == 1) {
                            j = 3 * Math.floor((m - 1) / 3) + 1;
                            if (3 * Math.floor((m - 1) / 3) + 1 == m)
                                k = 1;
                            if (3 * Math.floor((m - 1) / 3) + 2 == m)
                                k = 2;
                            if (3 * Math.floor((m - 1) / 3) + 3 == m)
                                k = 3;
                        } else {
                            j = 1;
                            k = 1;
                        }
                        while (1) {
                            if (s[j] == g) {
                                if (s[j + 2] == g) {
                                    if (s[j + 1] == 0) {
                                        s[j + 1] = -1;
                                        played = true;
                                        break;
                                    }
                                } else {
                                    if (s[j + 2] == 0 && s[j + 1] == g) {
                                        s[j + 2] = -1;
                                        played = true;
                                        break;
                                    }
                                }
                            } else {
                                if (s[j] != h && s[j + 2] == g && s[j + 1] == g) {
                                    s[j] = -1;
                                    played = true;
                                    break;
                                }
                            }
                            if (s[k] == g) {
                                if (s[k + 6] == g) {
                                    if (s[k + 3] == 0) {
                                        s[k + 3] = -1;
                                        played = true;
                                        break;
                                    }
                                } else {
                                    if (s[k + 6] == 0 && s[k + 3] == g) {
                                        s[k + 6] = -1;
                                        played = true;
                                        break;
                                    }
                                }
                            } else {
                                if (s[k] != h && s[k + 6] == g && s[k + 3] == g) {
                                    s[k] = -1;
                                    played = true;
                                    break;
                                }
                            }
                            if (g == 1)
                                break;
                            if (j == 7 && k == 3)
                                break;
                            k++;
                            if (k > 3) {
                                k = 1;
                                j += 3;
                                if (j > 7)
                                    break;
                            }
                        }
                        if (!played) {
                            if (s[5] == g) {
                                if (s[3] == g && s[7] == 0) {
                                    s[7] = -1;
                                    played = true;
                                } else if (s[9] == g && s[1] == 0) {
                                    s[1] = -1;
                                    played = true;
                                } else if (s[7] == g && s[3] == 0) {
                                    s[3] = -1;
                                    played = true;
                                } else if (s[9] == 0 && s[1] == g) {
                                    s[9] = -1;
                                    played = true;
                                }
                            }
                            if (!played) {
                                if (g == -1) {
                                    g = 1;
                                    h = -1;
                                }
                            }
                        }
                        if (played)
                            break;
                    }
                    if (!played) {
                        if (s[9] == 1 && s[3] == 0 && s[1] != 1) {
                            s[3] = -1;
                        } else {
                            for (i = 2; i <= 9; i++) {
                                if (s[i] == 0) {
                                    s[i] = -1;
                                    break;
                                }
                            }
                            if (i > 9) {
                                s[1] = -1;
                            }
                        }
                    }
                }
                print("\n");
                print("THE COMPUTER MOVES TO...");
                if (show_board())
                    break;
            }
            first_time = false;
            while (1) {
                print("\n");
                print("WHERE DO YOU MOVE");
                m = parseInt(await input());
                if (m == 0) {
                    print("THANKS FOR THE GAME.\n");
                    break;
                }
                if (m >= 1 && m <= 9 && s[m] == 0)
                    break;
                print("THAT SQUARE IS OCCUPIED.\n");
                print("\n");
                print("\n");
            }
            g = 1;
            s[m] = 1;
            if (show_board())
                break;
        }
    }
    
    main();
    
    
    ================================================
    FILE: 89_Tic-Tac-Toe/kotlin/Board.kt
    ================================================
    /**
     * @author John Long based on Java by Ollie Hensman-Crook
     */
    
    enum class Player(val char: Char) {
        X('X'),
        O('O')
    }
    
    class Board {
        // Initialize an array of size nine with all values set to null
        private var boxes: Array = arrayOfNulls(9)
    
        /**
         * Place 'X' or 'O' on the board position passed
         * @param position
         * @param player
         */
        fun setArr(position: Int, player: Player) {
            boxes[position - 1] = player
        }
    
        fun printBoard() {
            System.out.format(
                """
      %c !  %c !  %c
    ----+----+----
      %c !  %c !  %c
    ----+----+----
      %c !  %c !  %c
    """,
                // converts each box to a char and then passes them in order to format
                // If the person is unassigned, use a space ' '
         *(boxes.map{it?.char ?: ' '}.toTypedArray()))
        }
    
        /**
         * @param x
         * @return the value of the char at a given position
         */
        fun getBoardValue(x: Int): Player? {
            return boxes[x - 1]
        }
    
        private val winningCombos = listOf(
            // horizontal
            listOf(0,1,2),
            listOf(3,4,5),
            listOf(6,7,8),
            // diagonal
            listOf(0,4,8),
            listOf(2,4,6),
            // vertical
            listOf(0,3,6),
            listOf(1,4,7),
            listOf(2,5,8)
        )
        /**
         * Go through the board and check for win
         * @param player
         * @return whether a win has occurred
         */
        fun isWinFor(player: Player): Boolean {
            // Check if any winningCombos have all their boxes set to player
            return winningCombos.any{ combo ->
                combo.all { boxes[it] == player }
            }
        }
    
        fun isDraw(): Boolean {
            return !isWinFor(Player.X) && !isWinFor(Player.O) && boxes.all { it != null }
        }
    
        /**
         * Reset the board
         */
        fun clear() {
            boxes = arrayOfNulls(9)
        }
    }
    
    
    ================================================
    FILE: 89_Tic-Tac-Toe/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 89_Tic-Tac-Toe/kotlin/TicTacToe2.kt
    ================================================
    import java.util.Random
    import kotlin.system.exitProcess
    
    /**
     * @author John Long based on Java from Ollie Hensman-Crook
     */
    private val compChoice = Random()
    private val gameBoard = Board()
    
    fun main() {
        println("              TIC-TAC-TOE")
        println("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY")
        println("\nTHE BOARD IS NUMBERED: ")
        println(" 1  2  3\n 4  5  6\n 7  8  9\n")
        while (true) {
            // Let the player choose whether to be X or O (Player.X or Player.O)
            val (human, computer) = readXOrO()
            while (true) {
                // Get a valid move from the user and then move there
                val validMoveIndex = readValidMove()
                gameBoard.setArr(validMoveIndex, human)
                gameBoard.printBoard()
    
                // Computer randomly fills a square (if the game isn't already over)
                // This uses Kotlin's null handling and will only set the board
                // if validRandomMove returned a non-null value
                validRandomMove()?.let {
                    gameBoard.setArr(it, computer)
                    gameBoard.printBoard()
                }
    
                // if there is a win print if player won or the computer won and ask if they
                // want to play again
                when {
                    gameBoard.isWinFor(human) -> {
                        checkPlayAgain("YOU WIN")
                        break
                    }
                    gameBoard.isWinFor(computer) -> {
                        checkPlayAgain("YOU LOSE")
                        break
                    }
                    gameBoard.isDraw() -> {
                        checkPlayAgain("DRAW")
                        break
                    }
                }
            }
        }
    }
    
    private fun checkPlayAgain(result: String) {
        println("$result, PLAY AGAIN? (Y/N)")
        gameBoard.clear()
        if (!readYesOrNo()) exitProcess(0)
    }
    
    private fun readYesOrNo(): Boolean {
        while (true) {
            when (readLine()?.get(0)?.uppercaseChar()) {
                'Y' -> return true
                'N' -> return false
                else -> println("THAT'S NOT 'Y' OR 'N', TRY AGAIN")
            }
        }
    }
    
    private fun validRandomMove(): Int? {
        if (gameBoard.isDraw() || gameBoard.isWinFor(Player.O) || gameBoard.isWinFor(Player.X)) return null
        println("THE COMPUTER MOVES TO")
        // keep generating a random value until we find one that is null (unset)
        return generateSequence { 1 + compChoice.nextInt(9) }.first { gameBoard.getBoardValue(it) == null }
    }
    
    private fun readValidMove(): Int {
        println("WHERE DO YOU MOVE")
        while (true) {
            val input = readln().toIntOrNull()
            if (input != null && gameBoard.getBoardValue(input) == null) {
                return input
            } else {
                println("INVALID INPUT, TRY AGAIN")
            }
        }
    }
    
    private fun readXOrO(): Pair {
        println("DO YOU WANT 'X' OR 'O'")
        while (true) {
            when (readln()[0].uppercaseChar()) {
                'X' -> return Player.X to Player.O
                'O' -> return Player.O to Player.X
                else -> println("THAT'S NOT 'X' OR 'O', TRY AGAIN")
            }
        }
    }
    
    
    ================================================
    FILE: 89_Tic-Tac-Toe/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 89_Tic-Tac-Toe/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 89_Tic-Tac-Toe/perl/tictactoe2.pl
    ================================================
    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    #GLOBALs
    my %board = (
    	1	=>	0,
    	2	=>	0,
    	3	=>	0,
    	4	=>	0,
    	5	=>	0,
    	6	=>	0,
    	7	=>	0,
    	8	=>	0,
    	9	=>	0,
    );
    
    my %winning_combos = (
    	1	=> [1,2,3],
    	2	=> [4,5,6],
    	3	=> [7,8,9],
    	4	=> [1,4,7],
    	5	=> [2,5,8],
    	6	=> [3,6,9],
    	7	=> [1,5,9],
    	8	=> [7,5,3],
    );
    
    my $player=100;
    my $player_goal=0;
    my $computer=100;
    my $computer_goal=0;
    my $count=0;
    
    &main;
    
    sub main {
    	&print_intro;
    	print "DO YOU WANT 'X' OR 'O'\n";
    	chomp(my $ans = );
    	&assign_X_and_O($ans);
    	if ($ans eq "X") {
    		until ($count >= 9) {
    			&player_choice;
    			$count++;
    			&print_board;
    			&check_for_winners;
    			&computer_choice;
    			$count++;
    			&print_board;
    			&check_for_winners;
    		}
    	}
    	else {
    		until ($count >= 9) {
    			&computer_choice;
    			$count++;
    			&print_board;
    			&check_for_winners;
    			if ($count >= 9) {
    				print "IT'S A DRAW. THANK YOU.\n";
    				exit;
    			}
    			&player_choice;
    			$count++;
    			&print_board;
    			&check_for_winners;
    		}
    	}
    	print "IT'S A DRAW. THANK YOU.\n";
    	exit;
    }
    
    # This will check to see if anyone has won by adding up the various 3-in-a-row lines.
    sub check_for_winners {
    	my %tally;
    	foreach my $key (keys %winning_combos) {
    		foreach my $val (@{$winning_combos{$key}}) {
    			$tally{$key}+=$board{$val};
    		}
    	}
    	foreach my $key (keys %tally) {
    		if ($tally{$key} == $player_goal) {
    			print "YOU BEAT ME!! GOOD GAME.\n";
    			exit;
    		}
    		if ($tally{$key} == $computer_goal) {
    			print "I WIN, TURKEY!!!\n";
    			exit;
    		}
    	}
    }
    
    #On the computer's turn it will first check to see if it should block the player.  If it finds it isn't going to win or need to block a player, the it will choose a spot to place it's X or O.
    
    sub computer_choice {
    	my $move;
    	$move=&check_for_blocks_or_wins;;
    	if ($move > 9) {
    		$move=&check_for_corners;
    	}
    	print "THE COMPUTER MOVES TO...\n";
    	$board{$move}=$computer;
    }
    
    sub check_for_corners {
    	my @precedence;
    	if ($count == 0) {
    		@precedence=(1,9,7,3,5,2,4,6,8);
    	}
    	else {
    		@precedence=(5,2,4,6,8,1,9,7,3);
    	}
    	foreach my $move (@precedence) {
    		my $validity=&check_occupation($move);
    		if ($validity eq "valid") {
    			return $move;
    		}
    	}
    }
    
    sub check_for_blocks_or_wins {
    	my %tally;
    	my $validity = "invalid";
    	my $move = 10;
    	foreach my $key (keys %winning_combos) {
    		foreach my $val (@{$winning_combos{$key}}) {
    			$tally{$key}+=$board{$val};
    		}
    	}
    	foreach my $key (keys %tally) {
    		if (abs($tally{$key}) == 2) {
    			until ($validity eq "valid") {
    				foreach my $val (@{$winning_combos{$key}}) {
    					$validity=&check_occupation($val);
    					if ($validity eq "valid") {
    						$move = $val;
    						last;
    					}
    				}
    			}
    			return $move;
    		}
    	}
    	return $move;
    }
    
    sub player_choice {
    	my $validity = "invalid";
    	my $ans = "";
    	until ($validity eq "valid") {
    		print "WHERE DO YOU MOVE? ";
    		chomp($ans = );
    		$validity=&check_occupation($ans);
    		if ($validity eq "invalid") {print "THAT SQUARE IS OCCUPIED.\n\n"}
    	}
    	$board{$ans}=$player;
    }
    
    sub check_occupation {
    	my $space = shift;
    	if ($board{$space}==0) { return "valid" }
    	else {return "invalid"};
    }
    
    sub print_board {
    	foreach my $num (1..9) {
    		my $char = &which_char($board{$num});
    		if ($num == 4 || $num == 7) { print "\n---+---+---\n";}
    		print "$char";
    		if ($num % 3 > 0) { print "!" }
    	}
    	print "\n";
    }
    
    sub which_char {
    	my $val=shift;
    	if ($val == 0) {return "   ";}
    	elsif ($val == 1) {return " X ";}
    	else {return " O ";}
    }
    
    sub assign_X_and_O {
    	my $ans = shift;
    	if ($ans eq "X") {
    		$player = 1;
    		$computer = -1;
    		$player_goal=3;
    		$computer_goal=-3;
    	}
    	else {
    		$player = -1;
    		$computer = 1;
    		$player_goal=-3;
    		$computer_goal=3;
    	}
    }
    
    sub print_intro {
    	print ' ' x 30 . "TIC-TAC-TOE\n";
    	print ' ' x 15 . "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n\n\n";
    	print "THE BOARD IS NUMBERED:\n";
    	print "1  2  3\n4  5  6\n7  8  9\n\n\n";
    }
    
    
    ================================================
    FILE: 89_Tic-Tac-Toe/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 89_Tic-Tac-Toe/python/TicTacToe_Hard.py
    ================================================
    from typing import List, Tuple, Union
    
    
    class TicTacToe:
        def __init__(self, pick, sz=3) -> None:
            self.pick = pick
            self.dim_sz = sz
            self.board = self.clear_board()
    
        def clear_board(self) -> List[List[str]]:
            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:
                return "Out of Bounds"
            if self.board[r][c] != "blur":
                return "Spot Pre-Occupied"
            self.board[r][c] = self.pick
            return True
    
        def check_win(self) -> int:  # 1 you won, 0 computer won, -1 tie
            # Flag syntax -> first player no. ,
            # User is Player#1 ;
            # Check set 1 -> row and '\' diagonal & Check set 2 -> col and '/' diagonal
    
            for i in range(0, self.dim_sz):  # Rows
                flag11 = True
                flag21 = True
    
                flag12 = True
                flag22 = True
                for j in range(0, self.dim_sz):
    
                    ch2 = self.board[i][j]
                    ch1 = self.board[j][i]
                    # Row
                    if ch1 == self.pick:  # if it's mine, computer didn't make it
                        flag21 = False
                    elif ch1 == "blur":  # if it's blank no one made it
                        flag11 = False
                        flag21 = False
                    else:
                        flag11 = False  # else i didn't make it
    
                    if ch2 == self.pick:  # Same but for Col
                        flag22 = False
                    elif ch2 == "blur":
                        flag12 = False
                        flag22 = False
                    else:
                        flag12 = False
    
                if flag11 is True or flag12 is True:  # I won
                    return 1
                if flag21 is True or flag22 is True:  # Computer Won
                    return 0
    
            # Diagonals#
            flag11 = True
            flag21 = True
    
            flag12 = True
            flag22 = True
            for i in range(0, self.dim_sz):
    
                ch2 = self.board[i][i]
                ch1 = self.board[i][self.dim_sz - 1 - i]
    
                if ch1 == self.pick:
                    flag21 = False
                elif ch1 == "blur":
                    flag11 = False
                    flag21 = False
                else:
                    flag11 = False
    
                if ch2 == self.pick:
                    flag22 = False
                elif ch2 == "blur":
                    flag12 = False
                    flag22 = False
                else:
                    flag12 = False
    
            if flag11 or flag12:
                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 = "X" if self.pick == "O" else "O"
            for i in range(0, self.dim_sz):
                for j in range(0, self.dim_sz):
    
                    if self.board[i][j] == "blur":  # BLANK
                        t = (i, j)
                        available_moves.append(t)  # add it to available moves
                        self.board[i][j] = comp_pick  # Check if I (Computer can win)
                        if self.check_win() == 0:  # Best Case I(Computer) win!
                            return i, j
                        self.board[i][j] = self.pick
                        if (
                            self.check_win() == 1
                        ):  # Second Best Case, he (player) didn't won
                            player_win_spot.append(t)
                        self.board[i][j] = "blur"
    
            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 not available_moves:
                return -1, -1
    
            c1, c2 = self.dim_sz // 2, self.dim_sz // 2
            if (c1, c2) in available_moves:  # CENTER
                self.board[c1][c2] = comp_pick
                return c1, c2
            for i in range(c1 - 1, -1, -1):  # IN TO OUT
                gap = c1 - i
                # checking  - 4 possibilities at max
                # EDGES
                if (c1 - gap, c2 - gap) in available_moves:
                    self.board[c1 - gap][c2 - gap] = comp_pick
                    return c1 - gap, c2 - gap
                if (c1 - gap, c2 + gap) in available_moves:
                    self.board[c1 - gap][c2 + gap] = comp_pick
                    return c1 - gap, c2 + gap
                if (c1 + gap, c2 - gap) in available_moves:
                    self.board[c1 + gap][c2 - gap] = comp_pick
                    return c1 + gap, c2 - gap
                if (c1 + gap, c2 + gap) in available_moves:
                    self.board[c1 + gap][c2 + gap] = comp_pick
                    return c1 + gap, c2 + gap
    
                # Four Lines
    
                for i in range(0, gap):
                    if (c1 - gap, c2 - gap + i) in available_moves:  # TOP LEFT TO TOP RIGHT
                        self.board[c1 - gap][c2 - gap + i] = comp_pick
                        return c1 - gap, c2 - gap + i
                    if (
                        c1 + gap,
                        c2 - gap + i,
                    ) in available_moves:  # BOTTOM LEFT TO BOTTOM RIGHT
                        self.board[c1 + gap][c2 - gap + i] = comp_pick
                        return c1 + gap, c2 - gap + i
                    if (c1 - gap, c2 - gap) in available_moves:  # LEFT TOP TO LEFT BOTTOM
                        self.board[c1 - gap + i][c2 - gap] = comp_pick
                        return c1 - gap + i, c2 - gap
                    if (
                        c1 - gap + i,
                        c2 + gap,
                    ) in available_moves:  # RIGHT TOP TO RIGHT BOTTOM
                        self.board[c1 - gap + i][c2 + gap] = comp_pick
                        return c1 - gap + i, c2 + gap
            raise RuntimeError("No moves available")
    
    
    def display(game: TicTacToe) -> None:
        line1 = ""
        for i in range(0, game.dim_sz):
            for j in range(0, game.dim_sz - 1):
                if game.board[i][j] == "blur":
                    line1 = f"{line1}    |"
                else:
                    line1 = f"{line1}  {game.board[i][j]} |"
            if game.board[i][game.dim_sz - 1] == "blur":
                line1 = line1 + "    \n"
            else:
                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()
        game = TicTacToe("O") if pick == "O" else TicTacToe("X")
        display(game=game)
        while True:
            temp: Union[bool, str] = False
            while not temp:
                move = list(
                    map(
                        int,
                        input("Make A Move in Grid System from (0,0) to (2,2) ").split(),
                    )
                )
                temp = game.move_record(move[0], move[1])
                if not temp:
                    print(temp)
    
            if game.check_win() == 1:
                print("You Won!")
                break
            print("Your Move:- ")
            display(game)
            C1, C2 = game.next_move()
            if C1 == -1 and C2 == -1:
                print("Game Tie!")
                break
            if game.check_win() == 0:
                print("You lost!")
                break
            print("Computer's Move :-")
            display(game)
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 89_Tic-Tac-Toe/python/tictactoe2.py
    ================================================
    #!/usr/bin/env python3
    from enum import Enum
    
    
    class OccupiedBy(Enum):
        COMPUTER = -1
        EMPTY = 0
        PLAYER = 1
    
    
    class Winner(Enum):
        NONE = 0
        COMPUTER = 1
        PLAYER = 2
        DRAW = 3
    
    
    class Space(Enum):
        TOP_LEFT = 0
        TOP_CENTER = 1
        TOP_RIGHT = 2
        MID_LEFT = 3
        MID_CENTER = 4
        MID_RIGHT = 5
        BOT_LEFT = 6
        BOT_CENTER = 7
        BOT_RIGHT = 8
    
    
    def line_170(board, g, h, j, k):
        if g == OccupiedBy.Player and board[Space.MID_CENTER] == g:
            if (
                board[Space.TOP_RIGHT] == g and board[Space.BOTTOM_LEFT] is OccupiedBy.EMPTY
            ):  # Line 171
                return Space.BOTTOM_LEFT  # Line 187
            elif (
                board[Space.BOTTOM_RIGHT] == g and board[Space.TOP_LEFT] is OccupiedBy.EMPTY
            ):  # Line 172
                return Space.TOP_LEFT  # Line 181
            elif (
                board[Space.BOTTOM_LEFT] == g and board[Space.TOP_RIGHT] is OccupiedBy.EMPTY
            ) or (
                board[Space.BOTTOM_RIGHT] is OccupiedBy.PLAYER
                and board[Space.TOP_RIGHT] is OccupiedBy.EMPTY
            ):  # Line 173 and 174
                return Space.TOP_RIGHT  # Line 183 and Line 189
            elif g is OccupiedBy.COMPUTER:
                g = OccupiedBy.PLAYER
                h = OccupiedBy.COMPUTER
                return line_118(board, g, h, j, k)
    
    
    def line_150(board, g, h, j, k):
        if board[k] != g:  # line 150
            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
        elif board[k + 3]:  # line 156
            return -1
    
        return k + 6
    
    
    def line_120(board, g, h, j, k):
        pass
    
    
    def line_118(board, g, h):
        for j in range(7):
            for k in range(3):
                return line_120(board, g, h, j, k)
    
    
    def think(board, g, h, moves):
    
        if board[Space.MID_CENTER] is OccupiedBy.EMPTY:
            return Space.MID_CENTER
    
        if board[Space.MID_CENTER] is OccupiedBy.PLAYER:
            if (
                board[Space.TOP_CENTER] is OccupiedBy.PLAYER
                and board[Space.TOP_LEFT] is OccupiedBy.EMPTY
                or board[Space.MID_LEFT] is OccupiedBy.PLAYER
                and board[Space.TOP_LEFT] is OccupiedBy.EMPTY
            ):
                return Space.BOT_LEFT
            elif (
                board[Space.MID_RIGHT] is OccupiedBy.PLAYER
                and board[Space.BOT_RIGHT] is OccupiedBy.EMPTY
                or board[Space.BOT_CENTER] is OccupiedBy.PLAYER
                and board[Space.BOT_RIGHT] is OccupiedBy.EMPTY
            ):
                return Space.BOT_RIGHT
    
        if g == OccupiedBy.PLAYER:
            j = 3 * int((moves - 1) / 3)
            if move == j + 1:  # noqa: This definitely is a bug!
                k = 1
            if move == j + 2:  # noqa: This definitely is a bug!
                k = 2
            if move == j + 3:  # noqa: This definitely is a bug!
                k = 3
            return subthink(g, h, j, k)  # noqa: This definitely is a bug!
    
    
    def render_board(board, space_mapping):
        vertical_divider = "!"
        horizontal_divider = "---+---+---"
        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)
    
    
    def determine_winner(board, g):
        # Check for matching horizontal lines
        for i in range(Space.TOP_LEFT.value, Space.BOT_LEFT.value + 1, 3):  # Line 1095
            if board[i] != board[i + 1] or board[i] != board[i + 2]:  # Lines 1100 and 1105
                continue  # First third of Line 1115
            elif board[i] == OccupiedBy.COMPUTER:  #
                return Winner.COMPUTER
            elif board[i] == OccupiedBy.PLAYER:
                return Winner.PLAYER
    
        # Check for matching vertical lines
        for i in range(
            Space.TOP_LEFT.value, Space.TOP_RIGHT.value + 1, 1
        ):  # Second third of Line 1115
            if (
                board[i] != board[i + 3] or board[i] != board[i + 6]
            ):  # Last third of Line 1115
                continue  # First third of 1150
            elif board[i] == OccupiedBy.COMPUTER:  # Line 1135
                return Winner.COMPUTER
            elif board[i] == OccupiedBy.PLAYER:  # Line 1137
                return Winner.PLAYER
    
        # Check diagonals
        if any(space is OccupiedBy.EMPTY for space in board):
            if board[Space.MID_CENTER.value] != g:
                return Winner.NONE
            elif (
                board[Space.TOP_LEFT.value] == g and board[Space.BOT_RIGHT.value] == g
            ) or (board[Space.BOT_LEFT.value] == g and board[Space.TOP_RIGHT.value] == g):
                return Winner.COMPUTER if g is OccupiedBy.COMPUTER else Winner.PLAYER
            else:
                return Winner.NONE
    
        return Winner.DRAW
    
    
    def computer_think(board):
        empty_indices = [
            index for index, space in enumerate(board) if space is OccupiedBy.EMPTY
        ]
    
        return empty_indices[0]
    
    
    def prompt_player(board):
        while True:
            move = int(input("\nWHERE DO YOU MOVE? "))
    
            if move == 0:
                return 0
    
            if move > 9 or board[move - 1] is not OccupiedBy.EMPTY:
                print("THAT SQUARE IS OCCUPIED.\n\n")
                continue
    
            return move
    
    
    def main() -> None:
        print(" " * 30 + "TIC-TAC-TOE")
        print(" " * 15 + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY")
        print("\n\n")
    
        print("THE BOARD IS NUMBERED:")
        print(" 1  2  3")
        print(" 4  5  6")
        print(" 7  8  9")
        print("\n\n")
    
        # Default state
        board = [OccupiedBy.EMPTY] * 9
        current_player = OccupiedBy.PLAYER
        space_mapping = {
            OccupiedBy.EMPTY: "   ",
            OccupiedBy.PLAYER: " X ",
            OccupiedBy.COMPUTER: " O ",
        }
    
        symbol = input("DO YOU WANT 'X' OR 'O'? ").upper()
    
        # If the player doesn't choose X, then assume you want O
        # and the computer goes first.
        if symbol != "X":
            space_mapping[OccupiedBy.PLAYER] = " O "
            space_mapping[OccupiedBy.COMPUTER] = " X "
            current_player = OccupiedBy.COMPUTER
    
        while True:
            if current_player is OccupiedBy.PLAYER:
                move = prompt_player(board)
                if move == 0:
                    print("THANKS FOR THE GAME.")
                    break
                board[move - 1] = current_player
    
            elif current_player is OccupiedBy.COMPUTER:
                print("\nTHE COMPUTER MOVES TO...")
                board[computer_think(board)] = current_player
    
            print(render_board(board, space_mapping))
    
            winner = determine_winner(board, current_player)
    
            if winner is not Winner.NONE:
                print(winner)
                break
    
            if current_player is OccupiedBy.COMPUTER:
                current_player = OccupiedBy.PLAYER
            elif current_player is OccupiedBy.PLAYER:
                current_player = OccupiedBy.COMPUTER
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 89_Tic-Tac-Toe/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 89_Tic-Tac-Toe/rust/Cargo.toml
    ================================================
    [package]
    name = "rust"
    version = "0.1.0"
    [dependencies]
    
    
    ================================================
    FILE: 89_Tic-Tac-Toe/rust/README.md
    ================================================
    README.md
    
    Original source downloaded from Vintage Basic
    
    Conversion to Rust
    
    
    ================================================
    FILE: 89_Tic-Tac-Toe/rust/src/main.rs
    ================================================
    use std::fmt::{self, Display};
    use std::io::{stdin, stdout, Write};
    
    const WIN: [(usize, usize, usize); 8] = [
        (0, 1, 2),
        (3, 4, 5),
        (6, 7, 8),
        (0, 4, 8),
        (2, 4, 6),
        (0, 3, 6),
        (1, 4, 7),
        (2, 5, 8),
    ];
    
    type Board = [Sign; 9];
    
    fn main() {
        let mut board: Board = [Sign::E; 9];
        let mut sign = Sign::X;
        loop {
            clear();
            render(&board, &sign);
            let (win, winner) = check_board(board);
            if win {
                match winner {
                    Sign::X => break println!("Looks like X own this one!"),
                    Sign::O => break println!("O is the winner!!"),
                    Sign::C => break println!("Cat got this one!"),
                    Sign::E => {}
                }
            }
            let num = input("Pick a number 1 - 9:> ");
            if let Some(Sign::E) = board.get(num) {
                board.get_mut(num).map(|s| *s = sign);
                sign = if sign == Sign::X { Sign::O } else { Sign::X };
            }
        }
    }
    
    #[derive(Debug, Copy, Clone, PartialEq)]
    enum Sign {
        X,
        O,
        C,
        E,
    }
    
    impl Display for Sign {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            let sign = match self {
                Self::X => 'X',
                Self::O => 'O',
                Self::C => 'C',
                Self::E => ' ',
            };
            write!(f, "{}", sign)
        }
    }
    
    fn check_board(board: Board) -> (bool, Sign) {
        for &(a, b, c) in WIN.iter() {
            if board[a] == board[b] && board[a] == board[c] {
                return (true, board[a]);
            }
        }
        if !board.contains(&Sign::E) {
            return (true, Sign::C);
        }
        (false, Sign::E)
    }
    
    fn clear() {
        println!("\x1b[2J\x1b[0;0H");
    }
    
    fn input(message: &str) -> usize {
        let mut out = String::new();
        loop {
            print!("{}", message);
            stdout().flush().expect("Failed to flush to stdout.");
            stdin().read_line(&mut out).expect("Failed to read line");
            let num = out.trim().parse::();
            match num {
                Ok(n) => match n {
                    1..=9 => return n - 1,
                    _ => println!("The number needs to be between 1 - 9."),
                },
                Err(_) => println!("'{}' is not a number.", out.trim()),
            }
            out.clear();
        }
    }
    
    fn render(spots: &Board, sign: &Sign) {
        println!("                    The board is numbered");
        println!(
            " {} │ {} │ {}  [{}: Turn]    1 │ 2 │ 3",
            spots[0], spots[1], spots[2], sign
        );
        println!("⎼⎼⎼╄⎼⎼⎼╄⎼⎼⎼             ⎼⎼⎼╄⎼⎼⎼╄⎼⎼⎼");
        println!(
            " {} │ {} │ {}               4 │ 5 │ 6 ",
            spots[3], spots[4], spots[5]
        );
        println!("⎼⎼⎼╄⎼⎼⎼╄⎼⎼⎼             ⎼⎼⎼╄⎼⎼⎼╄⎼⎼⎼");
        println!(
            " {} │ {} │ {}               7 │ 8 │ 9 ",
            spots[6], spots[7], spots[8]
        );
    }
    
    
    ================================================
    FILE: 89_Tic-Tac-Toe/tictactoe1.bas
    ================================================
    10 PRINT TAB(30);"TIC TAC TOE"
    20 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    30 PRINT:PRINT:PRINT
    50 REM
    100 REM   THIS PROGRAM PLAYS TIC TAC TOE
    110 REM   THE MACHINE GOES FIRST
    120 PRINT "THE GAME BOARD IS NUMBERED:": PRINT
    130 PRINT "1  2  3": PRINT "8  9  4": PRINT "7  6  5"
    140 PRINT
    150 REM
    160 REM
    170 REM
    180 DEF FNM(X)=X-8*INT((X-1)/8)
    190 REM
    200 REM  MAIN PROGRAM
    210 PRINT
    220 PRINT
    230 A=9
    240 M=A
    250 GOSUB 650
    260 P=M
    270 B=FNM(P+1)
    280 M=B
    290 GOSUB 650
    300 Q=M
    310 IF Q=FNM(B+4) THEN 360
    320 C=FNM(B+4)
    330 M=C
    340 GOSUB 700
    350 GOTO 730
    360 C=FNM(B+2)
    370 M=C
    380 GOSUB 650
    390 R=M
    400 IF R=FNM(C+4) THEN 450
    410 D=FNM(C+4)
    420 M=D
    430 GOSUB 700
    440 GOTO 730
    450 IF P/2<>INT(P/2) THEN 500
    460 D=FNM(C+7)
    470 M=D
    480 GOSUB 700
    490 GOTO 730
    500 D=FNM(C+3)
    510 M=D
    520 GOSUB 650
    530 S=M
    540 IF S=FNM(D+4) THEN 590
    550 E=FNM(D+4)
    560 M=E
    570 GOSUB 700
    580 REM
    590 E=FNM(D+6)
    600 M=E
    610 GOSUB 700
    620 PRINT "THE GAME IS A DRAW."
    630 GOTO 210
    640 REM
    650 GOSUB 700
    660 PRINT "YOUR MOVE";
    670 INPUT M
    680 RETURN
    700 PRINT "COMPUTER MOVES";M
    710 RETURN
    720 REM
    730 PRINT "AND WINS ********"
    740 GOTO 210
    750 END
    
    
    ================================================
    FILE: 89_Tic-Tac-Toe/tictactoe2.bas
    ================================================
    2 PRINT TAB(30);"TIC-TAC-TOE"
    4 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    6 PRINT:PRINT:PRINT
    8 PRINT "THE BOARD IS NUMBERED:"
    10 PRINT " 1  2  3"
    12 PRINT " 4  5  6"
    14 PRINT " 7  8  9"
    16 PRINT:PRINT:PRINT
    20 DIM S(9)
    50 INPUT"DO YOU WANT 'X' OR 'O'";C$
    55 IF C$="X"THEN 475
    60 P$="O":Q$="X"
    100 G=-1:H=1:IF S(5)<>0 THEN 103
    102 S(5)=-1:GOTO 195
    103 IF S(5)<>1 THEN 106
    104 IF S(1)<>0 THEN 110
    105 S(1)=-1:GOTO 195
    106 IF S(2)=1 AND S(1)=0 THEN 181
    107 IF S(4)=1 AND S(1)=0 THEN 181
    108 IF S(6)=1 AND S(9)=0 THEN 189
    109 IF S(8)=1 AND S(9)=0 THEN 189
    110 IF G=1 THEN 112
    111 GOTO 118
    112 J=3*INT((M-1)/3)+1
    113 IF 3*INT((M-1)/3)+1=M THEN K=1
    114 IF 3*INT((M-1)/3)+2=M THEN K=2
    115 IF 3*INT((M-1)/3)+3=M THEN K=3
    116 GOTO 120
    118 FOR J=1 TO 7 STEP 3:FOR K=1 TO 3
    120 IF S(J)<>G THEN 130
    122 IF S(J+2)<>G THEN 135
    126 IF S(J+1)<>0 THEN 150
    128 S(J+1)=-1:GOTO 195
    130 IF S(J)=H THEN 150
    131 IF S(J+2)<>G THEN 150
    132 IF S(J+1)<>G THEN 150
    133 S(J)=-1:GOTO 195
    135 IF S(J+2)<>0 THEN 150
    136 IF S(J+1)<>G THEN 150
    138 S(J+2)=-1:GOTO 195
    150 IF S(K)<>G THEN 160
    152 IF S(K+6)<>G THEN 165
    156 IF S(K+3)<>0 THEN 170
    158 S(K+3)=-1:GOTO 195
    160 IF S(K)=H THEN 170
    161 IF S(K+6)<>G THEN 170
    162 IF S(K+3)<>G THEN 170
    163 S(K)=-1:GOTO 195
    165 IF S(K+6)<>0 THEN 170
    166 IF S(K+3)<>G THEN 170
    168 S(K+6)=-1:GOTO 195
    170 GOTO 450
    171 IF S(3)=G AND S(7)=0 THEN 187
    172 IF S(9)=G AND S(1)=0 THEN 181
    173 IF S(7)=G AND S(3)=0 THEN 183
    174 IF S(9)=0 AND S(1)=G THEN 189
    175 IF G=-1 THEN G=1:H=-1:GOTO 110
    176 IF S(9)=1 AND S(3)=0 THEN 182
    177 FOR I=2 TO 9:IF S(I)<>0 THEN 179
    178 S(I)=-1:GOTO 195
    179 NEXT I
    181 S(1)=-1:GOTO 195
    182 IF S(1)=1 THEN 177
    183 S(3)=-1:GOTO 195
    187 S(7)=-1:GOTO 195
    189 S(9)=-1
    195 PRINT:PRINT"THE COMPUTER MOVES TO..."
    202 GOSUB 1000
    205 GOTO 500
    450 IF G=1 THEN 465
    455 IF J=7 AND K=3 THEN 465
    460 NEXT K,J
    465 IF S(5)=G THEN 171
    467 GOTO 175
    475 P$="X":Q$="O"
    500 PRINT:INPUT"WHERE DO YOU MOVE";M
    502 IF M=0 THEN PRINT"THANKS FOR THE GAME.":GOTO 2000
    503 IF M>9 THEN 506
    505 IF S(M)=0 THEN 510
    506 PRINT"THAT SQUARE IS OCCUPIED.":PRINT:PRINT:GOTO 500
    510 G=1:S(M)=1
    520 GOSUB 1000
    530 GOTO 100
    1000 PRINT:FOR I=1 TO 9:PRINT" ";:IF S(I)<>-1 THEN 1014
    1012 PRINT Q$" ";:GOTO 1020
    1014 IF S(I)<>0 THEN 1018
    1016 PRINT"  ";:GOTO 1020
    1018 PRINT P$" ";
    1020 IF I<>3 AND I<>6 THEN 1050
    1030 PRINT:PRINT"---+---+---"
    1040 GOTO 1080
    1050 IF I=9 THEN 1080
    1060 PRINT"!";
    1080 NEXT I:PRINT:PRINT:PRINT
    1095 FOR I=1 TO 7 STEP 3
    1100 IF S(I)<>S(I+1)THEN 1115
    1105 IF S(I)<>S(I+2)THEN 1115
    1110 IF S(I)=-1 THEN 1350
    1112 IF S(I)=1 THEN 1200
    1115 NEXT I:FOR I=1 TO 3:IF S(I)<>S(I+3)THEN 1150
    1130 IF S(I)<>S(I+6)THEN 1150
    1135 IF S(I)=-1 THEN 1350
    1137 IF S(I)=1 THEN 1200
    1150 NEXT I:FOR I=1 TO 9:IF S(I)=0 THEN 1155
    1152 NEXT I:GOTO 1400
    1155 IF S(5)<>G THEN 1170
    1160 IF S(1)=G AND S(9)=G THEN 1180
    1165 IF S(3)=G AND S(7)=G THEN 1180
    1170 RETURN
    1180 IF G=-1 THEN 1350
    1200 PRINT"YOU BEAT ME!! GOOD GAME.":GOTO 2000
    1350 PRINT"I WIN, TURKEY!!!":GOTO 2000
    1400 PRINT"IT'S A DRAW. THANK YOU."
    2000 END
    
    
    ================================================
    FILE: 89_Tic-Tac-Toe/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 89_Tic-Tac-Toe/vbnet/TicTacToe.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "TicTacToe", "TicTacToe.vbproj", "{5B43817F-EEF6-4298-BADF-69203BE7D088}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{5B43817F-EEF6-4298-BADF-69203BE7D088}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{5B43817F-EEF6-4298-BADF-69203BE7D088}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{5B43817F-EEF6-4298-BADF-69203BE7D088}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{5B43817F-EEF6-4298-BADF-69203BE7D088}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 89_Tic-Tac-Toe/vbnet/TicTacToe.vbproj
    ================================================
    
      
        Exe
        TicTacToe
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 90_Tower/README.md
    ================================================
    ### Tower
    
    This is a simulation of a game of logic that originated in the middle East. It is sometimes called Pharaoh's Needles, but its most common name is the Towers of Hanoi.
    
    Legend has it that a secret society of monks live beneath the city of Hanoi. They possess three large towers or needles on which different size gold disks may be placed. Moving one at a time and never placing a large on a smaller disk, the monks endeavor to move the tower of disks from the left needle to the right needle. Legend says when they have finished moving this 64-disk tower, the world will end. How many moves will they have to make to accomplish this? If they can move 1 disk per minute and work 24 hours per day, how many years will it take?
    
    In the computer puzzle you are faced with three upright needles. On the leftmost needle are placed from two to seven graduated disks, the largest being on bottom and smallest on top. Your object is to move the entire stack of disks to the rightmost needle. However, you many only move one disk at a time and you may never place a larger disk on top of a smaller one.
    
    In this computer game, the disks are referred to by their size — i.e., the smallest is 3, next 5, 7, 9, 11, 13, and 15. If you play with fewer than 7 disks always use the largest, i.e. with 2 disks you would use nos. 13 and 15. The program instructions are self-explanatory. Good luck!
    
    Charles Lund wrote this program while at the American School in the Hague, Netherlands.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=173)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=188)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 90_Tower/csharp/Game.cs
    ================================================
    using System;
    using Tower.Models;
    using Tower.Resources;
    using Tower.UI;
    
    namespace Tower
    {
        internal class Game
        {
            private readonly Towers _towers;
            private readonly TowerDisplay _display;
            private readonly int _optimalMoveCount;
            private int _moveCount;
    
            public Game(int diskCount)
            {
                _towers = new Towers(diskCount);
                _display = new TowerDisplay(_towers);
                _optimalMoveCount = (1 << diskCount) - 1;
            }
    
            public bool Play()
            {
                Console.Write(Strings.Instructions);
    
                Console.Write(_display);
    
                while (true)
                {
                    if (!Input.TryReadNumber(Prompt.Disk, out int disk)) { return false; }
    
                    if (!_towers.TryFindDisk(disk, out var from, out var message))
                    {
                        Console.WriteLine(message);
                        continue;
                    }
    
                    if (!Input.TryReadNumber(Prompt.Needle, out var to)) { return false; }
    
                    if (!_towers.TryMoveDisk(from, to))
                    {
                        Console.Write(Strings.IllegalMove);
                        continue;
                    }
    
                    Console.Write(_display);
    
                    var result = CheckProgress();
                    if (result.HasValue) { return result.Value; }
                }
            }
    
            private bool? CheckProgress()
            {
                _moveCount++;
    
                if (_moveCount == 128)
                {
                    Console.Write(Strings.TooManyMoves);
                    return false;
                }
    
                if (_towers.Finished)
                {
                    if (_moveCount == _optimalMoveCount)
                    {
                        Console.Write(Strings.Congratulations);
                    }
    
                    Console.WriteLine(Strings.TaskFinished, _moveCount);
    
                    return true;
                }
    
                return default;
            }
        }
    }
    
    
    ================================================
    FILE: 90_Tower/csharp/Models/Needle.cs
    ================================================
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace Tower.Models
    {
        internal class Needle : IEnumerable
        {
            private readonly Stack _disks = new Stack();
    
            public bool IsEmpty => _disks.Count == 0;
    
            public int Top => _disks.TryPeek(out var disk) ? disk : default;
    
            public bool TryPut(int disk)
            {
                if (_disks.Count == 0 || disk < _disks.Peek())
                {
                    _disks.Push(disk);
                    return true;
                }
    
                return false;
            }
    
            public bool TryGetTopDisk(out int disk) => _disks.TryPop(out disk);
    
            public IEnumerator GetEnumerator() =>
                Enumerable.Repeat(0, 7 - _disks.Count).Concat(_disks).GetEnumerator();
    
            IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
        }
    }
    
    
    ================================================
    FILE: 90_Tower/csharp/Models/Towers.cs
    ================================================
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using Tower.Resources;
    
    namespace Tower.Models
    {
        internal class Towers : IEnumerable<(int, int, int)>
        {
            private static int[] _availableDisks = new[] { 15, 13, 11, 9, 7, 5, 3 };
    
            private readonly Needle[] _needles = new[] { new Needle(), new Needle(), new Needle() };
            private readonly int _smallestDisk;
    
            public Towers(int diskCount)
            {
                foreach (int disk in _availableDisks.Take(diskCount))
                {
                    this[1].TryPut(disk);
                    _smallestDisk = disk;
                }
            }
    
            private Needle this[int i] => _needles[i-1];
    
            public bool Finished => this[1].IsEmpty && this[2].IsEmpty;
    
            public bool TryFindDisk(int disk, out int needle, out string message)
            {
                needle = default;
                message = default;
    
                if (disk < _smallestDisk)
                {
                    message = Strings.DiskNotInPlay;
                    return false;
                }
    
                for (needle = 1; needle <= 3; needle++)
                {
                    if (this[needle].Top == disk) { return true; }
                }
    
                message = Strings.DiskUnavailable;
                return false;
            }
    
            public bool TryMoveDisk(int from, int to)
            {
                if (!this[from].TryGetTopDisk(out var disk))
                {
                    throw new InvalidOperationException($"Needle {from} is empty");
                }
    
                if (this[to].TryPut(disk)) { return true; }
    
                this[from].TryPut(disk);
                return false;
            }
    
            public IEnumerator<(int, int, int)> GetEnumerator() => new TowersEnumerator(_needles);
    
            IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
    
            private class TowersEnumerator : IEnumerator<(int, int, int)>
            {
                private readonly List> _enumerators;
    
                public TowersEnumerator(Needle[] needles)
                {
                    _enumerators = needles.Select(n => n.GetEnumerator()).ToList();
                }
    
                public (int, int, int) Current =>
                    (_enumerators[0].Current, _enumerators[1].Current, _enumerators[2].Current);
    
                object IEnumerator.Current => Current;
    
                public void Dispose() => _enumerators.ForEach(e => e.Dispose());
    
                public bool MoveNext() => _enumerators.All(e => e.MoveNext());
    
                public void Reset() => _enumerators.ForEach(e => e.Reset());
            }
        }
    }
    
    
    ================================================
    FILE: 90_Tower/csharp/Program.cs
    ================================================
    using System;
    using Tower.Resources;
    using Tower.UI;
    
    namespace Tower
    {
        class Program
        {
            static void Main(string[] args)
            {
                Console.Write(Strings.Title);
    
                do
                {
                    Console.Write(Strings.Intro);
    
                    if (!Input.TryReadNumber(Prompt.DiskCount, out var diskCount)) { return; }
    
                    var game = new Game(diskCount);
    
                    if (!game.Play()) { return; }
                } while (Input.ReadYesNo(Strings.PlayAgainPrompt, Strings.YesNoPrompt));
    
                Console.Write(Strings.Thanks);
            }
        }
    }
    
    
    ================================================
    FILE: 90_Tower/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 90_Tower/csharp/Resources/Congratulations.txt
    ================================================
    
    Congratulations!!
    
    
    ================================================
    FILE: 90_Tower/csharp/Resources/DiskCountPrompt.txt
    ================================================
    How many disks do you want to move (7 is max)
    
    
    ================================================
    FILE: 90_Tower/csharp/Resources/DiskCountQuit.txt
    ================================================
    All right, wise guy, if you can't play the game right, I'll
    just take my puzzle and go home.  So long.
    
    
    ================================================
    FILE: 90_Tower/csharp/Resources/DiskCountRetry.txt
    ================================================
    Sorry, but i can't do that job for you.
    
    
    ================================================
    FILE: 90_Tower/csharp/Resources/DiskNotInPlay.txt
    ================================================
    That disk is not in play.  Make another choice.
    
    
    ================================================
    FILE: 90_Tower/csharp/Resources/DiskPrompt.txt
    ================================================
    Which disk would you like to move
    
    
    ================================================
    FILE: 90_Tower/csharp/Resources/DiskQuit.txt
    ================================================
    Stop wasting my time.  Go bother someone else.
    
    
    ================================================
    FILE: 90_Tower/csharp/Resources/DiskRetry.txt
    ================================================
    Illegal entry... You may only type 3, 5, 7, 9, 11, 13, or 15.
    
    
    ================================================
    FILE: 90_Tower/csharp/Resources/DiskUnavailable.txt
    ================================================
    That disk is below another one.  Make another choice.
    
    
    ================================================
    FILE: 90_Tower/csharp/Resources/IllegalMove.txt
    ================================================
    You can't place a larger disk on top of a smaller one,
    it might crush it!
    Now then,
    
    
    ================================================
    FILE: 90_Tower/csharp/Resources/Instructions.txt
    ================================================
    In this program, we shall refer to disks by numerical code.
    3 will represent the smallest disk, 5 the next size,
    7 the next, and so on, up to 15.  If you do the puzzle with
    2 disks, their code names would be 13 and 15.  With 3 disks
    the code names would be 11, 13 and 15, etc.  The needles
    are numbered from left to right, 1 to 3.  We will
    startup with the disks on needle 1, and attempt to move them
    to needle 3.
    
    Good luck!
    
    
    ================================================
    FILE: 90_Tower/csharp/Resources/Intro.txt
    ================================================
    
    Towers of Hanoi puzzle.
    
    You must transfer the disks from the left to the right
    tower, one at a time, never putting a larger dish on a
    smaller disk.
    
    
    ================================================
    FILE: 90_Tower/csharp/Resources/NeedlePrompt.txt
    ================================================
    Place disk on which needle
    
    
    ================================================
    FILE: 90_Tower/csharp/Resources/NeedleQuit.txt
    ================================================
    I tried to warn you, but you wouldn't listen,
    Bye bye, big shot.
    
    
    ================================================
    FILE: 90_Tower/csharp/Resources/NeedleRetry.txt
    ================================================
    I'll assume you hit the wrong key this time.  But watch it,
    I only allow one mistake
    
    
    ================================================
    FILE: 90_Tower/csharp/Resources/PlayAgainPrompt.txt
    ================================================
    
    Try again (Yes or No)
    
    
    ================================================
    FILE: 90_Tower/csharp/Resources/Strings.cs
    ================================================
    using System.IO;
    using System.Reflection;
    using System.Runtime.CompilerServices;
    
    namespace Tower.Resources
    {
        internal static class Strings
        {
            internal static string Congratulations => GetResource();
            internal static string DiskCountPrompt => GetResource();
            internal static string DiskCountQuit => GetResource();
            internal static string DiskCountRetry => GetResource();
            internal static string DiskNotInPlay => GetResource();
            internal static string DiskPrompt => GetResource();
            internal static string DiskQuit => GetResource();
            internal static string DiskRetry => GetResource();
            internal static string DiskUnavailable => GetResource();
            internal static string IllegalMove => GetResource();
            internal static string Instructions => GetResource();
            internal static string Intro => GetResource();
            internal static string NeedlePrompt => GetResource();
            internal static string NeedleQuit => GetResource();
            internal static string NeedleRetry => GetResource();
            internal static string PlayAgainPrompt => GetResource();
            internal static string TaskFinished => GetResource();
            internal static string Thanks => GetResource();
            internal static string Title => GetResource();
            internal static string TooManyMoves => GetResource();
            internal static string YesNoPrompt => GetResource();
    
            private static string GetResource([CallerMemberName] string name = "")
            {
                var streamName = $"Tower.Resources.{name}.txt";
                using var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(streamName);
                using var reader = new StreamReader(stream);
    
                return reader.ReadToEnd();
            }
        }
    }
    
    
    ================================================
    FILE: 90_Tower/csharp/Resources/TaskFinished.txt
    ================================================
    
    You have performed the task in {0} moves.
    
    
    ================================================
    FILE: 90_Tower/csharp/Resources/Thanks.txt
    ================================================
    
    Thanks for the game!
    
    
    ================================================
    FILE: 90_Tower/csharp/Resources/Title.txt
    ================================================
                                     Towers
                   Creative Computing  Morristown, New Jersey
    
    
    ================================================
    FILE: 90_Tower/csharp/Resources/TooManyMoves.txt
    ================================================
    Sorry, but I have orders to stop if you make more than
    128 moves.
    
    
    ================================================
    FILE: 90_Tower/csharp/Resources/YesNoPrompt.txt
    ================================================
    
    'Yes' or 'No' please
    
    
    ================================================
    FILE: 90_Tower/csharp/Tower.csproj
    ================================================
    
    
      
        Exe
        net5.0
      
    
      
        
      
    
    
    
    
    ================================================
    FILE: 90_Tower/csharp/Tower.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 17
    VisualStudioVersion = 17.0.32014.148
    MinimumVisualStudioVersion = 15.0.26124.0
    Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tower", "Tower.csproj", "{EEED33AD-3DE2-49AA-8AF1-2174C510E128}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{EEED33AD-3DE2-49AA-8AF1-2174C510E128}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{EEED33AD-3DE2-49AA-8AF1-2174C510E128}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{EEED33AD-3DE2-49AA-8AF1-2174C510E128}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{EEED33AD-3DE2-49AA-8AF1-2174C510E128}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {BDCB8278-62ED-46E3-92C2-FF572E0E3ED3}
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 90_Tower/csharp/UI/Input.cs
    ================================================
    using System;
    using System.Collections.Generic;
    
    namespace Tower.UI
    {
        // Provides input methods which emulate the BASIC interpreter's keyboard input routines
        internal static class Input
        {
            private static void Prompt(string text = "") => Console.Write($"{text}? ");
    
            internal static bool ReadYesNo(string prompt, string retryPrompt)
            {
                var response = ReadString(prompt);
    
                while (true)
                {
                    if (response.Equals("No", StringComparison.InvariantCultureIgnoreCase)) { return false; }
                    if (response.Equals("Yes", StringComparison.InvariantCultureIgnoreCase)) { return true; }
                    response = ReadString(retryPrompt);
                }
            }
    
            internal static bool TryReadNumber(Prompt prompt, out int number)
            {
                var message = prompt.Message;
    
                for (int retryCount = 0; retryCount <= prompt.RetriesAllowed; retryCount++)
                {
                    if (retryCount > 0) { Console.WriteLine(prompt.RetryMessage); }
    
                    if (prompt.TryValidateResponse(ReadNumber(message), out number)) { return true; }
    
                    if (!prompt.RepeatPrompt) { message = ""; }
                }
    
                Console.WriteLine(prompt.QuitMessage);
    
                number = 0;
                return false;
            }
    
            private static float ReadNumber(string prompt)
            {
                Prompt(prompt);
    
                while (true)
                {
                    var inputValues = ReadStrings();
    
                    if (TryParseNumber(inputValues[0], out var number))
                    {
                        if (inputValues.Length > 1)
                        {
                            Console.WriteLine("!Extra input ingored");
                        }
    
                        return number;
                    }
                }
            }
    
            private static string ReadString(string prompt)
            {
                Prompt(prompt);
    
                var inputValues = ReadStrings();
                if (inputValues.Length > 1)
                {
                    Console.WriteLine("!Extra input ingored");
                }
                return inputValues[0];
            }
    
            private static string[] ReadStrings() => Console.ReadLine().Split(',', StringSplitOptions.TrimEntries);
    
            private static bool TryParseNumber(string text, out float number)
            {
                if (float.TryParse(text, out number)) { return true; }
    
                Console.WriteLine("!Number expected - retry input line");
                number = default;
                return false;
            }
        }
    }
    
    
    ================================================
    FILE: 90_Tower/csharp/UI/Prompt.cs
    ================================================
    using System.Collections.Generic;
    using System.Linq;
    using static Tower.Resources.Strings;
    
    namespace Tower.UI
    {
        internal class Prompt
        {
            public static Prompt DiskCount =
                new(DiskCountPrompt, DiskCountRetry, DiskCountQuit, 1, 2, 3, 4, 5, 6, 7) { RetriesAllowed = 2 };
    
            public static Prompt Disk =
                new(DiskPrompt, DiskRetry, DiskQuit, 3, 5, 7, 9, 11, 13, 15) { RepeatPrompt = false };
    
            public static Prompt Needle = new(NeedlePrompt, NeedleRetry, NeedleQuit, 1, 2, 3);
    
            private readonly HashSet _validValues;
    
            private Prompt(string prompt, string retryMessage, string quitMessage, params int[] validValues)
            {
                Message = prompt;
                RetryMessage = retryMessage;
                QuitMessage = quitMessage;
                _validValues = validValues.ToHashSet();
                RetriesAllowed = 1;
                RepeatPrompt = true;
            }
    
            public string Message { get; }
            public string RetryMessage { get; }
            public string QuitMessage { get; }
            public int RetriesAllowed { get; private set; }
            public bool RepeatPrompt { get; private set; }
    
            public bool TryValidateResponse(float number, out int integer)
            {
                integer = (int)number;
                return integer == number && _validValues.Contains(integer);
            }
        }
    }
    
    
    ================================================
    FILE: 90_Tower/csharp/UI/TowerDisplay.cs
    ================================================
    using System;
    using System.Text;
    using Tower.Models;
    
    namespace Tower.UI
    {
        internal class TowerDisplay
        {
            private readonly Towers _towers;
    
            public TowerDisplay(Towers towers)
            {
                _towers = towers;
            }
    
            public override string ToString()
            {
                var builder = new StringBuilder();
    
                foreach (var row in _towers)
                {
                    AppendTower(row.Item1);
                    AppendTower(row.Item2);
                    AppendTower(row.Item3);
                    builder.AppendLine();
                }
    
                return builder.ToString();
    
                void AppendTower(int size)
                {
                    var padding = 10 - size / 2;
                    builder.Append(' ', padding).Append('*', Math.Max(1, size)).Append(' ', padding);
                }
            }
        }
    }
    
    
    ================================================
    FILE: 90_Tower/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 90_Tower/java/Tower.java
    ================================================
    import java.lang.Math;
    import java.util.Scanner;
    
    /**
     * Game of Tower
     * 

    * Based on the BASIC game of Tower here * https://github.com/coding-horror/basic-computer-games/blob/main/90%20Tower/tower.bas *

    * Note: The idea was to create a version of the 1970's BASIC game in Java, without introducing * new features - no additional text, error checking, etc has been added. * * Converted from BASIC to Java by Darren Cardenas. */ public class Tower { private final static int MAX_DISK_SIZE = 15; private final static int MAX_NUM_COLUMNS = 3; private final static int MAX_NUM_MOVES = 128; private final static int MAX_NUM_ROWS = 7; private final Scanner scan; // For user input // Represent all possible disk positions private int[][] positions; private enum Step { INITIALIZE, SELECT_TOTAL_DISKS, SELECT_DISK_MOVE, SELECT_NEEDLE, CHECK_SOLUTION } public Tower() { scan = new Scanner(System.in); // Row 0 and column 0 are not used positions = new int[MAX_NUM_ROWS + 1][MAX_NUM_COLUMNS + 1]; } // End of constructor Tower public class Position { public int row; public int column; public Position(int row, int column) { this.row = row; this.column = column; } // End of constructor Position } // End of inner class Position public void play() { showIntro(); startGame(); } // End of method play private void showIntro() { System.out.println(" ".repeat(32) + "TOWERS"); System.out.println(" ".repeat(14) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println("\n\n"); } // End of method showIntro private void startGame() { boolean diskMoved = false; int column = 0; int disk = 0; int needle = 0; int numDisks = 0; int numErrors = 0; int numMoves = 0; int row = 0; Step nextStep = Step.INITIALIZE; String userResponse = ""; Position diskPosition = new Position(0, 0); // Begin outer while loop while (true) { switch (nextStep) { case INITIALIZE: // Initialize error count numErrors = 0; // Initialize positions for (row = 1; row <= MAX_NUM_ROWS; row++) { for (column = 1; column <= MAX_NUM_COLUMNS; column++) { positions[row][column] = 0; } } // Display description System.out.println(""); System.out.println("TOWERS OF HANOI PUZZLE.\n"); System.out.println("YOU MUST TRANSFER THE DISKS FROM THE LEFT TO THE RIGHT"); System.out.println("TOWER, ONE AT A TIME, NEVER PUTTING A LARGER DISK ON A"); System.out.println("SMALLER DISK.\n"); nextStep = Step.SELECT_TOTAL_DISKS; break; case SELECT_TOTAL_DISKS: while (numErrors <= 2) { // Get user input System.out.print("HOW MANY DISKS DO YOU WANT TO MOVE (" + MAX_NUM_ROWS + " IS MAX)? "); numDisks = scan.nextInt(); System.out.println(""); numMoves = 0; // Ensure the number of disks is valid if ((numDisks < 1) || (numDisks > MAX_NUM_ROWS)) { numErrors++; // Handle user input errors if (numErrors < 3) { System.out.println("SORRY, BUT I CAN'T DO THAT JOB FOR YOU."); } } else { break; // Leave the while loop } } // Too many user input errors if (numErrors > 2) { System.out.println("ALL RIGHT, WISE GUY, IF YOU CAN'T PLAY THE GAME RIGHT, I'LL"); System.out.println("JUST TAKE MY PUZZLE AND GO HOME. SO LONG."); return; } // Display detailed instructions System.out.println("IN THIS PROGRAM, WE SHALL REFER TO DISKS BY NUMERICAL CODE."); System.out.println("3 WILL REPRESENT THE SMALLEST DISK, 5 THE NEXT SIZE,"); System.out.println("7 THE NEXT, AND SO ON, UP TO 15. IF YOU DO THE PUZZLE WITH"); System.out.println("2 DISKS, THEIR CODE NAMES WOULD BE 13 AND 15. WITH 3 DISKS"); System.out.println("THE CODE NAMES WOULD BE 11, 13 AND 15, ETC. THE NEEDLES"); System.out.println("ARE NUMBERED FROM LEFT TO RIGHT, 1 TO 3. WE WILL"); System.out.println("START WITH THE DISKS ON NEEDLE 1, AND ATTEMPT TO MOVE THEM"); System.out.println("TO NEEDLE 3.\n"); System.out.println("GOOD LUCK!\n"); disk = MAX_DISK_SIZE; // Set disk starting positions for (row = MAX_NUM_ROWS; row > (MAX_NUM_ROWS - numDisks); row--) { positions[row][1] = disk; disk = disk - 2; } printPositions(); nextStep = Step.SELECT_DISK_MOVE; break; case SELECT_DISK_MOVE: System.out.print("WHICH DISK WOULD YOU LIKE TO MOVE? "); numErrors = 0; while (numErrors < 2) { disk = scan.nextInt(); // Validate disk numbers if ((disk - 3) * (disk - 5) * (disk - 7) * (disk - 9) * (disk - 11) * (disk - 13) * (disk - 15) == 0) { // Check if disk exists diskPosition = getDiskPosition(disk); // Disk found if ((diskPosition.row > 0) && (diskPosition.column > 0)) { // Disk can be moved if (isDiskMovable(disk, diskPosition.row, diskPosition.column) == true) { break; } // Disk cannot be moved else { System.out.println("THAT DISK IS BELOW ANOTHER ONE. MAKE ANOTHER CHOICE."); System.out.print("WHICH DISK WOULD YOU LIKE TO MOVE? "); } } // Mimic legacy handling of valid disk number but disk not found else { System.out.println("THAT DISK IS BELOW ANOTHER ONE. MAKE ANOTHER CHOICE."); System.out.print("WHICH DISK WOULD YOU LIKE TO MOVE? "); numErrors = 0; continue; } } // Invalid disk number else { System.out.println("ILLEGAL ENTRY... YOU MAY ONLY TYPE 3,5,7,9,11,13, OR 15."); numErrors++; if (numErrors > 1) { break; } System.out.print("? "); } } if (numErrors > 1) { System.out.println("STOP WASTING MY TIME. GO BOTHER SOMEONE ELSE."); return; } nextStep = Step.SELECT_NEEDLE; break; case SELECT_NEEDLE: numErrors = 0; while (true) { System.out.print("PLACE DISK ON WHICH NEEDLE? "); needle = scan.nextInt(); // Handle valid needle numbers if ((needle - 1) * (needle - 2) * (needle - 3) == 0) { // Ensure needle is safe for disk move if (isNeedleSafe(needle, disk, row) == false) { System.out.println("YOU CAN'T PLACE A LARGER DISK ON TOP OF A SMALLER ONE,"); System.out.println("IT MIGHT CRUSH IT!"); System.out.print("NOW THEN, "); nextStep = Step.SELECT_DISK_MOVE; break; } diskPosition = getDiskPosition(disk); // Attempt to move the disk on a non-empty needle diskMoved = false; for (row = 1; row <= MAX_NUM_ROWS; row++) { if (positions[row][needle] != 0) { row--; positions[row][needle] = positions[diskPosition.row][diskPosition.column]; positions[diskPosition.row][diskPosition.column] = 0; diskMoved = true; break; } } // Needle was empty, so move disk to the bottom if (diskMoved == false) { positions[MAX_NUM_ROWS][needle] = positions[diskPosition.row][diskPosition.column]; positions[diskPosition.row][diskPosition.column] = 0; } nextStep = Step.CHECK_SOLUTION; break; } // Handle invalid needle numbers else { numErrors++; if (numErrors > 1) { System.out.println("I TRIED TO WARN YOU, BUT YOU WOULDN'T LISTEN."); System.out.println("BYE BYE, BIG SHOT."); return; } else { System.out.println("I'LL ASSUME YOU HIT THE WRONG KEY THIS TIME. BUT WATCH IT,"); System.out.println("I ONLY ALLOW ONE MISTAKE."); } } } break; case CHECK_SOLUTION: printPositions(); numMoves++; // Puzzle is solved if (isPuzzleSolved() == true) { // Check for optimal solution if (numMoves == (Math.pow(2, numDisks) - 1)) { System.out.println(""); System.out.println("CONGRATULATIONS!!\n"); } System.out.println("YOU HAVE PERFORMED THE TASK IN " + numMoves + " MOVES.\n"); System.out.print("TRY AGAIN (YES OR NO)? "); // Prompt for retries while (true) { userResponse = scan.next(); if (userResponse.toUpperCase().equals("YES")) { nextStep = Step.INITIALIZE; break; } else if (userResponse.toUpperCase().equals("NO")) { System.out.println(""); System.out.println("THANKS FOR THE GAME!\n"); return; } else { System.out.print("'YES' OR 'NO' PLEASE? "); } } } // Puzzle is not solved else { // Exceeded maximum number of moves if (numMoves > MAX_NUM_MOVES) { System.out.println("SORRY, BUT I HAVE ORDERS TO STOP IF YOU MAKE MORE THAN"); System.out.println("128 MOVES."); return; } nextStep = Step.SELECT_DISK_MOVE; break; } break; default: System.out.println("INVALID STEP"); break; } } // End outer while loop } // End of method startGame private boolean isPuzzleSolved() { int column = 0; int row = 0; // Puzzle is solved if first 2 needles are empty for (row = 1; row <= MAX_NUM_ROWS; row++) { for (column = 1; column <= 2; column++) { if (positions[row][column] != 0) { return false; } } } return true; } // End of method isPuzzleSolved private Position getDiskPosition(int disk) { int column = 0; int row = 0; Position pos = new Position(0, 0); // Begin loop through all rows for (row = 1; row <= MAX_NUM_ROWS; row++) { // Begin loop through all columns for (column = 1; column <= MAX_NUM_COLUMNS; column++) { // Found the disk if (positions[row][column] == disk) { pos.row = row; pos.column = column; return pos; } } // End loop through all columns } // End loop through all rows return pos; } // End of method getDiskPosition private boolean isDiskMovable(int disk, int row, int column) { int ii = 0; // Loop iterator // Begin loop through all rows above disk for (ii = row; ii >= 1; ii--) { // Disk can be moved if (positions[ii][column] == 0) { continue; } // Disk cannot be moved if (positions[ii][column] < disk) { return false; } } // End loop through all rows above disk return true; } // End of method isDiskMovable private boolean isNeedleSafe(int needle, int disk, int row) { for (row = 1; row <= MAX_NUM_ROWS; row++) { // Needle is not empty if (positions[row][needle] != 0) { // Disk crush condition if (disk >= positions[row][needle]) { return false; } } } return true; } // End of method isNeedleSafe private void printPositions() { int column = 1; int ii = 0; // Loop iterator int numSpaces = 0; int row = 1; // Begin loop through all rows for (row = 1; row <= MAX_NUM_ROWS; row++) { numSpaces = 9; // Begin loop through all columns for (column = 1; column <= MAX_NUM_COLUMNS; column++) { // No disk at the current position if (positions[row][column] == 0) { System.out.print(" ".repeat(numSpaces) + "*"); numSpaces = 20; } // Draw a disk at the current position else { System.out.print(" ".repeat(numSpaces - ((int) (positions[row][column] / 2)))); for (ii = 1; ii <= positions[row][column]; ii++) { System.out.print("*"); } numSpaces = 20 - ((int) (positions[row][column] / 2)); } } // End loop through all columns System.out.println(""); } // End loop through all rows } // End of method printPositions public static void main(String[] args) { Tower tower = new Tower(); tower.play(); } // End of method main } // End of class Tower ================================================ FILE: 90_Tower/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 90_Tower/javascript/tower.html ================================================ TOWER

    
    
    
    
    
    
    ================================================
    FILE: 90_Tower/javascript/tower.js
    ================================================
    // TOWER
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    var ta = [];
    
    // Print subroutine
    function show_towers()
    {
        var z;
    
        for (var k = 1; k <= 7; k++) {
            z = 10;
            str = "";
            for (var j = 1; j <= 3; j++) {
                if (ta[k][j] != 0) {
                    while (str.length < z - Math.floor(ta[k][j] / 2))
                        str += " ";
                    for (v = 1; v <= ta[k][j]; v++)
                        str += "*";
                } else {
                    while (str.length < z)
                        str += " ";
                    str += "*";
                }
                z += 21;
            }
            print(str + "\n");
        }
    }
    
    // Main control section
    async function main()
    {
        print(tab(33) + "TOWERS\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        while (1) {
            print("\n");
            // Initialize
            e = 0;
            for (d = 1; d <= 7; d++) {
                ta[d] = [];
                for (n = 1; n <= 3; n++)
                    ta[d][n] = 0;
            }
            print("TOWERS OF HANOI PUZZLE.\n");
            print("\n");
            print("YOU MUST TRANSFER THE DISKS FROM THE LEFT TO THE RIGHT\n");
            print("TOWER, ONE AT A TIME, NEVER PUTTING A LARGER DISK ON A\n");
            print("SMALLER DISK.\n");
            print("\n");
            while (1) {
                print("HOW MANY DISKS DO YOU WANT TO MOVE (7 IS MAX)");
                s = parseInt(await input());
                print("\n");
                m = 0;
                if (s >= 1 && s <= 7)
                    break;
                e++;
                if (e < 2) {
                    print("SORRY, BUT I CAN'T DO THAT JOB FOR YOU.\n");
                    continue;
                }
                print("ALL RIGHT, WISE GUY, IF YOU CAN'T PLAY THE GAME RIGHT, I'LL\n");
                print("JUST TAKE MY PUZZLE AND GO HOME.  SO LONG.\n");
                return;
            }
            // Store disks from smallest to largest
            print("IN THIS PROGRAM, WE SHALL REFER TO DISKS BY NUMERICAL CODE.\n");
            print("3 WILL REPRESENT THE SMALLEST DISK, 5 THE NEXT SIZE,\n");
            print("7 THE NEXT, AND SO ON, UP TO 15.  IF YOU DO THE PUZZLE WITH\n");
            print("2 DISKS, THEIR CODE NAMES WOULD BE 13 AND 15.  WITH 3 DISKS\n");
            print("THE CODE NAMES WOULD BE 11, 13 AND 15, ETC.  THE NEEDLES\n");
            print("ARE NUMBERED FROM LEFT TO RIGHT, 1 TO 3.  WE WILL\n");
            print("START WITH THE DISKS ON NEEDLE 1, AND ATTEMPT TO MOVE THEM\n");
            print("TO NEEDLE 3.\n");
            print("\n");
            print("GOOD LUCK!\n");
            print("\n");
            y = 7;
            d = 15;
            for (x = s; x >= 1; x--) {
                ta[y][1] = d;
                d -= 2;
                y--;
            }
            show_towers();
            while (1) {
                print("WHICH DISK WOULD YOU LIKE TO MOVE");
                e = 0;
                while (1) {
                    d = parseInt(await input());
                    if (d % 2 == 0 || d < 3 || d > 15) {
                        print("ILLEGAL ENTRY... YOU MAY ONLY TYPE 3,5,7,9,11,13, OR 15.\n");
                        e++;
                        if (e <= 1)
                            continue;
                        print("STOP WASTING MY TIME.  GO BOTHER SOMEONE ELSE.\n");
                        return;
                    } else {
                        break;
                    }
                }
                // Check if requested disk is below another
                for (r = 1; r <= 7; r++) {
                    for (c = 1; c <= 3; c++) {
                        if (ta[r][c] == d)
                            break;
                    }
                    if (c <= 3)
                        break;
                }
                for (q = r; q >= 1; q--) {
                    if (ta[q][c] != 0 && ta[q][c] < d)
                        break;
                }
                if (q >= 1) {
                    print("THAT DISK IS BELOW ANOTHER ONE.  MAKE ANOTHER CHOICE.\n");
                    continue;
                }
                e = 0;
                while (1) {
                    print("PLACE DISK ON WHICH NEEDLE");
                    n = parseInt(await input());
                    if (n >= 1 && n <= 3)
                        break;
                    e++;
                    if (e <= 1) {
                        print("I'LL ASSUME YOU HIT THE WRONG KEY THI TIME.  BUT WATCH IT,\n");
                        print("I ONLY ALLOW ONE MISTAKE.\n");
                        continue;
                    } else {
                        print("I TRIED TO WARN YOU, BUT YOU WOULDN'T LISTEN.\n");
                        print("BYE BYE, BIG SHOT.\n");
                        return;
                    }
                }
                // Check if requested disk is below another
                for (r = 1; r <= 7; r++) {
                    if (ta[r][n] != 0)
                        break;
                }
                if (r <= 7) {
                    // Check if disk to be placed on a larger one
                    if (d >= ta[r][n]) {
                        print("YOU CAN'T PLACE A LARGER DISK ON TOP OF A SMALLER ONE,\n");
                        print("IT MIGHT CRUSH IT!\n");
                        print("NOW THEN, ");
                        continue;
                    }
                }
                // Move relocated disk
                for (v = 1; v <= 7; v++) {
                    for (w = 1; w <= 3; w++) {
                        if (ta[v][w] == d)
                            break;
                    }
                    if (w <= 3)
                        break;
                }
                // Locate empty space on needle n
                for (u = 1; u <= 7; u++) {
                    if (ta[u][n] != 0)
                        break;
                }
                ta[--u][n] = ta[v][w];
                ta[v][w] = 0;
                // Print out current status
                show_towers();
                // Check if done
                m++;
                for (r = 1; r <= 7; r++) {
                    for (c = 1; c <= 2; c++) {
                        if (ta[r][c] != 0)
                            break;
                    }
                    if (c <= 2)
                        break;
                }
                if (r > 7)
                    break;
                if (m > 128) {
                    print("SORRY, BUT I HAVE ORDERS TO STOP IF YOU MAKE MORE THAN\n");
                    print("128 MOVES.\n");
                    return;
                }
            }
            if (m == Math.pow(2, s) - 1) {
                print("\n");
                print("CONGRATULATIONS!!\n");
                print("\n");
            }
            print("YOU HAVE PERFORMED THE TASK IN " + m + " MOVES.\n");
            print("\n");
            print("TRY AGAIN (YES OR NO)");
            while (1) {
                str = await input();
                if (str == "YES" || str == "NO")
                    break;
                print("\n");
                print("'YES' OR 'NO' PLEASE");
            }
            if (str == "NO")
                break;
        }
        print("\n");
        print("THANKS FOR THE GAME!\n");
        print("\n");
    }
    
    main();
    
    
    ================================================
    FILE: 90_Tower/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 90_Tower/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 90_Tower/lua/tower.lua
    ================================================
    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()
    
    
    ================================================
    FILE: 90_Tower/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 90_Tower/perl/tower.pl
    ================================================
    #!/usr/bin/env perl
    
    use 5.010;      # To get 'state' and 'say'
    
    use strict;     # Require explicit declaration of variables
    use warnings;   # Enable optional compiler warnings
    
    use English;    # Use more friendly names for Perl's magic variables
    use Term::ReadLine;     # Prompt and return user input
    
    our $VERSION = '0.000_01';
    
    # Manifest constant representing the maximum number of disks. We can
    # change this within limits. It needs to be at least 3 or the
    # explanatory text will contain negative numbers. There is no known
    # upper limit, though if it is more than 10 the output lines can be more
    # than 80 columns, and if it is more than 49 the disk numbers will be
    # more than two digits, which will cause output lines not to align
    # properly.
    use constant MAX_DISKS  => 7;
    
    print <<'EOD';
                                     TOWERS
                   Creative Computing  Morristown, New Jersey
    
    
    EOD
    
    while ( 1 ) {   # Iterate until something makes us stop.
    
        print <<'EOD';
    Towers of Hanoi Puzzle.
    
    You must transfer the disks from the left to the right
    Tower, one at a time, never putting a larger disk on a
    smaller disk.
    
    EOD
    
        # Get the desired number of disks to work with.
        my $size = get_input(
    
            # Manifest constants do not interpolate into strings. This can
            # be worked around using the @{[ ... ]} construction, which
            # interpolates any expression.
            "How many disks do you want to move (@{[ MAX_DISKS ]} is max)? ",
    
            sub {
    
                # Accept any response which is an integer greater than zero
                # and less than or equal to the maximum number of disks.
                # NOTE that 'and' performs the same operation as &&, but
                # binds much more loosely.
                return m/ \A [0-9]+ \z /smx &&
                    $ARG > 0 &&
                    $ARG <= MAX_DISKS;
            },
    
            3,
            "Sorry, but I can't do that job for you.\n",    # Warning
            <<'EOD',
    All right, wise guy, if you can't play the game right, I'll
    just take my puzzle and go home.  So long.
    EOD
        );
    
        # Expressions can be interpolated using @{[ ... ]}
        print <<"EOD";
    In this program, we shall refer to disks by numerical code.
    3 will represent the smallest disk, 5 the next size,
    7 the next, and so on, up to @{[ MAX_DISKS * 2 + 1 ]}.  If you do the puzzle with
    2 disks, their code names would be @{[ MAX_DISKS * 2 - 1 ]} and @{[ MAX_DISKS * 2 + 1 ]}.  With 3 disks
    the code names would be @{[ MAX_DISKS * 2 - 3 ]}, @{[ MAX_DISKS * 2 - 1 ]} and @{[ MAX_DISKS * 2 + 1 ]}, etc.  The needles
    are numbered from left to right, 1 to 3.  We will
    start with the disks on needle 1, and attempt to move them
    to needle 3.
    
    
    Good luck!
    
    EOD
    
        # Compute the legal disk numbers for this puzzle. The expression
        # reads right to left thus:
        #   * The .. operator generates the integers between and including
        #     its end points, that is, from MAX_DISKS + 1 - $size to
        #     MAX_DISKS, inclusive.
        #   * map { .. } calls the block once for each of its arguments.
        #     The value of the argument appears in the topic variable $ARG,
        #     and the result of the block is returned.
        #   * The list generated by the map {} is assigned to array
        #     @legal_disks.
        my @legal_disks = map { $ARG * 2 + 1 }
            MAX_DISKS + 1 - $size ..  MAX_DISKS;
    
        # Generate the board. This is an array of needles, indexed from
        # zero. Each needle is represented by a reference to an array
        # containing the disks on that needle, bottom to top.
        my @board = (
            [ reverse @legal_disks ],
            [],
            []
        );
    
        display( \@board ); # Display the initial board.
    
        my $moves = 0;  # Move counter.
    
        while ( 1 ) {   # Iterate until something makes us stop.
            my $disk = get_input(
                'Which disk would you like to move? ',
                sub {
                    # Accept any odd integer in the required range.
                    # NOTE that 'and' performs the same operation as &&, but
                    # binds much more loosely.
                    return m/ \A [0-9]+ \z /smx &&
                        $ARG % 2 &&
                        $ARG >= ( MAX_DISKS + 1 - $size ) * 2 + 1 &&
                        $ARG <= MAX_DISKS * 2 + 1;
                },
                3,
                do {    # Compound statement and scope for 'local'
    
                    # We want to interpolate @legal_disks into our warning.
                    # Interpolation of an array places $LIST_SEPARATOR
                    # between the elements of the array. The default is ' ',
                    # but we want ', '. We use 'local' to restrict the
                    # change to the current block and code called by it.
                    # Failure to localize the change can cause Spooky Action
                    # at a Distance.
                    local $LIST_SEPARATOR = ', ';
    
                    "Illegal entry... You may only type @legal_disks\n";
                },
                "Stop wasting my time.  Go bother someone else.\n",
            );
    
            # Return the number (from zero) of the needle which has the
            # desired disk on top. If the desired disk is not found, we got
            # undef back. In this case we redo the innermost loop.
            redo unless defined( my $from = find_disk( $disk, \@board ) );
    
            # Find out where the chosen disk goes.
            # NOTE that unlike the BASIC implementation, we require the
            # needle to be moved.
            my $to = get_input(
                'Place disk on which needle? ',
                sub {
                    # Accept integers 1 through 3, but not the current
                    # location of the disk
                    return m/ \A [0-9]+ \z /smx &&
                        $ARG > 0 &&
                        $ARG <= 3 &&
                        $ARG != $from + 1;
                },
                2,
                <<'EOD',
    I'll assume you hit the wrong key this time.  But watch it,
    I only allow one mistake.
    EOD
                <<'EOD',
    I tried to warn you, but you wouldn't listen.
    Bye bye, big shot.
    EOD
            ) - 1;
    
            # Check for placing a larger disk on a smaller one. The check is
            # that the destination needle has something on it (an empty
            # array is false in Boolean context) and that the destination
            # needle's top disk ([-1] selects the last element of an array)
            # is smaller than the source needle's disk.
            if ( @{ $board[$to] } && $board[$to][-1] < $board[$from][-1] ) {
                warn <<'EOD';
    You can't place a larger disk on top of a smaller one,
    It might crush it!
    EOD
                redo;
            }
    
            # Remove the selected disk from its needle, and place it on the
            # destination needle.
            push @{ $board[$to] }, pop @{ $board[$from] };
    
            $moves++;   # Count another move.
    
            display( \@board ); # Display the current board.
    
            # If all the disks are on the last needle, we are done.
            if ( @{ $board[2] } == $size ) {
    
                # Print a success message
                print <<"EOD";
    Congratulations!
    
    You have performed the task in $moves moves.
    
    EOD
                last;   # Exit the innermost loop.
    
            # If the maximum allowed moves have been exceeded
            } elsif ( $moves >= 2 ** MAX_DISKS ) {
    
                # Warn
                warn <<"EOD";
    Sorry, but I have orders to stop if you make more than
    $moves moves.
    EOD
    
                last;   # Exit the innermost loop.
            }
        }
    
        say '';
        get_input(
            'Try again? [y/N]: ',
            sub {
                exit if $ARG eq '' || m/ \A n /smxi;
                return m/ \A y /smxi;
            },
            ~0, # The 1's complement of 0 = largest possible integer
            "Please respond 'y' or 'n'\n",
        );
    }
    
    # Display the board, which is passed in as a reference.
    sub display {
        my ( $board ) = @_;
        say '';
    
        # Use a manifest constant for an empty needle. This is global
        # despite its appearing to be nested in the subroutine. Perl uses
        # 'x' as its string replication operator. The initial 4 blanks
        # accommodate the disk number and spacing between needles.
        use constant EMPTY_NEEDLE   => ' ' x 4 . ' ' x MAX_DISKS . '|' .
            ' ' x MAX_DISKS;
    
        # Iterate over the rows to be printed.
        foreach my $inx ( reverse 0 .. MAX_DISKS ) {
    
            my $line;   # Line buffer.
    
            # Iterate over needles.
            foreach my $col ( 0 .. 2 ) {
    
                # If this position on the needle is occupied
                if ( my $disk_num = $board->[$col][$inx] ) {
    
                    # Compute the width of a half disk
                    my $half_width = ( $disk_num - 1 ) / 2;
    
                    # Compute the graphic for the half disk. Perl uses 'x'
                    # as its string replication operator.
                    my $half_disk = '*' x $half_width;
    
                    # Append the disk to the line. The inner sprintf() does
                    # most of the work; the outer simply pads the graphic to
                    # the required total width.
                    $line .= sprintf( '%*s', -( MAX_DISKS * 2 + 5 ),
                        sprintf( '%*d %s|%s', MAX_DISKS + 3 - $half_width,
                            $disk_num, $half_disk, $half_disk ) );
    
                # Else this position is not occupied
                } else {
    
                    # So just append the empty needle.
                    $line .= EMPTY_NEEDLE;
                }
            }
    
            # Remove white space at the end of the line
            $line =~ s/ \s+ \z //smx;
    
            # Display the line
            say $line;
        }
        {   # Display the needle numbers
            my $line;
            foreach my $col ( 0 .. 2 ) {
                $line .= sprintf '%*d%*s', MAX_DISKS + 5, $col + 1,
                MAX_DISKS, ' ';
            }
            $line =~ s/ \s+ \z //smx;
            say $line;
        }
    
        say ''; # Empty line
    
        return;
    }
    
    # Find the named disk. The arguments are the disk number (which is
    # assumed valid) and a reference to the board. If the disk is found on
    # the top of a needle, the needle's index (from zero) is returned.
    # Otherwise a warning is issued and undef is returned.
    sub find_disk {
        my ( $disk, $board ) = @_;
        foreach my $inx ( 0 .. 2 ) {
            @{ $board->[$inx] }                 # If the needle is occupied
                and $disk == $board->[$inx][-1] # and we want its topmost
                and return $inx;                # return needle index
        }
    
        # Since we assume the disk number is valid but we did not find it,
        # it must not be the topmost disk.
        warn "That disk is below another one.  Make another choice.\n";
    
        return undef;
    }
    
    # Input subroutine. The arguments are:
    # * The prompt.
    # * Validation code. This recieves the input in the topic variable $ARG,
    #   and returns a true value if the validation passed, and a false value
    #   if it failed.
    # * The maximum number of tries before dying.
    # * The warning message for a validation failure, with trailing "\n".
    # * The error message when the number of tries is exceeded, with
    #   trailing "\n".
    # The return is the valid input. We exit if end-of-file is reached,
    sub get_input {
        my ( $prompt, $validate, $tries, $warning, $error ) = @_;
    
        # Instantiate the readline object. A state variable is only
        # initialized once.
        state $term = Term::ReadLine->new( 'tower' );
    
        while ( 1 ) {   # Iterate until something makes us stop.
    
            # The input gets read into the localized topic variable. If it
            # is undefined, it signals end-of-file, so we exit.
            exit unless defined( local $ARG = $term->readline( $prompt ) );
    
            # Call the validation code. If it returns a true value, we
            # return our input.
            return $ARG if $validate->();
    
            # Die if we are out of retries. In Perl, 0 is false and all
            # other integers are true.
            die $error unless --$tries;
    
            # Warn.
            warn $warning;
        }
    }
    
    __END__
    
    =head1 TITLE
    
    tower.pl - Play the game 'tower' from Basic Computer Games
    
    =head1 SYNOPSIS
    
     tower.pl
    
    =head1 DETAILS
    
    This Perl script is a port of C, which is the 90th entry in Basic
    Computer Games.
    
    =head1 PORTED BY
    
    Thomas R. Wyant, III F
    
    =head1 COPYRIGHT AND LICENSE
    
    Copyright (C) 2022 by Thomas R. Wyant, III
    
    This program is free software; you can redistribute it and/or modify it
    under the same terms as Perl 5.10.0. For more details, see the Artistic
    License 1.0 at
    L, and/or the
    Gnu GPL at L.
    
    This program is distributed in the hope that it will be useful, but
    without any warranty; without even the implied warranty of
    merchantability or fitness for a particular purpose.
    
    =cut
    
    # ex: set expandtab tabstop=4 textwidth=72 :
    
    
    ================================================
    FILE: 90_Tower/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 90_Tower/python/tower.py
    ================================================
    import sys
    from typing import List, Optional
    
    
    class Disk:
        def __init__(self, size: int) -> None:
            self.__size = size
    
        def size(self) -> int:
            return self.__size
    
        def print(self) -> None:
            print(f"[ {self.size()} ]")
    
    
    class Tower:
        def __init__(self) -> None:
            self.__disks: List[Disk] = []
    
        def empty(self) -> bool:
            return len(self.__disks) == 0
    
        def top(self) -> Optional[Disk]:
            return None if self.empty() else self.__disks[-1]
    
        def add(self, disk: Disk) -> None:
            if not self.empty():
                t = self.top()
                assert t is not None  # cannot happen as it's not empty
                if disk.size() > t.size():
                    raise Exception(
                        "YOU CAN'T PLACE A LARGER DISK ON TOP OF A SMALLER ONE, IT MIGHT CRUSH IT!"
                    )
            self.__disks.append(disk)
    
        def pop(self) -> Disk:
            if self.empty():
                raise Exception("empty pop")
            return self.__disks.pop()
    
        def print(self) -> None:
            r = f'Needle: [{", ".join([str(x.size()) for x in self.__disks])}]'
            print(r)
    
    
    class Game:
        def __init__(self) -> None:
            # use fewer sizes to make debugging easier
            # self.__sizes = [3, 5, 7]  # ,9,11,13,15]
            self.__sizes = [3, 5, 7, 9, 11, 13, 15]
    
            self.__sizes.sort()
    
            self.__towers = []
            self.__moves = 0
            self.__towers = [Tower(), Tower(), Tower()]
            self.__sizes.reverse()
            for size in self.__sizes:
                disk = Disk(size)
                self.__towers[0].add(disk)
    
        def winner(self) -> bool:
            return self.__towers[0].empty() and self.__towers[1].empty()
    
        def print(self) -> None:
            for t in self.__towers:
                t.print()
    
        def moves(self) -> int:
            return self.__moves
    
        def which_disk(self) -> int:
            w = int(input("WHICH DISK WOULD YOU LIKE TO MOVE\n"))
            if w in self.__sizes:
                return w
            raise Exception()
    
        def pick_disk(self) -> Optional[Tower]:
            which = None
            while which is None:
                try:
                    which = self.which_disk()
                except Exception:
                    print("ILLEGAL ENTRY... YOU MAY ONLY TYPE 3,5,7,9,11,13, OR 15.\n")
    
            valids = [t for t in self.__towers if t.top() and t.top().size() == which]
            assert len(valids) in {0, 1}
            if not valids:
                print("THAT DISK IS BELOW ANOTHER ONE.  MAKE ANOTHER CHOICE.\n")
                return None
            else:
                assert valids[0].top().size() == which
                return valids[0]
    
        def which_tower(self) -> Optional[Tower]:
            try:
                needle = int(input("PLACE DISK ON WHICH NEEDLE\n"))
                tower = self.__towers[needle - 1]
            except Exception:
                print(
                    "I'LL ASSUME YOU HIT THE WRONG KEY THIS TIME.  BUT WATCH IT,\nI ONLY ALLOW ONE MISTAKE.\n"
                )
                return None
            else:
                return tower
    
        def take_turn(self) -> None:
            from_tower = None
            while from_tower is None:
                from_tower = self.pick_disk()
    
            to_tower = self.which_tower()
            if not to_tower:
                to_tower = self.which_tower()
    
            if not to_tower:
                print("I TRIED TO WARN YOU, BUT YOU WOULDN'T LISTEN.\nBYE BYE, BIG SHOT.\n")
                sys.exit(0)
    
            disk = from_tower.pop()
            try:
                to_tower.add(disk)
                self.__moves += 1
            except Exception as err:
                print(err)
                from_tower.add(disk)
    
    
    def main() -> None:
        print(
            """
        IN THIS PROGRAM, WE SHALL REFER TO DISKS BY NUMERICAL CODE.
        3 WILL REPRESENT THE SMALLEST DISK, 5 THE NEXT SIZE,
        7 THE NEXT, AND SO ON, UP TO 15.  IF YOU DO THE PUZZLE WITH
        2 DISKS, THEIR CODE NAMES WOULD BE 13 AND 15.  WITH 3 DISKS
        THE CODE NAMES WOULD BE 11, 13 AND 15, ETC.  THE NEEDLES
        ARE NUMBERED FROM LEFT TO RIGHT, 1 TO 3.  WE WILL
        START WITH THE DISKS ON NEEDLE 1, AND ATTEMPT TO MOVE THEM
        TO NEEDLE 3.
    
        GOOD LUCK!
    
        """
        )
    
        game = Game()
        while True:
            game.print()
    
            game.take_turn()
    
            if game.winner():
                print(
                    "CONGRATULATIONS!!\nYOU HAVE PERFORMED THE TASK IN %s MOVES.\n"
                    % game.moves()
                )
                while True:
                    yesno = input("TRY AGAIN (YES OR NO)\n")
                    if yesno.upper() == "YES":
                        game = Game()
                        break
                    elif yesno.upper() == "NO":
                        print("THANKS FOR THE GAME!\n")
                        sys.exit(0)
                    else:
                        print("'YES' OR 'NO' PLEASE\n")
            elif game.moves() > 128:
                print("SORRY, BUT I HAVE ORDERS TO STOP IF YOU MAKE MORE THAN 128 MOVES.")
                sys.exit(0)
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 90_Tower/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 90_Tower/rust/Cargo.toml
    ================================================
    [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]
    
    
    ================================================
    FILE: 90_Tower/rust/src/disk.rs
    ================================================
    pub struct Disk {
        pub size: u8,
    }
    
    impl Disk {
        pub fn new(size: u8) -> Self {
            Disk { size }
        }
    
        pub fn draw(&self) {
            let draw_space = || {
                let space_amount = (15 - self.size) / 2;
    
                if space_amount > 0 {
                    for _ in 0..space_amount {
                        print!(" ");
                    }
                }
            };
    
            draw_space();
            for _ in 0..self.size {
                print!("*");
            }
            draw_space();
            print!("   ");
        }
    }
    
    
    ================================================
    FILE: 90_Tower/rust/src/game.rs
    ================================================
    use crate::{
        disk::Disk,
        needle::Needle,
        util::{self, prompt, PromptResult},
    };
    
    pub struct Game {
        pub needles: Vec,
        disk_count: u8,
        moves: usize,
    }
    
    impl Game {
        pub fn new() -> Self {
            let mut needles = Vec::new();
            let disk_count = util::get_disk_count();
    
            for i in 1..=3 {
                let disks = match i {
                    1 => {
                        let mut disks = Vec::new();
    
                        let mut half_size = 7;
                        for _ in (1..=disk_count).rev() {
                            disks.push(Disk::new(half_size * 2 + 1));
                            half_size -= 1;
                        }
    
                        disks
                    }
                    2 | 3 => Vec::new(),
                    _ => panic!("THERE MUST BE EXACTLY THREE NEEDLES!"),
                };
    
                needles.push(Needle { disks, number: i });
            }
    
            Game {
                needles,
                disk_count,
                moves: 0,
            }
        }
    
        pub fn update(&mut self) -> bool {
            self.draw();
    
            loop {
                let (disk_index, from_needle_index) = self.get_disk_to_move();
                let to_needle_index = self.ask_which_needle();
    
                if from_needle_index == to_needle_index {
                    println!("DISK IS ALREADY AT THAT NEEDLE!");
                    break;
                }
    
                let to_needle = &self.needles[to_needle_index];
    
                if to_needle.disks.len() == 0
                    || to_needle.disks[0].size > self.needles[from_needle_index].disks[disk_index].size
                {
                    self.move_disk(disk_index, from_needle_index, to_needle_index);
                    break;
                } else {
                    println!("CAN'T PLACE ON A SMALLER DISK!");
                }
            }
    
            if self.needles[2].disks.len() == self.disk_count as usize {
                self.draw();
                println!("CONGRATULATIONS!!");
                println!("YOU HAVE PERFORMED THE TASK IN {} MOVES.", self.moves);
                return true;
            }
    
            false
        }
    
        pub fn draw(&self) {
            println!("");
            for r in (1..=7).rev() {
                for n in &self.needles {
                    n.draw(r)
                }
                println!("");
            }
            println!("");
        }
    
        fn get_disk_to_move(&self) -> (usize, usize) {
            loop {
                if let PromptResult::Number(n) = prompt(true, "WHICH DISK WOULD YOU LIKE TO MOVE?") {
                    let smallest_disk = 15 - ((self.disk_count - 1) * 2);
    
                    if n < smallest_disk as i32 || n > 15 || (n % 2) == 0 {
                        println!("PLEASE ENTER A VALID DISK!")
                    } else {
                        for (n_i, needle) in self.needles.iter().enumerate() {
                            if let Some((i, _)) = needle
                                .disks
                                .iter()
                                .enumerate()
                                .find(|(_, disk)| disk.size == n as u8)
                            {
                                if i == (needle.disks.len() - 1) {
                                    return (i, n_i);
                                }
    
                                println!("THAT DISK IS BELOW ANOTHER ONE. MAKE ANOTHER CHOICE.");
                            }
                        }
                    }
                }
            }
        }
    
        fn ask_which_needle(&self) -> usize {
            loop {
                if let PromptResult::Number(n) = prompt(true, "PLACE DISK ON WHICH NEEDLE?") {
                    if n <= 0 || n > 3 {
                        println!("PLEASE ENTER A VALID NEEDLE.");
                    } else {
                        return (n - 1) as usize;
                    }
                }
            }
        }
    
        fn move_disk(&mut self, disk: usize, from: usize, to: usize) {
            let from = &mut self.needles[from];
            let size = from.disks[disk].size;
    
            from.disks.remove(disk);
            self.needles[to].add(size);
    
            self.moves += 1;
        }
    }
    
    
    ================================================
    FILE: 90_Tower/rust/src/main.rs
    ================================================
    use game::Game;
    use util::PromptResult;
    
    mod disk;
    mod game;
    mod needle;
    mod util;
    
    fn main() {
        println!("\n\n\t\tTOWERS");
        println!("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n");
    
        println!("TOWERS OF HANOI PUZZLE\n");
    
        println!("YOU MUST TRANSFER THE DISKS FROM THE LEFT TO THE RIGHT");
        println!("TOWER, ON AT A TIME, NEVER PUTTING A LARGER DISK ON A");
        println!("SMALLER DISK.\n");
    
        let mut quit = false;
    
        while !quit {
            let mut game = Game::new();
    
            println!("");
            println!(
                r#"IN THIS PROGRAM, WE SHALL REFER TO DISKS BY NUMERICAL CODE.
    3 WILL REPRESENT THE SMALLEST DISK, 5 THE NEXT SIZE,
    7 THE NEXT, AND SO ON, UP TO 15.  IF YOU DO THE PUZZLE WITH
    2 DISKS, THEIR CODE NAMES WOULD BE 13 AND 15.  WITH 3 DISKS
    THE CODE NAMES WOULD BE 11, 13 AND 15, ETC.  THE NEEDLES
    ARE NUMBERED FROM LEFT TO RIGHT, 1 TO 3.  WE WILL
    START WITH THE DISKS ON NEEDLE 1, AND ATTEMPT TO MOVE THEM
    TO NEEDLE 3.
    
    GOOD LUCK!"#
            );
    
            loop {
                if game.update() {
                    break;
                }
            }
    
            if let PromptResult::YesNo(r) = util::prompt(false, "TRY AGAIN (YES OR NO)?") {
                quit = !r;
            }
        }
    
        println!("THANKS FOR THE GAME!");
    }
    
    
    ================================================
    FILE: 90_Tower/rust/src/needle.rs
    ================================================
    use crate::disk::Disk;
    
    pub struct Needle {
        pub disks: Vec,
        pub number: u8,
    }
    
    impl Needle {
        pub fn draw(&self, row: u8) {
            let row = row as usize;
    
            if self.disks.len() >= row {
                self.disks[row - 1].draw();
            } else {
                let offset = "       ";
    
                print!("{offset}");
                print!("*");
                print!("{offset}   ");
            }
        }
    
        pub fn add(&mut self, size: u8) {
            self.disks.push(Disk { size });
        }
    }
    
    
    ================================================
    FILE: 90_Tower/rust/src/util.rs
    ================================================
    use std::io;
    
    pub enum PromptResult {
        Number(i32),
        YesNo(bool),
    }
    
    pub fn prompt(numeric: bool, msg: &str) -> PromptResult {
        use PromptResult::*;
        loop {
            println!("{}", msg);
    
            let mut input = String::new();
    
            io::stdin()
                .read_line(&mut input)
                .expect("Failed to read line.");
    
            let input = input.trim().to_string();
    
            if numeric {
                if let Ok(n) = input.parse::() {
                    return Number(n);
                }
    
                println!("PLEASE ENTER A NUMBER.")
            } else {
                match input.to_uppercase().as_str() {
                    "YES" | "Y" => return YesNo(true),
                    "NO" | "N" => return YesNo(false),
                    _ => println!("PLEASE ENTER (Y)ES OR (N)O."),
                }
            }
        }
    }
    
    pub fn get_disk_count() -> u8 {
        loop {
            if let PromptResult::Number(n) =
                prompt(true, "HOW MANY DISKS DO YOU WANT TO MOVE (7 IS MAX)?")
            {
                if n <= 2 {
                    println!("THERE MUST BE AT LEAST 3 DISKS!")
                } else if n > 7 {
                    println!("THERE CAN'T BE MORE THAN 7 DISKS!")
                } else {
                    return n as u8;
                }
            }
        }
    }
    
    
    ================================================
    FILE: 90_Tower/tower.bas
    ================================================
    10 PRINT TAB(33);"TOWERS"
    20 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    30 PRINT:PRINT:PRINT
    90 PRINT
    100 REM*** INITIALIZE
    110 DIM T(7,3)
    120 E=0
    130 FOR D=1 TO 7
    140 FOR N=1 TO 3
    150 T(D,N)=0
    160 NEXT N
    170 NEXT D
    180 PRINT "TOWERS OF HANOI PUZZLE.": PRINT
    200 PRINT "YOU MUST TRANSFER THE DISKS FROM THE LEFT TO THE RIGHT"
    205 PRINT "TOWER, ONE AT A TIME, NEVER PUTTING A LARGER DISK ON A"
    210 PRINT "SMALLER DISK.": PRINT
    215 INPUT "HOW MANY DISKS DO YOU WANT TO MOVE (7 IS MAX)";S
    220 PRINT
    230 M=0
    240 FOR Q=1 TO 7
    250 IF Q=S THEN 350
    260 NEXT Q
    270 E=E+1
    280 IF E>2 THEN 310
    290 PRINT "SORRY, BUT I CAN'T DO THAT JOB FOR YOU.": GOTO 215
    310 PRINT "ALL RIGHT, WISE GUY, IF YOU CAN'T PLAY THE GAME RIGHT, I'LL"
    320 PRINT "JUST TAKE MY PUZZLE AND GO HOME.  SO LONG.": STOP
    340 REM *** STORE DISKS FROM SMALLEST TO LARGEST
    350 PRINT "IN THIS PROGRAM, WE SHALL REFER TO DISKS BY NUMERICAL CODE."
    355 PRINT "3 WILL REPRESENT THE SMALLEST DISK, 5 THE NEXT SIZE,"
    360 PRINT "7 THE NEXT, AND SO ON, UP TO 15.  IF YOU DO THE PUZZLE WITH"
    365 PRINT "2 DISKS, THEIR CODE NAMES WOULD BE 13 AND 15.  WITH 3 DISKS"
    370 PRINT "THE CODE NAMES WOULD BE 11, 13 AND 15, ETC.  THE NEEDLES"
    375 PRINT "ARE NUMBERED FROM LEFT TO RIGHT, 1 TO 3.  WE WILL"
    380 PRINT "START WITH THE DISKS ON NEEDLE 1, AND ATTEMPT TO MOVE THEM"
    385 PRINT "TO NEEDLE 3."
    390 PRINT: PRINT "GOOD LUCK!": PRINT
    400 Y=7: D=15
    420 FOR X=S TO 1 STEP -1
    430 T(Y,1)=D: D=D-2: Y=Y-1
    460 NEXT X
    470 GOSUB 1230
    480 PRINT "WHICH DISK WOULD YOU LIKE TO MOVE";:E=0
    500 INPUT D
    510 IF (D-3)*(D-5)*(D-7)*(D-9)*(D-11)*(D-13)*(D-15)=0 THEN 580
    520 PRINT "ILLEGAL ENTRY... YOU MAY ONLY TYPE 3,5,7,9,11,13, OR 15."
    530 E=E+1: IF E>1 THEN 560
    550 GOTO 500
    560 PRINT "STOP WASTING MY TIME.  GO BOTHER SOMEONE ELSE.": STOP
    580 REM *** CHECK IF REQUESTED DISK IS BELOW ANOTHER
    590 FOR R=1 TO 7
    600 FOR C=1 TO 3
    610 IF T(R,C)=D THEN 640
    620 NEXT C: NEXT R
    640 FOR Q=R TO 1 STEP -1
    645 IF T(Q,C)=0 THEN 660
    650 IF T(Q,C)1 THEN 780
    750 PRINT "I'LL ASSUME YOU HIT THE WRONG KEY THIS TIME.  BUT WATCH IT,"
    760 PRINT "I ONLY ALLOW ONE MISTAKE.": GOTO 705
    780 PRINT "I TRIED TO WARN YOU, BUT YOU WOULDN'T LISTEN."
    790 PRINT "BYE BYE, BIG SHOT.":STOP
    800 FOR R=1 TO 7
    810 IF T(R,N)<>0 THEN 840
    820 NEXT R
    830 GOTO 880
    835 REM *** CHECK IF DISK TO BE PLACED ON A LARGER ONE
    840 IF D0 THEN 970
    950 NEXT U
    960 U=7: GOTO 980
    965 REM *** MOVE DISK AND SET OLD LOCATION TO 0
    970 U=U-1
    980 T(U,N)=T(V,W): T(V,W)=0
    995 REM *** PRINT OUT CURRENT STATUS
    1000 GOSUB 1230
    1018 REM *** CHECK IF DONE
    1020 M=M+1
    1030 FOR R=1 TO 7: FOR C=1 TO 2
    1050 IF T(R,C)<>0 THEN 1090
    1060 NEXT C: NEXT R
    1080 GOTO 1120
    1090 IF M<=128 THEN 480
    1100 PRINT "SORRY, BUT I HAVE ORDERS TO STOP IF YOU MAKE MORE THAN"
    1110 PRINT "128 MOVES.": STOP
    1120 IF M<>2^S-1 THEN 1140
    1130 PRINT:PRINT "CONGRATULATIONS!!":PRINT
    1140 PRINT "YOU HAVE PERFORMED THE TASK IN";M;"MOVES."
    1150 PRINT: PRINT "TRY AGAIN (YES OR NO)";: INPUT A$
    1160 IF A$="NO" THEN 1390
    1170 IF A$="YES" THEN 90
    1180 PRINT: PRINT "'YES' OR 'NO' PLEASE";: INPUT A$: GOTO 1160
    1230 REM *** PRINT SUBROUTINE
    1240 FOR K=1 TO 7
    1250 Z=10
    1260 FOR J=1 TO 3
    1270 IF T(K,J)=0 THEN 1330
    1280 PRINT TAB(Z-INT(T(K,J)/2));
    1290 FOR V=1 TO T(K,J)
    1300 PRINT "*";
    1310 NEXT V
    1320 GOTO 1340
    1330 PRINT TAB(Z);"*";
    1340 Z=Z+21
    1350 NEXT J
    1360 PRINT
    1370 NEXT K
    1380 RETURN
    1390 PRINT: PRINT "THANKS FOR THE GAME!": PRINT: END
    
    
    ================================================
    FILE: 90_Tower/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 90_Tower/vbnet/Tower.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Tower", "Tower.vbproj", "{9B62545A-F076-4390-864B-ABE6FC20CC62}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{9B62545A-F076-4390-864B-ABE6FC20CC62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{9B62545A-F076-4390-864B-ABE6FC20CC62}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{9B62545A-F076-4390-864B-ABE6FC20CC62}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{9B62545A-F076-4390-864B-ABE6FC20CC62}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 90_Tower/vbnet/Tower.vbproj
    ================================================
    
      
        Exe
        Tower
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 91_Train/README.md
    ================================================
    ### Train
    
    TRAIN is a program which uses the computer to generate problems with random initial conditions to teach about the time-speed-distance relationship (distance = rate x time). You then input your answer and the computer verifies your response.
    
    TRAIN is merely an example of a student-generated problem. Maximum fun (and benefit) comes more from _writing_ programs like this as opposed to solving the specific problem posed. Exchange your program with others—you solve their problem and let them solve yours.
    
    TRAIN was originally written in FOCAL by one student for use by others in his class. It was submitted to us by Walt Koetke, Lexington High School, Lexington, Mass.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=175)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=190)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 91_Train/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 91_Train/csharp/Train/TrainGame.cs
    ================================================
    using System;
    using System.Linq;
    
    namespace Train
    {
        public class TrainGame
        {
            private Random Rnd { get; } = new Random();
            private readonly int ALLOWED_PERCENTAGE_DIFFERENCE = 5;
    
            static void Main()
            {
                TrainGame train = new TrainGame();
                train.GameLoop();
            }
    
            public void GameLoop()
            {
                DisplayIntroText();
    
                do
                {
                    PlayGame();
                } while (TryAgain());
            }
    
            private void PlayGame()
            {
                int carSpeed = (int)GenerateRandomNumber(40, 25);
                int timeDifference = (int)GenerateRandomNumber(5, 15);
                int trainSpeed = (int)GenerateRandomNumber(20, 19);
    
                Console.WriteLine($"A CAR TRAVELING {carSpeed} MPH CAN MAKE A CERTAIN TRIP IN");
                Console.WriteLine($"{timeDifference} HOURS LESS THAN A TRAIN TRAVELING AT {trainSpeed} MPH");
                Console.WriteLine("HOW LONG DOES THE TRIP TAKE BY CAR?");
    
                double userInputCarJourneyDuration = double.Parse(Console.ReadLine());
                double actualCarJourneyDuration = CalculateCarJourneyDuration(carSpeed, timeDifference, trainSpeed);
                int percentageDifference = CalculatePercentageDifference(userInputCarJourneyDuration, actualCarJourneyDuration);
    
                if (IsWithinAllowedDifference(percentageDifference, ALLOWED_PERCENTAGE_DIFFERENCE))
                {
                    Console.WriteLine($"GOOD! ANSWER WITHIN {percentageDifference} PERCENT.");
                }
                else
                {
                    Console.WriteLine($"SORRY.  YOU WERE OFF BY {percentageDifference} PERCENT.");
                }
                Console.WriteLine($"CORRECT ANSWER IS {actualCarJourneyDuration} HOURS.");
            }
    
            public static bool IsWithinAllowedDifference(int percentageDifference, int allowedDifference)
            {
                return percentageDifference <= allowedDifference;
            }
    
            private static int CalculatePercentageDifference(double userInputCarJourneyDuration, double carJourneyDuration)
            {
                return (int)(Math.Abs((carJourneyDuration - userInputCarJourneyDuration) * 100 / userInputCarJourneyDuration) + .5);
            }
    
            public static double CalculateCarJourneyDuration(double carSpeed, double timeDifference, double trainSpeed)
            {
                return timeDifference * trainSpeed / (carSpeed - trainSpeed);
            }
    
            public double GenerateRandomNumber(int baseSpeed, int multiplier)
            {
                return multiplier * Rnd.NextDouble() + baseSpeed;
            }
    
            private bool TryAgain()
            {
                Console.WriteLine("ANOTHER PROBLEM (YES OR NO)? ");
                return IsInputYes(Console.ReadLine());
            }
    
            public static bool IsInputYes(string consoleInput)
            {
                var options = new string[] { "Y", "YES" };
                return options.Any(o => o.Equals(consoleInput, StringComparison.CurrentCultureIgnoreCase));
            }
    
            private void DisplayIntroText()
            {
                Console.WriteLine("TRAIN");
                Console.WriteLine("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
                Console.WriteLine();
                Console.WriteLine("TIME - SPEED DISTANCE EXERCISE");
                Console.WriteLine();
            }
        }
    }
    
    
    ================================================
    FILE: 91_Train/csharp/Train/TrainGame.csproj
    ================================================
    
    
      
        Exe
        netcoreapp3.1
      
    
    
    
    
    ================================================
    FILE: 91_Train/csharp/Train.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.31129.286
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TrainGame", "Train\TrainGame.csproj", "{42617537-4E7C-4082-A17B-7F18DFA04C35}"
    EndProject
    Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TrainTests", "TrainTests\TrainTests.csproj", "{B967AA46-78F2-44F8-A30D-85D35F625991}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{42617537-4E7C-4082-A17B-7F18DFA04C35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{42617537-4E7C-4082-A17B-7F18DFA04C35}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{42617537-4E7C-4082-A17B-7F18DFA04C35}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{42617537-4E7C-4082-A17B-7F18DFA04C35}.Release|Any CPU.Build.0 = Release|Any CPU
    		{B967AA46-78F2-44F8-A30D-85D35F625991}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{B967AA46-78F2-44F8-A30D-85D35F625991}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{B967AA46-78F2-44F8-A30D-85D35F625991}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{B967AA46-78F2-44F8-A30D-85D35F625991}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {919F73B8-DE34-4992-9B05-E1FEC2D2F7C6}
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 91_Train/csharp/TrainTests/TrainGameTests.cs
    ================================================
    using Train;
    using Xunit;
    
    namespace TrainTests
    {
        public class TrainGameTests
        {
            [Fact]
            public void MiniumRandomNumber()
            {
                TrainGame game = new TrainGame();
                Assert.True(game.GenerateRandomNumber(10, 10) >= 10);
            }
    
            [Fact]
            public void MaximumRandomNumber()
            {
                TrainGame game = new TrainGame();
                Assert.True(game.GenerateRandomNumber(10, 10) <= 110);
            }
    
            [Fact]
            public void IsInputYesWhenY()
            {
                Assert.True(TrainGame.IsInputYes("y"));
            }
    
            [Fact]
            public void IsInputYesWhenNotY()
            {
                Assert.False(TrainGame.IsInputYes("a"));
            }
    
            [Fact]
            public void CarDurationTest()
            {
                Assert.Equal(1, TrainGame.CalculateCarJourneyDuration(30, 1, 15) );
            }
    
            [Fact]
            public void IsWithinAllowedDifference()
            {
                Assert.True(TrainGame.IsWithinAllowedDifference(5,5));
            }
    
    
            [Fact]
            public void IsNotWithinAllowedDifference()
            {
                Assert.False(TrainGame.IsWithinAllowedDifference(6, 5));
            }
        }
    }
    
    
    ================================================
    FILE: 91_Train/csharp/TrainTests/TrainTests.csproj
    ================================================
    
    
      
        netcoreapp3.1
    
        false
      
    
      
        
        
        
          runtime; build; native; contentfiles; analyzers; buildtransitive
          all
        
        
          runtime; build; native; contentfiles; analyzers; buildtransitive
          all
        
      
    
      
        
      
    
    
    
    
    ================================================
    FILE: 91_Train/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 91_Train/java/src/Train.java
    ================================================
    import java.util.Arrays;
    import java.util.Scanner;
    
    /**
     * Train
     * 

    * Based on the Basic program Train here * https://github.com/coding-horror/basic-computer-games/blob/main/91%20Train/train.bas *

    * Note: The idea was to create a version of the 1970's Basic program in Java, without introducing * new features - no additional text, error checking, etc has been added. */ public class Train { private final Scanner kbScanner; public Train() { kbScanner = new Scanner(System.in); } public void process() { intro(); boolean gameOver = false; do { double carMph = (int) (25 * Math.random() + 40); double hours = (int) (15 * Math.random() + 5); double train = (int) (19 * Math.random() + 20); System.out.println(" A CAR TRAVELING " + (int) carMph + " MPH CAN MAKE A CERTAIN TRIP IN"); System.out.println((int) hours + " HOURS LESS THAN A TRAIN TRAVELING AT " + (int) train + " MPH."); double howLong = Double.parseDouble(displayTextAndGetInput("HOW LONG DOES THE TRIP TAKE BY CAR? ")); double hoursAnswer = hours * train / (carMph - train); int percentage = (int) (Math.abs((hoursAnswer - howLong) * 100 / howLong) + .5); if (percentage > 5) { System.out.println("SORRY. YOU WERE OFF BY " + percentage + " PERCENT."); } else { System.out.println("GOOD! ANSWER WITHIN " + percentage + " PERCENT."); } System.out.println("CORRECT ANSWER IS " + hoursAnswer + " HOURS."); System.out.println(); if (!yesEntered(displayTextAndGetInput("ANOTHER PROBLEM (YES OR NO)? "))) { gameOver = true; } } while (!gameOver); } private void intro() { System.out.println("TRAIN"); System.out.println("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println(); System.out.println("TIME - SPEED DISTANCE EXERCISE"); System.out.println(); } /* * Print a message on the screen, then accept input from Keyboard. * * @param text message to be displayed on screen. * @return what was typed by the player. */ private String displayTextAndGetInput(String text) { System.out.print(text); return kbScanner.next(); } /** * Checks whether player entered Y or YES to a question. * * @param text player string from kb * @return true of Y or YES was entered, otherwise false */ private boolean yesEntered(String text) { return stringIsAnyValue(text, "Y", "YES"); } /** * Check whether a string equals one of a variable number of values * Useful to check for Y or YES for example * Comparison is case insensitive. * * @param text source string * @param values a range of values to compare against the source string * @return true if a comparison was found in one of the variable number of strings passed */ private boolean stringIsAnyValue(String text, String... values) { return Arrays.stream(values).anyMatch(str -> str.equalsIgnoreCase(text)); } /** * Program startup. * * @param args not used (from command line). */ public static void main(String[] args) { Train train = new Train(); train.process(); } } ================================================ FILE: 91_Train/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 91_Train/javascript/train.html ================================================ TRAIN

    
    
    
    
    
    
    ================================================
    FILE: 91_Train/javascript/train.js
    ================================================
    // TRAIN
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    // Main control section
    async function main()
    {
        print(tab(33) + "TRAIN\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        print("TIME - SPEED DISTANCE EXERCISE\n");
        print("\n ");
        while (1) {
            c = Math.floor(25 * Math.random()) + 40;
            d = Math.floor(15 * Math.random()) + 5;
            t = Math.floor(19 * Math.random()) + 20;
            print(" A CAR TRAVELING " + c + " MPH CAN MAKE A CERTAIN TRIP IN\n");
            print(d + " HOURS LESS THAN A TRAIN TRAVELING AT " + t + " MPH.\n");
            print("HOW LONG DOES THE TRIP TAKE BY CAR");
            a = parseFloat(await input());
            v = d * t / (c - t);
            e = Math.floor(Math.abs((v - a) * 100 / a) + 0.5);
            if (e > 5) {
                print("SORRY.  YOU WERE OFF BY " + e + " PERCENT.\n");
            } else {
                print("GOOD! ANSWER WITHIN " + e + " PERCENT.\n");
            }
            print("CORRECT ANSWER IS " + v + " HOURS.\n");
            print("\n");
            print("ANOTHER PROBLEM (YES OR NO)\n");
            str = await input();
            print("\n");
            if (str.substr(0, 1) != "Y")
                break;
        }
    }
    
    main();
    
    
    ================================================
    FILE: 91_Train/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 91_Train/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 91_Train/lua/train.lua
    ================================================
    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()
    
    ================================================
    FILE: 91_Train/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 91_Train/perl/train.pl
    ================================================
    #!/usr/bin/perl
    use strict;
    use warnings;
    
    print ' 'x33 ."TRAIN\n";
    print ' 'x15 ."CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n";
    print "\n\n\n";
    print "TIME - SPEED DISTANCE EXERCISE\n"; print "\n";
    
    
    my $A= ""; #We must declare this before...
    do {
    	my $C= int(25*rand(1))+40;
    	my $D= int(15*rand(1))+5;
    	my $T= int(19*rand(1))+20;
    
    	print " A CAR TRAVELING $C MPH CAN MAKE A CERTAIN TRIP IN\n";
    	print "$D HOURS LESS THAN A TRAIN TRAVELING AT $T MPH.\n";
    	print "HOW LONG DOES THE TRIP TAKE BY CAR\n";
    	chomp ($A = );
    
    	my $V= $D*$T/($C-$T);
    	my $E= int(abs(($V-$A)*100/$A)+.5);
    	if ($E>5) {
    		print "SORRY.  YOU WERE OFF BY $E PERCENT.\n";
    		} else {
    		print "GOOD! ANSWER WITHIN $E PERCENT.\n";
    		}
    
    	print "CORRECT ANSWER IS $V HOURS.\n";
    	print "\n";
    	print "ANOTHER PROBLEM (YES OR NO)\n";
    	chomp ($A = uc()); #Uppercased
    	} until ($A ne "YES");
    
    
    ================================================
    FILE: 91_Train/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 91_Train/python/train.py
    ================================================
    #!/usr/bin/env python3
    # TRAIN
    #
    # Converted from BASIC to Python by Trevor Hobson
    
    import random
    
    
    def play_game() -> None:
        """Play one round of the game"""
        car_speed = random.randint(40, 65)
        time_difference = random.randint(5, 20)
        train_speed = random.randint(20, 39)
        print("\nA car travelling", car_speed, "MPH can make a certain trip in")
        print(time_difference, "hours less than a train travelling at", train_speed, "MPH")
        time_answer: float = 0
        while time_answer == 0:
            try:
                time_answer = float(input("How long does the trip take by car "))
            except ValueError:
                print("Please enter a number.")
        car_time = time_difference * train_speed / (car_speed - train_speed)
        error_percent = int(abs((car_time - time_answer) * 100 / time_answer) + 0.5)
        if error_percent > 5:
            print("Sorry. You were off by", error_percent, "percent.")
            print("Correct answer is", round(car_time, 6), "hours")
        else:
            print("Good! Answer within", error_percent, "percent.")
    
    
    def main() -> None:
        print(" " * 33 + "TRAIN")
        print(" " * 15 + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n")
        print("Time - speed distance exercise")
    
        keep_playing = True
        while keep_playing:
            play_game()
            keep_playing = input("\nAnother problem (yes or no) ").lower().startswith("y")
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 91_Train/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 91_Train/ruby/train.rb
    ================================================
    def intro
      puts "                                 TRAIN
                   CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY
    
    
    
    TIME - SPEED DISTANCE EXERCISE
    
    "
    end
    
    def get_user_guess
      while true
        begin
          number = Float(gets.chomp)
          return number
        rescue ArgumentError
          # Ignored
        end
    
        puts "!NUMBER EXPECTED - RETRY INPUT LINE"
        print "? "
      end
    end
    
    def main
      intro
    
      loop do
        car_speed = rand(25) + 40
        car_time = rand(15) + 5
        train_speed = rand(19) + 20
    
        print " A CAR TRAVELING #{car_speed} MPH CAN MAKE A CERTAIN TRIP IN
     #{car_time} HOURS LESS THAN A TRAIN TRAVELING AT #{train_speed} MPH.
    HOW LONG DOES THE TRIP TAKE BY CAR? "
        guess = get_user_guess
    
        answer = ((car_time * train_speed) / (car_speed - train_speed).to_f).round(5)
        delta = (((answer - guess) * 100 / guess) + 0.5).abs.to_i
    
        if delta > 5
          puts "SORRY.  YOU WERE OFF BY #{delta} PERCENT."
        else
          puts "GOOD! ANSWER WITHIN #{delta} PERCENT."
        end
    
        print "CORRECT ANSWER IS #{answer == answer.to_i ? answer.to_i : answer} HOURS.
    
    ANOTHER PROBLEM (YES OR NO)? "
        option = (gets || '').chomp.upcase
        break unless option == 'YES'
      end
    end
    
    trap "SIGINT" do puts; exit 130 end
    
    main
    
    
    ================================================
    FILE: 91_Train/rust/Cargo.toml
    ================================================
    [package]
    name = "rust"
    version = "0.1.0"
    authors = ["AnthonyMichaelTDM <68485672+AnthonyMichaelTDM@users.noreply.github.com>"]
    edition = "2021"
    
    # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
    
    [dependencies]
    rand = "0.8.5"
    
    
    ================================================
    FILE: 91_Train/rust/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Rust](https://www.rust-lang.org/) by Anthony Rubick [AnthonyMichaelTDM](https://github.com/AnthonyMichaelTDM)
    
    
    ================================================
    FILE: 91_Train/rust/src/lib.rs
    ================================================
    /*
     lib.rs contains all the logic of the program
    */
    use rand::{Rng, prelude::thread_rng}; //rng
    use std::error::Error; //better errors
    use std::io::{self, Write}; //io interactions
    use std::{str::FromStr, fmt::Display}; //traits
    
    //DATA
    
    /// handles setup for the game
    pub struct Config {
    }
    impl Config {
        /// creates and returns a new Config from user input
        pub fn new() -> Result> {
            //DATA
            let config: Config = Config { 
            };
            
            //return new config
            return Ok(config);
        }
    }
    
    /// run the program
    pub fn run(_config: &Config) -> Result<(), Box> {
        //DATA
        let mut rng = thread_rng();
        
        let mut speed_train_1;
        let mut time_difference;
        let mut speed_train_2;
    
        let mut guess;
        let mut answer;
        
        let mut error:f32; 
    
        //Game loop
        loop {
            //initialize variables
            speed_train_1 =  rng.gen_range(40..65);
            time_difference = rng.gen_range(5..20); 
            speed_train_2 = rng.gen_range(20..39);
    
            //print starting message / conditions
            println!("A CAR TRAVELING {} MPH CAN MAKE A CERTAIN TRIP IN\n{} HOURS LESS THAN A TRAIN TRAVELING AT {} MPH",speed_train_1,time_difference,speed_train_2);
            println!();
    
            //get guess
            guess = loop {
                match get_number_from_input("HOW LONG DOES THE TRIP TAKE BY CAR?",0,-1) {
                    Ok(num) => break num,
                    Err(err) => {
                        eprintln!("{}",err);
                        continue;
                    },
                }
            };
    
            //calculate answer and error
            answer = time_difference * speed_train_2 / (speed_train_1 - speed_train_2);
            error = ((answer - guess) as isize).abs() as f32 * 100.0/(guess as f32) + 0.5;
    
            //check guess against answer
            if error > 5.0 {
                println!("SORRY, YOU WERE OFF BY {} PERCENT.", error);
                println!("CORRECT ANSWER IS {} HOURS.",answer);
            } else {
                println!("GOOD! ANSWER WITHIN {} PERCENT.", error);
            }
    
            //ask user if they want to go again
            match get_string_from_user_input("ANOTHER PROBLEM (Y/N)") {
                Ok(s) => if !s.to_uppercase().eq("Y") {break;} else {continue;},
                _ => break,
            }
        }
    
        //return to main
        Ok(())
    }
    
    /// gets a string from user input
    fn get_string_from_user_input(prompt: &str) -> Result> {
        //DATA
        let mut raw_input = String::new();
    
        //print prompt
        print!("{}", prompt);
        //make sure it's printed before getting input
        io::stdout().flush().expect("couldn't flush stdout");
    
        //read user input from standard input, and store it to raw_input, then return it or an error as needed
        raw_input.clear(); //clear input
        match io::stdin().read_line(&mut raw_input) {
            Ok(_num_bytes_read) => return Ok(String::from(raw_input.trim())),
            Err(err) => return Err(format!("ERROR: CANNOT READ INPUT!: {}", err).into()),
        }
    }
    /// generic function to get a number from the passed string (user input)
    /// pass a min lower  than the max to have minimum and maximum bounds
    /// pass a min higher than the max to only have a minimum bound
    /// pass a min equal   to  the max to only have a maximum bound
    /// 
    /// Errors:
    /// no number on user input
    fn get_number_from_input(prompt: &str, min:T, max:T) -> Result> {
        //DATA
        let raw_input: String;
        let processed_input: String;
    
        
        //input loop
        raw_input = loop {
            match get_string_from_user_input(prompt) {
                Ok(input) => break input,
                Err(e) => {
                    eprintln!("{}",e);
                    continue;
                },
            }
        };
    
        //filter out non-numeric characters from user input
        processed_input = raw_input.chars().filter(|c| c.is_numeric()).collect();
    
        //from input, try to read a number
        match processed_input.trim().parse() {
            Ok(i) => {
                //what bounds must the input fall into
                if min < max {  //have a min and max bound: [min,max]
                    if i >= min && i <= max {//is input valid, within bounds
                        return Ok(i); //exit the loop with the value i, returning it
                    } else { //print error message specific to this case
                        return Err(format!("ONLY BETWEEN {} AND {}, PLEASE!", min, max).into());
                    } 
                } else if min > max { //only a min bound: [min, infinity)
                    if i >= min {
                        return Ok(i);
                    } else {
                        return Err(format!("NO LESS THAN {}, PLEASE!", min).into());
                    }
                } else { //only a max bound: (-infinity, max]
                    if i <= max {
                        return Ok(i);
                    } else {
                        return Err(format!("NO MORE THAN {}, PLEASE!", max).into());
                    }
                }
            },
            Err(_e) => return Err(format!("Error: couldn't find a valid number in {}",raw_input).into()),
        }
    }
    
    
    ================================================
    FILE: 91_Train/rust/src/main.rs
    ================================================
    use std::process;//allows for some better error handling
    
    mod lib; //allows access to lib.rs
    use lib::Config;
    
    /// main function
    /// responsibilities:
    /// - Calling the command line logic with the argument values
    /// - Setting up any other configuration
    /// - Calling a run function in lib.rs
    /// - Handling the error if run returns an error
    fn main() {
        //greet user
        welcome();
    
        // set up other configuration
        let mut config = Config::new().unwrap_or_else(|err| {
            eprintln!("Problem configuring program: {}", err);
            process::exit(1);
        });
    
        // run the program
        if let Err(e) = lib::run(&mut config) {
            eprintln!("Application Error: {}", e); //use the eprintln! macro to output to standard error
            process::exit(1); //exit the program with an error code
        }
    
        //end of program
        println!("THANKS FOR PLAYING!");
    }
    
    /// print the welcome message
    fn welcome() {
        println!("
                                    Train
                  CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY
    
    
    TIME - SPEED DISTANCE EXERCISE
        ");
    }
    
    
    ================================================
    FILE: 91_Train/train.bas
    ================================================
    1 PRINT TAB(33);"TRAIN"
    2 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    3 PRINT: PRINT: PRINT
    4 PRINT "TIME - SPEED DISTANCE EXERCISE": PRINT
    10 C=INT(25*RND(1))+40
    15 D=INT(15*RND(1))+5
    20 T=INT(19*RND(1))+20
    25 PRINT " A CAR TRAVELING";C;"MPH CAN MAKE A CERTAIN TRIP IN"
    30 PRINT D;"HOURS LESS THAN A TRAIN TRAVELING AT";T;"MPH."
    35 PRINT "HOW LONG DOES THE TRIP TAKE BY CAR";
    40 INPUT A
    45 V=D*T/(C-T)
    50 E=INT(ABS((V-A)*100/A)+.5)
    55 IF E>5 THEN 70
    60 PRINT "GOOD! ANSWER WITHIN";E;"PERCENT."
    65 GOTO 80
    70 PRINT "SORRY.  YOU WERE OFF BY";E;"PERCENT."
    80 PRINT "CORRECT ANSWER IS";V;"HOURS."
    90 PRINT
    95 PRINT "ANOTHER PROBLEM (YES OR NO)";
    100 INPUT A$
    105 PRINT
    110 IF A$="YES" THEN 10
    999 END
    
    
    ================================================
    FILE: 91_Train/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 91_Train/vbnet/Train.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Train", "Train.vbproj", "{05F1229A-9138-4B1D-9781-CE8C4F7A6151}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{05F1229A-9138-4B1D-9781-CE8C4F7A6151}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{05F1229A-9138-4B1D-9781-CE8C4F7A6151}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{05F1229A-9138-4B1D-9781-CE8C4F7A6151}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{05F1229A-9138-4B1D-9781-CE8C4F7A6151}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 91_Train/vbnet/Train.vbproj
    ================================================
    
      
        Exe
        Train
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 92_Trap/README.md
    ================================================
    ### Trap
    
    This is another in the family of “guess the mystery number” games. In TRAP the computer selects a random number between 1 and 100 (or other limit set). Your object is to find the number. On each guess, you enter 2 numbers trying to trap the mystery number between your two trap numbers. The computer will tell you if you have trapped the number.
    
    To win the game, you must guess the mystery number by entering it as the same value for both of your trap numbers. You get 6 guesses (this should be changed if you change the guessing limit).
    
    After you have played GUESS, STARS, and TRAP, compare the guessing strategy you have found best for each game. Do you notice any similarities? What are the differences? Can you write a new guessing game with still another approach?
    
    TRAP was suggested by a 10-year-old when he was playing GUESS. It was originally programmed by Steve Ullman and extensively modified into its final form by Bob Albrecht of People’s Computer Co.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=176)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=191)
    
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 92_Trap/csharp/Program.cs
    ================================================
    using System;
    
    namespace trap_cs
    {
      class Program
      {
        const int maxGuesses = 6;
        const int maxNumber = 100;
        static void Main(string[] args)
        {
          int lowGuess  = 0;
          int highGuess = 0;
    
          Random randomNumberGenerator = new ();
    
          Print("TRAP");
          Print("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
          Print();
          Print();
          Print();
    
          PrintInstructions();
    
          int numberToGuess = randomNumberGenerator.Next(1, maxNumber);
    
          for (int nGuess = 1; nGuess <= maxGuesses + 1; nGuess++)
          {
            if (nGuess > maxGuesses)
            {
              Print(string.Format("SORRY, THAT'S {0} GUESSES. THE NUMBER WAS {1}", maxGuesses, numberToGuess));
              Print();
              break;
            }
    
            GetGuesses(nGuess, ref lowGuess, ref highGuess);
    
            if(lowGuess == highGuess && lowGuess == numberToGuess)
            {
              Print("YOU GOT IT!!!");
              Print();
              Print("TRY AGAIN.");
              Print();
              break;
            }
            if (highGuess < numberToGuess)
            {
              Print("MY NUMBER IS LARGER THAN YOUR TRAP NUMBERS.");
            }
            else if (lowGuess > numberToGuess)
            {
              Print("MY NUMBER IS SMALLER THAN YOUR TRAP NUMBERS.");
            }
            else
            {
              Print("YOU HAVE TRAPPED MY NUMBER.");
            }
          }
        }
    
    // TRAP
    // REM - STEVE ULLMAN, 8 - 1 - 72
        static void PrintInstructions()
        {
          Print("INSTRUCTIONS ?");
    
          char response = Console.ReadKey().KeyChar;
          if (response == 'Y')
          {
            Print(string.Format("I AM THINKING OF A NUMBER BETWEEN 1 AND {0}", maxNumber));
            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(string.Format("YOU GET {0} GUESSES TO GET MY NUMBER.", maxGuesses));
          }
        }
        static void Print(string stringToPrint)
        {
          Console.WriteLine(stringToPrint);
        }
        static void Print()
        {
          Console.WriteLine();
        }
        static void GetGuesses(int nGuess, ref int lowGuess, ref int highGuess)
        {
          Print();
          Print(string.Format("GUESS #{0}", nGuess));
    
          lowGuess  = GetIntFromConsole("Type low guess");
          highGuess = GetIntFromConsole("Type high guess");
    
          if(lowGuess > highGuess)
          {
            int tempGuess = lowGuess;
    
            lowGuess = highGuess;
            highGuess = tempGuess;
          }
        }
        static int GetIntFromConsole(string prompt)
        {
    
          Console.Write( prompt + " > ");
          string intAsString = Console.ReadLine();
    
          if(int.TryParse(intAsString, out int intValue) ==false)
          {
            intValue = 1;
          }
    
          return intValue;
        }
      }
    }
    
    
    ================================================
    FILE: 92_Trap/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 92_Trap/csharp/Trap.csproj
    ================================================
    
      
        Exe
        net6.0
        10
        enable
        enable
      
    
    
    
    ================================================
    FILE: 92_Trap/csharp/Trap.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Trap", "Trap.csproj", "{4386B4A0-16A9-48E7-8FBA-E35D4C9FEDAA}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{4386B4A0-16A9-48E7-8FBA-E35D4C9FEDAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{4386B4A0-16A9-48E7-8FBA-E35D4C9FEDAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{4386B4A0-16A9-48E7-8FBA-E35D4C9FEDAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{4386B4A0-16A9-48E7-8FBA-E35D4C9FEDAA}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 92_Trap/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 92_Trap/java/src/Trap.java
    ================================================
    import java.util.Arrays;
    import java.util.Scanner;
    
    /**
     * Game of Trap
     * 

    * Based on the Basic game of Trap here * https://github.com/coding-horror/basic-computer-games/blob/main/92%20Trap/trap.bas *

    * Note: The idea was to create a version of the 1970's Basic game in Java, without introducing * new features - no additional text, error checking, etc has been added. */ public class Trap { public static final int HIGH_NUMBER_RANGE = 100; public static final int MAX_GUESSES = 6; private enum GAME_STATE { STARTING, START_GAME, GUESSING, PLAY_AGAIN, GAME_OVER } // Used for keyboard input private final Scanner kbScanner; // Current game state private GAME_STATE gameState; // Players guess count; private int currentPlayersGuess; // Computers random number private int computersNumber; public Trap() { gameState = GAME_STATE.STARTING; // Initialise kb scanner kbScanner = new Scanner(System.in); } /** * Main game loop */ public void play() { do { switch (gameState) { // Show an introduction and optional instructions the first time the game is played. case STARTING: intro(); if (yesEntered(displayTextAndGetInput("INSTRUCTIONS? "))) { instructions(); } gameState = GAME_STATE.START_GAME; break; // Start new game case START_GAME: computersNumber = randomNumber(); currentPlayersGuess = 1; gameState = GAME_STATE.GUESSING; break; // Player guesses the number until they get it or run out of guesses case GUESSING: System.out.println(); String playerRangeGuess = displayTextAndGetInput("GUESS # " + currentPlayersGuess + "? "); int startRange = getDelimitedValue(playerRangeGuess, 0); int endRange = getDelimitedValue(playerRangeGuess, 1); // Has the player won? if (startRange == computersNumber && endRange == computersNumber) { System.out.println("YOU GOT IT!!!"); System.out.println(); gameState = GAME_STATE.PLAY_AGAIN; } else { // show where the guess is at System.out.println(showGuessResult(startRange, endRange)); currentPlayersGuess++; if (currentPlayersGuess > MAX_GUESSES) { System.out.println("SORRY, THAT'S " + MAX_GUESSES + " GUESSES. THE NUMBER WAS " + computersNumber); gameState = GAME_STATE.PLAY_AGAIN; } } break; // Play again, or exit game? case PLAY_AGAIN: System.out.println("TRY AGAIN"); gameState = GAME_STATE.START_GAME; } } while (gameState != GAME_STATE.GAME_OVER); } /** * Show the players guess result * * @param start start range entered by player * @param end end range * @return text to indicate their progress. */ private String showGuessResult(int start, int end) { String status; if (start <= computersNumber && computersNumber <= end) { status = "YOU HAVE TRAPPED MY NUMBER."; } else if (computersNumber < start) { status = "MY NUMBER IS SMALLER THAN YOUR TRAP NUMBERS."; } else { status = "MY NUMBER IS LARGER THAN YOUR TRAP NUMBERS."; } return status; } private void instructions() { System.out.println("I AM THINKING OF A NUMBER BETWEEN 1 AND " + HIGH_NUMBER_RANGE); System.out.println("TRY TO GUESS MY NUMBER. ON EACH GUESS,"); System.out.println("YOU ARE TO ENTER 2 NUMBERS, TRYING TO TRAP"); System.out.println("MY NUMBER BETWEEN THE TWO NUMBERS. I WILL"); System.out.println("TELL YOU IF YOU HAVE TRAPPED MY NUMBER, IF MY"); System.out.println("NUMBER IS LARGER THAN YOUR TWO NUMBERS, OR IF"); System.out.println("MY NUMBER IS SMALLER THAN YOUR TWO NUMBERS."); System.out.println("IF YOU WANT TO GUESS ONE SINGLE NUMBER, TYPE"); System.out.println("YOUR GUESS FOR BOTH YOUR TRAP NUMBERS."); System.out.println("YOU GET " + MAX_GUESSES + " GUESSES TO GET MY NUMBER."); } private void intro() { System.out.println("TRAP"); System.out.println("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println(); System.out.println(); } /** * Accepts a string delimited by comma's and returns the nth delimited * value (starting at count 0). * * @param text - text with values separated by comma's * @param pos - which position to return a value for * @return the int representation of the value */ private int getDelimitedValue(String text, int pos) { String[] tokens = text.split(","); return Integer.parseInt(tokens[pos]); } /** * Checks whether player entered Y or YES to a question. * * @param text player string from kb * @return true of Y or YES was entered, otherwise false */ private boolean yesEntered(String text) { return stringIsAnyValue(text, "Y", "YES"); } /** * Check whether a string equals one of a variable number of values * Useful to check for Y or YES for example * Comparison is case insensitive. * * @param text source string * @param values a range of values to compare against the source string * @return true if a comparison was found in one of the variable number of strings passed */ private boolean stringIsAnyValue(String text, String... values) { return Arrays.stream(values).anyMatch(str -> str.equalsIgnoreCase(text)); } /* * Print a message on the screen, then accept input from Keyboard. * * @param text message to be displayed on screen. * @return what was typed by the player. */ private String displayTextAndGetInput(String text) { System.out.print(text); return kbScanner.next(); } /** * Generate random number * Used as a single digit of the computer player * * @return random number */ private int randomNumber() { return (int) (Math.random() * (HIGH_NUMBER_RANGE) + 1); } } ================================================ FILE: 92_Trap/java/src/TrapGame.java ================================================ public class TrapGame { public static void main(String[] args) { Trap trap = new Trap(); trap.play(); } } ================================================ FILE: 92_Trap/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 92_Trap/javascript/trap.html ================================================ TRAP

    
    
    
    
    
    
    ================================================
    FILE: 92_Trap/javascript/trap.js
    ================================================
    // TRAP
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    // Main control section
    async function main()
    {
        print(tab(34) + "TRAP\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        g = 6;
        n = 100;
        // Trap
        // Steve Ullman, Aug/01/1972
        print("INSTRUCTIONS");
        str = await input();
        if (str.substr(0, 1) == "Y") {
            print("I AM THINKING OF A NUMBER BETWEEN 1 AND " + n + "\n");
            print("TRY TO GUESS MY NUMBER. ON EACH GUESS,\n");
            print("YOU ARE TO ENTER 2 NUMBERS, TRYING TO TRAP\n");
            print("MY NUMBER BETWEEN THE TWO NUMBERS. I WILL\n");
            print("TELL YOU IF YOU HAVE TRAPPED MY NUMBER, IF MY\n");
            print("NUMBER IS LARGER THAN YOUR TWO NUMBERS, OR IF\n");
            print("MY NUMBER IS SMALLER THAN YOUR TWO NUMBERS.\n");
            print("IF YOU WANT TO GUESS ONE SINGLE NUMBER, TYPE\n");
            print("YOUR GUESS FOR BOTH YOUR TRAP NUMBERS.\n");
            print("YOU GET " + g + " GUESSES TO GET MY NUMBER.\n");
        }
        while (1) {
            x = Math.floor(n * Math.random()) + 1;
            for (q = 1; q <= g; q++) {
                print("\n");
                print("GUESS #" + q + " ");
                str = await input();
                a = parseInt(str);
                b = parseInt(str.substr(str.indexOf(",") + 1));
                if (a == b && x == a) {
                    print("YOU GOT IT!!!\n");
                    break;
                }
                if (a > b) {
                    r = a;
                    a = b;
                    b = r;
                }
                if (a <= x && x <= b) {
                    print("YOU HAVE TRAPPED MY NUMBER.\n");
                } else if (x >= a) {
                    print("MY NUMBER IS LARGER THAN YOUR TRAP NUMBERS.\n");
                } else {
                    print("MY NUMBER IS SMALLER THAN YOUR TRAP NUMBERS.\n");
                }
            }
            print("\n");
            print("TRY AGAIN.\n");
            print("\n");
        }
    }
    
    main();
    
    
    ================================================
    FILE: 92_Trap/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 92_Trap/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 92_Trap/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 92_Trap/perl/trap.pl
    ================================================
    #!/usr/bin/perl
    use strict;
    
    print ' 'x 34 . "TRAP\n";
    print ' 'x 15 . "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n";
    print "\n"; print "\n"; print "\n";
    my $G=6;
    my $N=100;
    # REM-TRAP;
    # REM-STEVE ULLMAN, 8-1-72;
    
    print "INSTRUCTIONS";
    print "? "; chomp(my $Z = uc());
    if (substr($Z,0,1) eq "Y") {
    	print "I AM THINKING OF A NUMBER BETWEEN 1 AND $N\n";
    	print "TRY TO GUESS MY NUMBER. ON EACH GUESS,\n";
    	print "YOU ARE TO ENTER 2 NUMBERS, TRYING TO TRAP\n";
    	print "MY NUMBER BETWEEN THE TWO NUMBERS. I WILL\n";
    	print "TELL YOU IF YOU HAVE TRAPPED MY NUMBER, IF MY\n";
    	print "NUMBER IS LARGER THAN YOUR TWO NUMBERS, OR IF\n";
    	print "MY NUMBER IS SMALLER THAN YOUR TWO NUMBERS.\n";
    	print "IF YOU WANT TO GUESS ONE SINGLE NUMBER, TYPE\n";
    	print "YOUR GUESS FOR BOTH YOUR TRAP NUMBERS.\n";
    	print "YOU GET $G GUESSES TO GET MY NUMBER.\n";
    	}
    
    while (1) {
    	my $Flag= 0;
    	my $X=int($N*rand(1))+1;
    	for (my $Q=1; $Q<=$G; $Q++) {
    		print "\n";
    		print "GUESS #$Q ";
    		print "? "; chomp(my $Pair= uc());
    		my ($A, $B)= split(",", $Pair);
    		if ($A eq $B && $X eq $A) { $Flag=1; last; }
    
    		if ($A>$B) { ($A,$B)= ($B,$A); }
    		if ($X>$B) {
    			print "MY NUMBER IS LARGER THAN YOUR TRAP NUMBERS.\n";
    			next;
    			}
    		if ($X<$A) {
    			print "MY NUMBER IS SMALLER THAN YOUR TRAP NUMBERS.\n";
    			next;
    			}
    		print "YOU HAVE TRAPPED MY NUMBER.\n";
    		}
    
    	if ($Flag==0) {
    		print "SORRY, THAT'S $G GUESSES. THE NUMBER WAS $X\n";
    		} else {
    		print "YOU GOT IT!!!\n";
    		}
    
    	print "\n";
    	print "TRY AGAIN.\n";
    	print "\n";
    	}
    
    exit;
    
    
    ================================================
    FILE: 92_Trap/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 92_Trap/python/trap.py
    ================================================
    #!/usr/bin/env python3
    # TRAP
    #
    # STEVE ULLMAN, 8-1-72
    # Converted from BASIC to Python by Trevor Hobson
    
    import random
    
    number_max = 100
    guess_max = 6
    
    
    def play_game() -> None:
        """Play one round of the game"""
    
        number_computer = random.randint(1, number_max)
        turn = 0
        while True:
            turn += 1
            user_guess = [-1, -1]
            while user_guess == [-1, -1]:
                try:
                    user_input = [
                        int(item)
                        for item in input("\nGuess # " + str(turn) + " ? ").split(",")
                    ]
                    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):
                    print("Please enter a valid guess.")
            if user_guess[0] > user_guess[1]:
                user_guess[0], user_guess[1] = user_guess[1], user_guess[0]
            if user_guess[0] == user_guess[1] == number_computer:
                print("You got it!!!")
                break
            elif user_guess[0] <= number_computer <= user_guess[1]:
                print("You have trapped my number.")
            elif number_computer < user_guess[0]:
                print("My number is smaller than your trap numbers.")
            else:
                print("My number is larger than your trap numbers.")
            if turn == guess_max:
                print("That's", turn, "guesses. The number was", number_computer)
                break
    
    
    def main() -> None:
        print(" " * 34 + "TRAP")
        print(" " * 15 + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n")
        if input("Instructions ").lower().startswith("y"):
            print("\nI am thinking of a number between 1 and", number_max)
            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", guess_max, "guesses to get my number.")
    
        keep_playing = True
        while keep_playing:
            play_game()
            keep_playing = input("\nTry again. ").lower().startswith("y")
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 92_Trap/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 92_Trap/ruby/trap.rb
    ================================================
    #!/usr/bin/env ruby
    
    # Trap
    # Steve Ullman, 1972-08-01
    # Ruby version Glenn Vanderburg, 2022-03-04
    
    # Change these values to make the game easier or harder.
    GUESSES_PER_GAME = 6
    NUMBER_UPPER_BOUND = 100
    
    # Put everything in methods for order of presentation; we
    # want to be able to refer to methods before declaring them,
    # so the code reads nicely from top to bottom.
    def main
      print_banner_and_instructions
    
      loop do
        play_a_game
        break unless yes?("Try again?")
      end
    end
    
    def yes?(prompt)
      print "\n#{prompt} "
      answer = gets
      return answer.downcase.start_with?("y")
    end
    
    def print_banner_and_instructions
      banner = "Creative Computing -- Morristown, New Jersey"
    
      puts "Trap!".center(banner.size)
      puts banner
      2.times { puts }
    
      return unless yes?("Instructions?")
    
      puts <<~"END"
        I am thinking of a number between 1 and #{NUMBER_UPPER_BOUND}.
        Try to guess my number. On each guess,
        you are to enter 2 numbers, trying to trap
        my number between the two numbers. I will
        tell you if you have trapped my number, if my
        number is larger than your two numbers, or if
        my number is smaller than your two numbers.
        If you want to guess one single number, type
        your guess for both your trap numbers.
        You get #{GUESSES_PER_GAME} guesses to get my number.
      END
    end
    
    def play_a_game
      n = choose_number
    
      GUESSES_PER_GAME.times do |i|
        lower, upper = get_guesses("Guess ##{i+1}?")
    
        case
        when n < lower
          puts "My number is smaller than your trap numbers."
        when n > upper
          puts "My number is larger than your trap numbers."
        when lower != upper
          puts "You have trapped my number."
        else
          puts "You got it!!!"
          return
        end
      end
    
      puts "Sorry, that's #{GUESSES_PER_GAME} guesses. Number was #{n}"
    end
    
    def choose_number
      rand(NUMBER_UPPER_BOUND) + 1
    end
    
    def get_guesses(prompt)
      loop do
        print "\n#{prompt} "
    
        # This is forgiving of input format; it ignores spaces and
        # punctuation, returning only the strings of consecutive
        # digits in the input line.
        guesses = gets.scan(/\d+/)
    
        if guesses.size != 2
          puts "Please enter two numbers for each guess."
        else
          # convert the strings of digits to integers:
          numbers = guesses.map(&:to_i)
          # and return them, lowest number first:
          return numbers.sort
        end
      end
    end
    
    main
    
    
    ================================================
    FILE: 92_Trap/rust/Cargo.toml
    ================================================
    [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"
    
    
    ================================================
    FILE: 92_Trap/rust/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Rust](https://www.rust-lang.org/) by Uğur Küpeli [ugurkupeli](https://github.com/ugurkupeli)
    
    ================================================
    FILE: 92_Trap/rust/src/main.rs
    ================================================
    use std::io::stdin;
    
    use rand::Rng;
    
    fn main() {
        println!("\n\t\tTRAP");
        println!("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n");
    
        let max_guess = 6;
        let max_number = 100;
    
        prompt_instructions();
    
        loop {
            let number = rand::thread_rng().gen_range(1..(max_number + 1));
            let mut guesses = 1u8;
    
            loop {
                let (min, max) = prompt_numbers(guesses);
    
                if min == number && max == number {
                    println!("\nYou got it!!!");
                    break;
                } else if (min..=max).contains(&number) {
                    println!("You have trapped my number.");
                } else if number < min {
                    println!("My number is smaller than your trap numbers.");
                } else if number > max {
                    println!("My number is bigger than your trap numbers.");
                }
    
                guesses += 1;
                if guesses > max_guess {
                    println!("\nSorry, that was {max_guess} guesses. Number was {number}");
                    break;
                }
            }
    
            println!("\nTry again.");
        }
    }
    
    fn prompt_instructions() {
        println!("Instructions?\t");
    
        let mut input = String::new();
        if let Ok(_) = stdin().read_line(&mut input) {
            match input.to_uppercase().trim() {
                "YES" | "Y" => {
                    println!("\nI am thinking of a number between 1 and 100");
                    println!("Try to guess my number. On each guess,");
                    println!("you are to enter 2 numbers, trying to trap");
                    println!("my number between the two numbers. I will");
                    println!("tell you if you have trapped my number, if my");
                    println!("number is larger than your two numbers, or if");
                    println!("my number is smaller than your two numbers.");
                    println!("If you want to guess one single number, type");
                    println!("your guess for both your trap numbers.");
                    println!("You get 6 guesses to get my number.");
                }
                _ => (),
            }
        }
    }
    
    fn prompt_numbers(guess: u8) -> (u8, u8) {
        loop {
            let mut nums: Vec = Vec::new();
            println!("\nGuess # {guess} ?");
    
            let mut input = String::new();
            if let Ok(_) = stdin().read_line(&mut input) {
                let input: Vec<&str> = input.trim().split(",").collect();
    
                for string in input {
                    if let Ok(number) = string.parse::() {
                        nums.push(number);
                    } else {
                        break;
                    }
                }
    
                if nums.len() == 2 {
                    if nums[0] <= nums[1] {
                        return (nums[0], nums[1]);
                    }
                }
            }
        }
    }
    
    
    ================================================
    FILE: 92_Trap/trap.bas
    ================================================
    1 PRINT TAB(34);"TRAP"
    2 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    3 PRINT:PRINT:PRINT
    10 G=6
    20 N=100
    30 REM-TRAP
    40 REM-STEVE ULLMAN, 8-1-72
    50 PRINT "INSTRUCTIONS";
    60 INPUT Z$
    70 IF LEFT$(Z$,1)<>"Y" THEN 180
    80 PRINT "I AM THINKING OF A NUMBER BETWEEN 1 AND";N
    90 PRINT "TRY TO GUESS MY NUMBER. ON EACH GUESS,"
    100 PRINT "YOU ARE TO ENTER 2 NUMBERS, TRYING TO TRAP"
    110 PRINT "MY NUMBER BETWEEN THE TWO NUMBERS. I WILL"
    120 PRINT "TELL YOU IF YOU HAVE TRAPPED MY NUMBER, IF MY"
    130 PRINT "NUMBER IS LARGER THAN YOUR TWO NUMBERS, OR IF"
    140 PRINT "MY NUMBER IS SMALLER THAN YOUR TWO NUMBERS."
    150 PRINT "IF YOU WANT TO GUESS ONE SINGLE NUMBER, TYPE"
    160 PRINT "YOUR GUESS FOR BOTH YOUR TRAP NUMBERS."
    170 PRINT "YOU GET";G;"GUESSES TO GET MY NUMBER."
    180 X=INT(N*RND(1))+1
    190 FOR Q=1 TO G
    200 PRINT
    210 PRINT "GUESS #";Q;
    220 INPUT A,B
    230 IF A=B AND X=A THEN 400
    240 IF A <= B THEN 260
    250 GOSUB 360
    260 IF A <= X AND X <= B THEN 320
    270 IF X
      
        Exe
        Trap
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 93_23_Matches/23matches.bas
    ================================================
    20 PRINT TAB(31);"23 MATCHES"
    30 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    40 PRINT:PRINT:PRINT
    80 PRINT " THIS IS A GAME CALLED '23 MATCHES'."
    90 PRINT
    100 PRINT "WHEN IT IS YOUR TURN, YOU MAY TAKE ONE, TWO, OR THREE"
    110 PRINT "MATCHES. THE OBJECT OF THE GAME IS NOT TO HAVE TO TAKE"
    120 PRINT "THE LAST MATCH."
    130 PRINT
    140 PRINT "LET'S FLIP A COIN TO SEE WHO GOES FIRST."
    150 PRINT "IF IT COMES UP HEADS, I WILL WIN THE TOSS."
    155 PRINT
    160 REM
    165 N = 23
    170 Q = INT(2*RND(5))
    180 IF Q = 1 THEN 210
    190 PRINT "TAILS! YOU GO FIRST. "
    195 PRINT
    200 GOTO 300
    210 PRINT "HEADS! I WIN! HA! HA!"
    220 PRINT "PREPARE TO LOSE, MEATBALL-NOSE!!"
    230 PRINT
    250 PRINT "I TAKE 2 MATCHES"
    260 N = N -2
    270 PRINT "THE NUMBER OF MATCHES IS NOW" N
    280 PRINT
    290 PRINT "YOUR TURN -- YOU MAY TAKE 1, 2 OR 3 MATCHES."
    300 PRINT "HOW MANY DO YOU WISH TO REMOVE",
    310 INPUT K
    320 IF K > 3 THEN 430
    330 IF K <= 0 THEN 430
    340 N = N - K
    350 PRINT "THERE ARE NOW";N;"MATCHES REMAINING."
    351 IF N = 4 THEN 381
    352 IF N = 3 THEN 383
    353 IF N = 2 THEN 385
    360 IF N <= 1 THEN  530
    370 Z = 4 - K
    372 GOTO 390
    380 PRINT
    381 Z = 3
    382 GOTO 390
    383 Z = 2
    384 GOTO 390
    385 Z = 1
    390 PRINT "MY TURN ! I REMOVE" Z "MATCHES"
    400 N = N - Z
    410 IF N <= 1 THEN 470
    420 GOTO 270
    430 PRINT "VERY FUNNY! DUMMY!"
    440 PRINT "DO YOU WANT TO PLAY OR GOOF AROUND?"
    450 PRINT "NOW, HOW MANY MATCHES DO YOU WANT",
    460 GOTO 310
    470 PRINT
    480 PRINT"YOU POOR BOOB! YOU TOOK THE LAST MATCH! I GOTCHA!!"
    490 PRINT "HA ! HA ! I BEAT YOU !!!"
    500 PRINT
    510 PRINT "GOOD BYE LOSER!"
    520 GOTO 560
    530 PRINT "YOU WON, FLOPPY EARS !"
    540 PRINT "THINK YOU'RE PRETTY SMART !"
    550 PRINT "LETS PLAY AGAIN AND I'LL BLOW YOUR SHOES OFF !!"
    560 STOP
    570 END
    
    
    ================================================
    FILE: 93_23_Matches/README.md
    ================================================
    ### 23 Matches
    
    In the game of twenty-three matches, you start with 23 matches lying on a table. On each turn, you may take 1, 2, or 3 matches. You alternate moves with the computer and the one who has to take the last match loses.
    
    The easiest way to devise a winning strategy is to start at the end of the game. Since your wish to leave the last match to your opponent, you would like to have either 4, 3, or 2 on your last turn you so can take away 3, 2, or 1 and leave 1. Consequently, you would like to leave your opponent with 5 on his next to last turn so, no matter what his move, you are left with 4, 3, or 2. Work this backwards to the beginning and you’ll find the game can effectively be won on the first move. Fortunately, the computer gives you the first move, so if you play wisely, you can win.
    
    After you’ve mastered 23 Matches, move on to BATNUM and then to NUM.
    
    This version of 23 Matches was originally written by Bob Albrecht of People’s Computer Company.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=177)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=192)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    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.
    
    
    ================================================
    FILE: 93_23_Matches/csharp/23Matches.cs
    ================================================
    using System;
    
    namespace Program
    {
      class Program
      {
    
        // Initialize 3 public variables so that they can be ascessed anywhere in the code
        public static int numberOfMatches;
        public static int numberOfMatchesRemovedByPlayer;
        public static bool playerGoesFirst = false; // a flag to show if the player won the coin toss
        static void Main(string[] args)
        {
          // Print introduction text
    
          // Prints the title with 31 spaces placed in front of the text using the PadLeft() string function
          Console.WriteLine("23 MATCHES".PadLeft(31));
          Console.WriteLine("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY".PadLeft(15));
          
          // Print 3 blank lines with \n escape sequence
          Console.Write("\n\n\n");
          Console.WriteLine(" THIS IS A GAME CALLED '23 MATCHES'.");
          Console.Write("\n");
    
          Console.WriteLine("WHEN IT IS YOUR TURN, YOU MAY TAKE ONE, TWO, OR THREE");
          Console.WriteLine("MATCHES. THE OBJECT OF THE GAME IS NOT TO HAVE TO TAKE");
          Console.WriteLine("THE LAST MATCH.");
          Console.Write("\n");
          Console.WriteLine("LET'S FLIP A COIN TO SEE WHO GOES FIRST.");
          Console.WriteLine("IF IT COMES UP HEADS, I WILL WIN THE TOSS.");
          Console.Write("\n");
    
          // Set the number of matches to 23
          numberOfMatches = 23;
    
          // Create a random class object to generate the coin toss
          Random random = new Random();
          // Generates a random number between 0.0 and 1.0
          // Multiplies that number by 2 and then
          // Converts it into an integer giving either a 0 or a 1
          int coinTossResult = (int)(2 * random.NextDouble()); 
    
          if (coinTossResult == 1)
          {
            Console.WriteLine("TAILS! YOU GO FIRST. ");
            // Sets the player coin toss flag to true
            playerGoesFirst = true;
            PlayerTurn();
          }
          else
          {
            Console.WriteLine("HEADS! I WIN! HA! HA!");
            Console.WriteLine("PREPARE TO LOSE, MEATBALL-NOSE!!");
            Console.Write("\n");
            Console.WriteLine("I TAKE 2 MATCHES");
            numberOfMatches = numberOfMatches - 2;
          }
    
          // loops the code until there is 1 or fewer matches
          do
          {
            // Checks if the player has already gone 
            // because they won the coin toss
            // if they have not then the player can go
            if (playerGoesFirst == false)
            {
              Console.Write("THE NUMBER OF MATCHES IS NOW " + numberOfMatches);
              PlayerTurn();
            }
            // sets the coint toss flag to false since
            // this is only needed on the first loop of the code
            playerGoesFirst = false;
            ComputerTurn();        
          } while (numberOfMatches > 1);
    
        }
    
        static void PlayerTurn()
        {
          Console.WriteLine("\n");
          Console.WriteLine("YOUR TURN -- YOU MAY TAKE 1, 2, OR 3 MATCHES.");
          Console.Write("HOW MANY DO YOU WISH TO REMOVE ?? ");
          // Get player input
          numberOfMatchesRemovedByPlayer = ReadPlayerInput();
          // If the input is invalid (not 1, 2, or 3)
          // then ask the player to input again
          while (numberOfMatchesRemovedByPlayer > 3 || numberOfMatchesRemovedByPlayer <= 0)
          {
            Console.WriteLine("VERY FUNNY! DUMMY!");
            Console.WriteLine("DO YOU WANT TO PLAY OR GOOF AROUND?");
            Console.Write("NOW, HOW MANY MATCHES DO YOU WANT                 ?? ");
            numberOfMatchesRemovedByPlayer = ReadPlayerInput();
          }
    
          // Remove the player specified number of matches
          numberOfMatches = numberOfMatches - numberOfMatchesRemovedByPlayer;
    
          Console.WriteLine("THE ARE NOW " + numberOfMatches + " MATCHES REMAINING");      
        }
        static void ComputerTurn()
        {
          // Initialize the numberOfMatchesRemovedByComputer
          int numberOfMatchesRemovedByComputer = 0;
          switch (numberOfMatches)
          {
            case 4:
              numberOfMatchesRemovedByComputer = 3;
              break;
            case 3:
              numberOfMatchesRemovedByComputer = 2;
              break;
            case 2:
              numberOfMatchesRemovedByComputer = 1;
              break;
            case 1: case 0: // If the computer losses call this case
              Console.WriteLine("YOU WON, FLOPPY EARS !");
              Console.WriteLine("THING YOU'RE PRETTY SMART !");
              Console.WriteLine("LETS PLAY AGAIN AND I'LL BLOW YOUR SHOES OFF !!");
              break;
            default: // If there are > than 4 matches call this case
              numberOfMatchesRemovedByComputer = 4 - numberOfMatchesRemovedByPlayer;
              break;
          }
          // If the numberOfMatchesRemovedByComputer has been updated run this code,
          // if not them the computer has lost
          if (numberOfMatchesRemovedByComputer != 0)
          {
            Console.WriteLine("MY TURN ! I REMOVE " + numberOfMatchesRemovedByComputer + " MATCHES");
            numberOfMatches = numberOfMatches - numberOfMatchesRemovedByComputer;
            // If there are less than or equal to 1 matches
            // then the player has lost        
            if (numberOfMatches <= 1)
            {
              Console.Write("\n");
              Console.WriteLine("YOU POOR BOOB! YOU TOOK THE LAST MATCH! I GOTCHA!!");
              Console.WriteLine("HA ! HA ! I BEAT YOU !!!");
              Console.Write("\n");
              Console.WriteLine("GOOD BYE LOSER!");
            }
          }
        }
    
    
        // This method handles the player input 
        // and will handle inncorrect input
        static int ReadPlayerInput()
        {
          // Read user input and convert to integer
          int playerInput = 0;
          // Try to read player input
          try
          {
            playerInput = Convert.ToInt32(Console.ReadLine());
          }
          // If there is an error in the player input
          catch (System.Exception)
          {
            Console.WriteLine("?REENTER");
            Console.Write("?? ");
            // Ask the player to reenter their input
            playerInput = ReadPlayerInput();
          }
          return playerInput;      
        }
    
      }
    }
    
    ================================================
    FILE: 93_23_Matches/csharp/csharp.csproj
    ================================================
    
    
      
        Exe
        net6.0
        enable
        enable
      
    
    
    
    
    ================================================
    FILE: 93_23_Matches/java/CoinSide.java
    ================================================
    public enum CoinSide {
        HEADS,
        TAILS
    }
    
    
    ================================================
    FILE: 93_23_Matches/java/Messages.java
    ================================================
    public class Messages {
    
        // This is a utility class and contains only static members.
        // Utility classes are not meant to be instantiated.
        private Messages() {
            throw new IllegalStateException("Utility class");
        }
    
        public static final String INTRO = """
                                              23 MATCHES
                              CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY
    
    
    
                 THIS IS A GAME CALLED '23 MATCHES'.
    
                WHEN IT IS YOUR TURN, YOU MAY TAKE ONE, TWO, OR THREE
                MATCHES. THE OBJECT OF THE GAME IS NOT TO HAVE TO TAKE
                THE LAST MATCH.
    
                LET'S FLIP A COIN TO SEE WHO GOES FIRST.
                IF IT COMES UP HEADS, I WILL WIN THE TOSS.
                """;
    
        public static final String HEADS = """
                HEADS! I WIN! HA! HA!
                PREPARE TO LOSE, MEATBALL-NOSE!!
    
                I TAKE 2 MATCHES
                """;
    
        public static final String TAILS = """
                TAILS! YOU GO FIRST.
                """;
    
        public static final String MATCHES_LEFT = """
                THE NUMBER OF MATCHES IS NOW %d
    
                YOUR TURN -- YOU MAY TAKE 1, 2 OR 3 MATCHES.
                """;
    
        public static final String REMOVE_MATCHES_QUESTION = "HOW MANY DO YOU WISH TO REMOVE? ";
    
        public static final String REMAINING_MATCHES = """
                THERE ARE NOW %d MATCHES REMAINING.
                """;
    
        public static final String INVALID = """
                VERY FUNNY! DUMMY!
                DO YOU WANT TO PLAY OR GOOF AROUND?
                NOW, HOW MANY MATCHES DO YOU WANT?
                """;
    
        public static final String WIN = """
                YOU WON, FLOPPY EARS !
                THINK YOU'RE PRETTY SMART !
                LETS PLAY AGAIN AND I'LL BLOW YOUR SHOES OFF !!
                """;
    
        public static final String CPU_TURN = """
                MY TURN ! I REMOVE %d MATCHES.
                """;
    
        public static final String LOSE = """
                YOU POOR BOOB! YOU TOOK THE LAST MATCH! I GOTCHA!!
                HA ! HA ! I BEAT YOU !!!
    
                GOOD BYE LOSER!
                """;
    }
    
    
    ================================================
    FILE: 93_23_Matches/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 93_23_Matches/java/TwentyThreeMatches.java
    ================================================
    import java.util.Random;
    import java.util.Scanner;
    
    public class TwentyThreeMatches {
    
        private static final int MATCH_COUNT_START = 23;
        private static final Random RAND = new Random();
        private final Scanner scan = new Scanner(System.in);
    
        public void startGame() {
            //Initialize values
            int cpuRemoves = 0;
            int matchesLeft = MATCH_COUNT_START;
            int playerRemoves = 0;
    
            //Flip coin and decide who goes first.
            CoinSide coinSide = flipCoin();
            if (coinSide == CoinSide.HEADS) {
                System.out.println(Messages.HEADS);
                matchesLeft -= 2;
            } else {
                System.out.println(Messages.TAILS);
            }
    
            // Game loop
            while (true) {
                //Show matches left if CPU went first or Player already removed matches
                if (coinSide == CoinSide.HEADS) {
                    System.out.format(Messages.MATCHES_LEFT, matchesLeft);
                }
                coinSide = CoinSide.HEADS;
    
                // Player removes matches
                System.out.println(Messages.REMOVE_MATCHES_QUESTION);
                playerRemoves = turnOfPlayer();
                matchesLeft -= playerRemoves;
                System.out.format(Messages.REMAINING_MATCHES, matchesLeft);
    
                // If 1 match is left, the CPU has to take it. You win!
                if (matchesLeft <= 1) {
                    System.out.println(Messages.WIN);
                    return;
                }
    
                // CPU removes matches
                // At least two matches are left, because win condition above was not triggered.
                if (matchesLeft <= 4) {
                    cpuRemoves = matchesLeft - 1;
                } else {
                    cpuRemoves = 4 - playerRemoves;
                }
                System.out.format(Messages.CPU_TURN, cpuRemoves);
                matchesLeft -= cpuRemoves;
    
                // If 1 match is left, the Player has to take it. You lose!
                if (matchesLeft <= 1) {
                    System.out.println(Messages.LOSE);
                    return;
                }
            }
        }
    
        private CoinSide flipCoin() {
            return RAND.nextBoolean() ? CoinSide.HEADS : CoinSide.TAILS;
        }
    
        private int turnOfPlayer() {
            while (true) {
                int playerRemoves = scan.nextInt();
                // Handle invalid entries
                if ((playerRemoves > 3) || (playerRemoves <= 0)) {
                    System.out.println(Messages.INVALID);
                    continue;
                }
                return playerRemoves;
            }
        }
    
    }
    
    
    ================================================
    FILE: 93_23_Matches/java/TwentyThreeMatchesGame.java
    ================================================
    /**
     * Game of 23 Matches
     * 

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

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

    * Converted from BASIC to Java by Darren Cardenas. */ public class TwentyThreeMatchesGame { public static void main(String[] args) { showIntro(); TwentyThreeMatches game = new TwentyThreeMatches(); game.startGame(); } private static void showIntro() { System.out.println(Messages.INTRO); } } ================================================ FILE: 93_23_Matches/javascript/23matches.html ================================================ 23 MATCHES

    
    
    
    
    
    
    ================================================
    FILE: 93_23_Matches/javascript/23matches.js
    ================================================
    // 23 MATCHES
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str)
    {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function input()
    {
        var input_element;
        var input_str;
    
        return new Promise(function (resolve) {
                           input_element = document.createElement("INPUT");
    
                           print("? ");
                           input_element.setAttribute("type", "text");
                           input_element.setAttribute("length", "50");
                           document.getElementById("output").appendChild(input_element);
                           input_element.focus();
                           input_str = undefined;
                           input_element.addEventListener("keydown", function (event) {
                                                          if (event.keyCode == 13) {
                                                          input_str = input_element.value;
                                                          document.getElementById("output").removeChild(input_element);
                                                          print(input_str);
                                                          print("\n");
                                                          resolve(input_str);
                                                          }
                                                          });
                           });
    }
    
    function tab(space)
    {
        var str = "";
        while (space-- > 0)
            str += " ";
        return str;
    }
    
    // Main control section
    async function main()
    {
        print(tab(31) + "23 MATCHES\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        print(" THIS IS A GAME CALLED '23 MATCHES'.\n");
        print("\n");
        print("WHEN IT IS YOUR TURN, YOU MAY TAKE ONE, TWO, OR THREE\n");
        print("MATCHES. THE OBJECT OF THE GAME IS NOT TO HAVE TO TAKE\n");
        print("THE LAST MATCH.\n");
        print("\n");
        print("LET'S FLIP A COIN TO SEE WHO GOES FIRST.\n");
        print("IF IT COMES UP HEADS, I WILL WIN THE TOSS.\n");
        print("\n");
        n = 23;
        q = Math.floor(2 * Math.random());
        if (q != 1) {
            print("TAILS! YOU GO FIRST. \n");
            print("\n");
        } else {
            print("HEADS! I WIN! HA! HA!\n");
            print("PREPARE TO LOSE, MEATBALL-NOSE!!\n");
            print("\n");
            print("I TAKE 2 MATCHES\n");
            n -= 2;
        }
        while (1) {
            if (q == 1) {
                print("THE NUMBER OF MATCHES IS NOW " + n + "\n");
                print("\n");
                print("YOUR TURN -- YOU MAY TAKE 1, 2 OR 3 MATCHES.\n");
            }
            print("HOW MANY DO YOU WISH TO REMOVE ");
            while (1) {
                k = parseInt(await input());
                if (k <= 0 || k > 3) {
                    print("VERY FUNNY! DUMMY!\n");
                    print("DO YOU WANT TO PLAY OR GOOF AROUND?\n");
                    print("NOW, HOW MANY MATCHES DO YOU WANT ");
                } else {
                    break;
                }
            }
            n -= k;
            print("THERE ARE NOW " + n + " MATCHES REMAINING.\n");
            if (n == 4) {
                z = 3;
            } else if (n == 3) {
                z = 2;
            } else if (n == 2) {
                z = 1;
            } else if (n > 1) {
                z = 4 - k;
            } else {
                print("YOU WON, FLOPPY EARS !\n");
                print("THINK YOU'RE PRETTY SMART !\n");
                print("LETS PLAY AGAIN AND I'LL BLOW YOUR SHOES OFF !!\n");
                break;
            }
            print("MY TURN ! I REMOVE " + z + " MATCHES\n");
            n -= z;
            if (n <= 1) {
                print("\n");
                print("YOU POOR BOOB! YOU TOOK THE LAST MATCH! I GOTCHA!!\n");
                print("HA ! HA ! I BEAT YOU !!!\n");
                print("\n");
                print("GOOD BYE LOSER!\n");
                break;
            }
            q = 1;
        }
    
    }
    
    main();
    
    
    ================================================
    FILE: 93_23_Matches/javascript/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells)
    
    
    ================================================
    FILE: 93_23_Matches/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 93_23_Matches/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 93_23_Matches/perl/23matches.pl
    ================================================
    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    print ' ' x 31 . "23 MATCHES\n";
    print ' ' x 15 . "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n";
    print "\n\n\n";
    
    print " THIS IS A GAME CALLED '23 MATCHES'.\n\n";
    
    print "WHEN IT IS YOUR TURN, YOU MAY TAKE ONE, TWO, OR THREE\n";
    print "MATCHES. THE OBJECT OF THE GAME IS NOT TO HAVE TO TAKE\n";
    print "THE LAST MATCH.\n\n";
    
    print "LET'S FLIP A COIN TO SEE WHO GOES FIRST.\n";
    print "IF IT COMES UP HEADS, I WILL WIN THE TOSS.\n\n";
    
    my $N = 23;
    my $Q = int( 2 * rand(5) );
    
    if ( $Q == 1 ) {
        print "HEADS! I WIN! HA! HA!\n";
        print "PREPARE TO LOSE, MEATBALL-NOSE!!\n\n";
    
        print "I TAKE 2 MATCHES\n";
        $N -= 2;
    
        print "THE NUMBER OF MATCHES IS NOW $N\n\n";
    
        print "YOUR TURN -- YOU MAY TAKE 1, 2 OR 3 MATCHES.\n";
    }
    else {
        print "TAILS! YOU GO FIRST.\n\n";
    }
    
    print "HOW MANY DO YOU WISH TO REMOVE?\n";
    
    INPUT:
    {
        chomp( my $K =  );
    
        if ( $K > 3 or $K <= 0 ) {
            print "VERY FUNNY! DUMMY!\n";
            print "DO YOU WANT TO PLAY OR GOOF AROUND?\n";
            print "NOW, HOW MANY MATCHES DO YOU WANT?\n";
            redo INPUT;
        }
    
        $N -= $K;
    
        print "THERE ARE NOW $N MATCHES REMAINING.\n";
    
        my $Z;
    
        if ( $N <= 1 ) {
            print "YOU WON, FLOPPY EARS!\n";
            print "THINK YOU'RE PRETTY SMART!\n";
            print "LET'S PLAY AGAIN AND I'LL BLOW YOUR SHOES OFF!!\n";
            exit;
        }
        elsif ( $N > 4 ) {
            $Z = 4 - $K;
        }
        else {
            $Z = $N - 1;
        }
    
        print "MY TURN! I REMOVE $Z MATCHES\n";
    
        $N -= $Z;
    
        if ( $N <= 1 ) {
            print "\nYOU POOR BOOB! YOU TOOK THE LAST MATCH! I GOTCHA!!\n";
            print "HA! HA! I BEAT YOU!!!\n\n";
    
            print "GOOD BYE LOSER!\n";
        }
        else {
            print "THE NUMBER OF MATCHES IS NOW $N\n\n";
    
            print "YOUR TURN -- YOU MAY TAKE 1, 2 OR 3 MATCHES.\n";
            print "HOW MANY DO YOU WISH TO REMOVE?\n";
            redo INPUT;
        }
    }
    
    
    ================================================
    FILE: 93_23_Matches/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 93_23_Matches/python/23matches.py
    ================================================
    #!/usr/bin/env python3
    # 23 Matches
    #
    # Converted from BASIC to Python by Trevor Hobson
    
    import random
    
    
    def play_game() -> None:
        """Play one round of the game"""
    
        matches = 23
        humans_turn = random.randint(0, 1) == 1
        if humans_turn:
            print("Tails! You go first.\n")
            prompt_human = "How many do you wish to remove "
        else:
            print("Heads! I win! Ha! Ha!")
            print("Prepare to lose, meatball-nose!!")
    
        choice_human = 2
        while matches > 0:
            if humans_turn:
                choice_human = 0
                if matches == 1:
                    choice_human = 1
                while choice_human == 0:
                    try:
                        choice_human = int(input(prompt_human))
                        if choice_human not in [1, 2, 3] or choice_human > matches:
                            choice_human = 0
                            print("Very funny! Dummy!")
                            print("Do you want to play or goof around?")
                            prompt_human = "Now, how many matches do you want "
                    except ValueError:
                        print("Please enter a number.")
                        prompt_human = "How many do you wish to remove "
                matches -= choice_human
                if matches == 0:
                    print("You poor boob! You took the last match! I gotcha!!")
                    print("Ha ! Ha ! I beat you !!\n")
                    print("Good bye loser!")
                else:
                    print("There are now", matches, "matches remaining.\n")
            else:
                choice_computer = 4 - choice_human
                if matches == 1:
                    choice_computer = 1
                elif 1 < matches < 4:
                    choice_computer = matches - 1
                matches -= choice_computer
                if matches == 0:
                    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_computer, "matches")
                    print("The number of matches is now", matches, "\n")
            humans_turn = not humans_turn
            prompt_human = "Your turn -- you may take 1, 2 or 3 matches.\nHow many do you wish to remove "
    
    
    def main() -> None:
        print(" " * 31 + "23 MATCHHES")
        print(" " * 15 + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n")
        print("This is a game called '23 Matches'.\n")
        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.\n")
        print("Let's flip a coin to see who goes first.")
        print("If it comes up heads, I will win the toss.\n")
    
        keep_playing = True
        while keep_playing:
            play_game()
            keep_playing = input("\nPlay again? (yes or no) ").lower().startswith("y")
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 93_23_Matches/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 93_23_Matches/ruby/23_matches.rb
    ================================================
    class Matches
        def initialize
            puts " " * 31 + "23 MATCHES"
            puts "This is a game called '23 Matches'."
            puts "When it is your turn, you may take one, two, or three"
            puts "matches. The object of the game is not to have to take"
            puts "the last match."
            puts "Let's flip a coin to see who goes first."
            puts "If it comes up heads, I will win the toss."
    
            while true
                play_game
                print "Play again? (yes or no) "
                answer = gets.chomp!.upcase
                break unless ["Y", "YES"].include? answer
            end
        end
    
        private
            def play_game
                matches = 23
                humans_turn = rand(0..1) == 1
                if humans_turn
                    puts "Tails! You go first."
                    prompt_human = "How many do you wish to remove? "
                else
                    puts "Heads! I win! Ha! Ha!"
                    puts "Prepare to lose, meatball-nose!!"
                end
    
                choice_human = 2
    
                while matches > 0
                    if humans_turn
                        choice_human = 0
                        if matches == 1
                            choice_human = 1
                        end
    
                        while choice_human == 0
                            print "#{prompt_human}[1,2,3] "
                            choice_human = gets.chomp!
    
                            if ![1, 2, 3].include?(choice_human.to_i) || choice_human.to_i > matches
                                choice_human = 0
                                puts "Very funny! Dummy!"
                                puts "Do you want to play or goof around?"
                                prompt_human = "Now, how many matches do you want "
                            end
                        end
    
                        matches = matches - choice_human.to_i
    
                        if matches == 0
                            puts "You poor boob! You took the last match! I gotcha!!"
                            puts "Ha ! Ha ! I beat you !!"
                            puts "Good bye loser!"
                        else
                            puts "There are now #{matches} matches remaining."
                        end
                    else
                        choice_computer = 4 - choice_human.to_i
                        if matches == 1
                            choice_computer = 1
                        elsif (1 < matches) && (matches < 4)
                            choice_computer = matches - 1
                        end
    
                        matches = matches - choice_computer
                        if matches == 0
                            puts "You won, floppy ears !"
                            puts "Think you're pretty smart !"
                            puts "Let's play again and I'll blow your shoes off !!"
                        else
                            puts "My turn ! I remove #{choice_computer} matches"
                            puts "The number of matches is now #{matches}"
                        end
                    end
    
                    humans_turn = !humans_turn
                    prompt_human = "Your turn -- you may take 1, 2 or 3 matches.\nHow many do you wish to remove "
                end
            end
    end
    
    if __FILE__ == $0
        Matches.new
    end
    
    ================================================
    FILE: 93_23_Matches/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 93_23_Matches/rust/Cargo.toml
    ================================================
    [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"
    
    
    ================================================
    FILE: 93_23_Matches/rust/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [rust](https://www.rust-lang.org/)
    
    
    ================================================
    FILE: 93_23_Matches/rust/src/main.rs
    ================================================
    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
        }
    }
    
    
    ================================================
    FILE: 93_23_Matches/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 93_23_Matches/vbnet/TwentyThreeMatches.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "TwentyThreeMatches", "TwentyThreeMatches.vbproj", "{DEAA537A-6F73-4C2E-A85C-4B797B3D1900}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{DEAA537A-6F73-4C2E-A85C-4B797B3D1900}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{DEAA537A-6F73-4C2E-A85C-4B797B3D1900}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{DEAA537A-6F73-4C2E-A85C-4B797B3D1900}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{DEAA537A-6F73-4C2E-A85C-4B797B3D1900}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 93_23_Matches/vbnet/TwentyThreeMatches.vbproj
    ================================================
    
      
        Exe
        TwentyThreeMatches
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 94_War/README.md
    ================================================
    ### War
    
    This program plays the card game of War. In War, the card deck is shuffled, then two cards are dealt, one to each player. Players compare cards and the higher card (numerically) wins. In case of a tie, no one wins. The game ends when you have gone through the whole deck (52 cards, 26 games) or when you decide to quit.
    
    The computer gives cards by suit and number, for example, S-7 is the 7 of spades.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=178)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=193)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 94_War/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 94_War/csharp/War/Cards.cs
    ================================================
    using System;
    using System.Collections.Generic;
    
    
    
    namespace War
    {
        // These enums define the card's suit and rank.
        public enum Suit
        {
            clubs,
            diamonds,
            hearts,
            spades
        }
    
        public enum Rank
        {
            // Skip 1 because ace is high.
            two = 2,
            three,
            four,
            five,
            six,
            seven,
            eight,
            nine,
            ten,
            jack,
            queen,
            king,
            ace
        }
    
        // A class to represent a playing card.
        public class Card
        {
            // A card is an immutable object (i.e. it can't be changed) so its suit
            // and rank value are readonly; they can only be set in the constructor.
            private readonly Suit suit;
            private readonly Rank rank;
    
            // These dictionaries are used to convert a suit or rank value into a string.
            private readonly Dictionary suitNames = new Dictionary()
            {
                { Suit.clubs, "C"},
                { Suit.diamonds, "D"},
                { Suit.hearts, "H"},
                { Suit.spades, "S"},
            };
    
            private readonly Dictionary rankNames = new Dictionary()
            {
                { Rank.two, "2"},
                { Rank.three, "3"},
                { Rank.four, "4"},
                { Rank.five, "5"},
                { Rank.six, "6"},
                { Rank.seven, "7"},
                { Rank.eight, "8"},
                { Rank.nine, "9"},
                { Rank.ten, "10"},
                { Rank.jack, "J"},
                { Rank.queen, "Q"},
                { Rank.king, "K"},
                { Rank.ace, "A"},
            };
    
            public Card(Suit suit, Rank rank)
            {
                this.suit = suit;
                this.rank = rank;
            }
    
            // Relational Operator Overloading.
            //
            // You would normally expect the relational operators to consider both the suit and the
            // rank of a card, but in this program suit doesn't matter so we define the operators to just
            // compare rank.
    
            // When adding relational operators we would normally include == and != but they are not
            // relevant to this program so haven't been defined. Note that if they were defined we
            // should also override the Equals() and GetHashCode() methods. See, for example:
            // http://www.blackwasp.co.uk/CSharpRelationalOverload.aspx
    
            // If the == and != operators were defined they would look like this:
            //
            //public static bool operator ==(Card lhs, Card rhs)
            //{
            //    return lhs.rank == rhs.rank;
            //}
            //
            //public static bool operator !=(Card lhs, Card rhs)
            //{
            //    return !(lhs == rhs);
            //}
    
            public static bool operator <(Card lhs, Card rhs)
            {
                return lhs.rank < rhs.rank;
            }
    
            public static bool operator >(Card lhs, Card rhs)
            {
                return rhs < lhs;
            }
    
            public static bool operator <=(Card lhs, Card rhs)
            {
                return !(lhs > rhs);
            }
    
            public static bool operator >=(Card lhs, Card rhs)
            {
                return !(lhs < rhs);
            }
    
            public override string ToString()
            {
                // N.B. We are using string interpolation to create the card name.
                return $"{suitNames[suit]}-{rankNames[rank]}";
            }
        }
    
        // A class to represent a deck of cards.
        public class Deck
        {
            public const int deckSize = 52;
    
            private Card[] theDeck = new Card[deckSize];
    
            public Deck()
            {
                // Populate theDeck with all the cards in order.
                int i = 0;
                for (Suit suit = Suit.clubs; suit <= Suit.spades; suit++)
                {
                    for (Rank rank = Rank.two; rank <= Rank.ace; rank++)
                    {
                        theDeck[i] = new Card(suit, rank);
                        i++;
                    }
                }
            }
    
            // Return the card at a particular position in the deck.
            // N.B. As this is such a short method, we make it an
            // expression-body method.
            public Card GetCard(int i) => theDeck[i];
    
            // Shuffle the cards, this uses the modern version of the
            // Fisher-Yates shuffle, see:
            // https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm
            public void Shuffle()
            {
                var rand = new Random();
    
                // Iterate backwards through the deck.
                for (int i = deckSize - 1; i >= 1; i--)
                {
                    int j = rand.Next(0, i);
    
                    // Swap the cards at i and j
                    Card temp = theDeck[j];
                    theDeck[j] = theDeck[i];
                    theDeck[i] = temp;
                }
            }
        }
    }
    
    
    ================================================
    FILE: 94_War/csharp/War/Program.cs
    ================================================
    namespace War
    {
        class Program
        {
            static void Main(string[] args)
            {
                var ui = new UserInterface();
                ui.WriteIntro();
    
                var deck = new Deck();
                deck.Shuffle();
    
                int yourScore = 0;
                int computersScore = 0;
                bool usedAllCards = true;
    
                for (int i = 0; i < Deck.deckSize; i += 2)
                {
                    // Play the next hand.
                    var yourCard = deck.GetCard(i);
                    var computersCard = deck.GetCard(i + 1);
    
                    ui.WriteAResult(yourCard, computersCard, ref computersScore, ref yourScore);
    
                    if (!ui.AskAQuestion("DO YOU WANT TO CONTINUE? "))
                    {
                        usedAllCards = false;
                        break;
                    }
                }
    
                ui.WriteClosingRemarks(usedAllCards, yourScore, computersScore);
            }
        }
    }
    
    
    ================================================
    FILE: 94_War/csharp/War/UserInterface.cs
    ================================================
    using System;
    
    
    
    namespace War
    {
        // This class displays all the text that the user sees when playing the game.
        // It also handles asking the user a yes/no question and returning their answer.
        public class UserInterface
        {
            public void WriteIntro()
            {
                Console.WriteLine("                                 WAR");
                Console.WriteLine("               CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
                Console.WriteLine();
    
                Console.WriteLine("THIS IS THE CARD GAME OF WAR.  EACH CARD IS GIVEN BY SUIT-#");
                Console.Write("AS S-7 FOR SPADE 7.  ");
    
                if (AskAQuestion("DO YOU WANT DIRECTIONS? "))
                {
                    Console.WriteLine("THE COMPUTER GIVES YOU AND IT A 'CARD'.  THE HIGHER CARD");
                    Console.WriteLine("(NUMERICALLY) WINS.  THE GAME ENDS WHEN YOU CHOOSE NOT TO");
                    Console.WriteLine("CONTINUE OR WHEN YOU HAVE FINISHED THE PACK.");
                }
    
                Console.WriteLine();
                Console.WriteLine();
            }
    
            public void WriteAResult(Card yourCard, Card computersCard, ref int computersScore, ref int yourScore)
            {
                Console.WriteLine($"YOU: {yourCard}     COMPUTER: {computersCard}");
                if (yourCard < computersCard)
                {
                    computersScore++;
                    Console.WriteLine($"THE COMPUTER WINS!!! YOU HAVE {yourScore} AND THE COMPUTER HAS {computersScore}");
                }
                else if (yourCard > computersCard)
                {
                    yourScore++;
                    Console.WriteLine($"YOU WIN. YOU HAVE {yourScore} AND THE COMPUTER HAS {computersScore}");
                }
                else
                {
                    Console.WriteLine("TIE.  NO SCORE CHANGE");
                }
            }
    
            public bool AskAQuestion(string question)
            {
                // Repeat asking the question until the user answers "YES" or "NO".
                while (true)
                {
                    Console.Write(question);
                    string result = Console.ReadLine();
    
                    if (result.ToLower() == "yes")
                    {
                        Console.WriteLine();
                        return true;
                    }
                    else if (result.ToLower() == "no")
                    {
                        Console.WriteLine();
                        return false;
                    }
    
                    Console.WriteLine("YES OR NO, PLEASE.");
                }
            }
    
            public void WriteClosingRemarks(bool usedAllCards, int yourScore, int computersScore)
            {
                if (usedAllCards)
                {
                    Console.WriteLine("WE HAVE RUN OUT OF CARDS.");
                }
                Console.WriteLine($"FINAL SCORE:  YOU: {yourScore}  THE COMPUTER: {computersScore}");
                Console.WriteLine("THANKS FOR PLAYING.  IT WAS FUN.");
            }
        }
    }
    
    
    ================================================
    FILE: 94_War/csharp/War/War.csproj
    ================================================
    
    
      
        Exe
        netcoreapp3.1
      
    
    
    
    
    ================================================
    FILE: 94_War/csharp/War.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.31112.23
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "War", "War\War.csproj", "{C13BE0FA-D8F7-4CA7-A95D-DA03A9DE8950}"
    EndProject
    Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WarTester", "WarTester\WarTester.csproj", "{B539F618-EE83-486C-9A6D-404E998BED2D}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{C13BE0FA-D8F7-4CA7-A95D-DA03A9DE8950}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{C13BE0FA-D8F7-4CA7-A95D-DA03A9DE8950}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{C13BE0FA-D8F7-4CA7-A95D-DA03A9DE8950}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{C13BE0FA-D8F7-4CA7-A95D-DA03A9DE8950}.Release|Any CPU.Build.0 = Release|Any CPU
    		{B539F618-EE83-486C-9A6D-404E998BED2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{B539F618-EE83-486C-9A6D-404E998BED2D}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{B539F618-EE83-486C-9A6D-404E998BED2D}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{B539F618-EE83-486C-9A6D-404E998BED2D}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {3FA273C6-089E-4F3D-8DC0-F9143D1CDF3A}
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 94_War/csharp/WarTester/Tests.cs
    ================================================
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using System.Text;
    using War;
    
    
    
    namespace WarTester
    {
        [TestClass]
        public class CardTest
        {
            private Card c1 = new Card(Suit.clubs, Rank.two);
            private Card c2 = new Card(Suit.clubs, Rank.ten);
            private Card c3 = new Card(Suit.diamonds, Rank.ten);
            private Card c4 = new Card(Suit.diamonds, Rank.ten);
    
            // Test the relational operators.
    
            [TestMethod]
            public void LessThanIsValid()
            {
                Assert.IsTrue(c1 < c2, "c1 < c2");  // Same suit, different rank.
                Assert.IsFalse(c2 < c1, "c2 < c1"); // Same suit, different rank.
    
                Assert.IsFalse(c3 < c4, "c3 < c4"); // Same suit, same rank.
    
                Assert.IsTrue(c1 < c3, "c1 < c3");  // Different suit, different rank.
                Assert.IsFalse(c3 < c1, "c3 < c1"); // Different suit, different rank.
    
                Assert.IsFalse(c2 < c4, "c2 < c4"); // Different suit, same rank.
                Assert.IsFalse(c4 < c2, "c4 < c2"); // Different suit, same rank.
            }
    
            [TestMethod]
            public void GreaterThanIsValid()
            {
                Assert.IsFalse(c1 > c2, "c1 > c2"); // Same suit, different rank.
                Assert.IsTrue(c2 > c1, "c2 > c1");  // Same suit, different rank.
    
                Assert.IsFalse(c3 > c4, "c3 > c4"); // Same suit, same rank.
    
                Assert.IsFalse(c1 > c3, "c1 > c3"); // Different suit, different rank.
                Assert.IsTrue(c3 > c1, "c3 > c1");  // Different suit, different rank.
    
                Assert.IsFalse(c2 > c4, "c2 > c4"); // Different suit, same rank.
                Assert.IsFalse(c4 > c2, "c4 > c2"); // Different suit, same rank.
            }
    
            [TestMethod]
            public void LessThanEqualsIsValid()
            {
                Assert.IsTrue(c1 <= c2, "c1 <= c2");  // Same suit, different rank.
                Assert.IsFalse(c2 <= c1, "c2 <= c1"); // Same suit, different rank.
    
                Assert.IsTrue(c3 <= c4, "c3 <= c4");  // Same suit, same rank.
    
                Assert.IsTrue(c1 <= c3, "c1 <= c3");  // Different suit, different rank.
                Assert.IsFalse(c3 <= c1, "c3 <= c1"); // Different suit, different rank.
    
                Assert.IsTrue(c2 <= c4, "c2 <= c4");  // Different suit, same rank.
                Assert.IsTrue(c4 <= c2, "c4 <= c2");  // Different suit, same rank.
            }
    
            [TestMethod]
            public void GreaterThanEqualsIsValid()
            {
                Assert.IsFalse(c1 >= c2, "c1 >= c2"); // Same suit, different rank.
                Assert.IsTrue(c2 >= c1, "c2 >= c1");  // Same suit, different rank.
    
                Assert.IsTrue(c3 >= c4, "c3 >= c4");  // Same suit, same rank.
    
                Assert.IsFalse(c1 >= c3, "c1 >= c3"); // Different suit, different rank.
                Assert.IsTrue(c3 >= c1, "c3 >= c1");  // Different suit, different rank.
    
                Assert.IsTrue(c2 >= c4, "c2 >= c4");  // Different suit, same rank.
                Assert.IsTrue(c4 >= c2, "c4 >= c2");  // Different suit, same rank.
            }
    
            [TestMethod]
            public void ToStringIsValid()
            {
                var s1 = c1.ToString();
                var s2 = c3.ToString();
                var s3 = new Card(Suit.hearts, Rank.queen).ToString();
                var s4 = new Card(Suit.spades, Rank.ace).ToString();
    
                Assert.IsTrue(s1 == "C-2", "s1 invalid");
                Assert.IsTrue(s2 == "D-10", "s2 invalid");
                Assert.IsTrue(s3 == "H-Q", "s3 invalid");
                Assert.IsTrue(s4 == "S-A", "s4 invalid");
            }
        }
    
        [TestClass]
        public class DeckTest
        {
            private readonly string cardNamesInOrder = "C-2C-3C-4C-5C-6C-7C-8C-9C-10C-JC-QC-KC-AD-2D-3D-4D-5D-6D-7D-8D-9D-10D-JD-QD-KD-AH-2H-3H-4H-5H-6H-7H-8H-9H-10H-JH-QH-KH-AS-2S-3S-4S-5S-6S-7S-8S-9S-10S-JS-QS-KS-A";
    
            //Helper method. Adds the names of all the cards together into a single string.
            private string ConcatenateTheDeck(Deck d)
            {
                StringBuilder sb = new StringBuilder();
    
                for (int i = 0; i < Deck.deckSize; i++)
                {
                    sb.Append(d.GetCard(i));
                }
    
                return sb.ToString();
            }
    
            [TestMethod]
            public void InitialDeckContainsCardsInOrder()
            {
                Deck d = new Deck();
                string allTheCards = ConcatenateTheDeck(d);
    
                Assert.IsTrue(allTheCards == cardNamesInOrder);
            }
    
            [TestMethod]
            public void ShufflingChangesDeck()
            {
                // I'm not sure how to test that shuffling has worked other than to check that the cards aren't in the initial order.
                Deck d = new Deck();
                d.Shuffle();
                string allTheCards = ConcatenateTheDeck(d);
    
                Assert.IsTrue(allTheCards != cardNamesInOrder);
            }
        }
    }
    
    
    ================================================
    FILE: 94_War/csharp/WarTester/WarTester.csproj
    ================================================
    
    
      
        netcoreapp3.1
    
        false
    
        AnyCPU
      
    
      
        true
      
    
      
        
        
        
        
          all
          runtime; build; native; contentfiles; analyzers; buildtransitive
        
      
    
      
        
      
    
    
    
    
    ================================================
    FILE: 94_War/d/.gitignore
    ================================================
    *.exe
    *.obj
    
    
    ================================================
    FILE: 94_War/d/README.md
    ================================================
    Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Converted to [D](https://dlang.org/) by [Bastiaan Veelo](https://github.com/veelo).
    
    ## Running the code
    
    Assuming the reference [dmd](https://dlang.org/download.html#dmd) compiler:
    ```shell
    dmd -preview=dip1000 -run war.d
    ```
    
    [Other compilers](https://dlang.org/download.html) also exist.
    
    ## Specialties explained
    
    This game code contains some specialties that you might want to know more about. Here goes.
    
    ### Suits
    
    Most modern consoles are capable of displaying more than just ASCII, and so I have chosen to display the actual ♠, ♥, ♦
    and ♣ instead of substituting them by letters like the BASIC original did. Only the Windows console needs a nudge in
    the right direction with these instructions:
    ```d
    SetConsoleOutputCP(CP_UTF8); // Set code page
    SetConsoleOutputCP(GetACP);  // Restore the default
    ```
    Instead of cluttering the `main()` function with these lesser important details, we can move them into a
    [module constructor and module destructor](https://dlang.org/spec/module.html#staticorder), which run before and after
    `main()` respectively. And because order of declaration is irrelevant in a D module, we can push those all the way
    down to the bottom of the file. This is of course only necessary on Windows (and won't even work anywhere else) so
    we'll need to wrap this in a `version (Windows)` conditional code block:
    ```d
    version (Windows)
    {
        import core.sys.windows.windows;
    
        shared static this() @trusted
        {
            SetConsoleOutputCP(CP_UTF8);
        }
    
        shared static ~this() @trusted
        {
            SetConsoleOutputCP(GetACP);
        }
    }
    ```
    Although it doesn't matter much in this single-threaded program, the `shared` attribute makes that these
    constructors/destructors are run once per program invocation; non-shared module constructors and module destructors are
    run for every thread. The `@trusted` annotation is necessary because these are system API calls; The compiler cannot
    check these for memory-safety, and so we must indicate that we have reviewed the safety manually.
    
    ### Uniform Function Call Syntax
    
    In case you wonder why this line works:
    ```d
    if ("Do you want instructions?".yes)
        // ...
    ```
    then it is because this is equivalent to
    ```d
    if (yes("Do you want instructions?"))
        // ...
    ```
    where `yes()` is a Boolean function that is defined below `main()`. This is made possible by the language feature that
    is called [uniform function call syntax (UFCS)](https://dlang.org/spec/function.html#pseudo-member). UFCS works by
    passing what is in front of the dot as the first parameter to the function, and it was invented to make it possible to
    call free functions on objects as if they were member functions. UFCS can also be used to obtain a more natural order
    of function calls, such as this line inside `yes()`:
    ```d
    return trustedReadln.strip.toLower.startsWith("y");
    ```
    which reads easier than the equivalent
    ```d
    return startsWith(toLower(strip(trustedReadln())), "y");
    ```
    
    ### Type a lot or not?
    
    It would have been straight forward to define the `cards` array explicitly like so:
    ```d
    const cards = ["2♠", "2♥", "2♦", "2♣", "3♠", "3♥", "3♦", "3♣",
                   "4♠", "4♥", "4♦", "4♣", "5♠", "5♥", "5♦", "5♣",
                   "6♠", "6♥", "6♦", "6♣", "7♠", "7♥", "7♦", "7♣",
                   "8♠", "8♥", "8♦", "8♣", "9♠", "9♥", "9♦", "9♣",
                   "10♠", "10♥", "10♦", "10♣", "J♥", "J♦", "J♣", "J♣",
                   "Q♠", "Q♥", "Q♦", "Q♣", "K♠", "K♥", "K♦", "K♣",
                   "A♠", "A♥", "A♦", "A♣"];
    ```
    but that's tedious, difficult to spot errors in (*can you?*) and looks like something a computer can automate. Indeed
    it can:
    ```d
    static const cards = cartesianProduct(["2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"],
                                          ["♠", "♥", "♦", "♣"]).map!(a => a.expand.only.join).array;
    ```
    The function [`cartesianProduct`](https://dlang.org/phobos/std_algorithm_setops.html#cartesianProduct) takes two
    ranges, like the horizontal and vertical headers of a spreadsheet, and fills the table with the combinations that form
    the coordinates of the cells. But the output of that function is in the form of an array of
    [`Tuple`](https://dlang.org/phobos/std_typecons.html#Tuple)s, which looks like `[Tuple!(string, string)("2", "♠"),
    Tuple!(string, string)("2", "♥"), ... etc]`. [`map`](https://dlang.org/phobos/std_algorithm_iteration.html#map)
    comes to the rescue, converting each Tuple to a string, by calling
    [`expand`](https://dlang.org/phobos/std_typecons.html#.Tuple.expand), then
    [`only`](https://dlang.org/phobos/std_range.html#only) and then [`join`](https://dlang.org/phobos/std_array.html#join)
    on them. The result is a lazily evaluated range of strings. Finally,
    [`array`](https://dlang.org/phobos/std_array.html#array) turns the range into a random access array. The `static`
    attribute makes that all this is performed at compile-time, so the result is exactly the same as the manually entered
    data, but without the typo's.
    
    ### Shuffle the cards or not?
    
    The original BASIC code works with a constant array of cards, ordered by increasing numerical value, and indexing it
    with indices that have been shuffled. This is efficient because in comparing who wins, the indices can be compared
    directly, since a higher index correlates to a card with a higher numerical value (when divided by the number of suits,
    4). Some of the other reimplementations in other languages have been written in a lesser efficient way by shuffling the
    array of cards itself. This then requires the use of a lookup table or searching for equality in an auxiliary array
    when comparing cards.
    
    I find the original more elegant, so that's what you see here:
    ```d
    const indices = iota(0, cards.length).array.randomShuffle;
    ```
    [`iota`](https://dlang.org/phobos/std_range.html#iota) produces a range of integers, in this case starting at 0 and
    increasing up to the number of cards in the deck (exclusive). [`array`](https://dlang.org/phobos/std_array.html#array)
    turns the range into an array, so that [`randomShuffle`](https://dlang.org/phobos/std_random.html#randomShuffle) can
    do its work.
    
    
    ================================================
    FILE: 94_War/d/war.d
    ================================================
    @safe: // Make @safe the default for this file, enforcing memory-safety.
    import std;
    
    void main()
    {
        enum width = 50;
        writeln(center("War", width));
        writeln(center("(After Creative Computing  Morristown, New Jersey)\n\n", width));
        writeln(wrap("This is the card game of war.  Each card is given by suit-# " ~
                     "as 7♠ for seven of spades.  ", width));
    
        if ("Do you want instructions?".yes)
            writeln("\n", wrap("The computer gives you and it a 'card'.  The higher card " ~
                               "(numerically) wins.  The game ends when you choose not to " ~
                               "continue or when you have finished the pack.\n", width));
    
        static const cards = cartesianProduct(["2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"],
                                              ["♠", "♥", "♦", "♣"]).map!(a => a.expand.only.join).array;
        const indices = iota(0, cards.length).array.randomShuffle;
        int yourScore = 0, compScore = 0, i = 0;
        while (i < indices.length)
        {
            size_t your = indices[i++], comp = indices[i++];
            writeln("\nYou: ", cards[your].leftJustify(9), "Computer: ", cards[comp]);
            your /= 4; comp /= 4;
            if (your == comp)
                writeln("Tie. No score change.");
            else if (your < comp)
                writeln("The computer wins!!! You have ", yourScore,
                        " and the computer has ", ++compScore, ".");
            else
                writeln("You win. You have ", ++yourScore,
                        " and the computer has ", compScore, ".");
            if (i == indices.length)
                writeln("\nWe have run out of cards. Final score: You: ", yourScore,
                        ", the computer: ", compScore, ".");
            else if (!"Do you want to continue?".yes)
                break;
        }
        writeln("\nThanks for playing. It was fun.");
    }
    
    /// Returns whether question was answered positively.
    bool yes(string question)
    {
        writef!"%s "(question);
        try
            return trustedReadln.strip.toLower.startsWith("y");
        catch (Exception)   // readln throws on I/O and Unicode errors, which we handle here.
            return false;
    }
    
    /** An @trusted wrapper around readln.
    *
    * This is the only function that formally requires manual review for memory-safety.
    * [Arguably readln should be safe already](https://forum.dlang.org/post/rab398$1up$1@digitalmars.com)
    * which would remove the need to have any @trusted code in this program.
    */
    string trustedReadln() @trusted
    {
        return readln;
    }
    
    version (Windows)
    {
        // Make the Windows console do a better job at printing UTF-8 strings,
        // and restore the default upon termination.
    
        import core.sys.windows.windows;
    
        shared static this() @trusted
        {
            SetConsoleOutputCP(CP_UTF8);
        }
    
        shared static ~this() @trusted
        {
            SetConsoleOutputCP(GetACP);
        }
    }
    
    
    ================================================
    FILE: 94_War/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 94_War/java/War.java
    ================================================
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.Scanner;
    
    /**
     * Converted FROM BASIC to Java by Nahid Mondol.
     *
     * Based on Trevor Hobsons approach.
     */
    public class War {
    
        private static final int CARD_DECK_SIZE = 52;
        private static int playerTotalScore = 0;
        private static int computerTotalScore = 0;
        private static boolean invalidInput;
        private static Scanner userInput = new Scanner(System.in);
    
        // Simple approach for storing a deck of cards.
        // Suit-Value, ex: 2 of Spades = S-2, King of Diamonds = D-K, etc...
        private static ArrayList deckOfCards = new ArrayList(
                Arrays.asList("S-2", "H-2", "C-2", "D-2", "S-3", "H-3", "C-3", "D-3", "S-4", "H-4", "C-4", "D-4", "S-5",
                        "H-5", "C-5", "D-5", "S-6", "H-6", "C-6", "D-6", "S-7", "H-7", "C-7", "D-7", "S-8", "H-8", "C-8",
                        "D-8", "S-9", "H-9", "C-9", "D-9", "S-10", "H-10", "C-10", "D-10", "S-J", "H-J", "C-J", "D-J",
                        "S-Q", "H-Q", "C-Q", "D-Q", "S-K", "H-K", "C-K", "D-K", "S-A", "H-A", "C-A", "D-A"));
    
        public static void main(String[] args) {
            introMessage();
            showDirectionsBasedOnInput();
            playGame();
        }
    
        private static void introMessage() {
            System.out.println("\t         WAR");
            System.out.println("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
            System.out.println("THIS IS THE CARD GAME OF WAR. EACH CARD IS GIVEN BY SUIT-#");
            System.out.print("AS S-7 FOR SPADE 7. DO YOU WANT DIRECTIONS? ");
        }
    
        private static void showDirectionsBasedOnInput() {
            // Stay in loop until player chooses an option.
            invalidInput = true;
            while (invalidInput) {
                switch (userInput.nextLine().toLowerCase()) {
                    case "yes":
                        System.out.println("THE COMPUTER GIVES YOU AND IT A 'CARD'. THE HIGHER CARD");
                        System.out.println("(NUMERICALLY) WINS. THE GAME ENDS WHEN YOU CHOOSE NOT TO ");
                        System.out.println("CONTINUE OR WHEN YOU HAVE FINISHED THE PACK.\n");
                        invalidInput = false;
                        break;
                    case "no":
                        System.out.println();
                        invalidInput = false;
                        break;
                    default:
                        System.out.print("YES OR NO, PLEASE.   ");
                }
            }
        }
    
        private static void playGame() {
    
            // Checks to see if the player ends the game early.
            // Ending early will cause a different output to appear.
            boolean gameEndedEarly = false;
    
            // Shuffle the deck of cards.
            Collections.shuffle(deckOfCards);
    
            // Since the deck is already suffled, pull each card until the deck is empty or
            // until the user quits.
            outerloop:
            for (int i = 1; i <= CARD_DECK_SIZE; i += 2) {
                System.out.println("YOU: " + deckOfCards.get(i - 1) + "\t " + "COMPUTER: " + deckOfCards.get(i));
                getWinner(deckOfCards.get(i - 1), deckOfCards.get(i));
    
                invalidInput = true;
                while (invalidInput) {
                    if (endedEarly()) {
                        // Player ended game early.
                        // Break out of game loop and show end game output.
                        gameEndedEarly = true;
                        break outerloop;
                    }
                }
            }
    
            endGameOutput(gameEndedEarly);
        }
    
        /**
         * Outputs the winner of the current round.
         *
         * @param playerCard   Players card.
         * @param computerCard Computers card.
         */
        private static void getWinner(String playerCard, String computerCard) {
    
            // Return the number value of the card pulled.
            String playerCardScore = (playerCard.length() == 3) ? Character.toString(playerCard.charAt(2))
                    : playerCard.substring(2, 4);
            String computerCardScore = (computerCard.length() == 3) ? Character.toString(computerCard.charAt(2))
                    : computerCard.substring(2, 4);
    
            if (checkCourtCards(playerCardScore) > checkCourtCards(computerCardScore)) {
                System.out.println("YOU WIN.   YOU HAVE " + playerWonRound() + "   COMPUTER HAS " + getComputerScore());
            } else if (checkCourtCards(playerCardScore) < checkCourtCards(computerCardScore)) {
                System.out.println(
                        "COMPUTER WINS!!!   YOU HAVE " + getPlayerScore() + "   COMPUTER HAS " + computerWonRound());
            } else {
                System.out.println("TIE.  NO SCORE CHANGE");
            }
    
            System.out.print("DO YOU WANT TO CONTINUE? ");
        }
    
        /**
         * @param cardScore Score of the card being pulled.
         * @return an integer value of the current card's score.
         */
        private static int checkCourtCards(String cardScore) {
            switch (cardScore) {
                case "J":
                    return Integer.parseInt("11");
                case "Q":
                    return Integer.parseInt("12");
                case "K":
                    return Integer.parseInt("13");
                case "A":
                    return Integer.parseInt("14");
                default:
                    return Integer.parseInt(cardScore);
            }
        }
    
        /**
         * @return true if the player ended the game early. false otherwise.
         */
        private static boolean endedEarly() {
            switch (userInput.nextLine().toLowerCase()) {
                case "yes":
                    invalidInput = false;
                    return false;
                case "no":
                    invalidInput = false;
                    return true;
                default:
                    invalidInput = true;
                    System.out.print("YES OR NO, PLEASE.   ");
                    return false;
            }
        }
    
        /**
         * Show output based on if the game was ended early or not.
         *
         * @param endedEarly true if the game was ended early, false otherwise.
         */
        private static void endGameOutput(boolean endedEarly) {
            if (endedEarly) {
                System.out.println("YOU HAVE ENDED THE GAME. FINAL SCORE:  YOU: " + getPlayerScore() + " COMPUTER: "
                        + getComputerScore());
                System.out.println("THANKS FOR PLAYING.  IT WAS FUN.");
            } else {
                System.out.println("WE HAVE RUN OUT OF CARDS. FINAL SCORE:  YOU: " + getPlayerScore() + " COMPUTER: "
                        + getComputerScore());
                System.out.println("THANKS FOR PLAYING.  IT WAS FUN.");
            }
        }
    
        /**
         * Increment the player's total score if they have won the round.
         */
        private static int playerWonRound() {
            return playerTotalScore += 1;
        }
    
        /**
         * Get the player's total score.
         */
        private static int getPlayerScore() {
            return playerTotalScore;
        }
    
        /**
         * Increment the computer's total score if they have won the round.
         */
        private static int computerWonRound() {
            return computerTotalScore += 1;
        }
    
        /**
         * Get the computer's total score.
         */
        private static int getComputerScore() {
            return computerTotalScore;
        }
    }
    
    
    ================================================
    FILE: 94_War/javascript/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells)
    
    
    ================================================
    FILE: 94_War/javascript/war.html
    ================================================
    
    
    WAR
    
    
    
    
    
    
    
    
    
    
    ================================================
    FILE: 94_War/javascript/war.js
    ================================================
    // WAR
    //
    // Original conversion from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    function print(str) {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    function tab(space) {
        let str = "";
        while (space-- > 0) {
            str += " ";
        }
        return str;
    }
    
    function input() {
        return new Promise(function (resolve) {
            const input_element = document.createElement("INPUT");
    
            print("? ");
            input_element.setAttribute("type", "text");
            input_element.setAttribute("length", "50");
            document.getElementById("output").appendChild(input_element);
            input_element.focus();
            input_element.addEventListener("keydown", function (event) {
                if (event.keyCode == 13) {
                    const input_str = input_element.value;
                    document.getElementById("output").removeChild(input_element);
                    print(input_str);
                    print("\n");
                    resolve(input_str);
                }
            });
        });
    }
    
    async function askYesOrNo(question) {
        while (1) {
            print(question);
            const str = (await input()).toUpperCase();
            if (str === "YES") {
                return true;
            }
            else if (str === "NO") {
                return false;
            }
            else {
                print("YES OR NO, PLEASE.  ");
            }
        }
    }
    
    async function askAboutInstructions() {
        const playerWantsInstructions = await askYesOrNo("DO YOU WANT DIRECTIONS");
        if (playerWantsInstructions) {
            print("THE COMPUTER GIVES YOU AND IT A 'CARD'.  THE HIGHER CARD\n");
            print("(NUMERICALLY) WINS.  THE GAME ENDS WHEN YOU CHOOSE NOT TO\n");
            print("CONTINUE OR WHEN YOU HAVE FINISHED THE PACK.\n");
        }
        print("\n");
        print("\n");
    }
    
    function createGameDeck(cards, gameSize) {
        const deck = [];
        const deckSize = cards.length;
        for (let j = 0; j < gameSize; j++) {
            let card;
    
            // Compute a new card index until we find one that isn't already in the new deck
            do {
                card = Math.floor(deckSize * Math.random());
            } while (deck.includes(card));
            deck.push(card);
        }
        return deck;
    }
    
    function computeCardValue(cardIndex) {
        return Math.floor(cardIndex / 4);
    }
    
    function printGameOver(playerScore, computerScore) {
        print("\n");
        print("\n");
        print(`WE HAVE RUN OUT OF CARDS.  FINAL SCORE:  YOU: ${playerScore}  THE COMPUTER: ${computerScore}\n`);
        print("\n");
    }
    
    function printTitle() {
        print(tab(33) + "WAR\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        print("THIS IS THE CARD GAME OF WAR.  EACH CARD IS GIVEN BY SUIT-#\n");
        print("AS S-7 FOR SPADE 7.  ");
    }
    
    function printCards(playerCard, computerCard) {
        print("\n");
        print(`YOU: ${playerCard}\tCOMPUTER: ${computerCard}\n`);
    }
    
    const cards = [
        "S-2", "H-2", "C-2", "D-2",
        "S-3", "H-3", "C-3", "D-3",
        "S-4", "H-4", "C-4", "D-4",
        "S-5", "H-5", "C-5", "D-5",
        "S-6", "H-6", "C-6", "D-6",
        "S-7", "H-7", "C-7", "D-7",
        "S-8", "H-8", "C-8", "D-8",
        "S-9", "H-9", "C-9", "D-9",
        "S-10", "H-10", "C-10", "D-10",
        "S-J", "H-J", "C-J", "D-J",
        "S-Q", "H-Q", "C-Q", "D-Q",
        "S-K", "H-K", "C-K", "D-K",
        "S-A", "H-A", "C-A", "D-A"
    ];
    
    // Main control section
    async function main() {
        printTitle();
        await askAboutInstructions();
    
        let computerScore = 0;
        let playerScore = 0;
    
        // Generate a random deck
        const gameSize = cards.length;    // Number of cards to shuffle into the game deck.  Can be <= cards.length.
        const deck = createGameDeck(cards, gameSize);
        let shouldContinuePlaying = true;
    
        while (deck.length > 0 && shouldContinuePlaying) {
            const playerCard = deck.shift();    // Take a card
            const computerCard = deck.shift();    // Take a card
            printCards(cards[playerCard], cards[computerCard]);
    
            const playerCardValue = computeCardValue(playerCard);
            const computerCardValue = computeCardValue(computerCard);
            if (playerCardValue < computerCardValue) {
                computerScore++;
                print("THE COMPUTER WINS!!! YOU HAVE " + playerScore + " AND THE COMPUTER HAS " + computerScore + "\n");
            } else if (playerCardValue > computerCardValue) {
                playerScore++;
                print("YOU WIN. YOU HAVE " + playerScore + " AND THE COMPUTER HAS " + computerScore + "\n");
            } else {
                print("TIE.  NO SCORE CHANGE.\n");
            }
    
            if (deck.length === 0) {
                printGameOver(playerScore, computerScore);
            }
            else {
                shouldContinuePlaying = await askYesOrNo("DO YOU WANT TO CONTINUE");
            }
        }
        print("THANKS FOR PLAYING.  IT WAS FUN.\n");
        print("\n");
    }
    
    main();
    
    
    ================================================
    FILE: 94_War/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 94_War/kotlin/War.kt
    ================================================
    /**
     * Converted FROM BASIC to Kotlin by John Long, with hints from the Java by Nahid Mondol.
     *
     */
    
    val suits = listOf("S", "H", "C", "D")
    val ranks = listOf("2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A")
    
    // Create the deck programmatically
    val fullDeck = suits.flatMap { suit ->
        ranks.map { rank ->
            Card(suit, rank)
        }
    }
    
    class Card(private val suit: String, private val rank: String) {
        // Allow comparison of cards to each other
        operator fun compareTo(other: Card): Int = this.rankValue.compareTo(other.rankValue)
        // We can figure relative rank by the order in the ranks value
        private val rankValue: Int = ranks.indexOf(rank)
        override fun toString(): String = "$suit-$rank"
    }
    
    fun main() {
        introMessage()
        showDirectionsBasedOnInput()
        playGame()
        println("THANKS FOR PLAYING.  IT WAS FUN.")
    }
    
    private fun introMessage() {
        println("\t         WAR")
        println("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY")
        println("THIS IS THE CARD GAME OF WAR. EACH CARD IS GIVEN BY SUIT-#")
        print("AS S-7 FOR SPADE 7. DO YOU WANT DIRECTIONS? ")
    }
    
    private fun showDirectionsBasedOnInput() {
        if (getYesOrNo()) {
            println("THE COMPUTER GIVES YOU AND IT A 'CARD'. THE HIGHER CARD")
            println("(NUMERICALLY) WINS. THE GAME ENDS WHEN YOU CHOOSE NOT TO ")
            println("CONTINUE OR WHEN YOU HAVE FINISHED THE PACK.\n")
        }
    }
    
    // Stay in loop until player chooses an option, then return "true" for yes or "false" for no
    private fun getYesOrNo() = generateSequence { readln() }.firstNotNullOf { it.asYesOrNo }
    
    // Since this returns null for an incorrect value, above firstNotNullOf will keep looping until
    // we get something valid
    private val String.asYesOrNo: Boolean?
        get() =
            when (this.lowercase()) {
                "yes" -> true
                "no" -> false
                else -> {
                    println("YES OR NO, PLEASE.   ")
                    null
                }
            }
    
    
    private fun playGame() {
        // Shuffle the deck than break it into 26 pairs
        val pairs = fullDeck.shuffled().chunked(2)
        val score = Score(0, 0)
        val lastPlayerCard = pairs.last().first()
        // We use "destructuring" to extract the pair of cards directly to a variable here
        pairs.forEach { (playerCard, computerCard) ->
            println("YOU: $playerCard\tCOMPUTER: $computerCard")
            when {
                playerCard > computerCard -> score.playerWins()
                computerCard > playerCard -> score.computerWins()
                else -> println("TIE.  NO SCORE CHANGE.")
            }
            // Doesn't make sense to ask to continue if we have no more cards left to deal
            if (playerCard != lastPlayerCard) {
                println("DO YOU WANT TO CONTINUE")
                if (!getYesOrNo()) {
                    return
                }
            }
        }
        score.printFinalScore()
        return
    }
    
    
    class Score(private var player: Int, private var computer: Int) {
        fun playerWins() {
            player++
            printScore("YOU WIN.")
        }
    
        fun computerWins() {
            computer++
            printScore("THE COMPUTER WINS!!!")
        }
    
        private fun printScore(text: String) {
            println("$text YOU HAVE $player AND THE COMPUTER HAS $computer")
        }
    
        // Only print if you go through the whole deck
        fun printFinalScore() {
            println("WE HAVE RUN OUT OF CARDS.  FINAL SCORE:   YOU: $player  THE COMPUTER:$computer")
        }
    }
    
    
    ================================================
    FILE: 94_War/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 94_War/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    
    ================================================
    FILE: 94_War/perl/war.pl
    ================================================
    #!/usr/bin/env perl
    
    use 5.010;      # To get 'say'
    
    use strict;     # Require explicit declaration of variables
    use warnings;   # Enable optional compiler warnings
    
    use English;    # Use more friendly names for Perl's magic variables
    use Getopt::Long 2.33 qw{ :config auto_version };
    use List::Util qw{ shuffle };
    use Pod::Usage;
    use Term::ReadLine;     # Prompt and return user input
    
    our $VERSION = '0.000_01';
    
    my %opt;
    
    GetOptions( \%opt,
        qw{ unicode! },
        help => sub { pod2usage( { -verbose => 2 } ) },
    ) or pod2usage( { -verbose => 0 } );
    
    my @cards;
    my $current_rank_value = 1;
    my %rank_value;
    my @suits = $opt{unicode} ?
        ( map { chr } 0x2660 .. 0x2663 ) :
        ( qw{ S H D C } );
    foreach my $rank ( ( 2 .. 10 ), qw{ J Q K A } ) {
        $rank_value{$rank} = $current_rank_value++;
        foreach my $suit ( @suits ) {
            push @cards, "$suit-$rank";
        }
    }
    
    $opt{unicode}
        and binmode STDOUT, ':encoding(utf-8)';
    
    @cards = shuffle( @cards );
    
    print <<'EOD';
                                     WAR
                   Creative Computing  Morristown, New Jersey
    
    
    
    This is the card game of War.  Each card is given by suit-#
    EOD
    
    # Create the readline object.
    state $term = Term::ReadLine->new( 'word' );
    
    my $resp = $term->readline(
        "as $suits[0]-7 for Spade 7.  Do you want directions? [y/N]: " );
    exit unless defined $resp;
    if ( $resp =~ m/ \A y /smxi ) {
        print <<'EOD';
    The computer gives you and it a 'card'.  The higher card
    (numerically) wins.  The game ends when you choose not to
    continue or when you have finished the pack.
    
    EOD
    }
    
    my $your_score = my $computer_score = 0;
    
    while ( 1 ) {
        my ( $you, $computer ) = splice @cards, 0, 2;
        say '';
        say "You: $you; computer: $computer";
        my $result = $rank_value{ substr $you, 2 } <=>
            $rank_value{ substr $computer, 2 };
        if ( $result < 0 ) {
            $computer_score++;
            say "The computer wins!!!  ",
                "You have $your_score and the computer has $computer_score";
        } elsif ( $result > 0 ) {
            $your_score++;
            say "You win.  ",
                "You have $your_score and the computer has $computer_score";
        } else {
            say 'Tie.  No score change.';
        }
    
        last unless @cards;
    
        $resp = $term->readline( 'Do you want to continue? [Y/n]: ' );
        last unless defined $resp;
        last if $resp =~ m/ \A n /smxi;
    }
    
    say "We have run out of cards.  ",
        "Final score:  you: $your_score;  the computer: $computer_score"
        unless @cards;
    say '';
    say 'Thanks for playing.  It was fun.';
    __END__
    
    =head1 TITLE
    
    war.pl - Play the game 'War' from Basic Computer Games
    
    =head1 SYNOPSIS
    
     war.pl
     war.pl --help
     war.pl --version
    
    =head1 OPTIONS
    
    =head2 --help
    
    This option displays the documentation for this script. The script then
    exits.
    
    =head2 --unicode
    
    If this Boolean option is asserted, the suits are designated by their
    Unicode glyphs rather than by ASCII letters. For these to display
    properly your terminal must properly interpret Unicode.
    
    The default is C<--no-unicode>.
    
    =head2 --version
    
    This option displays the version of this script. The script then exits.
    
    =head1 DETAILS
    
    This Perl script is a port of C, which is the 94th entry in Basic
    Computer Games.
    
    =head1 PORTED BY
    
    Thomas R. Wyant, III F
    
    =head1 COPYRIGHT AND LICENSE
    
    Copyright (C) 2022 by Thomas R. Wyant, III
    
    This program is free software; you can redistribute it and/or modify it
    under the same terms as Perl 5.10.0. For more details, see the Artistic
    License 1.0 at
    L, and/or the
    Gnu GPL at L.
    
    This program is distributed in the hope that it will be useful, but
    without any warranty; without even the implied warranty of
    merchantability or fitness for a particular purpose.
    
    =cut
    
    # ex: set expandtab tabstop=4 textwidth=72 :
    
    
    ================================================
    FILE: 94_War/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 94_War/python/cards.json
    ================================================
    [
        "S-2",
        "H-2",
        "C-2",
        "D-2",
        "S-3",
        "H-3",
        "C-3",
        "D-3",
        "S-4",
        "H-4",
        "C-4",
        "D-4",
        "S-5",
        "H-5",
        "C-5",
        "D-5",
        "S-6",
        "H-6",
        "C-6",
        "D-6",
        "S-7",
        "H-7",
        "C-7",
        "D-7",
        "S-8",
        "H-8",
        "C-8",
        "D-8",
        "S-9",
        "H-9",
        "C-9",
        "D-9",
        "S-10",
        "H-10",
        "C-10",
        "D-10",
        "S-J",
        "H-J",
        "C-J",
        "D-J",
        "S-Q",
        "H-Q",
        "C-Q",
        "D-Q",
        "S-K",
        "H-K",
        "C-K",
        "D-K",
        "S-A",
        "H-A",
        "C-A",
        "D-A"
    ]
    
    
    ================================================
    FILE: 94_War/python/war.py
    ================================================
    #!/usr/bin/env python3
    
    """
    WAR
    
    Converted from BASIC to Python by Trevor Hobson
    """
    
    import json
    import random
    from pathlib import Path
    from typing import List
    
    
    def card_value(input: str) -> int:
        return ["2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"].index(
            input.split("-")[1]
        )
    
    
    def play_game() -> None:
        """Play one round of the game"""
        with open(Path(__file__).parent / "cards.json") as f:
            cards: List[str] = json.load(f)
    
        random.shuffle(cards)
        score_you = 0
        score_computer = 0
        cards_left = 52
        for round in range(26):
            print()
            card_you = cards[round]
            card_computer = cards[round * 2]
            print("You:", card_you, " " * (8 - len(card_you)) + "Computer:", card_computer)
            value_you = card_value(card_you)
            value_computer = card_value(card_computer)
            if value_you > value_computer:
                score_you += 1
                print(
                    "You win. You have", score_you, "and the computer has", score_computer
                )
            elif value_computer > value_you:
                score_computer += 1
                print(
                    "The computer wins!!! You have",
                    score_you,
                    "and the computer has",
                    score_computer,
                )
            else:
                print("Tie. No score change.")
            cards_left -= 2
            if cards_left > 2 and input("Do you want to continue ").lower().startswith("n"):
                break
        if cards_left == 0:
            print(
                "\nWe have run out of cards. Final score: You:",
                score_you,
                "the computer:",
                score_computer,
            )
        print("\nThanks for playing. It was fun.")
    
    
    def main() -> None:
        print(" " * 33 + "WAR")
        print(" " * 15 + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n")
        print("This is the card game of war. Each card is given by suit-#")
        print("as S-7 for Spade 7.")
        if input("Do you want directions ").lower().startswith("y"):
            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.")
    
        keep_playing = True
        while keep_playing:
            play_game()
            keep_playing = input("\nPlay again? (yes or no) ").lower().startswith("y")
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 94_War/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 94_War/ruby/war.rb
    ================================================
    #!/usr/bin/env ruby
    # reinterpreted from BASIC by stephan.com
    class War
      class Card
        class CardError < StandardError; end
    
        SUITS = %i[spades hearts clubs diamonds].freeze
        PIPS = %i[ace deuce trey four five six seven eight nine ten jack king queen].freeze
        CARDS = SUITS.product(PIPS).freeze
        VALUES = PIPS.zip(1..13).to_h.freeze
    
        attr_reader :value
    
        def initialize(suit, pip)
          @suit = suit
          @pip = pip
          raise CardError, 'invalid suit' unless SUITS.include? @suit
          raise CardError, 'invalid pip' unless PIPS.include? @pip
    
          @value = VALUES[pip]
        end
    
        def <=>(other)
          @value <=> other.value
        end
    
        def >(other)
          @value > other.value
        end
    
        def <(other)
          @value < other.value
        end
    
        def to_s
          "the #{@pip} of #{@suit}"
        end
    
        def self.shuffle
          CARDS.map { |suit, pip| new(suit, pip) }.shuffle
        end
      end
    
      def initialize
        @your_score = 0
        @computer_score = 0
        @your_deck = Card.shuffle
        @computer_deck = Card.shuffle
      end
    
      def play
        intro
    
        loop do
          puts "\nYou: #{@your_score} Computer: #{@computer_score}"
          round @your_deck.shift, @computer_deck.shift
          break if empty?
    
          puts 'Do you want to continue?'
          break unless yesno
        end
    
        outro
      end
    
      private
    
      def round(your_card, computer_card)
        puts "You: #{your_card} vs Computer: #{computer_card}"
        return puts 'Tie. No score change.' if your_card == computer_card
    
        if computer_card > your_card
          puts "Computer wins with #{computer_card}"
          @computer_score += 1
        else
          puts "You win with #{your_card}"
          @your_score += 1
        end
      end
    
      def yesno
        loop do
          wants = gets.strip
          return true if wants.downcase == 'yes'
          return false if wants.downcase == 'no'
    
          puts 'Yes or no, please.'
        end
      end
    
      def intro
        puts 'War'.center(80)
        puts 'stephan.com'.center(80)
        puts
        puts 'This is the card game of war.'
        puts 'Do you want directions'
        directions if yesno
      end
    
      def directions
        puts 'The computer gives you and it a \'card\'.  The higher card'
        puts '(numerically) wins.  The game ends when you choose not to'
        puts 'continue or when you have finished the pack.'
        puts
      end
    
      def outro
        puts "We've run out of cards" if empty?
        puts "Final score:\nYou: #{@your_score}\nComputer: #{@computer_score}"
        puts 'Thanks for playing!'
      end
    
      def empty?
        @your_deck.empty? || @computer_deck.empty?
      end
    end
    
    War.new.play
    
    
    ================================================
    FILE: 94_War/rust/Cargo.toml
    ================================================
    [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"
    
    
    ================================================
    FILE: 94_War/rust/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Rust](https://www.rust-lang.org/) by Anthony Rubick [AnthonyMichaelTDM](https://github.com/AnthonyMichaelTDM)
    
    
    ================================================
    FILE: 94_War/rust/src/main.rs
    ================================================
    use rand::prelude::{thread_rng, SliceRandom};
    use std::io;
    
    //DATA
    struct CARD {
        suit: u8, //1-4     1=hearts    2=diamonds     3=clubs     4=spades
        value: u8, //2-14   2,3,4,5,6,7,8,9,10,jack,queen,king,ace
    }
    impl CARD {
        /**
         * creates a new card from the passed card name
         */
        fn new(suit:u8,value:u8) -> CARD {
            return CARD { suit: suit, value: value };
        }
    }
    impl ToString for CARD {
        /**
         * returns string representation of the Card
         */
        fn to_string(&self) -> String {
            let mut name: String;
            //value
            name = match self.value {
                2..=10 => self.value.to_string() + " of ",
                11 => String::from("Jack of "),
                12 => String::from("Queen of "),
                13 => String::from("King of "),
                14 => String::from("Ace of "),
                _ => panic!("card has an invalid value"),
            };
            //suit
            name += match self.suit {
                1=>"Hearts",
                2=>"Diamonds",
                3=>"Clubs",
                4=>"Spades",
                _ => panic!("card has an invalid suit"),
            };
            return name;
        }
    }
    struct GAME {
        deck: Vec, //cards in the deck
        player_score: usize, //human player score
        computer_score:usize,//computer score
    }
    impl GAME {
        /**
         * creates a new game, filling a deck with 52 cards, and setting scores to 0
         */
        fn new() -> GAME{
            //DATA
            let mut game = GAME{deck: Vec::new(), player_score: 0, computer_score: 0};
            //fill deck
            for suit in 1..=4 { //4 suits
                for value in 2..=14 { //13 cards
                    game.deck.push( CARD::new(suit,value) );
                }
            }
            //shuffle deck
            game.deck.shuffle(&mut thread_rng());
            //return game
            return game;
        }
    
        /**
         * fully play game
         */
        fn play_game(&mut self) {
            //DATA
            let mut human_card: CARD;
            let mut computer_card: CARD;
    
            //game loop
            while self.deck.len() >=2 {
                //draw cards
                human_card = self.deck.pop().expect("DECK IS EMPTY!");
                computer_card = self.deck.pop().expect("DECK IS EMPTY!");
    
                //print cards
                println!("\nYou: {}\t\tComputer: {}",human_card.to_string(), computer_card.to_string());
    
                //determine winner
                if human_card.value > computer_card.value { //human wins
                    self.player_score += 1;
                    println!("You win. You have {} and the computer has {}", human_card.value, computer_card.value);
                }
                else if human_card.value < computer_card.value { //computer wins
                    self.computer_score += 1;
                    println!("The computer wins!!! You have {} and the computer has {}", human_card.value, computer_card.value);
                }
                else { //tie
                    println!("Tie. No score change.")
                }
                if !get_yes_no_from_user_input("Do you want to continue? y/n") {break;}
            }
            if self.deck.is_empty() {
                println!("WE HAVE RUN OUT OF CARDS.  FINAL SCORE:  YOU: {} THE COMPUTER: {}",self.player_score, self.computer_score);
            }
            println!("\nThanks for playing. It was fun.");
        }
    }
    
    fn main() {
        //print welcome message
        welcome();
    
        //game loop
        let mut game = GAME::new();
        loop {
            game.play_game();
            if !get_yes_no_from_user_input("\nPlay again? (yes or no) ") {break;}
            game = GAME::new();
        }
    }
    
    /**
     * print welcome message
     */
    fn welcome() {
        println!("
                                    War\n
                  CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n
        This is the card game of war. Each card is given by # of suit
        ex: Ace of Spades\n
        ");
        if get_yes_no_from_user_input("Do you want directions?") {
            println!("
            The computer gives you and it a 'card'. The higher card
            (numerically) wins. The game ends when you choose not to
            continue or when you have finished the pack.\n
            ");
        }
    }
    
    /**
     * returns true if user input starts with y or Y,
     * false otherwise
     */
    fn get_yes_no_from_user_input(prompt: &str) -> bool {
        let mut raw_input = String::new(); // temporary variable for user input that can be parsed later
    
        //print prompt
        println!("{}", prompt);
        //read user input from standard input, and store it to raw_input
        //raw_input.clear(); //clear input
        io::stdin().read_line(&mut raw_input).expect( "CANNOT READ INPUT!");
    
        //from input, try to read a valid character
        if let Some(i) = raw_input.trim().chars().nth(0) {
            if i == 'y' || i == 'Y' {
                return true;
            }
        }
        //default case
        return false;
    }
    
    
    ================================================
    FILE: 94_War/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 94_War/vbnet/War.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "War", "War.vbproj", "{36D070A1-08CB-424E-AB8E-E782B0A6654B}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{36D070A1-08CB-424E-AB8E-E782B0A6654B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{36D070A1-08CB-424E-AB8E-E782B0A6654B}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{36D070A1-08CB-424E-AB8E-E782B0A6654B}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{36D070A1-08CB-424E-AB8E-E782B0A6654B}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 94_War/vbnet/War.vbproj
    ================================================
    
      
        Exe
        War
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 94_War/war.bas
    ================================================
    10 PRINT TAB(33);"WAR"
    20 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    30 PRINT: PRINT: PRINT
    100 PRINT "THIS IS THE CARD GAME OF WAR.  EACH CARD IS GIVEN BY SUIT-#"
    110 PRINT "AS S-7 FOR SPADE 7.  ";
    120 PRINT "DO YOU WANT DIRECTIONS";
    130 INPUT B$
    140 IF B$="NO" THEN 210
    150 IF B$="YES" THEN 180
    160 PRINT "YES OR NO, PLEASE.  ";
    170 GOTO 120
    180 PRINT "THE COMPUTER GIVES YOU AND IT A 'CARD'.  THE HIGHER CARD"
    190 PRINT "(NUMERICALLY) WINS.  THE GAME ENDS WHEN YOU CHOOSE NOT TO"
    200 PRINT "CONTINUE OR WHEN YOU HAVE FINISHED THE PACK."
    210 PRINT
    220 PRINT
    230 DIM A$(52),L(54)
    240 FOR I=1 TO 52
    250 READ A$(I)
    260 NEXT I
    270 REM
    280 FOR J=1 TO 52
    290 LET L(J)=INT(52*RND(1))+1
    295 IF J=1 THEN 350
    300 FOR K=1 TO J-1
    310 IF L(K)<>L(J) THEN 340
    320 REM
    330 GOTO 290
    340 NEXT K
    350 NEXT J
    360 P=P+1
    370 M1=L(P)
    380 P=P+1
    390 M2=L(P)
    400 PRINT
    420 PRINT "YOU: ";A$(M1),"COMPUTER: ";A$(M2)
    430 N1=INT((M1-.5)/4)
    440 N2=INT((M2-.5)/4)
    450 IF N1>=N2 THEN 490
    460 A1=A1+1
    470 PRINT "THE COMPUTER WINS!!! YOU HAVE";B1;"AND THE COMPUTER HAS";A1
    480 GOTO 540
    490 IF N1=N2 THEN 530
    500 B1=B1+1
    510 PRINT "YOU WIN. YOU HAVE";B1;"AND THE COMPUTER HAS";A1
    520 GOTO 540
    530 PRINT "TIE.  NO SCORE CHANGE."
    540 IF L(P+1)=0 THEN 610
    550 PRINT "DO YOU WANT TO CONTINUE";
    560 INPUT V$
    570 IF V$="YES" THEN 360
    580 IF V$="NO" THEN 650
    590 PRINT "YES OR NO, PLEASE.  ";
    600 GOTO 540
    610 PRINT
    620 PRINT
    630 PRINT "WE HAVE RUN OUT OF CARDS.  FINAL SCORE:  YOU: ";B1;
    640 PRINT "  THE COMPUTER: ";A1:PRINT
    650 PRINT "THANKS FOR PLAYING.  IT WAS FUN."
    655 PRINT
    660 DATA "S-2","H-2","C-2","D-2","S-3","H-3","C-3","D-3"
    670 DATA "S-4","H-4","C-4","D-4","S-5","H-5","C-5","D-5"
    680 DATA "S-6","H-6","C-6","D-6","S-7","H-7","C-7","D-7"
    690 DATA "S-8","H-8","C-8","D-8","S-9","H-9","C-9","D-9"
    700 DATA "S-10","H-10","C-10","D-10","S-J","H-J","C-J","D-J"
    710 DATA "S-Q","H-Q","C-Q","D-Q","S-K","H-K","C-K","D-K"
    720 DATA "S-A","H-A","C-A","D-A"
    999 END
    
    
    ================================================
    FILE: 95_Weekday/README.md
    ================================================
    ### Weekday
    
    This program gives facts about your date of birth (or some other day of interest). It is not prepared to give information on people born before the use of the current type of calendar, i.e. year 1582.
    
    You merely enter today’s date in the form—month, day, year and your date of birth in the same form. The computer then tells you the day of the week of your birth date, your age, and how much time you have spent sleeping, eating, working, and relaxing.
    
    This program was adapted from a GE timesharing program by Tom Kloos at the Oregon Museum of Science and Industry.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=179)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=194)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 95_Weekday/csharp/Program.cs
    ================================================
    using System.Text;
    
    namespace Weekday
    {
        class Weekday
        {
            private void DisplayIntro()
            {
                Console.WriteLine("");
                Console.WriteLine("SYNONYM".PadLeft(23));
                Console.WriteLine("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
                Console.WriteLine("");
                Console.WriteLine("Weekday is a computer demonstration that");
                Console.WriteLine("gives facts about a date of interest to you.");
                Console.WriteLine("");
            }
    
            private bool ValidateDate(string InputDate, out DateTime ReturnDate)
            {
                // The expectation is that the input is in the format D,M,Y
                // but any valid date format (other than with commas) will work
                string DateString = InputDate.Replace(",", "/");
    
                return (DateTime.TryParse(DateString, out ReturnDate));
            }
    
            private DateTime PromptForADate(string Prompt)
            {
                bool Success = false;
                string LineInput = String.Empty;
                DateTime TodaysDate = DateTime.MinValue;
    
                // Get the date for input and validate it
                while (!Success)
                {
                    Console.Write(Prompt);
                    LineInput = Console.ReadLine().Trim().ToLower();
    
                    Success = ValidateDate(LineInput, out TodaysDate);
    
                    if (!Success)
                    {
                        Console.WriteLine("*** Invalid date.  Please try again.");
                        Console.WriteLine("");
                    }
                }
    
                return TodaysDate;
            }
    
            private void CalculateDateDiff(DateTime TodaysDate, DateTime BirthDate, Double Factor, out int AgeInYears, out int AgeInMonths, out int AgeInDays)
            {
                // leveraged Stack Overflow answer: https://stackoverflow.com/a/3055445
    
                // Convert to number of days since Birth Date, multiple by factor then store as new FactorDate
                TimeSpan TimeDiff = TodaysDate.Subtract(BirthDate);
                Double NumberOfDays = TimeDiff.Days * Factor;
                DateTime FactorDate = BirthDate.AddDays(NumberOfDays);
    
                // Compute difference between FactorDate (which is TodaysDate * Factor) and BirthDate
                AgeInMonths = FactorDate.Month - BirthDate.Month;
                AgeInYears = FactorDate.Year - BirthDate.Year;
    
                if (FactorDate.Day < BirthDate.Day)
                {
                    AgeInMonths--;
                }
    
                if (AgeInMonths < 0)
                {
                    AgeInYears--;
                    AgeInMonths += 12;
                }
    
                AgeInDays = (FactorDate - BirthDate.AddMonths((AgeInYears * 12) + AgeInMonths)).Days;
    
            }
    
            private void WriteColumnOutput(string Message, int Years, int Months, int Days)
            {
    
                Console.WriteLine("{0,-25} {1,-10:N0} {2,-10:N0} {3,-10:N0}", Message, Years, Months, Days);
    
            }
    
            private void DisplayOutput(DateTime TodaysDate, DateTime BirthDate)
            {
                Console.WriteLine("");
    
                // Not allowed to play if the current year is before 1582
                if (TodaysDate.Year < 1582)
                {
                    Console.WriteLine("Not prepared to give day of week prior to MDLXXXII.");
                    return;
                }
    
                // Share which day of the week the BirthDate was on
                Console.Write(" {0} ", BirthDate.ToString("d"));
    
                string DateVerb = "";
                if (BirthDate.CompareTo(TodaysDate) < 0)
                {
                    DateVerb = "was a ";
                }
                else if (BirthDate.CompareTo(TodaysDate) == 0)
                {
                    DateVerb = "is a ";
                }
                else
                {
                    DateVerb = "will be a ";
                }
                Console.Write("{0}", DateVerb);
    
                // Special warning if their birth date was on a Friday the 13th!
                if (BirthDate.DayOfWeek.ToString().Equals("Friday") && BirthDate.Day == 13)
                {
                    Console.WriteLine("{0} the Thirteenth---BEWARE", BirthDate.DayOfWeek.ToString());
                }
                else
                {
                    Console.WriteLine("{0}", BirthDate.DayOfWeek.ToString());
                }
    
                // If today's date is the same month & day as the birth date then wish them a happy birthday!
                if (BirthDate.Month == TodaysDate.Month && BirthDate.Day == TodaysDate.Day)
                {
                    Console.WriteLine("");
                    Console.Write("***Happy Birthday***");
                }
    
                Console.WriteLine("");
    
                // Only show the date calculations if BirthDate is before TodaysDate
                if (DateVerb.Trim().Equals("was a"))
                {
    
                    Console.WriteLine("{0,-24} {1,-10} {2,-10} {3,-10}", " ", "Years", "Months", "Days");
    
                    int TheYears = 0, TheMonths = 0, TheDays = 0;
                    int FlexYears = 0, FlexMonths = 0, FlexDays = 0;
    
                    CalculateDateDiff(TodaysDate, BirthDate, 1, out TheYears, out TheMonths, out TheDays);
                    WriteColumnOutput("Your age if birthdate", TheYears, TheMonths, TheDays);
    
                    FlexYears = TheYears;
                    FlexMonths = TheMonths;
                    FlexDays = TheDays;
                    CalculateDateDiff(TodaysDate, BirthDate, .35, out FlexYears, out FlexMonths, out FlexDays);
                    WriteColumnOutput("You have slept", FlexYears, FlexMonths, FlexDays);
    
                    FlexYears = TheYears;
                    FlexMonths = TheMonths;
                    FlexDays = TheDays;
                    CalculateDateDiff(TodaysDate, BirthDate, .17, out FlexYears, out FlexMonths, out FlexDays);
                    WriteColumnOutput("You have eaten", FlexYears, FlexMonths, FlexDays);
    
                    FlexYears = TheYears;
                    FlexMonths = TheMonths;
                    FlexDays = TheDays;
                    CalculateDateDiff(TodaysDate, BirthDate, .23, out FlexYears, out FlexMonths, out FlexDays);
                    string FlexPhrase = "You have played";
                    if (TheYears > 3)
                        FlexPhrase = "You have played/studied";
                    if (TheYears > 9)
                        FlexPhrase = "You have worked/played";
                    WriteColumnOutput(FlexPhrase, FlexYears, FlexMonths, FlexDays);
    
                    FlexYears = TheYears;
                    FlexMonths = TheMonths;
                    FlexDays = TheDays;
                    CalculateDateDiff(TodaysDate, BirthDate, .25, out FlexYears, out FlexMonths, out FlexDays);
                    WriteColumnOutput("You have relaxed", FlexYears, FlexMonths, FlexDays);
    
                    Console.WriteLine("");
                    Console.WriteLine("* You may retire in {0} *".PadLeft(38), BirthDate.Year + 65);
                }
            }
    
            public void PlayTheGame()
            {
                DateTime TodaysDate = DateTime.MinValue;
                DateTime BirthDate = DateTime.MinValue;
    
                DisplayIntro();
    
                TodaysDate = PromptForADate("Enter today's date in the form: 3,24,1978  ? ");
                BirthDate = PromptForADate("Enter day of birth (or other day of interest)? ");
    
                DisplayOutput(TodaysDate, BirthDate);
    
            }
        }
        class Program
        {
            static void Main(string[] args)
            {
    
                new Weekday().PlayTheGame();
    
            }
        }
    }
    
    
    ================================================
    FILE: 95_Weekday/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 95_Weekday/csharp/Weekday.csproj
    ================================================
    
      
        Exe
        net6.0
        10
        enable
        enable
      
    
    
    
    ================================================
    FILE: 95_Weekday/csharp/Weekday.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Weekday", "Weekday.csproj", "{6E93B1EC-5E19-47DB-821A-D19F1D0DA982}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{6E93B1EC-5E19-47DB-821A-D19F1D0DA982}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{6E93B1EC-5E19-47DB-821A-D19F1D0DA982}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{6E93B1EC-5E19-47DB-821A-D19F1D0DA982}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{6E93B1EC-5E19-47DB-821A-D19F1D0DA982}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 95_Weekday/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 95_Weekday/java/Weekday.java
    ================================================
    import java.util.Scanner;
    
    /**
     * WEEKDAY
     *
     * Converted from BASIC to Java by Aldrin Misquitta (@aldrinm)
     *
     */
    public class Weekday {
    
    	//TABLE OF VALUES FOR THE MONTHS TO BE USED IN CALCULATIONS.
    	//Dummy value added at index 0, so we can reference directly by the month number value
    	private final static int[] t = new int[]{-1, 0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5};
    
    	public static void main(String[] args) {
    		printIntro();
    
    		Scanner scanner = new Scanner(System.in);
    		System.out.print("ENTER TODAY'S DATE IN THE FORM: 3,24,1979 ");
    		DateStruct todaysDate = readDate(scanner);
    
    		System.out.print("ENTER DAY OF BIRTH (OR OTHER DAY OF INTEREST) ");
    		DateStruct dateOfInterest = readDate(scanner);
    
    		int I1 = (dateOfInterest.year - 1500) / 100;
    		//TEST FOR DATE BEFORE CURRENT CALENDAR.
    		if ((dateOfInterest.year - 1582) >= 0) {
    			int A = I1 * 5 + (I1 + 3) / 4;
    			int I2 = (A - b(A) * 7);
    			int Y2 = (dateOfInterest.year / 100);
    			int Y3 = (dateOfInterest.year - Y2 * 100);
    			A = Y3 / 4 + Y3 + dateOfInterest.day + t[dateOfInterest.month] + I2;
    			calculateAndPrintDayOfWeek(I1, A, todaysDate, dateOfInterest, Y3);
    
    			if ((todaysDate.year * 12 + todaysDate.month) * 31 + todaysDate.day
    					== (dateOfInterest.year * 12 + dateOfInterest.month) * 31 + dateOfInterest.day) {
    				return; //stop the program
    			}
    
    			int I5 = todaysDate.year - dateOfInterest.year;
    			System.out.print("\n");
    			int I6 = todaysDate.month - dateOfInterest.month;
    			int I7 = todaysDate.day - dateOfInterest.day;
    			if (I7 < 0) {
    				I6 = I6 - 1;
    				I7 = I7 + 30;
    			}
    			if (I6 < 0) {
    				I5 = I5 - 1;
    				I6 = I6 + 12;
    			}
    			if (I5 < 0) {
    				return; //do nothing. end the program
    			} else {
    				if (I7 != 0) {
    					printHeadersAndAge(I5, I6, I7);
    				} else {
    					if (I6 != 0) {
    						printHeadersAndAge(I5, I6, I7);
    					} else {
    						System.out.println("***HAPPY BIRTHDAY***");
    						printHeadersAndAge(I5, I6, I7);
    					}
    				}
    			}
    
    			int A8 = (I5 * 365) + (I6 * 30) + I7 + (I6 / 2);
    			int K5 = I5;
    			int K6 = I6;
    			int K7 = I7;
    			//CALCULATE RETIREMENT DATE.
    			int E = dateOfInterest.year + 65;
    			// CALCULATE TIME SPENT IN THE FOLLOWING FUNCTIONS.
    			float F = 0.35f;
    			System.out.printf("%-28s", "YOU HAVE SLEPT");
    			DateStruct scratchDate = new DateStruct(K6, K7, K5); //K5 is a temp year, K6 is month, K7 is day
    			printStatisticRow(F, A8, scratchDate);
    			K5 = scratchDate.year;
    			K6 = scratchDate.month;
    			K7 = scratchDate.day;
    
    			F = 0.17f;
    			System.out.printf("%-28s", "YOU HAVE EATEN");
    
    			scratchDate = new DateStruct(K6, K7, K5);
    			printStatisticRow(F, A8, scratchDate);
    			K5 = scratchDate.year;
    			K6 = scratchDate.month;
    			K7 = scratchDate.day;
    
    			F = 0.23f;
    			if (K5 > 3) {
    				if (K5 > 9) {
    					System.out.printf("%-28s", "YOU HAVE WORKED/PLAYED");
    				} else {
    					System.out.printf("%-28s", "YOU HAVE PLAYED/STUDIED");
    				}
    			} else {
    				System.out.printf("%-28s", "YOU HAVE PLAYED");
    			}
    
    			scratchDate = new DateStruct(K6, K7, K5);
    			printStatisticRow(F, A8, scratchDate);
    			K5 = scratchDate.year;
    			K6 = scratchDate.month;
    			K7 = scratchDate.day;
    
    			if (K6 == 12) {
    				K5 = K5 + 1;
    				K6 = 0;
    			}
    			System.out.printf("%-28s%14s%14s%14s%n", "YOU HAVE RELAXED", K5, K6, K7);
    			System.out.printf("%16s***  YOU MAY RETIRE IN %s ***%n", " ", E);
    			System.out.printf("%n%n%n%n%n");
    		} else {
    			System.out.println("NOT PREPARED TO GIVE DAY OF WEEK PRIOR TO MDLXXXII.");
    		}
    	}
    
    
    	private static void printStatisticRow(float F, int A8, DateStruct scratchDate) {
    		int K1 = (int) (F * A8);
    		int I5 = K1 / 365;
    		K1 = K1 - (I5 * 365);
    		int I6 = K1 / 30;
    		int I7 = K1 - (I6 * 30);
    		int K5 = scratchDate.year - I5;
    		int K6 = scratchDate.month - I6;
    		int K7 = scratchDate.day - I7;
    		if (K7 < 0) {
    			K7 = K7 + 30;
    			K6 = K6 - 1;
    		}
    		if (K6 <= 0) {
    			K6 = K6 + 12;
    			K5 = K5 - 1;
    		}
    		//to return the updated values of K5, K6, K7 we send them through the scratchDate
    		scratchDate.year = K5;
    		scratchDate.month = K6;
    		scratchDate.day = K7;
    		System.out.printf("%14s%14s%14s%n", I5, I6, I7);
    	}
    
    	private static void printHeadersAndAge(int I5, int I6, int I7) {
    		System.out.printf("%14s%14s%14s%14s%14s%n", " ", " ", "YEARS", "MONTHS", "DAYS");
    		System.out.printf("%14s%14s%14s%14s%14s%n", " ", " ", "-----", "------", "----");
    		System.out.printf("%-28s%14s%14s%14s%n", "YOUR AGE (IF BIRTHDATE)", I5, I6, I7);
    	}
    
    	private static void calculateAndPrintDayOfWeek(int i1, int a, DateStruct dateStruct, DateStruct dateOfInterest, int y3) {
    		int b = (a - b(a) * 7) + 1;
    		if (dateOfInterest.month > 2) {
    			printDayOfWeek(dateStruct, dateOfInterest, b);
    		} else {
    			if (y3 == 0) {
    				int aa = i1 - 1;
    				int t1 = aa - a(aa) * 4;
    				if (t1 == 0) {
    					if (b != 0) {
    						b = b - 1;
    						printDayOfWeek(dateStruct, dateOfInterest, b);
    					} else {
    						b = 6;
    						b = b - 1;
    						printDayOfWeek(dateStruct, dateOfInterest, b);
    					}
    				}
    			}
    		}
    	}
    
    	/**
    	 * PRINT THE DAY OF THE WEEK THE DATE FALLS ON.
    	 */
    	private static void printDayOfWeek(DateStruct dateStruct, DateStruct dateOfInterest, int b) {
    		if (b == 0) {
    			b = 7;
    		}
    		if ((dateStruct.year * 12 + dateStruct.month) * 31
    				+ dateStruct.day
    				<
    				(dateOfInterest.year * 12
    						+ dateOfInterest.month) * 31 + dateOfInterest.day) {
    			System.out.printf("%s / %s / %s WILL BE A ", dateOfInterest.month, dateOfInterest.day, dateOfInterest.year);
    		} else if ((dateStruct.year * 12 + dateStruct.month) * 31
    				+ dateStruct.day == (dateOfInterest.year * 12 + dateOfInterest.month)
    				* 31 + dateOfInterest.day) {
    			System.out.printf("%s / %s / %s IS A ", dateOfInterest.month, dateOfInterest.day, dateOfInterest.year);
    		} else {
    			System.out.printf("%s / %s / %s WAS A ", dateOfInterest.month, dateOfInterest.day, dateOfInterest.year);
    		}
    		switch (b) {
    			case 1:
    				System.out.println("SUNDAY.");
    				break;
    			case 2:
    				System.out.println("MONDAY.");
    				break;
    			case 3:
    				System.out.println("TUESDAY.");
    				break;
    			case 4:
    				System.out.println("WEDNESDAY.");
    				break;
    			case 5:
    				System.out.println("THURSDAY.");
    				break;
    			case 6:
    				if (dateOfInterest.day == 13) {
    					System.out.println("FRIDAY THE THIRTEENTH---BEWARE!");
    				} else {
    					System.out.println("FRIDAY.");
    				}
    				break;
    			case 7:
    				System.out.println("SATURDAY.");
    				break;
    		}
    	}
    
    	private static int a(int a) {
    		return a / 4;
    	}
    
    	private static int b(int a) {
    		return a / 7;
    	}
    
    
    	private static void printIntro() {
    		System.out.println("                                WEEKDAY");
    		System.out.println("              CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
    		System.out.println("\n\n\n");
    		System.out.println("WEEKDAY IS A COMPUTER DEMONSTRATION THAT");
    		System.out.println("GIVES FACTS ABOUT A DATE OF INTEREST TO YOU.");
    		System.out.println("\n");
    	}
    
    	/**
    	 * Read user input for a date, do some validation and return a simple date structure
    	 */
    	private static DateStruct readDate(Scanner scanner) {
    		boolean done = false;
    		int mm = 0, dd = 0, yyyy = 0;
    		while (!done) {
    			String input = scanner.next();
    			String[] tokens = input.split(",");
    			if (tokens.length < 3) {
    				System.out.println("DATE EXPECTED IN FORM: 3,24,1979 - RETRY INPUT LINE");
    			} else {
    				try {
    					mm = Integer.parseInt(tokens[0]);
    					dd = Integer.parseInt(tokens[1]);
    					yyyy = Integer.parseInt(tokens[2]);
    					done = true;
    				} catch (NumberFormatException nfe) {
    					System.out.println("NUMBER EXPECTED - RETRY INPUT LINE");
    				}
    			}
    		}
    		return new DateStruct(mm, dd, yyyy);
    	}
    
    	/**
    	 * Convenience date structure to hold user date input
    	 */
    	private static class DateStruct {
    		int month;
    		int day;
    		int year;
    
    		public DateStruct(int month, int day, int year) {
    			this.month = month;
    			this.day = day;
    			this.year = year;
    		}
    	}
    
    }
    
    
    ================================================
    FILE: 95_Weekday/javascript/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells)
    
    
    ================================================
    FILE: 95_Weekday/javascript/weekday.html
    ================================================
    
    
    WEEKDAY
    
    
    
    
    
    
    
    
    
    
    ================================================
    FILE: 95_Weekday/javascript/weekday.js
    ================================================
    // WEEKDAY
    //
    // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
    //
    
    /**
     * Print given string to the end of the "output" element.
     * @param str
     */
    function print(str) {
        document.getElementById("output").appendChild(document.createTextNode(str));
    }
    
    /**
     * Obtain user input
     * @returns {Promise}
     */
    function input() {
        return new Promise(function (resolve) {
            const input_element = document.createElement("INPUT");
    
            print("? ");
            input_element.setAttribute("type", "text");
            input_element.setAttribute("length", "50");
            document.getElementById("output").appendChild(input_element);
            input_element.focus();
            input_element.addEventListener("keydown", function (event) {
                if (event.keyCode === 13) {
                    const input_str = input_element.value;
                    document.getElementById("output").removeChild(input_element);
                    print(input_str);
                    print("\n");
                    resolve(input_str);
                }
            });
        });
    }
    
    /**
     * Create a string consisting of the given number of spaces
     * @param spaceCount
     * @returns {string}
     */
    function tab(spaceCount) {
        let str = "";
        while (spaceCount-- > 0)
            str += " ";
        return str;
    }
    
    const MONTHS_PER_YEAR = 12;
    const DAYS_PER_COMMON_YEAR = 365;
    const DAYS_PER_IDEALISED_MONTH = 30;
    const MAXIMUM_DAYS_PER_MONTH = 31;
    // In a common (non-leap) year the day of the week for the first of each month moves by the following amounts.
    const COMMON_YEAR_MONTH_OFFSET = [0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5];
    
    /**
     * Date representation.
     */
    class DateStruct {
        #year;
        #month;
        #day;
    
        /**
         * Build a DateStruct
         * @param {number} year
         * @param {number} month
         * @param {number} day
         */
        constructor(year, month, day) {
            this.#year = year;
            this.#month = month;
            this.#day = day;
        }
    
        get year() {
            return this.#year;
        }
    
        get month() {
            return this.#month;
        }
    
        get day() {
            return this.#day;
        }
    
        /**
         * Determine if the date could be a Gregorian date.
         * Be aware the Gregorian calendar was not introduced in all places at once,
         * see https://en.wikipedia.org/wiki/Gregorian_calendar
         * @returns {boolean} true if date could be Gregorian; otherwise false.
         */
        isGregorianDate() {
            let result = false;
            if (this.#year > 1582) {
                result = true;
            } else if (this.#year === 1582) {
                if (this.#month > 10) {
                    result = true;
                } else if (this.#month === 10 && this.#day >= 15) {
                    result = true;
                }
            }
            return result;
        }
    
        /**
         * The following performs a hash on the day parts which guarantees that
         * 1. different days will return different numbers
         * 2. the numbers returned are ordered.
         * @returns {number}
         */
        getNormalisedDay() {
            return (this.year * MONTHS_PER_YEAR + this.month) * MAXIMUM_DAYS_PER_MONTH + this.day;
        }
    
        /**
         * Determine the day of the week.
         * This calculation returns a number between 1 and 7 where Sunday=1, Monday=2, ..., Saturday=7.
         * @returns {number} Value between 1 and 7 representing Sunday to Saturday.
         */
        getDayOfWeek() {
            // Calculate an offset based on the century part of the year.
            const centuriesSince1500 = Math.floor((this.year - 1500) / 100);
            let centuryOffset = centuriesSince1500 * 5 + (centuriesSince1500 + 3) / 4;
            centuryOffset = Math.floor(centuryOffset % 7);
    
            // Calculate an offset based on the shortened two digit year.
            // January 1st moves forward by approximately 1.25 days per year
            const yearInCentury = this.year % 100;
            const yearInCenturyOffsets = yearInCentury / 4 + yearInCentury;
    
            // combine offsets with day and month
            let dayOfWeek = centuryOffset + yearInCenturyOffsets + this.day + COMMON_YEAR_MONTH_OFFSET[this.month - 1];
    
            dayOfWeek = Math.floor(dayOfWeek % 7) + 1;
            if (this.month <= 2 && this.isLeapYear()) {
                dayOfWeek--;
            }
            if (dayOfWeek === 0) {
                dayOfWeek = 7;
            }
            return dayOfWeek;
        }
    
        /**
         * Determine if the given year is a leap year.
         * @returns {boolean}
         */
        isLeapYear() {
            if ((this.year % 4) !== 0) {
                return false;
            } else if ((this.year % 100) !== 0) {
                return true;
            } else if ((this.year % 400) !== 0) {
                return false;
            }
            return true;
        }
    
        /**
         * Returns a US formatted date, i.e. Month/Day/Year.
         * @returns {string}
         */
        toString() {
            return this.#month + "/" + this.#day + "/" + this.#year;
        }
    }
    
    /**
     * Duration representation.
     * Note: this class only handles positive durations well
     */
    class Duration {
        #years;
        #months;
        #days;
    
        /**
         * Build a Duration
         * @param {number} years
         * @param {number} months
         * @param {number} days
         */
        constructor(years, months, days) {
            this.#years = years;
            this.#months = months;
            this.#days = days;
            this.#fixRanges();
        }
    
        get years() {
            return this.#years;
        }
    
        get months() {
            return this.#months;
        }
    
        get days() {
            return this.#days;
        }
    
        clone() {
            return new Duration(this.#years, this.#months, this.#days);
        }
    
        /**
         * Adjust Duration by removing years, months and days from supplied Duration.
         * This is a naive calculation which assumes all months are 30 days.
         * @param {Duration} timeToRemove
         */
        remove(timeToRemove) {
            this.#years -= timeToRemove.years;
            this.#months -= timeToRemove.months;
            this.#days -= timeToRemove.days;
            this.#fixRanges();
        }
    
        /**
         * Move days and months into expected range.
         */
        #fixRanges() {
            if (this.#days < 0) {
                this.#days += DAYS_PER_IDEALISED_MONTH;
                this.#months--;
            }
            if (this.#months < 0) {
                this.#months += MONTHS_PER_YEAR;
                this.#years--;
            }
        }
    
        /**
         * Computes an approximation of the days covered by the duration.
         * The calculation assumes all years are 365 days, months are 30 days each,
         * and adds on an extra bit the more months that have passed.
         * @returns {number}
         */
        getApproximateDays() {
            return (
                (this.#years * DAYS_PER_COMMON_YEAR)
                + (this.#months * DAYS_PER_IDEALISED_MONTH)
                + this.#days
                + Math.floor(this.#months / 2)
            );
        }
    
        /**
         * Returns a formatted duration with tab separated values, i.e. Years\tMonths\tDays.
         * @returns {string}
         */
        toString() {
            return this.#years + "\t" + this.#months + "\t" + this.#days;
        }
    
        /**
         * Determine approximate Duration between two dates.
         * This is a naive calculation which assumes all months are 30 days.
         * @param {DateStruct} date1
         * @param {DateStruct} date2
         * @returns {Duration}
         */
        static between(date1, date2) {
            let years = date1.year - date2.year;
            let months = date1.month - date2.month;
            let days = date1.day - date2.day;
            return new Duration(years, months, days);
        }
    
        /**
         * Calculate years, months and days as factor of days.
         * This is a naive calculation which assumes all months are 30 days.
         * @param dayCount Total day to convert to a duration
         * @param factor   Factor to apply when calculating the duration
         * @returns {Duration}
         */
        static fromDays(dayCount, factor) {
            let totalDays = Math.floor(factor * dayCount);
            const years = Math.floor(totalDays / DAYS_PER_COMMON_YEAR);
            totalDays -= years * DAYS_PER_COMMON_YEAR;
            const months = Math.floor(totalDays / DAYS_PER_IDEALISED_MONTH);
            const days = totalDays - (months * DAYS_PER_IDEALISED_MONTH);
            return new Duration(years, months, days);
        }
    }
    
    // Main control section
    async function main() {
        /**
         * Reads a date, and extracts the date information.
         * This expects date parts to be comma separated, using US date ordering,
         * i.e. Month,Day,Year.
         * @returns {Promise}
         */
        async function inputDate() {
            let dateString = await input();
            const month = parseInt(dateString);
            const day = parseInt(dateString.substr(dateString.indexOf(",") + 1));
            const year = parseInt(dateString.substr(dateString.lastIndexOf(",") + 1));
            return new DateStruct(year, month, day);
        }
    
        /**
         * Obtain text for the day of the week.
         * @param {DateStruct} date
         * @returns {string}
         */
        function getDayOfWeekText(date) {
            const dayOfWeek = date.getDayOfWeek();
            let dayOfWeekText = "";
            switch (dayOfWeek) {
                case 1:
                    dayOfWeekText = "SUNDAY.";
                    break;
                case 2:
                    dayOfWeekText = "MONDAY.";
                    break;
                case 3:
                    dayOfWeekText = "TUESDAY.";
                    break;
                case 4:
                    dayOfWeekText = "WEDNESDAY.";
                    break;
                case 5:
                    dayOfWeekText = "THURSDAY.";
                    break;
                case 6:
                    if (date.day === 13) {
                        dayOfWeekText = "FRIDAY THE THIRTEENTH---BEWARE!";
                    } else {
                        dayOfWeekText = "FRIDAY.";
                    }
                    break;
                case 7:
                    dayOfWeekText = "SATURDAY.";
                    break;
            }
            return dayOfWeekText;
        }
    
        print(tab(32) + "WEEKDAY\n");
        print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
        print("\n");
        print("\n");
        print("\n");
        print("WEEKDAY IS A COMPUTER DEMONSTRATION THAT\n");
        print("GIVES FACTS ABOUT A DATE OF INTEREST TO YOU.\n");
        print("\n");
        print("ENTER TODAY'S DATE IN THE FORM: 3,24,1979  ");
        const today = await inputDate();
        // This program determines the day of the week
        //  for a date after 1582
        print("ENTER DAY OF BIRTH (OR OTHER DAY OF INTEREST)");
        const dateOfBirth = await inputDate();
        print("\n");
        // Test for date before current calendar.
        if (!dateOfBirth.isGregorianDate()) {
            print("NOT PREPARED TO GIVE DAY OF WEEK PRIOR TO X.XV.MDLXXXII.\n");
        } else {
            const normalisedToday = today.getNormalisedDay();
            const normalisedDob = dateOfBirth.getNormalisedDay();
    
            let dayOfWeekText = getDayOfWeekText(dateOfBirth);
            if (normalisedToday < normalisedDob) {
                print(dateOfBirth + " WILL BE A " + dayOfWeekText + "\n");
            } else if (normalisedToday === normalisedDob) {
                print(dateOfBirth + " IS A " + dayOfWeekText + "\n");
            } else {
                print(dateOfBirth + " WAS A " + dayOfWeekText + "\n");
            }
    
            if (normalisedToday !== normalisedDob) {
                print("\n");
                let differenceBetweenDates = Duration.between(today, dateOfBirth);
                if (differenceBetweenDates.years >= 0) {
                    if (differenceBetweenDates.days === 0 && differenceBetweenDates.months === 0) {
                        print("***HAPPY BIRTHDAY***\n");
                    }
                    print("                        \tYEARS\tMONTHS\tDAYS\n");
                    print("                        \t-----\t------\t----\n");
                    print("YOUR AGE (IF BIRTHDATE) \t" + differenceBetweenDates + "\n");
    
                    const approximateDaysBetween = differenceBetweenDates.getApproximateDays();
                    const unaccountedTime = differenceBetweenDates.clone();
    
                    // 35% sleeping
                    const sleepTimeSpent = Duration.fromDays(approximateDaysBetween, 0.35);
                    print("YOU HAVE SLEPT \t\t\t" + sleepTimeSpent + "\n");
                    unaccountedTime.remove(sleepTimeSpent);
    
                    // 17% eating
                    const eatenTimeSpent = Duration.fromDays(approximateDaysBetween, 0.17);
                    print("YOU HAVE EATEN \t\t\t" + eatenTimeSpent + "\n");
                    unaccountedTime.remove(eatenTimeSpent);
    
                    // 23% working, studying or playing
                    const workPlayTimeSpent = Duration.fromDays(approximateDaysBetween, 0.23);
                    if (unaccountedTime.years <= 3) {
                        print("YOU HAVE PLAYED \t\t" + workPlayTimeSpent + "\n");
                    } else if (unaccountedTime.years <= 9) {
                        print("YOU HAVE PLAYED/STUDIED \t" + workPlayTimeSpent + "\n");
                    } else {
                        print("YOU HAVE WORKED/PLAYED \t\t" + workPlayTimeSpent + "\n");
                    }
                    unaccountedTime.remove(workPlayTimeSpent);
    
                    // Remaining time spent relaxing
                    print("YOU HAVE RELAXED \t\t" + unaccountedTime + "\n");
    
                    const retirementYear = dateOfBirth.year + 65;
                    print("\n");
                    print(tab(16) + "***  YOU MAY RETIRE IN " + retirementYear + " ***\n");
                    print("\n");
                }
            }
        }
        print("\n");
        print("\n");
        print("\n");
        print("\n");
        print("\n");
    }
    
    main();
    
    
    ================================================
    FILE: 95_Weekday/kotlin/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Kotlin](https://kotlinlang.org/)
    
    
    ================================================
    FILE: 95_Weekday/lua/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Lua](https://www.lua.org/)
    
    
    ================================================
    FILE: 95_Weekday/perl/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Perl](https://www.perl.org/)
    
    I have replaced the manual date logic with Perl built-ins to the extent
    possible. Unfortunately the kind of date math involved in the "time
    spent doing ..." functionality is not well-defined, so I have been
    forced to retain the original logic here. Sigh.
    
    You can use any punctuation character you please in the date
    input. So something like 2/29/2020 is perfectly acceptable.
    
    It would also have been nice to produce a localized version that
    supports day/month/year or year-month-day input, but that didn't happen.
    
    Also nice would have been language-specific output -- especially if it
    could have accommodated regional differences in which day of the week or
    month is unlucky.
    
    Tom Wyant
    
    
    ================================================
    FILE: 95_Weekday/perl/weekday.pl
    ================================================
    #!/usr/bin/env perl
    
    use 5.010;      # To get 'state' and 'say'
    
    use strict;     # Require explicit declaration of variables
    use warnings;   # Enable optional compiler warnings
    
    use English;    # Use more friendly names for Perl's magic variables
    use Term::ReadLine;     # Prompt and return user input
    use Time::Local qw{ timelocal };    # date-time to epoch
    # FIXME timelocal() is too smart for its own good in the interpretation
    # of years, and caused a bunch of Y2020 problems in Perl code that used
    # it. I believe that this script avoids these problems (which only occur
    # if the year is less than 1000), but it is probably safer in general to
    # use timelocal_modern() or timelocal_posix(). These are also exported
    # by Time::Local, but only by versions 1.28 and 1.30 respectively. This
    # means that they only come (by default) with Perl 5.30 and 5.34
    # respectively. Now, Time::Local is a dual-life module, meaning it can
    # be upgraded from the version packaged with older Perls. But I did not
    # want to assume that it HAD been upgraded. Caveat coder.
    use Time::Piece;    # O-O epoch to date-time, plus formatting
    
    our $VERSION = '0.000_01';
    
    print <<'EOD';
    
                                    WEEKDAY
                   Creative Computing  Morristown, New Jersey
    
    
    
    WEEKDAY is a computer demonstration that
    gives facts about a date of interest to you.
    
    EOD
    
    my $now = localtime;
    my $default_date = join ',', map { $now->$_() } qw{ mon mday year };
    
    my $today = get_date(
        "Enter today's date in the form month,day,year (default: $default_date): ",
        "Please enter month,day,year or return for default\n",
        $default_date,
    );
    
    my $birthday = get_date(
        'Ender day of birth (or other day of interest): ',
        "Please enter month,day,year\n",
    );
    
    say '';
    printf "%d/%d/%d %s a %s\n", $birthday->mon, $birthday->mday,
        $birthday->year, tense( $today, $birthday),
        ( $birthday->mday == 13 && $birthday->wday == 6 ) ?
            $birthday->fullday . ' the thirteenth --- Beware!' :
            $birthday->fullday . '.';
    
    if ( $birthday->epoch <= $today->epoch ) {
    
        say '*** Happy Birthday! ***'
            if $birthday->mon == $today->mon &&
                $birthday->mday == $today->mday;
    
        print <<'EOD';
                            Years   Months  Days
                            -----   ------  ----
    EOD
    
        my @delta = map { $today->$_() - $birthday->$_() } qw{ year mon mday };
        if ( $delta[2] < 0 ) {
            $delta[2] += 30;
            $delta[1] -= 1;
        }
        if ( $delta[1] < 0 ) {
            $delta[1] += 12;
            $delta[0] -= 1;
        }
        my @residue = @delta;
    
        my $delta_days = 365 * $delta[0] + 30 * $delta[1] + $delta[2];
    
        display_ymd( 'Your age (if birthdate)', compute_ymd( $delta_days ) );
        display_ymd( 'You have slept', compute_ymd( $delta_days, 0.35,
                \@residue ) );
        display_ymd( 'You have eaten', compute_ymd( $delta_days, 0.17,
                \@residue ) );
        display_ymd(
            $residue[0] > 9 ? 'You have worked/played' :
            $residue[0] > 3 ? 'You have played/studied' :
                'You have played',
            compute_ymd( $delta_days, 0.23,
                \@residue ) );
        display_ymd( 'You have relaxed', \@residue );
    
        say '';
        say "\t\t*** You may retire in @{[ $birthday->year + 65 ]} ***";
    }
    
    say '';
    
    sub compute_ymd {
        my ( $delta_days, $fract, $residue ) = @ARG;
        my $days = defined $fract ? int ( $delta_days * $fract ) : $delta_days;
        my $years = int( $days / 365 );
        $days -= $years * 365;
        my $months = int( $days / 30 );
        $days -= $months * 30;
    
        if ( $residue ) {
            $residue->[2] -= $days;
            if ( $residue->[2] < 0 ) {
                $residue->[2] += 30;
                $residue->[1] -= 1;
            }
            $residue->[1] -= $months;
            if ( $residue->[1] < 0 ) {
                $residue->[1] += 12;
                $residue->[0] -= 1;
            }
            $residue->[0] -= $years;
        }
    
        return [ $years, $months, $days ];
    }
    
    sub display_ymd {
        my ( $label, $ymd ) = @ARG;
        printf "%-24s%4d%6d%8d\n", $label, @{ $ymd };
        return;
    }
    
    sub get_date {
        my ( $prompt, $warning, $default ) = @ARG;
        my ( $month, $day, $year ) = split qr< [[:punct:]] >smx, get_input(
            $prompt,
            sub {
                return 0 unless m/ \A (?: [0-9]+ [[:punct:]] ){2} ( [0-9]+ ) \z /smx;
                return 1 if $1 >= 1582;
                warn "Not prepared to give day of week prior to MDLXXXII.\n";
                return 0;
            },
            $warning,
            $default,
        );
        return localtime timelocal( 0, 0, 0, $day, $month - 1, $year );
    }
    
    sub tense {
        my ( $today, $birthday ) = @ARG;
        my $cmp = $birthday->epoch <=> $today->epoch
            or return 'is';
        return $cmp < 0 ? 'was' : 'will be';
    }
    
    # Get input from the user. The arguments are:
    # * The prompt
    # * A reference to validation code. This code receives the response in
    #   $ARG and returns true for a valid response.
    # * A warning to print if the response is not valid. This must end in a
    #   return.
    # * A default to return if the user simply presses .
    # The first valid response is returned. An end-of-file terminates the
    # script.
    sub get_input {
        my ( $prompt, $validate, $warning, $default ) = @ARG;
    
        # If no validator is passed, default to one that always returns
        # true.
        $validate ||= sub { 1 };
    
        # Create the readline object. The 'state' causes the variable to be
        # initialized only once, no matter how many times this subroutine is
        # called. The do { ... } is a compound statement used because we
        # need to tweak the created object before we store it.
        state $term = do {
            my $obj = Term::ReadLine->new( 'reverse' );
            $obj->ornaments( 0 );
            $obj;
        };
    
        while ( 1 ) {   # Iterate indefinitely
    
            # Read the input into the topic variable, localized to prevent
            # Spooky Action at a Distance. We exit on undef, which signals
            # end-of-file.
            exit unless defined( local $ARG = $term->readline( $prompt ) );
    
            # Return the default if it exists AND we got an empty line
            return $default if defined( $default ) && $ARG eq '';
    
            # Return the input if it is valid.
            return $ARG if $validate->();
    
            # Issue the warning, and go around the merry-go-round again.
            warn $warning;
        }
    }
    
    __END__
    
    =head1 TITLE
    
    weekday - Play the game 'Weekday' from Basic Computer Games
    
    =head1 SYNOPSIS
    
     weekday.pl
    
    =head1 DETAILS
    
    This Perl script is a port of weekday.bas, which is the 95th entry in
    Basic Computer Games.
    
    I have replaced the manual date logic with Perl built-ins to the extent
    possible. Unfortunately the kind of date math involved in the "time
    spent doing ..." functionality is not well-defined, so I have been
    forced to retain the original logic here. Sigh.
    
    You can use any punctuation character you please in the date
    input. So something like 2/29/2020 is perfectly acceptable.
    
    It would also have been nice to produce a localized version that
    supports day/month/year or year-month-day input, but that didn't happen.
    
    Also nice would have been language-specific output -- especially if it
    could have accommodated regional differences in which day of the week or
    month is unlucky.
    
    =head1 PORTED BY
    
    Thomas R. Wyant, III F
    
    =head1 COPYRIGHT AND LICENSE
    
    Copyright (C) 2022 by Thomas R. Wyant, III
    
    This program is free software; you can redistribute it and/or modify it
    under the same terms as Perl 5.10.0. For more details, see the Artistic
    License 1.0 at
    L, and/or the
    Gnu GPL at L.
    
    This program is distributed in the hope that it will be useful, but
    without any warranty; without even the implied warranty of
    merchantability or fitness for a particular purpose.
    
    =cut
    
    # ex: set expandtab tabstop=4 textwidth=72 :
    
    
    ================================================
    FILE: 95_Weekday/python/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Python](https://www.python.org/about/)
    
    
    ================================================
    FILE: 95_Weekday/python/weekday.py
    ================================================
    """
    WEEKDAY
    
    Calculates which weekday an entered date is.
    
    Also estimates how long a person has done certain activities, if they
    entered their birthday.
    
    Also calculates the year of retirement, assuming retiring at age 65.
    
    Ported by Dave LeCompte.
    """
    
    import datetime
    from typing import Tuple
    
    GET_TODAY_FROM_SYSTEM = True
    
    
    def get_date_from_user(prompt: str) -> Tuple[int, int, int]:
        while True:
            print(prompt)
            date_str = input()
            try:
                month_num, day_num, year_num = (int(x) for x in date_str.split(","))
                return month_num, day_num, year_num
            except Exception:
                print("I COULDN'T UNDERSTAND THAT. TRY AGAIN.")
    
    
    def get_date_from_system() -> Tuple[int, int, int]:
        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",
            3: "TUESDAY",
            4: "WEDNESDAY",
            5: "THURSDAY",
            6: "FRIDAY",
            7: "SATURDAY",
        }
    
        return day_names[weekday_index]
    
    
    def previous_day(b) -> int:
        if b == 0:
            b = 6
        return b - 1
    
    
    def is_leap_year(year: int) -> bool:
        if (year % 4) != 0:
            return False
        return True if (year % 100) != 0 else year % 400 == 0
    
    
    def adjust_day_for_leap_year(b, year):
        if is_leap_year(year):
            b = previous_day(b)
        return b
    
    
    def adjust_weekday(b, month, year):
        if month <= 2:
            b = adjust_day_for_leap_year(b, year)
        if b == 0:
            b = 7
        return b
    
    
    def calc_day_value(year, month, day):
        return (year * 12 + month) * 31 + 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 = days_available // 365
        days_available -= years_used * 365
        months_used = days_available // 30
        days_used = days_available - (months_used * 30)
        years_remain = years_remain - years_used
        months_remain = months_remain - months_used
        days_remain = days_remain - days_used
    
        while days_remain < 0:
            days_remain += 30
            months_remain -= 1
    
        while months_remain < 0 and years_remain > 0:
            months_remain += 12
            years_remain -= 1
        return years_remain, months_remain, days_remain, years_used, months_used, days_used
    
    
    def time_report(msg, years, months, days):
        leading_spaces = 23 - len(msg)
        print(" " * leading_spaces + f"{msg}\t{years}\t{months}\t{days}")
    
    
    def make_occupation_label(years):
        if years <= 3:
            return "PLAYED"
        elif years <= 9:
            return "PLAYED/STUDIED"
        else:
            return "WORKED/PLAYED"
    
    
    def calculate_day_of_week(year, month, day):
        # Initial values for months
        month_table = [0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5]
    
        i1 = int((year - 1500) / 100)
        a = i1 * 5 + (i1 + 3) / 4
        i2 = int(a - int(a / 7) * 7)
        y2 = int(year / 100)
        y3 = int(year - y2 * 100)
        a = y3 / 4 + y3 + day + month_table[month - 1] + i2
        b = int(a - int(a / 7) * 7) + 1
        b = adjust_weekday(b, month, year)
    
        return b
    
    
    def end() -> None:
        for _ in range(5):
            print()
    
    
    def main() -> None:
        print(" " * 32 + "WEEKDAY")
        print(" " * 15 + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n\n\n")
        print("WEEKDAY IS A COMPUTER DEMONSTRATION THAT")
        print("GIVES FACTS ABOUT A DATE OF INTEREST TO YOU.")
        print()
    
        if GET_TODAY_FROM_SYSTEM:
            month_today, day_today, year_today = get_date_from_system()
        else:
            month_today, day_today, year_today = get_date_from_user(
                "ENTER TODAY'S DATE IN THE FORM: 3,24,1979"
            )
    
        # This program determines the day of the week
        # for a date after 1582
    
        print()
    
        month, day, year = get_date_from_user(
            "ENTER DAY OF BIRTH (OR OTHER DAY OF INTEREST) (like MM,DD,YYYY)"
        )
    
        print()
    
        # Test for date before current calendar
        if year < 1582:
            print("NOT PREPARED TO GIVE DAY OF WEEK PRIOR TO MDLXXXII.")
            end()
            return
    
        b = calculate_day_of_week(year, month, day)
    
        today_day_value = calc_day_value(year_today, month_today, day_today)
        target_day_value = calc_day_value(year, month, day)
    
        is_today = False
    
        if today_day_value < target_day_value:
            label = "WILL BE A"
        elif today_day_value == target_day_value:
            label = "IS A"
            is_today = True
        else:
            label = "WAS A"
    
        day_name = get_day_of_week(b, day)
    
        # print the day of the week the date falls on.
        print(f"{month}/{day}/{year} {label} {day_name}.")
    
        if is_today:
            # nothing to report for today
            end()
            return
    
        print()
    
        el_years = year_today - year
        el_months = month_today - month
        el_days = day_today - day
    
        if el_days < 0:
            el_months = el_months - 1
            el_days = el_days + 30
        if el_months < 0:
            el_years = el_years - 1
            el_months = el_months + 12
        if el_years < 0:
            # target date is in the future
            end()
            return
    
        if (el_months == 0) and (el_days == 0):
            print("***HAPPY BIRTHDAY***")
    
        # print report
        print(" " * 23 + "\tYEARS\tMONTHS\tDAYS")
        print(" " * 23 + "\t-----\t------\t----")
        print(f"YOUR AGE (IF BIRTHDATE)\t{el_years}\t{el_months}\t{el_days}")
    
        life_days = (el_years * 365) + (el_months * 30) + el_days + int(el_months / 2)
        rem_years = el_years
        rem_months = el_months
        rem_days = el_days
    
        rem_years, rem_months, rem_days, used_years, used_months, used_days = deduct_time(
            0.35, life_days, rem_years, rem_months, rem_days
        )
        time_report("YOU HAVE SLEPT", used_years, used_months, used_days)
        rem_years, rem_months, rem_days, used_years, used_months, used_days = deduct_time(
            0.17, life_days, rem_years, rem_months, rem_days
        )
        time_report("YOU HAVE EATEN", used_years, used_months, used_days)
    
        label = make_occupation_label(rem_years)
        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(f"YOU HAVE {label}", used_years, used_months, used_days)
        time_report("YOU HAVE RELAXED", rem_years, rem_months, rem_days)
    
        print()
    
        # Calculate retirement date
        e = year + 65
        print(" " * 16 + f"***  YOU MAY RETIRE IN {e} ***")
        end()
    
    
    if __name__ == "__main__":
        main()
    
    
    ================================================
    FILE: 95_Weekday/ruby/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Ruby](https://www.ruby-lang.org/en/)
    
    
    ================================================
    FILE: 95_Weekday/rust/Cargo.toml
    ================================================
    [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]
    
    
    ================================================
    FILE: 95_Weekday/rust/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Rust](https://www.rust-lang.org/) by Anthony Rubick [AnthonyMichaelTDM](https://github.com/AnthonyMichaelTDM)
    
    
    ================================================
    FILE: 95_Weekday/rust/src/main.rs
    ================================================
    use std::io::{self, stdout, Write};
    
    struct DATE {
        month: u32,
        day: u32,
        year:u32,
        day_of_week:u32,
    }
    impl DATE {
        /**
         * create new date with given paramets
         */
        fn new(month:u32,day:u32,year:u32,day_of_week:u32) -> DATE {
            return DATE { month: month, day: day, year: year, day_of_week: day_of_week };
        }
        /**
         * create date from user input
         */
        fn new_from_input(prompt:&str) -> DATE {
            //DATA
            let mut raw_date: Vec;
            //get date
            //input loop
            loop {
                //get user input,
                raw_date = get_str_from_user(prompt)
                .split(',')//split it up by ',''s
                .filter_map(|s| s.parse::().ok()).collect();//convert it to numbers, ignore things that fail
                
                //if they didn't give enough data
                if raw_date.len() == 3 { //is it long enough? (3 elements)
                    //are each ones valid things?
                    if (1..=12).contains(&raw_date[0]) { //valid month
                        if (1..=31).contains(&raw_date[1]) { //valid day
                            break;
                        }
                    }
                }
                //otherwise, print error message and go again
                println!("Invalid date, try again!");
            }
    
            //create date
            let mut date =DATE::new(raw_date[0], raw_date[1], raw_date[2], 0);
            date.update_day_of_week();
            //return date
            return date
        }
        /**
         * create a new date from a number of days
         */
        fn new_from_days(days:u32) -> DATE{
            let mut days_remaining = days;
            let d;
            let m;
            let y;
    
            //get the years
            y=(days_remaining as f64 / 365.25) as u32;
            //deduct
            days_remaining = (days_remaining as f64 % 365.25) as u32;
    
            //get months
            m=(days_remaining as f64 / 30.437) as u32;
            //deduct
            days_remaining = (days_remaining as f64 % 30.437) as u32;
    
            //get days
            d = days_remaining;
    
            //return new date
            return DATE::new(m, d, y, 0);
        }
    
        /**
         * caluclates the day of the week (1-7)
         * uses the methodology found here: https://cs.uwaterloo.ca/~alopez-o/math-faq/node73.html
         */
        fn update_day_of_week(&mut self) {
            //DATA
            let day = self.day as isize;
            let month = self.month as isize;
            let year = self.year as isize;
            let century = year/100;
            let year_of_century = year - century*100;
            let weekday; //as 0-6
    
            //calculate weekday
            if self.month <= 2 { //if jan or feb
                weekday = (day + (2.6 * ((month+10) as f64)-0.2)as isize - 2*century + (year_of_century-1) + (year_of_century-1)/4 + century/4) % 7;
            } else {
                weekday = (day + (2.6 * ((month-2)  as f64)-0.2)as isize - 2*century + year_of_century     + year_of_century/4     + century/4) % 7;
            }
    
            //update weekday
            self.day_of_week=(weekday+1) as u32; //weekday as 1-7
        }
    
        /**
         * return the string for the weekday
         */
        fn day_of_week_as_string(&self) -> String {
            match self.day_of_week {
                1 => {return String::from("SUNDAY")},
                2 => {return String::from("MONDAY")},
                3 => {return String::from("TUESDAY")},
                4 => {return String::from("WEDNESDAY")},
                5 => {return String::from("THURSDAY")},
                6 => {
                    if self.day == 13 {return String::from("FRIDAY THE THIRTEENTH---BEWARE!")}
                    else {return String::from("FRIDAY")}
                },
                7 => {return String::from("SATURDAY")}, 
                _ => {return String::from("")},
            }
        }
    
        /**
         * is the year a leap_year
         */
        fn _is_leap_year(&self) -> bool{
            if self.year % 4 != 0 {
                return false;
            } else if self.year %100 != 0 {
                return true;
            } else if self.year % 400 != 0 {
                return false;
            } else {
                return true;
            }
        }
    
        /**
         * calculates the day value, number of days the date represents
         */
        fn calc_days(&self) -> u32 {
            return (self.year as f64 * 365.25)as u32 + self.month*30 + self.day + self.month/2;
        }
    
        /**
         * calculates the time difference between self and the passed date,
         */
        fn time_since(&self, other: &DATE) -> Option {
            //DATA
            // /*
            let diff = self.calc_days()as i32 - other.calc_days()as i32;
            if diff < 0 { 
                return None;
            } else {
                return Some(DATE::new_from_days(diff as u32));
            }
        }
    
        /**
         * formats the date in a different format, used for time table
         */
        fn format_ymd(&self, spacer:&str) -> String {
            return format!(
                "{}{}{}{}{}",
                self.year, spacer,
                self.month, spacer,
                self.day,
            );
        }
    }
    impl ToString for DATE {
        fn to_string(&self) -> String {
            return format!("{}/{}/{}",self.month,self.day,self.year);
        }
    }
    
    fn main() {
        //DATA
        let today_date: DATE;
        let other_date:DATE;
        let delta_date:DATE; //represents the difference between the two dates
        let today_value;
        let other_day_value;
        let mut other_is_today = false;
    
        //print welcome
        welcome();
        
        //get todays date
        today_date = DATE::new_from_input("ENTER TODAY'S DATE IN THE FORM: 3,24,1979  ");
        //check todays date
        if today_date.year < 1582 {
            println!("NOT PREPARED TO GIVE DAY OF WEEK PRIOR TO MDLXXXII.");
            return;
        }
    
        println!();
    
        //get other date
        other_date = DATE::new_from_input("ENTER DAY OF BIRTH (OR OTHER DAY OF INTEREST) (like MM,DD,YYYY)");
        //check other date
        if other_date.year < 1582 {
            println!("NOT PREPARED TO GIVE DAY OF WEEK PRIOR TO MDLXXXII.");
            return;
        }
    
        //do some calculations
        today_value = today_date.calc_days();
        other_day_value = other_date.calc_days();
    
        //print the other date in a nice format
        println!(
            "{} {} A {}",
            other_date.to_string(),
            { //use proper tense of To Be
                if today_value < other_day_value {"WILL BE"}
                else if today_value == other_day_value {other_is_today=true;"IS"}
                else {"WAS"}
            },
            other_date.day_of_week_as_string(),
        );
    
        //end if both days are the same
        if other_is_today {
            return;
        } 
    
        //create date representing the difference between the two dates
        delta_date = today_date.time_since(&other_date).unwrap();
    
        //print happy birthday message
        if delta_date.month == 0 && delta_date.day == 0 {
            println!("***HAPPY BIRTHDAY***");
        }
    
        //print report
        println!("
                          \tYEARS\tMONTHS\tDAYS
                          \t-----\t------\t----"
        );
        println!("YOUR AGE (IF BIRTHDATE)\t{}", delta_date.format_ymd("\t"));
        
        //how much have they slept
        println!(
            "YOU HAVE SLEPT\t\t{}",
            DATE::new_from_days( (0.35 * delta_date.calc_days() as f64) as u32).format_ymd("\t"), //35% of their life
        );
    
        //how much they have eaten
        println!(
            "YOU HAVE EATEN\t\t{}",
            DATE::new_from_days( (0.17 * delta_date.calc_days() as f64) as u32).format_ymd("\t"), //17% of their life
        );
    
        //how much they have worked
        println!(
            "YOU HAVE {}\t{}",
            {
                if delta_date.year <= 3 {"PLAYED"}
                else if delta_date.year <= 9 {"PLAYED/STUDIED"}
                else {"WORKED/PLAYED"}
            },
            DATE::new_from_days( (0.23 * delta_date.calc_days() as f64) as u32).format_ymd("\t"), //23% of their life
        );
        //how much they have relaxed
        println!(
            "YOU HAVE RELAXED\t{}",
            DATE::new_from_days( ( (1.0-0.35-0.17-0.23) * delta_date.calc_days() as f64) as u32).format_ymd("\t"), //remaining% of their life
        );
        //when they can retire
        println!(
            "YOU MAY RETIRE IN\t{}",
            DATE::new(other_date.month, other_date.day, other_date.year + 65, 0).time_since(&today_date).unwrap().format_ymd("\t")
        );
    }
    
    
    /**
     * print welome message
     */
    fn welcome() {
        println!("
                                   WEEKDAY
                  CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY
        
        
        
        WEEKDAY IS A COMPUTER DEMONSTRATION THAT
        GIVES FACTS ABOUT A DATE OF INTEREST TO YOU.
        ");
    }
    
    /**
     * gets a string from user input
     */
    fn get_str_from_user(prompt:&str) -> String {
        //DATA
        let mut raw_input = String::new();
    
        //print prompt
        print!("{}",prompt);
        //flust std out //allows prompt to be on same line as input
        stdout().flush().expect("failed to flush");
    
        //get input and trim whitespaces
        io::stdin().read_line(&mut raw_input).expect("Failed to read input");
    
        //return raw input
        return raw_input.trim().to_string();
    }
    
    
    ================================================
    FILE: 95_Weekday/vbnet/README.md
    ================================================
    Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET)
    
    
    ================================================
    FILE: 95_Weekday/vbnet/Weekday.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 16
    VisualStudioVersion = 16.0.30114.105
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Weekday", "Weekday.vbproj", "{3BE031BE-D032-477A-A419-48FA7D3C2DD2}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{3BE031BE-D032-477A-A419-48FA7D3C2DD2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{3BE031BE-D032-477A-A419-48FA7D3C2DD2}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{3BE031BE-D032-477A-A419-48FA7D3C2DD2}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{3BE031BE-D032-477A-A419-48FA7D3C2DD2}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 95_Weekday/vbnet/Weekday.vbproj
    ================================================
    
      
        Exe
        Weekday
        net6.0
        16.9
      
    
    
    
    ================================================
    FILE: 95_Weekday/weekday.bas
    ================================================
    10 PRINT TAB(32);"WEEKDAY"
    20 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    30 PRINT:PRINT:PRINT
    100 PRINT "WEEKDAY IS A COMPUTER DEMONSTRATION THAT"
    110 PRINT"GIVES FACTS ABOUT A DATE OF INTEREST TO YOU."
    120 PRINT
    130 PRINT "ENTER TODAY'S DATE IN THE FORM: 3,24,1979  ";
    140 INPUT M1,D1,Y1
    150 REM THIS PROGRAM DETERMINES THE DAY OF THE WEEK
    160 REM  FOR A DATE AFTER 1582
    170 DEF FNA(A)=INT(A/4)
    180 DIM T(12)
    190 DEF FNB(A)=INT(A/7)
    200 REM SPACE OUTPUT AND READ IN INITIAL VALUES FOR MONTHS.
    210 FOR I= 1 TO 12
    220 READ T(I)
    230 NEXT I
    240 PRINT"ENTER DAY OF BIRTH (OR OTHER DAY OF INTEREST)";
    250 INPUT M,D,Y
    260 PRINT
    270 LET I1 = INT((Y-1500)/100)
    280 REM TEST FOR DATE BEFORE CURRENT CALENDAR.
    290 IF Y-1582 <0 THEN 1300
    300 LET A = I1*5+(I1+3)/4
    310 LET I2=INT(A-FNB(A)*7)
    320 LET Y2=INT(Y/100)
    330 LET Y3 =INT(Y-Y2*100)
    340 LET A =Y3/4+Y3+D+T(M)+I2
    350 LET B=INT(A-FNB(A)*7)+1
    360 IF M > 2 THEN 470
    370 IF Y3 = 0 THEN 440
    380 LET T1=INT(Y-FNA(Y)*4)
    390 IF T1 <> 0 THEN 470
    400 IF B<>0 THEN 420
    410 LET B=6
    420 LET B = B-1
    430 GOTO 470
    440 LET A = I1-1
    450 LET T1=INT(A-FNA(A)*4)
    460 IF T1 = 0 THEN 400
    470 IF B <>0 THEN 490
    480 LET B = 7
    490 IF (Y1*12+M1)*31+D1<(Y*12+M)*31+D THEN 550
    500 IF (Y1*12+M1)*31+D1=(Y*12+M)*31+D THEN 530
    510 PRINT M;"/";D;"/";Y;" WAS A ";
    520 GOTO 570
    530 PRINT M;"/";D;"/";Y;" IS A ";
    540 GOTO 570
    550 PRINT M;"/";D;"/";Y;" WILL BE A ";
    560 REM PRINT THE DAY OF THE WEEK THE DATE FALLS ON.
    570 IF B <>1 THEN 590
    580 PRINT "SUNDAY."
    590 IF B<>2 THEN 610
    600 PRINT "MONDAY."
    610 IF B<>3 THEN 630
    620 PRINT "TUESDAY."
    630 IF B<>4 THEN 650
    640 PRINT "WEDNESDAY."
    650 IF B<>5 THEN 670
    660 PRINT "THURSDAY."
    670 IF B<>6 THEN 690
    680 GOTO 1250
    690 IF B<>7 THEN 710
    700 PRINT "SATURDAY."
    710 IF (Y1*12+M1)*31+D1=(Y*12+M)*31+D THEN 1120
    720 LET I5=Y1-Y
    730 PRINT
    740 LET I6=M1-M
    750 LET I7=D1-D
    760 IF I7>=0 THEN 790
    770 LET I6= I6-1
    780 LET I7=I7+30
    790 IF I6>=0 THEN 820
    800 LET I5=I5-1
    810 LET I6=I6+12
    820 IF I5<0 THEN 1310
    830 IF I7 <> 0 THEN 850
    835 IF I6 <> 0 THEN 850
    840 PRINT"***HAPPY BIRTHDAY***"
    850 PRINT " "," ","YEARS","MONTHS","DAYS"
    855 PRINT " "," ","-----","------","----"
    860 PRINT "YOUR AGE (IF BIRTHDATE) ",I5,I6,I7
    870 LET A8 = (I5*365)+(I6*30)+I7+INT(I6/2)
    880 LET K5 = I5
    890 LET K6 = I6
    900 LET K7 = I7
    910 REM CALCULATE RETIREMENT DATE.
    920 LET E = Y+65
    930 REM CALCULATE TIME SPENT IN THE FOLLOWING FUNCTIONS.
    940 LET F = .35
    950 PRINT "YOU HAVE SLEPT ",
    960 GOSUB 1370
    970 LET F = .17
    980 PRINT "YOU HAVE EATEN ",
    990 GOSUB 1370
    1000 LET F = .23
    1010 IF K5 > 3 THEN 1040
    1020 PRINT "YOU HAVE PLAYED",
    1030 GOTO 1080
    1040 IF K5 > 9 THEN 1070
    1050 PRINT "YOU HAVE PLAYED/STUDIED",
    1060 GOTO  1080
    1070 PRINT "YOU HAVE WORKED/PLAYED",
    1080 GOSUB 1370
    1085 GOTO 1530
    1090 PRINT "YOU HAVE RELAXED ",K5,K6,K7
    1100 PRINT
    1110 PRINT TAB(16);"***  YOU MAY RETIRE IN";E;" ***"
    1120 PRINT
    1140 PRINT
    1200 PRINT
    1210 PRINT
    1220 PRINT
    1230 PRINT
    1240 END
    1250 IF D=13 THEN 1280
    1260 PRINT "FRIDAY."
    1270 GOTO 710
    1280 PRINT "FRIDAY THE THIRTEENTH---BEWARE!"
    1290 GOTO 710
    1300 PRINT "NOT PREPARED TO GIVE DAY OF WEEK PRIOR TO MDLXXXII. "
    1310 GOTO 1140
    1320 REM TABLE OF VALUES FOR THE MONTHS TO BE USED IN CALCULATIONS.
    1330 DATA 0, 3, 3, 6, 1, 4, 6, 2, 5, 0, 3, 5
    1340 REM THIS IS THE CURRENT DATE USED IN THE CALCULATIONS.
    1350 REM THIS IS THE DATE TO BE CALCULATED ON.
    1360 REM CALCULATE TIME IN YEARS, MONTHS, AND DAYS
    1370 LET K1=INT(F*A8)
    1380 LET I5 = INT(K1/365)
    1390 LET K1 = K1- (I5*365)
    1400 LET I6 = INT(K1/30)
    1410 LET I7 = K1 -(I6*30)
    1420 LET K5 = K5-I5
    1430 LET K6 =K6-I6
    1440 LET K7 = K7-I7
    1450 IF K7>=0 THEN 1480
    1460 LET K7=K7+30
    1470 LET K6=K6-1
    1480 IF K6>0 THEN 1510
    1490 LET K6=K6+12
    1500 LET K5=K5-1
    1510 PRINT I5,I6,I7
    1520 RETURN
    1530 IF K6=12 THEN 1550
    1540 GOTO 1090
    1550 LET K5=K5+1
    1560 LET K6=0
    1570 GOTO 1090
    1580 REM
    1590 END
    
    
    ================================================
    FILE: 96_Word/README.md
    ================================================
    ### Word
    
    WORD is a combination of HANGMAN and BAGELS. In this game, the player must guess a word with clues as to a letter position furnished by the computer. However, instead of guessing one letter at a time, in WORD you guess an entire word (or group of 5 letters, such as ABCDE). The computer will tell you if any letters that you have guessed are in the mystery word and if any of them are in the correct position. Armed with these clues, you go on guessing until you get the word or, if you can’t get it, input a “?” and the computer will tell you the mystery word.
    
    You may change the words in Data Statements, but they must be 5-letter words.
    
    The author of this program is Charles Reid of Lexington High School, Lexington, Massachusetts.
    
    ---
    
    As published in Basic Computer Games (1978):
    - [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=181)
    - [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=194)
    
    Downloaded from Vintage Basic at
    http://www.vintage-basic.net/games.html
    
    #### Porting Notes
    
    (please note any difficulties or challenges in porting here)
    
    
    ================================================
    FILE: 96_Word/csharp/Program.cs
    ================================================
    using System;
    using System.Linq;
    using System.Text;
    
    namespace word
    {
        class Word
        {
            // Here's the list of potential words that could be selected
            // as the winning word.
            private string[] words = { "DINKY", "SMOKE", "WATER", "GRASS", "TRAIN", "MIGHT", "FIRST",
             "CANDY", "CHAMP", "WOULD", "CLUMP", "DOPEY" };
    
            /// 
            /// Outputs the instructions of the game.
            /// 
            private void intro()
            {
                Console.WriteLine("WORD".PadLeft(37));
                Console.WriteLine("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY".PadLeft(59));
    
                Console.WriteLine("I am thinking of a word -- you guess it. I will give you");
                Console.WriteLine("clues to help you get it. Good luck!!");
            }
    
            /// 
            /// This allows the user to enter a guess - doing some basic validation
            /// on those guesses.
            /// 
            /// The guess entered by the user
            private string get_guess()
            {
                string guess = "";
    
                while (guess.Length == 0)
                {
                    Console.WriteLine($"{Environment.NewLine}Guess a five letter word. ");
                    guess = Console.ReadLine().ToUpper();
    
                    if ((guess.Length != 5) || (guess.Equals("?")) || (!guess.All(char.IsLetter)))
                    {
                        guess = "";
                        Console.WriteLine("You must guess a five letter word. Start again.");
                    }
                }
    
                return guess;
            }
    
            /// 
            /// This checks the user's guess against the target word - capturing
            /// any letters that match up between the two as well as the specific
            /// letters that are correct.
            /// 
            /// The user's guess
            /// The 'winning' word
            /// A string showing which specific letters have already been guessed
            /// The integer value showing the number of character matches between guess and target
            private int check_guess(string guess, string target, StringBuilder progress)
            {
                // Go through each letter of the guess and see which
                // letters match up to the target word.
                // For each position that matches, update the progress
                // to reflect the guess
                int matches = 0;
                string common_letters = "";
    
                for (int ctr = 0; ctr < 5; ctr++)
                {
                    // First see if this letter appears anywhere in the target
                    // and, if so, add it to the common_letters list.
                    if (target.Contains(guess[ctr]))
                    {
                        common_letters.Append(guess[ctr]);
                    }
                    // Then see if this specific letter matches the
                    // same position in the target. And, if so, update
                    // the progress tracker
                    if (guess[ctr].Equals(target[ctr]))
                    {
                        progress[ctr] = guess[ctr];
                        matches++;
                    }
                }
    
                Console.WriteLine($"There were {matches} matches and the common letters were... {common_letters}");
                Console.WriteLine($"From the exact letter matches, you know......... {progress}");
                return matches;
            }
    
            /// 
            /// This plays one full game.
            /// 
            private void play_game()
            {
                string guess_word, target_word;
                StringBuilder guess_progress = new StringBuilder("-----");
                Random rand = new Random();
                int count = 0;
    
                Console.WriteLine("You are starting a new game...");
    
                // Randomly select a word from the list of words
                target_word = words[rand.Next(words.Length)];
    
                // Just run as an infinite loop until one of the
                // endgame conditions are met.
                while (true)
                {
                    // Ask the user for their guess
                    guess_word = get_guess();
                    count++;
    
                    // If they enter a question mark, then tell them
                    // the answer and quit the game
                    if (guess_word.Equals("?"))
                    {
                        Console.WriteLine($"The secret word is {target_word}");
                        return;
                    }
    
                    // Otherwise, check the guess against the target - noting progress
                    if (check_guess(guess_word, target_word, guess_progress) == 0)
                    {
                        Console.WriteLine("If you give up, type '?' for your next guess.");
                    }
    
                    // Once they've guess the word, end the game.
                    if (guess_progress.Equals(guess_word))
                    {
                        Console.WriteLine($"You have guessed the word.  It took {count} guesses!");
                        return;
                    }
                }
            }
    
            /// 
            /// The main entry point for the class - just keeps
            /// playing the game until the user decides to quit.
            /// 
            public void play()
            {
                intro();
    
                bool keep_playing = true;
    
                while (keep_playing)
                {
                    play_game();
                    Console.WriteLine($"{Environment.NewLine}Want to play again? ");
                    keep_playing = Console.ReadLine().StartsWith("y", StringComparison.CurrentCultureIgnoreCase);
                }
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                new Word().play();
            }
        }
    }
    
    
    ================================================
    FILE: 96_Word/csharp/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
    
    
    ================================================
    FILE: 96_Word/csharp/word.csproj
    ================================================
    
    
      
        Exe
        netcoreapp3.1
      
    
    
    
    
    ================================================
    FILE: 96_Word/csharp/word.sln
    ================================================
    
    Microsoft Visual Studio Solution File, Format Version 12.00
    # Visual Studio Version 17
    VisualStudioVersion = 17.0.32014.148
    MinimumVisualStudioVersion = 10.0.40219.1
    Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Word", "Word.csproj", "{E2CF183B-EBC3-497C-8D34-32EBEE4E2B73}"
    EndProject
    Global
    	GlobalSection(SolutionConfigurationPlatforms) = preSolution
    		Debug|Any CPU = Debug|Any CPU
    		Release|Any CPU = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(ProjectConfigurationPlatforms) = postSolution
    		{E2CF183B-EBC3-497C-8D34-32EBEE4E2B73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
    		{E2CF183B-EBC3-497C-8D34-32EBEE4E2B73}.Debug|Any CPU.Build.0 = Debug|Any CPU
    		{E2CF183B-EBC3-497C-8D34-32EBEE4E2B73}.Release|Any CPU.ActiveCfg = Release|Any CPU
    		{E2CF183B-EBC3-497C-8D34-32EBEE4E2B73}.Release|Any CPU.Build.0 = Release|Any CPU
    	EndGlobalSection
    	GlobalSection(SolutionProperties) = preSolution
    		HideSolutionNode = FALSE
    	EndGlobalSection
    	GlobalSection(ExtensibilityGlobals) = postSolution
    		SolutionGuid = {B4D37881-6972-406B-978F-C1B60BA42638}
    	EndGlobalSection
    EndGlobal
    
    
    ================================================
    FILE: 96_Word/d/.gitignore
    ================================================
    *.exe
    *.obj
    
    
    ================================================
    FILE: 96_Word/d/README.md
    ================================================
    Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Converted to [D](https://dlang.org/) by [Bastiaan Veelo](https://github.com/veelo).
    
    The Basic original required words to be exactly five letters in length for the program to behave correctly.
    This version does not replicate that limitation, and the test for that requirement is commented out.
    
    ## Running the code
    
    Assuming the reference [dmd](https://dlang.org/download.html#dmd) compiler:
    ```shell
    dmd -dip1000 -run word.d
    ```
    
    [Other compilers](https://dlang.org/download.html) also exist.
    
    
    ================================================
    FILE: 96_Word/d/word.d
    ================================================
    @safe: // Make @safe the default for this file, enforcing memory-safety.
    import std;
    
    void main()
    {
        enum width = 80;
        writeln(center("Word", width));
        writeln(center("(After Creative Computing  Morristown, New Jersey)\n\n\n", width));
        writeln(wrap("I am thinking of a word -- you guess it.  I will give you " ~
                     "clues to help you get it.  Good luck!!\n\n", width));
    
        string[] words = ["dinky", "smoke", "water", "grass", "train", "might", "first",
                          "candy", "champ", "would", "clump", "dopey"];
    
        playLoop: while (true)
        {
            writeln("\n\nYou are starting a new game...");
    
            string word = words[uniform(0, $-1)];  // $ is a short-hand for words.length.
            int guesses = 0;
            string knownLetters = '-'.repeat(word.length).array;
    
            while (true)
            {
                writeln("Guess a ", word.length, " letter word");
                string guess = readString.toLower;
                if (guess == "?")
                {
                    writeln("The secret word is ", word, "\n");
                    continue playLoop;  // Start a new game.
                }
                /* Uncomment this for equivalence with Basic.
                if (guess.length != 5)
                {
                    writeln("You must guess a 5 letter word.  Start again.");
                    continue;           // Ask for new guess.
                }
                */
                guesses++;
                if (guess == word)
                    break;  // Done guessing
                string commonLetters;
                foreach (i, wordLetter; word)
                    foreach (j, guessLetter; guess)
                        if (guessLetter == wordLetter)
                        {
                            commonLetters ~= guessLetter;
                            if (i == j)
                                knownLetters.replaceInPlace(i, i + 1, [guessLetter]);
                        }
                writeln("There were ", commonLetters.length, " matches and the common letters were... ", commonLetters);
                writeln("From the exact letter matches, you know................ ", knownLetters);
                if (knownLetters == word)
                    break;  // Done guessing
                if (commonLetters.length < 2)
                    writeln("If you give up, type '?' for your next guess.");
                writeln;
            }
    
            writeln("You have guessed the word.  It took ", guesses, " guesses!");
            write("\n\nWant to play again? ");
            if (readString.toLower != "yes")
                break;  // Terminate playLoop
        }
    }
    
    /// Read a string from standard input, stripping newline and other enclosing whitespace.
    string readString() nothrow
    {
        try
            return trustedReadln.strip;
        catch (Exception)   // readln throws on I/O and Unicode errors, which we handle here.
            return "";
    }
    
    /** An @trusted wrapper around readln.
     *
     * This is the only function that formally requires manual review for memory-safety.
     * [Arguably readln should be safe already](https://forum.dlang.org/post/rab398$1up$1@digitalmars.com)
     * which would remove the need to have any @trusted code in this program.
     */
    string trustedReadln() @trusted
    {
        return readln;
    }
    
    
    ================================================
    FILE: 96_Word/java/README.md
    ================================================
    Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
    
    Conversion to [Oracle Java](https://openjdk.java.net/)
    
    
    ================================================
    FILE: 96_Word/java/Word.java
    ================================================
    import java.util.Arrays;
    import java.util.Scanner;
    
    /**
     * Game of Word
     * 

    * Based on the BASIC game of Word here * https://github.com/coding-horror/basic-computer-games/blob/main/96%20Word/word.bas *

    * Note: The idea was to create a version of the 1970's BASIC game in Java, without introducing * new features - no additional text, error checking, etc has been added. * * Converted from BASIC to Java by Darren Cardenas. */ public class Word { private final static String[] WORDS = { "DINKY", "SMOKE", "WATER", "GRASS", "TRAIN", "MIGHT", "FIRST", "CANDY", "CHAMP", "WOULD", "CLUMP", "DOPEY" }; private final Scanner scan; // For user input private enum Step { INITIALIZE, MAKE_GUESS, USER_WINS } public Word() { scan = new Scanner(System.in); } // End of constructor Word public void play() { showIntro(); startGame(); } // End of method play private void showIntro() { System.out.println(" ".repeat(32) + "WORD"); System.out.println(" ".repeat(14) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"); System.out.println("\n\n"); System.out.println("I AM THINKING OF A WORD -- YOU GUESS IT. I WILL GIVE YOU"); System.out.println("CLUES TO HELP YOU GET IT. GOOD LUCK!!"); System.out.println("\n"); } // End of method showIntro private void startGame() { char[] commonLetters = new char[8]; char[] exactLetters = new char[8]; int commonIndex = 0; int ii = 0; // Loop iterator int jj = 0; // Loop iterator int numGuesses = 0; int numMatches = 0; int wordIndex = 0; Step nextStep = Step.INITIALIZE; String commonString = ""; String exactString = ""; String guessWord = ""; String secretWord = ""; String userResponse = ""; // Begin outer while loop while (true) { switch (nextStep) { case INITIALIZE: System.out.println("\n"); System.out.println("YOU ARE STARTING A NEW GAME..."); // Select a secret word from the list wordIndex = (int) (Math.random() * WORDS.length); secretWord = WORDS[wordIndex]; numGuesses = 0; Arrays.fill(exactLetters, 1, 6, '-'); Arrays.fill(commonLetters, 1, 6, '\0'); nextStep = Step.MAKE_GUESS; break; case MAKE_GUESS: System.out.print("GUESS A FIVE LETTER WORD? "); guessWord = scan.nextLine().toUpperCase(); numGuesses++; // Win condition if (guessWord.equals(secretWord)) { nextStep = Step.USER_WINS; continue; } Arrays.fill(commonLetters, 1, 8, '\0'); // Surrender condition if (guessWord.equals("?")) { System.out.println("THE SECRET WORD IS " + secretWord); System.out.println(""); nextStep = Step.INITIALIZE; // Play again continue; } // Check for valid input if (guessWord.length() != 5) { System.out.println("YOU MUST GUESS A 5 LETTER WORD. START AGAIN."); numGuesses--; nextStep = Step.MAKE_GUESS; // Guess again continue; } numMatches = 0; commonIndex = 1; for (ii = 1; ii <= 5; ii++) { for (jj = 1; jj <= 5; jj++) { if (secretWord.charAt(ii - 1) != guessWord.charAt(jj - 1)) { continue; } // Avoid out of bounds errors if (commonIndex <= 5) { commonLetters[commonIndex] = guessWord.charAt(jj - 1); commonIndex++; } if (ii == jj) { exactLetters[jj] = guessWord.charAt(jj - 1); } // Avoid out of bounds errors if (numMatches < 5) { numMatches++; } } } exactString = ""; commonString = ""; // Build the exact letters string for (ii = 1; ii <= 5; ii++) { exactString += exactLetters[ii]; } // Build the common letters string for (ii = 1; ii <= numMatches; ii++) { commonString += commonLetters[ii]; } System.out.println("THERE WERE " + numMatches + " MATCHES AND THE COMMON LETTERS WERE..." + commonString); System.out.println("FROM THE EXACT LETTER MATCHES, YOU KNOW................" + exactString); // Win condition if (exactString.equals(secretWord)) { nextStep = Step.USER_WINS; continue; } // No matches if (numMatches <= 1) { System.out.println(""); System.out.println("IF YOU GIVE UP, TYPE '?' FOR YOUR NEXT GUESS."); } System.out.println(""); nextStep = Step.MAKE_GUESS; break; case USER_WINS: System.out.println("YOU HAVE GUESSED THE WORD. IT TOOK " + numGuesses + " GUESSES!"); System.out.println(""); System.out.print("WANT TO PLAY AGAIN? "); userResponse = scan.nextLine(); if (userResponse.toUpperCase().equals("YES")) { nextStep = Step.INITIALIZE; // Play again } else { return; // Quit game } break; default: System.out.println("INVALID STEP"); break; } } // End outer while loop } // End of method startGame public static void main(String[] args) { Word word = new Word(); word.play(); } // End of method main } // End of class Word ================================================ FILE: 96_Word/javascript/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Shells) ================================================ FILE: 96_Word/javascript/word.mjs ================================================ #!/usr/bin/env node // WORD // // Converted from BASIC to Javascript by Oscar Toledo G. (nanochess) import { print, tab, input } from '../../00_Common/javascript/common.mjs'; // These are the words that the game knows about> If you want a bigger challenge you could add more words to the array const WORDS = ["DINKY", "SMOKE", "WATER", "GLASS", "TRAIN", "MIGHT", "FIRST", "CANDY", "CHAMP", "WOULD", "CLUMP", "DOPEY"]; const WORD_COUNT = WORDS.length; // Main control section async function main() { print(tab(33) + "WORD\n"); print(tab(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n"); print("\n"); print("\n"); print("\n"); print("I AM THINKING OF A WORD -- YOU GUESS IT. I WILL GIVE YOU\n"); print("CLUES TO HELP YOU GET IT. GOOD LUCK!!\n"); print("\n"); print("\n"); outer: while (1) { print("\n"); print("\n"); print("YOU ARE STARTING A NEW GAME...\n"); const secretWord = WORDS[Math.floor(Math.random() * WORD_COUNT)]; let guessCount = 0; // This array holds the letters which have been found in the correct position across all guesses // For instance if the word is "PLAIN" and the guesses so far are // "SHALL" ("A" correct) and "CLIMB" ("L" correct) then it will hold "-LA--" const knownLetters = []; for (let i = 0; i < 5; i++) knownLetters[i] = "-"; let guess = undefined; while (1) { print("GUESS A FIVE LETTER WORD:"); guess = (await input()).toUpperCase(); guessCount++; if (secretWord === guess) { // The player has guessed correctly break; } if (guess.charAt(0) === "?") { // Player has given up print("THE SECRET WORD IS " + secretWord + "\n"); print("\n"); // Start a new game by going to the start of the outer while loop continue outer; } if (guess.length !== 5) { print("YOU MUST GUESS A 5 LETTER WORD. START AGAIN.\n"); print("\n"); guessCount--; continue; } // Two things happen in this double loop: // 1. Letters which are in both the guessed and secret words are put in the lettersInCommon array // 2. Letters which are in the correct position in the guessed word are added to the knownLetters array let lettersInCommonCount = 0; const lettersInCommon = []; for (let i = 0; i < 5; i++) {// loop round characters in secret word let secretWordCharacter = secretWord.charAt(i); for (let j = 0; j < 5; j++) {// loop round characters in guessed word let guessedWordCharacter = guess.charAt(j); if (secretWordCharacter === guessedWordCharacter) { lettersInCommon[lettersInCommonCount] = guessedWordCharacter; if (i === j) { // Letter is in the exact position so add to the known letters array knownLetters[j] = guessedWordCharacter; } lettersInCommonCount++; } } } const lettersInCommonText = lettersInCommon.join(""); print("THERE WERE " + lettersInCommonCount + " MATCHES AND THE COMMON LETTERS WERE... " + lettersInCommonText + "\n"); const knownLettersText = knownLetters.join(""); print("FROM THE EXACT LETTER MATCHES, YOU KNOW............ " + knownLettersText + "\n"); if (knownLettersText === secretWord) { guess = knownLettersText; break; } if (lettersInCommonCount <= 1) { print("\n"); print("IF YOU GIVE UP, TYPE '?' FOR YOUR NEXT GUESS.\n"); print("\n"); } } print("YOU HAVE GUESSED THE WORD. IT TOOK " + guessCount + " GUESSES!\n"); print("\n"); print("WANT TO PLAY AGAIN"); const playAgainResponse = (await input()).toUpperCase(); if (playAgainResponse !== "YES") break; } } main(); ================================================ FILE: 96_Word/kotlin/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Kotlin](https://kotlinlang.org/) ================================================ FILE: 96_Word/lua/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Lua](https://www.lua.org/) ================================================ FILE: 96_Word/perl/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Perl](https://www.perl.org/) ================================================ FILE: 96_Word/perl/word.pl ================================================ #!/usr/bin/env perl use 5.010; # To get 'state' and 'say' use strict; # Require explicit declaration of variables use warnings; # Enable optional compiler warnings use English; # Use more friendly names for Perl's magic variables use Term::ReadLine; # Prompt and return user input our $VERSION = '0.000_01'; print <<'EOD'; WORD Creative Computing Morristown, New Jersey I am thinking of a word -- you guess it. I will give you clues to help you get it. Good luck!! EOD # Read the content of __DATA__, remove the trailing newlines, and store # each line into @words. Stop at __END__, since Perl does not see this # as an end-of-file. my @words; while ( ) { chomp; last if $ARG eq '__END__'; push @words, lc $ARG; # Normalize case to lower. } # This loop represents an actual game. We execute it until the player # does something that makes us explicitly break out. while ( 1 ) { print <<'EOD'; You are starting a new game ... EOD # Choose a random target word. The rand() function returns a number # from 0 to its argument, and coerces its argument to a scalar. In # scalar context, an array evaluates to the number of elements it # contains. my $target = $words[ rand @words ]; # We generalize the code by using the actual length of the target. my $target_length = length $target; my $count = 0; # Number of guesses # Make an array of the individual letters in the target. We will # iterate over this to determine matching letters. my @target_array = split qr<>, $target; # Make a hash of those letters. We will use this to determine common # letters. Any true value will do for the value of the hash. By # making use of this hash we avoid the nested loops of the original # BASIC program. my %target_hash = map { $ARG => 1 } @target_array; # We keep prompting the player until we get a response that causes # us to break out of the loop. while ( 1 ) { # Create the readline object. The state keyword means the # variable is only initialized once, no matter how many times # execution passes this point. state $term = Term::ReadLine->new( 'word' ); # Read the next guess. A return of undef means end-of-file. my $guess = $term->readline( "Guess a $target_length letter word: " ); exit unless defined $guess; last if $guess eq '?'; # A question mark means we give up if ( length( $guess ) != $target_length ) { # Wrong length. Ask again. say "You must guess a $target_length letter word. Try again."; redo; # Redo the innermost loop } $guess = lc $guess; # Lower-case the guess $count++; # Count another guess if ( $guess eq $target ) { # We guessed the word. say "You have guessed the word. It took $count guesses!"; my $answer = $term->readline( 'Want to play again? [y/N]: '); exit unless defined $guess; # End of file exit unless $guess =~ m/ \A y /smxi; last; # Exit the innermost loop. } my @common_letters; # Letters common to guess and target my $match = '-' x length $target; # Assume no matches my $inx = 0; # Iterator foreach my $letter ( split qr<>, $guess ) { if ( $target_hash{$letter} ) { # If the letter is in the hash, it occurs in the target push @common_letters, $letter; # If it is at the current position in the target, it is # an actual match. $target_array[$inx] eq $letter and substr $match, $inx, 1, $letter; } $inx++; } say 'There were ', scalar @common_letters, ' matches and the common letters were... ', @common_letters; say "From the exact letter matches, you know................ $match"; say ''; say q; redo; } } __DATA__ dinky smoke water grass train might first candy champ would clump dopey __END__ =head1 TITLE word.pl - Play the game 'word' from Basic Computer Games =head1 SYNOPSIS word.pl =head1 DETAILS This Perl script is a port of C, which is the 96th entry in Basic Computer Games. =head1 PORTED BY Thomas R. Wyant, III F =head1 COPYRIGHT AND LICENSE Copyright (C) 2022 by Thomas R. Wyant, III This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.0. For more details, see the Artistic License 1.0 at L, and/or the Gnu GPL at L. This program is distributed in the hope that it will be useful, but without any warranty; without even the implied warranty of merchantability or fitness for a particular purpose. =cut # ex: set expandtab tabstop=4 textwidth=72 : ================================================ FILE: 96_Word/python/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Python](https://www.python.org/about/) ================================================ FILE: 96_Word/python/word.py ================================================ #!/usr/bin/env python3 """ WORD Converted from BASIC to Python by Trevor Hobson """ import random words = [ "DINKY", "SMOKE", "WATER", "GRASS", "TRAIN", "MIGHT", "FIRST", "CANDY", "CHAMP", "WOULD", "CLUMP", "DOPEY", ] def play_game() -> None: """Play one round of the game""" random.shuffle(words) target_word = words[0] guess_count = 0 guess_progress = ["-"] * 5 print("You are starting a new game...") while True: guess_word = "" while not guess_word: guess_word = input("\nGuess a five letter word. ").upper() if guess_word == "?": break elif not guess_word.isalpha() or len(guess_word) != 5: guess_word = "" print("You must guess a five letter word. Start again.") guess_count += 1 if guess_word == "?": print("The secret word is", target_word) break else: common_letters = "" matches = 0 for i in range(5): for j in range(5): if guess_word[i] == target_word[j]: matches += 1 common_letters = common_letters + guess_word[i] if i == j: guess_progress[j] = guess_word[i] print( f"There were {matches}", f"matches and the common letters were... {common_letters}", ) print( "From the exact letter matches, you know............ " + "".join(guess_progress) ) if "".join(guess_progress) == guess_word: print(f"\nYou have guessed the word. It took {guess_count} guesses!") break elif matches == 0: print("\nIf you give up, type '?' for you next guess.") def main() -> None: print(" " * 33 + "WORD") print(" " * 15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n") print("I am thinking of a word -- you guess it. I will give you") print("clues to help you get it. Good luck!!\n") keep_playing = True while keep_playing: play_game() keep_playing = input("\nWant to play again? ").lower().startswith("y") if __name__ == "__main__": main() ================================================ FILE: 96_Word/ruby/README.md ================================================ Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Ruby](https://www.ruby-lang.org/en/) ================================================ FILE: 96_Word/ruby/word.rb ================================================ #!/usr/bin/env ruby # WORD # # Converted from BASIC to Ruby WORDS = ["DINKY", "SMOKE", "WATER", "GRASS", "TRAIN", "MIGHT", "FIRST","CANDY", "CHAMP", "WOULD", "CLUMP", "DOPEY"] def game_loop target_word = WORDS.sample.downcase guess_count = 0 guess_progress = ["-"] * 5 puts "You are starting a new game..." while true guess_word = "" while guess_word == "" puts "Guess a five letter word. " guess_word = gets.chomp if guess_word == "?" break elsif !guess_word.match(/^[[:alpha:]]+$/) || guess_word.length != 5 guess_word = "" puts "You must guess a five letter word. Start again." end end guess_count += 1 if guess_word == "?" puts "The secret word is #{target_word}" break else common_letters = "" matches = 0 5.times do |i| 5.times do |j| if guess_word[i] == target_word[j] matches += 1 common_letters = common_letters + guess_word[i] guess_progress[j] = guess_word[i] if i == j end end end puts "There were #{matches} matches and the common letters were... #{common_letters}" puts "From the exact letter matches, you know............ #{guess_progress.join}" if guess_progress.join == guess_word puts "You have guessed the word. It took #{guess_count} guesses!" break elsif matches < 2 puts "If you give up, type '?' for you next guess." end end end end puts " " * 33 + "WORD" puts " " * 15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n" puts "I am thinking of a word -- you guess it. I will give you" puts "clues to help you get it. Good luck!!\n" keep_playing = true while keep_playing game_loop puts "\n Want to play again? " keep_playing = gets.chomp.downcase.index("y") == 0 end ================================================ FILE: 96_Word/rust/Cargo.toml ================================================ [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" ================================================ FILE: 96_Word/rust/src/main.rs ================================================ use crate::word_game::WordGame; use std::io; mod progress; mod word_game; fn main() { println!("\n\n~~WORD~~"); println!("Creative Computing Morristown, New Jersey"); println!("\nI am thinking of a word -- you guess it."); println!("I will give you clues to help you get it."); println!("Good luck!!\n"); let mut quit = false; while quit == false { let mut game = WordGame::new(); let mut game_over = false; while game_over == false { game_over = game.tick(); } quit = !play_again(); } } fn play_again() -> bool { let mut again = true; let mut valid_response = false; while valid_response == false { println!("Want to play again? (Y/n)"); let mut response = String::new(); io::stdin() .read_line(&mut response) .expect("Failed to read line."); match response.trim().to_uppercase().as_str() { "Y" | "YES" => valid_response = true, "N" | "NO" => { again = false; valid_response = true; } _ => (), } } again } ================================================ FILE: 96_Word/rust/src/progress.rs ================================================ pub struct Progress { chars: [char; 5], } impl Progress { pub fn new() -> Self { Progress { chars: ['-'; 5] } } pub fn set_char_at(&mut self, c: char, i: usize) { self.chars[i] = c; } pub fn print(&self) { for c in self.chars { print!("{}", c); } } pub fn done(&self) -> bool { for c in self.chars { if c == '-' { return false; } } true } } ================================================ FILE: 96_Word/rust/src/word_game.rs ================================================ use crate::progress::Progress; use rand::Rng; use std::io; pub struct WordGame<'a> { word: &'a str, progress: Progress, guess: String, guesses: usize, } impl WordGame<'_> { pub fn new() -> Self { const WORDS: [&str; 12] = [ "DINKY", "SMOKE", "WATER", "GRASS", "TRAIN", "MIGHT", "FIRST", "CANDY", "CHAMP", "WOULD", "CLUMP", "DOPEY", ]; println!("\nYou are starting a new game..."); let random_index: usize = rand::thread_rng().gen_range(0..WORDS.len()); //println!("word is: {}", WORDS[random_index]); WordGame { word: WORDS[random_index], progress: Progress::new(), guess: String::new(), guesses: 0, } } pub fn tick(&mut self) -> bool { self.guesses += 1; println!("\n\nGuess a five letter word?"); let mut game_over = false; if WordGame::<'_>::read_guess(self) { game_over = WordGame::<'_>::process_guess(self); } game_over } fn read_guess(&mut self) -> bool { let mut guess = String::new(); io::stdin() .read_line(&mut guess) .expect("Failed to read line."); let invalid_input = |message: &str| { println!("\n{} Guess again.", message); return false; }; let guess = guess.trim(); for c in guess.chars() { if c.is_numeric() { return invalid_input("Your guess cannot include numbers."); } if !c.is_ascii_alphabetic() { return invalid_input("Your guess must only include ASCII characters."); } } if guess.len() != 5 { return invalid_input("You must guess a 5 letter word."); } self.guess = guess.to_string(); true } fn process_guess<'a>(&mut self) -> bool { let guess = self.guess.to_uppercase(); let mut matches: Vec = Vec::new(); for (i, c) in guess.chars().enumerate() { if self.word.contains(c) { matches.push(c); if self.word.chars().nth(i).unwrap() == c { self.progress.set_char_at(c, i); } } } println!( "There were {} matches and the common letters were....{}", matches.len(), matches.into_iter().collect::() ); print!("From the exact letter matches you know...."); self.progress.print(); if self.progress.done() { println!( "\n\nYou have guessed the word. It took {} guesses!\n", self.guesses ); return true; } false } } ================================================ FILE: 96_Word/vbnet/Program.vb ================================================ Imports System Imports System.Text Imports System.Text.RegularExpressions Module Word ' Here's the list of potential words that could be selected ' as the winning word. Dim words As String() = {"DINKY", "SMOKE", "WATER", "GRASS", "TRAIN", "MIGHT", "FIRST", "CANDY", "CHAMP", "WOULD", "CLUMP", "DOPEY"} '

    ' Outputs the instructions of the game. ' Private Sub intro() Console.WriteLine("WORD".PadLeft(37)) Console.WriteLine("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY".PadLeft(59)) Console.WriteLine("I am thinking of a word -- you guess it. I will give you") Console.WriteLine("clues to help you get it. Good luck!!") End Sub ' ' This allows the user to enter a guess - doing some basic validation ' on those guesses. ' ' The guess entered by the user Private Function get_guess() As String Dim guess As String = "" While (guess.Length = 0) Console.WriteLine($"{Environment.NewLine}Guess a five letter word. ") guess = Console.ReadLine().ToUpper() If ((guess.Length <> 5) Or guess.Equals("?") Or Not Regex.IsMatch(guess, "^[A-Z]+$")) Then guess = "" Console.WriteLine("You must guess a give letter word. Start again.") End If End While Return guess End Function ' ' This checks the user's guess against the target word - capturing ' any letters that match up between the two as well as the specific ' letters that are correct. ' ' The user's guess ' The 'winning' word ' A string showing which specific letters have already been guessed ' The integer value showing the number of character matches between guess and target Private Function check_guess(guess As String, target As String, progress As StringBuilder) As Integer ' Go through each letter of the guess And see which ' letters match up to the target word. ' For each position that matches, update the progress ' to reflect the guess Dim matches As Integer = 0 Dim common_letters As String = "" For ctr As Integer = 0 To 4 ' First see if this letter appears anywhere in the target ' And, if so, add it to the common_letters list. If (target.Contains(guess(ctr))) Then common_letters.Append(guess(ctr)) End If ' Then see if this specific letter matches the ' same position in the target. And, if so, update ' the progress tracker If (guess(ctr).Equals(target(ctr))) Then progress(ctr) = guess(ctr) matches += 1 End If Next Console.WriteLine($"There were {matches} matches and the common letters were... {common_letters}") Console.WriteLine($"From the exact letter matches, you know......... {progress}") Return matches End Function ' ' This plays one full game. ' Private Sub play_game() Dim guess_word As String, target_word As String Dim guess_progress As StringBuilder = New StringBuilder("-----") Dim rand As Random = New Random() Dim count As Integer = 0 Console.WriteLine("You are starting a new game...") ' Randomly select a word from the list of words target_word = words(rand.Next(words.Length)) ' Just run as an infinite loop until one of the ' endgame conditions are met. While (True) ' Ask the user for their guess guess_word = get_guess() count += 1 ' If they enter a question mark, then tell them ' the answer and quit the game If (guess_word.Equals("?")) Then Console.WriteLine($"The secret word is {target_word}") Return End If ' Otherwise, check the guess against the target - noting progress If (check_guess(guess_word, target_word, guess_progress) = 0) Then Console.WriteLine("If you give up, type '?' for your next guess.") End If ' Once they've guess the word, end the game. If (guess_progress.Equals(guess_word)) Then Console.WriteLine($"You have guessed the word. It took {count} guesses!") Return End If End While End Sub ' ' The main entry point for the class - just keeps ' playing the game until the user decides to quit. ' Public Sub play() intro() Dim keep_playing As Boolean = True While (keep_playing) play_game() Console.WriteLine($"{Environment.NewLine}Want to play again? ") keep_playing = Console.ReadLine().StartsWith("y", StringComparison.CurrentCultureIgnoreCase) End While End Sub End Module Module Program Sub Main(args As String()) Word.play() End Sub End Module ================================================ FILE: 96_Word/vbnet/README.md ================================================ Original BASIC source [downloaded from Vintage Basic](http://www.vintage-basic.net/games.html) Conversion to [Visual Basic .NET](https://en.wikipedia.org/wiki/Visual_Basic_.NET) ================================================ FILE: 96_Word/vbnet/word.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.31321.278 MinimumVisualStudioVersion = 10.0.40219.1 Project("{778DAE3C-4631-46EA-AA77-85C1314464D9}") = "Word", "Word.vbproj", "{F0D2422C-983F-4DF3-9D17-D2480839DF07}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {F0D2422C-983F-4DF3-9D17-D2480839DF07}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F0D2422C-983F-4DF3-9D17-D2480839DF07}.Debug|Any CPU.Build.0 = Debug|Any CPU {F0D2422C-983F-4DF3-9D17-D2480839DF07}.Release|Any CPU.ActiveCfg = Release|Any CPU {F0D2422C-983F-4DF3-9D17-D2480839DF07}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {179D39EB-C497-4336-B795-49CC799929BB} EndGlobalSection EndGlobal ================================================ FILE: 96_Word/vbnet/word.vbproj ================================================ Exe Word netcoreapp3.1 16.9 ================================================ FILE: 96_Word/word.bas ================================================ 2 PRINT TAB(33);"WORD" 3 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" 4 PRINT: PRINT: PRINT 5 DIM S(7),A(7),L(7),D(7),P(7) 10 PRINT "I AM THINKING OF A WORD -- YOU GUESS IT. I WILL GIVE YOU" 15 PRINT "CLUES TO HELP YOU GET IT. GOOD LUCK!!": PRINT: PRINT 20 REM 30 PRINT: PRINT: PRINT "YOU ARE STARTING A NEW GAME..." 35 RESTORE 40 READ N 50 C=INT(RND(1)*N+1) 60 FOR I=1 TO C 70 READ S$ 80 NEXT I 90 G=0 95 S(0)=LEN(S$) 100 FOR I=1 TO LEN(S$): S(I)=ASC(MID$(S$,I,1)): NEXT I 110 FOR I=1 TO 5 120 A(I)=45 130 NEXT I 140 FOR J=1 TO 5 144 P(J)=0 146 NEXT J 150 PRINT "GUESS A FIVE LETTER WORD"; 160 INPUT L$ 170 G=G+1 172 IF S$=L$ THEN 500 173 FOR I=1 TO 7: P(I)=0: NEXT I 175 L(0)=LEN(L$) 180 FOR I=1 TO LEN(L$): L(I)=ASC(MID$(L$,I,1)): NEXT I 190 IF L(1)=63 THEN 300 200 IF L(0)<>5 THEN 400 205 M=0: Q=1 210 FOR I=1 TO 5 220 FOR J=1 TO 5 230 IF S(I)<>L(J) THEN 260 231 P(Q)=L(J) 232 Q=Q+1 233 IF I<>J THEN 250 240 A(J)=L(J) 250 M=M+1 260 NEXT J 265 NEXT I 270 A(0)=5 272 P(0)=M 275 A$="": FOR I=1 TO A(0): A$=A$+CHR$(A(I)): NEXT I 277 P$="": FOR I=1 TO P(0): P$=P$+CHR$(P(I)): NEXT I 280 PRINT "THERE WERE";M;"MATCHES AND THE COMMON LETTERS WERE...";P$ 285 PRINT "FROM THE EXACT LETTER MATCHES, YOU KNOW................";A$ 286 IF A$=S$ THEN 500 287 IF M>1 THEN 289 288 PRINT: PRINT "IF YOU GIVE UP, TYPE '?' FOR YOUR NEXT GUESS." 289 PRINT 290 GOTO 150 300 S$="": FOR I=1 TO 7: S$=S$+CHR$(S(I)): NEXT I 310 PRINT "THE SECRET WORD IS ";S$: PRINT 320 GOTO 30 400 PRINT "YOU MUST GUESS A 5 LETTER WORD. START AGAIN." 410 PRINT: G=G-1: GOTO 150 500 PRINT "YOU HAVE GUESSED THE WORD. IT TOOK";G;"GUESSES!": PRINT 510 INPUT "WANT TO PLAY AGAIN";Q$ 520 IF Q$="YES" THEN 30 530 DATA 12,"DINKY","SMOKE","WATER","GRASS","TRAIN","MIGHT","FIRST" 540 DATA "CANDY","CHAMP","WOULD","CLUMP","DOPEY" 999 END ================================================ FILE: HOW_TO_RUN_THE_GAMES.md ================================================ # How to run the games The games in this repository have been translated into a number of different languages. How to run them depends on the target language. ## csharp ### dotnet command-line The best cross-platform method for running the csharp examples is with the `dotnet` command-line tool. This can be downloaded for **MacOS**, **Windows** and **Linux** from [dotnet.microsoft.com](https://dotnet.microsoft.com/). From there, the program can be run by 1. Opening a terminal window 1. Navigating to the corresponding directory 1. Starting with `dotnet run` ### Visual Studio Alternatively, for non-dotnet compatible translations, you will need [Visual Studio](https://visualstudio.microsoft.com/vs/community/) which can be used to both open the project and run the example. 1. Open the corresponding `.csproj` or `.sln` file 1. Click `Run` from within the Visual Studio IDE ## 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 [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. `**```python javac ```** AceyDuceyGame.java` 1. Run the compiled program with `java`: * eg. `java AceyDuceyGame` or if you are **using JDK11 or later** you can now execute a self contained java file that has a main method directly with `java .java`. ## javascript There are two ways of javascript implementations: ### browser The html examples can be run from within your web browser. Simply open the corresponding `.html` file from your web browser. ### node.js Some games are implemented as a [node.js](https://nodejs.org/) script. In this case there is no `*.html` file in the folder. 1. [install node.js](https://nodejs.org/en/download/) for your system. 1. change directory to the root of this repository (e.g. `cd basic-computer-games`). 1. from a terminal call the script you want to run (e.g. `node 78_Sine_Wave/javascript/sinewave.mjs`). _Hint: Normally javascript files have a `*.js` extension. We are using `*.mjs` to let node know , that we are using [ES modules](https://nodejs.org/docs/latest/api/esm.html#modules-ecmascript-modules) instead of [CommonJS](https://nodejs.org/docs/latest/api/modules.html#modules-commonjs-modules)._ ## kotlin Kotlin programs are compiled with the Kotlin compiler, and run with the java runtime, just like java programs. In addition to the java runtime you will need the `kotlinc` compiler, which can be installed using [these instructions](https://kotlinlang.org/docs/command-line.html). 1. Navigate to the corresponding directory. 1. Compile the program with `kotlinc`: * eg. `kotlinc AceyDuceyGame.kt -include-runtime -d AceyDuceyGame.jar` 1. Run the compiled program with `java`: * eg. `java -jar AceyDuceyGame.jar` ## pascal 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_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` 1. The output is an executable file that can be run directly. ## perl The perl translations can be run using a perl interpreter (a copy can be downloaded from [perl.org](https://www.perl.org/)) if not already installed. 1. From the command-line, navigate to the corresponding directory. 1. Invoke with the `perl` command. * eg. `perl aceyducey.pl` ## python The python translations can be run from the command line by using the `py` interpreter. If not already installed, a copy can be downloaded from [python.org](https://www.python.org/downloads/) for **Windows**, **MacOS** and **Linux**. 1. From the command-line, navigate to the corresponding directory. 1. Invoke with the `py` or `python` interpreter (depending on your python version). * eg. `py acey_ducey_oo.py` * 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/). ## ruby If you don't already have a ruby interpreter, you can download it from the [ruby project site](https://www.ruby-lang.org/en/). 1. From the command-line, navigate to the corresponding directory. 1. Invoke with the `ruby` tool. * eg. `ruby aceyducey.rb` ## vbnet Follow the same steps as for the [csharp](#csharp) translations. This can be run with `dotnet` or `Visual Studio`. ## rust If you don't already have Rust on your computer, you can follow the instruction on [Rust Book](https://doc.rust-lang.org/book/ch01-01-installation.html) 1. From the command-line, navigate to the corresponding directory. 2. Run the following command. * `cargo run` [def]: https://www.jetbrains.com/idea/ ================================================ FILE: LICENSE ================================================ This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to ================================================ FILE: README.md ================================================ ### What are we doing? 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://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? Please see [the discussion here](https://discourse.codinghorror.com/t/-/7927) for a worklog and conversation around this project. ### Project structure I have moved all [the original BASIC source code](http://www.vintage-basic.net/games.html) into a folder for each project in the original book (first volume). Note that Lyle Kopnicky has generously normalized all the code (thanks Lyle!) to run against [Vintage Basic](http://www.vintage-basic.net/download.html) circa 2009: > I’ve included all the games here for your tinkering pleasure. I’ve tested and tweaked each one of them to make sure they’ll run with Vintage BASIC, though you may see a few oddities. That’s part of the fun of playing with BASIC: it never works quite the same on two machines. The games will play better if you keep CAPS LOCK on, as they were designed to be used with capital-letter input. 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# 2. Java 3. JavaScript 4. Kotlin 5. Lua 6. Perl 7. Python 8. Ruby 9. Rust 10. VB.NET > 📢 Note that in March 2022 we removed Pascal / Object Pascal and replaced it with Rust as we couldn’t determine if Pascal is effectively memory safe. We’ve also added Lua, as it made the top 20 in TIOBE (as of 2022) and it is both memory safe and a scripting language. The Pascal ports were moved to the alternate languages folder. > ⚠️ Please note that we have decided, as a project, that we **do not want any IDE-specific or build-specific files in the repository.** Please refrain from committing any files to the repository that only exist to work with a specific IDE or a specific build system. ### Alternate Languages If you wish to port one of the programs to a language not in our list – that is, a language which is either not memory safe, or not a general purpose scripting language, you can do so via the `00_Alternate_Languages` folder. Place your port in the appropriate game subfolder, in a subfolder named for the language. Please note that these ports are appreciated, but they will not count toward the donation total at the end of the project. ### Project goals 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: - **These are very old games**. They date from the mid-70s so they’re not exactly examples of what kids (or anyone, really?) would be playing these days. Consider them more like classic programming exercises to teach programming. We’re paying it forward by converting them into modern languages, so the next generation can learn from the programs in this classic book – and compare implementations across common modern languages. - **Stay true to the original program**. These are mostly unsophisticated, simple command line / console games, so we should strive to replicate the command line / console output and behavior illustrated in the original book. See the README in the project folder for links to the original scanned source input and output. Try [running the game in your browser](https://troypress.com/wp-content/uploads/user/js-basic/index.html). Avoid the impulse to add features; keep it simple, _except_ for modern conventions, see next item 👇 - **Please DO update for modern coding conventions**. Support uppercase and lowercase. Use structured programming. Use subroutines. Try to be an example of good, modern coding practices! - **Use lots of comments to explain what is going on**. Comment liberally! If there were clever tricks in the original code, decompose those tricks into simpler (even if more verbose) code, and use comments to explain what’s happening and why. If there is something particularly tricky about a program, edit the **Porting Notes** section of the `readme.md` to let everyone know. Those `GOTO`s can be very pesky.. - **Please don’t get _too_ fancy**. Definitely use the most recent versions and features of the target language, but also try to keep the code samples simple and explainable – the goal is to teach programming in the target language, not necessarily demonstrate the cleverest one-line tricks, or big system "enterprise" coding techniques designed for thousands of lines of code. - **Please don't check in any build specific or IDE specific files**. We want the repository to be simple and clean, so we have ruled out including any IDE or build system specific files from the repository. Git related files are OK, as we are using Git and this is GitHub. 😉 ### Emulation and Bugfixes We want the general behavior of the original programs to be preserved, _however_, we also want to update them, specifically: - allow both UPPERCASE and lowercase input and display - incorporate any bugfixes to the original programs; see the `readme.md` in the game folder - improved error handling for bad or erroneous input 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! 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 | | 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 | | 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 |
    ================================================ FILE: index.html ================================================ BASIC Computer Games

    BASIC Computer Games

    ================================================ FILE: pyproject.toml ================================================ [tool.pytest.ini_options] markers = [ "slow: marks tests as slow (deselect with '-m \"not slow\"')", ]