mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-21 14:50:54 -08:00
Merge branch 'coding-horror:main' into main
This commit is contained in:
35
.gitignore
vendored
35
.gitignore
vendored
@@ -0,0 +1,35 @@
|
||||
.local/
|
||||
.vscode/
|
||||
.gradle/
|
||||
node_modules/
|
||||
buildJvm/
|
||||
|
||||
build.gradle
|
||||
|
||||
.classpath
|
||||
.project
|
||||
.settings
|
||||
.metadata
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
*.class
|
||||
*/.vs
|
||||
*.suo
|
||||
|
||||
bin/
|
||||
obj/
|
||||
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
out/
|
||||
|
||||
*.py[co]
|
||||
|
||||
Pipfile
|
||||
|
||||
.DS_Store
|
||||
/.vs/basic-computer-games/v16
|
||||
/.vs
|
||||
|
||||
84
00_Utilities/find-missing-implementations.js
Normal file
84
00_Utilities/find-missing-implementations.js
Normal file
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Program to find games that are missing solutions in a given language
|
||||
*
|
||||
* Scan each game folder, check for a folder for each language, and also make
|
||||
* sure there's at least one file of the expected extension and not just a
|
||||
* readme or something
|
||||
*/
|
||||
|
||||
const fs = require("fs");
|
||||
const glob = require("glob");
|
||||
|
||||
// relative path to the repository root
|
||||
const ROOT_PATH = "../.";
|
||||
|
||||
const languages = [
|
||||
{ name: "csharp", extension: "cs" },
|
||||
{ name: "java", extension: "java" },
|
||||
{ name: "javascript", extension: "html" },
|
||||
{ name: "pascal", extension: "pas" },
|
||||
{ name: "perl", extension: "pl" },
|
||||
{ name: "python", extension: "py" },
|
||||
{ name: "ruby", extension: "rb" },
|
||||
{ name: "vbnet", extension: "vb" },
|
||||
];
|
||||
|
||||
const getFilesRecursive = async (path, extension) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
glob(`${path}/**/*.${extension}`, (err, matches) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
resolve(matches);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const getPuzzleFolders = () => {
|
||||
return fs
|
||||
.readdirSync(ROOT_PATH, { withFileTypes: true })
|
||||
.filter((dirEntry) => dirEntry.isDirectory())
|
||||
.filter(
|
||||
(dirEntry) =>
|
||||
![".git", "node_modules", "00_Utilities"].includes(dirEntry.name)
|
||||
)
|
||||
.map((dirEntry) => dirEntry.name);
|
||||
};
|
||||
|
||||
(async () => {
|
||||
let missingGames = {};
|
||||
let missingLanguageCounts = {};
|
||||
languages.forEach((l) => (missingLanguageCounts[l.name] = 0));
|
||||
const puzzles = getPuzzleFolders();
|
||||
for (const puzzle of puzzles) {
|
||||
for (const { name: language, extension } of languages) {
|
||||
const files = await getFilesRecursive(
|
||||
`${ROOT_PATH}/${puzzle}/${language}`,
|
||||
extension
|
||||
);
|
||||
if (files.length === 0) {
|
||||
if (!missingGames[puzzle]) missingGames[puzzle] = [];
|
||||
|
||||
missingGames[puzzle].push(language);
|
||||
missingLanguageCounts[language]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
const missingCount = Object.values(missingGames).flat().length;
|
||||
if (missingCount === 0) {
|
||||
console.log("All games have solutions for all languages");
|
||||
} else {
|
||||
console.log(`Missing ${missingCount} implementations:`);
|
||||
|
||||
Object.entries(missingGames).forEach(
|
||||
([p, ls]) => (missingGames[p] = ls.join(", "))
|
||||
);
|
||||
|
||||
console.log(`\nMissing languages by game:`);
|
||||
console.table(missingGames);
|
||||
console.log(`\nBy language:`);
|
||||
console.table(missingLanguageCounts);
|
||||
}
|
||||
})();
|
||||
|
||||
return;
|
||||
50
00_Utilities/markdown_todo.py
Normal file
50
00_Utilities/markdown_todo.py
Normal file
@@ -0,0 +1,50 @@
|
||||
import os
|
||||
|
||||
|
||||
lang_pos = {
|
||||
"csharp": 1, "java": 2, "javascript": 3,
|
||||
"pascal": 4, "perl": 5, "python": 6, "ruby": 7, "vbnet": 8
|
||||
}
|
||||
|
||||
write_string = "# TODO list \n game | csharp | java | javascript | pascal | perl | python | ruby | vbnet \n --- | --- | --- | --- | --- | --- | --- | --- | --- \n"
|
||||
# Set the directory you want to start from
|
||||
rootDir = '..'
|
||||
|
||||
strings_done = []
|
||||
|
||||
checklist = ["game", "csharp", "java", "javascript",
|
||||
"pascal", "perl", "python", "ruby", "vbnet"]
|
||||
|
||||
prev_game = ""
|
||||
|
||||
for dirName, subdirList, fileList in os.walk(rootDir):
|
||||
split_dir = dirName.split("/")
|
||||
|
||||
if len(split_dir) == 2 and not split_dir[1] in ['.git', '00_Utilities']:
|
||||
if prev_game == "":
|
||||
prev_game = split_dir[1]
|
||||
checklist[0] = split_dir[1]
|
||||
|
||||
if prev_game != split_dir[1]:
|
||||
# it's a new dir
|
||||
strings_done.append(checklist)
|
||||
checklist = [split_dir[1], "csharp", "java", "javascript",
|
||||
"pascal", "perl", "python", "ruby", "vbnet"]
|
||||
prev_game = split_dir[1]
|
||||
|
||||
elif len(split_dir) == 3 and split_dir[1] != '.git':
|
||||
if split_dir[2] in lang_pos.keys():
|
||||
if len(fileList) > 1 or len(subdirList) > 0:
|
||||
# there is more files than the readme
|
||||
checklist[lang_pos[split_dir[2]]] = "✅"
|
||||
else:
|
||||
checklist[lang_pos[split_dir[2]]] = "⬜️"
|
||||
|
||||
|
||||
sorted_strings = list(map(lambda l: " | ".join(l) + "\n",
|
||||
sorted(strings_done, key=lambda x: x[0])))
|
||||
write_string += ''.join(sorted_strings)
|
||||
|
||||
|
||||
with open("README.md", "w") as f:
|
||||
f.write(write_string)
|
||||
@@ -1,11 +0,0 @@
|
||||
### Acey Ducey
|
||||
|
||||
As published in Basic Computer Games (1978)
|
||||
https://www.atariarchives.org/basicgames/showpage.php?page=2
|
||||
|
||||
Downloaded from Vintage Basic at
|
||||
http://www.vintage-basic.net/games.html
|
||||
|
||||
#### Other languages:
|
||||
|
||||
- [Pascal/Object Pascal](https://github.com/gcarreno/basic-computer-games-in-pascal/tree/main/01%20Acey%20Ducey)
|
||||
@@ -1,135 +0,0 @@
|
||||
// ACEY DUCEY
|
||||
//
|
||||
// Converted from BASIC to Javascript by Oscar Toledo G. (nanochess)
|
||||
//
|
||||
|
||||
function print(str)
|
||||
{
|
||||
document.getElementById("output").appendChild(document.createTextNode(str));
|
||||
}
|
||||
|
||||
function input()
|
||||
{
|
||||
var input_element;
|
||||
var input_str;
|
||||
|
||||
return new Promise(function (resolve) {
|
||||
input_element = document.createElement("INPUT");
|
||||
|
||||
print("? ");
|
||||
input_element.setAttribute("type", "text");
|
||||
input_element.setAttribute("length", "50");
|
||||
document.getElementById("output").appendChild(input_element);
|
||||
input_element.focus();
|
||||
input_str = undefined;
|
||||
input_element.addEventListener("keydown", function (event) {
|
||||
if (event.keyCode == 13) {
|
||||
input_str = input_element.value;
|
||||
document.getElementById("output").removeChild(input_element);
|
||||
print(input_str);
|
||||
print("\n");
|
||||
resolve(input_str);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function tab(space)
|
||||
{
|
||||
var str = "";
|
||||
while (space-- > 0)
|
||||
str += " ";
|
||||
return str;
|
||||
}
|
||||
|
||||
print(tab(26) + "ACEY DUCEY CARD GAME\n");
|
||||
print(tab(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n");
|
||||
print("\n");
|
||||
print("\n");
|
||||
print("ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER\n");
|
||||
print("THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP\n");
|
||||
print("YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING\n");
|
||||
print("ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE\n");
|
||||
print("A VALUE BETWEEN THE FIRST TWO.\n");
|
||||
print("IF YOU DO NOT WANT TO BET, INPUT A 0\n");
|
||||
|
||||
function show_card(card)
|
||||
{
|
||||
if (card < 11)
|
||||
print(card + "\n");
|
||||
else if (card == 11)
|
||||
print("JACK\n");
|
||||
else if (card == 12)
|
||||
print("QUEEN\n");
|
||||
else if (card == 13)
|
||||
print("KING\n");
|
||||
else
|
||||
print("ACE\n");
|
||||
}
|
||||
|
||||
// Main program
|
||||
async function main()
|
||||
{
|
||||
q = 100;
|
||||
while (1) {
|
||||
print("YOU NOW HAVE " + q + " DOLLARS.\n");
|
||||
print("\n");
|
||||
|
||||
do {
|
||||
print("HERE ARE YOUR NEXT TWO CARDS: \n");
|
||||
do {
|
||||
a = Math.floor(Math.random() * 13 + 2);
|
||||
b = Math.floor(Math.random() * 13 + 2);
|
||||
} while (a >= b) ;
|
||||
show_card(a);
|
||||
show_card(b);
|
||||
print("\n");
|
||||
while (1) {
|
||||
print("\n");
|
||||
print("WHAT IS YOUR BET");
|
||||
m = parseInt(await input());
|
||||
if (m > 0) {
|
||||
if (m > q) {
|
||||
print("SORRY, MY FRIEND, BUT YOU BET TOO MUCH.\n");
|
||||
print("YOU HAVE ONLY " + q + "DOLLARS TO BET.\n");
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
m = 0;
|
||||
print("CHICKEN!!\n");
|
||||
print("\n");
|
||||
break;
|
||||
}
|
||||
} while (m == 0) ;
|
||||
c = Math.floor(Math.random() * 13 + 2);
|
||||
show_card(c);
|
||||
if (c > a && c < b) {
|
||||
print("YOU WIN!!!\n");
|
||||
q = q + m;
|
||||
} else {
|
||||
print("SORRY, YOU LOSE\n");
|
||||
if (m >= q) {
|
||||
print("\n");
|
||||
print("\n");
|
||||
print("SORRY, FRIEND, BUT YOU BLEW YOUR WAD.\n");
|
||||
print("\n");
|
||||
print("\n");
|
||||
print("TRY AGAIN (YES OR NO)");
|
||||
a = await input();
|
||||
print("\n");
|
||||
print("\n");
|
||||
if (a == "YES") {
|
||||
q = 100;
|
||||
} else {
|
||||
print("O.K., HOPE YOU HAD FUN!");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
q = q - m;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
20
01_Acey_Ducey/README.md
Normal file
20
01_Acey_Ducey/README.md
Normal file
@@ -0,0 +1,20 @@
|
||||
### Acey Ducey
|
||||
|
||||
This is a simulation of the Acey Ducey card game. In the game, the dealer (the computer) deals two cards face up. You have an option to bet or not to bet depending on whether or not you feel the next card dealt will have a value between the first two.
|
||||
|
||||
Your initial money is set to $100; you may want to alter this value if you want to start with more or less than $100. The game keeps going on until you lose all your money or interrupt the program.
|
||||
|
||||
The original program author was Bill Palmby of Prairie View, Illinois.
|
||||
|
||||
---
|
||||
|
||||
As published in Basic Computer Games (1978):
|
||||
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=2)
|
||||
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=17)
|
||||
|
||||
Downloaded from Vintage Basic at
|
||||
http://www.vintage-basic.net/games.html
|
||||
|
||||
#### External Links
|
||||
- Common Lisp: https://github.com/koalahedron/lisp-computer-games/blob/master/01%20Acey%20Ducey/common-lisp/acey-deucy.lisp
|
||||
- PowerShell: https://github.com/eweilnau/basic-computer-games-powershell/blob/main/AceyDucey.ps1
|
||||
@@ -10,7 +10,7 @@
|
||||
80 PRINT"IF YOU DO NOT WANT TO BET, INPUT A 0"
|
||||
100 N=100
|
||||
110 Q=100
|
||||
120 PRINT "YOU NOW HAVE";Q;"DOLLARS."
|
||||
120 PRINT "YOU NOW HAVE ";Q;" DOLLARS."
|
||||
130 PRINT
|
||||
140 GOTO 260
|
||||
210 Q=Q+M
|
||||
2
01_Acey_Ducey/d/.gitignore
vendored
Normal file
2
01_Acey_Ducey/d/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*.exe
|
||||
*.obj
|
||||
29
01_Acey_Ducey/d/README.md
Normal file
29
01_Acey_Ducey/d/README.md
Normal file
@@ -0,0 +1,29 @@
|
||||
Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html)
|
||||
|
||||
Converted to [D](https://dlang.org/) by [Bastiaan Veelo](https://github.com/veelo).
|
||||
|
||||
Two versions are supplied that are functionally equivalent, but differ in source layout:
|
||||
|
||||
<dl>
|
||||
<dt><tt>aceyducey_literal.d</tt></dt>
|
||||
<dd>A largely literal transcription of the original Basic source. All unnecessary uglyness is preserved.</dd>
|
||||
<dt><tt>aceyducey.d</tt></dt>
|
||||
<dd>An idiomatic D refactoring of the original, with a focus on increasing the readability and robustness.
|
||||
Memory-safety <A href="https://dlang.org/spec/memory-safe-d.html">is ensured by the language</a>, thanks to the
|
||||
<tt>@safe</tt> annotation.</dd>
|
||||
</dl>
|
||||
|
||||
## Running the code
|
||||
|
||||
Assuming the reference [dmd](https://dlang.org/download.html#dmd) compiler:
|
||||
```shell
|
||||
dmd -run aceyducey.d
|
||||
```
|
||||
|
||||
[Other compilers](https://dlang.org/download.html) also exist.
|
||||
|
||||
Note that there are compiler switches related to memory-safety (`-preview=dip25` and `-preview=dip1000`) that are not
|
||||
used here because they are unnecessary in this case. What these do is to make the analysis more thorough, so that with
|
||||
them some code that needed to be `@system` can then be inferred to be in fact `@safe`. [Code that compiles without
|
||||
these switches is just as safe as when compiled with them]
|
||||
(https://forum.dlang.org/post/dftgjalswvwfjpyushgn@forum.dlang.org).
|
||||
131
01_Acey_Ducey/d/aceyducey.d
Normal file
131
01_Acey_Ducey/d/aceyducey.d
Normal file
@@ -0,0 +1,131 @@
|
||||
@safe: // Make @safe the default for this file, enforcing memory-safety.
|
||||
|
||||
void main()
|
||||
{
|
||||
import std.stdio : write, writeln;
|
||||
import std.string : center, toUpper, wrap;
|
||||
import std.exception : ifThrown;
|
||||
|
||||
enum width = 80;
|
||||
writeln(center("Acey Ducey Card Game", width));
|
||||
writeln(center("(After Creative Computing Morristown, New Jersey)\n", width));
|
||||
writeln(wrap("Acey-Ducey is played in the following manner: The dealer (computer) deals two cards face up. " ~
|
||||
"You have an option to bet or not bet depending on whether or not you feel the third card will " ~
|
||||
"have a value between the first two. If you do not want to bet, input a 0.", width));
|
||||
|
||||
enum Hand {low, middle, high}
|
||||
Card[Hand.max + 1] cards; // Three cards.
|
||||
bool play = true;
|
||||
|
||||
while (play)
|
||||
{
|
||||
int cash = 100;
|
||||
while (cash > 0)
|
||||
{
|
||||
writeln("\nYou now have ", cash, " dollars.");
|
||||
int bet = 0;
|
||||
while (bet <= 0)
|
||||
{
|
||||
do // Draw new cards, until the first card has a smaller value than the last card.
|
||||
{
|
||||
foreach (ref card; cards)
|
||||
card.drawNew;
|
||||
} while (cards[Hand.low] >= cards[Hand.high]);
|
||||
writeln("Here are your next two cards:\n", cards[Hand.low], "\n", cards[Hand.high]);
|
||||
|
||||
int askBet() // A nested function.
|
||||
{
|
||||
import std.conv : to;
|
||||
|
||||
write("\nWhat is your bet? ");
|
||||
int answer = readString.to!int.
|
||||
ifThrown!Exception(askBet); // Try again when answer does not convert to int.
|
||||
if (answer <= cash)
|
||||
return answer;
|
||||
writeln("Sorry, my friend, but you bet too much.\nYou have only ", cash, " dollars to bet.");
|
||||
return askBet; // Recurse: Ask again.
|
||||
}
|
||||
bet = askBet;
|
||||
if (bet <= 0) // Negative bets are interpreted as 0.
|
||||
writeln("CHICKEN!!");
|
||||
} // bet is now > 0.
|
||||
|
||||
writeln(cards[Hand.middle]);
|
||||
if (cards[Hand.low] < cards[Hand.middle] && cards[Hand.middle] < cards[Hand.high])
|
||||
{
|
||||
writeln("YOU WIN!!!");
|
||||
cash += bet;
|
||||
}
|
||||
else
|
||||
{
|
||||
writeln("Sorry, you lose.");
|
||||
cash -= bet;
|
||||
if (cash <= 0)
|
||||
{
|
||||
writeln("\n\nSorry, friend, but you blew your wad.");
|
||||
write("\n\nTry again (Yes or No)? ");
|
||||
play = readString.toUpper == "YES";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
writeln("O.K., hope you had fun!");
|
||||
}
|
||||
|
||||
struct Card
|
||||
{
|
||||
int value = 2;
|
||||
alias value this; // Enables Card to stand in as an int, so that cards can be compared as ints.
|
||||
|
||||
invariant
|
||||
{
|
||||
assert(2 <= value && value <= 14); // Ensure cards always have a valid value.
|
||||
}
|
||||
|
||||
/// Adopt a new value.
|
||||
void drawNew()
|
||||
{
|
||||
import std.random : uniform;
|
||||
|
||||
value = uniform!("[]", int, int)(2, 14); // A random int between inclusive bounds.
|
||||
}
|
||||
|
||||
/// Called for implicit conversion to string.
|
||||
string toString() const pure
|
||||
{
|
||||
import std.conv : text;
|
||||
|
||||
switch (value)
|
||||
{
|
||||
case 11: return "Jack";
|
||||
case 12: return "Queen";
|
||||
case 13: return "King";
|
||||
case 14: return "Ace";
|
||||
default: return text(" ", value); // Basic prepends a space.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Read a string from standard input, stripping newline and other enclosing whitespace.
|
||||
string readString() nothrow
|
||||
{
|
||||
import std.string : strip;
|
||||
|
||||
try
|
||||
return trustedReadln.strip;
|
||||
catch (Exception) // readln throws on I/O and Unicode errors, which we handle here.
|
||||
return "";
|
||||
}
|
||||
|
||||
/** An @trusted wrapper around readln.
|
||||
*
|
||||
* This is the only function that formally requires manual review for memory-safety.
|
||||
* [Arguably readln should be safe already](https://forum.dlang.org/post/rab398$1up$1@digitalmars.com)
|
||||
* which would remove the need to have any @trusted code in this program.
|
||||
*/
|
||||
string trustedReadln() @trusted
|
||||
{
|
||||
import std.stdio : readln;
|
||||
|
||||
return readln;
|
||||
}
|
||||
104
01_Acey_Ducey/d/aceyducey_literal.d
Normal file
104
01_Acey_Ducey/d/aceyducey_literal.d
Normal file
@@ -0,0 +1,104 @@
|
||||
void main()
|
||||
{
|
||||
import std;
|
||||
|
||||
L10: writef("%26s", ' '); writeln("ACEY DUCEY CARD GAME");
|
||||
L20: writef("%15s", ' '); writeln("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
|
||||
L21: writeln;
|
||||
L22: writeln;
|
||||
L30: writeln("ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER ");
|
||||
L40: writeln("THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP");
|
||||
L50: writeln("YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING");
|
||||
L60: writeln("ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE");
|
||||
L70: writeln("A VALUE BETWEEN THE FIRST TWO.");
|
||||
L80: writeln("IF YOU DO NOT WANT TO BET, INPUT A 0");
|
||||
L100: int N=100;
|
||||
L110: int Q=100, M;
|
||||
L120: writeln("YOU NOW HAVE ",Q," DOLLARS.");
|
||||
L130: writeln;
|
||||
L140: goto L260;
|
||||
L210: Q=Q+M;
|
||||
L220: goto L120;
|
||||
L240: Q=Q-M;
|
||||
L250: goto L120;
|
||||
L260: writeln("HERE ARE YOUR NEXT TWO CARDS: ");
|
||||
L270: auto A=to!int(14*uniform01)+2;
|
||||
L280: if (A<2) goto L270;
|
||||
L290: if (A>14) goto L270;
|
||||
L300: auto B=to!int(14*uniform01)+2;
|
||||
L310: if (B<2) goto L300;
|
||||
L320: if (B>14) goto L300;
|
||||
L330: if (A>=B) goto L270;
|
||||
L350: if (A<11) goto L400;
|
||||
L360: if (A==11) goto L420;
|
||||
L370: if (A==12) goto L440;
|
||||
L380: if (A==13) goto L460;
|
||||
L390: if (A==14) goto L480;
|
||||
L400: writefln("%2d", A);
|
||||
L410: goto L500;
|
||||
L420: writeln("JACK");
|
||||
L430: goto L500;
|
||||
L440: writeln("QUEEN");
|
||||
L450: goto L500;
|
||||
L460: writeln("KING");
|
||||
L470: goto L500;
|
||||
L480: writeln("ACE");
|
||||
L500: if (B<11) goto L550;
|
||||
L510: if (B==11) goto L570;
|
||||
L520: if (B==12) goto L590;
|
||||
L530: if (B==13) goto L610;
|
||||
L540: if (B==14) goto L630;
|
||||
L550: writefln("%2d", B);
|
||||
L560: goto L650;
|
||||
L570: writeln("JACK");
|
||||
L580: goto L650;
|
||||
L590: writeln("QUEEN");
|
||||
L600: goto L650;
|
||||
L610: writeln("KING");
|
||||
L620: goto L650;
|
||||
L630: writeln("ACE");
|
||||
L640: writeln;
|
||||
L650: writeln;
|
||||
L660: write("WHAT IS YOUR BET? "); M = stdin.readln.strip.to!int;
|
||||
L670: if (M!=0) goto L680;
|
||||
L675: writeln("CHICKEN!!");
|
||||
L676: writeln;
|
||||
L677: goto L260;
|
||||
L680: if (M<=Q) goto L730;
|
||||
L690: writeln("SORRY, MY FRIEND, BUT YOU BET TOO MUCH.");
|
||||
L700: writeln("YOU HAVE ONLY ",Q," DOLLARS TO BET.");
|
||||
L710: goto L650;
|
||||
L730: auto C=to!int(14*uniform01)+2;
|
||||
L740: if (C<2) goto L730;
|
||||
L750: if (C>14) goto L730;
|
||||
L760: if (C<11) goto L810;
|
||||
L770: if (C==11) goto L830;
|
||||
L780: if (C==12) goto L850;
|
||||
L790: if (C==13) goto L870;
|
||||
L800: if (C==14) goto L890;
|
||||
L810: writeln(C);
|
||||
L820: goto L910;
|
||||
L830: writeln("JACK");
|
||||
L840: goto L910;
|
||||
L850: writeln("QUEEN");
|
||||
L860: goto L910;
|
||||
L870: writeln("KING");
|
||||
L880: goto L910;
|
||||
L890: writeln( "ACE");
|
||||
L900: writeln;
|
||||
L910: if (C>A) goto L930;
|
||||
L920: goto L970;
|
||||
L930: if (C>=B) goto L970;
|
||||
L950: writeln("YOU WIN!!!");
|
||||
L960: goto L210;
|
||||
L970: writeln("SORRY, YOU LOSE");
|
||||
L980: if (M<Q) goto L240;
|
||||
L990: writeln;
|
||||
L1000: writeln;
|
||||
L1010: writeln("SORRY, FRIEND, BUT YOU BLEW YOUR WAD.");
|
||||
L1015: writeln;writeln;
|
||||
L1020: write("TRY AGAIN (YES OR NO)? "); auto AS=stdin.readln;
|
||||
L1025: writeln;writeln;
|
||||
L1030: if (AS.strip.toUpper=="YES") goto L110;
|
||||
L1040: writeln("O.K., HOPE YOU HAD FUN!");
|
||||
}
|
||||
9
01_Acey_Ducey/java/README.md
Normal file
9
01_Acey_Ducey/java/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
|
||||
|
||||
Conversion to [Oracle Java](https://openjdk.java.net/)
|
||||
|
||||
Two versions of Acey Ducey have been contributed.
|
||||
|
||||
The original upload supported JDK 8/JDK 11 and uses multiple files and the second uses features in JDK 17 and is implemented in a single file AceyDucey17.java.
|
||||
|
||||
Both are in the src folder.
|
||||
@@ -167,6 +167,6 @@ public class AceyDucey {
|
||||
System.out.println("YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING");
|
||||
System.out.println("ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE");
|
||||
System.out.println("A VALUE BETWEEN THE FIRST TWO.");
|
||||
System.out.println("IF YOU DO NOT WANT TO BET, INPUT A 0");
|
||||
System.out.println("IF YOU DO NOT WANT TO BET, INPUT: 0");
|
||||
}
|
||||
}
|
||||
201
01_Acey_Ducey/java/src/AceyDucey17.java
Normal file
201
01_Acey_Ducey/java/src/AceyDucey17.java
Normal file
@@ -0,0 +1,201 @@
|
||||
import java.util.Random;
|
||||
import java.util.Scanner;
|
||||
|
||||
/**
|
||||
* A modern version (JDK17) of ACEY DUCEY using post Java 8 features. Notes
|
||||
* regarding new java features or differences in the original basic
|
||||
* implementation are numbered and at the bottom of this code.
|
||||
* The goal is to recreate the exact look and feel of the original program
|
||||
* minus a large glaring bug in the original code that lets you cheat.
|
||||
*/
|
||||
public class AceyDucey17 {
|
||||
|
||||
public static void main(String[] args) {
|
||||
// notes [1]
|
||||
System.out.println("""
|
||||
ACEY DUCEY CARD GAME
|
||||
CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
|
||||
|
||||
|
||||
ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER
|
||||
THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP
|
||||
YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING
|
||||
ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE
|
||||
A VALUE BETWEEN THE FIRST TWO.
|
||||
IF YOU DO NOT WANT TO BET, INPUT A 0""");
|
||||
|
||||
do {
|
||||
playGame();
|
||||
} while (stillInterested());
|
||||
System.out.println("O.K., HOPE YOU HAD FUN!");
|
||||
}
|
||||
|
||||
public static void playGame() {
|
||||
int cashOnHand = 100; // our only mutable variable note [11]
|
||||
System.out.println("YOU NOW HAVE "+ cashOnHand +" DOLLARS.");// note [6]
|
||||
while (cashOnHand > 0) {
|
||||
System.out.println();
|
||||
System.out.println("HERE ARE YOUR NEXT TWO CARDS:");
|
||||
|
||||
final Card lowCard = Card.getRandomCard(2, Card.KING); //note [3]
|
||||
System.out.println(lowCard);
|
||||
final Card highCard = Card.getRandomCard(lowCard.rank() + 1, Card.ACE);
|
||||
System.out.println(highCard);
|
||||
|
||||
final int bet = getBet(cashOnHand);
|
||||
final int winnings = determineWinnings(lowCard,highCard,bet);
|
||||
cashOnHand += winnings;
|
||||
if(winnings != 0 || cashOnHand != 0){ //note [2]
|
||||
System.out.println("YOU NOW HAVE "+ cashOnHand +" DOLLARS.");//note [6]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static int determineWinnings(Card lowCard, Card highCard, int bet){
|
||||
if (bet <= 0) { // note [5]
|
||||
System.out.println("CHICKEN!!");
|
||||
return 0;
|
||||
}
|
||||
Card nextCard = Card.getRandomCard(2, Card.ACE);
|
||||
System.out.println(nextCard);
|
||||
if(nextCard.between(lowCard,highCard)){
|
||||
System.out.println("YOU WIN!!!");
|
||||
return bet;
|
||||
}
|
||||
System.out.println("SORRY, YOU LOSE");
|
||||
return -bet;
|
||||
}
|
||||
|
||||
public static boolean stillInterested(){
|
||||
System.out.println();
|
||||
System.out.println();
|
||||
System.out.println("SORRY, FRIEND, BUT YOU BLEW YOUR WAD.");
|
||||
System.out.println();
|
||||
System.out.println();
|
||||
System.out.print("TRY AGAIN (YES OR NO)? ");
|
||||
Scanner input = new Scanner(System.in);
|
||||
boolean playAgain = input.nextLine()
|
||||
.toUpperCase()
|
||||
.startsWith("Y"); // note [9]
|
||||
System.out.println();
|
||||
System.out.println();
|
||||
return playAgain;
|
||||
}
|
||||
|
||||
public static int getBet(int cashOnHand){
|
||||
int bet;
|
||||
do{
|
||||
System.out.println();
|
||||
System.out.print("WHAT IS YOUR BET? ");
|
||||
bet = inputNumber();
|
||||
if (bet > cashOnHand) {
|
||||
System.out.println("SORRY, MY FRIEND, BUT YOU BET TOO MUCH.");
|
||||
System.out.println("YOU HAVE ONLY "+cashOnHand+" DOLLARS TO BET.");
|
||||
}
|
||||
}while(bet > cashOnHand);
|
||||
return bet;
|
||||
}
|
||||
|
||||
public static int inputNumber() {
|
||||
final Scanner input = new Scanner(System.in);
|
||||
// set to negative to mark as not entered yet in case of input error.
|
||||
int number = -1;
|
||||
while (number < 0) {
|
||||
try {
|
||||
number = input.nextInt();
|
||||
} catch(Exception ex) { // note [7]
|
||||
System.out.println("!NUMBER EXPECTED - RETRY INPUT LINE");
|
||||
System.out.print("? ");
|
||||
try{
|
||||
input.nextLine();
|
||||
}
|
||||
catch(Exception ns_ex){ // received EOF (ctrl-d or ctrl-z if windows)
|
||||
System.out.println("END OF INPUT, STOPPING PROGRAM.");
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return number;
|
||||
}
|
||||
|
||||
record Card(int rank){
|
||||
// Some constants to describe face cards.
|
||||
public static final int JACK = 11, QUEEN = 12, KING = 13, ACE = 14;
|
||||
private static final Random random = new Random();
|
||||
|
||||
public static Card getRandomCard(int from, int to){
|
||||
return new Card(random.nextInt(from, to+1)); // note [4]
|
||||
}
|
||||
|
||||
public boolean between(Card lower, Card higher){
|
||||
return lower.rank() < this.rank() && this.rank() < higher.rank();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() { // note [13]
|
||||
return switch (rank) {
|
||||
case JACK -> "JACK";
|
||||
case QUEEN -> "QUEEN";
|
||||
case KING -> "KING";
|
||||
case ACE -> "ACE\n"; // note [10]
|
||||
default -> " "+rank+" "; // note [6]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Notes:
|
||||
1. Multiline strings, a.k.a. text blocks, were added in JDK15.
|
||||
2. The original game only displays the players balance if it changed,
|
||||
which it does not when the player chickens out and bets zero.
|
||||
It also doesn't display the balance when it becomes zero because it has
|
||||
a more appropriate message: Sorry, You Lose.
|
||||
3. To pick two cards to show, the original BASIC implementation has a
|
||||
bug that could cause a race condition if the RND function never chose
|
||||
a lower number first and higher number second. It loops infinitely
|
||||
re-choosing random numbers until the condition is met of the first
|
||||
one being lower. The logic is changed a bit here so that the first
|
||||
card picked is anything but an ACE, the highest possible card,
|
||||
and then the second card is between the just picked first card upto
|
||||
and including the ACE.
|
||||
4. Random.nextInt(origin, bound) was added in JDK17, and allows to
|
||||
directly pick a range for a random integer to be generated. The second
|
||||
parameter is exclusive of the range and thus why they are stated with
|
||||
+1's to the face card.
|
||||
5. The original BASIC implementation has a bug that allows negative value
|
||||
bets. Since you can't bet MORE cash than you have you can always bet
|
||||
less including a very, very large negative value. You would do this when
|
||||
the chances of winning are slim or zero since losing a hand SUBTRACTS
|
||||
your bet from your cash; subtracting a negative number actually ADDS
|
||||
to your cash, potentially making you an instant billionaire.
|
||||
This loophole is now closed.
|
||||
6. The subtle behavior of the BASIC PRINT command causes a space to be
|
||||
printed before all positive numbers as well as a trailing space. Any
|
||||
place a non-face card or the players balance is printed has extra space
|
||||
to mimic this behavior.
|
||||
7. Errors on input were probably specific to the interpreter. This program
|
||||
tries to match the Vintage Basic interpreter's error messages. The final
|
||||
input.nextLine() command exists to clear the blockage of whatever
|
||||
non-number input was entered. But even that could fail if the user
|
||||
types Ctrl-D (windows Ctrl-Z), signifying an EOF (end of file) and thus
|
||||
the closing of STDIN channel. The original program on an EOF signal prints
|
||||
"END OF INPUT IN LINE 660" and thus we cover it roughly the same way.
|
||||
All of this is necessary to avoid a messy stack trace from being
|
||||
printed as the program crashes.
|
||||
9. The original game only accepted a full upper case "YES" to continue
|
||||
playing if bankrupted. This program is more lenient and will accept
|
||||
any input that starts with the letter 'y', uppercase or not.
|
||||
10. The original game prints an extra blank line if the card is an ACE. There
|
||||
is seemingly no rationale for this.
|
||||
11. Modern java best practices are edging toward a more functional paradigm
|
||||
and as such, mutating state is discouraged. All other variables besides
|
||||
the cashOnHand are final and initialized only once.
|
||||
12. Refactoring of the concept of a card is done with a record. Records were
|
||||
introduced in JDK14. Card functionality is encapsulated in this example
|
||||
of a record. An enum could be a better alternative since there are
|
||||
technically only 13 cards possible.
|
||||
13. Switch expressions were introduced as far back as JDK12 but continue to
|
||||
be refined for clarity, exhaustiveness. As of JDK17 pattern matching
|
||||
for switch expressions can be accessed by enabling preview features.
|
||||
*/
|
||||
}
|
||||
6
01_Acey_Ducey/javascript/.prettierrc.json
Normal file
6
01_Acey_Ducey/javascript/.prettierrc.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"trailingComma": "es5",
|
||||
"tabWidth": 4,
|
||||
"semi": true,
|
||||
"singleQuote": true
|
||||
}
|
||||
7
01_Acey_Ducey/javascript/aceyducey.html
Normal file
7
01_Acey_Ducey/javascript/aceyducey.html
Normal file
@@ -0,0 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<title>ACEY DUCEY</title>
|
||||
|
||||
<pre id="output" style="font-size: 12pt"></pre>
|
||||
<script src="aceyducey.js"></script>
|
||||
220
01_Acey_Ducey/javascript/aceyducey.js
Normal file
220
01_Acey_Ducey/javascript/aceyducey.js
Normal file
@@ -0,0 +1,220 @@
|
||||
// UTILITY VARIABLES
|
||||
|
||||
// By default:
|
||||
// — Browsers have a window object
|
||||
// — Node.js does not
|
||||
// Checking for an undefined window object is a loose check
|
||||
// to enable browser and Node.js support
|
||||
const isRunningInBrowser = typeof window !== 'undefined';
|
||||
|
||||
// To easily validate input strings with utility functions
|
||||
const validLowerCaseYesStrings = ['yes', 'y'];
|
||||
const validLowerCaseNoStrings = ['no', 'n'];
|
||||
const validLowerCaseYesAndNoStrings = [
|
||||
...validLowerCaseYesStrings,
|
||||
...validLowerCaseNoStrings,
|
||||
];
|
||||
// UTILITY VARIABLES
|
||||
|
||||
// Function to get a random number (card) 2-14 (ACE is 14)
|
||||
function getRandomCard() {
|
||||
// In our game, the value of ACE is greater than face cards;
|
||||
// instead of having the value of ACE be 1, we’ll have it be 14.
|
||||
// So, we want to shift the range of random numbers from 1-13 to 2-14
|
||||
let min = 2;
|
||||
let max = 14;
|
||||
// Return random integer between two values, inclusive
|
||||
return Math.floor(Math.random() * (max - min + 1) + min);
|
||||
}
|
||||
|
||||
function newGameCards() {
|
||||
let cardOne = getRandomCard();
|
||||
let cardTwo = getRandomCard();
|
||||
let cardThree = getRandomCard();
|
||||
// We want:
|
||||
// 1. cardOne and cardTwo to be different cards
|
||||
// 2. cardOne to be lower than cardTwo
|
||||
// So, while cardOne is greater than or equal too cardTwo
|
||||
// we will continue to generate random cards.
|
||||
while (cardOne >= cardTwo) {
|
||||
cardOne = getRandomCard();
|
||||
cardTwo = getRandomCard();
|
||||
}
|
||||
return [cardOne, cardTwo, cardThree];
|
||||
}
|
||||
|
||||
// Function to get card value
|
||||
function getCardValue(card) {
|
||||
let faceOrAce = {
|
||||
11: 'JACK',
|
||||
12: 'QUEEN',
|
||||
13: 'KING',
|
||||
14: 'ACE',
|
||||
};
|
||||
// If card value matches a key in faceOrAce, use faceOrAce value;
|
||||
// Else, return undefined and handle with the Nullish Coalescing Operator (??)
|
||||
// and default to card value.
|
||||
let cardValue = faceOrAce[card] ?? card;
|
||||
return cardValue;
|
||||
}
|
||||
|
||||
print(spaces(26) + 'ACEY DUCEY CARD GAME');
|
||||
print(spaces(15) + 'CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n');
|
||||
print('ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER');
|
||||
print('THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP');
|
||||
print('YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING');
|
||||
print('ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE');
|
||||
print('A VALUE BETWEEN THE FIRST TWO.');
|
||||
print("IF YOU DO NOT WANT TO BET, INPUT '0'");
|
||||
|
||||
main();
|
||||
|
||||
async function main() {
|
||||
let bet;
|
||||
let availableDollars = 100;
|
||||
|
||||
// Loop game forever
|
||||
while (true) {
|
||||
let [cardOne, cardTwo, cardThree] = newGameCards();
|
||||
|
||||
print(`YOU NOW HAVE ${availableDollars} DOLLARS.\n`);
|
||||
|
||||
print('HERE ARE YOUR NEXT TWO CARDS: ');
|
||||
print(getCardValue(cardOne));
|
||||
print(getCardValue(cardTwo));
|
||||
print('');
|
||||
|
||||
// Loop until receiving a valid bet
|
||||
let validBet = false;
|
||||
while (!validBet) {
|
||||
print('\nWHAT IS YOUR BET? ');
|
||||
bet = parseInt(await input(), 10);
|
||||
let minimumRequiredBet = 0;
|
||||
if (bet >= minimumRequiredBet) {
|
||||
if (bet > availableDollars) {
|
||||
print('SORRY, MY FRIEND, BUT YOU BET TOO MUCH.');
|
||||
print(`YOU HAVE ONLY ${availableDollars} DOLLARS TO BET.`);
|
||||
} else {
|
||||
validBet = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bet == 0)
|
||||
{
|
||||
// User chose not to bet.
|
||||
print('CHICKEN!!');
|
||||
print('');
|
||||
// Don't draw a third card, draw a new set of 2 cards.
|
||||
continue;
|
||||
}
|
||||
|
||||
print('\n\nHERE IS THE CARD WE DREW: ');
|
||||
print(getCardValue(cardThree));
|
||||
|
||||
// Determine if player won or lost
|
||||
if (cardThree > cardOne && cardThree < cardTwo) {
|
||||
print('YOU WIN!!!');
|
||||
availableDollars = availableDollars + bet;
|
||||
} else {
|
||||
print('SORRY, YOU LOSE');
|
||||
|
||||
if (bet >= availableDollars) {
|
||||
print('');
|
||||
print('');
|
||||
print('SORRY, FRIEND, BUT YOU BLEW YOUR WAD.');
|
||||
print('');
|
||||
print('');
|
||||
print('TRY AGAIN (YES OR NO)');
|
||||
|
||||
let tryAgainInput = await input();
|
||||
|
||||
print('');
|
||||
print('');
|
||||
|
||||
if (isValidYesString(tryAgainInput)) {
|
||||
availableDollars = 100;
|
||||
} else {
|
||||
print('O.K., HOPE YOU HAD FUN!');
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
availableDollars = availableDollars - bet;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// UTILITY FUNCTIONS
|
||||
function isValidYesNoString(string) {
|
||||
return validLowerCaseYesAndNoStrings.includes(string.toLowerCase());
|
||||
}
|
||||
|
||||
function isValidYesString(string) {
|
||||
return validLowerCaseYesStrings.includes(string.toLowerCase());
|
||||
}
|
||||
|
||||
function isValidNoString(string) {
|
||||
return validLowerCaseNoStrings.includes(string.toLowerCase());
|
||||
}
|
||||
|
||||
function print(string) {
|
||||
if (isRunningInBrowser) {
|
||||
// Adds trailing newline to match console.log behavior
|
||||
document
|
||||
.getElementById('output')
|
||||
.appendChild(document.createTextNode(string + '\n'));
|
||||
} else {
|
||||
console.log(string);
|
||||
}
|
||||
}
|
||||
|
||||
function input() {
|
||||
if (isRunningInBrowser) {
|
||||
// Accept input from the browser DOM input
|
||||
return new Promise((resolve) => {
|
||||
const outputElement = document.querySelector('#output');
|
||||
const inputElement = document.createElement('input');
|
||||
outputElement.append(inputElement);
|
||||
inputElement.focus();
|
||||
|
||||
inputElement.addEventListener('keydown', (event) => {
|
||||
if (event.key === 'Enter') {
|
||||
const result = inputElement.value;
|
||||
inputElement.remove();
|
||||
print(result);
|
||||
print('');
|
||||
resolve(result);
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
// Accept input from the command line in Node.js
|
||||
// See: https://nodejs.dev/learn/accept-input-from-the-command-line-in-nodejs
|
||||
return new Promise(function (resolve) {
|
||||
const readline = require('readline').createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout,
|
||||
});
|
||||
readline.question('', function (input) {
|
||||
resolve(input);
|
||||
readline.close();
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function printInline(string) {
|
||||
if (isRunningInBrowser) {
|
||||
document
|
||||
.getElementById('output')
|
||||
.appendChild(document.createTextNode(string));
|
||||
} else {
|
||||
process.stdout.write(string);
|
||||
}
|
||||
}
|
||||
|
||||
function spaces(numberOfSpaces) {
|
||||
return ' '.repeat(numberOfSpaces);
|
||||
}
|
||||
|
||||
// UTILITY FUNCTIONS
|
||||
36
01_Acey_Ducey/pascal/.gitattributes
vendored
Normal file
36
01_Acey_Ducey/pascal/.gitattributes
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
# Set the default behavior, in case people don't have core.autocrlf set.
|
||||
* text=auto
|
||||
|
||||
# Explicitly declare text files you want to always be normalized and converted
|
||||
# to native line endings on checkout.
|
||||
*.inc text
|
||||
*.pas text
|
||||
*.pp text
|
||||
*.lpk text
|
||||
*.lpi text
|
||||
*.lps text
|
||||
*.lpr text
|
||||
*.def text
|
||||
*.css text
|
||||
*.html text
|
||||
*.xml text
|
||||
*.sql text
|
||||
|
||||
# Declare files that will always have CRLF line endings on checkout.
|
||||
*.dpk text eol=crlf
|
||||
*.dproj text eol=crlf
|
||||
|
||||
# Declare files that will always have LF line endings on checkout.
|
||||
|
||||
|
||||
# Denote all files that are truly binary and should not be modified.
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
*.exe binary
|
||||
*.res binary
|
||||
*.ico binary
|
||||
*.dll binary
|
||||
|
||||
# Keep these files from archive/exports, mainly from production.
|
||||
.gitignore export-ignore
|
||||
.gitattributes export-ignore
|
||||
63
01_Acey_Ducey/pascal/.gitignore
vendored
Normal file
63
01_Acey_Ducey/pascal/.gitignore
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
# Basic Computer Programs project specific
|
||||
aceyducey
|
||||
aceyducey.exe
|
||||
|
||||
# Compiled l10n files: .mo should be ignored
|
||||
*.mo
|
||||
|
||||
# Ghostwriter backups
|
||||
*.backup
|
||||
|
||||
# nano editor backup files
|
||||
*.swp
|
||||
|
||||
# Uncomment these types if you want even more clean repository. But be careful.
|
||||
# It can make harm to an existing project source. Read explanations below.
|
||||
#
|
||||
# Resource files are binaries containing manifest, project icon and version info.
|
||||
# They can not be viewed as text or compared by diff-tools. Consider replacing them with .rc files.
|
||||
*.res
|
||||
|
||||
# Delphi/Lazarus compiler-generated binaries (safe to delete)
|
||||
*.exe
|
||||
*.dll
|
||||
*.bpl
|
||||
*.bpi
|
||||
*.dcp
|
||||
*.so
|
||||
*.apk
|
||||
*.drc
|
||||
*.map
|
||||
*.dres
|
||||
*.rsm
|
||||
*.tds
|
||||
*.dcu
|
||||
*.lib
|
||||
*.[ao]
|
||||
*.or
|
||||
*.ppu
|
||||
*.dbg
|
||||
*.compiled
|
||||
|
||||
# Delphi autogenerated files (duplicated info)
|
||||
*.cfg
|
||||
*Resource.rc
|
||||
|
||||
# Delphi local files (user-specific info)
|
||||
*.local
|
||||
*.identcache
|
||||
*.projdata
|
||||
*.tvsconfig
|
||||
*.dsk
|
||||
|
||||
# Delphi history and backups
|
||||
__history/
|
||||
*.~*
|
||||
|
||||
# Lazarus history, backups and session
|
||||
backup/
|
||||
*.bak
|
||||
*.lps
|
||||
|
||||
# Castalia statistics file
|
||||
*.stat
|
||||
3
01_Acey_Ducey/pascal/README.md
Normal file
3
01_Acey_Ducey/pascal/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
|
||||
|
||||
Conversion to [Pascal](https://en.wikipedia.org/wiki/Pascal_(programming_language)) by Gustavo Carreno [gcarreno@github](https://github.com/gcarreno)
|
||||
68
01_Acey_Ducey/pascal/object-pascal/aceyducey.lpi
Normal file
68
01_Acey_Ducey/pascal/object-pascal/aceyducey.lpi
Normal file
@@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<CONFIG>
|
||||
<ProjectOptions>
|
||||
<Version Value="11"/>
|
||||
<General>
|
||||
<Flags>
|
||||
<MainUnitHasCreateFormStatements Value="False"/>
|
||||
<MainUnitHasTitleStatement Value="False"/>
|
||||
<MainUnitHasScaledStatement Value="False"/>
|
||||
</Flags>
|
||||
<SessionStorage Value="InProjectDir"/>
|
||||
<MainUnit Value="0"/>
|
||||
<Title Value="aceyducey"/>
|
||||
<UseAppBundle Value="False"/>
|
||||
<ResourceType Value="res"/>
|
||||
</General>
|
||||
<BuildModes Count="1">
|
||||
<Item1 Name="Default" Default="True"/>
|
||||
</BuildModes>
|
||||
<PublishOptions>
|
||||
<Version Value="2"/>
|
||||
<UseFileFilters Value="True"/>
|
||||
</PublishOptions>
|
||||
<RunParams>
|
||||
<FormatVersion Value="2"/>
|
||||
<Modes Count="0"/>
|
||||
</RunParams>
|
||||
<Units Count="3">
|
||||
<Unit0>
|
||||
<Filename Value="aceyducey.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
</Unit0>
|
||||
<Unit1>
|
||||
<Filename Value="game.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
<UnitName Value="Game"/>
|
||||
</Unit1>
|
||||
<Unit2>
|
||||
<Filename Value="deck.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
<UnitName Value="Deck"/>
|
||||
</Unit2>
|
||||
</Units>
|
||||
</ProjectOptions>
|
||||
<CompilerOptions>
|
||||
<Version Value="11"/>
|
||||
<Target>
|
||||
<Filename Value="aceyducey"/>
|
||||
</Target>
|
||||
<SearchPaths>
|
||||
<IncludeFiles Value="$(ProjOutDir)"/>
|
||||
<UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
|
||||
</SearchPaths>
|
||||
</CompilerOptions>
|
||||
<Debugging>
|
||||
<Exceptions Count="3">
|
||||
<Item1>
|
||||
<Name Value="EAbort"/>
|
||||
</Item1>
|
||||
<Item2>
|
||||
<Name Value="ECodetoolError"/>
|
||||
</Item2>
|
||||
<Item3>
|
||||
<Name Value="EFOpenError"/>
|
||||
</Item3>
|
||||
</Exceptions>
|
||||
</Debugging>
|
||||
</CONFIG>
|
||||
19
01_Acey_Ducey/pascal/object-pascal/aceyducey.pas
Normal file
19
01_Acey_Ducey/pascal/object-pascal/aceyducey.pas
Normal file
@@ -0,0 +1,19 @@
|
||||
program aceyducey;
|
||||
|
||||
{$IFDEF FPC}
|
||||
{$mode objfpc}{$H+}
|
||||
{$ENDIF}
|
||||
|
||||
uses
|
||||
Game
|
||||
, Deck
|
||||
;
|
||||
|
||||
var
|
||||
Acey_Ducey: TGame;
|
||||
|
||||
begin
|
||||
Acey_Ducey:= TGame.Create;
|
||||
Acey_Ducey.Run;
|
||||
end.
|
||||
|
||||
111
01_Acey_Ducey/pascal/object-pascal/deck.pas
Normal file
111
01_Acey_Ducey/pascal/object-pascal/deck.pas
Normal file
@@ -0,0 +1,111 @@
|
||||
unit Deck;
|
||||
|
||||
{$IFDEF FPC}
|
||||
{$mode objfpc}{$H+}
|
||||
{$ENDIF}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes
|
||||
, SysUtils
|
||||
;
|
||||
|
||||
type
|
||||
{ TDeck }
|
||||
TDeck = class
|
||||
private
|
||||
FDealerLow: Integer;
|
||||
FDealerHigh: Integer;
|
||||
FPlayer: Integer;
|
||||
|
||||
procedure PrintCard(const ACard: Integer);
|
||||
protected
|
||||
public
|
||||
property DealerLow: Integer
|
||||
read FDealerLow;
|
||||
property DealerHigh: Integer
|
||||
read FDealerHigh;
|
||||
property Player: Integer
|
||||
read FPlayer;
|
||||
|
||||
procedure DrawCards;
|
||||
procedure ShowDealerCards;
|
||||
procedure ShowPlayerCard;
|
||||
function PlayerWins: Boolean;
|
||||
published
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TDeck }
|
||||
|
||||
procedure TDeck.PrintCard(const ACard: Integer);
|
||||
begin
|
||||
if ACard < 11 then
|
||||
begin
|
||||
Write(ACard);
|
||||
end;
|
||||
if ACard = 11 then
|
||||
begin
|
||||
Write('JACK');
|
||||
end;
|
||||
if ACard = 12 then
|
||||
begin
|
||||
Write('QUEEN');
|
||||
end;
|
||||
if ACard = 13 then
|
||||
begin
|
||||
Write('KING');
|
||||
end;
|
||||
if ACard = 14 then
|
||||
begin
|
||||
Write('ACE');
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TDeck.DrawCards;
|
||||
var
|
||||
tmp: Integer;
|
||||
begin
|
||||
repeat
|
||||
FDealerLow:= Random(14) + 2;
|
||||
until (FDealerLow >= 2) and (FDealerLow <= 14);
|
||||
repeat
|
||||
FDealerHigh:= Random(14) + 2;
|
||||
until (FDealerHigh >= 2) and (FDealerHigh <= 14) and (FDealerLow <> FDealerHigh);
|
||||
if FDealerLow > FDealerHigh then
|
||||
begin
|
||||
tmp:= FDealerHigh;
|
||||
FDealerHigh:= FDealerLow;
|
||||
FDealerLow:= tmp;
|
||||
end;
|
||||
repeat
|
||||
FPlayer:= Random(14) + 2;
|
||||
until (FPlayer >= 2) and (FPlayer <= 14);
|
||||
end;
|
||||
|
||||
procedure TDeck.ShowDealerCards;
|
||||
begin
|
||||
Write('HERE ARE YOUR NEXT TWO CARDS: ');
|
||||
PrintCard(FDealerLow);
|
||||
Write(' ');
|
||||
PrintCard(FDealerHigh);
|
||||
WriteLN;
|
||||
WriteLN;
|
||||
end;
|
||||
|
||||
procedure TDeck.ShowPlayerCard;
|
||||
begin
|
||||
PrintCard(FPlayer);
|
||||
WriteLN;
|
||||
WriteLN;
|
||||
end;
|
||||
|
||||
function TDeck.PlayerWins: Boolean;
|
||||
begin
|
||||
Result:= (FPlayer > FDealerLow) and (FPlayer < FDealerHigh);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
136
01_Acey_Ducey/pascal/object-pascal/game.pas
Normal file
136
01_Acey_Ducey/pascal/object-pascal/game.pas
Normal file
@@ -0,0 +1,136 @@
|
||||
unit Game;
|
||||
|
||||
{$IFDEF FPC}
|
||||
{$mode objfpc}{$H+}
|
||||
{$ENDIF}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes
|
||||
, SysUtils
|
||||
, Crt
|
||||
, Deck
|
||||
;
|
||||
|
||||
type
|
||||
{ TGame }
|
||||
TGame = class
|
||||
private
|
||||
FStash: Integer;
|
||||
FBet: Integer;
|
||||
FDeck: TDeck;
|
||||
|
||||
procedure PrintGreeting;
|
||||
procedure PrintBalance;
|
||||
function GetBet: Integer;
|
||||
function TryAgain: Boolean;
|
||||
protected
|
||||
public
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
|
||||
procedure Run;
|
||||
published
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TGame }
|
||||
|
||||
procedure TGame.PrintGreeting;
|
||||
begin
|
||||
WriteLN(' ':26, 'ACEY DUCEY CARD GAME');
|
||||
WriteLN(' ':15, 'CREATIVE COMPUTING MORRISTOWN, NEW JERSEY');
|
||||
WriteLN;
|
||||
WriteLN;
|
||||
WriteLN('ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER ');
|
||||
WriteLN('THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP');
|
||||
WriteLN('YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING');
|
||||
WriteLN('ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE');
|
||||
WriteLN('A VALUE BETWEEN THE FIRST TWO.');
|
||||
WriteLN('IF YOU DO NOT WANT TO BET, INPUT A 0');
|
||||
WriteLN;
|
||||
end;
|
||||
|
||||
procedure TGame.PrintBalance;
|
||||
begin
|
||||
WriteLN('YOU NOW HAVE ', FStash,' DOLLARS.');
|
||||
WriteLN;
|
||||
end;
|
||||
|
||||
function TGame.GetBet: Integer;
|
||||
begin
|
||||
Result:= 0;
|
||||
repeat
|
||||
Write('WHAT IS YOUR BET: ');
|
||||
ReadLN(Result);
|
||||
if Result > FStash then
|
||||
begin
|
||||
WriteLn('SORRY, MY FRIEND, BUT YOU BET TOO MUCH.');
|
||||
WriteLn('YOU HAVE ONLY ', FStash,' DOLLARS TO BET.');
|
||||
end;
|
||||
until (Result >=0) and (Result <= FStash);
|
||||
end;
|
||||
|
||||
function TGame.TryAgain: Boolean;
|
||||
var
|
||||
answer: String;
|
||||
begin
|
||||
Result:= False;
|
||||
Write('TRY AGAIN (YES OR NO)');
|
||||
ReadLn(answer);
|
||||
Result:= (LowerCase(answer)='yes') or (LowerCase(answer)='y');
|
||||
end;
|
||||
|
||||
constructor TGame.Create;
|
||||
begin
|
||||
Randomize;
|
||||
FDeck:= TDeck.Create;
|
||||
end;
|
||||
|
||||
destructor TGame.Destroy;
|
||||
begin
|
||||
FDeck.Free;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
procedure TGame.Run;
|
||||
begin
|
||||
ClrScr;
|
||||
PrintGreeting;
|
||||
repeat
|
||||
FStash:= 100;
|
||||
repeat
|
||||
PrintBalance;
|
||||
FDeck.DrawCards;
|
||||
//DrawDealerCards;
|
||||
FDeck.ShowDealerCards;
|
||||
FBet:= GetBet;
|
||||
if FBet = 0 then
|
||||
begin
|
||||
WriteLN('CHICKEN!!');
|
||||
continue;
|
||||
end;
|
||||
//DrawPlayerCard;
|
||||
FDeck.ShowPlayerCard;
|
||||
//if (FCardC > FCardA) and (FCardC < FCardB) then
|
||||
if FDeck.PlayerWins then
|
||||
begin
|
||||
WriteLN('YOU WIN!!!');
|
||||
Inc(FStash, FBet)
|
||||
end
|
||||
else
|
||||
begin
|
||||
WriteLN('SORRY, YOU LOSE');
|
||||
Dec(FStash, FBet)
|
||||
end;
|
||||
until FStash = 0;
|
||||
WriteLN('SORRY, FRIEND, BUT YOU BLEW YOUR WAD.');
|
||||
WriteLN;
|
||||
until not TryAgain;
|
||||
WriteLN('O.K., HOPE YOU HAD FUN!');
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
58
01_Acey_Ducey/pascal/simple/aceyducey.lpi
Normal file
58
01_Acey_Ducey/pascal/simple/aceyducey.lpi
Normal file
@@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<CONFIG>
|
||||
<ProjectOptions>
|
||||
<Version Value="11"/>
|
||||
<General>
|
||||
<Flags>
|
||||
<MainUnitHasCreateFormStatements Value="False"/>
|
||||
<MainUnitHasTitleStatement Value="False"/>
|
||||
<MainUnitHasScaledStatement Value="False"/>
|
||||
</Flags>
|
||||
<SessionStorage Value="InProjectDir"/>
|
||||
<MainUnit Value="0"/>
|
||||
<Title Value="aceyducey"/>
|
||||
<UseAppBundle Value="False"/>
|
||||
<ResourceType Value="res"/>
|
||||
</General>
|
||||
<BuildModes Count="1">
|
||||
<Item1 Name="Default" Default="True"/>
|
||||
</BuildModes>
|
||||
<PublishOptions>
|
||||
<Version Value="2"/>
|
||||
<UseFileFilters Value="True"/>
|
||||
</PublishOptions>
|
||||
<RunParams>
|
||||
<FormatVersion Value="2"/>
|
||||
<Modes Count="0"/>
|
||||
</RunParams>
|
||||
<Units Count="1">
|
||||
<Unit0>
|
||||
<Filename Value="aceyducey.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
</Unit0>
|
||||
</Units>
|
||||
</ProjectOptions>
|
||||
<CompilerOptions>
|
||||
<Version Value="11"/>
|
||||
<Target>
|
||||
<Filename Value="aceyducey"/>
|
||||
</Target>
|
||||
<SearchPaths>
|
||||
<IncludeFiles Value="$(ProjOutDir)"/>
|
||||
<UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
|
||||
</SearchPaths>
|
||||
</CompilerOptions>
|
||||
<Debugging>
|
||||
<Exceptions Count="3">
|
||||
<Item1>
|
||||
<Name Value="EAbort"/>
|
||||
</Item1>
|
||||
<Item2>
|
||||
<Name Value="ECodetoolError"/>
|
||||
</Item2>
|
||||
<Item3>
|
||||
<Name Value="EFOpenError"/>
|
||||
</Item3>
|
||||
</Exceptions>
|
||||
</Debugging>
|
||||
</CONFIG>
|
||||
152
01_Acey_Ducey/pascal/simple/aceyducey.pas
Normal file
152
01_Acey_Ducey/pascal/simple/aceyducey.pas
Normal file
@@ -0,0 +1,152 @@
|
||||
program aceyducey;
|
||||
|
||||
{$IFDEF FPC}
|
||||
{$mode objfpc}{$H+}
|
||||
{$ENDIF}
|
||||
|
||||
uses
|
||||
Crt;
|
||||
|
||||
var
|
||||
Stash: Integer;
|
||||
CardA: Integer;
|
||||
CardB: Integer;
|
||||
CardC: Integer;
|
||||
Bet: Integer;
|
||||
|
||||
procedure PrintGreeting;
|
||||
begin
|
||||
WriteLN(' ':26, 'ACEY DUCEY CARD GAME');
|
||||
WriteLN(' ':15, 'CREATIVE COMPUTING MORRISTOWN, NEW JERSEY');
|
||||
WriteLN;
|
||||
WriteLN;
|
||||
WriteLN('ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER ');
|
||||
WriteLN('THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP');
|
||||
WriteLN('YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING');
|
||||
WriteLN('ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE');
|
||||
WriteLN('A VALUE BETWEEN THE FIRST TWO.');
|
||||
WriteLN('IF YOU DO NOT WANT TO BET, INPUT A 0');
|
||||
WriteLN;
|
||||
end;
|
||||
|
||||
procedure PrintBalance;
|
||||
begin
|
||||
WriteLN('YOU NOW HAVE ', Stash,' DOLLARS.');
|
||||
WriteLN;
|
||||
end;
|
||||
|
||||
procedure PrintCard(const ACard: Integer);
|
||||
begin
|
||||
if ACard < 11 then
|
||||
begin
|
||||
Write(ACard);
|
||||
end;
|
||||
if ACard = 11 then
|
||||
begin
|
||||
Write('JACK');
|
||||
end;
|
||||
if ACard = 12 then
|
||||
begin
|
||||
Write('QUEEN');
|
||||
end;
|
||||
if ACard = 13 then
|
||||
begin
|
||||
Write('KING');
|
||||
end;
|
||||
if ACard = 14 then
|
||||
begin
|
||||
Write('ACE');
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure DrawDealerCards;
|
||||
var
|
||||
tmp: Integer;
|
||||
begin
|
||||
Write('HERE ARE YOUR NEXT TWO CARDS: ');
|
||||
repeat
|
||||
CardA:= Random(14) + 2;
|
||||
until (CardA >= 2) and (CardA <= 14);
|
||||
repeat
|
||||
CardB:= Random(14) + 2;
|
||||
until (CardB >= 2) and (CardB <= 14) and (CardA <> CardB);
|
||||
if CardA > CardB then
|
||||
begin
|
||||
tmp:= CardB;
|
||||
CardB:= CardA;
|
||||
CardA:= tmp;
|
||||
end;
|
||||
PrintCard(CardA);
|
||||
Write(' ');
|
||||
PrintCard(CardB);
|
||||
WriteLN;
|
||||
WriteLN;
|
||||
end;
|
||||
|
||||
procedure DrawPlayerCard;
|
||||
begin
|
||||
repeat
|
||||
CardC:= Random(14) + 2;
|
||||
until (CardC >= 2) and (CardC <= 14);
|
||||
PrintCard(CardC);
|
||||
WriteLN;
|
||||
WriteLN;
|
||||
end;
|
||||
|
||||
function GetBet: Integer;
|
||||
begin
|
||||
Result:= 0;
|
||||
repeat
|
||||
Write('WHAT IS YOUR BET: ');
|
||||
ReadLN(Result);
|
||||
if Result > Stash then
|
||||
begin
|
||||
WriteLn('SORRY, MY FRIEND, BUT YOU BET TOO MUCH.');
|
||||
WriteLn('YOU HAVE ONLY ', Stash,' DOLLARS TO BET.');
|
||||
end;
|
||||
until (Result >=0) and (Result <= Stash);
|
||||
end;
|
||||
|
||||
function TryAgain: Boolean;
|
||||
var
|
||||
answer: String;
|
||||
begin
|
||||
Result:= False;
|
||||
Write('TRY AGAIN (YES OR NO)');
|
||||
ReadLn(answer);
|
||||
Result:= (LowerCase(answer)='yes') or (LowerCase(answer)='y');
|
||||
end;
|
||||
|
||||
begin
|
||||
Randomize;
|
||||
ClrScr;
|
||||
PrintGreeting;
|
||||
repeat
|
||||
Stash:= 100;
|
||||
repeat
|
||||
PrintBalance;
|
||||
DrawDealerCards;
|
||||
Bet:= GetBet;
|
||||
if Bet = 0 then
|
||||
begin
|
||||
WriteLN('CHICKEN!!');
|
||||
continue;
|
||||
end;
|
||||
DrawPlayerCard;
|
||||
if (CardC > CardA) and (CardC < CardB) then
|
||||
begin
|
||||
WriteLN('YOU WIN!!!');
|
||||
Inc(Stash, Bet)
|
||||
end
|
||||
else
|
||||
begin
|
||||
WriteLN('SORRY, YOU LOSE');
|
||||
Dec(Stash, Bet)
|
||||
end;
|
||||
until Stash = 0;
|
||||
WriteLN('SORRY, FRIEND, BUT YOU BLEW YOUR WAD.');
|
||||
WriteLN;
|
||||
until not TryAgain;
|
||||
WriteLN('O.K., HOPE YOU HAD FUN!');
|
||||
end.
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
|
||||
|
||||
Conversion to [Raku](https://raku.org/)
|
||||
Conversion to [Perl](https://www.perl.org/)
|
||||
157
01_Acey_Ducey/perl/aceyducey.pl
Executable file
157
01_Acey_Ducey/perl/aceyducey.pl
Executable file
@@ -0,0 +1,157 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# The List::Util module is part of the core Perl distribution. Using this
|
||||
# means we don't need to re-invent the wheel and create a way to shuffle
|
||||
# a list.
|
||||
use List::Util qw(shuffle);
|
||||
|
||||
# Rather than put in a number of print (or say) statements here, we use a
|
||||
# "here document". This is very useful for long strings of text. In this
|
||||
# case, everything between the end of the "print" line and the line with
|
||||
# "END_INSTRUCTIONS" on it will be printed verbatim.
|
||||
print << 'END_INSTRUCTIONS';
|
||||
|
||||
Acey-Ducey
|
||||
Adapted from Creative Computing, Morristown, New Jersey
|
||||
|
||||
|
||||
Acey-Ducey is played as follows. The dealer (computer) deals two cards face up.
|
||||
You have an option to bet or not bet, depending on whether or not you feel that
|
||||
the next card drawn will have a value between the first two. Aces are low.
|
||||
|
||||
Bets must be in whole-dollar amounts only.
|
||||
|
||||
If you do not want to bet, input a 0. If you want to quit, input a -1.
|
||||
|
||||
END_INSTRUCTIONS
|
||||
|
||||
my @cards = ( 1 .. 13 ); # That is, Ace through King.
|
||||
my $keepPlaying = 1;
|
||||
|
||||
GAME:
|
||||
while ($keepPlaying) {
|
||||
my $playerBalance = 100; # The player starts with $100
|
||||
|
||||
HAND:
|
||||
while (1) {
|
||||
print "\nYou now have $playerBalance dollars.\n\n";
|
||||
|
||||
# We'll create a new array that is a shuffled version of the deck.
|
||||
my @shuffledDeck = shuffle(@cards);
|
||||
|
||||
# Then, by taking the two "top cards" off the deck, we're guaranteed
|
||||
# that those will be unique. This way we don't have to keep drawing
|
||||
# if we get, say, two queens. We sort them as we pull them to make
|
||||
# sure that the first card is lower than the second one.
|
||||
my ( $firstCard, $secondCard ) = sort { $a <=> $b } @shuffledDeck[ 0 .. 1 ];
|
||||
|
||||
print "I drew ", nameOfCard($firstCard), " and ", nameOfCard($secondCard), ".\n";
|
||||
|
||||
my $bet = getValidBet($playerBalance);
|
||||
if ( $bet == 0 ) {
|
||||
print "Chicken!\n\n";
|
||||
next HAND;
|
||||
}
|
||||
|
||||
if ( $bet < 0 ) {
|
||||
last GAME;
|
||||
}
|
||||
|
||||
# Now we re-shuffle the whole deck again and choose a third card.
|
||||
# (Note: This is how the odds get stacked in favor of the dealer since
|
||||
# the third card can be exactly the same as the first or second.)
|
||||
@shuffledDeck = shuffle(@cards);
|
||||
my $thirdCard = $shuffledDeck[0];
|
||||
|
||||
print "I drew ", nameOfCard($thirdCard), "!\n";
|
||||
|
||||
if ( ( $firstCard < $thirdCard ) && ( $thirdCard < $secondCard ) ) {
|
||||
print "You win!\n\n";
|
||||
$playerBalance += $bet;
|
||||
}
|
||||
else {
|
||||
print "You lose!\n\n";
|
||||
$playerBalance -= $bet;
|
||||
}
|
||||
|
||||
if ( $playerBalance <= 0 ) {
|
||||
print "Sorry, buddy, you blew your wad!\n\n";
|
||||
last HAND;
|
||||
}
|
||||
}
|
||||
|
||||
$keepPlaying = promptUserToKeepPlaying();
|
||||
}
|
||||
|
||||
print "Thanks for playing!\n";
|
||||
|
||||
###############
|
||||
sub getValidBet {
|
||||
my $maxBet = shift;
|
||||
|
||||
INPUT:
|
||||
{
|
||||
print "\nWhat's your bet? ";
|
||||
|
||||
chomp( my $input = <STDIN> );
|
||||
|
||||
# This regular expression will validate that the player entered an integer.
|
||||
# The !~ match operate *negates* the match, so if the player did NOT enter
|
||||
# an integer, they'll be given an error and prompted again.
|
||||
if (
|
||||
$input !~ /^ # Match the beginning of the string
|
||||
[+-]? # Optional plus or minus...
|
||||
\d+ # followed by one more more digits...
|
||||
$ # and then the end of the string
|
||||
/x # The x modifier ignores whitespace in this regex...
|
||||
)
|
||||
{
|
||||
print "Sorry, numbers only!\n";
|
||||
redo INPUT;
|
||||
}
|
||||
|
||||
if ( $input > $maxBet ) {
|
||||
print "Sorry, my friend, you can't bet more money than you have.\n";
|
||||
print "You only have $maxBet dollars to spend!\n";
|
||||
redo INPUT;
|
||||
}
|
||||
|
||||
return $input;
|
||||
}
|
||||
}
|
||||
|
||||
# Since arrays in Perl are 0-based, we need to convert the value that we drew from
|
||||
# the array to its proper position in the deck.
|
||||
sub nameOfCard {
|
||||
my $value = shift;
|
||||
|
||||
# Note that the Joker isn't used in this game, but since arrays in Perl are
|
||||
# 0-based, it's useful to have something there to represent the "0th"
|
||||
# position. This way the rest of the elements match their expected values
|
||||
# (e.g., element 1 is Ace, element 7 is 7, and element 12 is Queen).
|
||||
|
||||
my @cardlist = qw(Joker Ace 2 3 4 5 6 7 8 9 10 Jack Queen King);
|
||||
return $cardlist[$value];
|
||||
}
|
||||
|
||||
sub promptUserToKeepPlaying {
|
||||
YESNO:
|
||||
{
|
||||
print "Try again (Y/N)? ";
|
||||
|
||||
chomp( my $input = uc <STDIN> );
|
||||
|
||||
if ( $input eq 'Y' ) {
|
||||
return 1;
|
||||
}
|
||||
elsif ( $input eq 'N' ) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
redo YESNO;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -78,7 +78,7 @@ if __name__ == "__main__":
|
||||
"""
|
||||
Acey-Ducey is played in the following manner
|
||||
The dealer (computer) deals two cards face up
|
||||
You have an option to be or not bet depending
|
||||
You have an option to bet or not bet depending
|
||||
on whether or not you feel the card will have
|
||||
a value between the first two.
|
||||
If you do not want to bet, input a 0
|
||||
128
01_Acey_Ducey/python/acey_ducey_oo.py
Normal file
128
01_Acey_Ducey/python/acey_ducey_oo.py
Normal file
@@ -0,0 +1,128 @@
|
||||
#
|
||||
# AceyDuchy
|
||||
#
|
||||
# From: BASIC Computer Games (1978)
|
||||
# Edited by David Ahl
|
||||
#
|
||||
# "The original BASIC program author was Bill Palmby
|
||||
# of Prairie View, Illinois."
|
||||
#
|
||||
# Python port by Aviyam Fischer, 2022
|
||||
#
|
||||
######################################################
|
||||
|
||||
class Card:
|
||||
def __init__(self, suit, rank):
|
||||
self.suit = suit
|
||||
self.rank = rank
|
||||
|
||||
def __str__(self):
|
||||
r = self.rank
|
||||
if r == 11:
|
||||
r = 'J'
|
||||
elif r == 12:
|
||||
r = 'Q'
|
||||
elif r == 13:
|
||||
r = 'K'
|
||||
elif r == 14:
|
||||
r = 'A'
|
||||
return f'{r}{self.suit}'
|
||||
|
||||
|
||||
class Deck:
|
||||
def __init__(self):
|
||||
self.cards = []
|
||||
self.build()
|
||||
|
||||
def build(self):
|
||||
for suit in ['\u2665', '\u2666', '\u2663', '\u2660']:
|
||||
for rank in range(2, 15):
|
||||
self.cards.append(Card(suit, rank))
|
||||
|
||||
def shuffle(self):
|
||||
import random
|
||||
random.shuffle(self.cards)
|
||||
|
||||
def deal(self):
|
||||
return self.cards.pop()
|
||||
|
||||
|
||||
class Game:
|
||||
def __init__(self):
|
||||
self.deck = Deck()
|
||||
self.deck.shuffle()
|
||||
self.card_a = self.deck.deal()
|
||||
self.card_b = self.deck.deal()
|
||||
self.money = 100
|
||||
self.not_done = True
|
||||
|
||||
def play(self):
|
||||
while self.not_done:
|
||||
while self.money > 0:
|
||||
card_a = self.card_a
|
||||
card_b = self.card_b
|
||||
|
||||
if card_a.rank > card_b.rank:
|
||||
card_a, card_b = card_b, card_a
|
||||
|
||||
if card_a.rank == card_b.rank:
|
||||
self.card_b = self.deck.deal()
|
||||
card_b = self.card_b
|
||||
|
||||
print(f'You have:\t ${self.money} ')
|
||||
print(f'Your cards:\t {card_a} {card_b}')
|
||||
|
||||
bet = int(input('What is your bet? '))
|
||||
player_card = self.deck.deal()
|
||||
if 0 < bet <= self.money:
|
||||
|
||||
print(f'Your deal:\t {player_card}')
|
||||
if card_a.rank < player_card.rank < card_b.rank:
|
||||
print('You Win!')
|
||||
self.money += bet
|
||||
else:
|
||||
print('You Lose!')
|
||||
self.money -= bet
|
||||
self.not_done = False
|
||||
else:
|
||||
print('Chicken!')
|
||||
print(f'Your deal should have been: {player_card}')
|
||||
if card_a.rank < player_card.rank < card_b.rank:
|
||||
print(f'You could have won!')
|
||||
else:
|
||||
print(f'You would lose, so it was wise of you to chicken out!')
|
||||
|
||||
self.not_done = False
|
||||
break
|
||||
|
||||
if len(self.deck.cards) <= 3:
|
||||
print('You ran out of cards. Game over.')
|
||||
self.not_done = False
|
||||
break
|
||||
|
||||
self.card_a = self.deck.deal()
|
||||
self.card_b = self.deck.deal()
|
||||
|
||||
if self.money == 0:
|
||||
self.not_done = False
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print('''
|
||||
Acey Ducey is a card game where you play against the computer.
|
||||
The Dealer(computer) will deal two cards facing up.
|
||||
You have an option to bet or not bet depending on whether or not you
|
||||
feel the card will have a value between the first two.
|
||||
If you do not want to bet input a 0
|
||||
''')
|
||||
GAME_OVER = False
|
||||
|
||||
while not GAME_OVER:
|
||||
game = Game()
|
||||
game.play()
|
||||
print(f'You have ${game.money} left')
|
||||
print('Would you like to play again? (y/n)')
|
||||
if input() == 'n':
|
||||
GAME_OVER = True
|
||||
|
||||
print('\nThanks for playing!')
|
||||
@@ -174,7 +174,7 @@ print("OK Hope you had fun\n")
|
||||
#
|
||||
# Give the user the ability to quit the game, perhaps
|
||||
# by typing "quit" instead of making a bet. Provide a
|
||||
# final assement based on how much of the original
|
||||
# final assessment based on how much of the original
|
||||
# bankroll they have left.
|
||||
#
|
||||
# Or have the game run for a set number of rounds or
|
||||
3
01_Acey_Ducey/ruby/README.md
Normal file
3
01_Acey_Ducey/ruby/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
|
||||
|
||||
Conversion to [Ruby](https://www.ruby-lang.org/en/) by Christopher Özbek [coezbek@github](https://github.com/coezbek).
|
||||
139
01_Acey_Ducey/ruby/aceyducey.rb
Normal file
139
01_Acey_Ducey/ruby/aceyducey.rb
Normal file
@@ -0,0 +1,139 @@
|
||||
########################################################
|
||||
#
|
||||
# Acey Ducey
|
||||
#
|
||||
# From: BASIC Computer Games (1978)
|
||||
# Edited by David Ahl
|
||||
#
|
||||
# "This is a simulation of the Acey Ducey card game.
|
||||
# In the game, the dealer (the computer) deals two
|
||||
# cards face up. You have an option to bet or not to
|
||||
# bet depending on whether or not you feel the next
|
||||
# card dealt will have a value between the first two.
|
||||
#
|
||||
# "Your initial money is set to $100. The game keeps
|
||||
# going on until you lose all your money or interrupt
|
||||
# the program.
|
||||
#
|
||||
# "The original BASIC program author was Bill Palmby
|
||||
# of Prairie View, Illinois."
|
||||
#
|
||||
# Ruby port by Christopher Oezbek, 2021
|
||||
#
|
||||
# This uses the following techniques:
|
||||
#
|
||||
# - The programm largely consists of a GAME LOOP,
|
||||
# which is used to represent one round.
|
||||
# - The Kernel function rand(Range) is used to get an
|
||||
# Integer in the (inclusive) Range of 2 to 14.
|
||||
# - To ensure the user enters a proper Integer
|
||||
# via the console, an inline 'rescue' statement is
|
||||
# used.
|
||||
# - To capture the long text in the introduction, a
|
||||
# squiggly HEREDOC string declaration <<~ is used.
|
||||
#
|
||||
#
|
||||
########################################################
|
||||
|
||||
puts <<~INSTRUCTIONS
|
||||
🂡 ACEY DUCEY CARD GAME 🂱
|
||||
CREATIVE COMPUTING - MORRISTOWN, NEW JERSEY
|
||||
|
||||
ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER
|
||||
THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP
|
||||
YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING
|
||||
ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE
|
||||
A VALUE BETWEEN THE FIRST TWO.
|
||||
IF YOU DO NOT WANT TO BET IN A ROUND, ENTER 0
|
||||
INSTRUCTIONS
|
||||
|
||||
# The player starts with 100$
|
||||
stake = 100
|
||||
|
||||
while true # Game loop
|
||||
puts
|
||||
puts "YOU NOW HAVE #{stake} DOLLARS."
|
||||
puts
|
||||
puts "HERE ARE YOUR NEXT TWO CARDS:"
|
||||
|
||||
# Randomly draw two cards and make sure the first card is lower in value than the second
|
||||
# Using array destructuring, this sorted array can be assigned to `first_card` and `second_card`
|
||||
first_card, second_card = (2...14).to_a.shuffle.pop(2).sort
|
||||
|
||||
# Helper method to convert a numeric card into a String for printing
|
||||
def card_name(card)
|
||||
case card
|
||||
when 11
|
||||
"JACK"
|
||||
when 12
|
||||
"QUEEN"
|
||||
when 13
|
||||
"KING"
|
||||
when 14
|
||||
"ACE"
|
||||
else
|
||||
card
|
||||
end
|
||||
end
|
||||
|
||||
puts card_name(first_card)
|
||||
puts card_name(second_card)
|
||||
puts
|
||||
puts
|
||||
|
||||
# Loop until the user enters something sensible
|
||||
while true
|
||||
puts "WHAT IS YOUR BET? ENTER 0 IF YOU DON'T WANT TO BET (CTRL+C TO EXIT)"
|
||||
your_bet = Integer(gets.chomp) rescue nil
|
||||
|
||||
if your_bet == nil || your_bet < 0
|
||||
puts "PLEASE ENTER A POSITIVE NUMBER"
|
||||
puts
|
||||
next
|
||||
end
|
||||
|
||||
if your_bet > stake
|
||||
puts "SORRY, MY FRIEND, BUT YOU BET TOO MUCH."
|
||||
puts "YOU HAVE ONLY #{stake} DOLLARS TO BET."
|
||||
puts
|
||||
next
|
||||
end
|
||||
|
||||
break
|
||||
end
|
||||
|
||||
if your_bet == 0
|
||||
puts "CHICKEN!!"
|
||||
next
|
||||
end
|
||||
|
||||
puts "THANK YOU! YOUR BET IS #{your_bet} DOLLARS."
|
||||
puts ""
|
||||
puts "THE THIRD CARD IS:"
|
||||
third_card = rand(2..14)
|
||||
puts card_name(third_card)
|
||||
puts
|
||||
|
||||
if first_card <= third_card && third_card <= second_card
|
||||
puts "YOU WIN!!!"
|
||||
stake += your_bet
|
||||
else
|
||||
puts "SORRY, YOU LOSE"
|
||||
stake -= your_bet
|
||||
end
|
||||
|
||||
if stake == 0
|
||||
puts
|
||||
puts "SORRY, FRIEND, BUT YOU BLEW YOUR WAD."
|
||||
puts
|
||||
|
||||
puts "TRY AGAIN? (YES OR NO)"
|
||||
if gets.chomp.downcase.start_with? 'y'
|
||||
# Reset cash to 100
|
||||
stake = 100
|
||||
else
|
||||
puts "O.K., HOPE YOU HAD FUN!"
|
||||
exit
|
||||
end
|
||||
end
|
||||
end
|
||||
25
01_Acey_Ducey/vbnet/AceyDucy.sln
Normal file
25
01_Acey_Ducey/vbnet/AceyDucy.sln
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31903.59
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "AceyDucy", "AceyDucy\AceyDucy.vbproj", "{37496710-B458-4502-ADCB-4C57203866F9}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{37496710-B458-4502-ADCB-4C57203866F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{37496710-B458-4502-ADCB-4C57203866F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{37496710-B458-4502-ADCB-4C57203866F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{37496710-B458-4502-ADCB-4C57203866F9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {C01D9DAE-644C-455F-8365-E14E49074BC3}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
9
01_Acey_Ducey/vbnet/AceyDucy/AceyDucy.vbproj
Normal file
9
01_Acey_Ducey/vbnet/AceyDucy/AceyDucy.vbproj
Normal file
@@ -0,0 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>AceyDucy</RootNamespace>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
178
01_Acey_Ducey/vbnet/AceyDucy/Program.vb
Normal file
178
01_Acey_Ducey/vbnet/AceyDucy/Program.vb
Normal file
@@ -0,0 +1,178 @@
|
||||
Imports System
|
||||
|
||||
''' <summary>
|
||||
''' This is a modern adapation of Acey Ducey from BASIC Computer Games.
|
||||
'''
|
||||
''' The structural changes primarily consist of replacing the many GOTOs with
|
||||
''' Do/Loop constructs to force the continual execution of the program.
|
||||
'''
|
||||
''' Because modern Basic allows multi-line If/Then blocks, many GOTO jumps were
|
||||
''' able to be eliminated and the logic was able to be moved to more relevant areas,
|
||||
''' For example, the increment/decrement of the player's balance could be in the same
|
||||
''' area as the notification of win/loss.
|
||||
'''
|
||||
''' Some modern improvements were added, primarily the inclusion of a function, which
|
||||
''' eliminated a thrice-repeated block of logic to display the card value. The archaic
|
||||
''' RND function is greatly simplified with the .NET Framework's Random class.
|
||||
'''
|
||||
''' Elementary comments are provided for non-programmers or novices.
|
||||
''' </summary>
|
||||
Module Program
|
||||
Sub Main(args As String())
|
||||
' These are the variables that will hold values during the program's execution
|
||||
Dim input As String
|
||||
Dim rnd As New Random ' You can create a new instance of an object during declaration
|
||||
Dim currentBalance As Integer = 100 ' You can set a initial value at declaration
|
||||
Dim currentWager As Integer
|
||||
Dim cardA, cardB, cardC As Integer ' You can specify multiple variables of the same type in one declaration statement
|
||||
|
||||
' Display the opening title and instructions
|
||||
' Use a preceding $ to insert calculated values within the string using {}
|
||||
Console.WriteLine($"{Space((Console.WindowWidth \ 2) - 10)}ACEY DUCEY CARD GAME")
|
||||
Console.WriteLine($"{Space((Console.WindowWidth \ 2) - 21)}CREATIVE COMPUTING MORRISTOWN, NEW JERSEY")
|
||||
Console.WriteLine("")
|
||||
Console.WriteLine("")
|
||||
Console.WriteLine("ACEY-DUCEY IS PLAYED IN THE FOLLOWING MANNER")
|
||||
Console.WriteLine("THE DEALER (COMPUTER) DEALS TWO CARDS FACE UP")
|
||||
Console.WriteLine("YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING")
|
||||
Console.WriteLine("ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE")
|
||||
Console.WriteLine("A VALUE BETWEEN THE FIRST TWO.")
|
||||
Console.WriteLine("IF YOU DO NOT WANT TO BET, INPUT A 0")
|
||||
|
||||
Do ' This loop continues as long as the player wants to keep playing
|
||||
|
||||
Do ' This loop continues as long as the player has money to play
|
||||
|
||||
Console.WriteLine("")
|
||||
Console.WriteLine($"YOU NOW HAVE {currentBalance} DOLLARS.")
|
||||
Console.WriteLine("")
|
||||
|
||||
Console.WriteLine("HERE ARE YOUR NEXT TWO CARDS:")
|
||||
|
||||
' We need to ensure that card B is a higher value for our later comparison,
|
||||
' so we will loop until we have two cards that meet this criteria
|
||||
Do
|
||||
cardA = rnd.Next(2, 14)
|
||||
cardB = rnd.Next(2, 14)
|
||||
|
||||
Loop While cardA > cardB
|
||||
|
||||
' We use a function to display the text value of the numeric card value
|
||||
' because we do this 3 times and a function reduces repetition of code
|
||||
Console.WriteLine(DisplayCard(cardA))
|
||||
Console.WriteLine(DisplayCard(cardB))
|
||||
|
||||
Do ' This loop continues until the player provides a valid wager value
|
||||
Console.WriteLine("")
|
||||
Console.WriteLine("WHAT IS YOUR BET")
|
||||
|
||||
currentWager = 0
|
||||
input = Console.ReadLine
|
||||
|
||||
' Any input from the console is a string, but we require a number.
|
||||
' Test the input to make sure it is a numeric value.
|
||||
If Integer.TryParse(input, currentWager) Then
|
||||
' Test to ensure the player has not wagered more than their balance
|
||||
If currentWager > currentBalance Then
|
||||
Console.WriteLine("SORRY, MY FRIEND, BUT YOU BET TOO MUCH.")
|
||||
Console.WriteLine($"YOU HAVE ONLY {currentBalance} DOLLARS TO BET.")
|
||||
|
||||
Else
|
||||
' The player has provided a numeric value that is less/equal to their balance,
|
||||
' exit the loop and continue play
|
||||
Exit Do
|
||||
|
||||
End If ' check player balance
|
||||
|
||||
End If ' check numeric input
|
||||
|
||||
Loop ' wager loop
|
||||
|
||||
' If the player is wagering, draw the third card, otherwise, mock them.
|
||||
If currentWager > 0 Then
|
||||
cardC = rnd.Next(2, 14)
|
||||
|
||||
Console.WriteLine(DisplayCard(cardC))
|
||||
|
||||
' The effort we made to have two cards in numeric order earlier makes this check easier,
|
||||
' otherwise we would have to have a second check in the opposite direction
|
||||
If cardC < cardA OrElse cardC >= cardB Then
|
||||
Console.WriteLine("SORRY, YOU LOSE")
|
||||
currentBalance -= currentWager ' Shorthand code to decrement a number (currentBalance=currentBalance - currentWager)
|
||||
|
||||
Else
|
||||
Console.WriteLine("YOU WIN!!!")
|
||||
currentBalance += currentWager ' Shorthand code to increment a number (currentBalance=currentBalance + currentWager)
|
||||
|
||||
End If
|
||||
|
||||
Else
|
||||
Console.WriteLine("CHICKEN!!")
|
||||
Console.WriteLine("")
|
||||
|
||||
End If
|
||||
|
||||
Loop While currentBalance > 0 ' loop as long as the player has money
|
||||
|
||||
' At this point, the player has no money (currentBalance=0). Inform them of such.
|
||||
Console.WriteLine("")
|
||||
Console.WriteLine("SORRY, FRIEND, BUT YOU BLEW YOUR WAD.")
|
||||
Console.WriteLine("")
|
||||
Console.WriteLine("")
|
||||
|
||||
' We will loop to ensure the player provides some answer.
|
||||
Do
|
||||
Console.WriteLine("TRY AGAIN (YES OR NO)")
|
||||
Console.WriteLine("")
|
||||
|
||||
input = Console.ReadLine
|
||||
|
||||
Loop While String.IsNullOrWhiteSpace(input)
|
||||
|
||||
' We will assume that the player wants to play again only if they answer yes.
|
||||
' (yeah and ya are valid as well, because we only check the first letter)
|
||||
If input.Substring(0, 1).Equals("y", StringComparison.CurrentCultureIgnoreCase) Then ' This allows upper and lower case to be entered.
|
||||
currentBalance = 100 ' Reset the players balance before restarting
|
||||
|
||||
Else
|
||||
' Exit the outer loop which will end the game.
|
||||
Exit Do
|
||||
|
||||
End If
|
||||
|
||||
Loop ' The full game loop
|
||||
|
||||
Console.WriteLine("O.K., HOPE YOU HAD FUN!")
|
||||
|
||||
End Sub
|
||||
|
||||
' This function is called for each of the 3 cards used in the game.
|
||||
' The input and the output are both consistent, making it a good candidate for a function.
|
||||
Private Function DisplayCard(value As Integer) As String
|
||||
' We check the value of the input and run a block of code for whichever
|
||||
' evaluation matches
|
||||
Select Case value
|
||||
Case 2 To 10 ' Case statements can be ranges of values, also multiple values (Case 2,3,4,5,6,7,8,9,10)
|
||||
Return value.ToString
|
||||
|
||||
Case 11
|
||||
Return "JACK"
|
||||
|
||||
Case 12
|
||||
Return "QUEEN"
|
||||
|
||||
Case 13
|
||||
Return "KING"
|
||||
|
||||
Case 14
|
||||
Return "ACE"
|
||||
|
||||
End Select
|
||||
|
||||
' Although we have full knowledge of the program and never plan to send an invalid
|
||||
' card value, it's important to provide a message for the next developer who won't
|
||||
Throw New ArgumentOutOfRangeException("Card value must be between 2 and 14")
|
||||
|
||||
End Function
|
||||
|
||||
End Module
|
||||
@@ -1,7 +0,0 @@
|
||||
### Amazing
|
||||
|
||||
As published in Basic Computer Games (1978)
|
||||
https://www.atariarchives.org/basicgames/showpage.php?page=3
|
||||
|
||||
Downloaded from Vintage Basic at
|
||||
http://www.vintage-basic.net/games.html
|
||||
18
02_Amazing/README.md
Normal file
18
02_Amazing/README.md
Normal file
@@ -0,0 +1,18 @@
|
||||
### Amazing
|
||||
|
||||
This program will print out a different maze every time it is run and guarantees only one path through. You can choose the dimensions of the maze — i.e. the number of squares wide and long.
|
||||
|
||||
The original program author was Jack Hauber of Windsor, Connecticut.
|
||||
|
||||
---
|
||||
|
||||
As published in Basic Computer Games (1978):
|
||||
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=3)
|
||||
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=18)
|
||||
|
||||
Downloaded from Vintage Basic at
|
||||
http://www.vintage-basic.net/games.html
|
||||
|
||||
---
|
||||
|
||||
**2022-01-04:** patched original source in [#400](https://github.com/coding-horror/basic-computer-games/pull/400) to fix a minor bug where a generated maze may be missing an exit, particularly at small maze sizes.
|
||||
@@ -117,10 +117,15 @@
|
||||
975 V(R,S)=3:Q=0:GOTO 1000
|
||||
980 V(R,S)=1:Q=0:R=1:S=1:GOTO 250
|
||||
1000 GOTO 210
|
||||
1010 FOR J=1 TO V
|
||||
1011 PRINT "I";
|
||||
1012 FOR I=1 TO H
|
||||
1013 IF V(I,J)<2 THEN 1030
|
||||
1010 IF Z=1 THEN 1015
|
||||
1011 X=INT(RND(1)*H+1)
|
||||
1012 IF V(X,V)=0 THEN 1014
|
||||
1013 V(X,V)=3: GOTO 1015
|
||||
1014 V(X,V)=1
|
||||
1015 FOR J=1 TO V
|
||||
1016 PRINT "I";
|
||||
1017 FOR I=1 TO H
|
||||
1018 IF V(I,J)<2 THEN 1030
|
||||
1020 PRINT " ";
|
||||
1021 GOTO 1040
|
||||
1030 PRINT " I";
|
||||
320
02_Amazing/csharp/Amazing.cs
Normal file
320
02_Amazing/csharp/Amazing.cs
Normal file
@@ -0,0 +1,320 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Amazing
|
||||
{
|
||||
class AmazingGame
|
||||
{
|
||||
private const int FIRST_COL = 0;
|
||||
private const int FIRST_ROW = 0;
|
||||
private const int EXIT_UNSET = 0;
|
||||
private const int EXIT_DOWN = 1;
|
||||
private const int EXIT_RIGHT = 2;
|
||||
|
||||
private static int GetDelimitedValue(String text, int pos)
|
||||
{
|
||||
String[] tokens = text.Split(",");
|
||||
|
||||
int val;
|
||||
if (Int32.TryParse(tokens[pos], out val))
|
||||
{
|
||||
return val;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static String Tab(int spaces)
|
||||
{
|
||||
return new String(' ', spaces);
|
||||
}
|
||||
|
||||
public static int Random(int min, int max)
|
||||
{
|
||||
Random random = new Random();
|
||||
return random.Next(max - min) + min;
|
||||
}
|
||||
|
||||
public void Play()
|
||||
{
|
||||
Console.WriteLine(Tab(28) + "AMAZING PROGRAM");
|
||||
Console.WriteLine(Tab(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
|
||||
Console.WriteLine();
|
||||
|
||||
int width = 0;
|
||||
int length = 0;
|
||||
|
||||
do
|
||||
{
|
||||
String range = DisplayTextAndGetInput("WHAT ARE YOUR WIDTH AND LENGTH");
|
||||
if (range.IndexOf(",") > 0)
|
||||
{
|
||||
width = GetDelimitedValue(range, 0);
|
||||
length = GetDelimitedValue(range, 1);
|
||||
}
|
||||
}
|
||||
while (width < 1 || length < 1);
|
||||
|
||||
Grid grid = new Grid(length, width);
|
||||
int enterCol = grid.SetupEntrance();
|
||||
|
||||
int totalWalls = width * length + 1;
|
||||
int count = 2;
|
||||
Cell cell = grid.StartingCell();
|
||||
|
||||
while (count != totalWalls)
|
||||
{
|
||||
List<Direction> possibleDirs = GetPossibleDirs(grid, cell);
|
||||
|
||||
if (possibleDirs.Count != 0)
|
||||
{
|
||||
cell = SetCellExit(grid, cell, possibleDirs);
|
||||
cell.Count = count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
cell = grid.GetFirstUnset(cell);
|
||||
}
|
||||
}
|
||||
grid.SetupExit();
|
||||
|
||||
WriteMaze(width, grid, enterCol);
|
||||
}
|
||||
|
||||
private Cell SetCellExit(Grid grid, Cell cell, List<Direction> possibleDirs)
|
||||
{
|
||||
Direction direction = possibleDirs[Random(0, possibleDirs.Count)];
|
||||
if (direction == Direction.GO_LEFT)
|
||||
{
|
||||
cell = grid.GetPrevCol(cell);
|
||||
cell.ExitType = EXIT_RIGHT;
|
||||
}
|
||||
else if (direction == Direction.GO_UP)
|
||||
{
|
||||
cell = grid.GetPrevRow(cell);
|
||||
cell.ExitType = EXIT_DOWN;
|
||||
}
|
||||
else if (direction == Direction.GO_RIGHT)
|
||||
{
|
||||
cell.ExitType = cell.ExitType + EXIT_RIGHT;
|
||||
cell = grid.GetNextCol(cell);
|
||||
}
|
||||
else if (direction == Direction.GO_DOWN)
|
||||
{
|
||||
cell.ExitType = cell.ExitType + EXIT_DOWN;
|
||||
cell = grid.GetNextRow(cell);
|
||||
}
|
||||
return cell;
|
||||
}
|
||||
|
||||
private void WriteMaze(int width, Grid grid, int enterCol)
|
||||
{
|
||||
// top line
|
||||
for (int i = 0; i < width; i++)
|
||||
{
|
||||
if (i == enterCol) Console.Write(". ");
|
||||
else Console.Write(".--");
|
||||
}
|
||||
Console.WriteLine(".");
|
||||
|
||||
for (int i = 0; i < grid.Length; i++)
|
||||
{
|
||||
Console.Write("I");
|
||||
for (int j = 0; j < grid.Width; j++)
|
||||
{
|
||||
if (grid.Cells[i,j].ExitType == EXIT_UNSET || grid.Cells[i, j].ExitType == EXIT_DOWN)
|
||||
Console.Write(" I");
|
||||
else Console.Write(" ");
|
||||
}
|
||||
Console.WriteLine();
|
||||
for (int j = 0; j < grid.Width; j++)
|
||||
{
|
||||
if (grid.Cells[i,j].ExitType == EXIT_UNSET || grid.Cells[i, j].ExitType == EXIT_RIGHT)
|
||||
Console.Write(":--");
|
||||
else Console.Write(": ");
|
||||
}
|
||||
Console.WriteLine(".");
|
||||
}
|
||||
}
|
||||
|
||||
private List<Direction> GetPossibleDirs(Grid grid, Cell cell)
|
||||
{
|
||||
var possibleDirs = new List<Direction>();
|
||||
foreach (var val in Enum.GetValues(typeof(Direction)))
|
||||
{
|
||||
possibleDirs.Add((Direction)val);
|
||||
}
|
||||
|
||||
if (cell.Col == FIRST_COL || grid.IsPrevColSet(cell))
|
||||
{
|
||||
possibleDirs.Remove(Direction.GO_LEFT);
|
||||
}
|
||||
if (cell.Row == FIRST_ROW || grid.IsPrevRowSet(cell))
|
||||
{
|
||||
possibleDirs.Remove(Direction.GO_UP);
|
||||
}
|
||||
if (cell.Col == grid.LastCol || grid.IsNextColSet(cell))
|
||||
{
|
||||
possibleDirs.Remove(Direction.GO_RIGHT);
|
||||
}
|
||||
if (cell.Row == grid.LastRow || grid.IsNextRowSet(cell))
|
||||
{
|
||||
possibleDirs.Remove(Direction.GO_DOWN);
|
||||
}
|
||||
return possibleDirs;
|
||||
}
|
||||
|
||||
private String DisplayTextAndGetInput(String text)
|
||||
{
|
||||
Console.WriteLine(text);
|
||||
return Console.ReadLine();
|
||||
}
|
||||
|
||||
|
||||
private enum Direction
|
||||
{
|
||||
GO_LEFT,
|
||||
GO_UP,
|
||||
GO_RIGHT,
|
||||
GO_DOWN,
|
||||
}
|
||||
|
||||
public class Cell
|
||||
{
|
||||
public int ExitType { get; set; }
|
||||
public int Count { get; set; }
|
||||
|
||||
public int Col { get; set; }
|
||||
public int Row { get; set; }
|
||||
|
||||
public Cell(int row, int col)
|
||||
{
|
||||
ExitType = EXIT_UNSET;
|
||||
Row = row;
|
||||
Col = col;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class Grid
|
||||
{
|
||||
public Cell[,] Cells { get; private set; }
|
||||
|
||||
public int LastCol { get; set; }
|
||||
public int LastRow { get; set; }
|
||||
|
||||
public int Width { get; private set; }
|
||||
public int Length { get; private set; }
|
||||
|
||||
private int enterCol;
|
||||
|
||||
public Grid(int length, int width)
|
||||
{
|
||||
LastCol = width - 1;
|
||||
LastRow = length - 1;
|
||||
Width = width;
|
||||
Length = length;
|
||||
|
||||
Cells = new Cell[length,width];
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
this.Cells[i,j] = new Cell(i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int SetupEntrance()
|
||||
{
|
||||
this.enterCol = Random(0, Width);
|
||||
Cells[0, enterCol].Count = 1;
|
||||
return this.enterCol;
|
||||
}
|
||||
|
||||
public void SetupExit()
|
||||
{
|
||||
int exit = Random(0, Width - 1);
|
||||
Cells[LastRow, exit].ExitType += 1;
|
||||
}
|
||||
|
||||
public Cell StartingCell()
|
||||
{
|
||||
return Cells[0, enterCol];
|
||||
}
|
||||
|
||||
public bool IsPrevColSet(Cell cell)
|
||||
{
|
||||
return 0 != Cells[cell.Row, cell.Col - 1].Count;
|
||||
}
|
||||
|
||||
public bool IsPrevRowSet(Cell cell)
|
||||
{
|
||||
return 0 != Cells[cell.Row - 1, cell.Col].Count;
|
||||
}
|
||||
|
||||
public bool IsNextColSet(Cell cell)
|
||||
{
|
||||
return 0 != Cells[cell.Row, cell.Col + 1].Count;
|
||||
}
|
||||
|
||||
public bool IsNextRowSet(Cell cell)
|
||||
{
|
||||
return 0 != Cells[cell.Row + 1, cell.Col].Count;
|
||||
}
|
||||
|
||||
public Cell GetPrevCol(Cell cell)
|
||||
{
|
||||
return Cells[cell.Row, cell.Col - 1];
|
||||
}
|
||||
|
||||
public Cell GetPrevRow(Cell cell)
|
||||
{
|
||||
return Cells[cell.Row - 1, cell.Col];
|
||||
}
|
||||
|
||||
public Cell GetNextCol(Cell cell)
|
||||
{
|
||||
return Cells[cell.Row, cell.Col + 1];
|
||||
}
|
||||
|
||||
public Cell GetNextRow(Cell cell)
|
||||
{
|
||||
return Cells[cell.Row + 1, cell.Col];
|
||||
}
|
||||
|
||||
public Cell GetFirstUnset(Cell cell)
|
||||
{
|
||||
int col = cell.Col;
|
||||
int row = cell.Row;
|
||||
Cell newCell;
|
||||
do
|
||||
{
|
||||
if (col != this.LastCol)
|
||||
{
|
||||
col++;
|
||||
}
|
||||
else if (row != this.LastRow)
|
||||
{
|
||||
row++;
|
||||
col = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
row = 0;
|
||||
col = 0;
|
||||
}
|
||||
}
|
||||
while ((newCell = Cells[row, col]).Count == 0);
|
||||
return newCell;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
new AmazingGame().Play();
|
||||
}
|
||||
}
|
||||
}
|
||||
8
02_Amazing/csharp/Amazing.csproj
Normal file
8
02_Amazing/csharp/Amazing.csproj
Normal file
@@ -0,0 +1,8 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
25
02_Amazing/csharp/Amazing.sln
Normal file
25
02_Amazing/csharp/Amazing.sln
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.808.10
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Amazing", "Amazing.csproj", "{DD3483B4-F366-493C-8BFE-BAFBFE6F3016}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{DD3483B4-F366-493C-8BFE-BAFBFE6F3016}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{DD3483B4-F366-493C-8BFE-BAFBFE6F3016}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{DD3483B4-F366-493C-8BFE-BAFBFE6F3016}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{DD3483B4-F366-493C-8BFE-BAFBFE6F3016}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {F8762606-E467-47C1-A28F-BD5C4F7BFF5A}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
262
02_Amazing/java/Amazing.java
Normal file
262
02_Amazing/java/Amazing.java
Normal file
@@ -0,0 +1,262 @@
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Random;
|
||||
import java.util.Scanner;
|
||||
import static java.lang.System.in;
|
||||
import static java.lang.System.out;
|
||||
|
||||
/**
|
||||
* Core algorithm copied from amazing.py
|
||||
*/
|
||||
public class Amazing {
|
||||
|
||||
final static int FIRST_COL = 0;
|
||||
final static int FIRST_ROW = 0;
|
||||
final static int EXIT_UNSET = 0;
|
||||
final static int EXIT_DOWN = 1;
|
||||
final static int EXIT_RIGHT = 2;
|
||||
private final Scanner kbScanner;
|
||||
public Amazing() {
|
||||
kbScanner = new Scanner(in);
|
||||
}
|
||||
|
||||
private static int getDelimitedValue(String text, int pos) {
|
||||
String[] tokens = text.split(",");
|
||||
try {
|
||||
return Integer.parseInt(tokens[pos]);
|
||||
} catch (Exception ex) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static String tab(int spaces) {
|
||||
char[] spacesTemp = new char[spaces];
|
||||
Arrays.fill(spacesTemp, ' ');
|
||||
return new String(spacesTemp);
|
||||
}
|
||||
|
||||
public static int random(int min, int max) {
|
||||
Random random = new Random();
|
||||
return random.nextInt(max - min) + min;
|
||||
}
|
||||
|
||||
public void play() {
|
||||
out.println(tab(28) + "AMAZING PROGRAM");
|
||||
out.println(tab(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
|
||||
out.println();
|
||||
|
||||
int width = 0;
|
||||
int length = 0;
|
||||
|
||||
do {
|
||||
String range = displayTextAndGetInput("WHAT ARE YOUR WIDTH AND LENGTH");
|
||||
if (range.indexOf(",") > 0) {
|
||||
width = getDelimitedValue(range, 0);
|
||||
length = getDelimitedValue(range, 1);
|
||||
}
|
||||
} while (width < 1 || length < 1);
|
||||
|
||||
Grid grid = new Grid(length, width);
|
||||
int enterCol = grid.setupEntrance();
|
||||
|
||||
int totalWalls = width * length + 1;
|
||||
int count = 2;
|
||||
Cell cell = grid.startingCell();
|
||||
|
||||
while (count != totalWalls) {
|
||||
ArrayList<Direction> possibleDirs = getPossibleDirs(grid, cell);
|
||||
|
||||
if (possibleDirs.size() != 0) {
|
||||
cell = setCellExit(grid, cell, possibleDirs);
|
||||
cell.count = count++;
|
||||
} else {
|
||||
cell = grid.getFirstUnset(cell);
|
||||
}
|
||||
}
|
||||
grid.setupExit();
|
||||
|
||||
writeMaze(width, grid, enterCol);
|
||||
}
|
||||
|
||||
private Cell setCellExit(Grid grid, Cell cell, ArrayList<Direction> possibleDirs) {
|
||||
Direction direction = possibleDirs.get(random(0, possibleDirs.size()));
|
||||
if (direction == Direction.GO_LEFT) {
|
||||
cell = grid.getPrevCol(cell);
|
||||
cell.exitType = EXIT_RIGHT;
|
||||
} else if (direction == Direction.GO_UP) {
|
||||
cell = grid.getPrevRow(cell);
|
||||
cell.exitType = EXIT_DOWN;
|
||||
} else if (direction == Direction.GO_RIGHT) {
|
||||
cell.exitType = cell.exitType + EXIT_RIGHT;
|
||||
cell = grid.getNextCol(cell);
|
||||
} else if (direction == Direction.GO_DOWN) {
|
||||
cell.exitType = cell.exitType + EXIT_DOWN;
|
||||
cell = grid.getNextRow(cell);
|
||||
}
|
||||
return cell;
|
||||
}
|
||||
|
||||
private void writeMaze(int width, Grid grid, int enterCol) {
|
||||
// top line
|
||||
for (int i = 0; i < width; i++) {
|
||||
if (i == enterCol) {
|
||||
out.print(". ");
|
||||
} else {
|
||||
out.print(".--");
|
||||
}
|
||||
}
|
||||
out.println('.');
|
||||
|
||||
for (Cell[] rows : grid.cells) {
|
||||
out.print("I");
|
||||
for (Cell cell : rows) {
|
||||
if (cell.exitType == EXIT_UNSET || cell.exitType == EXIT_DOWN) {
|
||||
out.print(" I");
|
||||
} else {
|
||||
out.print(" ");
|
||||
}
|
||||
}
|
||||
out.println();
|
||||
for (Cell cell : rows) {
|
||||
if (cell.exitType == EXIT_UNSET || cell.exitType == EXIT_RIGHT) {
|
||||
out.print(":--");
|
||||
} else {
|
||||
out.print(": ");
|
||||
}
|
||||
}
|
||||
out.println(".");
|
||||
}
|
||||
}
|
||||
|
||||
private ArrayList<Direction> getPossibleDirs(Grid grid, Cell cell) {
|
||||
ArrayList<Direction> possibleDirs = new ArrayList<>(Arrays.asList(Direction.values()));
|
||||
|
||||
if (cell.col == FIRST_COL || grid.isPrevColSet(cell)) {
|
||||
possibleDirs.remove(Direction.GO_LEFT);
|
||||
}
|
||||
if (cell.row == FIRST_ROW || grid.isPrevRowSet(cell)) {
|
||||
possibleDirs.remove(Direction.GO_UP);
|
||||
}
|
||||
if (cell.col == grid.lastCol || grid.isNextColSet(cell)) {
|
||||
possibleDirs.remove(Direction.GO_RIGHT);
|
||||
}
|
||||
if (cell.row == grid.lastRow || grid.isNextRowSet(cell)) {
|
||||
possibleDirs.remove(Direction.GO_DOWN);
|
||||
}
|
||||
return possibleDirs;
|
||||
}
|
||||
|
||||
private String displayTextAndGetInput(String text) {
|
||||
out.print(text);
|
||||
return kbScanner.next();
|
||||
}
|
||||
|
||||
enum Direction {
|
||||
GO_LEFT,
|
||||
GO_UP,
|
||||
GO_RIGHT,
|
||||
GO_DOWN,
|
||||
}
|
||||
|
||||
public static class Cell {
|
||||
int exitType = EXIT_UNSET;
|
||||
int count = 0;
|
||||
|
||||
int col;
|
||||
int row;
|
||||
|
||||
public Cell(int row, int col) {
|
||||
this.row = row;
|
||||
this.col = col;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Grid {
|
||||
Cell[][] cells;
|
||||
|
||||
int lastCol;
|
||||
int lastRow;
|
||||
|
||||
int width;
|
||||
int enterCol;
|
||||
|
||||
public Grid(int length, int width) {
|
||||
this.lastCol = width - 1;
|
||||
this.lastRow = length - 1;
|
||||
this.width = width;
|
||||
|
||||
this.cells = new Cell[length][width];
|
||||
for (int i = 0; i < length; i++) {
|
||||
this.cells[i] = new Cell[width];
|
||||
for (int j = 0; j < width; j++) {
|
||||
this.cells[i][j] = new Cell(i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int setupEntrance() {
|
||||
this.enterCol = random(0, this.width);
|
||||
cells[0][this.enterCol].count = 1;
|
||||
return this.enterCol;
|
||||
}
|
||||
|
||||
public void setupExit() {
|
||||
int exit = random(0, width - 1);
|
||||
cells[lastRow][exit].exitType += 1;
|
||||
}
|
||||
|
||||
public Cell startingCell() {
|
||||
return cells[0][enterCol];
|
||||
}
|
||||
|
||||
public boolean isPrevColSet(Cell cell) {
|
||||
return 0 != cells[cell.row][cell.col - 1].count;
|
||||
}
|
||||
|
||||
public boolean isPrevRowSet(Cell cell) {
|
||||
return 0 != cells[cell.row - 1][cell.col].count;
|
||||
}
|
||||
|
||||
public boolean isNextColSet(Cell cell) {
|
||||
return 0 != cells[cell.row][cell.col + 1].count;
|
||||
}
|
||||
|
||||
public boolean isNextRowSet(Cell cell) {
|
||||
return 0 != cells[cell.row + 1][cell.col].count;
|
||||
}
|
||||
|
||||
public Cell getPrevCol(Cell cell) {
|
||||
return cells[cell.row][cell.col - 1];
|
||||
}
|
||||
|
||||
public Cell getPrevRow(Cell cell) {
|
||||
return cells[cell.row - 1][cell.col];
|
||||
}
|
||||
|
||||
public Cell getNextCol(Cell cell) {
|
||||
return cells[cell.row][cell.col + 1];
|
||||
}
|
||||
|
||||
public Cell getNextRow(Cell cell) {
|
||||
return cells[cell.row + 1][cell.col];
|
||||
}
|
||||
|
||||
public Cell getFirstUnset(Cell cell) {
|
||||
int col = cell.col;
|
||||
int row = cell.row;
|
||||
Cell newCell;
|
||||
do {
|
||||
if (col != this.lastCol) {
|
||||
col++;
|
||||
} else if (row != this.lastRow) {
|
||||
row++;
|
||||
col = 0;
|
||||
} else {
|
||||
row = 0;
|
||||
col = 0;
|
||||
}
|
||||
} while ((newCell = cells[row][col]).count == 0);
|
||||
return newCell;
|
||||
}
|
||||
}
|
||||
}
|
||||
6
02_Amazing/java/AmazingGame.java
Normal file
6
02_Amazing/java/AmazingGame.java
Normal file
@@ -0,0 +1,6 @@
|
||||
public class AmazingGame {
|
||||
public static void main(String[] args) {
|
||||
Amazing amazing = new Amazing();
|
||||
amazing.play();
|
||||
}
|
||||
}
|
||||
36
02_Amazing/pascal/.gitattributes
vendored
Normal file
36
02_Amazing/pascal/.gitattributes
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
# Set the default behavior, in case people don't have core.autocrlf set.
|
||||
* text=auto
|
||||
|
||||
# Explicitly declare text files you want to always be normalized and converted
|
||||
# to native line endings on checkout.
|
||||
*.inc text
|
||||
*.pas text
|
||||
*.pp text
|
||||
*.lpk text
|
||||
*.lpi text
|
||||
*.lps text
|
||||
*.lpr text
|
||||
*.def text
|
||||
*.css text
|
||||
*.html text
|
||||
*.xml text
|
||||
*.sql text
|
||||
|
||||
# Declare files that will always have CRLF line endings on checkout.
|
||||
*.dpk text eol=crlf
|
||||
*.dproj text eol=crlf
|
||||
|
||||
# Declare files that will always have LF line endings on checkout.
|
||||
|
||||
|
||||
# Denote all files that are truly binary and should not be modified.
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
*.exe binary
|
||||
*.res binary
|
||||
*.ico binary
|
||||
*.dll binary
|
||||
|
||||
# Keep these files from archive/exports, mainly from production.
|
||||
.gitignore export-ignore
|
||||
.gitattributes export-ignore
|
||||
63
02_Amazing/pascal/.gitignore
vendored
Normal file
63
02_Amazing/pascal/.gitignore
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
# Basic Computer Programs project specific
|
||||
amazing
|
||||
amazing.exe
|
||||
|
||||
# Compiled l10n files: .mo should be ignored
|
||||
*.mo
|
||||
|
||||
# Ghostwriter backups
|
||||
*.backup
|
||||
|
||||
# nano editor backup files
|
||||
*.swp
|
||||
|
||||
# Uncomment these types if you want even more clean repository. But be careful.
|
||||
# It can make harm to an existing project source. Read explanations below.
|
||||
#
|
||||
# Resource files are binaries containing manifest, project icon and version info.
|
||||
# They can not be viewed as text or compared by diff-tools. Consider replacing them with .rc files.
|
||||
*.res
|
||||
|
||||
# Delphi/Lazarus compiler-generated binaries (safe to delete)
|
||||
*.exe
|
||||
*.dll
|
||||
*.bpl
|
||||
*.bpi
|
||||
*.dcp
|
||||
*.so
|
||||
*.apk
|
||||
*.drc
|
||||
*.map
|
||||
*.dres
|
||||
*.rsm
|
||||
*.tds
|
||||
*.dcu
|
||||
*.lib
|
||||
*.[ao]
|
||||
*.or
|
||||
*.ppu
|
||||
*.dbg
|
||||
*.compiled
|
||||
|
||||
# Delphi autogenerated files (duplicated info)
|
||||
*.cfg
|
||||
*Resource.rc
|
||||
|
||||
# Delphi local files (user-specific info)
|
||||
*.local
|
||||
*.identcache
|
||||
*.projdata
|
||||
*.tvsconfig
|
||||
*.dsk
|
||||
|
||||
# Delphi history and backups
|
||||
__history/
|
||||
*.~*
|
||||
|
||||
# Lazarus history, backups and session
|
||||
backup/
|
||||
*.bak
|
||||
*.lps
|
||||
|
||||
# Castalia statistics file
|
||||
*.stat
|
||||
3
02_Amazing/pascal/README.md
Normal file
3
02_Amazing/pascal/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
|
||||
|
||||
Conversion to [Pascal](https://en.wikipedia.org/wiki/Pascal_(programming_language)) by Gustavo Carreno [gcarreno@github](https://github.com/gcarreno)
|
||||
71
02_Amazing/pascal/object-pascal/amazing.lpi
Normal file
71
02_Amazing/pascal/object-pascal/amazing.lpi
Normal file
@@ -0,0 +1,71 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<CONFIG>
|
||||
<ProjectOptions>
|
||||
<Version Value="12"/>
|
||||
<General>
|
||||
<Flags>
|
||||
<MainUnitHasCreateFormStatements Value="False"/>
|
||||
<MainUnitHasTitleStatement Value="False"/>
|
||||
<MainUnitHasScaledStatement Value="False"/>
|
||||
</Flags>
|
||||
<SessionStorage Value="InProjectDir"/>
|
||||
<Title Value="amazing"/>
|
||||
<UseAppBundle Value="False"/>
|
||||
<ResourceType Value="res"/>
|
||||
</General>
|
||||
<BuildModes Count="1">
|
||||
<Item1 Name="Default" Default="True"/>
|
||||
</BuildModes>
|
||||
<PublishOptions>
|
||||
<Version Value="2"/>
|
||||
<UseFileFilters Value="True"/>
|
||||
</PublishOptions>
|
||||
<RunParams>
|
||||
<FormatVersion Value="2"/>
|
||||
</RunParams>
|
||||
<Units Count="4">
|
||||
<Unit0>
|
||||
<Filename Value="amazing.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
</Unit0>
|
||||
<Unit1>
|
||||
<Filename Value="amazingapplication.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
<UnitName Value="AmazingApplication"/>
|
||||
</Unit1>
|
||||
<Unit2>
|
||||
<Filename Value="maze.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
<UnitName Value="Maze"/>
|
||||
</Unit2>
|
||||
<Unit3>
|
||||
<Filename Value="room.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
<UnitName Value="Room"/>
|
||||
</Unit3>
|
||||
</Units>
|
||||
</ProjectOptions>
|
||||
<CompilerOptions>
|
||||
<Version Value="11"/>
|
||||
<Target>
|
||||
<Filename Value="amazing"/>
|
||||
</Target>
|
||||
<SearchPaths>
|
||||
<IncludeFiles Value="$(ProjOutDir)"/>
|
||||
<UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
|
||||
</SearchPaths>
|
||||
</CompilerOptions>
|
||||
<Debugging>
|
||||
<Exceptions Count="3">
|
||||
<Item1>
|
||||
<Name Value="EAbort"/>
|
||||
</Item1>
|
||||
<Item2>
|
||||
<Name Value="ECodetoolError"/>
|
||||
</Item2>
|
||||
<Item3>
|
||||
<Name Value="EFOpenError"/>
|
||||
</Item3>
|
||||
</Exceptions>
|
||||
</Debugging>
|
||||
</CONFIG>
|
||||
17
02_Amazing/pascal/object-pascal/amazing.pas
Normal file
17
02_Amazing/pascal/object-pascal/amazing.pas
Normal file
@@ -0,0 +1,17 @@
|
||||
program amazing;
|
||||
|
||||
{$IFDEF FPC}
|
||||
{$mode ObjFPC}{$H+}
|
||||
{$ENDIF}
|
||||
|
||||
uses
|
||||
AmazingApplication, maze, Room;
|
||||
|
||||
var
|
||||
AmazingApp: TAmazingApplication;
|
||||
|
||||
begin
|
||||
AmazingApp:= TAmazingApplication.Create;
|
||||
AmazingApp.Run;
|
||||
end.
|
||||
|
||||
104
02_Amazing/pascal/object-pascal/amazingapplication.pas
Normal file
104
02_Amazing/pascal/object-pascal/amazingapplication.pas
Normal file
@@ -0,0 +1,104 @@
|
||||
unit AmazingApplication;
|
||||
|
||||
{$IFDEF FPC}
|
||||
{$mode ObjFPC}{$H+}
|
||||
{$ENDIF}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes
|
||||
, SysUtils
|
||||
, Crt
|
||||
, Maze
|
||||
;
|
||||
|
||||
type
|
||||
{ TAmazingApplication }
|
||||
TAmazingApplication = class(TObject)
|
||||
private
|
||||
FMaze: TMaze;
|
||||
|
||||
procedure PrintGreeting;
|
||||
procedure GetDimensions;
|
||||
procedure BuildMaze;
|
||||
procedure PrintMaze;
|
||||
protected
|
||||
public
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
|
||||
procedure Run;
|
||||
published
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TAmazingApplication }
|
||||
|
||||
procedure TAmazingApplication.PrintGreeting;
|
||||
begin
|
||||
WriteLN(' ':28, 'AMAZING PROGRAM');
|
||||
WriteLN(' ':15, 'CREATIVE COMPUTING MORRISTOWN, NEW JERSEY');
|
||||
WriteLN;
|
||||
WriteLN;
|
||||
WriteLN;
|
||||
WriteLN;
|
||||
end;
|
||||
|
||||
procedure TAmazingApplication.GetDimensions;
|
||||
var
|
||||
width: Integer;
|
||||
length: Integer;
|
||||
begin
|
||||
repeat
|
||||
Write('WHAT ARE YOUR WIDTH AND LENGTH (SPACE IN BETWEEN): ');
|
||||
ReadLN(width, length);
|
||||
if (width = 1) or (length = 1) then
|
||||
begin
|
||||
WriteLN('MEANINGLESS DIMENSIONS. TRY AGAIN.');
|
||||
end;
|
||||
until (width > 1) and (length > 1);
|
||||
FMaze:= TMaze.Create(width, length);
|
||||
WriteLN;
|
||||
WriteLN;
|
||||
WriteLN;
|
||||
WriteLN;
|
||||
end;
|
||||
|
||||
procedure TAmazingApplication.BuildMaze;
|
||||
begin
|
||||
FMaze.Build;
|
||||
end;
|
||||
|
||||
procedure TAmazingApplication.PrintMaze;
|
||||
begin
|
||||
FMaze.Print;
|
||||
WriteLN;
|
||||
end;
|
||||
|
||||
constructor TAmazingApplication.Create;
|
||||
begin
|
||||
//
|
||||
end;
|
||||
|
||||
destructor TAmazingApplication.Destroy;
|
||||
begin
|
||||
if Assigned(FMaze) then
|
||||
begin
|
||||
FMaze.Free;
|
||||
end;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
procedure TAmazingApplication.Run;
|
||||
begin
|
||||
//ClrScr;
|
||||
PrintGreeting;
|
||||
GetDimensions;
|
||||
BuildMaze;
|
||||
PrintMaze;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
280
02_Amazing/pascal/object-pascal/maze.pas
Normal file
280
02_Amazing/pascal/object-pascal/maze.pas
Normal file
@@ -0,0 +1,280 @@
|
||||
unit Maze;
|
||||
|
||||
{$IFDEF FPC}
|
||||
{$mode ObjFPC}{$H+}
|
||||
{$ENDIF}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes
|
||||
, SysUtils
|
||||
, Room
|
||||
;
|
||||
|
||||
type
|
||||
TDirection = (dUp, dRight, dDown, dLeft);
|
||||
TDirections = set of TDirection;
|
||||
{ TMaze }
|
||||
TMaze = class(TObject)
|
||||
private
|
||||
FWidth: Integer;
|
||||
FLength: Integer;
|
||||
FEntry: Integer;
|
||||
FLabyrinth: Array of Array of TRoom;
|
||||
|
||||
function GetRandomDirection(const ADirections: TDirections): TDirection;
|
||||
|
||||
procedure DebugVisited;
|
||||
procedure DebugWalls;
|
||||
protected
|
||||
public
|
||||
constructor Create(const AWidth, ALength: Integer);
|
||||
destructor Destroy; override;
|
||||
|
||||
procedure Build;
|
||||
procedure Print;
|
||||
published
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
const
|
||||
EXIT_DOWN = 1;
|
||||
EXIT_RIGHT = 2;
|
||||
|
||||
{ TMaze }
|
||||
|
||||
function TMaze.GetRandomDirection(const ADirections: TDirections): TDirection;
|
||||
var
|
||||
count: Integer;
|
||||
position: Integer;
|
||||
directions: array [0..3] of TDirection;
|
||||
begin
|
||||
count:= 0;
|
||||
position:= 0;
|
||||
if dUp in ADirections then
|
||||
begin
|
||||
Inc(count);
|
||||
directions[position]:= dUp;
|
||||
Inc(position);
|
||||
end;
|
||||
if dRight in ADirections then
|
||||
begin
|
||||
Inc(count);
|
||||
directions[position]:= dRight;
|
||||
Inc(position);
|
||||
end;
|
||||
if dDown in ADirections then
|
||||
begin
|
||||
Inc(count);
|
||||
directions[position]:= dDown;
|
||||
Inc(position);
|
||||
end;
|
||||
if dLeft in ADirections then
|
||||
begin
|
||||
Inc(count);
|
||||
directions[position]:= dLeft;
|
||||
Inc(position);
|
||||
end;
|
||||
Result:= directions[Random(count)];
|
||||
end;
|
||||
|
||||
procedure TMaze.DebugVisited;
|
||||
var
|
||||
indexW: Integer;
|
||||
indexL: Integer;
|
||||
begin
|
||||
WriteLN('Visited');
|
||||
for indexL:= 0 to Pred(FLength) do
|
||||
begin
|
||||
for indexW:= 0 to Pred(FWidth) do
|
||||
begin
|
||||
Write(FLabyrinth[indexW][indexL].Visited:3,' ');
|
||||
end;
|
||||
WriteLN;
|
||||
end;
|
||||
WriteLN;
|
||||
end;
|
||||
|
||||
procedure TMaze.DebugWalls;
|
||||
var
|
||||
indexW: Integer;
|
||||
indexL: Integer;
|
||||
begin
|
||||
WriteLN('Walls');
|
||||
for indexL:= 0 to Pred(FLength) do
|
||||
begin
|
||||
for indexW:= 0 to Pred(FWidth) do
|
||||
begin
|
||||
Write(FLabyrinth[indexW][indexL].Walls:3,' ');
|
||||
end;
|
||||
WriteLN;
|
||||
end;
|
||||
WriteLN;
|
||||
end;
|
||||
|
||||
constructor TMaze.Create(const AWidth, ALength: Integer);
|
||||
var
|
||||
indexW: Integer;
|
||||
indexL: Integer;
|
||||
begin
|
||||
Randomize;
|
||||
FWidth:= AWidth;
|
||||
FLength:= ALength;
|
||||
FEntry:= Random(FWidth);
|
||||
SetLength(FLabyrinth, FWidth, FLength);
|
||||
for indexW:= 0 to Pred(FWidth) do
|
||||
begin
|
||||
for indexL:= 0 to Pred(FLength) do
|
||||
begin
|
||||
FLabyrinth[indexW][indexL]:= TRoom.Create;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
destructor TMaze.Destroy;
|
||||
var
|
||||
indexW: Integer;
|
||||
indexL: Integer;
|
||||
begin
|
||||
for indexW:= 0 to Pred(FWidth) do
|
||||
begin
|
||||
for indexL:= 0 to Pred(FLength) do
|
||||
begin
|
||||
if Assigned(FLabyrinth[indexW][indexL]) then
|
||||
begin
|
||||
FLabyrinth[indexW][indexL].Free;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
procedure TMaze.Build;
|
||||
var
|
||||
indexW: Integer;
|
||||
indexL: Integer;
|
||||
direction: TDirection;
|
||||
directions: TDirections;
|
||||
count: Integer;
|
||||
begin
|
||||
FEntry:= Random(FWidth);
|
||||
indexW:= FEntry;
|
||||
indexL:= 0;
|
||||
count:= 1;
|
||||
FLabyrinth[indexW][indexL].Visited:= count;
|
||||
Inc(count);
|
||||
repeat
|
||||
directions:= [dUp, dRight, dDown, dLeft];
|
||||
if (indexW = 0) or (FLabyrinth[Pred(indexW)][indexL].Visited <> 0) then
|
||||
begin
|
||||
Exclude(directions, dLeft);
|
||||
end;
|
||||
if (indexL = 0) or (FLabyrinth[indexW][Pred(indexL)].Visited <> 0) then
|
||||
begin
|
||||
Exclude(directions, dUp);
|
||||
end;
|
||||
if (indexW = Pred(FWidth)) or (FLabyrinth[Succ(indexW)][indexL].Visited <> 0) then
|
||||
begin
|
||||
Exclude(directions, dRight);
|
||||
end;
|
||||
if (indexL = Pred(FLength)) or (FLabyrinth[indexW][Succ(indexL)].Visited <> 0) then
|
||||
begin
|
||||
Exclude(directions, dDown);
|
||||
end;
|
||||
|
||||
if directions <> [] then
|
||||
begin
|
||||
direction:= GetRandomDirection(directions);
|
||||
case direction of
|
||||
dLeft:begin
|
||||
Dec(indexW);
|
||||
FLabyrinth[indexW][indexL].Walls:= EXIT_RIGHT;
|
||||
end;
|
||||
dUp:begin
|
||||
Dec(indexL);
|
||||
FLabyrinth[indexW][indexL].Walls:= EXIT_DOWN;
|
||||
end;
|
||||
dRight:begin
|
||||
FLabyrinth[indexW][indexL].Walls:= FLabyrinth[indexW][indexL].Walls + EXIT_RIGHT;
|
||||
Inc(indexW);
|
||||
end;
|
||||
dDown:begin
|
||||
FLabyrinth[indexW][indexL].Walls:= FLabyrinth[indexW][indexL].Walls + EXIT_DOWN;
|
||||
Inc(indexL);
|
||||
end;
|
||||
end;
|
||||
FLabyrinth[indexW][indexL].Visited:= count;
|
||||
Inc(count);
|
||||
end
|
||||
else
|
||||
begin
|
||||
while True do
|
||||
begin
|
||||
if indexW <> Pred(FWidth) then
|
||||
begin
|
||||
Inc(indexW);
|
||||
end
|
||||
else if indexL <> Pred(FLength) then
|
||||
begin
|
||||
Inc(indexL);
|
||||
indexW:= 0;
|
||||
end
|
||||
else
|
||||
begin
|
||||
indexW:= 0;
|
||||
indexL:= 0;
|
||||
end;
|
||||
if FLabyrinth[indexW][indexL].Visited <> 0 then
|
||||
begin
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
until count = (FWidth * FLength) + 1;
|
||||
indexW:= Random(FWidth);
|
||||
indexL:= Pred(FLength);
|
||||
FLabyrinth[indexW][indexL].Walls:= FLabyrinth[indexW][indexL].Walls + 1;
|
||||
end;
|
||||
|
||||
procedure TMaze.Print;
|
||||
var
|
||||
indexW:Integer;
|
||||
indexL: Integer;
|
||||
begin
|
||||
|
||||
//DebugVisited;
|
||||
//DebugWalls;
|
||||
|
||||
for indexW:= 0 to Pred(FWidth) do
|
||||
begin
|
||||
if indexW = FEntry then
|
||||
begin
|
||||
Write('. ');
|
||||
end
|
||||
else
|
||||
begin
|
||||
Write('.--');
|
||||
end;
|
||||
end;
|
||||
WriteLN('.');
|
||||
|
||||
for indexL:= 0 to Pred(FLength) do
|
||||
begin
|
||||
Write('I');
|
||||
for indexW:= 0 to Pred(FWidth) do
|
||||
begin
|
||||
FLabyrinth[indexW][indexL].PrintRoom;
|
||||
end;
|
||||
WriteLN;
|
||||
for indexW:= 0 to Pred(FWidth) do
|
||||
begin
|
||||
FLabyrinth[indexW][indexL].PrintWall;
|
||||
end;
|
||||
WriteLN('.');
|
||||
end;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
71
02_Amazing/pascal/object-pascal/room.pas
Normal file
71
02_Amazing/pascal/object-pascal/room.pas
Normal file
@@ -0,0 +1,71 @@
|
||||
unit Room;
|
||||
|
||||
{$IFDEF FPC}
|
||||
{$mode ObjFPC}{$H+}
|
||||
{$ENDIF}
|
||||
|
||||
interface
|
||||
|
||||
uses
|
||||
Classes
|
||||
, SysUtils
|
||||
;
|
||||
|
||||
type
|
||||
{ TRoom }
|
||||
TRoom = class(TObject)
|
||||
private
|
||||
FVisited: Integer;
|
||||
FWalls: Integer;
|
||||
protected
|
||||
public
|
||||
constructor Create;
|
||||
|
||||
procedure PrintRoom;
|
||||
procedure PrintWall;
|
||||
|
||||
property Visited: Integer
|
||||
read FVisited
|
||||
write FVisited;
|
||||
property Walls: Integer
|
||||
read FWalls
|
||||
write FWalls;
|
||||
published
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
{ TRoom }
|
||||
|
||||
constructor TRoom.Create;
|
||||
begin
|
||||
FVisited:= 0;
|
||||
FWalls:= 0;
|
||||
end;
|
||||
|
||||
procedure TRoom.PrintRoom;
|
||||
begin
|
||||
if FWalls < 2 then
|
||||
begin
|
||||
Write(' I');
|
||||
end
|
||||
else
|
||||
begin
|
||||
Write(' ');
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TRoom.PrintWall;
|
||||
begin
|
||||
if (FWalls = 0) or (FWalls = 2) then
|
||||
begin
|
||||
Write(':--');
|
||||
end
|
||||
else
|
||||
begin
|
||||
Write(': ');
|
||||
end;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
58
02_Amazing/pascal/simple/amazing.lpi
Normal file
58
02_Amazing/pascal/simple/amazing.lpi
Normal file
@@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<CONFIG>
|
||||
<ProjectOptions>
|
||||
<Version Value="12"/>
|
||||
<General>
|
||||
<Flags>
|
||||
<MainUnitHasCreateFormStatements Value="False"/>
|
||||
<MainUnitHasTitleStatement Value="False"/>
|
||||
<MainUnitHasScaledStatement Value="False"/>
|
||||
<CompatibilityMode Value="True"/>
|
||||
</Flags>
|
||||
<SessionStorage Value="InProjectDir"/>
|
||||
<Title Value="amazing"/>
|
||||
<UseAppBundle Value="False"/>
|
||||
<ResourceType Value="res"/>
|
||||
</General>
|
||||
<BuildModes Count="1">
|
||||
<Item1 Name="Default" Default="True"/>
|
||||
</BuildModes>
|
||||
<PublishOptions>
|
||||
<Version Value="2"/>
|
||||
<UseFileFilters Value="True"/>
|
||||
</PublishOptions>
|
||||
<RunParams>
|
||||
<FormatVersion Value="2"/>
|
||||
</RunParams>
|
||||
<Units Count="1">
|
||||
<Unit0>
|
||||
<Filename Value="amazing.pas"/>
|
||||
<IsPartOfProject Value="True"/>
|
||||
<UnitName Value="Amazing"/>
|
||||
</Unit0>
|
||||
</Units>
|
||||
</ProjectOptions>
|
||||
<CompilerOptions>
|
||||
<Version Value="11"/>
|
||||
<Target>
|
||||
<Filename Value="amazing"/>
|
||||
</Target>
|
||||
<SearchPaths>
|
||||
<IncludeFiles Value="$(ProjOutDir)"/>
|
||||
<UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
|
||||
</SearchPaths>
|
||||
</CompilerOptions>
|
||||
<Debugging>
|
||||
<Exceptions Count="3">
|
||||
<Item1>
|
||||
<Name Value="EAbort"/>
|
||||
</Item1>
|
||||
<Item2>
|
||||
<Name Value="ECodetoolError"/>
|
||||
</Item2>
|
||||
<Item3>
|
||||
<Name Value="EFOpenError"/>
|
||||
</Item3>
|
||||
</Exceptions>
|
||||
</Debugging>
|
||||
</CONFIG>
|
||||
284
02_Amazing/pascal/simple/amazing.pas
Normal file
284
02_Amazing/pascal/simple/amazing.pas
Normal file
@@ -0,0 +1,284 @@
|
||||
program Amazing;
|
||||
|
||||
{$IFDEF FPC}
|
||||
{$mode objfpc}{$H+}
|
||||
{$ENDIF}
|
||||
|
||||
uses
|
||||
Crt;
|
||||
|
||||
type
|
||||
TDirection = (dUp, dRight, dDown, dLeft);
|
||||
TDirections = set of TDirection;
|
||||
|
||||
var
|
||||
Width: Integer; // H
|
||||
Length: Integer; // V
|
||||
Entry: Integer;
|
||||
MatrixWalls: Array of Array of Integer;
|
||||
MatrixVisited: Array of Array of Integer;
|
||||
|
||||
const
|
||||
EXIT_DOWN = 1;
|
||||
EXIT_RIGHT = 2;
|
||||
|
||||
procedure PrintGreeting;
|
||||
begin
|
||||
WriteLN(' ':28, 'AMAZING PROGRAM');
|
||||
WriteLN(' ':15, 'CREATIVE COMPUTING MORRISTOWN, NEW JERSEY');
|
||||
WriteLN;
|
||||
WriteLN;
|
||||
WriteLN;
|
||||
WriteLN;
|
||||
end;
|
||||
|
||||
procedure GetDimensions;
|
||||
begin
|
||||
repeat
|
||||
Write('WHAT ARE YOUR WIDTH AND LENGTH (SPACE IN BETWEEN): ');
|
||||
ReadLN(Width, Length);
|
||||
if (Width = 1) or (Length = 1) then
|
||||
begin
|
||||
WriteLN('MEANINGLESS DIMENSIONS. TRY AGAIN.');
|
||||
end;
|
||||
until (Width > 1) and (Length > 1);
|
||||
WriteLN;
|
||||
WriteLN;
|
||||
WriteLN;
|
||||
WriteLN;
|
||||
end;
|
||||
|
||||
procedure ClearMatrices;
|
||||
var
|
||||
indexW: Integer;
|
||||
indexL: Integer;
|
||||
begin
|
||||
SetLength(MatrixWalls, Width, Length);
|
||||
SetLength(MatrixVisited, Width, Length);
|
||||
for indexW:= 0 to Pred(Width) do
|
||||
begin
|
||||
for indexL:= 0 to Pred(Length) do
|
||||
begin
|
||||
MatrixWalls[indexW][indexL]:= 0;
|
||||
MatrixVisited[indexW][indexL]:= 0;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function GetRandomDirection(const ADirections: TDirections): TDirection;
|
||||
var
|
||||
count: Integer;
|
||||
position: Integer;
|
||||
directions: array [0..3] of TDirection;
|
||||
begin
|
||||
count:= 0;
|
||||
position:= 0;
|
||||
if dUp in ADirections then
|
||||
begin
|
||||
Inc(count);
|
||||
directions[position]:= dUp;
|
||||
Inc(position);
|
||||
end;
|
||||
if dRight in ADirections then
|
||||
begin
|
||||
Inc(count);
|
||||
directions[position]:= dRight;
|
||||
Inc(position);
|
||||
end;
|
||||
if dDown in ADirections then
|
||||
begin
|
||||
Inc(count);
|
||||
directions[position]:= dDown;
|
||||
Inc(position);
|
||||
end;
|
||||
if dLeft in ADirections then
|
||||
begin
|
||||
Inc(count);
|
||||
directions[position]:= dLeft;
|
||||
Inc(position);
|
||||
end;
|
||||
Result:= directions[Random(count)];
|
||||
end;
|
||||
|
||||
procedure BuildMaze;
|
||||
var
|
||||
indexW: Integer;
|
||||
indexL: Integer;
|
||||
direction: TDirection;
|
||||
directions: TDirections;
|
||||
count: Integer;
|
||||
begin
|
||||
Entry:= Random(Width);
|
||||
indexW:= Entry;
|
||||
indexL:= 0;
|
||||
count:= 1;
|
||||
MatrixVisited[indexW][indexL]:= count;
|
||||
Inc(count);
|
||||
repeat
|
||||
directions:= [dUp, dRight, dDown, dLeft];
|
||||
if (indexW = 0) or (MatrixVisited[Pred(indexW)][indexL] <> 0) then
|
||||
begin
|
||||
Exclude(directions, dLeft);
|
||||
end;
|
||||
if (indexL = 0) or (MatrixVisited[indexW][Pred(indexL)] <> 0) then
|
||||
begin
|
||||
Exclude(directions, dUp);
|
||||
end;
|
||||
if (indexW = Pred(Width)) or (MatrixVisited[Succ(indexW)][indexL] <> 0) then
|
||||
begin
|
||||
Exclude(directions, dRight);
|
||||
end;
|
||||
if (indexL = Pred(Length)) or (MatrixVisited[indexW][Succ(indexL)] <> 0) then
|
||||
begin
|
||||
Exclude(directions, dDown);
|
||||
end;
|
||||
|
||||
if directions <> [] then
|
||||
begin
|
||||
direction:= GetRandomDirection(directions);
|
||||
case direction of
|
||||
dLeft:begin
|
||||
Dec(indexW);
|
||||
MatrixWalls[indexW][indexL]:= EXIT_RIGHT;
|
||||
end;
|
||||
dUp:begin
|
||||
Dec(indexL);
|
||||
MatrixWalls[indexW][indexL]:= EXIT_DOWN;
|
||||
end;
|
||||
dRight:begin
|
||||
Inc(MatrixWalls[indexW][indexL], EXIT_RIGHT);
|
||||
Inc(indexW);
|
||||
end;
|
||||
dDown:begin
|
||||
Inc(MatrixWalls[indexW][indexL], EXIT_DOWN);
|
||||
Inc(indexL);
|
||||
end;
|
||||
end;
|
||||
MatrixVisited[indexW][indexL]:= count;
|
||||
Inc(count);
|
||||
end
|
||||
else
|
||||
begin
|
||||
while True do
|
||||
begin
|
||||
if indexW <> Pred(Width) then
|
||||
begin
|
||||
Inc(indexW);
|
||||
end
|
||||
else if indexL <> Pred(Length) then
|
||||
begin
|
||||
Inc(indexL);
|
||||
indexW:= 0;
|
||||
end
|
||||
else
|
||||
begin
|
||||
indexW:= 0;
|
||||
indexL:= 0;
|
||||
end;
|
||||
if MatrixVisited[indexW][indexL] <> 0 then
|
||||
begin
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
until count = (Width * Length) + 1;
|
||||
indexW:= Random(Width);
|
||||
indexL:= Pred(Length);
|
||||
Inc(MatrixWalls[indexW][indexL]);
|
||||
end;
|
||||
|
||||
procedure DegubVisited;
|
||||
var
|
||||
indexW: Integer;
|
||||
indexL: Integer;
|
||||
begin
|
||||
WriteLN('Visited');
|
||||
for indexL:= 0 to Pred(Length) do
|
||||
begin
|
||||
for indexW:= 0 to Pred(Width) do
|
||||
begin
|
||||
Write(MatrixVisited[indexW][indexL]:2,' ');
|
||||
end;
|
||||
WriteLN;
|
||||
end;
|
||||
WriteLN;
|
||||
end;
|
||||
|
||||
procedure DebugWalls;
|
||||
var
|
||||
indexW: Integer;
|
||||
indexL: Integer;
|
||||
begin
|
||||
WriteLN('Walls');
|
||||
for indexL:= 0 to Pred(Length) do
|
||||
begin
|
||||
for indexW:= 0 to Pred(Width) do
|
||||
begin
|
||||
Write(MatrixWalls[indexW, indexL]:2, ' ');
|
||||
end;
|
||||
WriteLN;
|
||||
end;
|
||||
WriteLN;
|
||||
end;
|
||||
|
||||
procedure PrintMaze;
|
||||
var
|
||||
indexW: Integer;
|
||||
indexL: Integer;
|
||||
begin
|
||||
|
||||
for indexW:= 0 to Pred(Width) do
|
||||
begin
|
||||
if indexW = Entry then
|
||||
begin
|
||||
Write('. ');
|
||||
end
|
||||
else
|
||||
begin
|
||||
Write('.--');
|
||||
end;
|
||||
end;
|
||||
WriteLN('.');
|
||||
for indexL:= 0 to Pred(Length) do
|
||||
begin
|
||||
Write('I');
|
||||
for indexW:= 0 to Pred(Width) do
|
||||
begin
|
||||
if MatrixWalls[indexW, indexL] < 2 then
|
||||
begin
|
||||
Write(' I');
|
||||
end
|
||||
else
|
||||
begin
|
||||
Write(' ');
|
||||
end;
|
||||
end;
|
||||
WriteLN;
|
||||
for indexW:= 0 to Pred(Width) do
|
||||
begin
|
||||
if (MatrixWalls[indexW, indexL] = 0) or (MatrixWalls[indexW, indexL] = 2) then
|
||||
begin
|
||||
Write(':--');
|
||||
end
|
||||
else
|
||||
begin
|
||||
Write(': ');
|
||||
end;
|
||||
end;
|
||||
WriteLN('.');
|
||||
end;
|
||||
WriteLN;
|
||||
end;
|
||||
|
||||
begin
|
||||
Randomize;
|
||||
ClrScr;
|
||||
PrintGreeting;
|
||||
GetDimensions;
|
||||
ClearMatrices;
|
||||
BuildMaze;
|
||||
//DegubVisited;
|
||||
//DebugWalls;
|
||||
PrintMaze;
|
||||
end.
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
|
||||
|
||||
Conversion to [Ruby](https://www.ruby-lang.org/en/)
|
||||
Conversion to [Perl](https://www.perl.org/)
|
||||
159
02_Amazing/perl/amazing.pl
Executable file
159
02_Amazing/perl/amazing.pl
Executable file
@@ -0,0 +1,159 @@
|
||||
#! /usr/bin/perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# Translated from BASIC by Alex Kapranoff
|
||||
|
||||
use feature qw/say/;
|
||||
|
||||
# width and height of the maze
|
||||
my ($width, $height) = input_dimensions();
|
||||
|
||||
# wall masks for all cells
|
||||
my @walls;
|
||||
|
||||
# flags of previous visitation for all cells
|
||||
my %is_visited;
|
||||
|
||||
# was the path out of the maze found?
|
||||
my $path_found = 0;
|
||||
|
||||
# column of entry to the maze in the top line
|
||||
my $entry_col = int(rand($width));
|
||||
|
||||
# cell coordinates for traversal
|
||||
my $col = $entry_col;
|
||||
my $row = 0;
|
||||
|
||||
$is_visited{$row, $col} = 1;
|
||||
|
||||
# looping until we visit every cell
|
||||
while (keys %is_visited < $width * $height) {
|
||||
if (my @dirs = get_possible_directions()) {
|
||||
my $dir = $dirs[rand @dirs];
|
||||
|
||||
# modify current cell wall if needed
|
||||
$walls[$row]->[$col] |= $dir->[2];
|
||||
|
||||
# move the position
|
||||
$row += $dir->[0];
|
||||
$col += $dir->[1];
|
||||
|
||||
# we found the exit!
|
||||
if ($row == $height) {
|
||||
$path_found = 1;
|
||||
--$row;
|
||||
|
||||
if ($walls[$row]->[$col] == 1) {
|
||||
($row, $col) = get_next_branch(0, 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
# modify the new cell wall if needed
|
||||
$walls[$row]->[$col] |= $dir->[3];
|
||||
|
||||
$is_visited{$row, $col} = 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
($row, $col) = get_next_branch($row, $col);
|
||||
}
|
||||
}
|
||||
|
||||
unless ($path_found) {
|
||||
$walls[-1]->[rand $width] |= 1;
|
||||
}
|
||||
|
||||
print_maze();
|
||||
|
||||
sub input_dimensions {
|
||||
# Print the banner and returns the dimensions as two integers > 1.
|
||||
# The integers are parsed from the first line of standard input.
|
||||
say ' ' x 28, 'AMAZING PROGRAM';
|
||||
say ' ' x 15, 'CREATIVE COMPUTING MORRISTOWN, NEW JERSEY';
|
||||
print "\n" x 4;
|
||||
|
||||
my ($w, $h) = (0, 0);
|
||||
|
||||
while ($w <= 1 || $h <= 1) {
|
||||
print 'WHAT ARE YOUR WIDTH AND LENGTH? ';
|
||||
|
||||
($w, $h) = <STDIN> =~ / \d+ /xg;
|
||||
|
||||
if ($w < 1 || $h < 1) {
|
||||
say "MEANINGLESS DIMENSIONS. TRY AGAIN."
|
||||
}
|
||||
}
|
||||
|
||||
print "\n" x 4;
|
||||
|
||||
return ($w, $h);
|
||||
}
|
||||
|
||||
sub get_possible_directions {
|
||||
# Returns a list of all directions that are available to go to
|
||||
# from the current coordinates. "Down" is available on the last line
|
||||
# until we go there once and mark it as the path through the maze.
|
||||
#
|
||||
# Each returned direction element contains changes to the coordindates and to
|
||||
# the wall masks of the previous and next cell after the move.
|
||||
|
||||
my @rv;
|
||||
# up
|
||||
if ($row > 0 && !$is_visited{$row - 1, $col}) {
|
||||
push @rv, [-1, 0, 0, 1];
|
||||
}
|
||||
# left
|
||||
if ($col > 0 && !$is_visited{$row, $col - 1}) {
|
||||
push @rv, [0, -1, 0, 2];
|
||||
}
|
||||
# right
|
||||
if ($col < $width - 1 && !$is_visited{$row, $col + 1}) {
|
||||
push @rv, [0, 1, 2, 0];
|
||||
}
|
||||
# down
|
||||
if ($row < $height - 1 && !$is_visited{$row + 1, $col}
|
||||
|| $row == $height - 1 && !$path_found
|
||||
) {
|
||||
push @rv, [1, 0, 1, 0];
|
||||
}
|
||||
|
||||
return @rv;
|
||||
}
|
||||
|
||||
sub get_next_branch {
|
||||
# Returns the cell coordinates to start a new maze branch from.
|
||||
# It looks for a visited cell starting from passed position and
|
||||
# going down in the natural traversal order incrementing column and
|
||||
# rows with a rollover to start at the bottom right corner.
|
||||
my ($y, $x) = @_;
|
||||
do {
|
||||
if ($x < $width - 1) {
|
||||
++$x;
|
||||
} elsif ($y < $height - 1) {
|
||||
($y, $x) = ($y + 1, 0);
|
||||
} else {
|
||||
($y, $x) = (0, 0);
|
||||
}
|
||||
} while (!$is_visited{$y, $x});
|
||||
|
||||
return ($y, $x);
|
||||
}
|
||||
|
||||
sub print_maze {
|
||||
# Print the full maze based on wall masks.
|
||||
# For each cell, we mark the absense of the wall to the right with
|
||||
# bit 2 and the absense of the wall down with bit 1. Full table:
|
||||
# 0 -> both walls are present
|
||||
# 1 -> wall down is absent
|
||||
# 2 -> wall to the right is absent
|
||||
# 3 -> both walls are absent
|
||||
say join('.', '', map { $_ == $entry_col ? ' ' : '--' } 0 .. $width - 1), '.';
|
||||
|
||||
for my $row (@walls) {
|
||||
say join(' ', map { $_ & 2 ? ' ' : 'I' } 0, @$row);
|
||||
say join(':', '', map { $_ & 1 ? ' ' : '--' } @$row), '.';
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
121
02_Amazing/python/amazing.py
Normal file
121
02_Amazing/python/amazing.py
Normal file
@@ -0,0 +1,121 @@
|
||||
import random
|
||||
|
||||
# Python translation by Frank Palazzolo - 2/2021
|
||||
|
||||
print(' '*28+'AMAZING PROGRAM')
|
||||
print(' '*15+'CREATIVE COMPUTING MORRISTOWN, NEW JERSEY')
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
|
||||
while True:
|
||||
width, length = input('What are your width and length?').split(',')
|
||||
width = int(width)
|
||||
length = int(length)
|
||||
if width != 1 and length != 1:
|
||||
break
|
||||
print('Meaningless dimensions. Try again.')
|
||||
|
||||
# Build two 2D arrays
|
||||
#
|
||||
# used:
|
||||
# Initially set to zero, unprocessed cells
|
||||
# Filled in with consecutive non-zero numbers as cells are processed
|
||||
#
|
||||
# walls:
|
||||
# Initially set to zero, (all paths blocked)
|
||||
# Remains 0 if there is no exit down or right
|
||||
# Set to 1 if there is an exit down
|
||||
# Set to 2 if there is an exit right
|
||||
# Set to 3 if there are exits down and right
|
||||
|
||||
used = []
|
||||
walls = []
|
||||
for i in range(length):
|
||||
used.append([0]*width)
|
||||
walls.append([0]*width)
|
||||
|
||||
# Use direction variables with nice names
|
||||
GO_LEFT,GO_UP,GO_RIGHT,GO_DOWN=[0,1,2,3]
|
||||
# Give Exit directions nice names
|
||||
EXIT_DOWN = 1
|
||||
EXIT_RIGHT = 2
|
||||
|
||||
# Pick a random entrance, mark as used
|
||||
enter_col=random.randint(0,width-1)
|
||||
row,col=0,enter_col
|
||||
count=1
|
||||
used[row][col]=count
|
||||
count=count+1
|
||||
|
||||
while count!=width*length+1:
|
||||
# remove possible directions that are blocked or
|
||||
# hit cells that we have already processed
|
||||
possible_dirs = [GO_LEFT,GO_UP,GO_RIGHT,GO_DOWN]
|
||||
if col==0 or used[row][col-1]!=0:
|
||||
possible_dirs.remove(GO_LEFT)
|
||||
if row==0 or used[row-1][col]!=0:
|
||||
possible_dirs.remove(GO_UP)
|
||||
if col==width-1 or used[row][col+1]!=0:
|
||||
possible_dirs.remove(GO_RIGHT)
|
||||
if row==length-1 or used[row+1][col]!=0:
|
||||
possible_dirs.remove(GO_DOWN)
|
||||
|
||||
# If we can move in a direction, move and make opening
|
||||
if len(possible_dirs)!=0:
|
||||
direction=random.choice(possible_dirs)
|
||||
if direction==GO_LEFT:
|
||||
col=col-1
|
||||
walls[row][col]=EXIT_RIGHT
|
||||
elif direction==GO_UP:
|
||||
row=row-1
|
||||
walls[row][col]=EXIT_DOWN
|
||||
elif direction==GO_RIGHT:
|
||||
walls[row][col]=walls[row][col]+EXIT_RIGHT
|
||||
col=col+1
|
||||
elif direction==GO_DOWN:
|
||||
walls[row][col]=walls[row][col]+EXIT_DOWN
|
||||
row=row+1
|
||||
used[row][col]=count
|
||||
count=count+1
|
||||
# otherwise, move to the next used cell, and try again
|
||||
else:
|
||||
while True:
|
||||
if col!=width-1:
|
||||
col=col+1
|
||||
elif row!=length-1:
|
||||
row,col=row+1,0
|
||||
else:
|
||||
row,col=0,0
|
||||
if used[row][col]!=0:
|
||||
break
|
||||
|
||||
# Add a random exit
|
||||
col=random.randint(0,width-1)
|
||||
row=length-1
|
||||
walls[row][col]=walls[row][col]+1
|
||||
|
||||
# Print the maze
|
||||
for col in range(width):
|
||||
if col==enter_col:
|
||||
print('. ',end='')
|
||||
else:
|
||||
print('.--',end='')
|
||||
print('.')
|
||||
for row in range(length):
|
||||
print('I',end='')
|
||||
for col in range(width):
|
||||
if walls[row][col]<2:
|
||||
print(' I',end='')
|
||||
else:
|
||||
print(' ',end='')
|
||||
print()
|
||||
for col in range(width):
|
||||
if walls[row][col]==0 or walls[row][col]==2:
|
||||
print(':--',end='')
|
||||
else:
|
||||
print(': ',end='')
|
||||
print('.')
|
||||
|
||||
|
||||
|
||||
9
02_Amazing/ruby/README.md
Normal file
9
02_Amazing/ruby/README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
|
||||
|
||||
Conversion to [Ruby](https://www.ruby-lang.org/en/)
|
||||
|
||||
Converted to Ruby (with tons of inspiration from the Python version) by @marcheiligers
|
||||
|
||||
Run `ruby amazing.rb`.
|
||||
|
||||
Run `DEBUG=1 ruby amazing.ruby` to see how it works (requires at least Ruby 2.7).
|
||||
216
02_Amazing/ruby/amazing.rb
Normal file
216
02_Amazing/ruby/amazing.rb
Normal file
@@ -0,0 +1,216 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
DEBUG = !ENV['DEBUG'].nil?
|
||||
|
||||
require 'io/console' if DEBUG
|
||||
|
||||
# BASIC arrays are 1-based, unlike Ruby 0-based arrays,
|
||||
# and this class simulates that. BASIC arrays are zero-filled,
|
||||
# which is also done here. While we could easily update the
|
||||
# algorithm to work with zero-based arrays, this class makes
|
||||
# the problem easier to reason about, row or col 1 are the
|
||||
# first row or column.
|
||||
class BasicArrayTwoD
|
||||
def initialize(rows, cols)
|
||||
@val = Array.new(rows) { Array.new(cols, 0) }
|
||||
end
|
||||
|
||||
def [](row, col = nil)
|
||||
if col
|
||||
@val[row - 1][col - 1]
|
||||
else
|
||||
@val[row - 1]
|
||||
end
|
||||
end
|
||||
|
||||
def []=(row, col, n)
|
||||
@val[row - 1][col - 1] = n
|
||||
end
|
||||
|
||||
def to_s(width: max_width, row_hilite: nil, col_hilite: nil)
|
||||
@val.map.with_index do |row, row_index|
|
||||
row.map.with_index do |val, col_index|
|
||||
if row_hilite == row_index + 1 && col_hilite == col_index + 1
|
||||
"[#{val.to_s.center(width)}]"
|
||||
else
|
||||
val.to_s.center(width + 2)
|
||||
end
|
||||
end.join
|
||||
end.join("\n")
|
||||
end
|
||||
|
||||
def max_width
|
||||
@val.flat_map { |row| row.map { |val| val.to_s.length } }.sort.last
|
||||
end
|
||||
end
|
||||
|
||||
class Maze
|
||||
EXIT_DOWN = 1
|
||||
EXIT_RIGHT = 2
|
||||
|
||||
# Set up a constant hash for directions
|
||||
# The values represent the direction of the move as changes to row, col
|
||||
# and the type of exit when moving in that direction
|
||||
DIRECTIONS = {
|
||||
left: { row: 0, col: -1, exit: EXIT_RIGHT },
|
||||
up: { row: -1, col: 0, exit: EXIT_DOWN },
|
||||
right: { row: 0, col: 1, exit: EXIT_RIGHT },
|
||||
down: { row: 1, col: 0, exit: EXIT_DOWN }
|
||||
}.freeze
|
||||
|
||||
attr_reader :width, :height, :used, :walls, :entry
|
||||
|
||||
def initialize(width, height)
|
||||
@width = width
|
||||
@height = height
|
||||
|
||||
@used = BasicArrayTwoD.new(height, width)
|
||||
@walls = BasicArrayTwoD.new(height, width)
|
||||
|
||||
create
|
||||
end
|
||||
|
||||
def draw
|
||||
# Print the maze
|
||||
draw_top(entry, width)
|
||||
(1..height - 1).each do |row|
|
||||
draw_row(walls[row])
|
||||
end
|
||||
draw_bottom(walls[height])
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def create
|
||||
# entry represents the location of the opening
|
||||
@entry = (rand * width).round + 1
|
||||
|
||||
# Set up our current row and column, starting at the top and the locations of the opening
|
||||
row = 1
|
||||
col = entry
|
||||
c = 1
|
||||
used[row, col] = c # This marks the opening in the first row
|
||||
c += 1
|
||||
|
||||
while c != width * height + 1 do
|
||||
debug walls, row, col
|
||||
# remove possible directions that are blocked or
|
||||
# hit cells that we have already processed
|
||||
possible_dirs = DIRECTIONS.reject do |dir, change|
|
||||
nrow = row + change[:row]
|
||||
ncol = col + change[:col]
|
||||
nrow < 1 || nrow > height || ncol < 1 || ncol > width || used[nrow, ncol] != 0
|
||||
end.keys
|
||||
|
||||
# If we can move in a direction, move and make opening
|
||||
if possible_dirs.size != 0
|
||||
direction = possible_dirs.sample
|
||||
change = DIRECTIONS[direction] # pick a random direction
|
||||
if %i[left up].include?(direction)
|
||||
row += change[:row]
|
||||
col += change[:col]
|
||||
walls[row, col] = change[:exit]
|
||||
else
|
||||
walls[row, col] += change[:exit]
|
||||
row += change[:row]
|
||||
col += change[:col]
|
||||
end
|
||||
used[row, col] = c
|
||||
c = c + 1
|
||||
# otherwise, move to the next used cell, and try again
|
||||
else
|
||||
loop do
|
||||
if col != width
|
||||
col += 1
|
||||
elsif row != height
|
||||
row += 1
|
||||
col = 1
|
||||
else
|
||||
row = col = 1
|
||||
end
|
||||
break if used[row, col] != 0
|
||||
debug walls, row, col
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Add a random exit
|
||||
walls[height, (rand * width).round] += 1
|
||||
end
|
||||
|
||||
def draw_top(entry, width)
|
||||
(1..width).each do |i|
|
||||
if i == entry
|
||||
print i == 1 ? '┏ ' : '┳ '
|
||||
else
|
||||
print i == 1 ? '┏━━' : '┳━━'
|
||||
end
|
||||
end
|
||||
|
||||
puts '┓'
|
||||
end
|
||||
|
||||
def draw_row(row)
|
||||
print '┃'
|
||||
row.each.with_index do |val, col|
|
||||
print val < 2 ? ' ┃' : ' '
|
||||
end
|
||||
puts
|
||||
row.each.with_index do |val, col|
|
||||
print val == 0 || val == 2 ? (col == 0 ? '┣━━' : '╋━━') : (col == 0 ? '┃ ' : '┫ ')
|
||||
end
|
||||
puts '┫'
|
||||
end
|
||||
|
||||
def draw_bottom(row)
|
||||
print '┃'
|
||||
row.each.with_index do |val, col|
|
||||
print val < 2 ? ' ┃' : ' '
|
||||
end
|
||||
puts
|
||||
row.each.with_index do |val, col|
|
||||
print val == 0 || val == 2 ? (col == 0 ? '┗━━' : '┻━━') : (col == 0 ? '┗ ' : '┻ ')
|
||||
end
|
||||
puts '┛'
|
||||
end
|
||||
|
||||
def debug(walls, row, col)
|
||||
return unless DEBUG
|
||||
|
||||
STDOUT.clear_screen
|
||||
puts walls.to_s(row_hilite: row, col_hilite: col)
|
||||
sleep 0.1
|
||||
end
|
||||
end
|
||||
|
||||
class Amazing
|
||||
def run
|
||||
draw_header
|
||||
|
||||
width, height = ask_dimensions
|
||||
while width <= 1 || height <= 1
|
||||
puts "MEANINGLESS DIMENSIONS. TRY AGAIN."
|
||||
width, height = ask_dimensions
|
||||
end
|
||||
|
||||
maze = Maze.new(width, height)
|
||||
puts "\n" * 3
|
||||
maze.draw
|
||||
end
|
||||
|
||||
def draw_header
|
||||
puts ' ' * 28 + 'AMAZING PROGRAM'
|
||||
puts ' ' * 15 + 'CREATIVE COMPUTING MORRISTOWN, NEW JERSEY'
|
||||
puts "\n" * 3
|
||||
end
|
||||
|
||||
def ask_dimensions
|
||||
print 'WHAT ARE YOUR WIDTH AND HEIGHT? '
|
||||
width = gets.to_i
|
||||
print '?? '
|
||||
height = gets.to_i
|
||||
[width, height]
|
||||
end
|
||||
end
|
||||
|
||||
Amazing.new.run
|
||||
295
02_Amazing/vbnet/program.vb
Normal file
295
02_Amazing/vbnet/program.vb
Normal file
@@ -0,0 +1,295 @@
|
||||
Imports System
|
||||
|
||||
Module Program
|
||||
|
||||
Enum Directions
|
||||
SolveAndReset = 0
|
||||
Left = 1
|
||||
Up = 2
|
||||
Right = 3
|
||||
Down = 4
|
||||
End Enum
|
||||
|
||||
'Program State
|
||||
Dim Width As Integer = 0, Height As Integer = 0, Q As Integer = 0, CellsVisited As Integer = 2, curCol As Integer, curRow As Integer = 1
|
||||
Dim SolutionCompleted As Boolean = False
|
||||
Dim CellVisitHistory(,) As Integer
|
||||
Dim CellState(,) As Integer
|
||||
|
||||
Dim rnd As New Random()
|
||||
|
||||
Public ReadOnly Property BlockedLeft As Boolean
|
||||
Get
|
||||
Return curCol - 1 = 0 OrElse CellVisitHistory(curCol - 1, curRow) <> 0
|
||||
End Get
|
||||
End Property
|
||||
Public ReadOnly Property BlockedAbove As Boolean
|
||||
Get
|
||||
Return curRow - 1 = 0 OrElse CellVisitHistory(curCol, curRow - 1) <> 0
|
||||
End Get
|
||||
End Property
|
||||
Public ReadOnly Property BlockedRight As Boolean
|
||||
Get
|
||||
Return curCol = Width OrElse CellVisitHistory(curCol + 1, curRow) <> 0
|
||||
End Get
|
||||
End Property
|
||||
'Note: "BlockedBelow" does NOT include checking if we have a solution!
|
||||
Public ReadOnly Property BlockedBelow As Boolean
|
||||
Get
|
||||
Return curRow = Height OrElse CellVisitHistory(curCol, curRow + 1) <> 0
|
||||
End Get
|
||||
End Property
|
||||
Public ReadOnly Property OnBottomRow As Boolean
|
||||
Get
|
||||
Return curRow.Equals(Height)
|
||||
End Get
|
||||
End Property
|
||||
|
||||
Sub Main(args As String())
|
||||
Const header As String =
|
||||
" AMAZING PROGRAM
|
||||
CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
|
||||
|
||||
|
||||
|
||||
"
|
||||
Console.WriteLine(header)
|
||||
|
||||
While Width <= 1 OrElse Height <= 1
|
||||
Console.Write("WHAT ARE YOUR WIDTH AND LENGTH? ")
|
||||
|
||||
'We no longer have the old convenient INPUT command, so need to parse out the inputs
|
||||
Dim parts = Console.ReadLine().Split(","c).Select(Function(s) Convert.ToInt32(s.Trim())).ToList()
|
||||
Width = parts(0)
|
||||
Height = parts(1)
|
||||
|
||||
If Width <= 1 OrElse Height <= 1 Then Console.WriteLine($"MEANINGLESS DIMENSIONS. TRY AGAIN.{vbCrLf}")
|
||||
End While
|
||||
|
||||
ReDim CellVisitHistory(Width, Height), CellState(Width, Height)
|
||||
|
||||
Console.WriteLine("
|
||||
|
||||
")
|
||||
|
||||
curCol = rnd.Next(1, Width + 1) 'Starting X position
|
||||
CellVisitHistory(curCol, 1) = 1
|
||||
Dim startXPos As Integer = curCol 'we need to know this at the end to print opening line
|
||||
|
||||
Dim keepGoing As Boolean = True
|
||||
While keepGoing
|
||||
If BlockedLeft Then
|
||||
keepGoing = ChoosePath_BlockedToTheLeft()
|
||||
ElseIf BlockedAbove Then
|
||||
keepGoing = ChoosePath_BlockedAbove()
|
||||
ElseIf BlockedRight Then
|
||||
keepGoing = ChoosePath_BlockedToTheRight()
|
||||
Else
|
||||
keepGoing = SelectRandomDirection(Directions.Left, Directions.Up, Directions.Right) 'Go anywhere but down
|
||||
End If
|
||||
End While
|
||||
|
||||
PrintFinalResults(startXPos)
|
||||
End Sub
|
||||
|
||||
Public Sub ResetCurrentPosition()
|
||||
Do
|
||||
If curCol <> Width Then 'not at the right edge
|
||||
curCol += 1
|
||||
ElseIf curRow <> Height Then 'not at the bottom
|
||||
curCol = 1
|
||||
curRow += 1
|
||||
Else
|
||||
curCol = 1
|
||||
curRow = 1
|
||||
End If
|
||||
Loop While CellVisitHistory(curCol, curRow) = 0
|
||||
End Sub
|
||||
|
||||
Dim methods() As Func(Of Boolean) = {AddressOf MarkSolvedAndResetPosition, AddressOf GoLeft, AddressOf GoUp, AddressOf GoRight, AddressOf GoDown}
|
||||
Public Function SelectRandomDirection(ParamArray possibles() As Directions) As Boolean
|
||||
Dim x As Integer = rnd.Next(0, possibles.Length)
|
||||
Return methods(possibles(x))()
|
||||
End Function
|
||||
|
||||
Public Function ChoosePath_BlockedToTheLeft() As Boolean
|
||||
If BlockedAbove Then
|
||||
If BlockedRight Then
|
||||
If curRow <> Height Then
|
||||
If CellVisitHistory(curCol, curRow + 1) <> 0 Then ' Can't go down, but not at the edge...blocked. Reset and try again
|
||||
ResetCurrentPosition()
|
||||
Return True
|
||||
Else
|
||||
Return GoDown()
|
||||
End If
|
||||
ElseIf SolutionCompleted Then 'Can't go Down (there's already another solution)
|
||||
ResetCurrentPosition()
|
||||
Return True
|
||||
Else 'Can't go LEFT, UP, RIGHT, or DOWN, but we're on the bottom and there's no solution yet
|
||||
Return MarkSolvedAndResetPosition()
|
||||
End If
|
||||
ElseIf BlockedBelow Then
|
||||
Return GoRight()
|
||||
ElseIf Not OnBottomRow Then
|
||||
Return SelectRandomDirection(Directions.Right, Directions.Down)
|
||||
ElseIf SolutionCompleted Then 'Can only go right, and we're at the bottom
|
||||
Return GoRight()
|
||||
Else 'Can only go right, we're at the bottom, and there's not a solution yet
|
||||
Return SelectRandomDirection(Directions.Right, Directions.SolveAndReset)
|
||||
End If
|
||||
'== Definitely can go Up ==
|
||||
ElseIf BlockedRight Then
|
||||
If BlockedBelow Then
|
||||
Return GoUp()
|
||||
ElseIf Not OnBottomRow Then
|
||||
Return SelectRandomDirection(Directions.Up, Directions.Down)
|
||||
ElseIf SolutionCompleted Then 'We're on the bottom row, can only go up
|
||||
Return GoUp()
|
||||
Else 'We're on the bottom row, can only go up, but there's no solution
|
||||
Return SelectRandomDirection(Directions.Up, Directions.SolveAndReset)
|
||||
End If
|
||||
'== Definitely can go Up and Right ==
|
||||
ElseIf BlockedBelow Then
|
||||
Return SelectRandomDirection(Directions.Up, Directions.Right)
|
||||
ElseIf Not OnBottomRow Then
|
||||
Return SelectRandomDirection(Directions.Up, Directions.Right, Directions.Down)
|
||||
ElseIf SolutionCompleted Then 'at the bottom, but already have a solution
|
||||
Return SelectRandomDirection(Directions.Up, Directions.Right)
|
||||
Else
|
||||
Return SelectRandomDirection(Directions.Up, Directions.Right, Directions.SolveAndReset)
|
||||
End If
|
||||
End Function
|
||||
|
||||
Public Function ChoosePath_BlockedAbove() As Boolean
|
||||
'No need to check the left side, only called from the "keepGoing" loop where LEFT is already cleared
|
||||
If BlockedRight Then
|
||||
If BlockedBelow Then
|
||||
Return GoLeft()
|
||||
ElseIf Not OnBottomRow Then
|
||||
Return SelectRandomDirection(Directions.Left, Directions.Down)
|
||||
ElseIf SolutionCompleted Then 'Can't go down because there's already a solution
|
||||
Return GoLeft()
|
||||
Else 'At the bottom, no solution yet...
|
||||
Return SelectRandomDirection(Directions.Left, Directions.SolveAndReset)
|
||||
End If
|
||||
ElseIf BlockedBelow Then
|
||||
Return SelectRandomDirection(Directions.Left, Directions.Right)
|
||||
ElseIf Not OnBottomRow Then
|
||||
Return SelectRandomDirection(Directions.Left, Directions.Right, Directions.Down)
|
||||
ElseIf SolutionCompleted Then
|
||||
Return SelectRandomDirection(Directions.Left, Directions.Right)
|
||||
Else
|
||||
Return SelectRandomDirection(Directions.Left, Directions.Right, Directions.SolveAndReset)
|
||||
End If
|
||||
End Function
|
||||
|
||||
Public Function ChoosePath_BlockedToTheRight() As Boolean
|
||||
'No need to check Left or Up, only called from the "keepGoing" loop where LEFT and UP are already cleared
|
||||
If BlockedRight Then 'Can't go Right -- why? we knew this when calling the function
|
||||
If BlockedBelow Then
|
||||
Return SelectRandomDirection(Directions.Left, Directions.Up)
|
||||
ElseIf Not OnBottomRow Then
|
||||
Return SelectRandomDirection(Directions.Left, Directions.Up, Directions.Down)
|
||||
ElseIf SolutionCompleted Then
|
||||
Return SelectRandomDirection(Directions.Left, Directions.Up)
|
||||
Else
|
||||
Return SelectRandomDirection(Directions.Left, Directions.Up, Directions.SolveAndReset)
|
||||
End If
|
||||
Else 'Should never get here
|
||||
Return SelectRandomDirection(Directions.Left, Directions.Up, Directions.Right) 'Go Left, Up, or Right (but path is blocked?)
|
||||
End If
|
||||
End Function
|
||||
|
||||
Public Sub PrintFinalResults(startPos As Integer)
|
||||
For i As Integer = 0 To Width - 1
|
||||
If i = startPos Then Console.Write(". ") Else Console.Write(".--")
|
||||
Next
|
||||
Console.WriteLine(".")
|
||||
|
||||
If Not SolutionCompleted Then 'Pick a random exit
|
||||
Dim X As Integer = rnd.Next(1, Width + 1)
|
||||
If CellState(X, Height) = 0 Then
|
||||
CellState(X, Height) = 1
|
||||
Else
|
||||
CellState(X, Height) = 3
|
||||
End If
|
||||
End If
|
||||
|
||||
For j As Integer = 1 To Height
|
||||
Console.Write("I")
|
||||
For i As Integer = 1 To Width
|
||||
If CellState(i, j) < 2 Then
|
||||
Console.Write(" I")
|
||||
Else
|
||||
Console.Write(" ")
|
||||
End If
|
||||
Next
|
||||
Console.WriteLine()
|
||||
|
||||
For i As Integer = 1 To Width
|
||||
If CellState(i, j) = 0 OrElse CellState(i, j) = 2 Then
|
||||
Console.Write(":--")
|
||||
Else
|
||||
Console.Write(": ")
|
||||
End If
|
||||
Next
|
||||
Console.WriteLine(".")
|
||||
Next
|
||||
End Sub
|
||||
|
||||
Public Function GoLeft() As Boolean
|
||||
curCol -= 1
|
||||
CellVisitHistory(curCol, curRow) = CellsVisited
|
||||
CellsVisited += 1
|
||||
CellState(curCol, curRow) = 2
|
||||
If CellsVisited > Width * Height Then Return False
|
||||
Q = 0
|
||||
Return True
|
||||
End Function
|
||||
|
||||
Public Function GoUp() As Boolean
|
||||
curRow -= 1
|
||||
CellVisitHistory(curCol, curRow) = CellsVisited
|
||||
CellsVisited += 1
|
||||
CellState(curCol, curRow) = 1
|
||||
If CellsVisited > Width * Height Then Return False
|
||||
Q = 0
|
||||
Return True
|
||||
End Function
|
||||
|
||||
Public Function GoRight() As Boolean
|
||||
CellVisitHistory(curCol + 1, curRow) = CellsVisited
|
||||
CellsVisited += 1
|
||||
If CellState(curCol, curRow) = 0 Then CellState(curCol, curRow) = 2 Else CellState(curCol, curRow) = 3
|
||||
curCol += 1
|
||||
If CellsVisited > Width * Height Then Return False
|
||||
Return ChoosePath_BlockedToTheLeft()
|
||||
End Function
|
||||
|
||||
Public Function GoDown() As Boolean
|
||||
If Q = 1 Then Return MarkSolvedAndResetPosition()
|
||||
|
||||
CellVisitHistory(curCol, curRow + 1) = CellsVisited
|
||||
CellsVisited += 1
|
||||
If CellState(curCol, curRow) = 0 Then CellState(curCol, curRow) = 1 Else CellState(curCol, curRow) = 3
|
||||
curRow += 1
|
||||
If CellsVisited > Width * Height Then Return False
|
||||
Return True
|
||||
End Function
|
||||
|
||||
Public Function MarkSolvedAndResetPosition() As Boolean
|
||||
' AlWAYS returns true
|
||||
SolutionCompleted = True
|
||||
Q = 1
|
||||
If CellState(curCol, curRow) = 0 Then
|
||||
CellState(curCol, curRow) = 1
|
||||
curCol = 1
|
||||
curRow = 1
|
||||
If CellVisitHistory(curCol, curRow) = 0 Then ResetCurrentPosition()
|
||||
Else
|
||||
CellState(curCol, curRow) = 3
|
||||
ResetCurrentPosition()
|
||||
End If
|
||||
Return True
|
||||
End Function
|
||||
End Module
|
||||
@@ -1,7 +0,0 @@
|
||||
### Animal
|
||||
|
||||
As published in Basic Computer Games (1978)
|
||||
https://www.atariarchives.org/basicgames/showpage.php?page=4
|
||||
|
||||
Downloaded from Vintage Basic at
|
||||
http://www.vintage-basic.net/games.html
|
||||
22
03_Animal/README.md
Normal file
22
03_Animal/README.md
Normal file
@@ -0,0 +1,22 @@
|
||||
### Animal
|
||||
|
||||
Unlike other computer games in which the computer picks a number or letter and you must guess what it is, in this game _you_ think of an animal and the _computer_ asks you questions and tries to guess the name of your animal. If the computer guesses incorrectly, it will ask you for a question that differentiates the animal you were thinking of. In this way the computer “learns” new animals. Questions to differentiate new animals should be input without a question mark.
|
||||
|
||||
This version of the game does not have a SAVE feature. If your system allows, you may modify the program to save and reload the array when you want to play the game again. This way you can save what the computer learns over a series of games.
|
||||
|
||||
At any time if you reply “LIST” to the question “ARE YOU THINKING OF AN ANIMAL,” the computer will tell you all the animals it knows so far.
|
||||
|
||||
The program starts originally by knowing only FISH and BIRD. As you build up a file of animals you should use broad, general questions first and then narrow down to more specific ones with later animals. For example, if an elephant was to be your first animal, the computer would ask for a question to distinguish an elephant from a bird. Naturally, there are hundreds of possibilities, however, if you plan to build a large file of animals a good question would be “IS IT A MAMMAL.”
|
||||
|
||||
This program can be easily modified to deal with categories of things other than animals by simply modifying the initial data and the dialogue references to animals. In an educational environment, this would be a valuable program to teach the distinguishing characteristics of many classes of objects — rock formations, geography, marine life, cell structures, etc.
|
||||
|
||||
Originally developed by Arthur Luehrmann at Dartmouth College, Animal was subsequently shortened and modified by Nathan Teichholtz at DEC and Steve North at Creative Computing.
|
||||
|
||||
---
|
||||
|
||||
As published in Basic Computer Games (1978)
|
||||
- [Atari Archives](https://www.atariarchives.org/basicgames/showpage.php?page=4)
|
||||
- [Annarchive](https://annarchive.com/files/Basic_Computer_Games_Microcomputer_Edition.pdf#page=19)
|
||||
|
||||
Downloaded from Vintage Basic at
|
||||
http://www.vintage-basic.net/games.html
|
||||
160
03_Animal/java/Animal.java
Normal file
160
03_Animal/java/Animal.java
Normal file
@@ -0,0 +1,160 @@
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Scanner;
|
||||
|
||||
/**
|
||||
* ANIMAL
|
||||
* <p>
|
||||
* Converted from BASIC to Java by Aldrin Misquitta (@aldrinm)
|
||||
*/
|
||||
public class Animal {
|
||||
|
||||
public static void main(String[] args) {
|
||||
printIntro();
|
||||
Scanner scan = new Scanner(System.in);
|
||||
|
||||
List<Question> questions = new ArrayList<>();
|
||||
questions.add(new Question("DOES IT SWIM", "FISH", "BIRD"));
|
||||
|
||||
boolean stopGame = false;
|
||||
while (!stopGame) {
|
||||
String choice = readMainChoice(scan);
|
||||
switch (choice) {
|
||||
case "LIST":
|
||||
printKnownAnimals(questions);
|
||||
break;
|
||||
case "Q":
|
||||
case "QUIT":
|
||||
stopGame = true;
|
||||
break;
|
||||
default:
|
||||
if (choice.toUpperCase(Locale.ROOT).startsWith("Y")) {
|
||||
int k = 0;
|
||||
boolean correctGuess = false;
|
||||
while (questions.size() > k && !correctGuess) {
|
||||
Question question = questions.get(k);
|
||||
correctGuess = askQuestion(question, scan);
|
||||
if (correctGuess) {
|
||||
System.out.println("WHY NOT TRY ANOTHER ANIMAL?");
|
||||
} else {
|
||||
k++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!correctGuess) {
|
||||
askForInformationAndSave(scan, questions);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void askForInformationAndSave(Scanner scan, List<Question> questions) {
|
||||
//Failed to get it right and ran out of questions
|
||||
//Let's ask the user for the new information
|
||||
System.out.print("THE ANIMAL YOU WERE THINKING OF WAS A ");
|
||||
String animal = scan.nextLine();
|
||||
System.out.printf("PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A %s FROM A %s ", animal, questions.get(
|
||||
questions.size() - 1).falseAnswer);
|
||||
String newQuestion = scan.nextLine();
|
||||
System.out.printf("FOR A %s THE ANSWER WOULD BE ", animal);
|
||||
boolean newAnswer = readYesOrNo(scan);
|
||||
//Add it to our list
|
||||
addNewAnimal(questions, animal, newQuestion, newAnswer);
|
||||
}
|
||||
|
||||
private static void addNewAnimal(List<Question> questions, String animal, String newQuestion, boolean newAnswer) {
|
||||
Question lastQuestion = questions.get(questions.size() - 1);
|
||||
String lastAnimal = lastQuestion.falseAnswer;
|
||||
lastQuestion.falseAnswer = null; //remove the false option to indicate that there is a next question
|
||||
|
||||
Question newOption;
|
||||
if (newAnswer) {
|
||||
newOption = new Question(newQuestion, animal, lastAnimal);
|
||||
} else {
|
||||
newOption = new Question(newQuestion, lastAnimal, animal);
|
||||
}
|
||||
questions.add(newOption);
|
||||
}
|
||||
|
||||
private static boolean askQuestion(Question question, Scanner scanner) {
|
||||
System.out.printf("%s ? ", question.question);
|
||||
|
||||
boolean chosenAnswer = readYesOrNo(scanner);
|
||||
if (chosenAnswer) {
|
||||
if (question.trueAnswer != null) {
|
||||
System.out.printf("IS IT A %s ? ", question.trueAnswer);
|
||||
return readYesOrNo(scanner);
|
||||
}
|
||||
//else go to the next question
|
||||
} else {
|
||||
if (question.falseAnswer != null) {
|
||||
System.out.printf("IS IT A %s ? ", question.falseAnswer);
|
||||
return readYesOrNo(scanner);
|
||||
}
|
||||
//else go to the next question
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean readYesOrNo(Scanner scanner) {
|
||||
boolean validAnswer = false;
|
||||
Boolean choseAnswer = null;
|
||||
while (!validAnswer) {
|
||||
String answer = scanner.nextLine();
|
||||
if (answer.toUpperCase(Locale.ROOT).startsWith("Y")) {
|
||||
validAnswer = true;
|
||||
choseAnswer = true;
|
||||
} else if (answer.toUpperCase(Locale.ROOT).startsWith("N")) {
|
||||
validAnswer = true;
|
||||
choseAnswer = false;
|
||||
}
|
||||
}
|
||||
return choseAnswer;
|
||||
}
|
||||
|
||||
private static void printKnownAnimals(List<Question> questions) {
|
||||
System.out.println("\nANIMALS I ALREADY KNOW ARE:");
|
||||
List<String> animals = new ArrayList<>();
|
||||
questions.forEach(q -> {
|
||||
if (q.trueAnswer != null) {
|
||||
animals.add(q.trueAnswer);
|
||||
}
|
||||
if (q.falseAnswer != null) {
|
||||
animals.add(q.falseAnswer);
|
||||
}
|
||||
});
|
||||
System.out.println(String.join("\t\t", animals));
|
||||
}
|
||||
|
||||
private static String readMainChoice(Scanner scan) {
|
||||
System.out.print("ARE YOU THINKING OF AN ANIMAL ? ");
|
||||
return scan.nextLine();
|
||||
}
|
||||
|
||||
private static void printIntro() {
|
||||
System.out.println(" ANIMAL");
|
||||
System.out.println(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
|
||||
System.out.println("\n\n");
|
||||
System.out.println("PLAY 'GUESS THE ANIMAL'");
|
||||
System.out.println("\n");
|
||||
System.out.println("THINK OF AN ANIMAL AND THE COMPUTER WILL TRY TO GUESS IT.");
|
||||
}
|
||||
|
||||
|
||||
public static class Question {
|
||||
String question;
|
||||
String trueAnswer;
|
||||
String falseAnswer;
|
||||
|
||||
public Question(String question, String trueAnswer, String falseAnswer) {
|
||||
this.question = question;
|
||||
this.trueAnswer = trueAnswer;
|
||||
this.falseAnswer = falseAnswer;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
119
03_Animal/kotlin/Animal.kt
Normal file
119
03_Animal/kotlin/Animal.kt
Normal file
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* ANIMAL
|
||||
*
|
||||
*
|
||||
* Converted from BASIC to Kotlin by John Long (@patimen)
|
||||
*
|
||||
* Animal is basically a perfect example of a binary tree. Implement it
|
||||
* as such, with the QuestionNode either having an answer if it is a terminal node
|
||||
* or a Question
|
||||
*/
|
||||
|
||||
fun main() {
|
||||
printIntro()
|
||||
val rootQuestionNode =
|
||||
QuestionOrAnswer(question = Question("DOES IT SWIM", QuestionOrAnswer("FISH"), QuestionOrAnswer("BIRD")))
|
||||
while (true) {
|
||||
val choice = ask("ARE YOU THINKING OF AN ANIMAL")
|
||||
when {
|
||||
choice == "LIST" -> printKnownAnimals(rootQuestionNode)
|
||||
choice.startsWith("Q") -> return
|
||||
choice.startsWith("Y") -> {
|
||||
// A wrong answer means it's a new animal!
|
||||
val wrongAnswer = rootQuestionNode.getWrongAnswer()
|
||||
if (wrongAnswer == null) {
|
||||
// The computer got the right answer!
|
||||
println("WHY NOT TRY ANOTHER ANIMAL?")
|
||||
} else {
|
||||
// Get a new question to ask next time
|
||||
wrongAnswer.askForInformationAndSave()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Takes care of asking a question (on the same line) and getting
|
||||
// an answer or a blank string
|
||||
fun ask(question: String): String {
|
||||
print("$question? ")
|
||||
return readln().uppercase() ?: ""
|
||||
}
|
||||
|
||||
// Special case for a "yes or no" question, returns true of yes
|
||||
fun askYesOrNo(question: String): Boolean {
|
||||
return generateSequence {
|
||||
print("$question? ")
|
||||
readln()
|
||||
}.firstNotNullOf { yesOrNo(it) }
|
||||
}
|
||||
|
||||
// If neither Y (true) or N (false), return null, so the above sequence
|
||||
// will just keep executing until it gets the answer
|
||||
private fun yesOrNo(string: String): Boolean? =
|
||||
when (string.uppercase().firstOrNull()) {
|
||||
'Y' -> true
|
||||
'N' -> false
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun printKnownAnimals(question: QuestionOrAnswer) {
|
||||
println("\nANIMALS I ALREADY KNOW ARE:")
|
||||
val animals = question.getAnswers().chunked(4)
|
||||
animals.forEach { line ->
|
||||
// The '*' in front of line.toTypedArray() "spreads" the array as a list of parameters instead
|
||||
System.out.printf("%-15s".repeat(line.size), *line.toTypedArray())
|
||||
println()
|
||||
}
|
||||
}
|
||||
|
||||
private fun printIntro() {
|
||||
println(" ANIMAL")
|
||||
println(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY")
|
||||
println("\n\n")
|
||||
println("PLAY 'GUESS THE ANIMAL'")
|
||||
println("\n")
|
||||
println("THINK OF AN ANIMAL AND THE COMPUTER WILL TRY TO GUESS IT.")
|
||||
}
|
||||
|
||||
class QuestionOrAnswer(private var answer: String? = null, var question: Question? = null) {
|
||||
fun getAnswers(): List<String> = answer?.let { listOf(it) } ?: question!!.getAnswers()
|
||||
fun getWrongAnswer(): QuestionOrAnswer? {
|
||||
if (answer != null) {
|
||||
// "takeUnless" will return null if the answer is "yes". In this case
|
||||
// we will return the "wrong answer", aka the terminal answer that was incorrect
|
||||
return this.takeUnless { askYesOrNo("IS IT A $answer") }
|
||||
}
|
||||
return question?.getWrongAnswer()
|
||||
}
|
||||
|
||||
fun askForInformationAndSave() {
|
||||
//Failed to get it right and ran out of questions
|
||||
//Let's ask the user for the new information
|
||||
val newAnimal = ask("THE ANIMAL YOU WERE THINKING OF WAS A")
|
||||
val newQuestion = ask("PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A \n$newAnimal FROM A $answer\n")
|
||||
val newAnswer = askYesOrNo("FOR A $newAnimal THE ANSWER WOULD BE")
|
||||
|
||||
val trueAnswer = if (newAnswer) newAnimal else answer
|
||||
val falseAnswer = if (newAnswer) answer else newAnimal
|
||||
// Replace our answer with null and set the question with the data we just got
|
||||
// This makes it a question instead of an answer
|
||||
this.answer = null
|
||||
this.question = Question(newQuestion, QuestionOrAnswer(trueAnswer), QuestionOrAnswer(falseAnswer))
|
||||
}
|
||||
}
|
||||
|
||||
class Question(
|
||||
private val question: String,
|
||||
private val trueAnswer: QuestionOrAnswer,
|
||||
private val falseAnswer: QuestionOrAnswer
|
||||
) {
|
||||
fun getAnswers(): List<String> = trueAnswer.getAnswers() + falseAnswer.getAnswers()
|
||||
|
||||
fun getWrongAnswer(): QuestionOrAnswer? =
|
||||
if (askYesOrNo(question)) {
|
||||
trueAnswer.getWrongAnswer()
|
||||
} else {
|
||||
falseAnswer.getWrongAnswer()
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
|
||||
|
||||
Conversion to [Ruby](https://www.ruby-lang.org/en/)
|
||||
Conversion to [Kotlin](https://kotlinlang.org/)
|
||||
@@ -1,3 +1,3 @@
|
||||
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
|
||||
|
||||
Conversion to [Microsoft C#](https://docs.microsoft.com/en-us/dotnet/csharp/)
|
||||
Conversion to [Pascal](https://en.wikipedia.org/wiki/Pascal_(programming_language))
|
||||
@@ -1,3 +1,3 @@
|
||||
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
|
||||
|
||||
Conversion to [Ruby](https://www.ruby-lang.org/en/)
|
||||
Conversion to [Perl](https://www.perl.org/)
|
||||
223
03_Animal/perl/animal.pl
Executable file
223
03_Animal/perl/animal.pl
Executable file
@@ -0,0 +1,223 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
use 5.010; # To get 'state' and 'say'
|
||||
|
||||
use strict; # Require explicit declaration of variables
|
||||
use warnings; # Enable optional compiler warnings
|
||||
|
||||
use English; # Use more friendly names for Perl's magic variables
|
||||
use Term::ReadLine; # Prompt and return user input
|
||||
|
||||
our $VERSION = '0.000_01';
|
||||
|
||||
# The Perl ref() built-in returns 'HASH' for a hash reference. But we
|
||||
# make it a manifest constant just to avoid typos.
|
||||
use constant REF_HASH => ref {};
|
||||
|
||||
print <<'EOD';
|
||||
ANIMAL
|
||||
Creative Computing Morristown, New Jersey
|
||||
|
||||
|
||||
|
||||
Play 'Guess the Animal'
|
||||
Think of an animal and the computer will try to guess it.
|
||||
|
||||
EOD
|
||||
|
||||
# We keep the accumulated data in a tree structure, initialized here. As
|
||||
# we accumulate animals, we replace the 'yes' or 'no' keys with new hash
|
||||
# references.
|
||||
my $database = {
|
||||
question => 'Does it swim', # Initial question
|
||||
yes => 'fish', # Result of answering 'y'
|
||||
no => 'bird', # Result of answering 'n'
|
||||
};
|
||||
|
||||
while ( 1 ) {
|
||||
|
||||
my $resp = get_input(
|
||||
'Are you thinking of an an animal? [y/n/list]: '
|
||||
);
|
||||
|
||||
if ( $resp =~ m/ \A y /smxi ) {
|
||||
# If we got an answer beginning with 'y', walk the database
|
||||
walk_tree( $database );
|
||||
} elsif ( $resp =~ m/ \A list \z /smxi ) {
|
||||
# If we got 'list', list the currently-known animals.
|
||||
say '';
|
||||
say 'Animals I already know are:';
|
||||
say " $_" for sort( list_animals( $database ) );
|
||||
}
|
||||
}
|
||||
|
||||
# Get input from the user. The arguments are:
|
||||
# * The prompt
|
||||
# * A reference to validation code. This code receives the response in
|
||||
# $ARG and returns true for a valid response.
|
||||
# * A warning to print if the response is not valid. This must end in a
|
||||
# return.
|
||||
# The first valid response is returned. An end-of-file terminates the
|
||||
# script.
|
||||
sub get_input {
|
||||
my ( $prompt, $validate, $warning ) = @ARG;
|
||||
|
||||
# If no validator is passed, default to one that always returns
|
||||
# true.
|
||||
$validate ||= sub { 1 };
|
||||
|
||||
# Create the readline object. The 'state' causes the variable to be
|
||||
# initialized only once, no matter how many times this subroutine is
|
||||
# called. The do { ... } is a compound statement used because we
|
||||
# need to tweak the created object before we store it.
|
||||
state $term = do {
|
||||
my $obj = Term::ReadLine->new( 'animal' );
|
||||
$obj->ornaments( 0 );
|
||||
$obj;
|
||||
};
|
||||
|
||||
while ( 1 ) { # Iterate indefinitely
|
||||
|
||||
# Read the input into the topic variable, localized to prevent
|
||||
# Spooky Action at a Distance. We exit on undef, which signals
|
||||
# end-of-file.
|
||||
exit unless defined( local $ARG = $term->readline( $prompt ) );
|
||||
|
||||
# Return the input if it is valid.
|
||||
return $ARG if $validate->();
|
||||
|
||||
# Issue the warning, and go around the merry-go-round again.
|
||||
warn $warning;
|
||||
}
|
||||
}
|
||||
|
||||
# Get a yes-or-no answer. The argument is the prompt, which will have
|
||||
# '? [y/n]: ' appended. The donkey work is done by get_input(), which is
|
||||
# requested to validate the response as beginning with 'y' or 'n',
|
||||
# case-insensitive. The return is a true value for 'y' and a false value
|
||||
# for 'n'.
|
||||
sub get_yes_no {
|
||||
my ( $prompt ) = @ARG;
|
||||
state $map_answer = {
|
||||
n => 0,
|
||||
y => 1,
|
||||
};
|
||||
my $resp = lc get_input(
|
||||
"$prompt? [y/n]: ",
|
||||
sub { m/ \A [yn] /smxi },
|
||||
"Please respond 'y' or 'n'\n",
|
||||
);
|
||||
return $map_answer->{ substr $resp, 0, 1 };
|
||||
}
|
||||
|
||||
# Recurse through the database, returning the names of all animals in
|
||||
# it, in an undefined order.
|
||||
sub list_animals {
|
||||
my ( $node ) = @ARG;
|
||||
return $node unless REF_HASH eq ref $node;
|
||||
return( map { list_animals( $node->{$_} ) } qw{ yes no } );
|
||||
}
|
||||
|
||||
# Find or create the desired animal.
|
||||
# Ask the question stored in the node given in its argument. If the key
|
||||
# selected by the answer ('yes' or 'no') is another node, recurse. If it
|
||||
# is an animal name, confirm it, or add a new animal as appropriate.
|
||||
sub walk_tree {
|
||||
my ( $node ) = @ARG;
|
||||
|
||||
# Ask the question associated with this node. Turn the true/false
|
||||
# response into 'yes' or 'no', since those are the names of the
|
||||
# respective keys.
|
||||
my $resp = get_yes_no ( $node->{question} ) ? 'yes' : 'no';
|
||||
|
||||
# Chose the datum for the response.
|
||||
my $choice = $node->{ $resp };
|
||||
|
||||
# If the datum is a hash reference
|
||||
if ( REF_HASH eq ref $choice ) {
|
||||
|
||||
# Recurse into it
|
||||
walk_tree( $choice );
|
||||
|
||||
# Otherwise it is an actual animal (i.e. terminal node). Check it.
|
||||
} else {
|
||||
|
||||
# If this is not the animal the player was thinking of
|
||||
unless ( get_yes_no( "Is it a $choice" ) ) {
|
||||
|
||||
# Find out what animal the player was thinking of
|
||||
my $animal = lc get_input(
|
||||
'The animal you were thinking of was a ',
|
||||
);
|
||||
|
||||
# Get a yes/no question that distinguishes the animal the
|
||||
# player was thinking of from the animal we found in the
|
||||
# tree.
|
||||
say 'Please type in a question that would distinguish a';
|
||||
my $question = get_input( "$animal from a $choice: " );
|
||||
|
||||
# Find out whether the new animal is selected by 'yes' or
|
||||
# 'no'. If 'no', swap the original animal with the new one
|
||||
# for convenience.
|
||||
( $choice, $animal ) = ( $animal, $choice ) if get_yes_no(
|
||||
"For a $animal the answer would be",
|
||||
);
|
||||
|
||||
# Replace the animal we originally found by a new node
|
||||
# giving the original animal, the new animal, and the
|
||||
# question that distinguishes them.
|
||||
$node->{ $resp } = {
|
||||
question => $question,
|
||||
no => $animal,
|
||||
yes => $choice,
|
||||
};
|
||||
}
|
||||
|
||||
# Find out if the player wants to play again. If not, exit. If
|
||||
# so, just return.
|
||||
say '';
|
||||
exit unless get_yes_no( 'Why not try another animal' );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
__END__
|
||||
|
||||
=head1 TITLE
|
||||
|
||||
animal.pl - Play the game 'animal' from Basic Computer Games
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
animal.pl
|
||||
|
||||
=head1 DETAILS
|
||||
|
||||
This Perl script is a port of C<animal>, which is the 3ed entry in Basic
|
||||
Computer Games.
|
||||
|
||||
The original BASIC was greatly complicated by the need to emulate a
|
||||
binary tree with an array. The implementation using hashes as nodes in
|
||||
an actual binary tree is much simpler.
|
||||
|
||||
=head1 PORTED BY
|
||||
|
||||
Thomas R. Wyant, III F<wyant at cpan dot org>
|
||||
|
||||
=head1 COPYRIGHT AND LICENSE
|
||||
|
||||
Copyright (C) 2022 by Thomas R. Wyant, III
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it
|
||||
under the same terms as Perl 5.10.0. For more details, see the Artistic
|
||||
License 1.0 at
|
||||
L<https://www.perlfoundation.org/artistic-license-10.html>, and/or the
|
||||
Gnu GPL at L<http://www.gnu.org/licenses/old-licenses/gpl-1.0.txt>.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but
|
||||
without any warranty; without even the implied warranty of
|
||||
merchantability or fitness for a particular purpose.
|
||||
|
||||
=cut
|
||||
|
||||
# ex: set expandtab tabstop=4 textwidth=72 :
|
||||
192
03_Animal/python/animal.py
Normal file
192
03_Animal/python/animal.py
Normal file
@@ -0,0 +1,192 @@
|
||||
########################################################
|
||||
#
|
||||
# Animal
|
||||
#
|
||||
# From: Basic computer Games(1978)
|
||||
#
|
||||
# Unlike other computer games in which the computer
|
||||
# picks a number or letter and you must guess what it is,
|
||||
# in this game you think of an animal and the computer asks
|
||||
# you questions and tries to guess the name of your animal.
|
||||
# If the computer guesses incorrectly, it will ask you for a
|
||||
# question that differentiates the animal it guessed
|
||||
# from the one you were thinking of. In this way the
|
||||
# computer "learns" new animals. Questions to differentiate
|
||||
# new animals should be input without a question mark.
|
||||
# This version of the game does not have a SAVE feature.
|
||||
# If your sistem allows, you may modify the program to
|
||||
# save array A$, then reload the array when you want
|
||||
# to play the game again. This way you can save what the
|
||||
# computer learns over a series of games.
|
||||
# At any time if you reply 'LIST' to the question "ARE YOU
|
||||
# THINKING OF AN ANIMAL", the computer will tell you all the
|
||||
# animals it knows so far.
|
||||
# The program starts originally by knowing only FISH and BIRD.
|
||||
# As you build up a file of animals you should use broad,
|
||||
# general questions first and then narrow down to more specific
|
||||
# ones with later animals. For example, If an elephant was to be
|
||||
# your first animal, the computer would ask for a question to distinguish
|
||||
# an elephant from a bird. Naturally there are hundreds of possibilities,
|
||||
# however, if you plan to build a large file of animals a good question
|
||||
# would be "IS IT A MAMAL".
|
||||
# This program can be easily modified to deal with categories of
|
||||
# things other than animals by simply modifying the initial data
|
||||
# in Line 530 and the dialogue references to animal in Lines 10,
|
||||
# 40, 50, 130, 230, 240 and 600. In an educational environment, this
|
||||
# would be a valuable program to teach the distinguishing chacteristics
|
||||
# of many classes of objects -- rock formations, geography, marine life,
|
||||
# cell structures, etc.
|
||||
# Originally developed by Arthur Luehrmann at Dartmouth College,
|
||||
# Animal was subsequently shortened and modified by Nathan Teichholtz at
|
||||
# DEC and Steve North at Creative Computing
|
||||
#
|
||||
########################################################
|
||||
|
||||
|
||||
class Node:
|
||||
"""
|
||||
Node of the binary tree of questions.
|
||||
"""
|
||||
|
||||
def __init__(self, text, yes_node, no_node):
|
||||
# the nodes that are leafs have as text the animal's name, otherwise
|
||||
# a yes/no question
|
||||
self.text = text
|
||||
self.yes_node = yes_node
|
||||
self.no_node = no_node
|
||||
|
||||
def update_node(self, new_question, answer_new_ques, new_animal):
|
||||
# update the leaf with a question
|
||||
old_animal = self.text
|
||||
# we replace the animal with a new question
|
||||
self.text = new_question
|
||||
|
||||
if answer_new_ques == 'y':
|
||||
self.yes_node = Node(new_animal, None, None)
|
||||
self.no_node = Node(old_animal, None, None)
|
||||
else:
|
||||
self.yes_node = Node(old_animal, None, None)
|
||||
self.no_node = Node(new_animal, None, None)
|
||||
|
||||
# the leafs have as children None
|
||||
def is_leaf(self):
|
||||
return self.yes_node == None and self.no_node == None
|
||||
|
||||
|
||||
def list_known_animals(root_node):
|
||||
# Traversing the tree by recursion until we reach the leafs
|
||||
if root_node == None:
|
||||
return
|
||||
|
||||
if root_node.is_leaf():
|
||||
print(root_node.text, end=' '*11)
|
||||
return
|
||||
|
||||
if root_node.yes_node:
|
||||
list_known_animals(root_node.yes_node)
|
||||
|
||||
if root_node.no_node:
|
||||
list_known_animals(root_node.no_node)
|
||||
|
||||
|
||||
def parse_input(message, check_list, root_node):
|
||||
# only accepts yes or no inputs and recognizes list operation
|
||||
correct_input = False
|
||||
while not correct_input:
|
||||
try:
|
||||
inp = input(message)
|
||||
|
||||
if check_list and inp.lower() == 'list':
|
||||
print('Animals I already know are:')
|
||||
list_known_animals(root_node)
|
||||
print('\n')
|
||||
|
||||
token = inp[0].lower()
|
||||
if token == 'y' or token == 'n':
|
||||
correct_input = True
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
return token
|
||||
|
||||
|
||||
def avoid_void_input(message):
|
||||
answer = ''
|
||||
while answer == '':
|
||||
answer = input(message)
|
||||
return answer
|
||||
|
||||
|
||||
def initial_message():
|
||||
print(' '*32 + 'Animal')
|
||||
print(' '*15 + 'Creative Computing Morristown, New Jersey\n')
|
||||
print('Play ´Guess the Animal´')
|
||||
print('Think of an animal and the computer will try to guess it.\n')
|
||||
|
||||
|
||||
# Initial tree
|
||||
yes_child = Node('Fish', None, None)
|
||||
no_child = Node('Bird', None, None)
|
||||
root = Node('Does it swim?', yes_child, no_child)
|
||||
|
||||
# Main loop of game
|
||||
initial_message()
|
||||
keep_playing = parse_input(
|
||||
'Are you thinking of an animal? ', True, root) == 'y'
|
||||
while keep_playing:
|
||||
keep_asking = True
|
||||
# Start traversing the tree by the root
|
||||
actual_node = root
|
||||
|
||||
while keep_asking:
|
||||
|
||||
if not actual_node.is_leaf():
|
||||
# we have to keep asking i.e. traversing nodes
|
||||
answer = parse_input(actual_node.text, False, None)
|
||||
|
||||
if answer == 'y':
|
||||
actual_node = actual_node.yes_node
|
||||
else:
|
||||
actual_node = actual_node.no_node
|
||||
else:
|
||||
# we have reached a possible answer
|
||||
answer = parse_input('Is it a {}? '.format(
|
||||
actual_node.text), False, None)
|
||||
if answer == 'n':
|
||||
# add the new animal to the tree
|
||||
new_animal = avoid_void_input(
|
||||
'The animal you were thinking of was a ? ')
|
||||
new_question = avoid_void_input(
|
||||
'Please type in a question that would distinguish a {} from a {}: '.format(new_animal, actual_node.text))
|
||||
answer_new_question = parse_input(
|
||||
'for a {} the answer would be: '.format(new_animal), False, None)
|
||||
|
||||
actual_node.update_node(
|
||||
new_question+'?', answer_new_question, new_animal)
|
||||
|
||||
else:
|
||||
print("Why not try another animal?")
|
||||
|
||||
keep_asking = False
|
||||
|
||||
keep_playing = parse_input(
|
||||
'Are you thinking of an animal? ', True, root) == 'y'
|
||||
|
||||
|
||||
########################################################
|
||||
# Porting Notes
|
||||
#
|
||||
# The data structure used for storing questions and
|
||||
# animals is a binary tree where each non-leaf node
|
||||
# has a question, while the leafs store the animals.
|
||||
#
|
||||
# As the original program, this program doesn't store
|
||||
# old questions and animals. A good modification would
|
||||
# be to add a database to store the tree.
|
||||
# Also as the original program, this one can be easily
|
||||
# modified to not only make guesses about animals, by
|
||||
# modyfing the initial data of the tree, the questions
|
||||
# that are asked to the user and the initial message
|
||||
# function (Lines 120 to 130, 135, 158, 160, 168, 173)
|
||||
|
||||
########################################################
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user