mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-25 12:25:10 -08:00
Merge branch 'coding-horror:main' into csharp-07-basketball
This commit is contained in:
@@ -17,7 +17,7 @@
|
||||
|
||||
overflow-y: scroll;
|
||||
width: 100%;
|
||||
max-width: 60rem;
|
||||
max-width: 640px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@@ -32,10 +32,7 @@
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* The "terminal" has one "prompt" element.
|
||||
* This prompt is not any kind of input, but just a simple <span>
|
||||
* with an id "prompt" and a
|
||||
*/
|
||||
/* The "terminal" has one "prompt" input-element. */
|
||||
@keyframes prompt-blink {
|
||||
100% {
|
||||
opacity: 0;
|
||||
@@ -57,6 +54,15 @@
|
||||
width: 0.75rem;
|
||||
opacity: 1;
|
||||
}
|
||||
.terminal input#prompt {
|
||||
text-transform: uppercase;
|
||||
background: none;
|
||||
border: none;
|
||||
outline: none;
|
||||
caret-color: var(--text);
|
||||
color: var(--text);
|
||||
font: var(--terminal-font);
|
||||
}
|
||||
|
||||
|
||||
/* Terminal scrollbar */
|
||||
|
||||
@@ -26,7 +26,7 @@ class HtmlTerminal {
|
||||
* @private
|
||||
* @type {HTMLElement}
|
||||
*/
|
||||
#$prompt = undefined;
|
||||
#$prompt;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@@ -45,13 +45,18 @@ class HtmlTerminal {
|
||||
this.$output.classList.add('terminal');
|
||||
|
||||
// Create a prompt element.
|
||||
// This element gets added if input is needed
|
||||
this.#$prompt = document.createElement("span");
|
||||
// This element gets added if input is needed.
|
||||
this.#$prompt = document.createElement("input");
|
||||
this.#$prompt.setAttribute("id", "prompt");
|
||||
this.#$prompt.innerText = "";
|
||||
this.#$prompt.setAttribute("type", "text");
|
||||
this.#$prompt.setAttribute("length", "50");
|
||||
this.#$prompt.addEventListener("keydown", this.#handleKey.bind(this));
|
||||
|
||||
//TODO: this handler shouls be only on the propt element and only active if cursor is visible
|
||||
document.addEventListener("keyup", this.#handleKey.bind(this));
|
||||
// Force focus on the promt on each click.
|
||||
// This is needed for mobile support.
|
||||
document.body.addEventListener('click', () => {
|
||||
this.#$prompt.focus();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,37 +82,16 @@ class HtmlTerminal {
|
||||
* @param {*} e
|
||||
*/
|
||||
#handleKey(e) {
|
||||
// if no input-callback is defined
|
||||
// if no input-callback is defined just return
|
||||
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 = "";
|
||||
if (e.keyCode == 13) {
|
||||
const text = this.#$prompt.value;
|
||||
this.#$prompt.value = '';
|
||||
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;
|
||||
this.#inputCallback(text + '\n');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +106,7 @@ class HtmlTerminal {
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO:
|
||||
* Create a new div and add html content.
|
||||
*
|
||||
* @public
|
||||
* @param {*} htmlContent
|
||||
@@ -189,7 +173,8 @@ class HtmlTerminal {
|
||||
*/
|
||||
input(callback) {
|
||||
// show prompt with a blinking prompt
|
||||
this.$output.appendChild(this.#$prompt);
|
||||
this.#inputCallback = callback;
|
||||
this.$output.appendChild(this.#$prompt);
|
||||
this.#$prompt.focus();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Minimal node.js terminal</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=.75">
|
||||
<meta name="viewport" content="width=640, initial-scale=1">
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="../../../00_Utilities/javascript/style_terminal.css"
|
||||
|
||||
@@ -57,9 +57,13 @@ export async function input(message = '') {
|
||||
* First we need to convert it into a string. */
|
||||
const data = input.toString();
|
||||
|
||||
/* add input to terminal
|
||||
* The data should end with a newline! */
|
||||
process.stdout.write(data);
|
||||
|
||||
/* The result fo onData is a string ending with an `\n`.
|
||||
* We just need the actual content so let's remove the newline at the end: */
|
||||
const content = data[data.length] === '\n' ? data.slice(0, -1) : data;
|
||||
const content = data.endsWith('\n') ? data.slice(0, -1) : data;
|
||||
|
||||
resolve(content);
|
||||
});
|
||||
|
||||
@@ -84,143 +84,10 @@ a:hover {
|
||||
}
|
||||
|
||||
/* add all the face flicker effects (only on desktop) */
|
||||
@media screen and (min-width: 960px) {
|
||||
@media not screen and (max-width: 960px) and (prefers-reduced-motion) {
|
||||
main {
|
||||
padding: 3rem;
|
||||
}
|
||||
@keyframes flicker {
|
||||
0% {
|
||||
opacity: 0.27861;
|
||||
}
|
||||
5% {
|
||||
opacity: 0.34769;
|
||||
}
|
||||
10% {
|
||||
opacity: 0.23604;
|
||||
}
|
||||
15% {
|
||||
opacity: 0.90626;
|
||||
}
|
||||
20% {
|
||||
opacity: 0.18128;
|
||||
}
|
||||
25% {
|
||||
opacity: 0.83891;
|
||||
}
|
||||
30% {
|
||||
opacity: 0.65583;
|
||||
}
|
||||
35% {
|
||||
opacity: 0.67807;
|
||||
}
|
||||
40% {
|
||||
opacity: 0.26559;
|
||||
}
|
||||
45% {
|
||||
opacity: 0.84693;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.96019;
|
||||
}
|
||||
55% {
|
||||
opacity: 0.08594;
|
||||
}
|
||||
60% {
|
||||
opacity: 0.20313;
|
||||
}
|
||||
65% {
|
||||
opacity: 0.71988;
|
||||
}
|
||||
70% {
|
||||
opacity: 0.53455;
|
||||
}
|
||||
75% {
|
||||
opacity: 0.37288;
|
||||
}
|
||||
80% {
|
||||
opacity: 0.71428;
|
||||
}
|
||||
85% {
|
||||
opacity: 0.70419;
|
||||
}
|
||||
90% {
|
||||
opacity: 0.7003;
|
||||
}
|
||||
95% {
|
||||
opacity: 0.36108;
|
||||
}
|
||||
100% {
|
||||
opacity: 0.24387;
|
||||
}
|
||||
}
|
||||
@keyframes textShadow {
|
||||
0% {
|
||||
text-shadow: 0.4389924193300864px 0 1px rgba(0,30,255,0.5), -0.4389924193300864px 0 1px rgba(255,0,80,0.3), 0 0 3px;
|
||||
}
|
||||
5% {
|
||||
text-shadow: 2.7928974010788217px 0 1px rgba(0,30,255,0.5), -2.7928974010788217px 0 1px rgba(255,0,80,0.3), 0 0 3px;
|
||||
}
|
||||
10% {
|
||||
text-shadow: 0.02956275843481219px 0 1px rgba(0,30,255,0.5), -0.02956275843481219px 0 1px rgba(255,0,80,0.3), 0 0 3px;
|
||||
}
|
||||
15% {
|
||||
text-shadow: 0.40218538552878136px 0 1px rgba(0,30,255,0.5), -0.40218538552878136px 0 1px rgba(255,0,80,0.3), 0 0 3px;
|
||||
}
|
||||
20% {
|
||||
text-shadow: 3.4794037899852017px 0 1px rgba(0,30,255,0.5), -3.4794037899852017px 0 1px rgba(255,0,80,0.3), 0 0 3px;
|
||||
}
|
||||
25% {
|
||||
text-shadow: 1.6125630401149584px 0 1px rgba(0,30,255,0.5), -1.6125630401149584px 0 1px rgba(255,0,80,0.3), 0 0 3px;
|
||||
}
|
||||
30% {
|
||||
text-shadow: 0.7015590085143956px 0 1px rgba(0,30,255,0.5), -0.7015590085143956px 0 1px rgba(255,0,80,0.3), 0 0 3px;
|
||||
}
|
||||
35% {
|
||||
text-shadow: 3.896914047650351px 0 1px rgba(0,30,255,0.5), -3.896914047650351px 0 1px rgba(255,0,80,0.3), 0 0 3px;
|
||||
}
|
||||
40% {
|
||||
text-shadow: 3.870905614848819px 0 1px rgba(0,30,255,0.5), -3.870905614848819px 0 1px rgba(255,0,80,0.3), 0 0 3px;
|
||||
}
|
||||
45% {
|
||||
text-shadow: 2.231056963361899px 0 1px rgba(0,30,255,0.5), -2.231056963361899px 0 1px rgba(255,0,80,0.3), 0 0 3px;
|
||||
}
|
||||
50% {
|
||||
text-shadow: 0.08084290417898504px 0 1px rgba(0,30,255,0.5), -0.08084290417898504px 0 1px rgba(255,0,80,0.3), 0 0 3px;
|
||||
}
|
||||
55% {
|
||||
text-shadow: 2.3758461067427543px 0 1px rgba(0,30,255,0.5), -2.3758461067427543px 0 1px rgba(255,0,80,0.3), 0 0 3px;
|
||||
}
|
||||
60% {
|
||||
text-shadow: 2.202193051050636px 0 1px rgba(0,30,255,0.5), -2.202193051050636px 0 1px rgba(255,0,80,0.3), 0 0 3px;
|
||||
}
|
||||
65% {
|
||||
text-shadow: 2.8638780614874975px 0 1px rgba(0,30,255,0.5), -2.8638780614874975px 0 1px rgba(255,0,80,0.3), 0 0 3px;
|
||||
}
|
||||
70% {
|
||||
text-shadow: 0.48874025155497314px 0 1px rgba(0,30,255,0.5), -0.48874025155497314px 0 1px rgba(255,0,80,0.3), 0 0 3px;
|
||||
}
|
||||
75% {
|
||||
text-shadow: 1.8948491305757957px 0 1px rgba(0,30,255,0.5), -1.8948491305757957px 0 1px rgba(255,0,80,0.3), 0 0 3px;
|
||||
}
|
||||
80% {
|
||||
text-shadow: 0.0833037308038857px 0 1px rgba(0,30,255,0.5), -0.0833037308038857px 0 1px rgba(255,0,80,0.3), 0 0 3px;
|
||||
}
|
||||
85% {
|
||||
text-shadow: 0.09769827255241735px 0 1px rgba(0,30,255,0.5), -0.09769827255241735px 0 1px rgba(255,0,80,0.3), 0 0 3px;
|
||||
}
|
||||
90% {
|
||||
text-shadow: 3.443339761481782px 0 1px rgba(0,30,255,0.5), -3.443339761481782px 0 1px rgba(255,0,80,0.3), 0 0 3px;
|
||||
}
|
||||
95% {
|
||||
text-shadow: 2.1841838852799786px 0 1px rgba(0,30,255,0.5), -2.1841838852799786px 0 1px rgba(255,0,80,0.3), 0 0 3px;
|
||||
}
|
||||
100% {
|
||||
text-shadow: 2.6208764473832513px 0 1px rgba(0,30,255,0.5), -2.6208764473832513px 0 1px rgba(255,0,80,0.3), 0 0 3px;
|
||||
}
|
||||
}
|
||||
#output {
|
||||
animation: textShadow 1.6s infinite;
|
||||
}
|
||||
#output::before {
|
||||
content: " ";
|
||||
display: block;
|
||||
@@ -246,6 +113,5 @@ a:hover {
|
||||
opacity: 0;
|
||||
z-index: 2;
|
||||
pointer-events: none;
|
||||
animation: flicker 0.15s infinite;
|
||||
}
|
||||
}
|
||||
121
05_Bagels/lua/Bagels.lua
Normal file
121
05_Bagels/lua/Bagels.lua
Normal file
@@ -0,0 +1,121 @@
|
||||
---
|
||||
--- Bagels
|
||||
--- Ported by Joe Nellis.
|
||||
--- Text displayed is altered slightly from the original program to allow for
|
||||
--- more (or less) than three digits to be guessed. Change the difficulty to
|
||||
--- the number of digits you wish in the secret code.
|
||||
---
|
||||
|
||||
--- difficult is number of digits to use
|
||||
local difficulty = 3
|
||||
|
||||
print [[
|
||||
BAGELS
|
||||
CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
|
||||
|
||||
]]
|
||||
|
||||
function getInput(prompt)
|
||||
io.write(prompt)
|
||||
io.flush()
|
||||
local input = io.read("l")
|
||||
if not input then --- test for EOF
|
||||
print("GOODBYE")
|
||||
os.exit(0)
|
||||
end
|
||||
return input
|
||||
end
|
||||
|
||||
local needsRules = getInput("WOULD YOU LIKE THE RULES? (YES OR NO) ")
|
||||
print()
|
||||
if needsRules:match("[yY].*") then
|
||||
print(string.format( [[
|
||||
I AM THINKING OF A %u DIGIT NUMBER. TRY TO GUESS
|
||||
MY NUMBER AND I WILL GIVE YOU CLUES AS FOLLOWS:
|
||||
PICO - ONE DIGIT CORRECT BUT IN THE WRONG POSITION
|
||||
FERMI - ONE DIGIT CORRECT AND IN THE RIGHT POSITION
|
||||
BAGELS - NO DIGITS
|
||||
]], difficulty))
|
||||
end
|
||||
|
||||
function play(numDigits, score)
|
||||
local digits = { "1", "2", "3", "4", "5", "6", "7", "8", "9", "0" }
|
||||
--- secret number must not have duplicate digits
|
||||
--- randomly swap numDigits at the head of this list to create secret number
|
||||
for i = 1, numDigits do
|
||||
local j = math.random(1, 10)
|
||||
digits[i], digits[j] = digits[j], digits[i]
|
||||
end
|
||||
|
||||
print "O.K. I HAVE A NUMBER IN MIND."
|
||||
for guessNum = 1, 20 do
|
||||
:: GUESS_AGAIN :: ---<!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
local guess = getInput(string.format("GUESS #%u\t?",guessNum))
|
||||
if #guess ~= numDigits then
|
||||
print("TRY GUESSING A", numDigits, "DIGIT NUMBER.")
|
||||
goto GUESS_AGAIN
|
||||
elseif not guess:match("^(%d+)$") then
|
||||
print("WHAT?")
|
||||
goto GUESS_AGAIN
|
||||
else
|
||||
--- check if user has duplicate digits
|
||||
for i = 1, numDigits - 1 do
|
||||
for j = i + 1, numDigits do
|
||||
if (guess:sub(i, i) == guess:sub(j, j)) then
|
||||
print("OH, I FORGOT TO TELL YOU THAT THE NUMBER I HAVE")
|
||||
print("IN MIND HAS NO TWO DIGITS THE SAME.")
|
||||
goto GUESS_AGAIN
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local report = ""
|
||||
--- check for picos, right digit, wrong place
|
||||
for i = 1, numDigits do
|
||||
for j = i + 1, numDigits - 1 + i do
|
||||
if (guess:sub(i, i) == digits[(j - 1) % numDigits + 1]) then
|
||||
report = report .. "PICO "
|
||||
end
|
||||
end
|
||||
end
|
||||
--- check for fermis, right digit, right place
|
||||
for i = 1, numDigits do
|
||||
if (guess:sub(i, i) == digits[i]) then
|
||||
report = report .. "FERMI "
|
||||
end
|
||||
end
|
||||
|
||||
if (report == string.rep("FERMI ", numDigits)) then
|
||||
print "YOU GOT IT!!!"
|
||||
print ""
|
||||
score = score + 1
|
||||
goto PLAY_AGAIN
|
||||
end
|
||||
if (report == "") then
|
||||
print("BAGELS")
|
||||
else
|
||||
print(report)
|
||||
end
|
||||
end
|
||||
print "OH WELL."
|
||||
print("THAT'S TWENTY GUESSES. MY NUMBER WAS "
|
||||
.. table.concat(digits, "", 1, numDigits))
|
||||
|
||||
:: PLAY_AGAIN :: ---<!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
|
||||
local playAgain = getInput("PLAY AGAIN (YES OR NO)?")
|
||||
print()
|
||||
if (playAgain:match("[yY].*")) then
|
||||
return play(numDigits, score)
|
||||
else
|
||||
if (score > 0) then
|
||||
print("A", score, "POINT BAGELS BUFF!!")
|
||||
end
|
||||
print "HOPE YOU HAD FUN. BYE."
|
||||
end
|
||||
end
|
||||
|
||||
play(difficulty, 0) --- default is numDigits=3, score=0
|
||||
|
||||
9
33_Dice/rust/Cargo.toml
Normal file
9
33_Dice/rust/Cargo.toml
Normal file
@@ -0,0 +1,9 @@
|
||||
[package]
|
||||
name = "rust"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
rand = "0.8.5"
|
||||
3
33_Dice/rust/README.md
Normal file
3
33_Dice/rust/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
|
||||
|
||||
Conversion to [Rust](https://www.rust-lang.org/)
|
||||
85
33_Dice/rust/src/main.rs
Normal file
85
33_Dice/rust/src/main.rs
Normal file
@@ -0,0 +1,85 @@
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Dice
|
||||
//
|
||||
// From: BASIC Computer Games (1978)
|
||||
// Edited by David H. Ahl
|
||||
//
|
||||
// "Not exactly a game, this program simulates rolling
|
||||
// a pair of dice a large number of times and prints out
|
||||
// the frequency distribution. You simply input the
|
||||
// number of rolls. It is interesting to see how many
|
||||
// rolls are necessary to approach the theoretical
|
||||
// distribution:
|
||||
//
|
||||
// 2 1/36 2.7777...%
|
||||
// 3 2/36 5.5555...%
|
||||
// 4 3/36 8.3333...%
|
||||
// etc.
|
||||
//
|
||||
// "Daniel Freidus wrote this program while in the
|
||||
// seventh grade at Harrison Jr-Sr High School,
|
||||
// Harrison, New York."
|
||||
//
|
||||
// Rust Port by Jay, 2022
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
use rand::Rng;
|
||||
use std::io::{self, Write};
|
||||
|
||||
fn main() {
|
||||
let mut frequency: [i32; 13] = [0; 13];
|
||||
println!(
|
||||
"{: >38}\n{: >57}\n\n",
|
||||
"DICE", "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"
|
||||
);
|
||||
// DANNY FREIDUS
|
||||
println!("THIS PROGRAM SIMULATES THE ROLLING OF A");
|
||||
println!("PAIR OF DICE.");
|
||||
println!("YOU ENTER THE NUMBER OF TIMES YOU WANT THE COMPUTER TO");
|
||||
println!("'ROLL' THE DICE. WATCH OUT, VERY LARGE NUMBERS TAKE");
|
||||
println!("A LONG TIME. IN PARTICULAR, NUMBERS OVER 5000.");
|
||||
let mut playing = true;
|
||||
while playing {
|
||||
let n = match readinput(&"HOW MANY ROLLS").trim().parse::<i32>() {
|
||||
Ok(num) => num,
|
||||
Err(_) => {
|
||||
println!("PLEASE ENTER A NUMBER");
|
||||
continue;
|
||||
}
|
||||
};
|
||||
// Dice Rolled n times
|
||||
for _i in 0..n {
|
||||
let die_1 = rand::thread_rng().gen_range(1..=6);
|
||||
let die_2 = rand::thread_rng().gen_range(1..=6);
|
||||
let total = die_1 + die_2;
|
||||
frequency[total] += 1;
|
||||
}
|
||||
|
||||
// Results tabel
|
||||
println!("\nTOTAL SPOTS NUMBER OF TIMES");
|
||||
for i in 2..13 {
|
||||
println!("{:^4}\t\t{}", i, frequency[i]);
|
||||
}
|
||||
|
||||
// Continue the game
|
||||
let reply = readinput("TRY AGAIN").to_ascii_uppercase();
|
||||
if reply.starts_with("Y") || reply.eq("YES") {
|
||||
frequency = [0; 13];
|
||||
} else {
|
||||
playing = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// function for getting input on same line
|
||||
fn readinput(str: &str) -> String {
|
||||
print!("\n{}? ", str);
|
||||
let mut input = String::new();
|
||||
io::stdout().flush().unwrap();
|
||||
io::stdin()
|
||||
.read_line(&mut input)
|
||||
.expect("Failed to get Input");
|
||||
input
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user