diff --git a/00_Common/javascript/WebTerminal/HtmlTerminal.css b/00_Common/javascript/WebTerminal/HtmlTerminal.css new file mode 100644 index 00000000..0150bb9a --- /dev/null +++ b/00_Common/javascript/WebTerminal/HtmlTerminal.css @@ -0,0 +1,72 @@ +: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: 60rem; + 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
elements */ +.terminal pre.line { + display: inline-block; + font: var(--terminal-font); + margin: 0; + padding: 0; +} + +/* The "terminal" has one "prompt" element. + * This prompt is not any kind of input, but just a simple + * with an id "prompt" and a + */ +@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 scrollbar */ +::-webkit-scrollbar { + width: 3px; + height: 3px; +} +::-webkit-scrollbar-track { + background: var(--background-color); +} +::-webkit-scrollbar-thumb { + background: var(--text-color); +} diff --git a/00_Common/javascript/WebTerminal/HtmlTerminal.js b/00_Common/javascript/WebTerminal/HtmlTerminal.js new file mode 100644 index 00000000..c8d529d9 --- /dev/null +++ b/00_Common/javascript/WebTerminal/HtmlTerminal.js @@ -0,0 +1,195 @@ +/** + * @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 + */ +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 = undefined; + + /** + * 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("span"); + this.#$prompt.setAttribute("id", "prompt"); + this.#$prompt.innerText = ""; + + //TODO: this handler shouls be only on the propt element and only active if cursor is visible + document.addEventListener("keyup", this.#handleKey.bind(this)); + } + + /** + * 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

+   */
+  #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 
+    if (!this.#inputCallback) {
+      return;
+    }
+
+    if (e.keyCode === 13 /* ENTER */) {
+      // create a new line with the text input and remove the prompt
+      const text = this.#$prompt.innerText;
+      this.write(text + "\n");
+      this.#$prompt.innerText = "";
+      this.#$prompt.remove();
+
+      // return the inputed text
+      this.#inputCallback(text);
+      
+      // remove the callback and the key handler
+      this.#inputCallback = undefined;
+    } else if (e.keyCode === 8 /* BACKSPACE */) {
+      this.#$prompt.innerText = this.#$prompt.innerText.slice(0, -1);
+    } else if (
+      e.keyCode == 16 // "Shift"
+      || e.keyCode == 17 // "Control"
+      || e.keyCode == 20 // "CapsLock"
+      || !e.key.match(/^[a-z0-9!"§#$%&'()*+,.\/:;<=>?@\[\] ^_`{|}~-]$/i)
+    ) {
+      // ignore non-visible characters
+      return e;
+    } else {
+      this.#$prompt.innerHtml = '';
+      const key = e.shiftKey ? e.key.toUpperCase() : e.key;
+      this.#$prompt.innerText =  this.#$prompt.innerText + key;
+    }
+  }
+
+  /**
+   * Clear the terminal.
+   * Remove all lines.
+   * 
+   * @public
+   */
+  clear() {
+    this.$output.innerText = "";
+  }
+
+  /**
+   * TODO:
+   * 
+   * @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.$output.appendChild(this.#$prompt);
+    this.#inputCallback = callback;
+  }
+}
diff --git a/00_Common/javascript/WebTerminal/terminal.html b/00_Common/javascript/WebTerminal/terminal.html
new file mode 100644
index 00000000..d9fab1d6
--- /dev/null
+++ b/00_Common/javascript/WebTerminal/terminal.html
@@ -0,0 +1,129 @@
+
+  
+    Minimal node.js terminal
+    
+    
+    
+    
+  
+  
+    
+

BASIC Computer Games

+
+
+ + + + diff --git a/00_Common/javascript/WebTerminal/terminal_tests.mjs b/00_Common/javascript/WebTerminal/terminal_tests.mjs new file mode 100644 index 00000000..d2470c95 --- /dev/null +++ b/00_Common/javascript/WebTerminal/terminal_tests.mjs @@ -0,0 +1,30 @@ +#!/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(); diff --git a/00_Common/javascript/common.mjs b/00_Common/javascript/common.mjs new file mode 100644 index 00000000..765f5cc2 --- /dev/null +++ b/00_Common/javascript/common.mjs @@ -0,0 +1,67 @@ +/** + * 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(); + + /* 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[data.length] === '\n' ? data.slice(0, -1) : data; + + resolve(content); + }); + }); +} diff --git a/00_Utilities/TODO.md b/00_Utilities/TODO.md index 05fc2130..be873e7d 100644 --- a/00_Utilities/TODO.md +++ b/00_Utilities/TODO.md @@ -10,7 +10,7 @@ 07_Basketball | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ 08_Batnum | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ 09_Battle | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ -10_Blackjack | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ✅ | ⬜️ +10_Blackjack | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ 11_Bombardment | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ 12_Bombs_Away | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ 13_Bounce | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ @@ -47,13 +47,13 @@ 44_Hangman | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ 45_Hello | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ 46_Hexapawn | ✅ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ -47_Hi-Lo | ✅ | ✅ | ✅ | ✅ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ +47_Hi-Lo | ✅ | ✅ | ✅ | ✅ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ⬜️ 48_High_IQ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ 49_Hockey | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ | ⬜️ | ⬜️ | ⬜️ | ⬜️ 50_Horserace | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ 51_Hurkle | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ 52_Kinema | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ -53_King | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ⬜️ | ⬜️ | ⬜️ +53_King | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ 54_Letter | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ 55_Life | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ 56_Life_for_Two | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ | ⬜️ | ⬜️ | ⬜️ | ⬜️ @@ -64,7 +64,7 @@ 61_Math_Dice | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ✅ | ⬜️ 62_Mugwump | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ 63_Name | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ -64_Nicomachus | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ +64_Nicomachus | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ 65_Nim | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ 66_Number | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ✅ | ⬜️ 67_One_Check | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ @@ -82,7 +82,7 @@ 79_Slalom | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ 80_Slots | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ 81_Splat | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ -82_Stars | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ +82_Stars | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ✅ | ✅ | ⬜️ | ✅ | ⬜️ 83_Stock_Market | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ 84_Super_Star_Trek | ✅ | ✅ | ✅ | ⬜️ | ⬜️ | ⬜️ | ✅ | ⬜️ | ⬜️ | ⬜️ 85_Synonym | ✅ | ✅ | ✅ | ✅ | ⬜️ | ✅ | ✅ | ✅ | ⬜️ | ⬜️ diff --git a/00_Utilities/build-index.js b/00_Utilities/build-index.js index 2785957e..1ed862ef 100644 --- a/00_Utilities/build-index.js +++ b/00_Utilities/build-index.js @@ -14,16 +14,29 @@ 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) { - if (game.htmlFiles.length > 1) { - const entries = game.htmlFiles.map(htmlFile => { - const name = path.basename(htmlFile).replace('.html', ''); + const creatFileLink = (file, name = path.basename(file)) => { + if (file.endsWith('.html')) { return ` -
  • - ${name} -
  • +
  • ${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 `
  • @@ -32,7 +45,7 @@ function createGameLinks(game) {
  • `; } else { - return `
  • ${game.name}
  • `; + return creatFileLink(game.files[0], game.name); } } @@ -73,11 +86,11 @@ function createIndexHtml(title, games) { `.trim().replace(/\s\s+/g, ''); } -function findHtmlFilesInFolder(folder) { +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 implementation`); + throw new Error(`Game "${folder}" is missing a javascript folder`); } // get all files in the javascript folder @@ -85,12 +98,17 @@ function findHtmlFilesInFolder(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 (htmlFiles.length == 0) { - throw new Error(`Game "${folder}" is missing a html file in the "${folder}/${JAVASCRIPT_FOLDER}" folder`); + if (entries.length == 0) { + throw new Error(`Game "${folder}" is missing a HTML or node.js file in the folder "${folder}/${JAVASCRIPT_FOLDER}"`); } - return htmlFiles.map(htmlFile => path.join(folder, JAVASCRIPT_FOLDER, htmlFile)); + return entries.map(file => path.join(folder, JAVASCRIPT_FOLDER, file)); } function main() { @@ -111,10 +129,10 @@ function main() { // get name and javascript file from folder const games = folders.map(folder => { const name = folder.replace('_', ' '); - let htmlFiles; + let files; try { - htmlFiles = findHtmlFilesInFolder(folder); + files = findJSFilesInFolder(folder); } catch (error) { console.warn(`Game "${name}" is missing a javascript implementation: ${error.message}`); return null; @@ -122,7 +140,7 @@ function main() { return { name, - htmlFiles + files } }).filter(game => game !== null); diff --git a/00_Utilities/javascript/style_terminal.css b/00_Utilities/javascript/style_terminal.css index 1301983f..dce94bb9 100644 --- a/00_Utilities/javascript/style_terminal.css +++ b/00_Utilities/javascript/style_terminal.css @@ -31,7 +31,11 @@ body { background-color: var(--background); color: var(--text); font: var(--font); - padding: 3rem; + margin: 0; +} + +#output { + padding: 1rem; } /* format input fields */ @@ -80,7 +84,10 @@ a:hover { } /* add all the face flicker effects (only on desktop) */ -@media screen and (min-width: 640px) { +@media screen and (min-width: 960px) { + main { + padding: 3rem; + } @keyframes flicker { 0% { opacity: 0.27861; diff --git a/01_Acey_Ducey/python/acey_ducey.py b/01_Acey_Ducey/python/acey_ducey.py index 550d94b2..603e01af 100755 --- a/01_Acey_Ducey/python/acey_ducey.py +++ b/01_Acey_Ducey/python/acey_ducey.py @@ -24,7 +24,6 @@ cards = { def play_game() -> None: - """Play the game""" cash = 100 while cash > 0: print(f"You now have {cash} dollars\n") @@ -64,16 +63,6 @@ def play_game() -> None: def main() -> None: - """Main""" - 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__": print( """ Acey-Ducey is played in the following manner @@ -84,4 +73,13 @@ 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__": main() diff --git a/01_Acey_Ducey/python/acey_ducey_oo.py b/01_Acey_Ducey/python/acey_ducey_oo.py index b7aff06d..35b26d84 100644 --- a/01_Acey_Ducey/python/acey_ducey_oo.py +++ b/01_Acey_Ducey/python/acey_ducey_oo.py @@ -7,16 +7,15 @@ From: BASIC Computer Games (1978) Python port by Aviyam Fischer, 2022 """ -from typing import List, Literal, TypeAlias, get_args +from typing import List, Literal, NamedTuple, TypeAlias, get_args 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: - def __init__(self, suit: Suit, rank: Rank) -> None: - self.suit = suit - self.rank = rank +class Card(NamedTuple): + suit: Suit + rank: Rank def __str__(self) -> str: r = str(self.rank) @@ -115,7 +114,7 @@ def game_loop() -> None: game_over = True -def main(): +def main() -> None: print( """ Acey Ducey is a card game where you play against the computer. diff --git a/02_Amazing/python/amazing.py b/02_Amazing/python/amazing.py index bdffe7e7..9d7ab419 100644 --- a/02_Amazing/python/amazing.py +++ b/02_Amazing/python/amazing.py @@ -1,17 +1,13 @@ -import random import enum -from typing import List, Tuple +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, - ): + def __init__(self, width: int, length: int) -> None: assert width >= 2 and length >= 2 used: List[List[int]] = [] walls: List[List[int]] = [] @@ -77,18 +73,15 @@ EXIT_RIGHT = 2 def main() -> None: - welcome_header() + print_intro() width, length = get_maze_dimensions() maze = build_maze(width, length) maze.display() -def welcome_header() -> None: +def print_intro() -> None: print(" " * 28 + "AMAZING PROGRAM") - print(" " * 15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") - print() - print() - print() + print(" " * 15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n") def build_maze(width: int, length: int) -> Maze: @@ -120,7 +113,7 @@ def build_maze(width: int, length: int) -> Maze: else: while True: if position.col != width - 1: - position.col = position.col + 1 + position.col += 1 elif position.row != length - 1: position.row, position.col = position.row + 1, 0 else: diff --git a/03_Animal/python/animal.py b/03_Animal/python/animal.py index 10e9041f..618ef7d6 100644 --- a/03_Animal/python/animal.py +++ b/03_Animal/python/animal.py @@ -119,7 +119,7 @@ def avoid_void_input(message: str) -> str: return answer -def initial_message() -> None: +def print_intro() -> None: print(" " * 32 + "Animal") print(" " * 15 + "Creative Computing Morristown, New Jersey\n") print("Play ´Guess the Animal´") @@ -133,7 +133,7 @@ def main() -> None: root = Node("Does it swim?", yes_child, no_child) # Main loop of game - initial_message() + print_intro() keep_playing = parse_input("Are you thinking of an animal? ", True, root) == "y" while keep_playing: keep_asking = True diff --git a/04_Awari/python/awari.py b/04_Awari/python/awari.py index d05cb0ec..85669cee 100644 --- a/04_Awari/python/awari.py +++ b/04_Awari/python/awari.py @@ -81,14 +81,6 @@ MAX_HISTORY = 9 LOSING_BOOK_SIZE = 50 -def print_with_tab(space_count: int, msg: str) -> None: - if space_count > 0: - spaces = " " * space_count - else: - spaces = "" - print(spaces + msg) - - def draw_pit(line: str, board, pit_index) -> str: val = board[pit_index] line = line + " " @@ -362,10 +354,8 @@ def player_move(board) -> Tuple[int, bool, int]: def main() -> None: - print_with_tab(34, "AWARI") - print_with_tab(15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") - print() - print() + print(" " * 34 + "AWARI") + print(" " * 15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n") board = [0] * 14 # clear the board representation global losing_book diff --git a/05_Bagels/python/bagels.py b/05_Bagels/python/bagels.py index 77e8b473..c9c0ca84 100644 --- a/05_Bagels/python/bagels.py +++ b/05_Bagels/python/bagels.py @@ -106,8 +106,7 @@ def build_result_string(num: List[str], guess: str) -> str: def main() -> None: # Intro text print("\n Bagels") - print("Creative Computing Morristown, New Jersey") - print("\n\n") + 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)? ") diff --git a/06_Banner/python/banner.py b/06_Banner/python/banner.py index 6e563e46..ad85b807 100644 --- a/06_Banner/python/banner.py +++ b/06_Banner/python/banner.py @@ -86,10 +86,10 @@ def print_banner() -> None: for statement_char in statement: s = letters[statement_char].copy() - xStr = character + x_str = character if character == "ALL": - xStr = statement_char - if xStr == " ": + x_str = statement_char + if x_str == " ": print("\n" * (7 * horizontal)) else: for u in range(0, 7): @@ -103,13 +103,13 @@ def print_banner() -> None: f[u] = 8 - k break for _t1 in range(1, horizontal + 1): - line_str = " " * int((63 - 4.5 * vertical) * g1 / len(xStr) + 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(xStr) + line_str = line_str + " " * len(x_str) else: - line_str = line_str + xStr * vertical + 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 diff --git a/07_Basketball/python/basketball.py b/07_Basketball/python/basketball.py index 762b0d1b..758e5cb9 100644 --- a/07_Basketball/python/basketball.py +++ b/07_Basketball/python/basketball.py @@ -6,10 +6,10 @@ You are able to choose your shot types as well as defensive formations """ import random -from typing import Optional, List, Literal +from typing import List, Literal, Optional -def explain_keyboard_inputs(): +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. ") @@ -35,7 +35,7 @@ class Basketball: self.shot_choices: List[Literal[0, 1, 2, 3, 4]] = [0, 1, 2, 3, 4] self.z1: Optional[float] = None - explain_keyboard_inputs() + print_intro() self.defense = get_defense_choice(self.defense_choices) @@ -154,7 +154,7 @@ class Basketball: # ball is passed back to you self.ball_passed_back() else: - print("") + print() self.dartmouth_non_jump_shot() else: print("Shot is good.") @@ -268,7 +268,7 @@ class Basketball: self.opponent_ball() else: if random.random() > 0.5: - print("") + print() self.opponent_non_jumpshot() else: print("Pass back to " + self.opponent + " guard.\n") @@ -304,14 +304,14 @@ class Basketball: self.opponent_ball() else: if random.random() > 0.5: - print("") + print() self.opponent_non_jumpshot() else: print("Pass back to " + self.opponent + " guard.\n") self.opponent_ball() else: if random.random() > 0.5: - print("") + print() self.opponent_non_jumpshot() else: print("Pass back to " + self.opponent + " guard\n") diff --git a/08_Batnum/python/batnum.py b/08_Batnum/python/batnum.py index 60c7aad9..3a54ee49 100644 --- a/08_Batnum/python/batnum.py +++ b/08_Batnum/python/batnum.py @@ -1,5 +1,5 @@ from enum import IntEnum -from typing import Tuple, Any +from typing import Any, Tuple class WinOptions(IntEnum): @@ -41,7 +41,7 @@ class StartOptions(IntEnum): def print_intro() -> None: - """Prints out the introduction and rules for the game.""" + """Print out the introduction and rules for the game.""" print("BATNUM".rjust(33, " ")) print("CREATIVE COMPUTING MORRISSTOWN, NEW JERSEY".rjust(15, " ")) print() @@ -64,12 +64,12 @@ 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: - pileSize - the starting size of the object pile - minSelect - minimum selection that can be made on each turn - maxSelect - maximum selection that can be made on each turn - startOption - 1 if the computer is first + 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 - winOption - 1 if the goal is to take the last object + 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() @@ -123,7 +123,7 @@ def player_move( 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.""" + 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 ")) @@ -167,7 +167,7 @@ def computer_move( 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 pileSize.""" + 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 @@ -200,7 +200,7 @@ def play_game( of the win/lose conditions is met. """ game_over = False - # playersTurn is a boolean keeping track of whether it's the + # players_turn is a boolean keeping track of whether it's the # player's or computer's turn players_turn = start_option == StartOptions.PlayerFirst diff --git a/10_Blackjack/python/blackjack.py b/10_Blackjack/python/blackjack.py index e4d6fa40..a2c38260 100644 --- a/10_Blackjack/python/blackjack.py +++ b/10_Blackjack/python/blackjack.py @@ -242,7 +242,7 @@ class Game: players.append(Player.new(PlayerType.Player, i)) if get_char_from_user_input("Do you want instructions", ["y", "n"]) == "y": - instructions() + print_instructions() print() return Game(players=players, decks=Decks.new(), games_played=0) @@ -284,7 +284,7 @@ class Game: # turn loop, ends when player finishes their turn while True: clear() - welcome() + print_welcome_screen() print(f"\n\t\t\tGame {game}") print(scores) print(player_hands_message) @@ -407,7 +407,7 @@ STARTING_BALANCE: int = 100 def main() -> None: game: Game - welcome() + print_welcome_screen() # create game game = Game.new( @@ -421,9 +421,7 @@ def main() -> None: char = get_char_from_user_input("Play Again?", ["y", "n"]) -def welcome() -> None: - """prints the welcome screen""" - # welcome message +def print_welcome_screen() -> None: print( """ BLACK JACK @@ -432,8 +430,7 @@ def welcome() -> None: ) -def instructions() -> None: - """prints the instructions""" +def print_instructions() -> None: print( """ THIS IS THE GAME OF 21. AS MANY AS 7 PLAYERS MAY PLAY THE diff --git a/11_Bombardment/python/bombardment.py b/11_Bombardment/python/bombardment.py index 2457a87a..b125987a 100755 --- a/11_Bombardment/python/bombardment.py +++ b/11_Bombardment/python/bombardment.py @@ -4,9 +4,9 @@ from functools import partial from typing import Callable, List, Set -def display_intro() -> None: - print("" * 33 + "BOMBARDMENT") - print("" * 15 + " CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") +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.") @@ -28,7 +28,6 @@ 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) @@ -128,8 +127,8 @@ ENEMY_PROGRESS_MESSAGES = ( ) -def play() -> None: - display_intro() +def main() -> None: + print_intro() display_field() enemy_positions = generate_enemy_positions() @@ -162,4 +161,4 @@ def play() -> None: if __name__ == "__main__": - play() + main() diff --git a/14_Bowling/python/bowling.py b/14_Bowling/python/bowling.py index f4a566aa..c7773978 100644 --- a/14_Bowling/python/bowling.py +++ b/14_Bowling/python/bowling.py @@ -33,7 +33,7 @@ def calculate_score(rolls: List[int]) -> int: class Player: - def __init__(self, name: str): + def __init__(self, name: str) -> None: self.name = name self.rolls: List[int] = [] diff --git a/15_Boxing/python/boxing.py b/15_Boxing/python/boxing.py index f1c2ae59..1943e74e 100755 --- a/15_Boxing/python/boxing.py +++ b/15_Boxing/python/boxing.py @@ -77,7 +77,7 @@ def read_punch_profiles(filepath: Path) -> Dict[Literal[1, 2, 3, 4], PunchProfil return result # type: ignore -def play() -> None: +def main() -> None: print("BOXING") print("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") print("\n\n") @@ -179,4 +179,4 @@ def play_round(round_number: int, player: Player, opponent: Player) -> None: if __name__ == "__main__": - play() + main() diff --git a/16_Bug/python/bug.py b/16_Bug/python/bug.py index 4d93c158..846002f8 100644 --- a/16_Bug/python/bug.py +++ b/16_Bug/python/bug.py @@ -36,10 +36,6 @@ class State: print_legs(self.legs) -def print_n_whitespaces(n: int) -> None: - print(" " * n, end="") - - def print_n_newlines(n: int) -> None: for _ in range(n): print() @@ -47,7 +43,7 @@ def print_n_newlines(n: int) -> None: def print_feelers(n_feelers: int, is_player: bool = True) -> None: for _ in range(4): - print_n_whitespaces(10) + print(" " * 10, end="") for _ in range(n_feelers): print("A " if is_player else "F ", end="") print() @@ -77,7 +73,7 @@ def print_body(has_tail: bool = False) -> None: def print_legs(n_legs: int) -> None: for _ in range(2): - print_n_whitespaces(5) + print(" " * 5, end="") for _ in range(n_legs): print(" L", end="") print() @@ -156,10 +152,8 @@ def handle_roll(diceroll: Literal[1, 2, 3, 4, 5, 6], state: State) -> bool: def main() -> None: - print_n_whitespaces(34) - print("BUG") - print_n_whitespaces(15) - print("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") + print(" " * 34 + "BUG") + print(" " * 15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") print_n_newlines(3) print("THE GAME BUG") diff --git a/17_Bullfight/python/bullfight.py b/17_Bullfight/python/bullfight.py index 0c85ae1e..2bb789e2 100644 --- a/17_Bullfight/python/bullfight.py +++ b/17_Bullfight/python/bullfight.py @@ -3,10 +3,6 @@ import random from typing import Dict, List, Literal, Tuple, Union -def print_n_whitespaces(n: int) -> None: - print(" " * n, end="") - - def print_n_newlines(n: int) -> None: for _ in range(n): print() @@ -73,10 +69,8 @@ def calculate_final_score( def print_header() -> None: - print_n_whitespaces(34) - print("BULL") - print_n_whitespaces(15) - print("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") + print(" " * 34 + "BULL") + print(" " * 15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") print_n_newlines(2) @@ -112,10 +106,10 @@ def print_intro() -> None: def ask_bool(prompt: str) -> bool: while True: - answer = input(prompt) - if answer == "YES": + answer = input(prompt).lower() + if answer == "yes": return True - elif answer == "NO": + elif answer == "no": return False else: print("INCORRECT ANSWER - - PLEASE TYPE 'YES' OR 'NO'.") diff --git a/19_Bunny/python/bunny.py b/19_Bunny/python/bunny.py index e6e267cf..31b09eb3 100755 --- a/19_Bunny/python/bunny.py +++ b/19_Bunny/python/bunny.py @@ -1,258 +1,21 @@ #!/usr/bin/env python3 +import json + # This data is meant to be read-only, so we are storing it in a tuple -DATA = ( - 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, -) +with open("data.json") as f: + DATA = tuple(json.load(f)) -def display_intro() -> None: - print(tab(33) + "BUNNY") - print(tab(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") +def print_intro() -> None: + print(" " * 33 + "BUNNY") + print(" " * 15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") print("\n\n") -def tab(column) -> str: - """Emulates the TAB command in BASIC. Returns a string with ASCII - codes for setting the cursor to the specified column.""" - return f"\r\33[{column}C" - - -def play() -> None: - display_intro() +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. @@ -286,7 +49,7 @@ def play() -> None: # position of a line segment. start = command # Position cursor at start - print(tab(start), end="") + print(" " * start, end="") # The following number, indicates the end of the segment. end = next(data) @@ -298,4 +61,4 @@ def play() -> None: if __name__ == "__main__": - play() + main() diff --git a/19_Bunny/python/data.json b/19_Bunny/python/data.json new file mode 100644 index 00000000..9e6ae902 --- /dev/null +++ b/19_Bunny/python/data.json @@ -0,0 +1,235 @@ +[ + 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 +] diff --git a/20_Buzzword/python/buzzword.py b/20_Buzzword/python/buzzword.py index 820b89df..48552edc 100644 --- a/20_Buzzword/python/buzzword.py +++ b/20_Buzzword/python/buzzword.py @@ -1,30 +1,28 @@ -###################################################################### -# -# 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 -# -###################################################################### +""" +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 = [ + words = [ [ "Ability", "Basal", @@ -85,13 +83,13 @@ def main() -> None: still_running = True while still_running: phrase = "" - for section in WORDS: + for section in words: if len(phrase) > 0: phrase += " " phrase += section[random.randint(0, len(section) - 1)] print(phrase) - print("") + print() response = input("? ") try: diff --git a/21_Calendar/python/calendar.py b/21_Calendar/python/calendar.py index 29faff01..922e348e 100644 --- a/21_Calendar/python/calendar.py +++ b/21_Calendar/python/calendar.py @@ -1,24 +1,23 @@ -######################################################## -# 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. -# -######################################################## +""" +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 @@ -65,7 +64,7 @@ def parse_input() -> Tuple[int, bool]: return day, leap_day -def calendar(weekday, leap_year): +def calendar(weekday: int, leap_year: bool) -> None: """ function to print a year's calendar. @@ -104,9 +103,8 @@ def calendar(weekday, leap_year): for n in range(1, 13): days_count += months_days[n - 1] print( - "** {} ****************** {} ****************** {} **\n".format( - days_count, months_names[n - 1], years_day - days_count - ) + f"** {days_count} ****************** {months_names[n - 1]} " + f"****************** {years_day - days_count} **\n" ) print(days) print(sep) @@ -121,7 +119,7 @@ def calendar(weekday, leap_year): break if d2 <= 0: - print("{}".format(" "), end=" ") + print(" ", end=" ") elif d2 < 10: print(f" {d2}", end=" ") else: @@ -151,8 +149,6 @@ def main() -> None: if __name__ == "__main__": main() -######################################################## -# ######################################################## # # Porting notes: diff --git a/22_Change/python/change.py b/22_Change/python/change.py index bba5a860..14ee4d4f 100644 --- a/22_Change/python/change.py +++ b/22_Change/python/change.py @@ -16,23 +16,17 @@ def print_centered(msg: str) -> None: def print_header(title: str) -> None: print_centered(title) - print_centered("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") - print() - print() - print() + 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.") - print() - print() + print("THE CORRECT CHANGE FOR ITEMS COSTING UP TO $100.\n\n") -def pennies_to_dollar_string(p): +def pennies_to_dollar_string(p: float) -> str: d = p / 100 - ds = f"${d:0.2f}" - return ds + return f"${d:0.2f}" def compute_change() -> None: @@ -94,19 +88,13 @@ def compute_change() -> None: print(f"{change_in_pennies} PENNY(S)") -def print_thanks() -> None: - print("THANK YOU, COME AGAIN.") - print() - print() - - def main() -> None: print_header("CHANGE") print_introduction() while True: compute_change() - print_thanks() + print("THANK YOU, COME AGAIN.\n\n") if __name__ == "__main__": diff --git a/23_Checkers/python/checkers.py b/23_Checkers/python/checkers.py index f05353ad..7ae2b7e5 100644 --- a/23_Checkers/python/checkers.py +++ b/23_Checkers/python/checkers.py @@ -37,10 +37,7 @@ def print_centered(msg: str) -> None: def print_header(title: str) -> None: print_centered(title) - print_centered("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") - print() - print() - print() + print_centered("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n") def get_coordinates(prompt: str) -> Tuple[int, int]: @@ -373,20 +370,15 @@ def print_instructions() -> None: 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() + print("JUMP. TYPE TWO NEGATIVE NUMBERS IF YOU CANNOT JUMP.\n\n\n") def print_human_won() -> None: - print() - print("YOU WIN.") + print("\nYOU WIN.") def print_computer_won() -> None: - print() - print("I WIN.") + print("\nI WIN.") def play_game() -> None: diff --git a/24_Chemist/python/chemist.py b/24_Chemist/python/chemist.py index a019eea7..561f67c3 100644 --- a/24_Chemist/python/chemist.py +++ b/24_Chemist/python/chemist.py @@ -11,16 +11,7 @@ import random MAX_LIVES = 9 -def print_with_tab(space_count: int, msg: str) -> None: - if space_count > 0: - spaces = " " * space_count - else: - spaces = "" - - print(spaces + msg) - - -def play_scenario(): +def play_scenario() -> bool: acid_amount = random.randint(1, 50) water_amount = 7 * acid_amount / 3 @@ -43,27 +34,23 @@ def play_scenario(): return True -def show_failure(): +def show_failure() -> None: print(" SIZZLE! YOU HAVE JUST BEEN DESALINATED INTO A BLOB") print(" OF QUIVERING PROTOPLASM!") -def show_success(): - print(" GOOD JOB! YOU MAY BREATHE NOW, BUT DON'T INHALE THE FUMES!") - print() +def show_success() -> None: + print(" GOOD JOB! YOU MAY BREATHE NOW, BUT DON'T INHALE THE FUMES!\n") -def show_ending(): +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_with_tab(33, "CHEMIST") - print_with_tab(15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") - print() - print() - print() + 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.") diff --git a/25_Chief/python/Chief.py b/25_Chief/python/chief.py similarity index 72% rename from 25_Chief/python/Chief.py rename to 25_Chief/python/chief.py index bf65d3bf..d8af319b 100644 --- a/25_Chief/python/Chief.py +++ b/25_Chief/python/chief.py @@ -1,5 +1,4 @@ def print_lightning_bolt() -> None: - print("*" * 36) n = 24 while n > 16: @@ -17,8 +16,7 @@ def print_lightning_bolt() -> None: print("*" * 36) -def print_solution(n: int) -> None: - +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( @@ -27,58 +25,56 @@ def print_solution(n: int) -> None: ) -def Game(): +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") - resp = float(input("\nWhat do you have? ")) - comp_guess = (((resp - 4) * 5) / 8) * 5 - 3 - resp2 = input(f"\nI bet your number was {comp_guess} was i right(Yes or No)? ") + 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 resp2 == "Yes" or resp2 == "YES" or resp2 == "yes": + 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) - input("") - + input() else: - resp3 = float(input("\nHUH!! what was you original number? ")) + original_number = float(input("\nHUH!! what was you original number? ")) - if resp3 == comp_guess: + 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) - input("") - + input() else: print("\nSo you think you're so smart, EH?") print("Now, Watch") - print_solution(resp3) + print_solution(original_number) - resp4 = input("\nNow do you believe me? ") + believe_me = input("\nNow do you believe me? ") - if resp4 == "Yes" or resp4 == "YES" or resp4 == "yes": + if believe_me.lower() == "yes": print("\nOk, Lets play again sometime bye!!!!") - input("") - + input() else: print("\nYOU HAVE MADE ME VERY MAD!!!!!") print("BY THE WRATH OF THE MATHEMATICS AND THE RAGE OF THE GODS") print("THERE SHALL BE LIGHTNING!!!!!!!") print_lightning_bolt() print("\nI Hope you believe me now, for your own sake") - input("") + input() if __name__ == "__main__": - 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 == "Yes" or play == "YES" or play == "yes": - Game() + if play.lower() == "yes": + game() else: print("Ok, Nevermind. Let me go back to my great slumber, Bye") - input("") + input() diff --git a/26_Chomp/python/chomp.py b/26_Chomp/python/chomp.py index e4d37981..69810a5f 100755 --- a/26_Chomp/python/chomp.py +++ b/26_Chomp/python/chomp.py @@ -1,13 +1,16 @@ #!/usr/bin/env python3 -# CHOMP -# -# Converted from BASIC to Python by Trevor Hobson + +""" +CHOMP + +Converted from BASIC to Python by Trevor Hobson +""" class Canvas: """For drawing the cookie""" - def __init__(self, width=9, height=9, fill="*"): + def __init__(self, width=9, height=9, fill="*") -> None: self._buffer = [] for _ in range(height): line = [] @@ -16,13 +19,13 @@ class Canvas: self._buffer.append(line) self._buffer[0][0] = "P" - def render(self): + def render(self) -> str: lines = [" 1 2 3 4 5 6 7 8 9"] for row, line in enumerate(self._buffer, start=1): lines.append(" " + str(row) + " " * 5 + " ".join(line)) return "\n".join(lines) - def chomp(self, r, c): + 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] == " ": @@ -36,7 +39,7 @@ class Canvas: return "Chomp" -def play_game(): +def play_game() -> None: """Play one round of the game""" players = 0 while players == 0: @@ -69,9 +72,9 @@ def play_game(): player = 0 alive = True while alive: - print("") + print() print(cookie.render()) - print("") + print() player += 1 if player > players: player = 1 @@ -125,7 +128,6 @@ def main() -> None: keep_playing = True while keep_playing: - play_game() keep_playing = input("\nAgain (1=Yes, 0=No!) ") == "1" diff --git a/27_Civil_War/python/Civilwar.py b/27_Civil_War/python/Civilwar.py index 4878bffa..14b88e01 100644 --- a/27_Civil_War/python/Civilwar.py +++ b/27_Civil_War/python/Civilwar.py @@ -1,9 +1,95 @@ +""" +Original game design: Cram, Goodie, Hibbard Lexington H.S. +Modifications: G. Paul, R. Hess (Ties), 1973 +""" +import enum import math -from typing import List +import random +from dataclasses import dataclass +from typing import Dict, List, Literal, Tuple -def tab(n: int) -> str: - return " " * n +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): + if self.is_player: + factor = 1 + (self.r - self.q) / (self.r + 1) + else: + factor = 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)) + tmp = math.floor(tmp * (1 + 1 / player1.morale) + 0.5) + return tmp + + +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: @@ -14,7 +100,15 @@ def get_choice(prompt: str, choices: List[str]) -> str: return choice -def main(): +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", @@ -73,42 +167,39 @@ def main(): ], ] - historical_data = [ - [], - ["BULL RUN", 18000, 18500, 1967, 2708, 1], - ["SHILOH", 40000.0, 44894.0, 10699, 13047, 3], - ["SEVEN DAYS", 95000.0, 115000.0, 20614, 15849, 3], - ["SECOND BULL RUN", 54000.0, 63000.0, 10000, 14000, 2], - ["ANTIETAM", 40000.0, 50000.0, 10000, 12000, 3], - ["FREDERICKSBURG", 75000.0, 120000.0, 5377, 12653, 1], - ["MURFREESBORO", 38000.0, 45000.0, 11000, 12000, 1], - ["CHANCELLORSVILLE", 32000, 90000.0, 13000, 17197, 2], - ["VICKSBURG", 50000.0, 70000.0, 12000, 19000, 1], - ["GETTYSBURG", 72500.0, 85000.0, 20000, 23000, 3], - ["CHICKAMAUGA", 66000.0, 60000.0, 18000, 16000, 2], - ["CHATTANOOGA", 37000.0, 60000.0, 36700.0, 5800, 2], - ["SPOTSYLVANIA", 62000.0, 110000.0, 17723, 18000, 2], - ["ATLANTA", 65000.0, 100000.0, 8500, 3700, 1], + 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), ] - sa = {} - dollars_available = {} - food_array = {} - salaries = {} - ammunition = {} - oa = {} - print(tab(26) + "CIVIL WAR") - print(tab(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 + 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 - sa[1] = 25 - sa[2] = 25 - sa[3] = 25 - sa[4] = 25 - party = -1 # number of players in the game + 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"] @@ -145,10 +236,12 @@ def main(): print() print() print("ARE THERE TWO GENERALS PRESENT ", end="") - bs = get_choice("(ANSWER YES OR NO) ", ["YES", "NO"]) - if bs == "YES": - party = 2 - elif bs == "NO": + 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!") @@ -165,282 +258,286 @@ def main(): print("AFTER REQUESTING A BATTLE, DO YOU WISH ", end="") print("BATTLE DESCRIPTIONS ", end="") xs = get_choice("(ANSWER YES OR NO) ", ["YES", "NO"]) - line = 0 - w = 0 - r1 = 0 - q1 = 0 - m3 = 0 - m4 = 0 - p1 = 0 - p2 = 0 - t1 = 0 - t2 = 0 - for i in range(1, 3): - dollars_available[i] = 0 - food_array[i] = 0 - salaries[i] = 0 - ammunition[i] = 0 - oa[i] = 0 - r2 = 0 - q2 = 0 - c6 = 0 - food = 0 - w0 = 0 - strategy_index = 0 - union_strategy_index = 0 - u = 0 - u2 = 0 - random_nb = 0 + 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(input("WHICH BATTLE DO YOU WISH TO SIMULATE? ")) + 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: - cs = historical_data[simulated_battle_index][0] - m1 = historical_data[simulated_battle_index][1] - m2 = historical_data[simulated_battle_index][2] - c1 = historical_data[simulated_battle_index][3] - c2 = historical_data[simulated_battle_index][4] - m = historical_data[simulated_battle_index][5] - u = 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 - i1 = 10 + (line - w) * 2 - i2 = 10 + (w - line) * 2 - # Money available - dollars_available[1] = 100 * math.floor( - (m1 * (100 - i1) / 2000) * (1 + (r1 - q1) / (r1 + 1)) + 0.5 - ) - dollars_available[2] = 100 * math.floor(m2 * (100 - i2) / 2000 + 0.5) - if bs == "YES": - dollars_available[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 + 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 {cs}") + print(f"THIS IS THE BATTLE OF {battle_name}") if xs != "NO": print("\n".join(battles[simulated_battle_index - 1])) else: - print(cs + " INSTANT REPLAY") + print(f"{battle_name} INSTANT REPLAY") print() - print(" \tCONFEDERACY\t UNION") - print(f"MEN\t {m5}\t\t {m6}") - print(f"MONEY\t ${dollars_available[1]}\t\t${dollars_available[2]}") - print(f"INFLATION\t {i1 + 15}%\t {i2}%") + 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 i in range(1, party + 1): - if bs == "YES" and i == 1: + 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 = int(input(" - FOOD...... ? ")) - if food < 0: - if r1 == 0: + 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 - food_array[i] = food + stats[player_index].food = food_input while True: - salaries[i] = int(input(" - SALARIES.. ? ")) - if salaries[i] >= 0: + stats[player_index].salaries = int(input(" - SALARIES.. ? ")) + if stats[player_index].salaries >= 0: break print("NEGATIVE VALUES NOT ALLOWED.") while True: - ammunition[i] = int(input(" - AMMUNITION ? ")) - if ammunition[i] >= 0: + stats[player_index].ammunition = int(input(" - AMMUNITION ? ")) + if stats[player_index].ammunition >= 0: break print("NEGATIVE VALUES NOT ALLOWED.") print() - if food_array[i] + salaries[i] + ammunition[i] > dollars_available[i]: - print("THINK AGAIN! YOU HAVE ONLY $" + dollars_available[i]) + 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 bs != "YES" or i == 2: + if not two_generals or player_index == 2: break print("UNION GENERAL---", end="") - for z in range(1, party + 1): - if bs == "YES": - if z == 1: + for player_index in range(1, party + 1): + if two_generals: + if player_index == 1: print("CONFEDERATE ", end="") else: print(" UNION ", end="") - # Find morale - o = (2 * math.pow(food_array[z], 2) + math.pow(salaries[z], 2)) / math.pow( - f1, 2 - ) + 1 - if o >= 10: + morale = get_morale(stats[player_index], stats[1 + player_index % 2]) + + if morale >= 10: print("MORALE IS HIGH") - elif o >= 5: + elif morale >= 5: print("MORALE IS FAIR") else: print("MORALE IS POOR") - if bs != "YES": + if not two_generals: break - oa[z] = o + stats[player_index].morale = morale # type: ignore - o2 = oa[2] - o = oa[1] + 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 m == 3: + if loaded_battle[5] == AttackState.OFFENSIVE: print("YOU ARE ON THE OFFENSIVE") - elif m == 1: + elif loaded_battle[5] == AttackState.DEFENSIVE: print("YOU ARE ON THE DEFENSIVE") else: print("BOTH SIDES ARE ON THE OFFENSIVE") print() # Choose strategies - if bs != "YES": + if not two_generals: while True: - strategy_index = int(input("YOUR STRATEGY ")) - if abs(strategy_index - 3) < 3: + stats[CONF].strategy = int(input("YOUR STRATEGY ")) + if abs(stats[CONF].strategy - 3) < 3: break - print(f"STRATEGY {strategy_index} NOT ALLOWED.") - if strategy_index == 5: + 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: - union_strategy_index = int(input("UNION STRATEGY IS ")) - if union_strategy_index > 0 and union_strategy_index < 5: + 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 = math.random() * 100 - for i in range(1, 5): - s0 += sa[i] + 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 - union_strategy_index = i - print(union_strategy_index) + stats[UNION].strategy = player_index + print(stats[UNION].strategy) else: - for i in range(1, 3): - if i == 1: + for player_index in [1, 2]: + if player_index == 1: print("CONFEDERATE STRATEGY ? ", end="") while True: - strategy_index = int(input()) - if abs(strategy_index - 3) < 3: + stats[CONF].strategy = int(input()) + if abs(stats[CONF].strategy - 3) < 3: break - print(f"STRATEGY {strategy_index} NOT ALLOWED.") + print(f"STRATEGY {stats[CONF].strategy} NOT ALLOWED.") print("YOUR STRATEGY ? ", end="") - if i == 2: - union_strategy_index = strategy_index - strategy_index = previous_strategy # noqa: F821 - if union_strategy_index != 5: + 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 = strategy_index # noqa: F841 + previous_strategy = stats[CONF].strategy # noqa: F841 print("UNION STRATEGY ? ", end="") - # Simulated losses - North - c6 = (2 * c2 / 5) * ( - 1 + 1 / (2 * (abs(union_strategy_index - strategy_index) + 1)) - ) - c6 = c6 * (1.28 + (5 * m2 / 6) / (ammunition[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 + + update_army(stats[UNION], stats[CONF], use_factor=False) + # Calculate simulated losses print() print() print() print("\t\tCONFEDERACY\tUNION") - c5 = (2 * c1 / 5) * ( - 1 + 1 / (2 * (abs(union_strategy_index - strategy_index) + 1)) - ) - c5 = math.floor(c5 * (1 + 1 / o) * (1.28 + f1 / (ammunition[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 + update_army(stats[CONF], stats[UNION], use_factor=True) if party == 1: - c6 = math.floor(17 * c2 * c1 / (c5 * 20)) - e2 = 5 * o + 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(c5) + "\t\t" + str(c6)) - print("DESERTIONS\t" + str(math.floor(e)) + "\t\t" + str(math.floor(e2))) + 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 bs == "YES": - print("COMPARED TO THE ACTUAL CASUALTIES AT " + str(cs)) + if two_generals: + print("COMPARED TO THE ACTUAL CASUALTIES AT " + str(battle_name)) print( "CONFEDERATE: " - + str(math.floor(100 * (c5 / c1) + 0.5)) + + str( + math.floor( + 100 * (stats[CONF].casualties / stats[CONF].army_c) + 0.5 + ) + ) + "% OF THE ORIGINAL" ) print( "UNION: " - + str(math.floor(100 * (c6 / c2) + 0.5)) + + str( + math.floor( + 100 * (stats[UNION].casualties / stats[UNION].army_c) + 0.5 + ) + ) + "% OF THE ORIGINAL" ) print() # Find who won - if u == 1 and u2 == 1 or (u != 1 and u2 != 1 and c5 + e == c6 + e2): + 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") - w0 += 1 - elif u == 1 or (u != 1 and u2 != 1 and c5 + e > c6 + e2): - print("THE UNION WINS " + str(cs)) + 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: - line += 1 + confederacy_lost += 1 else: - print("THE CONFEDERACY WINS " + str(cs)) + print(f"THE CONFEDERACY WINS {battle_name}") if simulated_battle_index != 0: - w += 1 + confederacy_win += 1 # Lines 2530 to 2590 from original are unreachable. if simulated_battle_index != 0: - t1 += c5 + e - t2 += c6 + e2 - p1 += c1 - p2 += c2 - q1 += food_array[1] + salaries[1] + ammunition[1] - q2 += food_array[2] + salaries[2] + ammunition[2] - r1 += m1 * (100 - i1) / 20 - r2 += m2 * (100 - i2) / 20 - m3 += m1 - m4 += m2 + 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 startegy of south gains 3*s, others lose s + # 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(1, 5): - if sa[i] <= 5: + for player_index in range(1, 5): + if confederate_strategy_prob_distribution[player_index] <= 5: continue - sa[i] -= 5 + confederate_strategy_prob_distribution[player_index] -= 5 s0 += s - sa[strategy_index] += s0 + confederate_strategy_prob_distribution[stats[CONF].strategy] += s0 - u = 0 - u2 = 0 + stats[CONF].excessive_losses = False + stats[UNION].excessive_losses = False print("---------------") continue @@ -450,27 +547,39 @@ def main(): print() print() print() - print(f"THE CONFEDERACY HAS WON {w} BATTLES AND LOST {line}") - if strategy_index == 5 or (union_strategy_index != 5 and w <= line): + 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 r1 > 0: - print(f"FOR THE {w + line + w0} BATTLES FOUGHT (EXCLUDING RERUNS)") + 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(p1 + 0.5)}\t{math.floor(p2 + 0.5)}") - print(f"SIMULATED LOSSES\t{math.floor(t1 + 0.5)}\t{math.floor(t2 + 0.5)}") + 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 * (t1 / p1) + 0.5)}\t{math.floor(100 * (t2 / p2) + 0.5)}" + 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 bs != "YES": + 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"{sa[1]} {sa[2]} {sa[3]} {sa[4]}") + 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__": diff --git a/28_Combat/python/combat.py b/28_Combat/python/combat.py index d6eed5eb..f8a7a5de 100644 --- a/28_Combat/python/combat.py +++ b/28_Combat/python/combat.py @@ -116,7 +116,7 @@ def attack_second() -> None: num_units = 0 unit_type = 0 - print("") + print() print(" YOU ME") print("ARMY ", end="") print("%-14s%s\n" % (usr_army, cpu_army), end="") @@ -176,7 +176,7 @@ def attack_second() -> None: plane_crash_win = True if not plane_crash_win: - print("") + print() print("FROM THE RESULTS OF BOTH OF YOUR ATTACKS,") if plane_crash_win or ( diff --git a/29_Craps/python/craps.py b/29_Craps/python/craps.py index 9d265e4e..dd01fa5e 100644 --- a/29_Craps/python/craps.py +++ b/29_Craps/python/craps.py @@ -20,10 +20,7 @@ def throw_dice() -> int: def main() -> None: print(" " * 33 + "Craps") - print(" " * 15 + "Creative Computing Morristown, New Jersey") - print() - print() - print() + 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.") diff --git a/30_Cube/python/cube.py b/30_Cube/python/cube.py index 393a494e..4928b08e 100755 --- a/30_Cube/python/cube.py +++ b/30_Cube/python/cube.py @@ -1,7 +1,10 @@ #!/usr/bin/env python3 -# CUBE -# -# Converted from BASIC to Python by Trevor Hobson + +""" +CUBE + +Converted from BASIC to Python by Trevor Hobson +""" import random from typing import Tuple diff --git a/31_Depth_Charge/python/depth_charge.py b/31_Depth_Charge/python/depth_charge.py index c0180c79..a177af9a 100644 --- a/31_Depth_Charge/python/depth_charge.py +++ b/31_Depth_Charge/python/depth_charge.py @@ -1,13 +1,16 @@ -# 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 +""" +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(): +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 @@ -18,14 +21,14 @@ def show_welcome(): print("Creative Computing Morristown, New Jersey\n\n".center(45)) -def get_num_charges(): +def get_num_charges() -> Tuple[int, int]: print("Depth Charge game\n") while True: - search_area = input("Dimensions of search area? ") + search_area_str = input("Dimensions of search area? ") # Make sure the input is an integer try: - search_area = int(search_area) + search_area = int(search_area_str) break except ValueError: print("Must enter an integer number. Please try again...") @@ -34,16 +37,16 @@ def get_num_charges(): return search_area, num_charges -def ask_for_new_game(): +def ask_for_new_game() -> None: answer = input("Another game (Y or N): ") if answer.lower().strip()[0] == "y": - start_new_game() + main() else: print("OK. Hope you enjoyed yourself") exit() -def show_shot_result(shot, location): +def show_shot_result(shot, location) -> None: result = "Sonar reports shot was " if shot[1] > location[1]: # y-direction result += "north" @@ -66,23 +69,23 @@ def show_shot_result(shot, location): return -def get_shot_input(): +def get_shot_input() -> Tuple[int, int, int]: while True: raw_guess = input("Enter coordinates: ") try: - x, y, z = raw_guess.split() + 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 [x, y, z]) + 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): +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.") @@ -111,10 +114,10 @@ def play_game(search_area, num_charges): ask_for_new_game() -def start_new_game(): +def main() -> None: search_area, num_charges = get_num_charges() play_game(search_area, num_charges) if __name__ == "__main__": - start_new_game() + main() diff --git a/32_Diamond/python/diamond.py b/32_Diamond/python/diamond.py index 88713a88..5ebcd2aa 100644 --- a/32_Diamond/python/diamond.py +++ b/32_Diamond/python/diamond.py @@ -7,16 +7,8 @@ Ported by Dave LeCompte """ -def print_with_tab(space_count: int, msg: str) -> None: - if space_count > 0: - spaces = " " * space_count - else: - spaces = "" - print(spaces + msg) - - def print_diamond(begin_width, end_width, step, width, count) -> None: - edgeString = "CC" + edge_string = "CC" fill = "!" n = begin_width @@ -24,10 +16,10 @@ def print_diamond(begin_width, end_width, step, width, count) -> None: line_buffer = " " * ((width - n) // 2) for across in range(count): for a in range(n): - if a >= len(edgeString): + if a >= len(edge_string): line_buffer += fill else: - line_buffer += edgeString[a] + line_buffer += edge_string[a] line_buffer += " " * ( (width * (across + 1) + (width - n) // 2) - len(line_buffer) ) @@ -38,11 +30,8 @@ def print_diamond(begin_width, end_width, step, width, count) -> None: def main() -> None: - print_with_tab(33, "DIAMOND") - print_with_tab(15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") - print() - print() - print() + 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()) diff --git a/33_Dice/python/dice.py b/33_Dice/python/dice.py index 1529a739..5c47f2b6 100644 --- a/33_Dice/python/dice.py +++ b/33_Dice/python/dice.py @@ -1,30 +1,27 @@ -######################################################## -# -# 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 -# -######################################################## +""" +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 @@ -48,7 +45,7 @@ def main() -> None: still_playing = True while still_playing: - print("") + print() n = int(input("How many rolls? ")) # Roll the dice n times @@ -64,7 +61,7 @@ def main() -> None: print(" %-14d%d" % (i, freq[i])) # Keep playing? - print("") + print() response = input("Try again? ") if len(response) > 0 and response.upper()[0] == "Y": # Clear out the frequency list diff --git a/34_Digits/python/Digits.py b/34_Digits/python/Digits.py index f018c539..16e2db7d 100644 --- a/34_Digits/python/Digits.py +++ b/34_Digits/python/Digits.py @@ -1,4 +1,5 @@ import random +from typing import List def print_intro() -> None: @@ -30,7 +31,7 @@ def print_instructions() -> None: print() -def read_10_numbers(): +def read_10_numbers() -> List[int]: print("TEN NUMBERS, PLEASE ? ") numbers = [] @@ -47,7 +48,7 @@ def read_10_numbers(): return numbers -def read_continue_choice(): +def read_continue_choice() -> bool: print("\nDO YOU WANT TO TRY AGAIN (1 FOR YES, 0 FOR NO) ? ") try: choice = int(input()) diff --git a/35_Even_Wins/python/evenwins.py b/35_Even_Wins/python/evenwins.py index b18806c7..066c7d57 100644 --- a/35_Even_Wins/python/evenwins.py +++ b/35_Even_Wins/python/evenwins.py @@ -1,41 +1,36 @@ -# evenwins.py +""" +This version of evenwins.bas based on game decscription and does *not* +follow the source. The computer chooses marbles at random. -# -# 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. -# +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. -# global variables -marbles_in_middle = -1 -human_marbles = -1 -computer_marbles = -1 -whose_turn = "" +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. +""" -def serious_error(msg): - """ - Only call this function during development for serious errors that are due - to mistakes in the program. Should never be called during a regular game. - """ - print("serious_error: " + msg) - exit(1) +from dataclasses import dataclass +from typing import Literal, Tuple + +PlayerType = Literal["human", "computer"] -def welcome_screen(): +@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() @@ -50,22 +45,19 @@ def welcome_screen(): print() -def marbles_str(n): +def marbles_str(n: int) -> str: if n == 1: return "1 marble" return f"{n} marbles" -def choose_first_player(): - global whose_turn +def choose_first_player() -> PlayerType: while True: ans = input("Do you want to play first? (y/n) --> ") if ans == "y": - whose_turn = "human" - return + return "human" elif ans == "n": - whose_turn = "computer" - return + return "computer" else: print() print('Please enter "y" if you want to play first,') @@ -73,18 +65,15 @@ def choose_first_player(): print() -def next_player(): - global whose_turn +def toggle_player(whose_turn: PlayerType) -> PlayerType: if whose_turn == "human": - whose_turn = "computer" - elif whose_turn == "computer": - whose_turn = "human" + return "computer" else: - serious_error(f"play_game: unknown player {whose_turn}") + return "human" -# Converts a string s to an int, if possible. -def to_int(s): +def to_int(s: str) -> Tuple[bool, int]: + """Convert a string s to an int, if possible.""" try: n = int(s) return True, n @@ -92,142 +81,108 @@ def to_int(s): return False, 0 -def print_board() -> None: - global marbles_in_middle - global human_marbles - global computer_marbles +def print_board(marbles: MarbleCounts) -> None: print() - print(f" marbles in the middle: {marbles_in_middle} " + marbles_in_middle * "*") - print(f" # marbles you have: {human_marbles}") - print(f"# marbles computer has: {computer_marbles}") + print(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(): - global marbles_in_middle - global human_marbles - - # get number in range 1 to min(4, marbles_in_middle) - max_choice = min(4, marbles_in_middle) +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() - print(f" Please enter a whole number from 1 to {max_choice}") - print() + print(f"\n Please enter a whole number from 1 to {max_choice}\n") continue if n < 1: - print() - print(" You must take at least 1 marble!") - print() + print("\n You must take at least 1 marble!\n") continue if n > max_choice: - print() - print(f" You can take at most {marbles_str(max_choice)}") - print() + print(f"\n You can take at most {marbles_str(max_choice)}\n") continue - print() - print(f"Okay, taking {marbles_str(n)} ...") - marbles_in_middle -= n - human_marbles += n + print(f"\nOkay, taking {marbles_str(n)} ...") + marbles.middle -= n + marbles.human += n return -def game_over(): - global marbles_in_middle - global human_marbles - global computer_marbles +def game_over(marbles: MarbleCounts) -> None: print() print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") print("!! All the marbles are taken: Game Over!") print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") print() - print_board() - if human_marbles % 2 == 0: + print_board(marbles) + if marbles.human % 2 == 0: print("You are the winner! Congratulations!") else: print("The computer wins: all hail mighty silicon!") - print("") + print() -def computer_turn(): - global marbles_in_middle - global computer_marbles - global human_marbles - +def computer_turn(marbles: MarbleCounts) -> None: marbles_to_take = 0 print("It's the computer's turn ...") - r = marbles_in_middle - 6 * int(marbles_in_middle / 6) # line 500 + r = marbles.middle - 6 * int(marbles.middle / 6) - if int(human_marbles / 2) == human_marbles / 2: # line 510 - if r < 1.5 or r > 5.3: # lines 710 and 720 + if int(marbles.human / 2) == marbles.human / 2: + if r < 1.5 or r > 5.3: marbles_to_take = 1 else: marbles_to_take = r - 1 - elif marbles_in_middle < 4.2: # line 580 - marbles_to_take = marbles_in_middle - elif r > 3.4: # line 530 + 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_in_middle -= marbles_to_take - computer_marbles += marbles_to_take + marbles.middle -= marbles_to_take + marbles.computer += marbles_to_take -def play_game(): - global marbles_in_middle - global human_marbles - global computer_marbles - - # initialize the game state - marbles_in_middle = 27 - human_marbles = 0 - computer_marbles = 0 - print_board() +def play_game(whose_turn: PlayerType) -> None: + marbles = MarbleCounts(middle=27, human=0, computer=0) + print_board(marbles) while True: - if marbles_in_middle == 0: - game_over() + if marbles.middle == 0: + game_over(marbles) return elif whose_turn == "human": - human_turn() - print_board() - next_player() + human_turn(marbles) + print_board(marbles) + whose_turn = toggle_player(whose_turn) elif whose_turn == "computer": - computer_turn() - print_board() - next_player() + computer_turn(marbles) + print_board(marbles) + whose_turn = toggle_player(whose_turn) else: - serious_error(f"play_game: unknown player {whose_turn}") + raise Exception(f"whose_turn={whose_turn} is not 'human' or 'computer'") def main() -> None: - global whose_turn - - welcome_screen() + print_intro() while True: - choose_first_player() - play_game() + whose_turn = choose_first_player() + play_game(whose_turn) - # ask if the user if they want to play again print() - again = input("Would you like to play again? (y/n) --> ") + again = input("Would you like to play again? (y/n) --> ").lower() if again == "y": - print() - print("Ok, let's play again ...") - print() + print("\nOk, let's play again ...\n") else: - print() - print("Ok, thanks for playing ... goodbye!") - print() + print("\nOk, thanks for playing ... goodbye!\n") return diff --git a/36_Flip_Flop/python/flipflop.py b/36_Flip_Flop/python/flipflop.py index 6472a58f..8ea7e5b1 100644 --- a/36_Flip_Flop/python/flipflop.py +++ b/36_Flip_Flop/python/flipflop.py @@ -61,7 +61,7 @@ def print_instructions() -> None: print("11 (ELEVEN).\n") -def main(): +def main() -> None: q = random.random() print("HERE IS THE STARTING LINE OF X'S.\n") @@ -75,9 +75,9 @@ def main(): if legal_move: print(" ".join([str(i) for i in range(1, 11)])) print(" ".join(row[1:]) + "\n") - m = input("INPUT THE NUMBER\n") + m_str = input("INPUT THE NUMBER\n") try: - m = int(m) + m = int(m_str) if m > 11 or m < 0: raise ValueError() except ValueError: diff --git a/38_Fur_Trader/python/furtrader.py b/38_Fur_Trader/python/furtrader.py index f65bf518..190b7c3f 100755 --- a/38_Fur_Trader/python/furtrader.py +++ b/38_Fur_Trader/python/furtrader.py @@ -1,7 +1,8 @@ -#! /usr/bin/env python3 +#!/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 @@ -22,13 +23,7 @@ FORT_NEWYORK = 3 FORT_NAMES = ["HOCHELAGA (MONTREAL)", "STADACONA (QUEBEC)", "NEW YORK"] -def print_at_column(column: int, words: str) -> None: - """Print the words at the specified column""" - spaces = " " * column # make a fat string of spaces - print(spaces + words) - - -def show_introduction(): +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") @@ -36,16 +31,16 @@ def show_introduction(): 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("") + print() -def get_fort_choice(): +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() 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.") @@ -68,9 +63,9 @@ def get_fort_choice(): return result -def show_fort_comment(which_fort): +def show_fort_comment(which_fort) -> None: """Print the description for the fort""" - print("") + print() if which_fort == FORT_MONTREAL: print("YOU HAVE CHOSEN THE EASIEST ROUTE. HOWEVER, THE FORT") print("IS FAR FROM ANY SEAPORT. THE VALUE") @@ -89,15 +84,15 @@ def show_fort_comment(which_fort): else: print("Internal error #1, fort " + str(which_fort) + " does not exist") sys.exit(1) # you have a bug - print("") + print() -def get_yes_or_no(): +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 = 0 + result = "" while result not in ("Y", "N"): print("ANSWER YES OR NO") player_choice = input(">> ") @@ -109,32 +104,30 @@ def get_yes_or_no(): return result -def get_furs_purchase(): +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 = [] + results: List[int] = [] print("YOUR " + str(MAX_FURS) + " FURS ARE DISTRIBUTED AMONG THE FOLLOWING") print("KINDS OF PELTS: MINK, BEAVER, ERMINE AND FOX.") - print("") + print() - for i in range(len(FUR_NAMES)): - print("HOW MANY " + FUR_NAMES[i] + " DO YOU HAVE") + 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 - i -= 1 + except Exception: # invalid input, prompt again by re-looping + pass return results -if __name__ == "__main__": - - print_at_column(31, "FUR TRADER") - print_at_column(15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") - print_at_column(15, "(Ported to Python Oct 2012 krt@krt.com.au)") +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") game_state = "starting" @@ -145,7 +138,7 @@ if __name__ == "__main__": if game_state == "starting": show_introduction() - player_funds = 600 # Initial player start money + player_funds: float = 600 # Initial player start money player_furs = [0, 0, 0, 0] # Player fur inventory print("DO YOU WISH TO TRADE FURS?") @@ -155,13 +148,13 @@ if __name__ == "__main__": game_state = "trading" elif game_state == "trading": - print("") + print() print("YOU HAVE $ %1.2f IN SAVINGS" % (player_funds)) print("AND " + str(MAX_FURS) + " FURS TO BEGIN THE EXPEDITION") player_furs = get_furs_purchase() if sum(player_furs) > MAX_FURS: - print("") + print() print("YOU MAY NOT HAVE THAT MANY FURS.") print("DO NOT TRY TO CHEAT. I CAN ADD.") print("YOU MUST START AGAIN.") @@ -178,7 +171,7 @@ if __name__ == "__main__": game_state = "travelling" elif game_state == "travelling": - print("") + print() if which_fort == FORT_MONTREAL: mink_price = ( int((0.2 * random.random() + 0.70) * 100 + 0.5) / 100 @@ -234,7 +227,7 @@ if __name__ == "__main__": ) sys.exit(1) # you have a bug - print("") + print() print("SUPPLIES AT FORT STADACONA COST $125.00.") print("YOUR TRAVEL EXPENSES TO STADACONA WERE $15.00.") player_funds -= 140 @@ -281,7 +274,7 @@ if __name__ == "__main__": ) sys.exit(1) # you have a bug - print("") + print() print("SUPPLIES AT NEW YORK COST $85.00.") print("YOUR TRAVEL EXPENSES TO NEW YORK WERE $25.00.") player_funds -= 105 @@ -296,7 +289,7 @@ if __name__ == "__main__": ermine_value = ermine_price * player_furs[FUR_ERMINE] mink_value = mink_price * player_furs[FUR_MINK] - print("") + 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)) @@ -304,15 +297,19 @@ if __name__ == "__main__": player_funds += beaver_value + fox_value + ermine_value + mink_value - print("") + print() print( "YOU NOW HAVE $ %1.2f INCLUDING YOUR PREVIOUS SAVINGS" % (player_funds) ) - print("") + 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() diff --git a/39_Golf/python/golf.py b/39_Golf/python/golf.py index 9339c236..9132da2e 100644 --- a/39_Golf/python/golf.py +++ b/39_Golf/python/golf.py @@ -129,8 +129,8 @@ def clear_console() -> None: class Point(NamedTuple): - X: int - Y: int + x: int + y: int class GameObjType(enum.Enum): @@ -183,14 +183,14 @@ class HoleGeometry(NamedTuple): @dataclass class Plot: - X: int - Y: int - Offline: int + 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)) + 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: @@ -392,9 +392,9 @@ ace = 0b10000000000000 class Golf: - BALL: Ball - HOLE_NUM: int = 0 - STROKE_NUM: int = 0 + ball: Ball + hole_num: int = 0 + stroke_num: int = 0 handicap: int = 0 player_difficulty: int = 0 hole_geometry: HoleGeometry @@ -491,10 +491,10 @@ class Golf: self.new_hole() def new_hole(self) -> None: - self.HOLE_NUM += 1 - self.STROKE_NUM = 0 + self.hole_num += 1 + self.stroke_num = 0 - info: HoleInfo = CourseInfo[self.HOLE_NUM] + info: HoleInfo = CourseInfo[self.hole_num] yards: int = info.yards # from tee to cup @@ -517,19 +517,19 @@ class Golf: GameObjType.ROUGH, ) - self.BALL = Ball(0, yards, 0, GameObjType.BALL) + 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(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}." + f"Hole #{self.hole_num}. You are at the tee. Distance {info.yards} yards, par {info.par}." ) print(info.description) @@ -537,7 +537,7 @@ class Golf: def set_putter_and_stroke(self, strength: float) -> None: putter = self.clubs[self.putt] - self.Stroke((putter[1] * (strength / 10.0)), self.putt) + self.stroke((putter[1] * (strength / 10.0)), self.putt) def ask_gauge(self, c: int) -> None: self.club = self.clubs[c] @@ -555,13 +555,13 @@ class Golf: ) def make_stroke(self, strength: float, c: int) -> None: - self.Stroke((self.club[1] * (strength / 10.0)), c) + 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 + 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]") @@ -579,13 +579,13 @@ class Golf: else: self.ask("What club do you choose? (1-10)", 1, 10, self.ask_gauge) - def Stroke(self, clubAmt: float, clubIndex: int) -> None: - self.STROKE_NUM += 1 + 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 (clubAmt > 210) and odds(30): + if (self.stroke_num == 1) and (club_amt > 210) and odds(30): print('"...Fore !"') # dub @@ -596,37 +596,37 @@ class Golf: # 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) + self.is_in_rough(self.ball) + or self.is_in_hazard(self.ball, GameObjType.SAND) ) - and not (clubIndex == 8 or clubIndex == 9) + and not (club_index == 8 or club_index == 9) and odds(40) ): flags |= dub # trap difficulty if ( - self.is_in_hazard(self.BALL, GameObjType.SAND) + 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 playerDifficulty then increase chance to 30% - # if it's a putt & putting is a playerDifficulty increase to 30% + # 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% - randHookSlice: bool + 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)) + or (self.player_difficulty == 5 and self.is_on_green(self.ball)) ): - randHookSlice = odds(30) + rand_hook_slice = odds(30) else: - randHookSlice = odds(10) + rand_hook_slice = odds(10) - if randHookSlice: + if rand_hook_slice: if self.player_difficulty == 1: if odds(80): flags |= hook @@ -650,7 +650,7 @@ class Golf: # 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: + if CourseInfo[self.hole_num].par == 3 and odds(10) and self.stroke_num == 1: flags |= ace # distance: @@ -659,29 +659,29 @@ class Golf: # 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% + # If poor distance is a known player_difficulty, then reduce distance by 10% distance: float rnd = random.randint(1, 101) if self.handicap < 15: if rnd <= 25: - distance = clubAmt - (clubAmt * (self.handicap / 100.0)) + distance = club_amt - (club_amt * (self.handicap / 100.0)) elif rnd > 25 and rnd <= 75: - distance = clubAmt + distance = club_amt else: - distance = clubAmt + (clubAmt * 0.10) + distance = club_amt + (club_amt * 0.10) else: if rnd <= 75: - distance = clubAmt - (clubAmt * (self.handicap / 100.0)) + distance = club_amt - (club_amt * (self.handicap / 100.0)) else: - distance = clubAmt + distance = club_amt if self.player_difficulty == 3 and odds(80): # poor distance distance = distance * 0.80 if (flags & luck) == luck: - distance = clubAmt + distance = club_amt # angle # For all strokes, there's a possible "drift" of 4 degrees @@ -695,43 +695,45 @@ class Golf: if (flags & luck) == luck: angle = 0 - plot = self.plot_ball(self.BALL, distance, angle) + plot = self.plot_ball(self.ball, distance, angle) # calculate a new location - if (flags & luck) == luck and plot.Y > 0: - plot.Y = 2 + 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 + Ball(plot.x, plot.y, plot.offline, GameObjType.BALL), flags ) self.interpret_results(plot, flags) - def plot_ball(self, ball: Ball, strokeDistance: float, degreesOff: float) -> Plot: - cupVector = Point(0, -1) - radFromCup = math.atan2(ball.Y, ball.X) - math.atan2(cupVector.Y, cupVector.X) - radFromBall = radFromCup - math.pi + 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 = strokeDistance - adjacent = math.cos(radFromBall + to_radians(degreesOff)) * hypotenuse + 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)) - newPos: Point - if to_degrees_360(radFromBall + to_radians(degreesOff)) > 180: - newPos = Point(int(ball.X - opposite), int(ball.Y - adjacent)) + 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: - newPos = Point(int(ball.X + opposite), int(ball.Y - adjacent)) + new_pos = Point(int(ball.X + opposite), int(ball.Y - adjacent)) - return Plot(newPos.X, newPos.Y, int(opposite)) + return Plot(new_pos.x, new_pos.y, int(opposite)) def interpret_results(self, plot: Plot, flags: int) -> None: - cupDistance: int = int( + cup_distance: int = int( get_distance( - Point(plot.X, plot.Y), + Point(plot.x, plot.y), Point(self.hole_geometry.cup.X, self.hole_geometry.cup.Y), ) ) - travelDistance: int = int( - get_distance(Point(plot.X, plot.Y), Point(self.BALL.X, self.BALL.Y)) + travel_distance: int = int( + get_distance(Point(plot.x, plot.y), Point(self.ball.X, self.ball.Y)) ) print(" ") @@ -739,12 +741,12 @@ class Golf: if (flags & ace) == ace: print("Hole in One! You aced it.") self.score_card_record_stroke(Ball(0, 0, 0, GameObjType.BALL)) - self.ReportCurrentScore() + 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.score_card_record_stroke(self.ball) self.tee_up() return @@ -754,19 +756,19 @@ class Golf: else: msg = "Your ball is lost in the water." print(msg + " Take a penalty stroke.") - self.score_card_record_stroke(self.BALL) + 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.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.score_card_record_stroke(self.ball) self.tee_up() return @@ -776,8 +778,8 @@ class Golf: else: msg = "It's in!" print(msg) - self.score_card_record_stroke(Ball(plot.X, plot.Y, 0, GameObjType.BALL)) - self.ReportCurrentScore() + self.score_card_record_stroke(Ball(plot.x, plot.y, 0, GameObjType.BALL)) + self.report_current_score() return if ((flags & slice_) == slice_) and not ((flags & on_green) == on_green): @@ -785,22 +787,22 @@ class Golf: bad = "badly" else: bad = "" - print(f"You sliced{bad}: {plot.Offline} yards offline.") + print(f"You sliced{bad}: {plot.offline} yards offline.") if ((flags & hook) == hook) and not ((flags & on_green) == on_green): if (flags & out_of_bounds) == out_of_bounds: bad = "badly" else: bad = "" - print(f"You hooked{bad}: {plot.Offline} yards offline.") + print(f"You hooked{bad}: {plot.offline} yards offline.") - if self.STROKE_NUM > 1: - prevBall = self.score_card_get_previous_stroke() + if self.stroke_num > 1: + prev_ball = self.score_card_get_previous_stroke() d1 = get_distance( - Point(prevBall.X, prevBall.Y), + Point(prev_ball.X, prev_ball.Y), Point(self.hole_geometry.cup.X, self.hole_geometry.cup.Y), ) - d2 = cupDistance + d2 = cup_distance if d2 > d1: print("Too much club.") @@ -811,55 +813,55 @@ class Golf: print("You're in a sand trap.") if (flags & on_green) == on_green: - if cupDistance < 4: - pd = str(cupDistance * 3) + " feet" + if cup_distance < 4: + pd = str(cup_distance * 3) + " feet" else: - pd = f"{cupDistance} yards" + 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 {travelDistance} yards. " - f"It's {cupDistance} yards from the cup." + 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.score_card_record_stroke(Ball(plot.x, plot.y, 0, GameObjType.BALL)) - self.BALL = Ball(plot.X, plot.Y, 0, GameObjType.BALL) + self.ball = Ball(plot.x, plot.y, 0, GameObjType.BALL) self.tee_up() - def ReportCurrentScore(self) -> None: - par = CourseInfo[self.HOLE_NUM].par - if len(self.score_card[self.HOLE_NUM]) == par + 1: + 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: + if len(self.score_card[self.hole_num]) == par: print("Par. Nice.") - if len(self.score_card[self.HOLE_NUM]) == (par - 1): + 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): + 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): + if len(self.score_card[self.hole_num]) == (par - 3): print("Double Eagle! Unbelievable.") - totalPar: int = 0 - for i in range(1, self.HOLE_NUM + 1): - totalPar += CourseInfo[i].par + total_par: int = 0 + for i in range(1, self.hole_num + 1): + total_par += CourseInfo[i].par print(" ") print("-----------------------------------------------------") - if self.HOLE_NUM > 1: + if self.hole_num > 1: hole_str = "holes" else: hole_str = "hole" print( - f" Total par for {self.HOLE_NUM} {hole_str} is: {totalPar}. " + 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: + if self.hole_num == 18: self.game_over() else: time.sleep(2) @@ -930,10 +932,10 @@ class Golf: 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) + 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] + return self.score_card[self.hole_num][len(self.score_card[self.hole_num]) - 1] def score_card_get_total(self) -> int: total: int = 0 @@ -986,9 +988,7 @@ class Golf: print(" ") def quit_game(self) -> None: - print("") - print("Looks like rain. Goodbye!") - print("") + print("\nLooks like rain. Goodbye!\n") return def game_over(self) -> None: diff --git a/40_Gomoko/python/Gomoko.py b/40_Gomoko/python/Gomoko.py index db3e02b7..7036d7f2 100644 --- a/40_Gomoko/python/Gomoko.py +++ b/40_Gomoko/python/Gomoko.py @@ -2,10 +2,6 @@ import random from typing import Any, List, Tuple -def print_n_whitespaces(n: int) -> None: - print(" " * n, end="") - - def print_board(A: List[List[Any]], n: int) -> None: """PRINT THE BOARD""" for i in range(n): @@ -23,25 +19,17 @@ def check_move(_I, _J, _N) -> bool: # 910 def print_banner() -> None: - print_n_whitespaces(33) - print("GOMOKU") - print_n_whitespaces(15) - print("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") - print() - print() - print() - print("WELCOME TO THE ORIENTAL GAME OF GOMOKO.") - print() + 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'.") - print() + 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.") - print() + print("TO END THE GAME, TYPE -1,-1 FOR YOUR MOVE.\n") def get_board_dimensions() -> int: @@ -74,10 +62,10 @@ def initialize_board(n: int) -> List[List[int]]: # Initialize the board board = [] for _x in range(n): - subA = [] + sub_a = [] for _y in range(n): - subA.append(0) - board.append(subA) + sub_a.append(0) + board.append(sub_a) return board diff --git a/41_Guess/python/guess.py b/41_Guess/python/guess.py index 8786119b..8c1775f4 100644 --- a/41_Guess/python/guess.py +++ b/41_Guess/python/guess.py @@ -1,24 +1,22 @@ -######################################################## -# -# 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. -# -######################################################## +""" +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 @@ -28,13 +26,14 @@ 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(): +def limit_set() -> Tuple[int, int]: print(" Guess") print("Creative Computing Morristown, New Jersey") print("\n\n\n") diff --git a/42_Gunner/python/gunner.py b/42_Gunner/python/gunner.py index ad421e7c..06d55d18 100644 --- a/42_Gunner/python/gunner.py +++ b/42_Gunner/python/gunner.py @@ -71,12 +71,10 @@ def gunner() -> None: return -if __name__ == "__main__": +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") @@ -85,7 +83,11 @@ if __name__ == "__main__": while True: gunner() - Y = input("TRY AGAIN (Y OR N)? ") - if Y != "Y": + not_again = input("TRY AGAIN (Y OR N)? ").upper() != "Y" + if not_again: print("\nOK. RETURN TO BASE CAMP.") break + + +if __name__ == "__main__": + main() diff --git a/43_Hammurabi/python/hamurabi.py b/43_Hammurabi/python/hamurabi.py index 5c476ca9..79de9ac1 100644 --- a/43_Hammurabi/python/hamurabi.py +++ b/43_Hammurabi/python/hamurabi.py @@ -1,31 +1,32 @@ from random import random, seed -def gen_random(): +def gen_random() -> int: return int(random() * 5) + 1 -def bad_input_850(): +def bad_input_850() -> None: print("\nHAMURABI: I CANNOT DO WHAT YOU WISH.") print("GET YOURSELF ANOTHER STEWARD!!!!!") -def bad_input_710(S): +def bad_input_710(grain_bushels: int) -> None: print("HAMURABI: THINK AGAIN. YOU HAVE ONLY") - print(S, "BUSHELS OF GRAIN. NOW THEN,") + print(f"{grain_bushels} BUSHELS OF GRAIN. NOW THEN,") -def bad_input_720(A): - print("HAMURABI: THINK AGAIN. YOU OWN ONLY", A, "ACRES. NOW THEN,") +def bad_input_720(acres: float) -> None: + print(f"HAMURABI: THINK AGAIN. YOU OWN ONLY {acres} ACRES. NOW THEN,") -def national_fink(): +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): # emulate BASIC input. It rejects non-numeric values +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? ") diff --git a/44_Hangman/python/hangman.py b/44_Hangman/python/hangman.py index cbceb6a2..3e84ce8d 100755 --- a/44_Hangman/python/hangman.py +++ b/44_Hangman/python/hangman.py @@ -1,15 +1,19 @@ #!/usr/bin/env python3 -# HANGMAN -# -# Converted from BASIC to Python by Trevor Hobson and Daniel Piron + +""" +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=12, height=12, fill=" "): + def __init__(self, width: int = 12, height: int = 12, fill: str = " ") -> None: self._buffer = [] for _ in range(height): line = [] @@ -19,12 +23,12 @@ class Canvas: self.clear() - def clear(self, fill=" "): + def clear(self, fill: str = " ") -> None: for row in self._buffer: for x in range(len(row)): row[x] = fill - def render(self): + def render(self) -> str: lines = [] for line in self._buffer: # Joining by the empty string ("") smooshes all of the @@ -32,13 +36,13 @@ class Canvas: lines.append("".join(line)) return "\n".join(lines) - def put(self, s, x, y): + 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): +def init_gallows(canvas: Canvas) -> None: for i in range(12): canvas.put("X", 0, i) for i in range(7): @@ -46,7 +50,7 @@ def init_gallows(canvas): canvas.put("X", 6, 1) -def draw_head(canvas): +def draw_head(canvas: Canvas) -> None: canvas.put("-", 5, 2) canvas.put("-", 6, 2) canvas.put("-", 7, 2) @@ -59,47 +63,47 @@ def draw_head(canvas): canvas.put("-", 7, 4) -def draw_body(canvas): +def draw_body(canvas: Canvas) -> None: for i in range(5, 9, 1): canvas.put("X", 6, i) -def draw_right_arm(canvas): +def draw_right_arm(canvas: Canvas) -> None: for i in range(3, 7): canvas.put("\\", i - 1, i) -def draw_left_arm(canvas): +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): +def draw_right_leg(canvas: Canvas) -> None: canvas.put("/", 5, 9) canvas.put("/", 4, 10) -def draw_left_leg(canvas): +def draw_left_leg(canvas: Canvas) -> None: canvas.put("\\", 7, 9) canvas.put("\\", 8, 10) -def draw_left_hand(canvas): +def draw_left_hand(canvas: Canvas) -> None: canvas.put("\\", 10, 2) -def draw_right_hand(canvas): +def draw_right_hand(canvas: Canvas) -> None: canvas.put("/", 2, 2) -def draw_left_foot(canvas): +def draw_left_foot(canvas: Canvas) -> None: canvas.put("\\", 9, 11) canvas.put("-", 10, 11) -def draw_right_foot(canvas): +def draw_right_foot(canvas: Canvas) -> None: canvas.put("-", 2, 11) canvas.put("/", 3, 11) @@ -172,11 +176,11 @@ words = [ ] -def play_game(guess_target): +def play_game(guess_target: str) -> None: """Play one round of the game""" wrong_guesses = 0 guess_progress = ["-"] * len(guess_target) - guess_list = [] + guess_list: List[str] = [] gallows = Canvas() init_gallows(gallows) @@ -206,7 +210,7 @@ def play_game(guess_target): ] for i in indices: guess_progress[i] = guess_letter - if guess_progress == guess_target: + if "".join(guess_progress) == guess_target: print("You found the word!") break else: diff --git a/45_Hello/python/hello.py b/45_Hello/python/hello.py index 8875a86f..ccf0501c 100644 --- a/45_Hello/python/hello.py +++ b/45_Hello/python/hello.py @@ -9,17 +9,10 @@ Ported by Dave LeCompte """ import time +from typing import Optional, Tuple -def print_with_tab(space_count: int, msg: str) -> None: - if space_count > 0: - spaces = " " * space_count - else: - spaces = "" - print(spaces + msg) - - -def get_yes_or_no(): +def get_yes_or_no() -> Tuple[bool, Optional[bool], str]: msg = input() if msg.upper() == "YES": return True, True, msg @@ -29,7 +22,7 @@ def get_yes_or_no(): return False, None, msg -def ask_enjoy_question(user_name): +def ask_enjoy_question(user_name: str) -> None: print(f"HI THERE, {user_name}, ARE YOU ENJOYING YOURSELF HERE?") while True: @@ -48,7 +41,7 @@ def ask_enjoy_question(user_name): print("PLEASE ANSWER 'YES' OR 'NO'. DO YOU LIKE IT HERE?") -def prompt_for_problems(user_name): +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") @@ -58,7 +51,7 @@ def prompt_for_problems(user_name): return problem_type -def prompt_too_much_or_too_little(): +def prompt_too_much_or_too_little() -> Tuple[bool, Optional[bool]]: answer = input().upper() if answer == "TOO MUCH": return True, True @@ -67,7 +60,7 @@ def prompt_too_much_or_too_little(): return False, None -def solve_sex_problem(user_name): +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() @@ -85,31 +78,31 @@ def solve_sex_problem(user_name): print("WITH 'TOO MUCH' OR 'TOO LITTLE'. WHICH IS IT?") -def solve_money_problem(user_name): +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): +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): +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, problem_type): +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): +def ask_question_loop(user_name: str) -> None: while True: problem_type = prompt_for_problems(user_name) if problem_type == "SEX": @@ -137,7 +130,7 @@ def ask_question_loop(user_name): print(f"JUST A SIMPLE 'YES' OR 'NO' PLEASE, {user_name}.") -def ask_for_fee(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.") @@ -166,26 +159,21 @@ def ask_for_fee(user_name): print("PLEASE RESPOND WITH 'YES' or 'NO'.") -def unhappy_goodbye(user_name): +def unhappy_goodbye(user_name: str) -> None: print() print(f"TAKE A WALK, {user_name}.") print() print() -def happy_goodbye(user_name): +def happy_goodbye(user_name: str) -> None: print(f"NICE MEETING YOU, {user_name}, HAVE A NICE DAY.") def main() -> None: - print_with_tab(33, "HELLO") - print_with_tab(15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") - print() - print() - print() - print("HELLO. MY NAME IS CREATIVE COMPUTER.") - print() - print() + 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() diff --git a/46_Hexapawn/python/hexapawn.py b/46_Hexapawn/python/hexapawn.py index 8c333447..9a2801c3 100644 --- a/46_Hexapawn/python/hexapawn.py +++ b/46_Hexapawn/python/hexapawn.py @@ -44,8 +44,8 @@ Port to Python by Dave LeCompte # BoardLayout matches the current board, as well as removing losing move # have been moved into methods of this class. -import collections import random +from typing import Iterator, List, NamedTuple, Optional, Tuple PAGE_WIDTH = 64 @@ -53,9 +53,13 @@ HUMAN_PIECE = 1 EMPTY_SPACE = 0 COMPUTER_PIECE = -1 -ComputerMove = collections.namedtuple( - "ComputerMove", ["board_index", "move_index", "m1", "m2"] -) + +class ComputerMove(NamedTuple): + board_index: int + move_index: int + m1: int + m2: int + wins = 0 losses = 0 @@ -68,10 +72,7 @@ def print_centered(msg: str) -> None: def print_header(title: str) -> None: print_centered(title) - print_centered("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") - print() - print() - print() + print_centered("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n") def print_instructions() -> None: @@ -110,7 +111,7 @@ GO FIRST. ) -def prompt_yes_no(msg): +def prompt_yes_no(msg: str) -> bool: while True: print(msg) response = input().upper() @@ -120,7 +121,7 @@ def prompt_yes_no(msg): return False -def reverse_space_name(space_name): +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 @@ -128,36 +129,38 @@ def reverse_space_name(space_name): return reflections[space_name] -def is_space_in_center_column(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, move_list): + 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): + 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): + 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): + 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): + 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)) @@ -193,7 +196,7 @@ boards = [ ] -def get_move(board_index, move_index): +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] @@ -202,7 +205,7 @@ def get_move(board_index, move_index): return board.moves[move_index] -def remove_move(board_index, 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] @@ -211,11 +214,11 @@ def remove_move(board_index, move_index): del board.moves[move_index] -def init_board(): +def init_board() -> List[int]: return [COMPUTER_PIECE] * 3 + [EMPTY_SPACE] * 3 + [HUMAN_PIECE] * 3 -def print_board(board) -> None: +def print_board(board: List[int]) -> None: piece_dict = {COMPUTER_PIECE: "X", EMPTY_SPACE: ".", HUMAN_PIECE: "O"} space = " " * 10 @@ -231,7 +234,7 @@ def print_board(board) -> None: print() -def get_coordinates(): +def get_coordinates() -> Tuple[int, int]: while True: try: print("YOUR MOVE?") @@ -246,15 +249,15 @@ def print_illegal() -> None: print("ILLEGAL MOVE.") -def board_contents(board, space_number): +def board_contents(board: List[int], space_number: int) -> int: return board[space_number - 1] -def set_board(board, space_number, new_value): +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, m1, m2): +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 @@ -285,30 +288,30 @@ def is_legal_human_move(board, m1, m2): return True -def player_piece_on_back_row(board): +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): +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): +def all_human_pieces_captured(board: List[int]) -> bool: return len(list(get_human_spaces(board))) == 0 -def all_computer_pieces_captured(board): +def all_computer_pieces_captured(board: List[int]) -> bool: return len(list(get_computer_spaces(board))) == 0 -def human_win(last_computer_move): +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): +def computer_win(has_moves: bool) -> None: if not has_moves: msg = "YOU CAN'T MOVE, SO " else: @@ -319,12 +322,11 @@ def computer_win(has_moves): wins += 1 -def show_scores(): - print(f"I HAVE WON {wins} AND YOU {losses} OUT OF {wins + losses} GAMES.") - print() +def show_scores() -> None: + print(f"I HAVE WON {wins} AND YOU {losses} OUT OF {wins + losses} GAMES.\n") -def human_has_move(board): +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 @@ -353,31 +355,31 @@ def human_has_move(board): return False -def get_board_spaces(): +def get_board_spaces() -> Iterator[int]: """generates the space names (1-9)""" yield from range(1, 10) -def get_board_spaces_with(board, val): +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): +def get_human_spaces(board: List[int]) -> Iterator[int]: yield from get_board_spaces_with(board, HUMAN_PIECE) -def get_empty_spaces(board): +def get_empty_spaces(board: List[int]) -> Iterator[int]: yield from get_board_spaces_with(board, EMPTY_SPACE) -def get_computer_spaces(board): +def get_computer_spaces(board: List[int]) -> Iterator[int]: yield from get_board_spaces_with(board, COMPUTER_PIECE) -def has_computer_move(board): +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) @@ -406,7 +408,7 @@ def has_computer_move(board): return False -def find_board_index_that_matches_board(board): +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: @@ -417,7 +419,7 @@ def find_board_index_that_matches_board(board): raise RuntimeError("ILLEGAL BOARD PATTERN.") -def pick_computer_move(board): +def pick_computer_move(board: List[int]) -> Optional[ComputerMove]: if not has_computer_move(board): return None @@ -434,7 +436,7 @@ def pick_computer_move(board): return ComputerMove(board_index, move_index, m1, m2) -def get_human_move(board): +def get_human_move(board: List[int]) -> Tuple[int, int]: while True: m1, m2 = get_coordinates() @@ -444,12 +446,12 @@ def get_human_move(board): return m1, m2 -def apply_move(board, m1, m2, piece_value): +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(): +def play_game() -> None: last_computer_move = None board = init_board() @@ -464,11 +466,13 @@ def play_game(): 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 diff --git a/47_Hi-Lo/python/hilo.py b/47_Hi-Lo/python/hilo.py index d0badf73..5850b3f7 100755 --- a/47_Hi-Lo/python/hilo.py +++ b/47_Hi-Lo/python/hilo.py @@ -5,7 +5,7 @@ MAX_ATTEMPTS = 6 QUESTION_PROMPT = "? " -def play(): +def main() -> None: print("HI LO") print("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n") print("THIS IS THE GAME OF HI LO.\n") @@ -50,4 +50,4 @@ def play(): if __name__ == "__main__": - play() + main() diff --git a/48_High_IQ/python/High_IQ.py b/48_High_IQ/python/High_IQ.py index 16843553..6dcceaf5 100644 --- a/48_High_IQ/python/High_IQ.py +++ b/48_High_IQ/python/High_IQ.py @@ -1,43 +1,46 @@ -def new_board(): - # Using a dictionary in python to store the board, since we are not including all numbers within a given range. - board = {} - for i in [ - 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, - ]: - board[i] = "!" - board[41] = "O" - return board +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: @@ -73,7 +76,7 @@ NUMBERS. OK, LET'S BEGIN. ) -def print_board(board) -> None: +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]) @@ -131,7 +134,7 @@ def play_game() -> None: print("SAVE THIS PAPER AS A RECORD OF YOUR ACCOMPLISHMENT!") -def move(board): +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? ") @@ -154,7 +157,7 @@ def move(board): return False difference = abs(start - end) - center = (end + start) / 2 + center = int((end + start) / 2) if ( (difference == 2 or difference == 18) and board[end] == "O" @@ -175,20 +178,20 @@ def main() -> None: play_game() -def is_game_finished(board): - # Checks all locations and whether or not a move is possible at that location. +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 - nextToPeg = ((pos + space) in board) and board[pos + space] == "!" + next_to_peg = ((pos + space) in board) and board[pos + space] == "!" # Checks both going forward (+ location) or backwards (-location) - hasMovableSpace = ( + has_movable_space = ( not ((pos - space) in board and board[pos - space] == "!") ) or ( not ((pos + space * 2) in board and board[pos + space * 2] == "!") ) - if nextToPeg and hasMovableSpace: + if next_to_peg and has_movable_space: return False return True diff --git a/50_Horserace/python/horserace.py b/50_Horserace/python/horserace.py index a891597f..b00a6316 100644 --- a/50_Horserace/python/horserace.py +++ b/50_Horserace/python/horserace.py @@ -1,6 +1,7 @@ import math import random import time +from typing import List, Tuple def basic_print(*zones, **kwargs) -> None: @@ -17,7 +18,7 @@ def basic_print(*zones, **kwargs) -> None: print(" " * identation + line, end=end) -def basic_input(prompt, type_conversion=None): +def basic_input(prompt: str, type_conversion=None): """BASIC INPUT command with optional type conversion""" while True: @@ -45,7 +46,7 @@ HORSE_NAMES = [ ] -def introduction(): +def introduction() -> None: """Print the introduction, and optional the instructions""" basic_print("HORSERACE", indent=31) @@ -66,7 +67,7 @@ def introduction(): basic_print("") -def setup_players(): +def setup_players() -> List[str]: """Gather the number of players and their names""" # ensure we get an integer value from the user @@ -80,7 +81,7 @@ def setup_players(): return player_names -def setup_horses(): +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.""" @@ -101,14 +102,14 @@ def print_horse_odds(odds) -> None: basic_print("") -def get_bets(player_names): +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 = [] + bets: List[Tuple[int, float]] = [] for name in player_names: horse = basic_input(name, int) amount = None @@ -124,7 +125,7 @@ def get_bets(player_names): return bets -def get_distance(odd): +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""" @@ -180,7 +181,7 @@ def print_race_state(total_distance, race_pos) -> None: basic_print("XXXXFINISHXXXX") -def simulate_race(odds): +def simulate_race(odds) -> List[int]: num_horses = len(HORSE_NAMES) # in spirit of the original implementation, using two arrays to diff --git a/51_Hurkle/python/hurkle.py b/51_Hurkle/python/hurkle.py index 62cffecb..c8e2ca9b 100644 --- a/51_Hurkle/python/hurkle.py +++ b/51_Hurkle/python/hurkle.py @@ -1,12 +1,12 @@ #!/usr/bin/env python3 -# -# Ported to Python by @iamtraction + +"""Ported to Python by @iamtraction""" from random import random -def direction(A, B, X, Y): - """Prints the direction hint for finding the hurkle.""" +def direction(A, B, X, Y) -> None: + """Print the direction hint for finding the hurkle.""" print("GO ", end="") if Y < B: @@ -22,7 +22,7 @@ def direction(A, B, X, Y): print() -if __name__ == "__main__": +def main() -> None: print(" " * 33 + "HURKLE") print(" " * 15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") @@ -63,3 +63,7 @@ if __name__ == "__main__": continue print("\n\nLET'S PLAY AGAIN, HURKLE IS HIDING.\n") + + +if __name__ == "__main__": + main() diff --git a/52_Kinema/python/kinema.py b/52_Kinema/python/kinema.py index 65fbd08f..2a4831a7 100644 --- a/52_Kinema/python/kinema.py +++ b/52_Kinema/python/kinema.py @@ -20,15 +20,7 @@ g = 10 EXPECTED_ACCURACY_PERCENT = 15 -def print_with_tab(spaces_count, msg) -> None: - if spaces_count > 0: - spaces = " " * spaces_count - else: - spaces = "" - print(spaces + msg) - - -def do_quiz(): +def do_quiz() -> None: print() print() num_questions_correct = 0 @@ -58,7 +50,7 @@ def do_quiz(): print(" NOT BAD.") -def ask_player(question, answer): +def ask_player(question: str, answer) -> int: print(question) player_answer = float(input()) @@ -75,11 +67,8 @@ def ask_player(question, answer): def main() -> None: - print_with_tab(33, "KINEMA") - print_with_tab(15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") - print() - print() - print() + print(" " * 33 + "KINEMA") + print(" " * 15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n") while True: do_quiz() diff --git a/53_King/README.md b/53_King/README.md index 411d4876..a363a2be 100644 --- a/53_King/README.md +++ b/53_King/README.md @@ -21,7 +21,9 @@ http://www.vintage-basic.net/games.html #### Porting Notes -Implementers should be aware that this game contains at least one bug. +Implementers should be aware that this game contains bugs. + +### Bug 1 On basic line 1450 @@ -41,4 +43,26 @@ A quick fix for this bug in the original code would be 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." diff --git a/53_King/python/README.md b/53_King/python/README.md index 781945ec..b96c2f42 100644 --- a/53_King/python/README.md +++ b/53_King/python/README.md @@ -1,3 +1,44 @@ 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 diff --git a/53_King/python/king.py b/53_King/python/king.py new file mode 100644 index 00000000..9c8d075c --- /dev/null +++ b/53_King/python/king.py @@ -0,0 +1,444 @@ +""" +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 = int(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 ") + reason = randint(0, 10) + if reason <= 2: + print("FISH POPULATION HAS DWINDLED DUE TO WATER POLLUTION.") + if reason <= 4: + print("AIR POLLUTION IS KILLING GAME BIRD POPULATION.") + if reason <= 6: + print("MINERAL BATHS ARE BEING RUINED BY WATER POLLUTION.") + if reason <= 8: + print("UNPLEASANT SMOG IS DISCOURAGING SUN BATHERS.") + if reason <= 10: + 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 = 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 += int(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 / 100 + ) > 5 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() diff --git a/53_King/python/king_variable_update.py b/53_King/python/king_variable_update.py new file mode 100644 index 00000000..e69de29b diff --git a/54_Letter/python/letter.py b/54_Letter/python/letter.py index 77c87c7f..ca4599ee 100644 --- a/54_Letter/python/letter.py +++ b/54_Letter/python/letter.py @@ -15,15 +15,6 @@ import random BELLS_ON_SUCCESS = False -def print_with_tab(space_count: int, msg: str) -> None: - if space_count > 0: - spaces = " " * space_count - else: - spaces = "" - - print(spaces + msg) - - def print_instructions() -> None: print("LETTER GUESSING GAME") print() @@ -32,7 +23,7 @@ def print_instructions() -> None: print("AS TO HOW CLOSE YOU'RE GETTING TO MY LETTER.") -def play_game(): +def play_game() -> None: target_value = random.randint(ord("A"), ord("Z")) num_guesses = 0 print() @@ -52,8 +43,8 @@ def play_game(): print("GOOD JOB !!!!!") if BELLS_ON_SUCCESS: - bellStr = chr(7) * 15 - print(bellStr) + bell_str = chr(7) * 15 + print(bell_str) print() print("LET'S PLAY AGAIN.....") @@ -67,11 +58,8 @@ def play_game(): def main() -> None: - print_with_tab(33, "LETTER") - print_with_tab(15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") - print() - print() - print() + print(" " * 33 + "LETTER") + print(" " * 15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n") print_instructions() diff --git a/55_Life/python/life.py b/55_Life/python/life.py index b473632e..5a71dfa8 100644 --- a/55_Life/python/life.py +++ b/55_Life/python/life.py @@ -6,6 +6,7 @@ An implementation of John Conway's popular cellular automaton Ported by Dave LeCompte """ +from typing import Dict PAGE_WIDTH = 64 @@ -26,11 +27,11 @@ def print_header(title) -> None: print() -def get_pattern(): +def get_pattern() -> Dict[int, str]: print("ENTER YOUR PATTERN:") c = 0 - pattern = {} + pattern: Dict[int, str] = {} while True: line = input() if line == "DONE": @@ -98,8 +99,8 @@ def main() -> None: print() for x in range(min_x, max_x + 1): - print - line = [" "] * MAX_WIDTH + print() + line_list = [" "] * MAX_WIDTH for y in range(min_y, max_y + 1): if a[x][y] == 2: a[x][y] = 0 @@ -109,15 +110,14 @@ def main() -> None: elif a[x][y] != 1: continue - # line 261 - line[y] = "*" + 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)) + print("".join(line_list)) # line 295 for _ in range(max_x + 1, MAX_HEIGHT): diff --git a/57_Literature_Quiz/javascript/literature-quiz-node.mjs b/57_Literature_Quiz/javascript/litquiz.mjs similarity index 53% rename from 57_Literature_Quiz/javascript/literature-quiz-node.mjs rename to 57_Literature_Quiz/javascript/litquiz.mjs index 5dadd50b..89daef92 100644 --- a/57_Literature_Quiz/javascript/literature-quiz-node.mjs +++ b/57_Literature_Quiz/javascript/litquiz.mjs @@ -1,37 +1,23 @@ -import * as readline from 'readline' +#!/usr/bin/env node -// start reusable code -async function input(prompt = "") { - const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout - }) +import { println, input } from '../../00_Common/javascript/common.mjs'; - return new Promise((resolve, _) => { - rl.setPrompt(prompt) - // show user the question - rl.prompt() - // listen for user answer, - // callback is triggered as soon as user hits enter key - rl.on('line', answer => { - rl.close() - // resolve the promise, with the input the user entered - resolve(answer) - }) - }) -} - -function println(message = "", align = "left"){ - let padColCount = 0 - if(align === "center"){ +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 - // process.stdout.columns is the number of spaces per line in the terminal - padColCount = Math.round(process.stdout.columns / 2 + message.length / 2) + 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); } - console.log(message.padStart(padColCount, " ")) } -// end reusable code - function equalIgnoreCase(correct, provided){ return correct.toString().toLowerCase() === provided.toString().toLowerCase() @@ -39,8 +25,10 @@ function equalIgnoreCase(correct, provided){ 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(question + "\n" + answerOptions + "\n") + const answer = await input('?') const isCorrect = equalIgnoreCase(correctAnswer, answer) println(isCorrect ? correctMessage : wrongMessage) return isCorrect ? 1 : 0 @@ -48,31 +36,40 @@ async function evaluateQuestion(question, answerOptions, correctAnswer, correctM async function main(){ let score = 0 - println("LITERATURE QUIZ", "center") - println("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY", "center") - println();println();println() + + 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?", - "1)TIGGER, 2)CICERO, 3)FIGARO, 4)GUIPETTO", 3, + [ "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?", - "1)MR. NIXON'S, 2)ELMER FUDD'S, 3)CLEM JUDD'S, 4)STROMBOLI'S", 2, + [ "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", - "1)CICERO, 2)TRIXIA, 3)KING, 4)TOTO", 4, + [ "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", - "1)SLEEPING BEAUTY, 2)CINDERELLA, 3)SNOW WHITE, 4)WENDY", 3, + [ "SLEEPING BEAUTY", "CINDERELLA", "SNOW WHITE", "WENDY" ], 3, "GOOD MEMORY!", "OH, COME ON NOW...IT WAS SNOW WHITE.") - println();println() + println("\n") if(score === 4) { println("WOW! THAT'S SUPER! YOU REALLY KNOW YOUR NURSERY\n"+ diff --git a/57_Literature_Quiz/python/litquiz.py b/57_Literature_Quiz/python/litquiz.py index d9e581d3..9af12398 100644 --- a/57_Literature_Quiz/python/litquiz.py +++ b/57_Literature_Quiz/python/litquiz.py @@ -6,20 +6,19 @@ A children's literature quiz Ported by Dave LeCompte """ +from typing import List, NamedTuple + PAGE_WIDTH = 64 -class Question: - def __init__( - self, question, answer_list, correct_number, incorrect_message, correct_message - ): - self.question = question - self.answer_list = answer_list - self.correct_number = correct_number - self.incorrect_message = incorrect_message - self.correct_message = correct_message +class Question(NamedTuple): + question: str + answer_list: List[str] + correct_number: int + incorrect_message: str + correct_message: str - def ask(self): + def ask(self) -> bool: print(self.question) options = [f"{i+1}){self.answer_list[i]}" for i in range(len(self.answer_list))] @@ -69,7 +68,6 @@ questions = [ def print_centered(msg: str) -> None: spaces = " " * ((64 - len(msg)) // 2) - print(spaces + msg) diff --git a/58_Love/python/love.py b/58_Love/python/love.py index a15237ba..2bb93c68 100644 --- a/58_Love/python/love.py +++ b/58_Love/python/love.py @@ -1,24 +1,22 @@ -###################################################################### -# -# 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 -# -###################################################################### +""" +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 @@ -108,7 +106,7 @@ def main() -> None: position += length print(line_text) - print("") + print() if __name__ == "__main__": diff --git a/59_Lunar_LEM_Rocket/python/lunar.py b/59_Lunar_LEM_Rocket/python/lunar.py index d9a94885..82f21fc0 100644 --- a/59_Lunar_LEM_Rocket/python/lunar.py +++ b/59_Lunar_LEM_Rocket/python/lunar.py @@ -6,8 +6,9 @@ Lunar landing simulation Ported by Dave LeCompte """ -import collections import math +from dataclasses import dataclass +from typing import Any, NamedTuple PAGE_WIDTH = 64 @@ -32,7 +33,10 @@ FUEL_RIGHT = FUEL_LEFT + FUEL_WIDTH BURN_LEFT = FUEL_RIGHT + COLUMN_WIDTH BURN_RIGHT = BURN_LEFT + BURN_WIDTH -PhysicalState = collections.namedtuple("PhysicalState", ["velocity", "altitude"]) + +class PhysicalState(NamedTuple): + velocity: float + altitude: float def print_centered(msg: str) -> None: @@ -42,29 +46,24 @@ def print_centered(msg: str) -> None: def print_header(title: str) -> None: print_centered(title) - print_centered("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") - print() - print() - print() + print_centered("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n") -def add_rjust(line, s, pos): - # adds a new field to a line right justified to end at pos - - s = str(s) - slen = len(s) +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 + return line + s_str -def add_ljust(line, s, pos): - # adds a new field to a line left justified starting at pos - +def add_ljust(line: str, s: str, pos: int) -> str: + """Add a new field to a line left justified starting at pos""" s = str(s) if len(line) > pos: line = line[:pos] @@ -75,32 +74,129 @@ def add_ljust(line, s, pos): def print_instructions() -> None: - # Somebody had a bad experience with Xerox. - + """Somebody had a bad experience with Xerox.""" print("THIS IS A COMPUTER SIMULATION OF AN APOLLO LUNAR") - print("LANDING CAPSULE.") - print() - print() + 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.") - print() + 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.") - print() - print("CAPSULE WEIGHT 32,500 LBS; FUEL WEIGHT 16,500 LBS.") - print() - print() - print() - print("GOOD LUCK") - print() + print("SET NEW BURN RATE EVERY 10 SECONDS.\n") + print("CAPSULE WEIGHT 32,500 LBS; FUEL WEIGHT 16,500 LBS.\n\n\n") + print("GOOD LUCK\n") -def show_landing(sim_clock, capsule): - w = 3600 * capsule.v +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" ) @@ -117,121 +213,20 @@ def show_landing(sim_clock, capsule): end_sim() -def show_out_of_fuel(sim_clock, capsule): +def show_out_of_fuel(sim_clock: SimulationClock, capsule: Capsule) -> None: print(f"FUEL OUT AT {sim_clock.elapsed_time} SECONDS") delta_t = ( - -capsule.v + math.sqrt(capsule.v**2 + 2 * capsule.a * capsule.g) + -capsule.velocity + + math.sqrt(capsule.velocity**2 + 2 * capsule.altitude * capsule.g) ) / capsule.g - capsule.v += capsule.g * delta_t + capsule.velocity += capsule.g * delta_t sim_clock.advance(delta_t) show_landing(sim_clock, capsule) -def format_line_for_report(t, miles, feet, velocity, fuel, burn_rate, is_header) -> 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 Capsule: - def __init__( - self, - altitude=120, - velocity=1, - mass_with_fuel=33000, - mass_without_fuel=16500, - g=1e-3, - z=1.8, - ): - self.a = altitude # in miles above the surface - self.v = velocity # downward - self.m = mass_with_fuel - self.n = mass_without_fuel - self.g = g - self.z = z - self.fuel_per_second = 0 - - def remaining_fuel(self): - return self.m - self.n - - def is_out_of_fuel(self): - return self.remaining_fuel() < 1e-3 - - def update_state(self, sim_clock, delta_t, new_state): - sim_clock.advance(delta_t) - self.m = self.m - delta_t * self.fuel_per_second - self.a = new_state.altitude - self.v = new_state.velocity - - def fuel_time_remaining(self): - # 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): - # 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.v - + self.g * delta_t - + self.z * (-q - q**2 / 2 - q**3 / 3 - q**4 / 4 - q**5 / 5) - ) - - # new altitude - new_altitude = ( - self.a - - self.g * delta_t**2 / 2 - - self.v * 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) -> str: - seconds = sim_clock.elapsed_time - miles = int(self.a) - feet = int(5280 * (self.a - miles)) - velocity = int(3600 * self.v) - 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): - msg = self.make_state_display_string(sim_clock) - - self.fuel_per_second = float(input(msg)) - sim_clock.time_until_next_prompt = 10 - - -class SimulationClock: - def __init__(self, elapsed_time, time_until_next_prompt): - self.elapsed_time = elapsed_time - self.time_until_next_prompt = time_until_next_prompt - - def time_for_prompt(self): - return self.time_until_next_prompt < 1e-3 - - def advance(self, delta_t): - self.elapsed_time += delta_t - self.time_until_next_prompt -= delta_t - - -def process_final_tick(delta_t, 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. @@ -242,20 +237,20 @@ def process_final_tick(delta_t, sim_clock, capsule): return # line 35 average_vel = ( - capsule.v + capsule.velocity + math.sqrt( - capsule.v**2 + capsule.velocity**2 + 2 - * capsule.a + * capsule.altitude * (capsule.g - capsule.z * capsule.fuel_per_second / capsule.m) ) ) / 2 - delta_t = capsule.a / average_vel + 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, capsule): +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 @@ -268,11 +263,11 @@ def handle_flyaway(sim_clock, capsule): w = (1 - capsule.m * capsule.g / (capsule.z * capsule.fuel_per_second)) / 2 delta_t = ( capsule.m - * capsule.v + * capsule.velocity / ( capsule.z * capsule.fuel_per_second - * math.sqrt(w**2 + capsule.v / capsule.z) + * math.sqrt(w**2 + capsule.velocity / capsule.z) ) ) + 0.05 @@ -284,22 +279,16 @@ def handle_flyaway(sim_clock, capsule): capsule.update_state(sim_clock, delta_t, new_state) - if (new_state.velocity > 0) or (capsule.v <= 0): + if (new_state.velocity > 0) or (capsule.velocity <= 0): # return to normal sim return False -def end_sim(): - print() - print() - print() - print("TRY AGAIN??") - print() - print() - print() +def end_sim() -> None: + print("\n\n\nTRY AGAIN??\n\n\n") -def run_simulation(): +def run_simulation() -> None: print() print( format_line_for_report("SEC", "MI", "FT", "MPH", "LB FUEL", "BURN RATE", True) @@ -334,7 +323,7 @@ def run_simulation(): process_final_tick(delta_t, sim_clock, capsule) return - if capsule.v > 0 and new_state.velocity < 0: + if capsule.velocity > 0 and new_state.velocity < 0: # moving away from the moon landed = handle_flyaway(sim_clock, capsule) diff --git a/60_Mastermind/python/mastermind.py b/60_Mastermind/python/mastermind.py index 94b92385..acf7cf6b 100644 --- a/60_Mastermind/python/mastermind.py +++ b/60_Mastermind/python/mastermind.py @@ -178,7 +178,7 @@ def main() -> None: # 470 -def get_invalid_letters(user_command): +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 = "" @@ -190,7 +190,7 @@ def get_invalid_letters(user_command): # 2000 def print_board(guesses) -> None: - """Prints previous guesses within the round.""" + """Print previous guesses within the round.""" print("Board") print("Move\tGuess\tBlack White") for idx, guess in enumerate(guesses): @@ -204,7 +204,7 @@ def print_board(guesses) -> None: # "We did try a version that kept an actual list of all possible combinations # (as a string array), which was significantly faster than this versionn but # which ate tremendous amounts of memory." -def get_possibility(possibility): +def get_possibility(possibility) -> List[int]: # print(possibility) if possibility[0] > -1: # 3530 current_position = 0 # Python arrays are zero-indexed @@ -248,7 +248,7 @@ def compare_two_positions(guess: str, answer: str) -> List[Union[str, int]]: # 5000 + logic from 1160 def print_score(computer_score, human_score, is_final_score: bool = False) -> None: - """Prints score after each turn ends, including final score at end of game.""" + """Print score after each turn ends, including final score at end of game.""" if is_final_score: print("GAME OVER") print("FINAL SCORE:") diff --git a/62_Mugwump/python/mugwump.py b/62_Mugwump/python/mugwump.py index 6eb63ccc..d1e3f7c3 100644 --- a/62_Mugwump/python/mugwump.py +++ b/62_Mugwump/python/mugwump.py @@ -1,5 +1,6 @@ from math import sqrt from random import randint +from typing import List, Tuple def introduction() -> None: @@ -18,7 +19,7 @@ you how far you are from each mugwump.""" ) -def generate_mugwumps(n=4): +def generate_mugwumps(n: int = 4) -> List[List[int]]: mugwumps = [] for _ in range(n): current = [randint(0, 9), randint(0, 9)] @@ -26,14 +27,14 @@ def generate_mugwumps(n=4): return mugwumps -def reveal_mugwumps(mugwumps): +def reveal_mugwumps(mugwumps: List[List[int]]) -> None: print("Sorry, that's 10 tries. Here's where they're hiding.") for idx, mugwump in enumerate(mugwumps, 1): if mugwump[0] != -1: print(f"Mugwump {idx} is at {mugwump[0]},{mugwump[1]}") -def calculate_distance(guess, mugwump): +def calculate_distance(guess: Tuple[int, int], mugwump: List[int]) -> float: d = sqrt(((mugwump[0] - guess[0]) ** 2) + ((mugwump[1] - guess[1]) ** 2)) return d @@ -47,7 +48,7 @@ def play_again() -> None: exit() -def play_round(): +def play_round() -> None: mugwumps = generate_mugwumps() turns = 1 score = 0 diff --git a/63_Name/python/name.py b/63_Name/python/name.py index b3067f7e..3d7eeb1c 100644 --- a/63_Name/python/name.py +++ b/63_Name/python/name.py @@ -7,45 +7,29 @@ Ported by Dave LeCompte """ -def print_with_tab(space_count: int, msg: str) -> None: - if space_count > 0: - spaces = " " * space_count - else: - spaces = "" - print(spaces + msg) - - -def is_yes_ish(answer): +def is_yes_ish(answer: str) -> bool: cleaned = answer.strip().upper() - if cleaned == "Y" or cleaned == "YES": + if cleaned in ["Y", "YES"]: return True return False def main() -> None: - print_with_tab(34, "NAME") - print_with_tab(15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") - print() - print() - print() + print(" " * 34 + "NAME") + print(" " * 15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n") print("HELLO.") print("MY NAME iS CREATIVE COMPUTER.") name = input("WHAT'S YOUR NAME (FIRST AND LAST)?") print() name_as_list = list(name) reversed_name = "".join(name_as_list[::-1]) - print(f"THANK YOU, {reversed_name}.") - print() + print(f"THANK YOU, {reversed_name}.\n") print("OOPS! I GUESS I GOT IT BACKWARDS. A SMART") - print("COMPUTER LIKE ME SHOULDN'T MAKE A MISTAKE LIKE THAT!") - print() - print() + print("COMPUTER LIKE ME SHOULDN'T MAKE A MISTAKE LIKE THAT!\n\n") print("BUT I JUST NOTICED YOUR LETTERS ARE OUT OF ORDER.") sorted_name = "".join(sorted(name_as_list)) - print(f"LET'S PUT THEM IN ORDER LIKE THIS: {sorted_name}") - print() - print() + print(f"LET'S PUT THEM IN ORDER LIKE THIS: {sorted_name}\n\n") print("DON'T YOU LIKE THAT BETTER?") like_answer = input() diff --git a/64_Nicomachus/python/nicomachus.py b/64_Nicomachus/python/nicomachus.py index abc48293..84f47703 100644 --- a/64_Nicomachus/python/nicomachus.py +++ b/64_Nicomachus/python/nicomachus.py @@ -15,15 +15,7 @@ Ported by Dave LeCompte import time -def print_with_tab(spaces_count: int, msg: str) -> None: - if spaces_count > 0: - spaces = " " * spaces_count - else: - spaces = "" - print(spaces + msg) - - -def get_yes_or_no(): +def get_yes_or_no() -> bool: while True: response = input().upper() if response == "YES": @@ -33,7 +25,7 @@ def get_yes_or_no(): print(f"EH? I DON'T UNDERSTAND '{response}' TRY 'YES' OR 'NO'.") -def play_game(): +def play_game() -> None: print("PLEASE THINK OF A NUMBER BETWEEN 1 AND 100.") print("YOUR NUMBER DIVIDED BY 3 HAS A REMAINDER OF") a = int(input()) @@ -62,11 +54,8 @@ def play_game(): def main() -> None: - print_with_tab(33, "NICOMA") - print_with_tab(15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") - print() - print() - print() + print(" " * 33 + "NICOMA") + print(" " * 15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n") print("BOOMERANG PUZZLE FROM ARITHMETICA OF NICOMACHUS -- A.D. 90!") print() diff --git a/65_Nim/python/Traditional_NIM.py b/65_Nim/python/Traditional_NIM.py index 6c3a2d08..bd1470c5 100644 --- a/65_Nim/python/Traditional_NIM.py +++ b/65_Nim/python/Traditional_NIM.py @@ -1,12 +1,12 @@ import random +from typing import Tuple -# Class of the Game class NIM: - def __init__(self): + def __init__(self) -> None: self.piles = {1: 7, 2: 5, 3: 3, 4: 1} - def remove_pegs(self, command): + def remove_pegs(self, command) -> None: try: pile, num = command.split(",") @@ -29,7 +29,7 @@ class NIM: else: print("\nInvalid value of either Peg or Pile\n") - def get_ai_move(self): + def get_ai_move(self) -> Tuple[int, int]: possible_pile = [] for k, v in self.piles.items(): if v != 0: @@ -41,14 +41,14 @@ class NIM: return pile, num - def _command_integrity(self, num, pile): + def _command_integrity(self, num, pile) -> bool: return pile <= 4 and pile >= 1 and num <= self.piles[pile] def print_pegs(self) -> None: for pile, peg in self.piles.items(): print("Pile {} : {}".format(pile, "O " * peg)) - def help(self): + def help(self) -> None: print("-" * 10) print('\nThe Game is player with a number of Piles of Objects("O" == one peg)') print("\nThe Piles are arranged as given below(Tradional NIM)\n") @@ -62,7 +62,7 @@ class NIM: print("\nThe winner is defined as the one that picks the last remaning object") print("-" * 10) - def check_for_win(self): + def check_for_win(self) -> bool: sum = 0 for v in self.piles.values(): sum += v @@ -96,13 +96,13 @@ def main() -> None: break # Computers Move - command = game.get_ai_move() + ai_command = game.get_ai_move() print( "\nA.I MOVE - A.I Removed {} pegs from Pile {}".format( - command[1], command[0] + ai_command[1], ai_command[0] ) ) - game.remove_pegs(str(command[0]) + "," + str(command[1])) + game.remove_pegs(str(ai_command[0]) + "," + str(ai_command[1])) end = game.check_for_win() if end: print("\nComputer Wins the Game, Better Luck Next Time\n") diff --git a/66_Number/python/number.py b/66_Number/python/number.py index e58dd20b..e6840d92 100644 --- a/66_Number/python/number.py +++ b/66_Number/python/number.py @@ -9,15 +9,6 @@ Ported by Dave LeCompte import random -def print_with_tab(num_spaces: int, msg: str) -> None: - if num_spaces > 0: - spaces = " " * num_spaces - else: - spaces = "" - - print(spaces + msg) - - def print_instructions() -> None: print("YOU HAVE 100 POINTS. BY GUESSING NUMBERS FROM 1 TO 5, YOU") print("CAN GAIN OR LOSE POINTS DEPENDING UPON HOW CLOSE YOU GET TO") @@ -28,16 +19,13 @@ def print_instructions() -> None: print() -def fnr(): +def fnr() -> int: return random.randint(1, 5) def main() -> None: - print_with_tab(33, "NUMBER") - print_with_tab(15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") - print() - print() - print() + print(" " * 33 + "NUMBER") + print(" " * 15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") print_instructions() diff --git a/67_One_Check/python/onecheck.py b/67_One_Check/python/onecheck.py index b866ebd9..21ffbd6e 100644 --- a/67_One_Check/python/onecheck.py +++ b/67_One_Check/python/onecheck.py @@ -1,22 +1,17 @@ -# ONE CHECK +""" +ONE CHECK -# Port to python by imiro +Port to Python by imiro +""" - -def tab(x): - return " " * x +from typing import Tuple def main() -> None: - # Initial instructions - print(tab(30) + "ONE CHECK") - print(tab(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") - print() - print() - print() - print("SOLITAIRE CHECKER PUZZLE BY DAVID AHL") - print() + print(" " * 30 + "ONE CHECK") + print(" " * 15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n") + print("SOLITAIRE CHECKER PUZZLE BY DAVID AHL\n") 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") @@ -25,35 +20,29 @@ def main() -> None: 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() - print("HERE IS THE NUMERICAL BOARD:") - print() + print("QUESTION 'JUMP FROM ?'\n") + print("HERE IS THE NUMERICAL BOARD:\n") while True: for j in range(1, 64, 8): for i in range(j, j + 7): print(i, end=(" " * (3 if i < 10 else 2))) print(j + 7) - print() - print("AND HERE IS THE OPENING POSITION OF THE CHECKERS.") - print() + print("\nAND HERE IS THE OPENING POSITION OF THE CHECKERS.\n") (jumps, left) = play_game() print() - print("YOU MADE " + jumps + " JUMPS AND HAD " + left + " PIECES") - print("REMAINING ON THE BOARD.") - print() + print(f"YOU MADE {jumps} JUMPS AND HAD {left} PIECES") + print("REMAINING ON THE BOARD.\n") if not (try_again()): break - print() - print("O.K. HOPE YOU HAD FUN!!") + print("\nO.K. HOPE YOU HAD FUN!!") -def play_game(): +def play_game() -> Tuple[str, str]: # Initialize board # Give more than 64 elements to accomodate 1-based indexing board = [1] * 70 @@ -71,13 +60,13 @@ def play_game(): while True: print("JUMP FROM", end=" ") - f = input() - f = int(f) + f_str = input() + f = int(f_str) if f == 0: break print("TO", end=" ") - t = input() - t = int(t) + t_str = input() + t = int(t_str) print() # Check legality of move @@ -113,15 +102,15 @@ def play_game(): return (str(jumps), str(left)) -def try_again(): +def try_again() -> bool: print("TRY AGAIN", end=" ") - answer = input() - if answer.upper() == "YES": + answer = input().upper() + if answer == "YES": return True - elif answer.upper() == "NO": + elif answer == "NO": return False print("PLEASE ANSWER 'YES' OR 'NO'.") - try_again() + return try_again() if __name__ == "__main__": diff --git a/68_Orbit/python/orbit.py b/68_Orbit/python/orbit.py index ce22a9b4..f0ce1637 100644 --- a/68_Orbit/python/orbit.py +++ b/68_Orbit/python/orbit.py @@ -81,7 +81,7 @@ GOOD LUCK. THE FEDERATION IS COUNTING ON YOU. ) -def get_yes_or_no(): +def get_yes_or_no() -> bool: while True: response = input().upper() if response == "YES": @@ -92,7 +92,7 @@ def get_yes_or_no(): print("PLEASE TYPE 'YES' OR 'NO'") -def game_over(is_success): +def game_over(is_success: bool) -> bool: if is_success: print("YOU HAVE SUCCESSFULLY COMPLETED YOUR MISSION.") else: @@ -103,7 +103,7 @@ def game_over(is_success): return get_yes_or_no() -def play_game(): +def play_game() -> bool: rom_angle = random.randint(0, 359) rom_distance = random.randint(100, 300) rom_angular_velocity = random.randint(10, 30) @@ -145,10 +145,7 @@ def play_game(): def main() -> None: print_centered("ORBIT") - print_centered("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") - print() - print() - print() + print_centered("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n") print_instructions() diff --git a/68_Orbit/ruby/orbit.rb b/68_Orbit/ruby/orbit.rb new file mode 100644 index 00000000..cbf86b4d --- /dev/null +++ b/68_Orbit/ruby/orbit.rb @@ -0,0 +1,132 @@ +PAGE_WIDTH = 64 + +def print_centered(msg) + spaces = " " * ((PAGE_WIDTH / msg.length).fdiv(2)) + puts "#{spaces}#{msg}" +end + +def print_instructions + puts "SOMEWHERE ABOVE YOUR PLANET IS A ROMULAN SHIP. +THE SHIP IS IN A CONSTANT POLAR ORBIT. ITS +DISTANCE FROM THE CENTER OF YOUR PLANET IS FROM +10,000 TO 30,000 MILES AND AT ITS PRESENT VELOCITY CAN +CIRCLE YOUR PLANET ONCE EVERY 12 TO 36 HOURS. +UNFORTUNATELY, THEY ARE USING A CLOAKING DEVICE SO +YOU ARE UNABLE TO SEE THEM, BUT WITH A SPECIAL +INSTRUMENT YOU CAN TELL HOW NEAR THEIR SHIP YOUR +PHOTON BOMB EXPLODED. YOU HAVE SEVEN HOURS UNTIL THEY +HAVE BUILT UP SUFFICIENT POWER IN ORDER TO ESCAPE +YOUR PLANET'S GRAVITY. +YOUR PLANET HAS ENOUGH POWER TO FIRE ONE BOMB AN HOUR. +AT THE BEGINNING OF EACH HOUR YOU WILL BE ASKED TO GIVE AN +ANGLE (BETWEEN 0 AND 360) AND A DISTANCE IN UNITS OF +100 MILES (BETWEEN 100 AND 300), AFTER WHICH YOUR BOMB'S +DISTANCE FROM THE ENEMY SHIP WILL BE GIVEN. +AN EXPLOSION WITHIN 5,000 MILES OF THE ROMULAN SHIP +WILL DESTROY IT. +BELOW IS A DIAGRAM TO HELP YOU VISUALIZE YOUR PLIGHT. + 90 + 0000000000000 + 0000000000000000000 + 000000 000000 + 00000 00000 + 00000 XXXXXXXXXXX 00000 + 00000 XXXXXXXXXXXXX 00000 + 0000 XXXXXXXXXXXXXXX 0000 + 0000 XXXXXXXXXXXXXXXXX 0000 + 0000 XXXXXXXXXXXXXXXXXXX 0000 +180<== 00000 XXXXXXXXXXXXXXXXXXX 00000 ==>0 + 0000 XXXXXXXXXXXXXXXXXXX 0000 + 0000 XXXXXXXXXXXXXXXXX 0000 + 0000 XXXXXXXXXXXXXXX 0000 + 00000 XXXXXXXXXXXXX 00000 + 00000 XXXXXXXXXXX 00000 + 00000 00000 + 000000 000000 + 0000000000000000000 + 0000000000000 + 270 +X - YOUR PLANET +O - THE ORBIT OF THE ROMULAN SHIP +ON THE ABOVE DIAGRAM, THE ROMULAN SHIP IS CIRCLING +COUNTERCLOCKWISE AROUND YOUR PLANET. DON'T FORGET THAT +WITHOUT SUFFICIENT POWER THE ROMULAN SHIP'S ALTITUDE +AND ORBITAL RATE WILL REMAIN CONSTANT. +GOOD LUCK. THE FEDERATION IS COUNTING ON YOU. +" +end + + +def get_yes_or_no() + while true + response = gets.chomp!.upcase + if response == "YES" + return true + elsif response == "NO" + return false + else + print("PLEASE TYPE 'YES' OR 'NO'") + end + end +end + +def game_over(is_success) + if is_success + puts "YOU HAVE SUCCESSFULLY COMPLETED YOUR MISSION." + else + puts "YOU HAVE ALLOWED THE ROMULANS TO ESCAPE." + puts "ANOTHER ROMULAN SHIP HAS GONE INTO ORBIT." + puts "DO YOU WISH TO TRY TO DESTROY IT?" + return get_yes_or_no() + end +end + +def play_game + rom_angle = rand(1...360) + rom_distance = rand(100...301) + rom_angular_velocity = rand(10...31) + hour = 0 + while hour < 7 + hour += 1 + puts "\n\n" + puts "THIS IS HOUR #{hour}, AT WHAT ANGLE DO YOU WISH TO SEND" + puts "YOUR PHOTON BOMB?" + + bomb_angle = gets.chomp!.to_f + puts "HOW FAR OUT DO YOU WISH TO DETONATE IT?" + bomb_distance = gets.chomp!.to_f + puts "\n\n" + + rom_angle = (rom_angle + rom_angular_velocity) % 360 + angular_difference = rom_angle - bomb_angle + c = Math.sqrt(rom_distance**2 + bomb_distance**2 - 2 * rom_distance * bomb_distance * Math.cos(angular_difference * Math::PI / 180)) + + puts "YOUR PHOTON BOMB EXPLODED #{sprintf('%.4f', 5)}*10^2 MILES FROM THE" + puts "ROMULAN SHIP." + + if c <= 50 + # Destroyed the Romulan + return true + end + end + return false +end + +def main + print_centered "ORBIT" + + print_instructions() + + while true + success = play_game() + again = game_over(success) + if !again + return + end + end +end + +if __FILE__ == $0 + main +end + \ No newline at end of file diff --git a/69_Pizza/python/pizza.py b/69_Pizza/python/pizza.py index bb8ed3ed..e4e36bea 100644 --- a/69_Pizza/python/pizza.py +++ b/69_Pizza/python/pizza.py @@ -89,7 +89,7 @@ def print_instructions() -> str: return player_name -def yes_no_prompt(msg): +def yes_no_prompt(msg: str) -> bool: while True: print(msg) response = input().upper() @@ -113,11 +113,11 @@ def print_more_directions(player_name: str) -> None: print() -def calculate_customer_index(x, y): +def calculate_customer_index(x: int, y: int) -> int: return 4 * (y - 1) + x - 1 -def deliver_to(customer_index, customer_name, player_name): +def deliver_to(customer_index, customer_name, player_name) -> bool: print(f" DRIVER TO {player_name}: WHERE DOES {customer_name} LIVE?") coords = input() @@ -133,7 +133,7 @@ def deliver_to(customer_index, customer_name, player_name): return False -def play_game(num_turns, player_name): +def play_game(num_turns, player_name) -> None: for _turn in range(num_turns): x = random.randint(1, 4) y = random.randint(1, 4) diff --git a/70_Poetry/python/poetry.py b/70_Poetry/python/poetry.py index 84b0b2ae..30212630 100644 --- a/70_Poetry/python/poetry.py +++ b/70_Poetry/python/poetry.py @@ -6,24 +6,20 @@ A poetry generator Ported by Dave LeCompte """ -# PORTING EDITORIAL NOTE: -# -# The original code is a pretty convoluted mesh of GOTOs and global -# state. This adaptation pulls things apart into phrases, but I have -# left the variables as globals, which makes goes against decades of -# wisdom that global state is bad. import random +from dataclasses import dataclass PAGE_WIDTH = 64 -# globals -u = 0 -i = 0 -j = 0 -k = 0 -phrase = 1 -line = "" +@dataclass +class State: + u: int = 0 + i: int = 0 + j: int = 0 + k: int = 0 + phrase: int = 1 + line: str = "" def print_centered(msg: str) -> None: @@ -31,9 +27,7 @@ def print_centered(msg: str) -> None: print(spaces + msg) -def process_phrase_1() -> str: - global line - +def process_phrase_1(state: State) -> str: line_1_options = [ "MIDNIGHT DREARY", "FIERY EYES", @@ -41,15 +35,11 @@ def process_phrase_1() -> str: "THING OF EVIL", "PROPHET", ] - - line = line + line_1_options[i] - return line + state.line = state.line + line_1_options[state.i] + return state.line -def process_phrase_2() -> None: - global line - global u - +def process_phrase_2(state: State) -> None: line_2_options = [ ("BEGUILING ME", 2), ("THRILLED ME", None), @@ -57,15 +47,13 @@ def process_phrase_2() -> None: ("NEVER FLITTING", 2), ("BURNED", None), ] - words, u_modifier = line_2_options[i] - line += words + words, u_modifier = line_2_options[state.i] + state.line += words if not (u_modifier is None): - u = u_modifier + state.u = u_modifier -def process_phrase_3() -> None: - global line - +def process_phrase_3(state: State) -> None: phrases = [ (False, "AND MY SOUL"), (False, "DARKNESS THERE"), @@ -74,14 +62,12 @@ def process_phrase_3() -> None: (True, "SIGN OF PARTING"), ] - only_if_u, words = phrases[i] - if (not only_if_u) or (u > 0): - line = line + words + only_if_u, words = phrases[state.i] + if (not only_if_u) or (state.u > 0): + state.line = state.line + words -def process_phrase_4() -> None: - global line - +def process_phrase_4(state: State) -> None: phrases = [ ("NOTHING MORE"), ("YET AGAIN"), @@ -90,55 +76,42 @@ def process_phrase_4() -> None: ("NEVERMORE"), ] - line += phrases[i] + state.line += phrases[state.i] -def maybe_comma(): - # line 210 - global u - global line - - if len(line) > 0 and line[-1] == ".": +def maybe_comma(state: State) -> None: + if len(state.line) > 0 and state.line[-1] == ".": # don't follow a period with a comma, ever return - if u != 0 and random.random() <= 0.19: - line += ", " - u = 2 - # line 212 + if state.u != 0 and random.random() <= 0.19: + state.line += ", " + state.u = 2 if random.random() <= 0.65: - line += " " - u += 1 + state.line += " " + state.u += 1 else: - # line 214 - print(line) - line = "" - u = 0 + print(state.line) + state.line = "" + state.u = 0 -def pick_phrase(): - global phrase - global line - global i, j, k +def pick_phrase(state: State) -> None: + state.i = random.randint(0, 4) + state.j += 1 + state.k += 1 - i = random.randint(0, 4) - j += 1 - k += 1 - - if u <= 0 and (j % 2) != 0: + if state.u <= 0 and (state.j % 2) != 0: # random indentation is fun! - line += " " * 5 - phrase = j + 1 + state.line += " " * 5 + state.phrase = state.j + 1 def main() -> None: print_centered("POETRY") - print_centered("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") - print() - print() - print() + print_centered("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n") - global line, phrase, j, k, u + state = State() phrase_processors = { 1: process_phrase_1, @@ -148,21 +121,21 @@ def main() -> None: } while True: - if phrase >= 1 and phrase <= 4: - phrase_processors[phrase]() - maybe_comma() - elif phrase == 5: - j = 0 - print(line) - line = "" - if k > 20: + if state.phrase >= 1 and state.phrase <= 4: + phrase_processors[state.phrase](state) + maybe_comma(state) + elif state.phrase == 5: + state.j = 0 + print(state.line) + state.line = "" + if state.k > 20: print() - u = 0 - k = 0 + state.u = 0 + state.k = 0 else: - phrase = 2 + state.phrase = 2 continue - pick_phrase() + pick_phrase(state) if __name__ == "__main__": diff --git a/72_Queen/python/queen.py b/72_Queen/python/queen.py index 201ea5d0..94f377a1 100755 --- a/72_Queen/python/queen.py +++ b/72_Queen/python/queen.py @@ -134,17 +134,10 @@ SAFE_SPOTS: Final[FrozenSet[Tuple[int, int]]] = COMPUTER_SAFE_SPOTS | frozenset( ) -def str_with_tab(indent: int, text: str, uppercase: bool = True) -> str: - """Create a string with ``indent`` spaces followed by ``text``.""" - if uppercase: - text = text.upper() - return " " * indent + text - - def intro() -> None: """Print the intro and print instructions if desired.""" - print(str_with_tab(33, "Queen")) - print(str_with_tab(15, "Creative Computing Morristown, New Jersey")) + print(" " * 33 + "Queen") + print(" " * 15 + "Creative Computing Morristown, New Jersey") print("\n" * 2) if ask("DO YOU WANT INSTRUCTIONS"): print(INSTR_TXT) diff --git a/73_Reverse/python/reverse.py b/73_Reverse/python/reverse.py index b72aa320..f75ece2f 100755 --- a/73_Reverse/python/reverse.py +++ b/73_Reverse/python/reverse.py @@ -5,7 +5,7 @@ import textwrap NUMCNT = 9 # How many numbers are we playing with? -def play(): +def main() -> None: print("REVERSE".center(72)) print("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY".center(72)) print() @@ -14,7 +14,7 @@ def play(): print() if not input("DO YOU WANT THE RULES? (yes/no) ").lower().startswith("n"): - rules() + print_rules() while True: game_loop() @@ -23,7 +23,7 @@ def play(): return -def game_loop(): +def game_loop() -> None: """Play the main game.""" # Make a random list from 1 to NUMCNT numbers = list(range(1, NUMCNT + 1)) @@ -67,12 +67,10 @@ def game_loop(): def print_list(numbers) -> None: - """Print out the list""" print(" ".join(map(str, numbers))) -def rules(): - """Print out the rules""" +def print_rules() -> None: help = textwrap.dedent( """ THIS IS THE GAME OF "REVERSE". TO WIN, ALL YOU HAVE @@ -103,6 +101,6 @@ def rules(): if __name__ == "__main__": try: - play() + main() except KeyboardInterrupt: pass diff --git a/74_Rock_Scissors_Paper/javascript/rockscissors.html b/74_Rock_Scissors_Paper/javascript/rockscissors.html deleted file mode 100644 index 3b5199a4..00000000 --- a/74_Rock_Scissors_Paper/javascript/rockscissors.html +++ /dev/null @@ -1,10 +0,0 @@ - - -ROCK, SCISSORS, PAPER - - - -
    
    -
    -
    -
    diff --git a/74_Rock_Scissors_Paper/javascript/rockscissors.js b/74_Rock_Scissors_Paper/javascript/rockscissors.js
    deleted file mode 100644
    index 707712fb..00000000
    --- a/74_Rock_Scissors_Paper/javascript/rockscissors.js
    +++ /dev/null
    @@ -1,107 +0,0 @@
    -// ROCK, SCISSORS, PAPER
    -//
    -// 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(21) + "GAME OF ROCK, SCISSORS, PAPER\n");
    -    print(tab(15) + "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY\n");
    -    print("\n");
    -    print("\n");
    -    print("\n");
    -    while (1) {
    -        print("HOW MANY GAMES");
    -        q = parseInt(await input());
    -        if (q >= 11)
    -            print("SORRY, BUT WE AREN'T ALLOWED TO PLAY THAT MANY.\n");
    -        else
    -            break;
    -    }
    -    h = 0;  // Human
    -    c = 0;  // Computer
    -    for (g = 1; g <= q; g++ ) {
    -        print("\n");
    -        print("GAME NUMBER " + g + "\n");
    -        x = Math.floor(Math.random() * 3 + 1);
    -        while (1) {
    -            print("3=ROCK...2=SCISSORS...1=PAPER\n");
    -            print("1...2...3...WHAT'S YOUR CHOICE");
    -            k = parseInt(await input());
    -            if (k != 1 && k != 2 && k != 3)
    -                print("INVALID.\n");
    -            else
    -                break;
    -        }
    -        print("THIS IS MY CHOICE...");
    -        switch (x) {
    -            case 1:
    -                print("...PAPER\n");
    -                break;
    -            case 2:
    -                print("...SCISSORS\n");
    -                break;
    -            case 3:
    -                print("...ROCK\n");
    -                break;
    -        }
    -        if (x == k) {
    -            print("TIE GAME.  NO WINNER.\n");
    -        } else if ((x > k && (k != 1 || x != 3)) || (x == 1 && k == 3)) {
    -            print("WOW!  I WIN!!!\n");
    -            c++;
    -        } else {
    -            print("YOU WIN!!!\n");
    -            h++;
    -        }
    -    }
    -    print("\n");
    -    print("HERE IS THE FINAL GAME SCORE:\n");
    -    print("I HAVE WON " + c + " GAME(S).\n");
    -    print("YOU HAVE WON " + h + " GAME(S).\n");
    -    print("AND " + (q - (c + h)) + " GAME(S) ENDED IN A TIE.\n");
    -    print("\n");
    -    print("THANKS FOR PLAYING!!\n");
    -}
    -
    -main();
    diff --git a/74_Rock_Scissors_Paper/javascript/rockscissors.mjs b/74_Rock_Scissors_Paper/javascript/rockscissors.mjs
    new file mode 100644
    index 00000000..be2328f2
    --- /dev/null
    +++ b/74_Rock_Scissors_Paper/javascript/rockscissors.mjs
    @@ -0,0 +1,117 @@
    +#!/usr/bin/env node
    +// ROCK, SCISSORS, PAPER
    +//
    +// Converted from BASIC to Javascript by Alexander Wunschik (mojoaxel)
    +
    +import { println, tab, input } from '../../00_Common/javascript/common.mjs';
    +
    +let userWins = 0;
    +let computerWins = 0;
    +let ties = 0;
    +
    +// 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
    +async function getGameCount() {
    +	let gameCount = await input("HOW MANY GAMES");
    +	if (gameCount > 10) {
    +		println("SORRY, BUT WE AREN'T ALLOWED TO PLAY THAT MANY.");
    +		return await getGameCount();
    +	}
    +	return gameCount;
    +}
    +
    +// #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
    +async function getUserInput() {
    +	println("3=ROCK...2=SCISSORS...1=PAPER");
    +	const userChoice = await input("1...2...3...WHAT'S YOUR CHOICE");
    +	if (userChoice < 1 || userChoice > 3) {
    +		println("INVALID.");
    +		return await getUserInput();
    +	}
    +	return userChoice;
    +}
    +
    +async function game() {
    +	// 10 PRINT TAB(21);"GAME OF ROCK, SCISSORS, PAPER"
    +	// 20 PRINT TAB(15);"CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"
    +	// 25 PRINT:PRINT:PRINT
    +	println(tab(21), 'GAME OF ROCK, SCISSORS, PAPER');
    +	println(tab(15), 'CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY');
    +	println('\n\n');
    +
    +	let gameCount = await getGameCount();
    +
    +	async function playGame(gameNumber) {
    +		// 70 PRINT: PRINT "GAME NUMBER";G
    +		println("\nGAME NUMBER ", gameNumber);
    +
    +		const ROCK = 3;
    +		const SCISSORS = 2;
    +		const PAPER = 1;
    +
    +		const usersChoice = await getUserInput();
    +
    +		// 80 X=INT(RND(1)*3+1)
    +		const computersChoice = Math.floor(Math.random()*3) + 1;
    +
    +		// 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"
    +		println("THIS IS MY CHOICE...", 
    +			computersChoice === PAPER ? "...PAPER" : 
    +				computersChoice === SCISSORS ? "...SCISSORS" : 
    +					"...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."
    +		if (computersChoice == usersChoice) {
    +			println("TIE GAME.  NO WINNER.");
    +			ties++;
    +		} else if (
    +			(computersChoice == ROCK && usersChoice == SCISSORS) ||
    +			(computersChoice == PAPER && usersChoice == ROCK) ||
    +			(computersChoice == SCISSORS && usersChoice == PAPER)
    +		) {
    +			println("WOW!  I WIN!!!");
    +			computerWins++;
    +		} else {
    +			println("YOU WIN!!!");
    +			userWins++;
    +		}
    +	}
    +
    +	for (let gameNumber = 1; gameNumber <= gameCount; gameNumber++) {
    +		await playGame(gameNumber);
    +		// 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+H20);"GAME(S) ENDED IN A TIE."
    +	println("\nHERE IS THE FINAL GAME SCORE:");
    +	println(`I HAVE WON ${computerWins} GAME(S).`);
    +	println(`YOU HAVE WON ${userWins} GAME(S).`);
    +	println(`AND ${ties} GAME(S) ENDED IN A TIE.`);
    +
    +	// 310 PRINT: PRINT "THANKS FOR PLAYING!!"
    +	println("\nTHANKS FOR PLAYING!!");
    +	
    +	// 320 END
    +	process.exit(0);
    +}
    +game();
    diff --git a/74_Rock_Scissors_Paper/python/rockscissors.py b/74_Rock_Scissors_Paper/python/rockscissors.py
    index 09d63d2d..898f709c 100644
    --- a/74_Rock_Scissors_Paper/python/rockscissors.py
    +++ b/74_Rock_Scissors_Paper/python/rockscissors.py
    @@ -6,7 +6,7 @@
     import random
     
     
    -def play_game():
    +def play_game() -> None:
         """Play one round of the game"""
     
         while True:
    diff --git a/75_Roulette/python/roulette.py b/75_Roulette/python/roulette.py
    index b20b00e6..7a21d36d 100644
    --- a/75_Roulette/python/roulette.py
    +++ b/75_Roulette/python/roulette.py
    @@ -75,8 +75,8 @@ def query_bets() -> Tuple[List[int], List[int]]:
         for i in range(bet_count):
             while bet_ids[i] == -1:
                 try:
    -                inString = input("NUMBER " + str(i + 1) + "? ").split(",")
    -                id_, val = int(inString[0]), int(inString[1])
    +                in_string = input("NUMBER " + str(i + 1) + "? ").split(",")
    +                id_, val = int(in_string[0]), int(in_string[1])
     
                     # check other bet_IDs
                     for j in range(i):
    @@ -93,7 +93,7 @@ def query_bets() -> Tuple[List[int], List[int]]:
         return bet_ids, bet_values
     
     
    -def bet_results(bet_ids: List[int], bet_values: List[int], result):
    +def bet_results(bet_ids: List[int], bet_values: List[int], result) -> int:
         """Computes the results, prints them, and returns the total net winnings"""
         total_winnings = 0
     
    @@ -134,7 +134,7 @@ def bet_results(bet_ids: List[int], bet_values: List[int], result):
     
     
     def print_check(amount: int) -> None:
    -    """Prints a check of a given amount"""
    +    """Print a check of a given amount"""
         name = input("TO WHOM SHALL I MAKE THE CHECK? ")
     
         print("-" * 72)
    diff --git a/76_Russian_Roulette/python/russianroulette.py b/76_Russian_Roulette/python/russianroulette.py
    index 3af23515..f402fb03 100644
    --- a/76_Russian_Roulette/python/russianroulette.py
    +++ b/76_Russian_Roulette/python/russianroulette.py
    @@ -1,19 +1,17 @@
    -########################################################
    -#
    -# Russian Roulette
    -#
    -# From Basic Computer Games (1978)
    -#
    -#    In this game, you are given by the computer a
    -#   revolver loaded with one bullet and five empty
    -#   chambers. You spin the chamber and pull the trigger
    -#   by inputting a "1", or, if you want to quit, input
    -#   a "2". You win if you play ten times and are still
    -#   alive.
    -#    Tom Adametx wrote this program while a student at
    -#   Curtis Jr. High School in Sudbury, Massachusetts.
    -#
    -########################################################
    +"""
    +Russian Roulette
    +
    +From Basic Computer Games (1978)
    +
    +   In this game, you are given by the computer a
    +  revolver loaded with one bullet and five empty
    +  chambers. You spin the chamber and pull the trigger
    +  by inputting a "1", or, if you want to quit, input
    +  a "2". You win if you play ten times and are still
    +  alive.
    +   Tom Adametx wrote this program while a student at
    +  Curtis Jr. High School in Sudbury, Massachusetts.
    +"""
     
     
     from random import random
    diff --git a/77_Salvo/python/salvo.py b/77_Salvo/python/salvo.py
    index 456ad755..368b15da 100644
    --- a/77_Salvo/python/salvo.py
    +++ b/77_Salvo/python/salvo.py
    @@ -1,17 +1,14 @@
     import random
     import re
    +from typing import List, Optional, Tuple
     
    -###################
    -#
    -# static variables
    -#
    -###################
    +BoardType = List[List[Optional[int]]]
    +CoordinateType = Tuple[int, int]
     
     BOARD_WIDTH = 10
     BOARD_HEIGHT = 10
     
    -# game ships
    -#
    +
     # data structure keeping track of information
     # about the ships in the game. for each ship,
     # the following information is provided:
    @@ -35,26 +32,21 @@ VALID_MOVES = [
         [1, 0],  # South
         [1, -1],  # South West
         [0, -1],  # West
    -    [-1, -1],
    -]  # North West
    +    [-1, -1],  # North West
    +]
     
     COORD_REGEX = "[ \t]{0,}(-?[0-9]{1,3})[ \t]{0,},[ \t]{0,}(-?[0-9]{1,2})"
     
    -####################
    -#
    -# global variables
    -#
    -####################
     
     # array of BOARD_HEIGHT arrays, BOARD_WIDTH in length,
     # representing the human player and computer
    -player_board = []
    -computer_board = []
    +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 = []
    +computer_ship_coords: List[List[CoordinateType]] = []
     
     
     ####################################
    @@ -88,10 +80,9 @@ num_player_shots = 7
     #
     ####################################
     
    -# flag indicating whose turn
    -# it currently is
    -COMPUTER = 0
    -PLAYER = 1
    +# flag indicating whose turn it currently is
    +COMPUTER = False
    +PLAYER = True
     active_turn = COMPUTER
     
     ####################
    @@ -108,26 +99,27 @@ random.seed()
     
     # random_x_y
     #
    -# generate a valid x,y coordinate on the board
    -# returns: x,y
    -#   x: integer between 1 and BOARD_HEIGHT
    -#   y: integer between 1 and BOARD WIDTH
    -def 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)
     
     
    -# input_coord
    -#
    -# 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.
    -def input_coord():
    +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("? ")
    @@ -148,25 +140,25 @@ def input_coord():
         return x, y
     
     
    -# generate_ship_coordinates
    -#
    -# 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)
    -def generate_ship_coordinates(ship):
    +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()
     
    @@ -213,25 +205,18 @@ def generate_ship_coordinates(ship):
         return coords
     
     
    -# create_blank_board
    -#
    -# helper function to create a game board
    -# that is blank
    -def create_blank_board():
    -    return [[None for y in range(BOARD_WIDTH)] for x in range(BOARD_HEIGHT)]
    +def create_blank_board() -> BoardType:
    +    """Create a blank game board"""
    +    return [[None for _y in range(BOARD_WIDTH)] for _x in range(BOARD_HEIGHT)]
     
     
    -# print_board
    -#
    -# print out the game board for testing
    -# purposes
    -def print_board(board) -> None:
    -
    +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("")
    +    print()
     
         for x in range(len(board)):
             print(f"{x+1:2}", end="")
    @@ -240,30 +225,34 @@ def print_board(board) -> None:
                     print(f"{' ':3}", end="")
                 else:
                     print(f"{board[x][y]:3}", end="")
    -        print("")
    +        print()
     
     
    -# place_ship
    -#
    -# 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 repreesnting the type of ship (given in SHIPS)
    -def place_ship(board, coords, ship):
    +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
     
     
    -# 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)]
    -def generate_board():
    +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 = []
    @@ -284,7 +273,9 @@ def generate_board():
         return board, ship_coords
     
     
    -def execute_shot(turn, board, x, y, current_turn):
    +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
    @@ -298,12 +289,8 @@ def execute_shot(turn, board, x, y, current_turn):
         return ship_hit
     
     
    -# calculate_shots
    -#
    -# function to examine each board
    -# and determine how many shots remaining
    -def calculate_shots(board):
    -
    +def calculate_shots(board: BoardType) -> int:
    +    """Examine each board and determine how many shots remaining"""
         ships_found = [0 for x in range(len(SHIPS))]
         for x in range(BOARD_HEIGHT):
             for y in range(BOARD_WIDTH):
    @@ -318,19 +305,12 @@ def calculate_shots(board):
         return shots
     
     
    -# initialize
    -#
    -# function to initialize global variables used
    -# during game play.
    -def initialize_game():
    -
    -    # initialize the global player and computer
    -    # boards
    +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
    +    # generate the ships for the computer's board
         global computer_board
         global computer_ship_coords
         computer_board, computer_ship_coords = generate_board()
    @@ -338,7 +318,7 @@ def initialize_game():
         # print out the title 'screen'
         print("{:>38}".format("SALVO"))
         print("{:>57s}".format("CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY"))
    -    print("")
    +    print()
         print("{:>52s}".format("ORIGINAL BY LAWRENCE SIEGEL, 1973"))
         print("{:>56s}".format("PYTHON 3 PORT BY TODD KAISER, MARCH 2021"))
         print("\n")
    @@ -355,8 +335,8 @@ def initialize_game():
             ship_coords.append(list)
     
         # add ships to the user's board
    -    for ship in range(len(SHIPS)):
    -        place_ship(player_board, ship_coords[ship], ship)
    +    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
    @@ -366,9 +346,9 @@ def initialize_game():
         while input_loop:
             player_start = input("DO YOU WANT TO START? ")
             if player_start == "WHERE ARE YOUR SHIPS?":
    -            for ship in range(len(SHIPS)):
    -                print(SHIPS[ship][0])
    -                coords = computer_ship_coords[ship]
    +            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]
    @@ -384,14 +364,11 @@ def initialize_game():
             print_computer_shots = True
     
         global first_turn
    -    global second_turn
         if player_start.lower() != "yes":
             first_turn = COMPUTER
    -        second_turn = PLAYER
     
         # calculate the initial number of shots for each
    -    global num_computer_shots
    -    global num_player_shots
    +    global num_computer_shots, num_player_shots
         num_player_shots = calculate_shots(player_board)
         num_computer_shots = calculate_shots(computer_board)
     
    @@ -407,31 +384,22 @@ def initialize_game():
     # forth, replicating the gotos in the original game
     
     
    -# initialize the first_turn function to the
    -# player's turn
    +# initialize the first_turn function to the player's turn
     first_turn = PLAYER
     
     
    -# initialize the second_turn to the computer's
    -# turn
    -second_turn = COMPUTER
    +def execute_turn(turn: bool, current_turn: int) -> int:
    +    global num_computer_shots, num_player_shots
     
    -
    -def execute_turn(turn, current_turn):
    -
    -    global num_computer_shots
    -    global num_player_shots
    -
    -    # print out the number of shots the current
    -    # player has
    +    # print out the number of shots the current player has
         board = None
         num_shots = 0
         if turn == COMPUTER:
    -        print("I HAVE", num_computer_shots, "SHOTS.")
    +        print(f"I HAVE {num_computer_shots} SHOTS.")
             board = player_board
             num_shots = num_computer_shots
         else:
    -        print("YOU HAVE", num_player_shots, "SHOTS.")
    +        print(f"YOU HAVE {num_player_shots} SHOTS.")
             board = computer_board
             num_shots = num_player_shots
     
    @@ -486,34 +454,22 @@ def execute_turn(turn, current_turn):
     
     
     def main() -> None:
    -    # keep track of the turn
         current_turn = 0
    -
    -    # initialize the player and computer
    -    # boards
         initialize_game()
     
         # execute turns until someone wins or we run
         # out of squares to shoot
    -
         game_over = False
         while not game_over:
    -
    -        # increment the turn
    -        current_turn = current_turn + 1
    +        current_turn += 1
     
             print("\n")
             print("TURN", current_turn)
     
    -        # print("computer")
    -        # print_board(computer_board)
    -        # print("player")
    -        # print_board(player_board)
    -
    -        if execute_turn(first_turn, current_turn) == 0:
    -            game_over = True
    -            continue
    -        if execute_turn(second_turn, current_turn) == 0:
    +        if (
    +            execute_turn(first_turn, current_turn) == 0
    +            or execute_turn(not first_turn, current_turn) == 0
    +        ):
                 game_over = True
                 continue
     
    diff --git a/78_Sine_Wave/javascript/sinewave.html b/78_Sine_Wave/javascript/sinewave.html
    deleted file mode 100644
    index c9ff7d9b..00000000
    --- a/78_Sine_Wave/javascript/sinewave.html
    +++ /dev/null
    @@ -1,16 +0,0 @@
    -
    -
    -SINE WAVE
    -
    -
    -
    -
    
    -
    -
    -
    -
    diff --git a/78_Sine_Wave/javascript/sinewave.js b/78_Sine_Wave/javascript/sinewave.js
    deleted file mode 100644
    index 3823273d..00000000
    --- a/78_Sine_Wave/javascript/sinewave.js
    +++ /dev/null
    @@ -1,22 +0,0 @@
    -print(tab(30), "SINE WAVE");
    -print(tab(15), "CREATIVE COMPUTING  MORRISTOWN, NEW JERSEY");
    -print("\n\n\n\n");
    -
    -// 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));
    -  print(tab(indent), toggleWord ? "CREATIVE" : "COMPUTING");
    -  toggleWord = !toggleWord;
    -}
    -
    -function print(...messages) {
    -  console.log(messages.join(" "));
    -}
    -
    -function tab(count) {
    -  return " ".repeat(count);
    -}
    diff --git a/78_Sine_Wave/javascript/sinewave.mjs b/78_Sine_Wave/javascript/sinewave.mjs
    new file mode 100644
    index 00000000..bd390e75
    --- /dev/null
    +++ b/78_Sine_Wave/javascript/sinewave.mjs
    @@ -0,0 +1,18 @@
    +#!/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;
    +}
    diff --git a/79_Slalom/python/slalom.py b/79_Slalom/python/slalom.py
    index 1db5d5d8..a6833331 100644
    --- a/79_Slalom/python/slalom.py
    +++ b/79_Slalom/python/slalom.py
    @@ -7,17 +7,17 @@ medals = {
     }
     
     
    -def ask(question):
    +def ask(question: str) -> str:
         print(question, end="? ")
         return input().upper()
     
     
    -def ask_int(question):
    +def ask_int(question: str) -> int:
         reply = ask(question)
         return int(reply) if reply.isnumeric() else -1
     
     
    -def pre_run(gates, max_speeds):
    +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')
    @@ -50,10 +50,10 @@ def pre_run(gates, max_speeds):
                 cmd = ask(f'"{cmd}" is an illegal command--Retry')
     
     
    -def run(gates, lvl, max_speeds):
    +def run(gates, lvl, max_speeds) -> None:
         global medals
         print("The starter counts down...5...4...3...2...1...Go!")
    -    time = 0
    +    time: float = 0
         speed = int(random() * (18 - 9) + 9)
         print("You're off")
         for i in range(0, gates):
    diff --git a/80_Slots/python/slots.py b/80_Slots/python/slots.py
    index e54f3b4e..22c2b3a3 100644
    --- a/80_Slots/python/slots.py
    +++ b/80_Slots/python/slots.py
    @@ -34,9 +34,10 @@
     import sys
     from collections import Counter
     from random import choices
    +from typing import List
     
     
    -def initial_message():
    +def initial_message() -> None:
         print(" " * 30 + "Slots")
         print(" " * 15 + "Creative Computing Morrison, New Jersey")
         print("\n" * 3)
    @@ -45,7 +46,7 @@ def initial_message():
         print("To pull the arm, punch the return key after making your bet.")
     
     
    -def input_betting():
    +def input_betting() -> int:
         print("\n")
         b = -1
         while b < 1 or b > 100:
    @@ -61,7 +62,7 @@ def input_betting():
         return int(b)
     
     
    -def beeping():
    +def beeping() -> None:
         # Function to produce a beep sound.
         # In the original program is the subroutine at line 1270
         for _ in range(5):
    @@ -69,7 +70,7 @@ def beeping():
             sys.stdout.flush()
     
     
    -def spin_wheels():
    +def spin_wheels() -> List[str]:
         possible_fruits = ["Bar", "Bell", "Orange", "Lemon", "Plum", "Cherry"]
         wheel = choices(possible_fruits, k=3)
     
    @@ -79,7 +80,7 @@ def spin_wheels():
         return wheel
     
     
    -def adjust_profits(wheel, m, profits):
    +def adjust_profits(wheel: List[str], m: int, profits: int) -> int:
         # we remove the duplicates
         s = set(wheel)
     
    @@ -117,7 +118,7 @@ def adjust_profits(wheel, m, profits):
         return profits
     
     
    -def final_message(profits) -> None:
    +def final_message(profits: int) -> None:
         if profits < 0:
             print("Pay up!  Please leave your money on the terminal")
         elif profits == 0:
    @@ -140,7 +141,7 @@ def main() -> None:
             answer = input("Again?")
     
             try:
    -            if not answer[0].lower() == "y":
    +            if answer[0].lower() != "y":
                     keep_betting = False
             except IndexError:
                 keep_betting = False
    diff --git a/81_Splat/python/splat.py b/81_Splat/python/splat.py
    index 665494b3..30ec74b5 100644
    --- a/81_Splat/python/splat.py
    +++ b/81_Splat/python/splat.py
    @@ -24,22 +24,22 @@ Ported in 2021 by Jonas Nockert / @lemonad
     """
     from math import sqrt
     from random import choice, random, uniform
    -from typing import List
    +from typing import List, Tuple
     
     PAGE_WIDTH = 72
     
     
    -def numeric_input(question, default=0):
    +def numeric_input(question, default=0) -> float:
         """Ask user for a numeric value."""
         while True:
    -        answer = input(f"{question} [{default}]: ").strip() or default
    +        answer_str = input(f"{question} [{default}]: ").strip() or default
             try:
    -            return float(answer)
    +            return float(answer_str)
             except ValueError:
                 pass
     
     
    -def yes_no_input(question, default="YES"):
    +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"]:
    @@ -47,7 +47,7 @@ def yes_no_input(question, default="YES"):
         return answer.lower() in ["y", "yes"]
     
     
    -def get_terminal_velocity():
    +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)
    @@ -60,7 +60,7 @@ def get_terminal_velocity():
         return v1 * (5280 / 3600)
     
     
    -def get_acceleration():
    +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)
    @@ -70,14 +70,14 @@ def get_acceleration():
         return a2
     
     
    -def get_freefall_time():
    +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 = 0
    +    t_freefall: float = 0
         # A zero or negative freefall time is not handled by the motion
         # equations during the jump.
         while t_freefall <= 0:
    @@ -85,13 +85,13 @@ def get_freefall_time():
         return t_freefall
     
     
    -def jump():
    +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 = 0  # Terminal velocity.
    -    a = 0  # Acceleration.
    +    v: float = 0  # Terminal velocity.
    +    a: float = 0  # Acceleration.
         initial_altitude = int(9001 * random() + 1000)
     
         v1 = get_terminal_velocity()
    @@ -181,9 +181,9 @@ def jump():
         return altitude
     
     
    -def pick_random_celestial_body():
    +def pick_random_celestial_body() -> Tuple[str, float]:
         """Pick a random planet, the moon, or the sun with associated gravity."""
    -    body, gravity = choice(
    +    return choice(
             [
                 ("MERCURY", 12.2),
                 ("VENUS", 28.3),
    @@ -197,10 +197,9 @@ def pick_random_celestial_body():
                 ("THE SUN", 896.0),
             ]
         )
    -    return body, gravity
     
     
    -def jump_stats(previous_jumps, chute_altitude):
    +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
    diff --git a/82_Stars/python/stars.py b/82_Stars/python/stars.py
    index 00e17bb5..d2024d6b 100644
    --- a/82_Stars/python/stars.py
    +++ b/82_Stars/python/stars.py
    @@ -47,14 +47,12 @@ def print_stars(secret_number, guess) -> None:
         print(stars)
     
     
    -def get_guess():
    -    valid_response = False
    -    while not valid_response:
    -        guess = input("Your guess? ")
    -        if guess.isdigit():
    -            valid_response = True
    -            guess = int(guess)
    -    return guess
    +def get_guess(prompt: str) -> int:
    +    while True:
    +        guess_str = input(prompt)
    +        if guess_str.isdigit():
    +            guess = int(guess_str)
    +            return guess
     
     
     def main() -> None:
    @@ -80,8 +78,8 @@ def main() -> None:
             player_has_won = False
             while (guess_number < MAX_GUESSES) and not player_has_won:
     
    -            print("")
    -            guess = get_guess()
    +            print()
    +            guess = get_guess("Your guess? ")
                 guess_number += 1
     
                 if guess == secret_number:
    diff --git a/82_Stars/rust/Cargo.toml b/82_Stars/rust/Cargo.toml
    index 3b1d02f5..439dcb0e 100644
    --- a/82_Stars/rust/Cargo.toml
    +++ b/82_Stars/rust/Cargo.toml
    @@ -1,9 +1,2829 @@
    -[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"
    +
    +
    +
    +
    +
    +
    +  
    +    
    +  
    +  
    +  
    +  
    +  
    +  
    +
    +
    +
    +  
    +    
    +    
    +  
    +
    +    
    +
    +
    +  
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +  
    +
    +  
    +  
    +  basic-computer-games/Cargo.toml at main · coding-horror/basic-computer-games
    +    
    +    
    +  
    +  
    +  
    +    
    +    
    +    
    +
    +
    +
    +    
    +
    +  
    +    
    +    
    +  
    +
    +
    +  
    +
    +    
    +
    +
    +  
    +
    +  
    +
    +  
    +
    +    
    +  
    +  
    +  
    +
    +
    +
    +  
    +
    +  
    +
    +
    +
    +
    +  
    +
    +      
    +    
    +
    +
    +    
    +    
    +
    +      
    +
    +      
    +
    +    
    +
    +
    +  
    +  
    +  
    +  
    +  
    +
    +    
    +  
    +
    +  
    +
    +
    +
    +    
    +
    +
    +  
    +
    +  
    +
    +  
    +
    +  
    +  
    +  
    +
    +
    +
    +
    +
    +  
    +
    +  
    +
    +  
    +    
    +
    +    
    + Skip to content + + + + + + + + + + + + + + +
    + +
    + + + + + + + +
    + + + +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + coding-horror  /   + basic-computer-games  /   + +
    +
    + + + +
    + + +
    + + +
    + + + +
    +
    + Tip: + Type # to search pull requests +
    +
    + Type ? for help and tips +
    +
    +
    + +
    +
    + Tip: + Type # to search issues +
    +
    + Type ? for help and tips +
    +
    +
    + +
    +
    + Tip: + Type # to search discussions +
    +
    + Type ? for help and tips +
    +
    +
    + +
    +
    + Tip: + Type ! to search projects +
    +
    + Type ? for help and tips +
    +
    +
    + +
    +
    + Tip: + Type @ to search teams +
    +
    + Type ? for help and tips +
    +
    +
    + +
    +
    + Tip: + Type @ to search people and organizations +
    +
    + Type ? for help and tips +
    +
    +
    + +
    +
    + Tip: + Type > to activate command mode +
    +
    + Type ? for help and tips +
    +
    +
    + +
    +
    + Tip: + Go to your accessibility settings to change your keyboard shortcuts +
    +
    + Type ? for help and tips +
    +
    +
    + +
    +
    + Tip: + Type author:@me to search your content +
    +
    + Type ? for help and tips +
    +
    +
    + +
    +
    + Tip: + Type is:pr to filter to pull requests +
    +
    + Type ? for help and tips +
    +
    +
    + +
    +
    + Tip: + Type is:issue to filter to issues +
    +
    + Type ? for help and tips +
    +
    +
    + +
    +
    + Tip: + Type is:project to filter to projects +
    +
    + Type ? for help and tips +
    +
    +
    + +
    +
    + Tip: + Type is:open to filter to open content +
    +
    + Type ? for help and tips +
    +
    +
    + +
    + +
    +
    + We’ve encountered an error and some results aren't available at this time. Type a new search or try again later. +
    +
    + + No results matched your search + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + Search for issues and pull requests + + # + + + + Search for issues, pull requests, discussions, and projects + + # + + + + Search for organizations, repositories, and users + + @ + + + + Search for projects + + ! + + + + Search for files + + / + + + + Activate command mode + + > + + + + Search your issues, pull requests, and discussions + + # author:@me + + + + Search your issues, pull requests, and discussions + + # author:@me + + + + Filter to pull requests + + # is:pr + + + + Filter to issues + + # is:issue + + + + Filter to discussions + + # is:discussion + + + + Filter to projects + + # is:project + + + + Filter to open issues, pull requests, and discussions + + # is:open + + + + + + + + + + + + + + + + +
    +
    +
    + +
    + + + + + + + + + + +
    + + + + +
    +
    +
    + + + + + + + + + +
    + +
    + +
    +

    + + + / + + basic-computer-games + + + Public +

    + +
    + +
      + + + +
    • + +
      + + + + + + + Watch + + + 126 + + + + +
      +
      +

      Notifications

      + +
      + +
      +
      + + + + + + + + +
      + + +
      + + + + + Get push notifications on iOS or Android. + +
      +
      +
      +
      + + + + +
      +
      +
      + + + +
    • + +
    • +
      +
      + Fork + 1k + + + +
      + +

      Fork basic-computer-games

      +
      + +
      + + + + + +

      If this dialog fails to load, you can visit the fork page directly.

      +
      +
      + +
      +
      +
      + +
    • + +
    • + + +
      +
      + + +
      +
      + +
      +
      + + + + +
      + +
      +
      + + + + + + + +
      + +
      +
      +
      +
      +
      +
    • + + + +
    + +
    + +
    +
    + + + + +
    + + + +
    + Open in github.dev + Open in a new github.dev tab + + + + + + +
    + + +
    + + + + + + +Permalink + +
    + +
    +
    + + + main + + + + +
    +
    +
    + Switch branches/tags + +
    + + + +
    + +
    + +
    + + +
    + +
    + + + + + + + + + + + + + + + + +
    + + +
    +
    +
    +
    + +
    + +
    + + + Go to file + + +
    + + + + + +
    +
    +
    + + + + + + + + + +
    + +
    +
    + + @Jay-0331 + +
    + + + + + + +
    +
    + + Latest commit + 34baeec + Apr 1, 2022 + + + + + + History + + +
    +
    + +
    + +
    +
    + + + 1 + + contributor + + +
    + +

    + Users who have contributed to this file +

    +
    + + + + + + +
    +
    +
    +
    + + + + + + + + + + + + +
    + +
    + + +
    + + 9 lines (7 sloc) + + 188 Bytes +
    + +
    + + + + +
    + + + + + + + + + + + + + + + +
    + +
    +
    + +
    +
    + +
    + +
    +
    + + + +
    + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    [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.3"
    +
    + + + +
    + +
    + + + + +
    + + +
    + + +
    +
    + + +
    + +
    + + +
    + +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + diff --git a/82_Stars/rust/README.md b/82_Stars/rust/README.md index 22218bd7..334493b2 100644 --- a/82_Stars/rust/README.md +++ b/82_Stars/rust/README.md @@ -1,23 +1,2692 @@ -#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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + basic-computer-games/README.md at main · coding-horror/basic-computer-games + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Skip to content + + + + + + + + + + + + + + +
    + +
    + + + + + + + +
    + + + +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + coding-horror  /   + basic-computer-games  /   + +
    +
    + + + +
    + + +
    + + +
    + + + +
    +
    + Tip: + Type # to search pull requests +
    +
    + Type ? for help and tips +
    +
    +
    + +
    +
    + Tip: + Type # to search issues +
    +
    + Type ? for help and tips +
    +
    +
    + +
    +
    + Tip: + Type # to search discussions +
    +
    + Type ? for help and tips +
    +
    +
    + +
    +
    + Tip: + Type ! to search projects +
    +
    + Type ? for help and tips +
    +
    +
    + +
    +
    + Tip: + Type @ to search teams +
    +
    + Type ? for help and tips +
    +
    +
    + +
    +
    + Tip: + Type @ to search people and organizations +
    +
    + Type ? for help and tips +
    +
    +
    + +
    +
    + Tip: + Type > to activate command mode +
    +
    + Type ? for help and tips +
    +
    +
    + +
    +
    + Tip: + Go to your accessibility settings to change your keyboard shortcuts +
    +
    + Type ? for help and tips +
    +
    +
    + +
    +
    + Tip: + Type author:@me to search your content +
    +
    + Type ? for help and tips +
    +
    +
    + +
    +
    + Tip: + Type is:pr to filter to pull requests +
    +
    + Type ? for help and tips +
    +
    +
    + +
    +
    + Tip: + Type is:issue to filter to issues +
    +
    + Type ? for help and tips +
    +
    +
    + +
    +
    + Tip: + Type is:project to filter to projects +
    +
    + Type ? for help and tips +
    +
    +
    + +
    +
    + Tip: + Type is:open to filter to open content +
    +
    + Type ? for help and tips +
    +
    +
    + +
    + +
    +
    + We’ve encountered an error and some results aren't available at this time. Type a new search or try again later. +
    +
    + + No results matched your search + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + Search for issues and pull requests + + # + + + + Search for issues, pull requests, discussions, and projects + + # + + + + Search for organizations, repositories, and users + + @ + + + + Search for projects + + ! + + + + Search for files + + / + + + + Activate command mode + + > + + + + Search your issues, pull requests, and discussions + + # author:@me + + + + Search your issues, pull requests, and discussions + + # author:@me + + + + Filter to pull requests + + # is:pr + + + + Filter to issues + + # is:issue + + + + Filter to discussions + + # is:discussion + + + + Filter to projects + + # is:project + + + + Filter to open issues, pull requests, and discussions + + # is:open + + + + + + + + + + + + + + + + +
    +
    +
    + +
    + + + + + + + + + + +
    + + + + +
    +
    +
    + + + + + + + + + +
    + +
    + +
    +

    + + + / + + basic-computer-games + + + Public +

    + +
    + +
      + + + +
    • + +
      + + + + + + + Watch + + + 126 + + + + +
      +
      +

      Notifications

      + +
      + +
      +
      + + + + + + + + +
      + + +
      + + + + + Get push notifications on iOS or Android. + +
      +
      +
      +
      + + + + +
      +
      +
      + + + +
    • + +
    • +
      +
      + Fork + 1k + + + +
      + +

      Fork basic-computer-games

      +
      + +
      + + + + + +

      If this dialog fails to load, you can visit the fork page directly.

      +
      +
      + +
      +
      +
      + +
    • + +
    • + + +
      +
      + + +
      +
      + +
      +
      + + + + +
      + +
      +
      + + + + + + + +
      + +
      +
      +
      +
      +
      +
    • + + + +
    + +
    + +
    +
    + + + + +
    + + + +
    + Open in github.dev + Open in a new github.dev tab + + + + + + +
    + + +
    + + + + + + +Permalink + +
    + +
    +
    + + + main + + + + +
    +
    +
    + Switch branches/tags + +
    + + + +
    + +
    + +
    + + +
    + +
    + + + + + + + + + + + + + + + + +
    + + +
    +
    +
    +
    + +
    + +
    + + + Go to file + + +
    + + + + + +
    +
    +
    + + + + + + + + + +
    + +
    +
    +
     
    +
    + +
    +
     
    + Cannot retrieve contributors at this time +
    +
    + + + + + + + + + + + + +
    + +
    + + +
    + + 3 lines (2 sloc) + + 139 Bytes +
    + +
    + + + + + +
    + + + + + + + + + + + + + + + +
    + +
    +
    + +
    +
    + +
    + +
    +
    + + +
    + +
    + +
    + + + + +
    + + +
    + + +
    +
    + + + + +
    + +
    + + +
    + +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + diff --git a/82_Stars/rust/src/main.rs b/82_Stars/rust/src/main.rs index 2da01ce9..9cf285da 100644 --- a/82_Stars/rust/src/main.rs +++ b/82_Stars/rust/src/main.rs @@ -1,149 +1,3138 @@ -// -// 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(()); - } - } -} + + + + + + + + + + -// 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(); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + basic-computer-games/main.rs at main · coding-horror/basic-computer-games + + + + + + + - // 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); -} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + Skip to content + + + + + + + + + + + + + + +
    + +
    + + + + + + + +
    + + + +
    + + + + + + + + + +
    + + + + + + + + + + + + + + + + + + + + + + + + +
    + +
    + + + + coding-horror  /   + basic-computer-games  /   + +
    +
    + + + +
    + + +
    + + +
    + + + +
    +
    + Tip: + Type # to search pull requests +
    +
    + Type ? for help and tips +
    +
    +
    + +
    +
    + Tip: + Type # to search issues +
    +
    + Type ? for help and tips +
    +
    +
    + +
    +
    + Tip: + Type # to search discussions +
    +
    + Type ? for help and tips +
    +
    +
    + +
    +
    + Tip: + Type ! to search projects +
    +
    + Type ? for help and tips +
    +
    +
    + +
    +
    + Tip: + Type @ to search teams +
    +
    + Type ? for help and tips +
    +
    +
    + +
    +
    + Tip: + Type @ to search people and organizations +
    +
    + Type ? for help and tips +
    +
    +
    + +
    +
    + Tip: + Type > to activate command mode +
    +
    + Type ? for help and tips +
    +
    +
    + +
    +
    + Tip: + Go to your accessibility settings to change your keyboard shortcuts +
    +
    + Type ? for help and tips +
    +
    +
    + +
    +
    + Tip: + Type author:@me to search your content +
    +
    + Type ? for help and tips +
    +
    +
    + +
    +
    + Tip: + Type is:pr to filter to pull requests +
    +
    + Type ? for help and tips +
    +
    +
    + +
    +
    + Tip: + Type is:issue to filter to issues +
    +
    + Type ? for help and tips +
    +
    +
    + +
    +
    + Tip: + Type is:project to filter to projects +
    +
    + Type ? for help and tips +
    +
    +
    + +
    +
    + Tip: + Type is:open to filter to open content +
    +
    + Type ? for help and tips +
    +
    +
    + +
    + +
    +
    + We’ve encountered an error and some results aren't available at this time. Type a new search or try again later. +
    +
    + + No results matched your search + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    + + + + + + + Search for issues and pull requests + + # + + + + Search for issues, pull requests, discussions, and projects + + # + + + + Search for organizations, repositories, and users + + @ + + + + Search for projects + + ! + + + + Search for files + + / + + + + Activate command mode + + > + + + + Search your issues, pull requests, and discussions + + # author:@me + + + + Search your issues, pull requests, and discussions + + # author:@me + + + + Filter to pull requests + + # is:pr + + + + Filter to issues + + # is:issue + + + + Filter to discussions + + # is:discussion + + + + Filter to projects + + # is:project + + + + Filter to open issues, pull requests, and discussions + + # is:open + + + + + + + + + + + + + + + + +
    +
    +
    + +
    + + + + + + + + + + +
    + + + + +
    +
    +
    + + + + + + + + + +
    + +
    + +
    +

    + + + / + + basic-computer-games + + + Public +

    + +
    + +
      + + + +
    • + +
      + + + + + + + Watch + + + 126 + + + + +
      +
      +

      Notifications

      + +
      + +
      +
      + + + + + + + + +
      + + +
      + + + + + Get push notifications on iOS or Android. + +
      +
      +
      +
      + + + + +
      +
      +
      + + + +
    • + +
    • +
      +
      + Fork + 1k + + + +
      + +

      Fork basic-computer-games

      +
      + +
      + + + + + +

      If this dialog fails to load, you can visit the fork page directly.

      +
      +
      + +
      +
      +
      + +
    • + +
    • + + +
      +
      + + +
      +
      + +
      +
      + + + + +
      + +
      +
      + + + + + + + +
      + +
      +
      +
      +
      +
      +
    • + + + +
    + +
    + +
    +
    + + + + +
    + + + +
    + Open in github.dev + Open in a new github.dev tab + + + + + + +
    + + +
    + + + + + + +Permalink + +
    + +
    +
    + + + main + + + + +
    +
    +
    + Switch branches/tags + +
    + + + +
    + +
    + +
    + + +
    + +
    + + + + + + + + + + + + + + + + +
    + + +
    +
    +
    +
    + +
    + +
    + + + Go to file + + +
    + + + + + +
    +
    +
    + + + + + + + + + +
    + +
    +
    + + @Jay-0331 + +
    + + + + + + +
    +
    + + Latest commit + 34baeec + Apr 1, 2022 + + + + + + History + + +
    +
    + +
    + +
    +
    + + + 1 + + contributor + + +
    + +

    + Users who have contributed to this file +

    +
    + + + + + + +
    +
    +
    +
    + + + + + + + + + + + + +
    + +
    + + +
    + + 85 lines (78 sloc) + + 2.63 KB +
    + +
    + + + + +
    + + + + + + + + + + + + + + + +
    + +
    +
    + +
    +
    + +
    + +
    +
    + + + +
    + + +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    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");
    }
    }
    +
    + + + +
    + +
    + + + + +
    + + +
    + + +
    +
    + + +
    + +
    + + +
    + +
    +
    + +
    + + + + + + + + + + + + + + + + + + + + + + diff --git a/82_Stars/rust_JWB/Cargo.toml b/82_Stars/rust_JWB/Cargo.toml new file mode 100644 index 00000000..b35f5d94 --- /dev/null +++ b/82_Stars/rust_JWB/Cargo.toml @@ -0,0 +1,17 @@ +[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 diff --git a/82_Stars/rust_JWB/README.md b/82_Stars/rust_JWB/README.md new file mode 100644 index 00000000..06139f03 --- /dev/null +++ b/82_Stars/rust_JWB/README.md @@ -0,0 +1,29 @@ +<<<<<<< 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 diff --git a/82_Stars/rust_JWB/src/main.rs b/82_Stars/rust_JWB/src/main.rs new file mode 100644 index 00000000..162b8b4f --- /dev/null +++ b/82_Stars/rust_JWB/src/main.rs @@ -0,0 +1,236 @@ +<<<<<<< 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 diff --git a/83_Stock_Market/python/Stock_Market.py b/83_Stock_Market/python/Stock_Market.py index dbc9d949..e32572ca 100644 --- a/83_Stock_Market/python/Stock_Market.py +++ b/83_Stock_Market/python/Stock_Market.py @@ -1,10 +1,9 @@ import random +from typing import Any, Dict, List -# Stock_Market class Stock_Market: - def __init__(self): - + def __init__(self) -> None: # Hard Coded Names short_names = ["IBM", "RCA", "LBJ", "ABC", "CBS"] full_names = [ @@ -16,7 +15,7 @@ class Stock_Market: ] # Initializing Dictionary to hold all the information systematically - self.data = {} + 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} @@ -31,20 +30,17 @@ class Stock_Market: self.cash_assets = 10000 self.stock_assets = 0 - def total_assets(self): - + def total_assets(self) -> float: return self.cash_assets + self.stock_assets - def _generate_day_change(self): - + def _generate_day_change(self) -> None: self.changes = [] for _ in range(len(self.data)): self.changes.append( round(random.uniform(-5, 5), 2) ) # Random % Change b/w -5 and 5 - def update_prices(self): - + 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) @@ -57,9 +53,8 @@ class Stock_Market: print(f"\nNEW YORK STOCK EXCHANGE AVERAGE: ${sum / 5:.2f}") - def get_average_change(self): - - sum = 0 + def get_average_change(self) -> float: + sum: float = 0 for change in self.changes: sum += change @@ -77,8 +72,7 @@ class Stock_Market: self.print_exchange_average() self.print_assets() - def take_inputs(self): - + def take_inputs(self) -> List[str]: print("\nWHAT IS YOUR TRANSACTION IN") flag = False while not flag: @@ -92,7 +86,7 @@ class Stock_Market: if len(new_holdings) == 5: flag = self._check_transaction(new_holdings) - return new_holdings + return new_holdings # type: ignore def print_trading_day(self) -> None: @@ -107,8 +101,7 @@ class Stock_Market: ) ) - def update_cash_assets(self, new_holdings): - + def update_cash_assets(self, new_holdings) -> None: sell = 0 buy = 0 for stock, holding in zip(self.data.values(), new_holdings): @@ -120,8 +113,7 @@ class Stock_Market: self.cash_assets = self.cash_assets + sell - buy - def update_stock_assets(self): - + def update_stock_assets(self) -> None: sum = 0 for data in self.data.values(): sum += data["Price"] * data["Holdings"] @@ -129,13 +121,11 @@ class Stock_Market: 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): - + def _check_transaction(self, new_holdings) -> bool: sum = 0 for stock, holding in zip(self.data.values(), new_holdings): if holding > 0: @@ -156,8 +146,7 @@ class Stock_Market: return True - def update_holdings(self, new_holdings): - + def update_holdings(self, new_holdings) -> None: for stock, new_holding in zip(self.data.values(), new_holdings): stock["Holdings"] += new_holding @@ -186,13 +175,12 @@ HAVE $10,000 TO INVEST. USE INTEGERS FOR ALL YOUR INPUTS. ) -if __name__ == "__main__": - +def main() -> None: print("\t\t STOCK MARKET") help = input("\nDO YOU WANT INSTRUCTIONS(YES OR NO)? ") # Printing Instruction - if help == "YES" or help == "yes" or help == "Yes": + if help.lower() == "yes": print_instruction() # Initialize Game @@ -225,4 +213,8 @@ if __name__ == "__main__": print("\n------------END OF TRADING DAY--------------\n") print("\nHOPE YOU HAD FUN!!!!") - input("") + input() + + +if __name__ == "__main__": + main() diff --git a/84_Super_Star_Trek/javascript/cli.mjs b/84_Super_Star_Trek/javascript/cli.mjs index bacceff8..4277c232 100644 --- a/84_Super_Star_Trek/javascript/cli.mjs +++ b/84_Super_Star_Trek/javascript/cli.mjs @@ -2,12 +2,9 @@ import { onExit, onPrint, onInput, - setGameOptions, - getGameState, gameMain, } from "./superstartrek.mjs"; -import util from "util"; import readline from "readline"; onExit(function exit() { diff --git a/84_Super_Star_Trek/python/superstartrek.py b/84_Super_Star_Trek/python/superstartrek.py index 5f4f030d..40ccb9f6 100644 --- a/84_Super_Star_Trek/python/superstartrek.py +++ b/84_Super_Star_Trek/python/superstartrek.py @@ -1,771 +1,1118 @@ -# **** **** 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 . . . -# -# Python translation by Jack Boyce - February 2021 -# Output is identical to BASIC version except for a few -# fixes (as noted, search `bug`) and minor cleanup. +""" +**** **** 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 . . . + +Python translation by Jack Boyce - February 2021 + Output is identical to BASIC version except for a few + fixes (as noted, search `bug`) and minor cleanup. +""" import random +import sys +from dataclasses import dataclass +from enum import Enum from math import sqrt -from typing import Any, Callable, Dict, List, Tuple +from typing import Callable, Dict, Final, List, Optional, Tuple -# Global variables -restart = False -s = 0 -e = 0 -d: List[int] = [] -k: List[List[float]] = [[0, 0, 0], [0, 0, 0], [0, 0, 0]] # Klingons in current quadrant -devices = [ - "WARP ENGINES", - "SHORT RANGE SENSORS", - "LONG RANGE SENSORS", - "PHASER CONTROL", - "PHOTON TUBES", - "DAMAGE CONTROL", - "SHIELD CONTROL", - "LIBRARY-COMPUTER", -] -c = [ - [0, 1], - [-1, 1], - [-1, 0], - [-1, -1], - [0, -1], - [1, -1], - [1, 0], - [1, 1], - [0, 1], + +def get_user_float(prompt: str) -> float: + """Get input from user and return it.""" + while True: + answer = input(prompt) + try: + answer_float = float(answer) + return answer_float + 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 += " " + 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" + ) + self.world = World() + 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 = 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): + if ship.damage_stats[i] < 0: + 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: + com = int(command) + 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 = " " + 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 -q1 = s1 = 0 -q2 = s2 = 0 -k3 = b3 = s3 = 0 # Klingons, bases, stars in quad. - -b4 = b5 = 0 -qs = " " * 192 # quadrant string -# set up global game variables -g = [[0] * 8 for _ in range(8)] # galaxy map -z = [[0] * 8 for _ in range(8)] # charted galaxy map -d = [0] * 8 # damage stats for devices -t = t0 = 100 * random.randint(20, 39) # stardate (current, initial) -t9 = random.randint(25, 34) # mission duration (stardates) -docked = False # true when docked at starbase -e = e0 = 3000 # energy (current, initial) -p = p0 = 10 # torpedoes (current, initial) -s = 0 # shields -k9, b9 = 0, 0 # total Klingons, bases in galaxy -# ^ bug in original, was b9 = 2 -s9 = 200 # avg. Klingon shield strength - -k7 = k9 # Klingons at start of game -d4 = 0.5 * random.random() # extra delay in repairs at base - -# ------------------------------------------------------------------------- -# Utility functions -# ------------------------------------------------------------------------- -def fnr(): - # Generate a random integer from 0 to 7 inclusive. +def fnr() -> int: + """Generate a random integer from 0 to 7 inclusive.""" return random.randint(0, 7) -def quadrant_name(row, col, region_only=False): - # 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 += " " + modifier[col % 4] - - return quadrant - - -def insert_marker(row, col, marker): - # Insert a marker into a given position in the quadrant string `qs`. The - # contents of a quadrant (Enterprise, stars, etc.) are stored in `qs`. - global qs - - if len(marker) != 3: - print("ERROR") - exit() - - pos = round(col) * 3 + round(row) * 24 - qs = qs[0:pos] + marker + qs[(pos + 3) : 192] - - -def compare_marker(row, col, test_marker): - # Check whether the position in the current quadrant is occupied with a - # given marker. - pos = round(col) * 3 + round(row) * 24 - return qs[pos : (pos + 3)] == test_marker - - -def find_empty_place() -> Tuple[int, int]: - # Find an empty location in the current quadrant. - while True: - row, col = fnr(), fnr() - if compare_marker(row, col, " "): - return row, col - - -# ------------------------------------------------------------------------- -# Functions for individual player commands -# ------------------------------------------------------------------------- - - -def navigation() -> None: - # Take navigation input and move the Enterprise. - global d, s, e, k, qs, t, q1, q2, s1, s2 - - while True: - c1s = input("COURSE (1-9)? ") - if len(c1s) > 0: - c1 = float(c1s) - break - if c1 == 9: - c1 = 1 - if c1 < 1 or c1 >= 9: - print(" LT. SULU REPORTS, 'INCORRECT COURSE DATA, SIR!'") - return - - while True: - warps = input(f"WARP FACTOR (0-{'0.2' if d[0] < 0 else '8'})? ") - if len(warps) > 0: - warp = float(warps) - break - if d[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 - - n = round(warp * 8) - if e < n: - print("ENGINEERING REPORTS 'INSUFFICIENT ENERGY AVAILABLE") - print(f" FOR MANEUVERING AT WARP {warp}!'") - if s >= n - e and d[6] >= 0: - print(f"DEFLECTOR CONTROL ROOM ACKNOWLEDGES {s} UNITS OF ENERGY") - print(" PRESENTLY DEPLOYED TO SHIELDS.") - return - - # klingons move and fire - for i in range(3): - if k[i][2] != 0: - insert_marker(k[i][0], k[i][1], " ") - k[i][0], k[i][1] = find_empty_place() - insert_marker(k[i][0], k[i][1], "+K+") - - klingons_fire() - - # repair damaged devices and print damage report - line = "" - for i in range(8): - if d[i] < 0: - d[i] += min(warp, 1) # type: ignore - if -0.1 < d[i] < 0: - d[i] = -0.1 # type: ignore - elif d[i] >= 0: - if len(line) == 0: - line = "DAMAGE CONTROL REPORT:" - line += " " + devices[i] + " REPAIR COMPLETED\n" - if len(line) > 0: - print(line) - if random.random() <= 0.2: - r1 = fnr() - if random.random() < 0.6: - d[r1] -= random.random() * 5 + 1 - print(f"DAMAGE CONTROL REPORT: {devices[r1]} DAMAGED\n") - else: - d[r1] += random.random() * 3 + 1 - print(f"DAMAGE CONTROL REPORT: {devices[r1]} STATE OF REPAIR IMPROVED\n") - - # begin moving starship - insert_marker(int(s1), int(s2), " ") - ic1 = int(c1) - x1 = c[ic1 - 1][0] + (c[ic1][0] - c[ic1 - 1][0]) * (c1 - ic1) - x2 = c[ic1 - 1][1] + (c[ic1][1] - c[ic1 - 1][1]) * (c1 - ic1) - q1_start, q2_start = q1, q2 - x, y = s1, s2 - - for _ in range(n): - s1 += x1 # type: ignore - s2 += x2 # type: ignore - - if s1 < 0 or s1 > 7 or s2 < 0 or s2 > 7: - # exceeded quadrant limits; calculate final position - x += 8 * q1 + n * x1 # type: ignore - y += 8 * q2 + n * x2 # type: ignore - q1, q2 = int(x / 8), int(y / 8) - s1, s2 = int(x - q1 * 8), int(y - q2 * 8) - if s1 < 0: - q1 -= 1 - s1 = 7 - if s2 < 0: - q2 -= 1 - s2 = 7 - - hit_edge = False - if q1 < 0: - hit_edge = True - q1 = s1 = 0 - if q1 > 7: - hit_edge = True - q1 = s1 = 7 - if q2 < 0: - hit_edge = True - q2 = s2 = 0 - if q2 > 7: - hit_edge = True - q2 = s2 = 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 {s1 + 1} , {s2 + 1} OF QUADRANT " - f"{q1 + 1} , {q2 + 1}.'" - ) - if t > t0 + t9: - end_game(won=False, quit=False) - return - - if q1 == q1_start and q2 == q2_start: - break - t += 1 - maneuver_energy(n) - new_quadrant() - return - else: - pos = int(s1) * 24 + int(s2) * 3 - if qs[pos : (pos + 2)] != " ": - s1, s2 = int(s1 - x1), int(s2 - x2) - print( - "WARP ENGINES SHUT DOWN AT SECTOR " - f"{s1 + 1} , {s2 + 1} DUE TO BAD NAVAGATION" - ) - break - else: - s1, s2 = int(s1), int(s2) - - insert_marker(int(s1), int(s2), "<*>") - maneuver_energy(n) - - t += 0.1 * int(10 * warp) if warp < 1 else 1 # type: ignore - if t > t0 + t9: - end_game(won=False, quit=False) - return - - short_range_scan() - - -def maneuver_energy(n): - # Deduct the energy for navigation from energy/shields. - global e, s - - e -= n + 10 - - if e <= 0: - print("SHIELD CONTROL SUPPLIES ENERGY TO COMPLETE THE MANEUVER.") - s += e - e = 0 - s = max(0, s) - - -def short_range_scan() -> None: - # Print a short range scan. - global docked, e, p, s - - docked = False - for i in (s1 - 1, s1, s1 + 1): - for j in (s2 - 1, s2, s2 + 1): - if 0 <= i <= 7 and 0 <= j <= 7 and compare_marker(i, j, ">!<"): - docked = True - cs = "DOCKED" - e, p = e0, p0 - print("SHIELDS DROPPED FOR DOCKING PURPOSES") - s = 0 - break - else: - continue - break - else: - if k3 > 0: - cs = "*RED*" - elif e < e0 * 0.1: - cs = "YELLOW" - else: - cs = "GREEN" - - if d[1] < 0: - print("\n*** SHORT RANGE SENSORS ARE OUT ***\n") - return - - sep = "---------------------------------" - print(sep) - for i in range(8): - line = "" - for j in range(8): - pos = i * 24 + j * 3 - line = line + " " + qs[pos : (pos + 3)] - - if i == 0: - line += f" STARDATE {round(int(t * 10) * 0.1, 1)}" - elif i == 1: - line += f" CONDITION {cs}" - elif i == 2: - line += f" QUADRANT {q1 + 1} , {q2 + 1}" - elif i == 3: - line += f" SECTOR {s1 + 1} , {s2 + 1}" - elif i == 4: - line += f" PHOTON TORPEDOES {int(p)}" - elif i == 5: - line += f" TOTAL ENERGY {int(e + s)}" - elif i == 6: - line += f" SHIELDS {int(s)}" - else: - line += f" KLINGONS REMAINING {k9}" - - print(line) - print(sep) - - -def long_range_scan() -> None: - # Print a long range scan. - global z, g - - if d[2] < 0: - print("LONG RANGE SENSORS ARE INOPERABLE") - return - - print(f"LONG RANGE SCAN FOR QUADRANT {q1 + 1} , {q2 + 1}") - print_scan_results(q1, q2, g, z) - - def print_scan_results( - q1: int, q2: int, g: List[List[Any]], z: List[List[Any]] + quadrant: Point, + galaxy_map: List[List[QuadrantData]], + charted_galaxy_map: List[List[QuadrantData]], ) -> None: sep = "-------------------" print(sep) - for i in (q1 - 1, q1, q1 + 1): - n = [-1, -2, -3] + for x in (quadrant.x - 1, quadrant.x, quadrant.x + 1): + n: List[Optional[int]] = [None, None, None] - for j in (q2 - 1, q2, q2 + 1): - if 0 <= i <= 7 and 0 <= j <= 7: - n[j - q2 + 1] = g[i][j] - z[i][j] = g[i][j] + # 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_index in range(3): - if n[line_index] < 0: + for line_col in n: + if line_col is None: line += "*** : " else: - line += str(n[line_index] + 1000).rjust(4, " ")[-3:] + " : " + line += str(line_col + 1000).rjust(4, " ")[-3:] + " : " print(line) print(sep) -def phaser_control() -> None: - # Take phaser control input and fire phasers. - global e, k, g, z, k3, k9 - - if d[3] < 0: - print("PHASERS INOPERATIVE") - return - - if k3 <= 0: - print("SCIENCE OFFICER SPOCK REPORTS 'SENSORS SHOW NO ENEMY SHIPS") - print(" IN THIS QUADRANT'") - return - - if d[7] < 0: - print("COMPUTER FAILURE HAMPERS ACCURACY") - - print(f"PHASERS LOCKED ON TARGET; ENERGY AVAILABLE = {e} UNITS") - x = 0 - while True: - while True: - units_to_fire = input("NUMBER OF UNITS TO FIRE? ") - if len(units_to_fire) > 0: - x = int(units_to_fire) - break - if x <= 0: - return - if e >= x: - break - print(f"ENERGY AVAILABLE = {e} UNITS") - - e -= x - if d[7] < 0: # bug in original, was d[6] - x *= random.random() # type: ignore - - h1 = int(x / k3) - for i in range(3): - if k[i][2] <= 0: - continue - - h = int((h1 / fnd(i)) * (random.random() + 2)) - if h <= 0.15 * k[i][2]: - print(f"SENSORS SHOW NO DAMAGE TO ENEMY AT {k[i][0] + 1} , {k[i][1] + 1}") - else: - k[i][2] -= h - print(f" {h} UNIT HIT ON KLINGON AT SECTOR {k[i][0] + 1} , {k[i][1] + 1}") - if k[i][2] <= 0: - print("*** KLINGON DESTROYED ***") - k3 -= 1 - k9 -= 1 - insert_marker(k[i][0], k[i][1], " ") - k[i][2] = 0 - g[q1][q2] -= 100 - z[q1][q2] = g[q1][q2] - if k9 <= 0: - end_game(won=True, quit=False) - return - else: - print(f" (SENSORS SHOW {round(k[i][2],6)} UNITS REMAINING)") - - klingons_fire() - - -def photon_torpedoes() -> None: - # Take photon torpedo input and process firing of torpedoes. - global e, p, k3, k9, k, b3, b9, docked, g, z - - if p <= 0: - print("ALL PHOTON TORPEDOES EXPENDED") - return - if d[4] < 0: - print("PHOTON TUBES ARE NOT OPERATIONAL") - return - - while True: - torpedo_course = input("PHOTON TORPEDO COURSE (1-9)? ") - if len(torpedo_course) > 0: - c1 = float(torpedo_course) - break - if c1 == 9: - c1 = 1 - if c1 < 1 or c1 >= 9: - print("ENSIGN CHEKOV REPORTS, 'INCORRECT COURSE DATA, SIR!'") - return - - ic1 = int(c1) - x1 = c[ic1 - 1][0] + (c[ic1][0] - c[ic1 - 1][0]) * (c1 - ic1) - e -= 2 - p -= 1 - x2 = c[ic1 - 1][1] + (c[ic1][1] - c[ic1 - 1][1]) * (c1 - ic1) - x, y = s1, s2 - x3, y3 = x, y - print("TORPEDO TRACK:") - while True: - x += x1 # type: ignore - y += x2 # type: ignore - x3, y3 = round(x), round(y) - if x3 < 0 or x3 > 7 or y3 < 0 or y3 > 7: - print("TORPEDO MISSED") - klingons_fire() - return - print(f" {x3 + 1} , {y3 + 1}") - if not compare_marker(x3, y3, " "): - break - - if compare_marker(x3, y3, "+K+"): - print("*** KLINGON DESTROYED ***") - k3 -= 1 - k9 -= 1 - if k9 <= 0: - end_game(won=True, quit=False) - return - for i in range(3): - if x3 == k[i][0] and y3 == k[i][1]: - k[i][2] = 0 - elif compare_marker(x3, y3, " * "): - print(f"STAR AT {x3 + 1} , {y3 + 1} ABSORBED TORPEDO ENERGY.") - klingons_fire() - return - elif compare_marker(x3, y3, ">!<"): - print("*** STARBASE DESTROYED ***") - b3 -= 1 - b9 -= 1 - if b9 == 0 and k9 <= t - t0 - t9: - print("THAT DOES IT, CAPTAIN!! YOU ARE HEREBY RELIEVED OF COMMAND") - print("AND SENTENCED TO 99 STARDATES AT HARD LABOR ON CYGNUS 12!!") - end_game(won=False) - return - else: - print("STARFLEET COMMAND REVIEWING YOUR RECORD TO CONSIDER") - print("COURT MARTIAL!") - docked = False - - insert_marker(x3, y3, " ") - g[q1][q2] = k3 * 100 + b3 * 10 + s3 - z[q1][q2] = g[q1][q2] - klingons_fire() - - -def fnd(i): - # Find distance between Enterprise and i'th Klingon warship. - return sqrt((k[i][0] - s1) ** 2 + (k[i][1] - s2) ** 2) - - -def klingons_fire(): - # Process nearby Klingons firing on Enterprise. - global s, k, d - - if k3 <= 0: - return - if docked: - print("STARBASE SHIELDS PROTECT THE ENTERPRISE") - return - - for i in range(3): - if k[i][2] <= 0: - continue - - h = int((k[i][2] / fnd(i)) * (random.random() + 2)) - s -= h - k[i][2] /= random.random() + 3 - print(f" {h} UNIT HIT ON ENTERPRISE FROM SECTOR {k[i][0] + 1} , {k[i][1] + 1}") - if s <= 0: - end_game(won=False, quit=False, enterprise_killed=True) - return - print(f" ") - if h >= 20 and random.random() < 0.60 and h / s > 0.02: - r1 = fnr() - d[r1] -= h / s + 0.5 * random.random() - print(f"DAMAGE CONTROL REPORTS '{devices[r1]} DAMAGED BY THE HIT'") - - -def shield_control() -> None: - # Raise or lower the shields. - global e, s - - if d[6] < 0: - print("SHIELD CONTROL INOPERABLE") - return - - while True: - energy_to_shield = input( - f"ENERGY AVAILABLE = {e + s} NUMBER OF UNITS TO SHIELDS? " - ) - if len(energy_to_shield) > 0: - x = int(energy_to_shield) - break - - if x < 0 or s == x: - print("") - return - - if x > e + s: - print( - "SHIELD CONTROL REPORTS 'THIS IS NOT THE FEDERATION " - "TREASURY.'\n" - "" - ) - return - - e += s - x - s = x - print("DEFLECTOR CONTROL ROOM REPORT:") - print(f" 'SHIELDS NOW AT {s} UNITS PER YOUR COMMAND.'") - - -def damage_control(): - # Print a damage control report. - global d, t - - if d[5] < 0: - print("DAMAGE CONTROL REPORT NOT AVAILABLE") - else: - print("\nDEVICE STATE OF REPAIR") - for r1 in range(8): - print(f"{devices[r1].ljust(26, ' ')}{int(d[r1] * 100) * 0.01:g}") - print() - - if not docked: - return - - d3 = sum(0.1 for i in range(8) if d[i] < 0) - if d3 == 0: - return - - d3 += d4 - if d3 >= 1: - d3 = 0.9 - print("\nTECHNICIANS STANDING BY TO EFFECT REPAIRS TO YOUR SHIP;") - print(f"ESTIMATED TIME TO REPAIR: {round(0.01 * int(100 * d3), 2)} STARDATES") - if input("WILL YOU AUTHORIZE THE REPAIR ORDER (Y/N)? ").upper().strip() != "Y": - return - - for i in range(8): - if d[i] < 0: - d[i] = 0 - t += d3 + 0.1 - - -def computer() -> None: - # Perform the various functions of the library computer. - global d, z, k9, t0, t9, t, b9, s1, s2, b4, b5 - - if d[7] < 0: - print("COMPUTER DISABLED") - return - - while True: - command = input("COMPUTER ACTIVE AND AWAITING COMMAND? ") - if len(command) == 0: - com = 6 - else: - com = int(command) - if com < 0: - return - - print() - - if com == 0 or com == 5: - if com == 5: - print(" THE GALAXY") - else: - print( - "\n COMPUTER RECORD OF GALAXY FOR " - f"QUADRANT {q1 + 1} , {q2 + 1}\n" - ) - - print(" 1 2 3 4 5 6 7 8") - sep = " ----- ----- ----- ----- ----- ----- ----- -----" - print(sep) - - for i in range(8): - line = " " + str(i + 1) + " " - - if com == 5: - g2s = quadrant_name(i, 0, True) - line += (" " * int(12 - 0.5 * len(g2s))) + g2s - g2s = quadrant_name(i, 4, True) - line += (" " * int(39 - 0.5 * len(g2s) - len(line))) + g2s - else: - for j in range(8): - line += " " - if z[i][j] == 0: - line += "***" - else: - line += str(z[i][j] + 1000)[-3:] - - print(line) - print(sep) - - print() - return - elif com == 1: - print(" STATUS REPORT:") - print(f"KLINGON{'S' if k9 > 1 else ''} LEFT: {k9}") - print( - "MISSION MUST BE COMPLETED IN " - f"{round(0.1 * int((t0+t9-t) * 10), 1)} STARDATES" - ) - - if b9 == 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 {b9} " - f"STARBASE{'S' if b9 > 1 else ''} IN THE GALAXY" - ) - - damage_control() - return - elif com == 2: - if k3 <= 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 k3 > 1 else ''}") - - for i in range(3): - if k[i][2] > 0: - print_direction(s1, s2, k[i][0], k[i][1]) - return - elif com == 3: - if b3 == 0: - print( - "MR. SPOCK REPORTS, 'SENSORS SHOW NO STARBASES IN THIS " - "QUADRANT.'" - ) - return - - print("FROM ENTERPRISE TO STARBASE:") - print_direction(s1, s2, b4, b5) - return - elif com == 4: - print("DIRECTION/DISTANCE CALCULATOR:") - print(f"YOU ARE AT QUADRANT {q1+1} , {q2+1} SECTOR {s1+1} , {s2+1}") - 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(from1, from2, to1, to2) - return - 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 print_direction(from1, from2, to1, to2) -> None: - # Print direction and distance between two locations in the grid. - delta1 = -(to1 - from1) # flip so positive is up (heading = 3) - delta2 = to2 - from2 +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: @@ -791,212 +1138,32 @@ def print_direction(from1, from2, to1, to2) -> None: print(f"DISTANCE = {round(sqrt(delta1 ** 2 + delta2 ** 2), 6)}") -# ------------------------------------------------------------------------- -# Game transitions -# ------------------------------------------------------------------------- - - -def startup() -> None: - # Initialize the game variables and map, and print startup messages. - global g, z, d, t, t0, t9, docked, e, e0, p, p0, s, k9, b9, s9, c - global devices, q1, q2, s1, s2, k7 - - 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" - ) - - # set up global game variables - g = [[0] * 8 for _ in range(8)] # galaxy map - z = [[0] * 8 for _ in range(8)] # charted galaxy map - d = [0] * 8 # damage stats for devices - t = t0 = 100 * random.randint(20, 39) # stardate (current, initial) - t9 = random.randint(25, 34) # mission duration (stardates) - docked = False # true when docked at starbase - e = e0 = 3000 # energy (current, initial) - p = p0 = 10 # torpedoes (current, initial) - s = 0 # shields - k9, b9 = 0, 0 # total Klingons, bases in galaxy - # ^ bug in original, was b9 = 2 - s9 = 200 # avg. Klingon shield strength - - c = [ - [0, 1], - [-1, 1], - [-1, 0], - [-1, -1], - [0, -1], - [1, -1], - [1, 0], - [1, 1], - [0, 1], - ] # vectors in cardinal directions - - devices = [ - "WARP ENGINES", - "SHORT RANGE SENSORS", - "LONG RANGE SENSORS", - "PHASER CONTROL", - "PHOTON TUBES", - "DAMAGE CONTROL", - "SHIELD CONTROL", - "LIBRARY-COMPUTER", - ] - - # initialize Enterprise's position - q1, q2 = fnr(), fnr() # Enterprise's quadrant - s1, s2 = fnr(), fnr() # ...and sector - - # initialize contents of galaxy - for i in range(8): - for j in range(8): - k3 = 0 - r1 = random.random() - - if r1 > 0.98: - k3 = 3 - elif r1 > 0.95: - k3 = 2 - elif r1 > 0.80: - k3 = 1 - k9 += k3 - - b3 = 0 - if random.random() > 0.96: - b3 = 1 - b9 += 1 - g[i][j] = k3 * 100 + b3 * 10 + fnr() + 1 - - if k9 > t9: - t9 = k9 + 1 - - if b9 == 0: # original has buggy extra code here - b9 = 1 - g[q1][q2] += 10 - q1, q2 = fnr(), fnr() - - k7 = k9 # Klingons at start of game - - print( - "YOUR ORDERS ARE AS FOLLOWS:\n" - f" DESTROY THE {k9} KLINGON WARSHIPS WHICH HAVE INVADED\n" - " THE GALAXY BEFORE THEY CAN ATTACK FEDERATION HEADQUARTERS\n" - f" ON STARDATE {t0+t9}. THIS GIVES YOU {t9} DAYS. THERE " - f"{'IS' if b9 == 1 else 'ARE'}\n" - f" {b9} STARBASE{'' if b9 == 1 else 'S'} IN THE GALAXY FOR " - "RESUPPLYING YOUR SHIP.\n" - ) - - -def new_quadrant() -> None: - # Enter a new quadrant: populate map and print a short range scan. - global z, k3, b3, s3, d4, k, qs, b4, b5 - - k3 = b3 = s3 = 0 # Klingons, bases, stars in quad. - d4 = 0.5 * random.random() # extra delay in repairs at base - z[q1][q2] = g[q1][q2] - - if 0 <= q1 <= 7 and 0 <= q2 <= 7: - quad = quadrant_name(q1, q2, False) - if t == t0: - print("\nYOUR MISSION BEGINS WITH YOUR STARSHIP LOCATED") - print(f"IN THE GALACTIC QUADRANT, '{quad}'.\n") - else: - print(f"\nNOW ENTERING {quad} QUADRANT . . .\n") - - k3 = g[q1][q2] // 100 - b3 = g[q1][q2] // 10 - 10 * k3 - s3 = g[q1][q2] - 100 * k3 - 10 * b3 - - if k3 != 0: - print("COMBAT AREA CONDITION RED") - if s <= 200: - print(" SHIELDS DANGEROUSLY LOW") - - k = [[0, 0, 0], [0, 0, 0], [0, 0, 0]] # Klingons in current quadrant - qs = " " * 192 # quadrant string - - # build quadrant string - insert_marker(s1, s2, "<*>") - for i in range(k3): - r1, r2 = find_empty_place() - insert_marker(r1, r2, "+K+") - k[i] = [r1, r2, s9 * (0.5 + random.random())] - if b3 > 0: - b4, b5 = find_empty_place() # position of starbase (sector) - insert_marker(b4, b5, ">!<") - for _ in range(s3): - r1, r2 = find_empty_place() - insert_marker(r1, r2, " * ") - - short_range_scan() - - -def end_game( - won: bool = False, quit: bool = True, enterprise_killed: bool = False -) -> None: - # Handle end-of-game situations. - global restart - - 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 * (k7 / (t - t0))**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(t, 1)}") - - print(f"THERE WERE {k9} KLINGON BATTLE CRUISERS LEFT AT") - print("THE END OF YOUR MISSION.\n\n") - - if b9 == 0: - 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": - exit() - restart = True - - -# ------------------------------------------------------------------------- -# Entry point and main game loop -# ------------------------------------------------------------------------- - - def main() -> None: - global restart + game = Game() + world = game.world + ship = world.ship f: Dict[str, Callable[[], None]] = { - "NAV": navigation, - "SRS": short_range_scan, - "LRS": long_range_scan, - "PHA": phaser_control, - "TOR": photon_torpedoes, - "SHE": shield_control, - "DAM": damage_control, - "COM": computer, - "XXX": end_game, + "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: - startup() - new_quadrant() + game.startup() + game.new_quadrant() restart = False while not restart: - if s + e <= 10 or (e <= 10 and d[6] != 0): + 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, " diff --git a/84_Super_Star_Trek/python/superstartrekins.py b/84_Super_Star_Trek/python/superstartrekins.py index 333bb2e4..59a8a61b 100644 --- a/84_Super_Star_Trek/python/superstartrekins.py +++ b/84_Super_Star_Trek/python/superstartrekins.py @@ -8,7 +8,7 @@ Ported by Dave LeCompte """ -def get_yes_no(prompt): +def get_yes_no(prompt: str) -> bool: response = input(prompt).upper() return response[0] != "N" diff --git a/85_Synonym/python/synonym.py b/85_Synonym/python/synonym.py index 6d1636e6..0680bb78 100644 --- a/85_Synonym/python/synonym.py +++ b/85_Synonym/python/synonym.py @@ -53,7 +53,7 @@ def print_right() -> None: print(random.choice(right_words)) -def ask_question(question_number): +def ask_question(question_number: int) -> None: words = synonym_words[question_number] clues = words[:] base_word = clues.pop(0) diff --git a/86_Target/python/target.py b/86_Target/python/target.py index f10f0a56..122c82d1 100644 --- a/86_Target/python/target.py +++ b/86_Target/python/target.py @@ -8,6 +8,7 @@ Ported by Dave LeCompte import math import random +from typing import List PAGE_WIDTH = 64 @@ -41,7 +42,7 @@ def print_instructions() -> None: print() -def prompt(): +def prompt() -> List[float]: while True: response = input("INPUT ANGLE DEVIATION FROM X, DEVIATION FROM Z, DISTANCE? ") if "," not in response: @@ -54,14 +55,14 @@ def prompt(): return [float(t) for t in terms] -def next_target(): +def next_target() -> None: for _ in range(5): print() print("NEXT TARGET...") print() -def describe_miss(x, y, z, x1, y1, z1, d): +def describe_miss(x, y, z, x1, y1, z1, d) -> None: x2 = x1 - x y2 = y1 - y z2 = z1 - z @@ -88,7 +89,7 @@ def describe_miss(x, y, z, x1, y1, z1, d): print() -def do_shot_loop(p1, x, y, z): +def do_shot_loop(p1, x, y, z) -> None: shot_count = 0 while True: shot_count += 1 @@ -137,11 +138,11 @@ def do_shot_loop(p1, x, y, z): describe_miss(x, y, z, x1, y1, z1, distance) -def show_radians(a, b): +def show_radians(a, b) -> None: print(f"RADIANS FROM X AXIS = {a:.4f} FROM Z AXIS = {b:.4f}") -def play_game(): +def play_game() -> None: while True: a = random.uniform(0, 2 * math.pi) # random angle b = random.uniform(0, 2 * math.pi) # random angle diff --git a/88_3-D_Tic-Tac-Toe/python/qubit.py b/88_3-D_Tic-Tac-Toe/python/qubit.py index 4081e70d..5f2f0e77 100644 --- a/88_3-D_Tic-Tac-Toe/python/qubit.py +++ b/88_3-D_Tic-Tac-Toe/python/qubit.py @@ -1,10 +1,11 @@ -#!/usr/bin/python3 +#!/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): @@ -31,8 +32,7 @@ class Player(Enum): class TicTacToe3D: """The game logic for 3D Tic Tac Toe and the machine opponent""" - def __init__(self): - + 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 @@ -120,7 +120,7 @@ class TicTacToe3D: [12, 25, 38, 51], ] - def get(self, x, y, z): + def get(self, x, y, z) -> Player: m = self.board[4 * (4 * z + y) + x] if m == 40: return Player.MACHINE @@ -129,11 +129,11 @@ class TicTacToe3D: else: return Player.EMPTY - def move3D(self, x, y, z, player): + 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): + def move(self, m, player) -> bool: if self.board[m] > 1: return False @@ -143,13 +143,13 @@ class TicTacToe3D: self.board[m] = 8 return True - def get3DPosition(self, m): + 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 evaluateLines(self): + def evaluate_lines(self) -> None: self.lineValues = [0] * 76 for j in range(76): value = 0 @@ -157,18 +157,18 @@ class TicTacToe3D: value += self.board[self.lines[j][k]] self.lineValues[j] = value - def strategyMarkLine(self, i): + 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 clearStrategyMarks(self): + def clear_strategy_marks(self) -> None: for i in range(64): if self.board[i] == 1: self.board[i] = 0 - def markAndMove(self, vlow, vhigh, vmove): + 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 @@ -180,37 +180,37 @@ class TicTacToe3D: self.lineValues[i] = value if vlow <= value < vhigh: if value > vlow: - return self.moveTriple(i) - self.strategyMarkLine(i) - self.evaluateLines() + return self.move_triple(i) + self.strategy_mark_line(i) + self.evaluate_lines() for i in range(76): value = self.lineValues[i] if value == 4 or value == vmove: - return self.moveDiagonals(i, 1) + return self.move_diagonals(i, 1) return None - def machineMove(self): + def machine_move(self) -> Union[None, Tuple[Move, int], Tuple[Move, int, int]]: """machine works out what move to play""" - self.clearStrategyMarks() + self.clear_strategy_marks() - self.evaluateLines() + self.evaluate_lines() for value, event in [ - (32, self.humanWin), - (120, self.machineWin), - (24, self.blockHumanWin), + (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.markAndMove(80, 88, 43) + m = self.mark_and_move(80, 88, 43) if m is not None: return m - self.clearStrategyMarks() + self.clear_strategy_marks() - m = self.markAndMove(16, 24, 11) + m = self.mark_and_move(16, 24, 11) if m is not None: return m @@ -222,11 +222,11 @@ class TicTacToe3D: if (32 <= value < 40) or (72 <= value < 80): for s in [1, 0]: for i in range(4 * k, 4 * k + 4): - m = self.moveDiagonals(i, s) + m = self.move_diagonals(i, s) if m is not None: return m - self.clearStrategyMarks() + self.clear_strategy_marks() for y in self.corners: if self.board[y] == 0: @@ -238,24 +238,24 @@ class TicTacToe3D: return (Move.DRAW, -1) - def humanWin(self, i): + def human_win(self, i) -> Tuple[Move, int, int]: return (Move.HUMAN_WIN, -1, i) - def machineWin(self, 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 blockHumanWin(self, i): + 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 moveTriple(self, i): + 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] @@ -267,7 +267,7 @@ class TicTacToe3D: return (Move.CONCEDES, -1) # choose move in corners or center boxes of square 4x4 - def moveDiagonals(self, i, s): + def move_diagonals(self, i, s) -> Optional[Tuple[Move, int]]: if 0 < (i % 4) < 3: jrange = [1, 2] else: @@ -280,15 +280,15 @@ class TicTacToe3D: class Qubit: - def moveCode(self, board, m): + def move_code(self, board, m) -> str: x, y, z = board.get3DPosition(m) return f"{z + 1:d}{y + 1:d}{x + 1:d}" - def showWin(self, board, i): + def show_win(self, board, i) -> None: for m in board.lines[i]: - print(self.moveCode(board, m)) + print(self.move_code(board, m)) - def showBoard(self, board): + def show_board(self, board) -> None: c = " YM" for z in range(4): for y in range(4): @@ -299,15 +299,15 @@ class Qubit: print("\n") print("\n") - def humanMove(self, board): - print("") + 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.showBoard(board) + 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]) @@ -322,7 +322,7 @@ class Qubit: return True - def play(self): + def play(self) -> None: print("Qubic\n") print("Create Computing Morristown, New Jersey\n\n\n") while True: @@ -356,7 +356,7 @@ class Qubit: break print("Incorrect answer. Please type 'yes' or 'no'.") - skipHuman = s[0] in "nN" + skip_human = s[0] in "nN" move_text = [ "Machine moves to", @@ -368,22 +368,23 @@ class Qubit: ] while True: - if not skipHuman and not self.humanMove(board): + if not skip_human and not self.human_move(board): break - skipHuman = False + skip_human = False - m = board.machineMove() + m = board.machine_move() + assert m is not None if m[0] == Move.HUMAN_WIN: print("You win as follows,") - self.showWin(board, m[2]) + self.show_win(board, m[2]) # type: ignore break elif m[0] == Move.MACHINE_WIN: print( "Machine moves to {}, and wins as follows".format( - self.moveCode(board, m[1]) + self.move_code(board, m[1]) ) ) - self.showWin(board, m[2]) + self.show_win(board, m[2]) # type: ignore break elif m[0] == Move.DRAW: print("The game is a draw.") @@ -393,10 +394,10 @@ class Qubit: break else: print(move_text[m[0].value - Move.MOVES.value]) - print(self.moveCode(board, m[1])) + print(self.move_code(board, m[1])) board.move(m[1], Player.MACHINE) - self.showBoard(board) + self.show_board(board) print(" ") while True: diff --git a/89_Tic-Tac-Toe/python/TicTacToe_Hard.py b/89_Tic-Tac-Toe/python/TicTacToe_Hard.py index 400ec7f1..b5708d80 100644 --- a/89_Tic-Tac-Toe/python/TicTacToe_Hard.py +++ b/89_Tic-Tac-Toe/python/TicTacToe_Hard.py @@ -1,15 +1,18 @@ +from typing import List, Tuple, Union + + class TicTacToe: - def __init__(self, pick, sz=3): + def __init__(self, pick, sz=3) -> None: self.pick = pick self.dim_sz = sz self.board = self.clear_board() - def clear_board(self): + def clear_board(self) -> List[List[str]]: board = [["blur" for i in range(self.dim_sz)] for j in range(self.dim_sz)] # made a 3x3 by-default board return board - def move_record(self, r, c): + 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": @@ -17,7 +20,7 @@ class TicTacToe: self.board[r][c] = self.pick return True - def check_win(self): # 1 you won, 0 computer won, -1 tie + 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 @@ -88,7 +91,7 @@ class TicTacToe: return -1 - def next_move(self): + def next_move(self) -> Union[Tuple[int, int], Tuple[List[int], List[int]]]: available_moves = [] # will carry all available moves player_win_spot = [] # if player (user Wins) comp_pick = "O" @@ -113,7 +116,6 @@ class TicTacToe: if len(player_win_spot) != 0: 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] - # print(AvailableMoves) 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]] @@ -121,7 +123,6 @@ class TicTacToe: return -1, -1 c1, c2 = self.dim_sz // 2, self.dim_sz // 2 - # print(c1,c2,self.dim_sz) if (c1, c2) in available_moves: # CENTER self.board[c1][c2] = comp_pick return c1, c2 @@ -163,34 +164,33 @@ class TicTacToe: ) 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: +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": + for i in range(0, game.dim_sz): + for j in range(0, game.dim_sz - 1): + if game.board[i][j] == "blur": line1 = line1 + " |" else: - line1 = line1 + " " + Game.board[i][j] + " |" - if Game.board[i][Game.dim_sz - 1] == "blur": + line1 = line1 + " " + game.board[i][j] + " |" + if game.board[i][game.dim_sz - 1] == "blur": line1 = line1 + " \n" else: - line1 = line1 + " " + Game.board[i][Game.dim_sz - 1] + " \n" - # line1 = line1 + " " + Game.board[i][Game.dim_sz-1] + "\n" + line1 = line1 + " " + game.board[i][game.dim_sz - 1] + " \n" print(line1, "\n\n") -def play() -> None: - Pick = input("Pick 'X' or 'O' ").strip().upper() - if Pick == "O": - Game = TicTacToe("O") +def main() -> None: + pick = input("Pick 'X' or 'O' ").strip().upper() + if pick == "O": + game = TicTacToe("O") else: - Game = TicTacToe("X") - display(Game=Game) + game = TicTacToe("X") + display(game=game) while True: - # Display(Game) - temp = False + temp: Union[bool, str] = False while not temp: move = list( map( @@ -198,25 +198,25 @@ def play() -> None: input("Make A Move in Grid System from (0,0) to (2,2) ").split(), ) ) - temp = Game.move_record(move[0], move[1]) + temp = game.move_record(move[0], move[1]) if not temp: print(temp) - if Game.check_win() == 1: + if game.check_win() == 1: print("You Won!") break print("Your Move:- ") - display(Game) - C1, C2 = Game.next_move() + display(game) + C1, C2 = game.next_move() if C1 == -1 and C2 == -1: print("Game Tie!") break - if Game.check_win() == 0: + if game.check_win() == 0: print("You lost!") break print("Computer's Move :-") - display(Game) + display(game) if __name__ == "__main__": - play() + main() diff --git a/89_Tic-Tac-Toe/python/tictactoe2.py b/89_Tic-Tac-Toe/python/tictactoe2.py index 519cd7d3..210ff2e7 100755 --- a/89_Tic-Tac-Toe/python/tictactoe2.py +++ b/89_Tic-Tac-Toe/python/tictactoe2.py @@ -191,7 +191,7 @@ def prompt_player(board): return move -def play(): +def main() -> None: print(" " * 30 + "TIC-TAC-TOE") print(" " * 15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") print("\n\n") @@ -247,4 +247,4 @@ def play(): if __name__ == "__main__": - play() + main() diff --git a/90_Tower/python/tower.py b/90_Tower/python/tower.py index 982f8bb3..7d30c9eb 100644 --- a/90_Tower/python/tower.py +++ b/90_Tower/python/tower.py @@ -1,11 +1,12 @@ import sys +from typing import List, Optional class Disk: - def __init__(self, size): + def __init__(self, size: int) -> None: self.__size = size - def size(self): + def size(self) -> int: return self.__size def print(self) -> None: @@ -13,28 +14,29 @@ class Disk: class Tower: - def __init__(self): - self.__disks = [] + def __init__(self) -> None: + self.__disks: List[Disk] = [] - def empty(self): + def empty(self) -> bool: return len(self.__disks) == 0 - def top(self): + def top(self) -> Optional[Disk]: if self.empty(): return None else: return self.__disks[-1] - def add(self, disk): + 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): + def pop(self) -> Disk: if self.empty(): raise Exception("empty pop") return self.__disks.pop() @@ -45,7 +47,7 @@ class Tower: class Game: - def __init__(self): + 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] @@ -60,23 +62,23 @@ class Game: disk = Disk(size) self.__towers[0].add(disk) - def winner(self): + 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): + def moves(self) -> int: return self.__moves - def which_disk(self): + 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): + def pick_disk(self) -> Optional[Tower]: which = None while which is None: try: @@ -93,7 +95,7 @@ class Game: assert valids[0].top().size() == which return valids[0] - def which_tower(self): + def which_tower(self) -> Optional[Tower]: try: needle = int(input("PLACE DISK ON WHICH NEEDLE\n")) tower = self.__towers[needle - 1] @@ -105,7 +107,7 @@ class Game: else: return tower - def take_turn(self): + def take_turn(self) -> None: from_tower = None while from_tower is None: from_tower = self.pick_disk() diff --git a/91_Train/python/train.py b/91_Train/python/train.py index d7ac55d8..2affcc0a 100644 --- a/91_Train/python/train.py +++ b/91_Train/python/train.py @@ -6,14 +6,14 @@ import random -def play_game(): +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 = 0 + time_answer: float = 0 while time_answer == 0: try: time_answer = float(input("How long does the trip take by car ")) diff --git a/92_Trap/python/trap.py b/92_Trap/python/trap.py index 5f74a760..64220234 100644 --- a/92_Trap/python/trap.py +++ b/92_Trap/python/trap.py @@ -10,7 +10,7 @@ number_max = 100 guess_max = 6 -def play_game(): +def play_game() -> None: """Play one round of the game""" number_computer = random.randint(1, number_max) diff --git a/93_23_Matches/python/23matches.py b/93_23_Matches/python/23matches.py index 5488f08b..5375a176 100755 --- a/93_23_Matches/python/23matches.py +++ b/93_23_Matches/python/23matches.py @@ -6,7 +6,7 @@ import random -def play_game(): +def play_game() -> None: """Play one round of the game""" matches = 23 diff --git a/94_War/python/cards.json b/94_War/python/cards.json new file mode 100644 index 00000000..b27e9bd4 --- /dev/null +++ b/94_War/python/cards.json @@ -0,0 +1,54 @@ +[ + "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" +] diff --git a/94_War/python/war.py b/94_War/python/war.py index f8d34004..48137c38 100755 --- a/94_War/python/war.py +++ b/94_War/python/war.py @@ -1,75 +1,27 @@ #!/usr/bin/env python3 -# WAR -# -# Converted from BASIC to Python by Trevor Hobson +""" +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): +def card_value(input: str) -> int: return ["2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"].index( input.split("-")[1] ) -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", -] - - -def play_game(): +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 diff --git a/95_Weekday/python/weekday.py b/95_Weekday/python/weekday.py index 6da78bb2..15cdae44 100644 --- a/95_Weekday/python/weekday.py +++ b/95_Weekday/python/weekday.py @@ -12,35 +12,28 @@ Ported by Dave LeCompte. """ import datetime +from typing import Tuple GET_TODAY_FROM_SYSTEM = True -def print_with_tab(space_count: int, s: str) -> None: - if space_count > 0: - spaces = " " * space_count - else: - spaces = "" - print(spaces + s) - - -def get_date_from_user(prompt): +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.") - return month_num, day_num, year_num -def get_date_from_system(): +def get_date_from_system() -> Tuple[int, int, int]: dt = datetime.datetime.today() return dt.month, dt.day, dt.year -def get_day_of_week(weekday_index, day): +def get_day_of_week(weekday_index, day) -> str: day_names = { 1: "SUNDAY", 2: "MONDAY", @@ -56,13 +49,13 @@ def get_day_of_week(weekday_index, day): return day_names[weekday_index] -def previous_day(b): +def previous_day(b) -> int: if b == 0: b = 6 return b - 1 -def is_leap_year(year): +def is_leap_year(year: int) -> bool: if (year % 4) != 0: return False if (year % 100) != 0: @@ -113,7 +106,7 @@ def deduct_time(frac, days, years_remain, months_remain, days_remain): def time_report(msg, years, months, days): leading_spaces = 23 - len(msg) - print_with_tab(leading_spaces, msg + f"\t{years}\t{months}\t{days}") + print(" " * leading_spaces + f"{msg}\t{years}\t{months}\t{days}") def make_occupation_label(years): @@ -141,17 +134,14 @@ def calculate_day_of_week(year, month, day): return b -def end(): +def end() -> None: for _ in range(5): print() def main() -> None: - print_with_tab(32, "WEEKDAY") - print_with_tab(15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY") - print() - print() - print() + 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() @@ -226,8 +216,8 @@ def main() -> None: print("***HAPPY BIRTHDAY***") # print report - print_with_tab(23, "\tYEARS\tMONTHS\tDAYS") - print_with_tab(23, "\t-----\t------\t----") + 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) @@ -255,11 +245,9 @@ def main() -> None: # Calculate retirement date e = year + 65 - print_with_tab(16, f"*** YOU MAY RETIRE IN {e} ***") + print(" " * 16 + f"*** YOU MAY RETIRE IN {e} ***") end() if __name__ == "__main__": main() - - # test_harness() diff --git a/96_Word/javascript/word.html b/96_Word/javascript/word.html deleted file mode 100644 index 02419ce4..00000000 --- a/96_Word/javascript/word.html +++ /dev/null @@ -1,10 +0,0 @@ - - -WORD - - - -
    
    -
    -
    -
    diff --git a/96_Word/javascript/word.js b/96_Word/javascript/word.mjs
    similarity index 74%
    rename from 96_Word/javascript/word.js
    rename to 96_Word/javascript/word.mjs
    index 3d7e3a5a..e4a1bd8e 100644
    --- a/96_Word/javascript/word.js
    +++ b/96_Word/javascript/word.mjs
    @@ -1,43 +1,9 @@
    +#!/usr/bin/env node
     // WORD
     //
     // 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;
    -}
    +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",
    @@ -74,7 +40,7 @@ async function main()
     
             let guess = undefined;
             while (1) {
    -            print("GUESS A FIVE LETTER WORD");
    +            print("GUESS A FIVE LETTER WORD:");
                 guess = (await input()).toUpperCase();
                 guessCount++;
                 if (secretWord === guess) {
    diff --git a/96_Word/python/word.py b/96_Word/python/word.py
    old mode 100644
    new mode 100755
    index fcb3fc11..8b7f0d51
    --- a/96_Word/python/word.py
    +++ b/96_Word/python/word.py
    @@ -1,7 +1,10 @@
     #!/usr/bin/env python3
    -# WORD
    -#
    -# Converted from BASIC to Python by Trevor Hobson
    +
    +"""
    +WORD
    +
    +Converted from BASIC to Python by Trevor Hobson
    +"""
     
     import random
     
    @@ -21,7 +24,7 @@ words = [
     ]
     
     
    -def play_game():
    +def play_game() -> None:
         """Play one round of the game"""
     
         random.shuffle(words)
    @@ -54,16 +57,15 @@ def play_game():
                             if i == j:
                                 guess_progress[j] = guess_word[i]
                 print(
    -                "There were",
    -                matches,
    -                "matches and the common letters were... " + common_letters,
    +                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("\nYou have guessed the word. It took", guess_count, "guesses!")
    +                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.")
    diff --git a/HOW_TO_RUN_THE_GAMES.md b/HOW_TO_RUN_THE_GAMES.md
    index fae0656f..688ee136 100644
    --- a/HOW_TO_RUN_THE_GAMES.md
    +++ b/HOW_TO_RUN_THE_GAMES.md
    @@ -40,9 +40,21 @@ or if you are **using JDK11 or later** you can now execute a self contained java
     
     ## javascript
     
    -The javascript examples can be run from within your web browser:
    +There are two ways of javascript implementations:
     
    -1. Simply open the corresponding `.html` file from your web browser.
    +### 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
     
    @@ -93,3 +105,11 @@ If you don't already have a ruby interpreter, you can download it from the [ruby
     ## 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`
    diff --git a/index.html b/index.html
    index 45e6ec84..a14d61f6 100644
    --- a/index.html
    +++ b/index.html
    @@ -1 +1 @@
    -BASIC Computer Games

    BASIC Computer Games

    \ No newline at end of file +BASIC Computer Games

    BASIC Computer Games

    \ No newline at end of file