Merge branch 'coding-horror:main' into main

This commit is contained in:
Paul Holt
2022-01-08 01:49:00 +11:00
committed by GitHub
51 changed files with 4200 additions and 255 deletions

View 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)

View File

@@ -15,4 +15,5 @@ As published in Basic Computer Games (1978):
Downloaded from Vintage Basic at Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html http://www.vintage-basic.net/games.html
A Common Lisp port is [here](https://github.com/koalahedron/lisp-computer-games/blob/master/01%20Acey%20Ducey/common-lisp/acey-deucy.lisp). #### External Links
- Common Lisp: https://github.com/koalahedron/lisp-computer-games/blob/master/01%20Acey%20Ducey/common-lisp/acey-deucy.lisp

2
01_Acey_Ducey/d/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*.exe
*.obj

29
01_Acey_Ducey/d/README.md Normal file
View 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
View 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;
}

View 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!");
}

View File

@@ -167,6 +167,6 @@ public class AceyDucey {
System.out.println("YOU HAVE AN OPTION TO BET OR NOT BET DEPENDING"); 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("ON WHETHER OR NOT YOU FEEL THE CARD WILL HAVE");
System.out.println("A VALUE BETWEEN THE FIRST TWO."); 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");
} }
} }

View File

@@ -28,17 +28,15 @@ If you do not want to bet, input a 0. If you want to quit, input a -1.
END_INSTRUCTIONS END_INSTRUCTIONS
my @cards = (1 .. 13); # That is, Ace through King. my @cards = ( 1 .. 13 ); # That is, Ace through King.
my $keepPlaying = 1; my $keepPlaying = 1;
GAME: GAME:
while ($keepPlaying) while ($keepPlaying) {
{ my $playerBalance = 100; # The player starts with $100
my $playerBalance = 100; # The player starts with $100
HAND: HAND:
while (1) while (1) {
{
print "\nYou now have $playerBalance dollars.\n\n"; print "\nYou now have $playerBalance dollars.\n\n";
# We'll create a new array that is a shuffled version of the deck. # We'll create a new array that is a shuffled version of the deck.
@@ -48,19 +46,17 @@ while ($keepPlaying)
# that those will be unique. This way we don't have to keep drawing # 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 # 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. # sure that the first card is lower than the second one.
my ($firstCard, $secondCard) = sort { $a <=> $b } @shuffledDeck[ 0 .. 1 ]; my ( $firstCard, $secondCard ) = sort { $a <=> $b } @shuffledDeck[ 0 .. 1 ];
print "I drew ", nameOfCard($firstCard), " and ", nameOfCard($secondCard), ".\n"; print "I drew ", nameOfCard($firstCard), " and ", nameOfCard($secondCard), ".\n";
my $bet = getValidBet($playerBalance); my $bet = getValidBet($playerBalance);
if ($bet == 0) if ( $bet == 0 ) {
{
print "Chicken!\n\n"; print "Chicken!\n\n";
next HAND; next HAND;
} }
if ($bet < 0) if ( $bet < 0 ) {
{
last GAME; last GAME;
} }
@@ -72,19 +68,16 @@ while ($keepPlaying)
print "I drew ", nameOfCard($thirdCard), "!\n"; print "I drew ", nameOfCard($thirdCard), "!\n";
if (($firstCard < $thirdCard) && ($thirdCard < $secondCard)) if ( ( $firstCard < $thirdCard ) && ( $thirdCard < $secondCard ) ) {
{
print "You win!\n\n"; print "You win!\n\n";
$playerBalance += $bet; $playerBalance += $bet;
} }
else else {
{
print "You lose!\n\n"; print "You lose!\n\n";
$playerBalance -= $bet; $playerBalance -= $bet;
} }
if ($playerBalance <= 0) if ( $playerBalance <= 0 ) {
{
print "Sorry, buddy, you blew your wad!\n\n"; print "Sorry, buddy, you blew your wad!\n\n";
last HAND; last HAND;
} }
@@ -96,49 +89,43 @@ while ($keepPlaying)
print "Thanks for playing!\n"; print "Thanks for playing!\n";
############### ###############
sub getValidBet sub getValidBet {
{
my $maxBet = shift; my $maxBet = shift;
print "\nWhat's your bet? "; INPUT:
my $input = <STDIN>;
chomp $input;
# This regular expression will validate that the player entered an integer.
# The !~ match operate *negates* the match, so if the player did NOT enter
# an integer, they'll be given an error and prompted again.
if ($input !~ /^ # Match the beginning of the string
[+-]? # Optional plus or minus...
\d+ # followed by one more more digits...
$ # and then the end of the string
/x # The x modifier ignores whitespace in this regex...
)
{ {
print "Sorry, numbers only!\n"; print "\nWhat's your bet? ";
$input = getValidBet($maxBet);
}
if ($input > $maxBet) chomp( my $input = <STDIN> );
{
print "Sorry, my friend, you can't bet more money than you have.\n";
print "You only have $maxBet dollars to spend!\n";
$input = getValidBet($maxBet);
}
if ($input != int($input)) # This regular expression will validate that the player entered an integer.
{ # The !~ match operate *negates* the match, so if the player did NOT enter
print "Sorry, you must bet in whole dollars. No change!\n"; # an integer, they'll be given an error and prompted again.
$input = getValidBet($maxBet); 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;
}
return $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 # 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. # the array to its proper position in the deck.
sub nameOfCard sub nameOfCard {
{
my $value = shift; my $value = shift;
# Note that the Joker isn't used in this game, but since arrays in Perl are # Note that the Joker isn't used in this game, but since arrays in Perl are
@@ -150,25 +137,21 @@ sub nameOfCard
return $cardlist[$value]; return $cardlist[$value];
} }
sub promptUserToKeepPlaying sub promptUserToKeepPlaying {
{ YESNO:
print "Try again (Y/N)? "; {
my $input = <STDIN>; print "Try again (Y/N)? ";
chomp $input;
my $keepGoing; chomp( my $input = uc <STDIN> );
if (uc($input) eq 'Y')
{
$keepGoing = 1;
}
elsif (uc($input) eq 'N')
{
$keepGoing = 0;
}
else
{
$keepGoing = promptUserToKeepPlaying();
}
return $keepGoing; if ( $input eq 'Y' ) {
return 1;
}
elsif ( $input eq 'N' ) {
return 0;
}
else {
redo YESNO;
}
}
} }

View File

@@ -12,3 +12,7 @@ As published in Basic Computer Games (1978):
Downloaded from Vintage Basic at Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html 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.

View File

@@ -117,10 +117,15 @@
975 V(R,S)=3:Q=0:GOTO 1000 975 V(R,S)=3:Q=0:GOTO 1000
980 V(R,S)=1:Q=0:R=1:S=1:GOTO 250 980 V(R,S)=1:Q=0:R=1:S=1:GOTO 250
1000 GOTO 210 1000 GOTO 210
1010 FOR J=1 TO V 1010 IF Z=1 THEN 1015
1011 PRINT "I"; 1011 X=INT(RND(1)*H+1)
1012 FOR I=1 TO H 1012 IF V(X,V)=0 THEN 1014
1013 IF V(I,J)<2 THEN 1030 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 " "; 1020 PRINT " ";
1021 GOTO 1040 1021 GOTO 1040
1030 PRINT " I"; 1030 PRINT " I";

159
02_Amazing/perl/amazing.pl Executable file
View 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;
}

View File

@@ -37,14 +37,14 @@ fun main() {
// an answer or a blank string // an answer or a blank string
fun ask(question: String): String { fun ask(question: String): String {
print("$question? ") print("$question? ")
return readLine()?.uppercase() ?: "" return readln().uppercase() ?: ""
} }
// Special case for a "yes or no" question, returns true of yes // Special case for a "yes or no" question, returns true of yes
fun askYesOrNo(question: String): Boolean { fun askYesOrNo(question: String): Boolean {
return generateSequence { return generateSequence {
print("$question? ") print("$question? ")
readLine() readln()
}.firstNotNullOf { yesOrNo(it) } }.firstNotNullOf { yesOrNo(it) }
} }

267
04_Awari/perl/awari.pl Normal file
View File

@@ -0,0 +1,267 @@
#!/usr/bin/env perl
use v5.24;
use warnings;
use experimental 'signatures';
no warnings 'experimental::signatures';
use List::Util 'none';
# our board will be represented with an array of 14 slots, from 0 to 13.
# Positions 6 and 13 represent the "home pit" for the human and the
# computer, respectively.
use constant PLAYER_HOME => 6;
use constant COMPUTER_HOME => 13;
use constant FIRST => 0;
use constant AGAIN => 1;
exit main(@ARGV);
sub main {
$|++; # disable buffering on standard output, every print will be
# done immediately
welcome(); # startup message
# this array will keep track of computer-side failures, defined as
# "the computer did not win". Whenever the computer loses or draws, the
# specific sequence of moves will be saved and then used to drive
# the search for a (hopefully) optimal move.
my $failures = [];
while ('enjoying') {
# a new game starts, let's reset the board to the initial condition
my $board = [ (3) x 6, 0, (3) x 6, 0 ];
# this string will keep track of all moves performed
my $moves = '/';
# the human player starts
my $turn = 'player';
say "\n";
print_board($board);
while (not is_game_over($board)) {
my $move; # this will collect the move in this turn
if ($turn eq 'player') { # "first" move for player
# player_move(...) does the move selected by the player,
# returning both the selected move as well as the pit id
# where the last seed landed
($move, my $landing) = player_move($board);
# if we landed on the Player's Home Pit we get another move
$turn = $landing == PLAYER_HOME ? 'player-again' : 'computer';
}
elsif ($turn eq 'player-again') { # "second" move for player
# here we call player_move making it clear that it's the
# second move, to get the right prompt eventually. We only
# care for the $move as the result, so we ignore the other.
($move) = player_move($board, AGAIN);
$turn = 'computer';
}
else {
# the computer_move(...) function analyzes the $board as well
# as adapting the strategy based on past "failures" (i.e.
# matches where the computer did not win). For this it's
# important to pass the log of these failures, as well as the
# full record of moves in this specific match.
($move, my $landing) = computer_move($board, $failures, $moves);
print "\nMY MOVE IS ", $move - 6;
# do the second move in the turn if conditions apply
if ($landing == COMPUTER_HOME && ! is_game_over($board)) {
# save the first move before doing the second one!
$moves .= "$move/";
my ($move) = computer_move($board, $failures, $moves);
print ',', $move - 6;
}
$turn = 'player';
}
# append the last selected move by either party, to track this
# specific match (useful for computer's AI and ML)
$moves .= "$move/";
print_board($board);
}
# assess_victory() returns the difference between player's and
# computer's seeds, so a negative value is a win for the computer.
my $computer_won = assess_victory($board) < 0;
# if this last match was a "failure" (read: not a win for the
# computer), then record it for future memory.
push $failures->@*, $moves unless $computer_won;
}
return 0;
}
# calculate the difference between the two home pits. Negative values mean
# that the computer won, 0 is a draw, positive values is a player's win.
# The difference is also returned back, in case of need.
sub assess_victory ($board) {
say "\nGAME OVER";
my $difference = $board->[PLAYER_HOME] - $board->[COMPUTER_HOME];
if ($difference < 0) {
say 'I WIN BY ', -$difference, ' POINTS';
}
else {
say $difference ? "YOU WIN BY $difference POINTS" : 'DRAWN GAME';
}
return $difference;
}
# move the seeds from $pit and take into account possible bonuses
sub move_seeds ($board, $pit) {
# get the seeds from the selected pit $pit
my $seeds = $board->[$pit];
$board->[$pit] = 0;
# $landing will be our "moving cursor" to place seeds around
my $landing = $pit;
while ($seeds > 0) {
$landing = ($landing + 1) % 14; # 12 --> 13 -[wrap]-> 0 --> 1
--$seeds;
++$board->[$landing];
}
# check for "stealing seeds" condition. This cannot happen in home pits
if ($landing != PLAYER_HOME && $landing != COMPUTER_HOME
&& $board->[$landing] == 1 && $board->[12 - $landing] > 0) {
my $home = $pit < 7 ? PLAYER_HOME : COMPUTER_HOME;
$board->[$home] += 1 + $board->[12 - $landing];
$board->@[$landing, 12 - $landing] = (0, 0);
}
return ($pit, $landing);
}
sub get_player_move ($board, $prompt) {
print "\n$prompt? ";
while (defined(my $move = <STDIN>)) {
chomp($move); # remove newline
return $move - 1 if $move =~ m{\A[1-6]\z}mxs && $board->[$move - 1];
print 'ILLEGAL MOVE\nAGAIN? ';
}
die "goodbye\n";
}
sub player_move ($board, $stage = FIRST) {
my $prompt = $stage == FIRST ? 'YOUR MOVE' : 'AGAIN';
my $selected_move = get_player_move($board, $prompt);
return move_seeds($board, $selected_move);
}
sub computer_move ($board, $failures, $moves) {
# we will go through all possible moves for the computer and all
# possible responses by the player, collecting the "best" move in terms
# of reasonable outcome (assuming that each side wants to maximize their
# outcome. $best_move will eventually contain the best move for the
# computer, and $best_difference the best difference in scoring (as
# seen from the computer).
my ($best_move, $best_difference);
for my $c_move (7 .. 12) {
next unless $board->[$c_move]; # only consider pits with seeds inside
# we work on a copy of the board to do all our trial-and-errors
my $copy = [ $board->@* ];
move_seeds($copy, $c_move);
# it's time to "think like a player" and see what's the "best" move
# for the player in this situation. This heuristic is "not perfect"
# but it seems OK anyway.
my $best_player_score = 0;
for my $p_move (0 .. 5) {
next unless $copy->[$p_move]; # only pits with seeds inside
my $landing = $copy->[$p_move] + $p_move;
# the player's score for this move, calculated as additional seeds
# placed in the player's pit. The original algorithm sets this to
# 1 only if the $landing position is greater than 13, which can
# be obtained by setting the ORIGINAL environment variable to a
# "true" value (in Perl terms). Otherwise it is calculated
# according to the real rules for the game.
my $p_score = $ENV{ORIGINAL} ? $landing > 13 : int(($landing - 5) / 14);
# whatever, the landing position must be within the bounds
$landing %= 14;
# if the conditions apply, the player's move might win additional
# seeds, which we have to to take into account.
$p_score += $copy->[12 - $landing]
if $copy->[$landing] == 0
&& $landing != PLAYER_HOME && $landing != COMPUTER_HOME;
# let's compare this move's score against the best collected
# so far (as a response to a specific computer's move).
$best_player_score = $p_score if $p_score > $best_player_score;
}
# the overall score for the player is the additional seeds we just
# calculated into $best_player_score plus the seeds that were already
# in the player's pit
$best_player_score += $copy->[PLAYER_HOME];
# the best difference we can aim for with this computer's move must
# assume that the player will try its best
my $difference = $copy->[COMPUTER_HOME] - $best_player_score;
# now it's time to check this computer's move against the history
# of failed matches. $candidate_moves will be the "candidate" list
# of moves if we accept this one.
my $candidate_moves = $moves . $c_move . '/';
for my $failure ($failures->@*) {
# index(.) returns 0 if and only if $candidate_moves appears at
# the very beginning of $failure, i.e. it matches a previous
# behaviour.
next if index($failure, $candidate_moves) != 0;
# same sequence of moves as before... assign a penalty
$difference -= 2;
}
# update $best_move and $best_difference if they need to
($best_move, $best_difference) = ($c_move, $difference)
if (! defined $best_move) || ($best_difference < $difference);
}
# apply the selected move and return
return move_seeds($board, $best_move);
}
sub welcome {
say ' ' x 34, 'AWARI';
say ' ' x 15, 'CREATIVE COMPUTING MORRISTOWN, NEW JERSEY';
}
sub print_board ($board) {
my $template = '
%2d %2d %2d %2d %2d %2d
%2d %d
%2d %2d %2d %2d %2d %2d
';
printf $template, $board->@[12, 11, 10, 9, 8 , 7, 13, 6, 0 .. 5];
return;
}
sub is_game_over ($board) {
# game over if the player's side is empty
return 1 if none { $_ } $board->@[0 .. 5];
# game over if the computers' side is empty
return 1 if none { $_ } $board->@[7 .. 12];
# not game over
return 0;
}

View File

@@ -0,0 +1,183 @@
using System;
using System.Collections.Generic;
namespace Bombardment
{
// <summary>
// Game of Bombardment
// Based on the Basic game of Bombardment here
// https://github.com/coding-horror/basic-computer-games/blob/main/11%20Bombardment/bombardment.bas
// Note: The idea was to create a version of the 1970's Basic game in C#, without introducing
// new features - no additional text, error checking, etc has been added.
// </summary>
internal class Bombardment
{
private static int MAX_GRID_SIZE = 25;
private static int MAX_PLATOONS = 4;
private static Random random = new Random();
private List<int> computerPositions = new List<int>();
private List<int> playerPositions = new List<int>();
private List<int> computerGuesses = new List<int>();
private void PrintStartingMessage()
{
Console.WriteLine("{0}BOMBARDMENT", new string(' ', 33));
Console.WriteLine("{0}CREATIVE COMPUTING MORRISTOWN, NEW JERSEY", new string(' ', 15));
Console.WriteLine();
Console.WriteLine();
Console.WriteLine();
Console.WriteLine("YOU ARE ON A BATTLEFIELD WITH 4 PLATOONS AND YOU");
Console.WriteLine("HAVE 25 OUTPOSTS AVAILABLE WHERE THEY MAY BE PLACED.");
Console.WriteLine("YOU CAN ONLY PLACE ONE PLATOON AT ANY ONE OUTPOST.");
Console.WriteLine("THE COMPUTER DOES THE SAME WITH ITS FOUR PLATOONS.");
Console.WriteLine();
Console.WriteLine("THE OBJECT OF THE GAME IS TO FIRE MISSLES AT THE");
Console.WriteLine("OUTPOSTS OF THE COMPUTER. IT WILL DO THE SAME TO YOU.");
Console.WriteLine("THE ONE WHO DESTROYS ALL FOUR OF THE ENEMY'S PLATOONS");
Console.WriteLine("FIRST IS THE WINNER.");
Console.WriteLine();
Console.WriteLine("GOOD LUCK... AND TELL US WHERE YOU WANT THE BODIES SENT!");
Console.WriteLine();
Console.WriteLine("TEAR OFF MATRIX AND USE IT TO CHECK OFF THE NUMBERS.");
// As an alternative to repeating the call to WriteLine(),
// we can print the new line character five times.
Console.Write(new string('\n', 5));
// Print a sample board (presumably the game was originally designed to be
// physically printed on paper while played).
for (var i = 1; i <= 25; i += 5)
{
// The token replacement can be padded by using the format {tokenPosition, padding}
// Negative values for the padding cause the output to be left-aligned.
Console.WriteLine("{0,-3}{1,-3}{2,-3}{3,-3}{4,-3}", i, i + 1, i + 2, i + 3, i + 4);
}
Console.WriteLine("\n");
}
// Generate 5 random positions for the computer's platoons.
private void PlaceComputerPlatoons()
{
do
{
var nextPosition = random.Next(1, MAX_GRID_SIZE);
if (!computerPositions.Contains(nextPosition))
{
computerPositions.Add(nextPosition);
}
} while (computerPositions.Count < MAX_PLATOONS);
}
private void StoreHumanPositions()
{
Console.WriteLine("WHAT ARE YOUR FOUR POSITIONS");
// The original game assumed that the input would be five comma-separated values, all on one line.
// For example: 12,22,1,4,17
var input = Console.ReadLine();
var playerPositionsAsStrings = input.Split(",");
foreach (var playerPosition in playerPositionsAsStrings) {
playerPositions.Add(int.Parse(playerPosition));
}
}
private void HumanTurn()
{
Console.WriteLine("WHERE DO YOU WISH TO FIRE YOUR MISSLE");
var input = Console.ReadLine();
var humanGuess = int.Parse(input);
if(computerPositions.Contains(humanGuess))
{
Console.WriteLine("YOU GOT ONE OF MY OUTPOSTS!");
computerPositions.Remove(humanGuess);
switch(computerPositions.Count)
{
case 3:
Console.WriteLine("ONE DOWN, THREE TO GO.");
break;
case 2:
Console.WriteLine("TWO DOWN, TWO TO GO.");
break;
case 1:
Console.WriteLine("THREE DOWN, ONE TO GO.");
break;
case 0:
Console.WriteLine("YOU GOT ME, I'M GOING FAST.");
Console.WriteLine("BUT I'LL GET YOU WHEN MY TRANSISTO&S RECUP%RA*E!");
break;
}
}
else
{
Console.WriteLine("HA, HA YOU MISSED. MY TURN NOW:");
}
}
private int GenerateComputerGuess()
{
int computerGuess;
do
{
computerGuess = random.Next(1, 25);
}
while(computerGuesses.Contains(computerGuess));
computerGuesses.Add(computerGuess);
return computerGuess;
}
private void ComputerTurn()
{
var computerGuess = GenerateComputerGuess();
if (playerPositions.Contains(computerGuess))
{
Console.WriteLine("I GOT YOU. IT WON'T BE LONG NOW. POST {0} WAS HIT.", computerGuess);
playerPositions.Remove(computerGuess);
switch(playerPositions.Count)
{
case 3:
Console.WriteLine("YOU HAVE ONLY THREE OUTPOSTS LEFT.");
break;
case 2:
Console.WriteLine("YOU HAVE ONLY TWO OUTPOSTS LEFT.");
break;
case 1:
Console.WriteLine("YOU HAVE ONLY ONE OUTPOST LEFT.");
break;
case 0:
Console.WriteLine("YOU'RE DEAD. YOUR LAST OUTPOST WAS AT {0}. HA, HA, HA.", computerGuess);
Console.WriteLine("BETTER LUCK NEXT TIME.");
break;
}
}
else
{
Console.WriteLine("I MISSED YOU, YOU DIRTY RAT. I PICKED {0}. YOUR TURN:", computerGuess);
}
}
public void Play()
{
PrintStartingMessage();
PlaceComputerPlatoons();
StoreHumanPositions();
while (playerPositions.Count > 0 && computerPositions.Count > 0)
{
HumanTurn();
if (computerPositions.Count > 0)
{
ComputerTurn();
}
}
}
}
}

View File

@@ -0,0 +1,8 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,13 @@
using System;
namespace Bombardment
{
class Program
{
static void Main(string[] args)
{
var bombardment = new Bombardment();
bombardment.Play();
}
}
}

View File

@@ -0,0 +1,129 @@
#!/usr/bin/perl
use strict;
use warnings;
#GLOBAL
my %player_bases;
my %computer_bases;
my %player_choices;
my %computer_choices;
&main;
sub main {
&print_intro;
&display_field;
&populate_computer_bases;
&populate_player_bases;
&game_play;
}
sub game_play {
until (keys %computer_bases == 0 || keys %player_bases == 0) {
&player_turn;
if (keys %computer_bases == 0) {
exit;
}
&computer_turn;
}
exit;
}
sub computer_turn {
# There is logic in here to ensure that the computer doesn't try to pick a target it has already picked
my $valid_choice = 0;
until ($valid_choice == 1) {
my $target = int(rand(25)+1);
if (exists $computer_choices{$target}) {
$valid_choice = 0;
}
else {
$valid_choice = 1;
$computer_choices{$target}=1;
if (exists $player_bases{$target}) {
delete($player_bases{$target});
my $size = keys %player_bases;
if ($size > 0) {
print "I GOT YOU. IT WON'T BE LONG NOW. POST $target WAS HIT.\n";
if ($size == 3) { print "YOU HAVE ONLY THREE OUTPOSTS LEFT.\n"};
if ($size == 2) { print "YOU HAVE ONLY TWO OUTPOSTS LEFT.\n"};
if ($size == 1) { print "YOU HAVE ONLY ONE OUTPOSTS LEFT.\n"};
}
else {
print "YOU'RE DEAD. YOUR LAST OUTPOST WAS AT $target. HA, HA, HA.\nBETTER LUCK NEXT TIME\n";
}
}
else {
print "I MISSED YOU, YOU DIRTY RAT. I PICKED $target. YOUR TURN:\n";
}
}
}
}
sub player_turn {
print "WHERE DO YOU WISH TO FIRE YOUR MISSILE\n";
chomp(my $target=<STDIN>);
if (exists $computer_bases{$target}) {
print "YOU GOT ONE OF MY OUTPOSTS!\n";
delete($computer_bases{$target});
my $size = keys %computer_bases;
if ($size == 3) { print "ONE DOWN, THREE TO GO.\n"};
if ($size == 2) { print "TWO DOWN, TWO TO GO.\n"};
if ($size == 1) { print "THREE DOWN, ONE TO GO.\n"};
if ($size == 0) { print "YOU GOT ME, I'M GOING FAST. BUT I'LL GET YOU WHEN\nMY TRANSISTO&S RECUP%RA*E!\n"};
}
else {
print "HA, HA YOU MISSED. MY TURN NOW:\n";
}
}
sub populate_player_bases {
print "WHAT ARE YOUR FOUR POSITIONS\n";
my $positions=<STDIN>;
chomp($positions);
my @positions = split/ /,$positions;
foreach my $base (@positions) {
$player_bases{$base}=0;
}
}
sub display_field {
for my $num (1..25) {
if (length($num) < 2) {
$num = " $num";
}
print "$num ";
if ($num % 5 == 0) {
print "\n";
}
}
}
sub populate_computer_bases {
my $size = 0;
until ($size == 4) {
my $base = int(rand(25)+1);
$computer_bases{$base}=0;
$size = keys %computer_bases;
}
}
sub print_intro {
print " " x 33, "BOMBARDMENT\n";
print " " x 15, " CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n";
print "\n\n";
print "YOU ARE ON A BATTLEFIELD WITH 4 PLATOONS AND YOU\n";
print "HAVE 25 OUTPOSTS AVAILABLE WHERE THEY MAY BE PLACED.\n";
print "YOU CAN ONLY PLACE ONE PLATOON AT ANY ONE OUTPOST.\n";
print "THE COMPUTER DOES THE SAME WITH ITS FOUR PLATOONS.\n\n";
print "THE OBJECT OF THE GAME IS TO FIRE MISSLES AT THE\n";
print "OUTPOSTS OF THE COMPUTER. IT WILL DO THE SAME TO YOU.\n";
print "THE ONE WHO DESTROYS ALL FOUR OF THE ENEMY'S PLATOONS\n";
print "FIRST IS THE WINNER.\n\n";
print "GOOD LUCK... AND TELL US WHERE YOU WANT THE BODIES SENT!\n\n";
print "TEAR OFF MATRIX AND USE IT TO CHECK OFF THE NUMBERS.\n";
print "\n\n\n\n";
}

View File

@@ -0,0 +1,140 @@
#!/usr/bin/env perl
use v5.24;
use warnings;
use experimental 'signatures';
no warnings 'experimental::signatures';
exit main(@ARGV);
sub main {
$|++;
my $mission = 'y';
my @choices = (
{ # 1 - Italy
ask => 'YOUR TARGET -- ALBANIA(1), GREECE(2), NORTH AFRICA(3)',
comments => [
q{SHOULD BE EASY -- YOU'RE FLYING A NAZI-MADE PLANE.},
'BE CAREFUL!!!',
q{YOU'RE GOING FOR THE OIL, EH?},
],
},
{ # 2 - Allies
ask => 'AIRCRAFT -- LIBERATOR(1), B-29(2), B-17(3), LANCASTER(4)',
comments => [
q{YOU'VE GOT 2 TONS OF BOMBS FLYING FOR PLOESTI.},
q{YOU'RE DUMPING THE A-BOMB ON HIROSHIMA.},
q{YOU'RE CHASING THE BISMARK IN THE NORTH SEA.},
q{YOU'RE BUSTING A GERMAN HEAVY WATER PLANT IN THE RUHR.},
],
},
\&japan,
{ # 4 - Germany
ask => "A NAZI, EH? OH WELL. ARE YOU GOING FOR RUSSIA(1),\n"
. 'ENGLAND(2), OR FRANCE(3)',
comments => [
q{YOU'RE NEARING STALINGRAD.},
q{NEARING LONDON. BE CAREFUL, THEY'VE GOT RADAR.},
q{NEARING VERSAILLES. DUCK SOUP. THEY'RE NEARLY DEFENSELESS.},
],
},
);
while (fc($mission // 'n') eq fc('y')) {
say 'YOU ARE A PILOT IN A WORLD WAR II BOMBER.';
my $side = choose(
'WHAT SIDE -- ITALY(1), ALLIES(2), JAPAN(3), GERMANY(4)? ', 4);
my $choice = $choices[$side - 1];
ref($choice) eq 'HASH' ? multiple($choice) : $choice->();
print "\n\n\nANOTHER MISSION (Y OR N)? ";
chomp($mission = <STDIN>);
}
say "CHICKEN !!!\n";
return 0;
}
sub choose ($prompt, $n_max) {
while ('necessary') {
print "$prompt? ";
chomp(my $side = <STDIN>);
return $side if $side =~ m{\A [1-9]\d* \z}mxs && $side <= $n_max;
say 'TRY AGAIN...';
}
}
sub multiple ($spec) {
my $target = choose("$spec->{ask}? ", scalar $spec->{comments}->@*);
say $spec->{comments}->[$target - 1];
say '';
my $missions;
while ('necessary') {
print 'HOW MANY MISSIONS HAVE YOU FLOWN? ';
chomp($missions = <STDIN>);
last if $missions < 160;
print 'MISSIONS, NOT MILES...
150 MISSIONS IS HIGH EVEN FOR OLD-TIMERS.
NOW THEN, ';
}
if ($missions < 25) { say 'FRESH OUT OF TRANING, EH?' }
elsif ($missions >= 100) { say q{THAT'S PUSHING THE ODDS!} }
return direct_hit() if $missions >= rand(160);
my $miss = 2 + int rand(30);
say "MISSED TARGET BY $miss MILES!";
say "NOW YOU'RE REALLY IN FOR IT !!\n";
our $double_fire = 0;
my $response = choose(
'DOES THE ENEMY HAVE GUNS(1), MISSILES(2), OR BOTH(3)', 3);
if ($response != 2) {
print q{WHAT'S THE PERCENT HIT RATE OF ENEMY GUNNERS (10 TO 50)? };
chomp (our $hit_rate = <STDIN>);
if ($hit_rate < 10) {
say q{YOU LIE, BUT YOU'LL PAY...};
return endgame('fail'); # sure failure
}
say '';
}
if ($response > 1) {
$double_fire = 35;
}
return endgame();
}
sub direct_hit {
my $killed = int rand(100);
say "DIRECT HIT!!!! $killed KILLED.\nMISSION SUCCESSFUL";
return;
}
sub endgame ($fail = 0) {
our $double_fire //= 0;
our $hit_rate //= 0;
$fail ||= ($double_fire + $hit_rate) > rand(100);
if ($fail) {
say '* * * * BOOM * * * *
YOU HAVE BEEN SHOT DOWN.....
DEARLY BELOVED, WE ARE GATHERED HERE TODAY TO PAY OUR
LAST TRIBUTE...';
}
else {
say 'YOU MADE IT THROUGH TREMENDOUS FLAK!!';
}
return;
}
sub japan {
say q{YOU'RE FLYING A KAMIKAZE MISSION OVER THE USS LEXINGTON.};
print q{YOUR FIRST KAMIKAZE MISSION(Y OR N)? };
chomp(my $is_first_kamikaze = <STDIN>);
if (fc($is_first_kamikaze) eq fc('n')) {
our $hit_rate = 0;
say '';
return endgame();
}
return direct_hit() if rand(1) > 0.65;
return endgame('fail');
}

184
13_Bounce/ruby/bounce.rb Normal file
View File

@@ -0,0 +1,184 @@
## Global constants
# Gravity accelaration (F/S^2) ~= 32
G = 32
# Used to indent the plotting of ball positions
# so that the height digits don't affect
# where we start plotting ball positions
BALL_PLOT_INDENT = "\t"
# The deviation between current plotted height and the actual
# height of the ball that we will accept to plot the ball in
# that plotted height
BALL_PLOT_DEVIATION = 0.25
# The step we will take as we move down vertically while
# plotting ball positions
BALL_PLOT_HEIGHT_STEP = 0.5
## Helper functions
# Calculates the bounce speed (up) of the ball for a given
# bounce number and coefficient
def calc_velocity_for_bounce(v0, bounce, coefficient)
v = v0 * coefficient**bounce
end
# Check https://physics.stackexchange.com/a/333436 for nice explanation
def calc_bounce_total_time(v0, bounce, coefficient)
v = calc_velocity_for_bounce(v0, bounce, coefficient)
t = 2 * v / G
end
# Check https://physics.stackexchange.com/a/333436 for nice explanation
def calc_ball_height(v0, bounce, coefficient, t)
v = calc_velocity_for_bounce(v0, bounce, coefficient)
h = v * t - 0.5 * G * t**2
end
def heighest_position_in_next_bounce(time_in_bounce, v0, i, c)
time_in_next_bounce = time_in_bounce[i+1]
return -1 if time_in_next_bounce.nil?
return calc_ball_height(v0, i, c, time_in_next_bounce / 2) unless time_in_next_bounce.nil?
end
def intro
puts <<~INSTRUCTIONS
BOUNCE
CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
THIS SIMULATION LETS YOU SPECIFY THE INITIAL VELOCITY
OF A BALL THROWN STRAIGHT UP, AND THE COEFFICIENT OF
ELASTICITY OF THE BALL. PLEASE USE A DECIMAL FRACTION
COEFFICIENCY (LESS THAN 1).
YOU ALSO SPECIFY THE TIME INCREMENT TO BE USED IN
'STROBING' THE BALL'S FLIGHT (TRY .1 INITIALLY).
INSTRUCTIONS
end
## Plottin functions
def plot_header
puts
puts "FEET"
end
def plot_bouncing_ball(strobbing_time, v0, c)
## Initializing helper values
# How many bounces we want to plot
# original BASIC version is 70 / (V / (16 * S2))
# 70 is assumed to be an arbitrary number higher than 2G and 16 is 1/2G
bounces_to_plot = (G**2 / (v0 / strobbing_time)).to_i
# Holds the total time the ball spends in the air in every bounce
time_in_bounce = bounces_to_plot.times.map { |i| calc_bounce_total_time v0, i, c }
plot_width = 0
# Calculate the highest position for the ball after the very first bounce
plotted_height = (calc_ball_height(v0, 0, c, v0/G) + 0.5).to_i
## Plotting bouncing ball
while plotted_height >= 0 do
# We will print only whole-number heights
print plotted_height.to_i if plotted_height.to_i === plotted_height
print BALL_PLOT_INDENT
bounces_to_plot.times { |i|
(0..time_in_bounce[i]).step(strobbing_time) { |t|
ball_pos = calc_ball_height v0, i, c, t
# If the ball is within the acceptable deviation
# from the current height, we will plot it
if (plotted_height - ball_pos).abs <= BALL_PLOT_DEVIATION then
print "0"
else
print " "
end
# Increment the plot width when we are plotting height = 0
# which will definitely be the longest since it never gets
# skipped by line 98
plot_width += 1 if plotted_height == 0
}
if heighest_position_in_next_bounce(time_in_bounce, v0, i, c) < plotted_height then
# If we got no more ball positions at or above current height in the next bounce,
# we can skip the rest of the bounces and move down to the next height to plot
puts
break
end
}
plotted_height -= BALL_PLOT_HEIGHT_STEP
end
# Return plot_width to be used by the plot_footer
plot_width
end
def plot_footer (plot_width, strobbing_time)
# Dotted separator line
puts
print BALL_PLOT_INDENT
(plot_width).times { |_| print "." }
puts
# Time values line
print BALL_PLOT_INDENT
points_in_sec = (1 / strobbing_time).to_i
plot_width.times { |i|
if i % points_in_sec == 0 then
print (i / points_in_sec).to_i
else
print " "
end
}
puts
# Time unit line
print BALL_PLOT_INDENT
(plot_width / 2 - 4).to_i.times { |_| print " " }
puts "SECONDS"
puts
end
def game_loop
# Read strobing, velocity and coefficient parameters from user input
puts "TIME INCREMENT (SEC)"
strobbing_time = gets.to_f
puts "VELOCITY (FPS)"
v0 = gets.to_f
puts "COEFFICIENT"
c = gets.to_f
# Plotting
plot_header
plot_width = plot_bouncing_ball strobbing_time, v0, c
plot_footer plot_width, strobbing_time
end
## Entry point
begin
intro
loop do
game_loop
end
rescue SystemExit, Interrupt
exit
rescue => exception
p exception
end

View File

@@ -0,0 +1,156 @@
########################################################
# Calendar
#
# From: BASIC Computer Games (1978)
# Edited by David Ahl#
#
# This program prints out a calendar
# for any year. You must specify the
# starting day of the week of the year in
# statement 130. (Sunday(0), Monday
# (-1), Tuesday(-2), etc.) You can determine
# this by using the program WEEKDAY.
# You must also make two changes
# for leap years in statement 360 and 620.
# The program listing describes the necessary
# changes. Running the program produces a
# nice 12-month calendar.
# The program was written by Geofrey
# Chase of the Abbey, Portsmouth, Rhode Island.
#
########################################################
def parse_input():
"""
function to parse input for weekday and leap year boolean
"""
days_mapping = {
"sunday": 0,
"monday": -1,
"tuesday": -2,
"wednesday": -3,
"thursday": -4,
"friday": -5,
"saturday": -6
}
day = 0
leap_day = False
correct_day_input = False
while not correct_day_input:
weekday = input("INSERT THE STARTING DAY OF THE WEEK OF THE YEAR:")
for day_k in days_mapping.keys():
if weekday.lower() in day_k:
day = days_mapping[day_k]
correct_day_input = True
break
while True:
leap = input("IS IT A LEAP YEAR?:")
if 'y' in leap.lower():
leap_day = True
break
if 'n' in leap.lower():
leap_day = False
break
return day, leap_day
def calendar(weekday, leap_year):
"""
function to print a year's calendar.
input:
_weekday_: int - the initial day of the week (0=SUN, -1=MON, -2=TUES...)
_leap_year_: bool - indicates if the year is a leap year
"""
months_days = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
days = 'S M T W T F S\n'
sep = "*" * 59
years_day = 365
d = weekday
if leap_year:
months_days[2] = 29
years_day = 366
months_names = [" JANUARY ",
" FEBRUARY",
" MARCH ",
" APRIL ",
" MAY ",
" JUNE ",
" JULY ",
" AUGUST ",
"SEPTEMBER",
" OCTOBER ",
" NOVEMBER",
" DECEMBER"]
days_count = 0 # S in the original program
# main loop
for n in range(1, 13):
days_count += months_days[n-1]
print("** {} ****************** {} ****************** {} **\n".format(days_count,
months_names[n-1], years_day-days_count))
print(days)
print(sep)
for w in range(1, 7):
print("\n")
for g in range(1, 8):
d += 1
d2 = d - days_count
if d2 > months_days[n]:
break
if d2 <= 0:
print("{}".format(' '), end=' ')
elif d2 < 10:
print(" {}".format(d2), end=' ')
else:
print("{}".format(d2), end=' ')
print()
if d2 >= months_days[n]:
break
if d2 > months_days[n]:
d -= g
print("\n")
print("\n")
def main():
print(" "*32 + "CALENDAR")
print(" "*15 + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY")
print("\n"*11)
day, leap_year = parse_input()
calendar(day, leap_year)
if __name__ == "__main__":
main()
########################################################
#
########################################################
#
# Porting notes:
#
# It has been added an input at the beginning of the
# program so the user can specify the first day of the
# week of the year and if the year is leap or not.
#
########################################################

View File

@@ -13,4 +13,5 @@ As published in Basic Computer Games (1978):
Downloaded from Vintage Basic at Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html http://www.vintage-basic.net/games.html
[Conversion to C](https://github.com/ericfischer/basic-computer-games/blob/main/24%20Chemist/c/chemist.c) #### External Links
- C: https://github.com/ericfischer/basic-computer-games/blob/main/24%20Chemist/c/chemist.c

69
25_Chief/perl/chief.pl Normal file
View File

@@ -0,0 +1,69 @@
#!/usr/bin/perl
use strict;
use warnings;
print ' ' x 30 . "CHIEF\n";
print ' ' x 15 . "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n";
print "\n\n\n";
print "I AM CHIEF NUMBERS FREEK, THE GREAT INDIAN MATH GOD.\n";
print "ARE YOU READY TO TAKE THE TEST YOU CALLED ME OUT FOR?\n";
chomp( my $A = uc <STDIN> );
print "SHUT UP, PALE FACE WITH WISE TONGUE.\n" unless ( $A eq 'YES' );
print " TAKE A NUMBER AND ADD 3. DIVIDE THIS NUMBER BY 5 AND\n";
print "MULTIPLY BY 8. DIVIDE BY 5 AND ADD THE SAME. SUBTRACT 1.\n";
print " WHAT DO YOU HAVE?\n";
chomp( my $B = <STDIN> );
my $C = ( $B + 1 - 5 ) * 5 / 8 * 5 - 3;
print "I BET YOUR NUMBER WAS $C. AM I RIGHT?\n";
chomp( my $D = uc <STDIN> );
if ( $D eq 'YES' ) {
print "BYE!!!\n";
exit;
}
print "WHAT WAS YOUR ORIGINAL NUMBER?\n";
chomp( my $K = <STDIN> );
my $F = $K + 3;
my $G = $F / 5;
my $H = $G * 8;
my $I = $H / 5 + 5;
my $J = $I - 1;
print "SO YOU THINK YOU'RE SO SMART, EH?\n";
print "NOW WATCH.\n";
print "$K PLUS 3 EQUALS $F. THIS DIVIDED BY 5 EQUALS $G;\n";
print "THIS TIMES 8 EQUALS $H. IF WE DIVIDE BY 5 AND ADD 5,\n";
print "WE GET $I , WHICH, MINUS 1, EQUALS $J.\n";
print "NOW DO YOU BELIEVE ME?\n";
chomp( my $Z = uc <STDIN> );
if ( $Z eq 'YES' ) {
print "BYE!!!\n";
exit;
}
print "YOU HAVE MADE ME MAD!!!\n";
print "THERE MUST BE A GREAT LIGHTNING BOLT!\n\n\n";
for my $i ( reverse 22 .. 30 ) {
print ' ' x $i . "X X\n";
}
print ' ' x 21 . "X XXX\n";
print ' ' x 20 . "X X\n";
print ' ' x 19 . "XX X\n";
for my $i ( reverse 13 .. 20 ) {
print ' ' x $i . "X X\n";
}
print ' ' x 12 . "XX\n";
print ' ' x 11 . "X\n";
print ' ' x 10 . "*\n";
print "\n#########################\n\n";
print "I HOPE YOU BELIEVE ME NOW, FOR YOUR SAKE!!\n";

77
25_Chief/python/Chief.py Normal file
View File

@@ -0,0 +1,77 @@
def print_lightning_bolt():
print('*'*36)
n = 24
while n > 16:
print(' '*n + 'x x')
n -=1
print(' '*16 + 'x xxx')
print(' '*15 + 'x x')
print(' '*14+ 'xxx x')
n -=1
while n > 8:
print(' '*n + 'x x')
n -=1
print(' '*8 + 'xx')
print(' '*7 +'x')
print('*'*36)
def print_solution(n):
print('\n{} plus 3 gives {}. This Divided by 5 equals {}'.format(n, n+3, (n+3)/5))
print('This times 8 gives {}. If we divide 5 and add 5.'.format(( (n+3)/5 )*8 ))
print('We get {}, which, minus 1 equals {}'.format(( ((n+3)/5)*8)/5+5, ((((n+3)/5)*8)/5+5)-1 ))
def Game():
print('\nTake a Number and ADD 3. Now, Divide this number by 5 and')
print('multiply by 8. Now, Divide by 5 and add the same. Subtract 1')
resp = float(input('\nWhat do you have? '))
comp_guess = (((resp - 4)*5)/8)*5 -3
resp2 = input('\nI bet your number was {} was i right(Yes or No)? '.format(comp_guess))
if resp2 == 'Yes' or resp2 == 'YES' or resp2 == 'yes':
print('\nHuh, I Knew I was unbeatable')
print('And here is how i did it')
print_solution(comp_guess)
input('')
else:
resp3 = float(input('\nHUH!! what was you original number? '))
if resp3 == comp_guess:
print('\nThat was my guess, AHA i was right')
print("Shamed to accept defeat i guess, don't worry you can master mathematics too")
print('Here is how i did it')
print_solution(comp_guess)
input('')
else:
print('\nSo you think you\'re so smart, EH?')
print('Now, Watch')
print_solution(resp3)
resp4 = input('\nNow do you believe me? ')
if resp4 == 'Yes' or resp4 == 'YES' or resp4 == 'yes':
print('\nOk, Lets play again sometime bye!!!!')
input('')
else:
print('\nYOU HAVE MADE ME VERY MAD!!!!!')
print("BY THE WRATH OF THE MATHEMATICS AND THE RAGE OF THE GODS")
print("THERE SHALL BE LIGHTNING!!!!!!!")
print_lightning_bolt()
print('\nI Hope you believe me now, for your own sake')
input('')
if __name__ == '__main__':
print('I am CHIEF NUMBERS FREEK, The GREAT INDIAN MATH GOD.')
play = input('\nAre you ready to take the test you called me out for(Yes or No)? ')
if play == 'Yes' or play == 'YES' or play == 'yes':
Game()
else:
print('Ok, Nevermind. Let me go back to my great slumber, Bye')
input('')

226
30_Cube/java/src/Cube.java Normal file
View File

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

57
31_Depth_Charge/ruby/.gitignore vendored Normal file
View File

@@ -0,0 +1,57 @@
# Package Shell Specific Things
## Build Process
/build
## Transient files
/src/input
/src/output
/src/log
/drop
# Programming Languages
## Java
/src/java/**/*.class
## Rakudo / Perl6
/src/**/.precomp
## PHP
/vendor/
## Python
*.swp
__pycache__/
*.pyc
*.egg-info
/dist
## Ruby
*.gem
.bundle
# Editors
## Dia
*.dia.autosave
## Emacs
\#*\#
.\#*
## Vi / Vim
*.swp
# Filesystem Artifacts
## Mac
.DS_Store
## NFS
.nfs*

View File

@@ -13,7 +13,6 @@ class DepthCharge
break if ! get_input_another_game() break if ! get_input_another_game()
end end
# 420 PRINT "OK. HOPE YOU ENJOYED YOURSELF." : GOTO 600
printf("OK. HOPE YOU ENJOYED YOURSELF.\n") printf("OK. HOPE YOU ENJOYED YOURSELF.\n")
end end
@@ -51,7 +50,7 @@ class DepthCharge
the_input = Integer(value) rescue nil the_input = Integer(value) rescue nil
if the_input == nil || the_input < 0 if the_input == nil || the_input < 1
printf("PLEASE ENTER A POSITIVE NUMBER\n\n") printf("PLEASE ENTER A POSITIVE NUMBER\n\n")
next next
@@ -61,25 +60,7 @@ class DepthCharge
end end
end end
def get_search_area_dimension
# 20 INPUT "DIMENSION OF SEARCH AREA";G: PRINT
@search_area_dimension = get_input_positive_integer("DIMENSION OF SEARCH AREA: ")
# 30 N=INT(LOG(G)/LOG(2))+1
@num_tries = Integer(
Math.log(@search_area_dimension)/Math.log(2)
)
end
def print_instructions def print_instructions
# 40 PRINT "YOU ARE THE CAPTAIN OF THE DESTROYER USS COMPUTER"
# 50 PRINT "AN ENEMY SUB HAS BEEN CAUSING YOU TROUBLE. YOUR"
# 60 PRINT "MISSION IS TO DESTROY IT. YOU HAVE";N;"SHOTS."
# 70 PRINT "SPECIFY DEPTH CHARGE EXPLOSION POINT WITH A"
# 80 PRINT "TRIO OF NUMBERS -- THE FIRST TWO ARE THE"
# 90 PRINT "SURFACE COORDINATES; THE THIRD IS THE DEPTH."
# 100 PRINT : PRINT "GOOD LUCK !": PRINT
printf( <<~INSTRUCTIONS printf( <<~INSTRUCTIONS
YOU ARE THE CAPTAIN OF THE DESTROYER USS COMPUTER YOU ARE THE CAPTAIN OF THE DESTROYER USS COMPUTER
AN ENEMY SUB HAS BEEN CAUSING YOU TROUBLE. YOUR AN ENEMY SUB HAS BEEN CAUSING YOU TROUBLE. YOUR
@@ -110,19 +91,21 @@ GOOD LUCK !
end end
def setup_game def setup_game
get_search_area_dimension() @search_area_dimension = get_input_positive_integer("DIMENSION OF SEARCH AREA: ")
@num_tries = Integer(
Math.log(@search_area_dimension)/Math.log(2) + 1
)
setup_enemy() setup_enemy()
end end
def setup_enemy def setup_enemy
# 110 A=INT(G*RND(1)) : B=INT(G*RND(1)) : C=INT(G*RND(1))
@enemy_x = rand(1..@search_area_dimension) @enemy_x = rand(1..@search_area_dimension)
@enemy_y = rand(1..@search_area_dimension) @enemy_y = rand(1..@search_area_dimension)
@enemy_z = rand(1..@search_area_dimension) @enemy_z = rand(1..@search_area_dimension)
end end
def game_loop def game_loop
# 120 FOR D=1 TO N : PRINT : PRINT "TRIAL #";D; : INPUT X,Y,Z
for @trial in 1..@num_tries do for @trial in 1..@num_tries do
output_game_status() output_game_status()
@@ -130,7 +113,6 @@ GOOD LUCK !
@shot_y = get_input_positive_integer("Y: ") @shot_y = get_input_positive_integer("Y: ")
@shot_z = get_input_positive_integer("Z: ") @shot_z = get_input_positive_integer("Z: ")
# 130 IF ABS(X-A)+ABS(Y-B)+ABS(Z-C)=0 THEN 300
if ( if (
(@enemy_x - @shot_x).abs \ (@enemy_x - @shot_x).abs \
+ (@enemy_y - @shot_y).abs \ + (@enemy_y - @shot_y).abs \
@@ -140,7 +122,6 @@ GOOD LUCK !
you_win() you_win()
return return
else else
# 140 GOSUB 500 : PRINT : NEXT D
missed_shot() missed_shot()
end end
end end
@@ -156,54 +137,41 @@ GOOD LUCK !
printf("TRIAL \#%d\n", @trial) printf("TRIAL \#%d\n", @trial)
end end
def you_win def you_win
printf("B O O M ! ! YOU FOUND IT IN %d TRIES!\n\n", @trial ) printf("\nB O O M ! ! YOU FOUND IT IN %d TRIES!\n\n", @trial )
end end
def missed_shot def missed_shot
missed_directions = [] missed_directions = []
# 530 IF X>A THEN PRINT "EAST";
# 540 IF X<A THEN PRINT "WEST";
if @shot_x > @enemy_x if @shot_x > @enemy_x
missed_directions.push('TOO FAR EAST') missed_directions.push('TOO FAR EAST')
elsif @shot_x < @enemy_x elsif @shot_x < @enemy_x
missed_directions.push('TOO FAR WEST') missed_directions.push('TOO FAR WEST')
end end
# 510 IF Y>B THEN PRINT "NORTH";
# 520 IF Y<B THEN PRINT "SOUTH";
if @shot_y > @enemy_y if @shot_y > @enemy_y
missed_directions.push('TOO FAR NORTH') missed_directions.push('TOO FAR NORTH')
elsif @shot_y < @enemy_y elsif @shot_y < @enemy_y
missed_directions.push('TOO FAR SOUTH') missed_directions.push('TOO FAR SOUTH')
end end
# 560 IF Z>C THEN PRINT " TOO LOW."
# 570 IF Z<C THEN PRINT " TOO HIGH."
# 580 IF Z=C THEN PRINT " DEPTH OK."
if @shot_z > @enemy_z if @shot_z > @enemy_z
missed_directions.push('TOO DEEP') missed_directions.push('TOO DEEP')
elsif @shot_z < @enemy_z elsif @shot_z < @enemy_z
missed_directions.push('TOO SHALLOW') missed_directions.push('TOO SHALLOW')
end end
# 500 PRINT "SONAR REPORTS SHOT WAS ";
printf("SONAR REPORTS SHOT WAS: \n") printf("SONAR REPORTS SHOT WAS: \n")
printf("%s\n", "\t" + missed_directions.join("\n\t")) printf("%s\n", "\t" + missed_directions.join("\n\t"))
# 550 IF Y<>B OR X<>A THEN PRINT " AND";
# 590 RETURN
end end
def you_lose def you_lose
# You took too long!
printf("YOU HAVE BEEN TORPEDOED! ABANDON SHIP!\n") printf("YOU HAVE BEEN TORPEDOED! ABANDON SHIP!\n")
printf("THE SUBMARINE WAS AT %d %d %d\n", @enemy_x, @enemy_y, @enemy_z) printf("THE SUBMARINE WAS AT %d %d %d\n", @enemy_x, @enemy_y, @enemy_z)
end end
def get_input_another_game def get_input_another_game
# 400 PRINT : PRINT: INPUT "ANOTHER GAME (Y OR N)";A$
return get_input_y_or_n("ANOTHER GAME (Y OR N): ") return get_input_y_or_n("ANOTHER GAME (Y OR N): ")
# 410 IF A$="Y" THEN 100
end end
end end

View File

@@ -0,0 +1,179 @@
#!/usr/bin/perl
use strict;
use warnings;
&main;
sub main {
&print_intro;
&game_play;
}
sub game_play {
my $marbles = 27;
my $turn = 0;
my $player_total = 0;
my $computer_total = 0;
print "TYPE A '1' IF YOU WANT TO GO FIRST AND TYPE A '0' IF YOU WANT ME TO GO FIRST\n";
my $choice = <STDIN>;
chomp($choice);
if ($choice == 0) {
until ($marbles == 0) {
my $computer_choice = &computer_select($marbles,$turn,$player_total);
$marbles = $marbles - $computer_choice;
$computer_total = $computer_total + $computer_choice;
print "MY TOTAL IS $computer_total\n";
print "TOTAL= $marbles\n";
if ($marbles == 0) {&determine_winner($computer_total,$player_total)};
my $player_choice = &player_select($marbles,$turn);
$marbles = $marbles - $player_choice;
$player_total = $player_total + $player_choice;
print "YOUR TOTAL IS $player_total\n";
$turn++;
print "TOTAL= $marbles\n";
if ($marbles == 0) {&determine_winner($computer_total,$player_total)};
}
}
elsif ($choice == 1) {
until ($marbles == 0) {
my $player_choice = &player_select($marbles,$turn);
$marbles = $marbles - $player_choice;
$player_total = $player_total + $player_choice;
$turn++;
print "YOUR TOTAL IS $player_total\n";
print "TOTAL= $marbles\n";
if ($marbles == 0) {&determine_winner($computer_total,$player_total)};
my $computer_choice = &computer_select($marbles,$turn,$player_total);
$marbles = $marbles - $computer_choice;
$computer_total = $computer_total + $computer_choice;
print "MY TOTAL IS $computer_total\n";
print "TOTAL= $marbles\n";
if ($marbles == 0) {&determine_winner($computer_total,$player_total)};
}
}
}
sub determine_winner {
my $computer = shift;
my $player = shift;
print "THAT IS ALL OF THE MARBLES.\n\n";
print "MY TOTAL IS $computer, YOUR TOTAL IS $player\n";
if ($player % 2 == 0) {
print " YOU WON.\n";
}
if ($computer % 2 == 0) {
print " I WON.\n";
}
my $answer = -1;
until ($answer == 1 || $answer == 0) {
print "DO YOU WANT TO PLAY AGAIN? TYPE 1 FOR YES AND 0 FOR NO.\n";
$answer=<STDIN>;
chomp($answer);
}
if ($answer == 1) {
&game_play;
}
else {
print "OK. SEE YOU LATER.\n";
exit;
}
}
sub player_select {
my $marbles = shift;
my $turn = shift;
my $validity="invalid";
if ($turn == 0) {
print "WHAT IS YOUR FIRST MOVE\n";
}
else {
print "WHAT IS YOUR NEXT MOVE\n";
}
until ($validity eq "valid") {
my $num = <STDIN>;
chomp($num);
my $validity=&validity_check($marbles,$num);
if ($validity eq "valid") {
return $num;
}
}
}
sub computer_select {
my $marbles = shift;
my $turn = shift;
my $player_total = shift;
my $num = 2;
my $validity = "invalid";
if ($turn == 0) {
print "I PICK UP $num MARBLES.\n\n";
}
else {
until ($validity eq "valid") {
my $R=$marbles-6*int(($marbles/6));
if (int($player_total/2) == $player_total/2) {
if ($R < 1.5 || $R > 5.3) {
$num = 1;
}
else {
$num = $R - 1;
}
}
elsif ($marbles < 4.2) {
$num = $marbles;
}
elsif ($R > 3.4) {
if ($R < 4.7 || $R > 3.5) {
$num = 4;
}
else {
$num = 1;
}
}
$validity=&validity_check($marbles,$num);
}
print "\nI PICK UP $num MARBLES.\n\n";
}
return $num;
}
sub validity_check {
my $marbles = shift;
my $num = shift;
if ($num > $marbles) {
print "YOU HAVE TRIED TO TAKE MORE MARBLES THAN THERE ARE LEFT. TRY AGAIN. THERE ARE $marbles MARBLES LEFT\n";
return "invalid";
}
if ($num > 0 && $num <= 4) {
return "valid";
}
if ($num < 1 || $num > 4) {
print "THE NUMBER OF MARBLES YOU TAKE MUST BE A POSITIVE INTEGER BETWEEN 1 AND 4\nWHAT IS YOUR NEXT MOVE?\n";
return "invalid";
}
else {
return "invalid";
}
}
sub print_intro {
print ' ' x 31 . "EVEN WINS\n";
print ' ' x 15 . "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n";
print "THIS IS A 2 PERSON GAME CALLED 'EVEN WINS'. TO PLAY THE GAME, THE PLAYERS NEED 27 MARBLES OR OTHER OBJECTS ON THE TABLE\n\n";
print "THE 2 PLAYERS ALTERNATE TURNS, WITH EACH PLAYER REMOVING FROM 1 TO 4 MARBLES ON EACH MOVE. THE GAME ENDS WHEN THERE ARE NO MARBLES LEFT, AND THE WINNER IS THE ONE WITH AN EVEN NUMBER OF MARBLES\n";
print "THE ONLY RULES ARE THAT (1) YOU MUST ALTERNATE TURNS, (2) YOU MUST TAKE BETWEEN 1 AND 4 MARBLES EACH TURN, AND (3) YOU CANNOT SKIP A TURN.\n\n";
}

View File

@@ -142,17 +142,32 @@ def game_over():
print('') print('')
def computer_turn(): def computer_turn():
global marbles_in_middle global marbles_in_middle
global computer_marbles global computer_marbles
global human_marbles
print("It's the computer's turn ...") marbles_to_take=0
max_choice = min(4, marbles_in_middle)
# choose at random print("It's the computer's turn ...")
n = random.randint(1, max_choice) r = marbles_in_middle - 6 * int((marbles_in_middle/6)) #line 500
print(f'Computer takes {marbles_str(n)} ...')
marbles_in_middle -= n if int(human_marbles/2) == human_marbles/2: #line 510
computer_marbles += n if r < 1.5 or r > 5.3: #lines 710 and 720
marbles_to_take = 1
else:
marbles_to_take = r - 1
elif marbles_in_middle < 4.2: #line 580
marbles_to_take = marbles_in_middle
elif r > 3.4: #line 530
if r < 4.7 or r > 3.5:
marbles_to_take = 4
else:
marbles_to_take = r + 1
print(f'Computer takes {marbles_str(marbles_to_take)} ...')
marbles_in_middle -= marbles_to_take
computer_marbles += marbles_to_take
def play_game(): def play_game():
global marbles_in_middle global marbles_in_middle

View File

@@ -0,0 +1,162 @@
#A class representing the internal state of a single game of flip flop
# state represents the list of X's (A in the original code)
# guesses represents the number of guesses the user has made (C in the original code)
# seed represents the random seed for an instance of the game (Q in the original code)
Game = Struct.new(:state, :guesses, :seed) do
#The original BASIC program used 1 indexed arrays while Ruby has 0-indexed arrays.
#We can't use 0 indexed arrays for the flip functions or we'll get divide by zero errors.
#These convenience functions allow us to modify and access internal game state in a 1-indexed fashion
def flip_letter(letter_number)
index = letter_number -1
if self.state[index] == 'X'
self.state[index] = 'O'
else
self.state[index] = 'X'
end
end
def letter_at(letter_number)
self.state[letter_number - 1]
end
end
def print_welcome
puts 'FLIPFLOP'.center(72)
puts 'CREATIVE COMPUTING MORRISTOWN, NEW JERSEY'.center(72)
puts <<~EOS
THE OBJECT OF THIS PUZZLE IS TO CHANGE THIS:
X X X X X X X X X X
TO THIS:
O O O O O O O O O O
BY TYPING THE NUMBER CORRESPONDING TO THE POSITION OF THE
LETTER ON SOME NUMBERS, ONE POSITION WILL CHANGE, ON
OTHERS, TWO WILL CHANGE. TO RESET LINE TO ALL X'S, TYPE 0
(ZERO) AND TO START OVER IN THE MIDDLE OF A GAME, TYPE
11 (ELEVEN).
EOS
end
def print_starting_message
puts <<~EOS
HERE IS THE STARTING LINE OF X'S.
1 2 3 4 5 6 7 8 9 10
X X X X X X X X X X
EOS
end
#Create a new game with [X,X,X,X,X,X,X,X,X,X] as the state
#0 as the number of guesses and a random seed between 0 and 1
def generate_new_game
Game.new(Array.new(10, 'X'), 0, rand())
end
#Given a game, an index, and a shuffle function, flip one or more letters
def shuffle_board(game, index, shuffle_function)
n = method(shuffle_function).call(game, index)
if game.letter_at(n) == "O"
game.flip_letter(n)
if index == n
n = shuffle_board(game, index, shuffle_function)
end
else
game.flip_letter(n)
end
return n
end
#Shuffle logic copied from original BASIC code
def shuffle_function1(game, index)
r = Math.tan(game.seed + index / game.seed - index) - Math.sin(game.seed / index) + 336 * Math.sin(8 * index)
n = r - r.floor
(10 * n).floor
end
def shuffle_function2(game, index)
r = 0.592 * (1/ Math.tan(game.seed / index + game.seed)) / Math.sin(index * 2 + game.seed) - Math.cos(index)
n = r - r.floor
(10 * n)
end
def play_game
print_starting_message
game = generate_new_game
working_index = nil
loop do
puts "INPUT THE NUMBER"
input = gets.chomp.downcase
#See if the user input a valid integer, fail otherwise
if numeric_input = Integer(input, exception: false)
#If 11 is entered, we're done with this version of the game
if numeric_input == 11
return :restart
end
if numeric_input > 11
puts 'ILLEGAL ENTRY--TRY AGAIN.'
next #illegal entries don't count towards your guesses
end
if working_index == numeric_input
game.flip_letter(numeric_input)
working_index = shuffle_board(game, numeric_input, :shuffle_function2)
#If 0 is entered, we want to reset the state, but not the random seed or number of guesses and keep playing
elsif numeric_input == 0
game.state = Array.new(10, 'X')
elsif game.letter_at(numeric_input) == "O"
game.flip_letter(numeric_input)
if numeric_input == working_index
working_index = shuffle_board(game, numeric_input, :shuffle_function1)
end
else
game.flip_letter(numeric_input)
working_index = shuffle_board(game, numeric_input, :shuffle_function1)
end
else
puts 'ILLEGAL ENTRY--TRY AGAIN.'
next #illegal entries don't count towards your guesses
end
game.guesses += 1
puts '1 2 3 4 5 6 7 8 9 10'
puts game.state.join(' ')
if game.state.all? { |x| x == 'O' }
if game.guesses > 12
puts "TRY HARDER NEXT TIME. IT TOOK YOU #{game.guesses} GUESSES."
else
puts "VERY GOOD. YOU GUESSED IT IN ONLY #{game.guesses} GUESSES."
end
#game is complete
return
end
end
end
#Execution starts
print_welcome
loop do
result = play_game
if result == :restart
next
end
puts 'DO YOU WANT TO TRY ANOTHER PUZZLE'
if gets.chomp.downcase[0] == 'n'
break
end
end

View File

@@ -21,6 +21,6 @@ As published in Basic Computer Games (1978):
Downloaded from Vintage Basic at Downloaded from Vintage Basic at
http://www.vintage-basic.net/games.html http://www.vintage-basic.net/games.html
[Port to C language](https://github.com/beyonddream/hamurabi) #### External Links
- C: https://github.com/beyonddream/hamurabi
[Port to Rust language](https://github.com/beyonddream/hamurabi.rs) - Rust: https://github.com/beyonddream/hamurabi.rs

223
44_Hangman/perl/hangman.pl Executable file
View File

@@ -0,0 +1,223 @@
#!/usr/bin/perl
use strict;
use warnings;
# global variables defined here
my(@WORDS) = qw(
GUM SIN FOR CRY LUG BYE FLY
UGLY EACH FROM WORK TALK WITH SELF
PIZZA THING FEIGN FIEND ELBOW FAULT DIRTY
BUDGET SPIRIT QUAINT MAIDEN ESCORT PICKAX
EXAMPLE TENSION QUININE KIDNEY REPLICA SLEEPER
TRIANGLE KANGAROO MAHOGANY SERGEANT SEQUENCE
MOUSTACHE DANGEROUS SCIENTIST DIFFERENT QUIESCENT
MAGISTRATE ERRONEOUSLY LOUDSPEAKER PHYTOTOXIC
MATRIMONIAL PARASYMPATHOMIMETIC THIGMOTROPISM
);
my(@PIC,$board,@guessedLetters,$guessCount,$hangCount);
my(%GUESSED);
# Subroutines defined here.
# init_variables: initialize all of the variables needed
# (this covers lines 50-90 in the original BASIC program)
sub init_variables {
@guessedLetters = ();
@PIC = (
'XXXXXXX ',
'X X ',
'X ',
'X ',
'X ',
'X ',
'X ',
'X ',
'X ',
'X ',
'X ',
'X ',
);
$guessCount = 0; %GUESSED = ();
$hangCount = 0;
}
# addchar: given a row & column, put the specified char in that place in @PIC
sub addchar {
my($row,$col, $c) = @_;
substr($PIC[$row],$col,1) = $c;
}
# main code starts here
print ' 'x31; print "Hangman\n";
print ' 'x14; print "Creative Computing Morristown, New Jersey\n\n\n\n";
# an iteration of the PLAY block is one complete game.
# There is a continue block that will ask if the user
# wants to play another game.
PLAY: while (1) {
init_variables();
# Any words left?
if (@WORDS == 0) {
print "You did all the words!\n";
last PLAY;
}
# splice a random word out of the @WORDS array
my($thisWord) = splice(@WORDS, int(rand(scalar @WORDS)),1);
# $board is the "game board" of the filled-out word
# that the user is working on
$board = '.'x(length $thisWord);
# GUESS loop is run for every time the user guesses a letter
GUESS: while(1) {
print "Here are the letters you used:\n";
printf("%s\n", join(',',@guessedLetters));
printf("\n\n%s\n", $board);
print "What is your guess for a letter ? ";
chomp(my $guess = <STDIN>);
# The %GUESSED hash allows us to quickly identify
# letters that have already been guessed
if ($GUESSED{lc $guess}) {
print "You guessed that letter before!\n\n";
redo GUESS;
}
# save the guessed letter
push @guessedLetters, $guess;
$GUESSED{lc $guess} = 1;
++$guessCount;
# now look for the letter in the $thisWord var
# and put it into the $board var wherever it
# shows up. $foundLetter is a flag that indicates
# whether or not the letter is found.
my $foundLetter = 0;
for (my $i = 0; $i < length $thisWord; ++$i) {
if (lc substr($thisWord,$i,1) eq lc $guess) {
$foundLetter = 1;
substr($board, $i, 1) = substr($thisWord, $i, 1);
}
}
# The user found a letter in the solution!
if ($foundLetter) {
# Are there any '.' chars left in the board?
if (index($board, '.') < 0) {
print "You found the word!\n\n";
} else {
printf("%s\n\n", $board);
print "What is your guess for the word ? ";
chomp(my $guessword = <STDIN>);
if (lc $thisWord ne lc $guessword) {
print "Wrong. Try another letter.\n";
# Go to the next iteration of the GUESS loop
next GUESS;
}
printf("Right! It took you %d %s!\n", $guessCount, ($guessCount == 1 ? 'guess' : 'guesses'));
}
# At this point the user has discovered the word and won.
# This "next" statement takes execution down to the
# continue block for the PLAY loop;
next PLAY;
} else { # didn't find a letter
++$hangCount;
print "\n\n\nSorry, that letter isn't in the word.\n";
# The addchar() calls in the block below piece together the
# hangman graphic, depending on how many wrong letters
# the user has.
if ($hangCount == 1) {
print "First, we draw a head\n";
addchar(2,5,"-");addchar(2,6,"-");addchar(2,7,"-");
addchar(3,4,"("); addchar(3,5,"."); addchar(3,7,"."); addchar(3,8,")");
addchar(4,5,"-");addchar(4,6,"-");addchar(4,7,"-");
}
if ($hangCount == 2) {
print "Now we draw a body.\n";
for (5 .. 8) {
addchar($_, 6, "X");
}
}
if ($hangCount == 3) {
print "Next we draw an arm.\n";
for (3 .. 6) {
addchar($_, $_-1, "\\");
}
}
if ($hangCount == 4) {
print "This time it's the other arm.\n";
addchar(3,10, "/");
addchar(4, 9, "/");
addchar(5, 8, "/");
addchar(6, 7, "/");
}
if ($hangCount == 5) {
print "Now, let's draw the right leg.\n";
addchar( 9,5, "/");
addchar(10,4, "/");
}
if ($hangCount == 6) {
print "This time we draw the left leg.\n";
addchar(9,7,"\\");
addchar(10,8,"\\");
}
if ($hangCount == 7) {
print "Now we put up a hand.\n";
addchar(2,10,"\\");
}
if ($hangCount == 8) {
print "Next the other hand.\n";
addchar(2,2,"/");
}
if ($hangCount == 9) {
print "Now we draw one foot\n";
addchar(11,9,"\\");
addchar(11,10, "-");
}
if ($hangCount == 10) {
print "Here's the other foot -- you're hung!!\n";
addchar(11,2,"-");
addchar(11,3, "/");
}
printf("$_\n") for @PIC;
print "\n\n";
# Next guess if the user has not lost
if ($hangCount < 10) {
next GUESS;
}
printf("Sorry, you lose. The word was %s\n", $thisWord);
next PLAY;
} # didn't find a letter block
} # GUESS block
} # PLAY block
# This block is reached either by the player winning (see the "next PLAY")
# statement) or by the user losing (as the PLAY block is complete and
# execution naturally comes to this continue block).
continue {
print "Want another word ? ";
chomp(my $in = <STDIN>);
if ($in !~ m/^y/i) {
# Exit the PLAY loop
print "\nIt's been fun! Bye for now.\n\n";
last PLAY;
}
# At this point execution goes to the start of the PLAY block,
# meaning a new game
}

View File

@@ -0,0 +1,153 @@
import java.io.PrintStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
/**
* Game of HighIQ
* <p>
* Based on the Basic Game of HighIQ Here:
* https://github.com/coding-horror/basic-computer-games/blob/main/48_High_IQ/highiq.bas
*
* No additional functionality has been added
*/
public class HighIQ {
//Game board, as a map of position numbers to their values
private final Map<Integer, Boolean> board;
//Output stream
private final PrintStream out;
//Input scanner to use
private final Scanner scanner;
public HighIQ(Scanner scanner) {
out = System.out;
this.scanner = scanner;
board = new HashMap<>();
//Set of all locations to put initial pegs on
int[] locations = new int[]{
13, 14, 15, 22, 23, 24, 29, 30, 31, 32, 33, 34, 35, 38, 39, 40, 42, 43, 44, 47, 48, 49, 50, 51, 52, 53, 58, 59, 60, 67, 68, 69
};
for (int i : locations) {
board.put(i, true);
}
board.put(41, false);
}
/**
* Plays the actual game, from start to finish.
*/
public void play() {
do {
printBoard();
while (!move()) {
out.println("ILLEGAL MOVE, TRY AGAIN...");
}
} while (!isGameFinished());
int pegCount = 0;
for (Integer key : board.keySet()) {
if (board.getOrDefault(key, false)) {
pegCount++;
}
}
out.println("YOU HAD " + pegCount + " PEGS REMAINING");
if (pegCount == 1) {
out.println("BRAVO! YOU MADE A PERFECT SCORE!");
out.println("SAVE THIS PAPER AS A RECORD OF YOUR ACCOMPLISHMENT!");
}
}
/**
* Makes an individual move
* @return True if the move was valid, false if the user made an error and the move is invalid
*/
public boolean move() {
out.println("MOVE WHICH PIECE");
int from = scanner.nextInt();
//using the getOrDefault, which will make the statement false if it is an invalid position
if (!board.getOrDefault(from, false)) {
return false;
}
out.println("TO WHERE");
int to = scanner.nextInt();
if (board.getOrDefault(to, true)) {
return false;
}
//Do nothing if they are the same
if (from == to) {
return true;
}
//using the difference to check if the relative locations are valid
int difference = Math.abs(to - from);
if (difference != 2 && difference != 18) {
return false;
}
//check if there is a peg between from and to
if (!board.getOrDefault((to + from) / 2, false)) {
return false;
}
//Actually move
board.put(from,false);
board.put(to,true);
board.put((from + to) / 2, false);
return true;
}
/**
* Checks if the game is finished
* @return True if there are no more moves, False otherwise
*/
public boolean isGameFinished() {
for (Integer key : board.keySet()) {
if (board.get(key)) {
//Spacing is either 1 or 9
//Looking to the right and down from every point, checking for both directions of movement
for (int space : new int[]{1, 9}) {
Boolean nextToPeg = board.getOrDefault(key + space, false);
Boolean hasMovableSpace = !board.getOrDefault(key - space, true) || !board.getOrDefault(key + space * 2, true);
if (nextToPeg && hasMovableSpace) {
return false;
}
}
}
}
return true;
}
public void printBoard() {
for (int i = 0; i < 7; i++) {
for (int j = 11; j < 18; j++) {
out.print(getChar(j + 9 * i));
}
out.println();
}
}
private char getChar(int position) {
Boolean value = board.get(position);
if (value == null) {
return ' ';
} else if (value) {
return '!';
} else {
return 'O';
}
}
}

View File

@@ -0,0 +1,37 @@
import java.util.Scanner;
public class HighIQGame {
public static void main(String[] args) {
printInstructions();
Scanner scanner = new Scanner(System.in);
do {
new HighIQ(scanner).play();
System.out.println("PLAY AGAIN (YES OR NO)");
} while(scanner.nextLine().equalsIgnoreCase("yes"));
}
public static void printInstructions() {
System.out.println("\t\t\t H-I-Q");
System.out.println("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
System.out.println("HERE IS THE BOARD:");
System.out.println(" ! ! !");
System.out.println(" 13 14 15\n");
System.out.println(" ! ! !");
System.out.println(" 22 23 24\n");
System.out.println("! ! ! ! ! ! !");
System.out.println("29 30 31 32 33 34 35\n");
System.out.println("! ! ! ! ! ! !");
System.out.println("38 39 40 41 42 43 44\n");
System.out.println("! ! ! ! ! ! !");
System.out.println("47 48 49 50 51 52 53\n");
System.out.println(" ! ! !");
System.out.println(" 58 59 60\n");
System.out.println(" ! ! !");
System.out.println(" 67 68 69");
System.out.println("TO SAVE TYPING TIME, A COMPRESSED VERSION OF THE GAME BOARD");
System.out.println("WILL BE USED DURING PLAY. REFER TO THE ABOVE ONE FOR PEG");
System.out.println("NUMBERS. OK, LET'S BEGIN.");
}
}

View File

@@ -5,40 +5,37 @@ print ' 'x33 . "LETTER\n";
print ' 'x15 . "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n"; print ' 'x15 . "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n";
print "\n\n\n"; print "\n\n\n";
print "LETTER GUESSING GAME\n"; print "\n"; print "LETTER GUESSING GAME\n\n";
print "I'LL THINK OF A LETTER OF THE ALPHABET, A TO Z.\n"; print "I'LL THINK OF A LETTER OF THE ALPHABET, A TO Z.\n";
print "TRY TO GUESS MY LETTER AND I'LL GIVE YOU CLUES\n"; print "TRY TO GUESS MY LETTER AND I'LL GIVE YOU CLUES\n";
print "AS TO HOW CLOSE YOU'RE GETTING TO MY LETTER.\n"; print "AS TO HOW CLOSE YOU'RE GETTING TO MY LETTER.\n";
my $A;
while (1) { while (1) {
my $L= 65+int(rand(1)*26); my $letter = 65 + int(rand(26));
my $G= 0; my $guesses = 0;
print "\n"; print "O.K., I HAVE A LETTER. START GUESSING.\n"; print "\nO.K., I HAVE A LETTER. START GUESSING.\n";
my $answer;
do { do {
print "\n"; print "WHAT IS YOUR GUESS? "; print "\nWHAT IS YOUR GUESS? ";
$G=$G+1; $guesses++;
chomp($A= <STDIN>); chomp($answer = <STDIN>);
$A= ord($A); $answer = ord($answer);
print "\n"; print "\n";
if ($A<$L) { print "TOO LOW. TRY A HIGHER LETTER.\n"; } print "TOO LOW. TRY A HIGHER LETTER.\n" if $answer < $letter;
if ($A>$L) { print "TOO HIGH. TRY A LOWER LETTER.\n"; } print "TOO HIGH. TRY A LOWER LETTER.\n" if $answer > $letter;
} until($A eq $L); } until($answer eq $letter);
print "\n"; print "YOU GOT IT IN $G GUESSES!!\n"; print "\nYOU GOT IT IN $guesses GUESSES!!\n";
if ($G<=5) { if ($guesses <= 5) {
print "GOOD JOB !!!!!\n"; print "GOOD JOB !!!!!\n";
for (my $N=1; $N<=15; $N++) { print chr(7); } #ASCII Bell. print chr(7) x 15; # ASCII Bell
} else { } else {
print "BUT IT SHOULDN'T TAKE MORE THAN 5 GUESSES!\n"; print "BUT IT SHOULDN'T TAKE MORE THAN 5 GUESSES!\n";
}
print "\n";
print "LET'S PLAN AGAIN.....\n";
} }
print "\nLET'S PLAY AGAIN.....\n";
}
exit; exit;

96
62_Mugwump/perl/mugwump.pl Executable file
View File

@@ -0,0 +1,96 @@
#!/usr/bin/perl
use strict;
use warnings;
# global variables defined here
my(@MUGWUMP) = ();
# subroutines defined here
# init_mugwump: pick the random places for the Mugwumps
sub init_mugwump() {
@MUGWUMP = ();
for (1 .. 4) {
push @MUGWUMP, [ int(rand 10), int(rand 10) ];
}
}
# main code starts here
# print introductory text
print <<HERE;
Mugwump
Creative Computing Morristown, New Jersey
The object of this game is to find four Mugwumps
hidden on a 10 by 10 grid. Homebase is position 0,0.
Any guess you make must be two numbers with each
number between 0 and 9, inclusive. First number
is distance to right of homebase and second number
is distance above homebase.
You get 10 tries. After each try, I will tell
you how far you are from each Mugwump.
HERE
# PLAY block implements a complete game, and the
# continue block prints the "let's play again" msg
PLAY: while (1) {
init_mugwump();
TURN: for my $turn (1 .. 10) {
printf("\nTurn no %d -- what is your guess ? ", $turn);
# Note that parsing of input is rudimentary, in keeping with the
# spirit of the original BASIC program. Increased input checks
# would be a good place to start working on this program!
chomp(my $in = <STDIN>);
my($M,$N) = split(/,/,$in);
$M = int($M);
$N = int($N);
for my $i (0 .. $#MUGWUMP) {
# -1 indicates a Mugwump that was already found
next if $MUGWUMP[$i]->[0] == -1;
if ($MUGWUMP[$i]->[0] == $M && $MUGWUMP[$i]->[1] == $N) {
$MUGWUMP[$i]->[0] = -1;
printf("You have found Mugwump %d\n", $i+1);
} else {
my $d = sqrt(($MUGWUMP[$i]->[0] - $M) ** 2 + ($MUGWUMP[$i]->[1] - $N) ** 2);
printf("You are %.1f units away from Mugwump %d\n", $d, $i+1);
}
}
# If a Mugwump still has not been found,
# go to the next turn
for my $j (0 .. $#MUGWUMP) {
if ($MUGWUMP[$j]->[0] != -1) {
next TURN;
}
}
# You win!
printf("You got all of them in %d %s!\n\n", $turn, ($turn == 1 ? 'turn' : 'turns'));
# Pass execution down to the continue block
next PLAY;
} # end of TURN loop
print "\nSorry, that's 10 tries. Here's where they're hiding:\n";
for my $i (0 .. $#MUGWUMP) {
printf("Mugwump %d is at (%d, %d)\n", $i+1, $MUGWUMP[$i]->[0], $MUGWUMP[$i]->[1])
if $MUGWUMP[$i]->[0] != -1;
}
}
continue {
print "\nThat was fun! Let's play again.......\n";
print "Four more Mugwumps are now in hiding.\n\n";
}

View File

@@ -0,0 +1,126 @@
import random
#Class of the Game
class NIM:
def __init__(self):
self.Piles = {
1 : 7,
2 : 5,
3 : 3,
4 : 1
}
def Remove_pegs(self, command):
try:
pile, num = command.split(',')
num = int(num)
pile = int(pile)
except Exception as e:
if 'not enough values' in str(e):
print('\nNot a valid command. Your command should be in the form of "1,3", Try Again\n')
else:
print('\nError, Try again\n')
return None
if self._command_integrity(num, pile) == True:
self.Piles[pile] -= num
else:
print('\nInvalid value of either Peg or Pile\n')
def get_AI_move(self):
possible_pile = []
for k,v in self.Piles.items():
if v != 0:
possible_pile.append(k)
pile = random.choice(possible_pile)
num = random.randint(1,self.Piles[pile])
return pile, num
def _command_integrity(self, num, pile):
if pile <= 4 and pile >= 1:
if num <= self.Piles[pile]:
return True
return False
def print_pegs(self):
for pile, peg in self.Piles.items():
print('Pile {} : {}'.format(pile, 'O '*peg))
def Help(self):
print('-'*10)
print('\nThe Game is player with a number of Piles of Objects("O" == one peg)')
print('\nThe Piles are arranged as given below(Tradional NIM)\n')
self.print_pegs()
print('\nAny Number of of Objects are removed one pile by "YOU" and the machine alternatively')
print('\nOn your turn, you may take all the objects that remain in any pile')
print('but you must take ATLEAST one object')
print('\nAnd you may take objects from only one pile on a single turn.')
print('\nThe winner is defined as the one that picks the last remaning object')
print('-'*10)
def Checkforwin(self):
sum = 0
for k,v in self.Piles.items():
sum += v
if sum == 0:
return True
else:
return False
#main program
if __name__ == "__main__":
#Game initialization
game = NIM()
print('Hello, This is a game of NIM')
help = input('Do You Need Instruction (YES or NO): ')
if help == 'yes' or help == 'YES' or help == 'Yes':
#Printing Game Instruction
game.Help()
#Start game loop
input('\nPress Enter to start the Game:\n')
End = False
while True:
game.print_pegs()
#Players Move
command = input('\nYOUR MOVE - Number of PILE, Number of Object? ')
game.Remove_pegs(command)
End = game.Checkforwin()
if End == True:
print('\nPlayer Wins the Game, Congratulations!!')
input('\nPress any key to exit')
break
#Computers Move
command = game.get_AI_move()
print('\nA.I MOVE - A.I Removed {} pegs from Pile {}'.format(command[1],command[0]))
game.Remove_pegs(str(command[0]) +',' + str(command[1]))
End = game.Checkforwin()
if End == True:
print('\nComputer Wins the Game, Better Luck Next Time\n')
input('Press any key to exit')
break

View File

@@ -1,3 +1,3 @@
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html) Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
Conversion to [Oracle Java](https://openjdk.java.net/) Conversion to [Java](https://openjdk.java.net/)

View File

@@ -0,0 +1,357 @@
import java.util.*;
/**
* SPLAT simulates a parachute jump in which you try to open your parachute at the last possible moment without going
* splat! You may select your own terminal velocity or let the computer do it for you. You many also select the
* acceleration due to gravity or, again, let the computer do it in which case you might wind up on any of eight
* planets (out to Neptune), the moon, or the sun.
* <p>
* The computer then tells you the height youre jumping from and asks for the seconds of free fall. It then divides
* your free fall time into eight intervals and gives you progress reports on your way down. The computer also keeps
* track of all prior jumps in the array A and lets you know how you compared with previous successful jumps. If you
* want to recall information from previous runs, then you should store array A in a disk or take file and read it
* before each run.
* <p>
* John Yegge created this program while at the Oak Ridge Associated Universities.
* <p>
* Ported from BASIC by jason plumb (@breedx2)
* </p>
*/
public class Splat {
private static final Random random = new Random();
private final Scanner scanner = new Scanner(System.in);
private final List<Float> pastSuccessfulJumpDistances = new ArrayList<>();
public static void main(String[] args) {
new Splat().run();
}
public void run() {
showIntroduction();
while (true) {
InitialJumpConditions initial = buildInitialConditions();
System.out.println();
System.out.printf(" ALTITUDE = %d FT\n", initial.getAltitude());
System.out.printf(" TERM. VELOCITY = %.2f FT/SEC +/-5%%\n", initial.getOriginalTerminalVelocity());
System.out.printf(" ACCELERATION = %.2f FT/SEC/SEC +/-5%%\n", initial.getOriginalAcceleration());
System.out.println("SET THE TIMER FOR YOUR FREEFALL.");
float freefallTime = promptFloat("HOW MANY SECONDS ");
System.out.println("HERE WE GO.\n");
System.out.println("TIME (SEC) DIST TO FALL (FT)");
System.out.println("========== =================");
JumpResult jump = executeJump(initial, freefallTime);
showJumpResults(initial, jump);
if (!playAgain()) {
System.out.println("SSSSSSSSSS.");
return;
}
}
}
private void showIntroduction() {
System.out.printf("%33s%s\n", " ", "SPLAT");
System.out.printf("%15s%s\n", " ", "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
System.out.print("\n\n\n");
System.out.println("WELCOME TO 'SPLAT' -- THE GAME THAT SIMULATES A PARACHUTE");
System.out.println("JUMP. TRY TO OPEN YOUR CHUTE AT THE LAST POSSIBLE");
System.out.println("MOMENT WITHOUT GOING SPLAT.");
}
private InitialJumpConditions buildInitialConditions() {
System.out.print("\n\n");
float terminalVelocity = promptTerminalVelocity();
float acceleration = promptGravitationalAcceleration();
return InitialJumpConditions.create(terminalVelocity, acceleration);
}
private float promptTerminalVelocity() {
if (askYesNo("SELECT YOUR OWN TERMINAL VELOCITY")) {
float terminalVelocity = promptFloat("WHAT TERMINAL VELOCITY (MI/HR) ");
return mphToFeetPerSec(terminalVelocity);
}
float terminalVelocity = (int) (1000 * random.nextFloat());
System.out.printf("OK. TERMINAL VELOCITY = %.2f MI/HR\n", terminalVelocity);
return mphToFeetPerSec(terminalVelocity);
}
private float promptFloat(String prompt){
while(true){
System.out.print(prompt);
try {
return scanner.nextFloat();
} catch (Exception e) {
scanner.next(); // clear current input
}
}
}
private float promptGravitationalAcceleration() {
if (askYesNo("WANT TO SELECT ACCELERATION DUE TO GRAVITY")) {
return promptFloat("WHAT ACCELERATION (FT/SEC/SEC) ");
}
return chooseRandomAcceleration();
}
private JumpResult executeJump(InitialJumpConditions initial, float chuteOpenTime) {
JumpResult jump = new JumpResult(initial.getAltitude());
for (float time = 0.0f; time < chuteOpenTime; time += chuteOpenTime / 8) {
if (!jump.hasReachedTerminalVelocity() && time > initial.getTimeOfTerminalAccelerationReached()) {
jump.setReachedTerminalVelocity();
System.out.printf("TERMINAL VELOCITY REACHED AT T PLUS %f SECONDS.\n", initial.getTimeOfTerminalAccelerationReached());
}
float newDistance = computeDistance(initial, time, jump.hasReachedTerminalVelocity());
jump.setDistance(newDistance);
if (jump.isSplat()) {
return jump;
}
System.out.printf("%10.2f %f\n", time, jump.getDistance());
}
return jump;
}
private float computeDistance(InitialJumpConditions initial, float i, boolean hasReachedTerminalVelocity) {
final float V = initial.getTerminalVelocity();
final float A = initial.getAcceleration();
if (hasReachedTerminalVelocity) {
return initial.getAltitude() - ((V * V / (2 * A)) + (V * (i - (V / A))));
}
return initial.getAltitude() - ((A / 2) * i * i);
}
private void showJumpResults(InitialJumpConditions initial, JumpResult jump) {
if (jump.isSplat()) {
showSplatMessage(initial, jump);
showCleverSplatMessage();
return;
}
System.out.println("CHUTE OPEN");
int worseJumpCount = countWorseHistoricalJumps(jump);
int successfulJumpCt = pastSuccessfulJumpDistances.size();
pastSuccessfulJumpDistances.add(jump.getDistance());
if (pastSuccessfulJumpDistances.size() <= 2) {
List<String> ordinals = Arrays.asList("1ST", "2ND", "3RD");
System.out.printf("AMAZING!!! NOT BAD FOR YOUR %s SUCCESSFUL JUMP!!!\n", ordinals.get(successfulJumpCt));
return;
}
int betterThanCount = successfulJumpCt - worseJumpCount;
if (betterThanCount <= 0.1 * successfulJumpCt) {
System.out.printf("WOW! THAT'S SOME JUMPING. OF THE %d SUCCESSFUL JUMPS\n", successfulJumpCt);
System.out.printf("BEFORE YOURS, ONLY %d OPENED THEIR CHUTES LOWER THAN\n", betterThanCount);
System.out.println("YOU DID.");
} else if (betterThanCount <= 0.25 * successfulJumpCt) {
System.out.printf("PRETTY GOOD! %d SUCCESSFUL JUMPS PRECEDED YOURS AND ONLY\n", successfulJumpCt);
System.out.printf("%d OF THEM GOT LOWER THAN YOU DID BEFORE THEIR CHUTES\n", betterThanCount);
System.out.println("OPENED.");
} else if (betterThanCount <= 0.5 * successfulJumpCt) {
System.out.printf("NOT BAD. THERE HAVE BEEN %d SUCCESSFUL JUMPS BEFORE YOURS.\n", successfulJumpCt);
System.out.printf("YOU WERE BEATEN OUT BY %d OF THEM.\n", betterThanCount);
} else if (betterThanCount <= 0.75 * successfulJumpCt) {
System.out.printf("CONSERVATIVE, AREN'T YOU? YOU RANKED ONLY %d IN THE\n", betterThanCount);
System.out.printf("%d SUCCESSFUL JUMPS BEFORE YOURS.\n", successfulJumpCt);
} else if (betterThanCount <= -0.9 * successfulJumpCt) {
System.out.println("HUMPH! DON'T YOU HAVE ANY SPORTING BLOOD? THERE WERE");
System.out.printf("%d SUCCESSFUL JUMPS BEFORE YOURS AND YOU CAME IN %d JUMPS\n", successfulJumpCt, worseJumpCount);
System.out.println("BETTER THAN THE WORST. SHAPE UP!!!\n");
} else {
System.out.printf("HEY! YOU PULLED THE RIP CORD MUCH TOO SOON. %d SUCCESSFUL\n", successfulJumpCt);
System.out.printf("JUMPS BEFORE YOURS AND YOU CAME IN NUMBER %d! GET WITH IT!\n", betterThanCount);
}
}
private void showSplatMessage(InitialJumpConditions initial, JumpResult jump) {
double timeOfSplat = computeTimeOfSplat(initial, jump);
System.out.printf("%10.2f SPLAT\n", timeOfSplat);
}
/**
* Returns the number of jumps for which this jump was better
*/
private double computeTimeOfSplat(InitialJumpConditions initial, JumpResult jump) {
final float V = initial.getTerminalVelocity();
final float A = initial.getAcceleration();
if (jump.hasReachedTerminalVelocity()) {
return (V / A) + ((initial.getAltitude() - (V * V / (2 * A))) / V);
}
return Math.sqrt(2 * initial.getAltitude() / A);
}
private int countWorseHistoricalJumps(JumpResult jump) {
return (int) pastSuccessfulJumpDistances.stream()
.filter(distance -> jump.getDistance() < distance)
.count();
}
private void showCleverSplatMessage() {
List<String> messages = Arrays.asList(
"REQUIESCAT IN PACE.",
"MAY THE ANGEL OF HEAVEN LEAD YOU INTO PARADISE.",
"REST IN PEACE.",
"SON-OF-A-GUN.",
"#$%&&%!$",
"A KICK IN THE PANTS IS A BOOST IF YOU'RE HEADED RIGHT.",
"HMMM. SHOULD HAVE PICKED A SHORTER TIME.",
"MUTTER. MUTTER. MUTTER.",
"PUSHING UP DAISIES.",
"EASY COME, EASY GO."
);
System.out.println(messages.get(random.nextInt(10)));
}
private boolean playAgain() {
if (askYesNo("DO YOU WANT TO PLAY AGAIN ")) {
return true;
}
return askYesNo("PLEASE");
}
private float mphToFeetPerSec(float speed) {
return speed * (5280.0f / 3600.0f);
}
private boolean askYesNo(String prompt) {
System.out.printf("%s (YES OR NO) ", prompt);
while (true) {
String answer = scanner.next();
switch (answer) {
case "YES":
return true;
case "NO":
return false;
default:
System.out.print("YES OR NO ");
}
}
}
private float chooseRandomAcceleration() {
Planet planet = Planet.pickRandom();
System.out.printf("%s %s. ACCELERATION=%.2f FT/SEC/SEC.\n", planet.getMessage(), planet.name(), planet.getAcceleration());
return planet.getAcceleration();
}
enum Planet {
MERCURY("FINE. YOU'RE ON", 12.2f),
VENUS("ALL RIGHT. YOU'RE ON", 28.3f),
EARTH("THEN YOU'RE ON", 32.16f),
MOON("FINE. YOU'RE ON THE", 5.15f),
MARS("ALL RIGHT. YOU'RE ON", 12.5f),
JUPITER("THEN YOU'RE ON", 85.2f),
SATURN("FINE. YOU'RE ON", 37.6f),
URANUS("ALL RIGHT. YOU'RE ON", 33.8f),
NEPTUNE("THEN YOU'RE ON", 39.6f),
SUN("FINE. YOU'RE ON THE", 896.0f);
private static final Random random = new Random();
private final String message;
private final float acceleration;
Planet(String message, float acceleration) {
this.message = message;
this.acceleration = acceleration;
}
static Planet pickRandom() {
return values()[random.nextInt(Planet.values().length)];
}
String getMessage() {
return message;
}
float getAcceleration() {
return acceleration;
}
}
// Mutable
static class JumpResult {
private boolean reachedTerminalVelocity = false;
private float distance; // from the ground
public JumpResult(float distance) {
this.distance = distance;
}
boolean isSplat() {
return distance <= 0;
}
boolean hasReachedTerminalVelocity() {
return reachedTerminalVelocity;
}
float getDistance() {
return distance;
}
void setDistance(float distance) {
this.distance = distance;
}
void setReachedTerminalVelocity() {
reachedTerminalVelocity = true;
}
}
// Immutable
static class InitialJumpConditions {
private final float originalTerminalVelocity;
private final float originalAcceleration;
private final float terminalVelocity;
private final float acceleration;
private final int altitude;
private InitialJumpConditions(float originalTerminalVelocity, float originalAcceleration,
float terminalVelocity, float acceleration, int altitude) {
this.originalTerminalVelocity = originalTerminalVelocity;
this.originalAcceleration = originalAcceleration;
this.terminalVelocity = terminalVelocity;
this.acceleration = acceleration;
this.altitude = altitude;
}
// Create initial jump conditions with adjusted velocity/acceleration and a random initial altitude
private static InitialJumpConditions create(float terminalVelocity, float gravitationalAcceleration) {
final int altitude = (int) (9001.0f * random.nextFloat() + 1000);
return new InitialJumpConditions(terminalVelocity, gravitationalAcceleration,
plusMinus5Percent(terminalVelocity), plusMinus5Percent(gravitationalAcceleration), altitude);
}
private static float plusMinus5Percent(float value) {
return value + ((value * random.nextFloat()) / 20.0f) - ((value * random.nextFloat()) / 20.0f);
}
float getOriginalTerminalVelocity() {
return originalTerminalVelocity;
}
float getOriginalAcceleration() {
return originalAcceleration;
}
float getTerminalVelocity() {
return terminalVelocity;
}
float getAcceleration() {
return acceleration;
}
int getAltitude() {
return altitude;
}
float getTimeOfTerminalAccelerationReached() {
return terminalVelocity / acceleration;
}
}
}

View File

@@ -101,4 +101,4 @@ Instructions in this directory at
instructions.txt instructions.txt
#### External Links #### External Links
- Super Star Trek in C++ : https://www.codeproject.com/Articles/28399/The-Object-Oriented-Text-Star-Trek-Game-in-C - C++: https://www.codeproject.com/Articles/28399/The-Object-Oriented-Text-Star-Trek-Game-in-C

View File

@@ -0,0 +1,3 @@
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
Conversion to [Kotlin](https://kotlinlang.org/)

View File

@@ -0,0 +1,73 @@
/**
* Game of Synonym
*
*
* Based on the Basic game of Synonym here
* https://github.com/coding-horror/basic-computer-games/blob/main/85%20Synonym/synonym.bas
*
*
* Note: The idea was to create a version of the 1970's Basic game in Java, without introducing
* new features - no additional text, error checking, etc has been added.
*/
fun main() {
println(introText)
synonyms.forEach {
it.testUser()
}
println("SYNONYM DRILL COMPLETED.")
}
// We could put this inside of SynonymList, but this keeps the core implementation
// right here at the top
private fun SynonymList.testUser() {
do {
val answer = ask(" WHAT IS A SYNONYM OF $word ? ")
when {
answer == "HELP" ->
println("""**** A SYNONYM OF $word IS ${synonyms.random()}.""")
synonyms.contains(answer) ->
println(AFFIRMATIONS.random())
else ->
println("TRY AGAIN.")
}
} while (!synonyms.contains(answer))
}
val introText = """
${tab(33)}SYNONYM
${tab(15)}CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
A SYNONYM OF A WORD MEANS ANOTHER WORD IN THE ENGLISH
LANGUAGE WHICH HAS THE SAME OR VERY NEARLY THE SAME
MEANING.
I CHOOSE A WORD -- YOU TYPE A SYNONYM.
IF YOU CAN'T THINK OF A SYNONYM, TYPE THE WORD 'HELP'
AND I WILL TELL YOU A SYNONYM.
"""
// prints a question and reads a string (and converts to uppercase)
private fun ask(text: String): String {
print(text)
return readln().uppercase()
}
// Just like TAB in BASIC
private fun tab(spaces: Int): String = " ".repeat(spaces)
val AFFIRMATIONS = arrayOf("RIGHT", "CORRECT", "FINE", "GOOD!", "CHECK")
// List of words and synonyms
private val synonyms = listOf(
SynonymList("FIRST", listOf("START", "BEGINNING", "ONSET", "INITIAL")),
SynonymList("SIMILAR", listOf("SAME", "LIKE", "RESEMBLING")),
SynonymList("MODEL", listOf("PATTERN", "PROTOTYPE", "STANDARD", "CRITERION")),
SynonymList("SMALL", listOf("INSIGNIFICANT", "LITTLE", "TINY", "MINUTE")),
SynonymList("STOP", listOf("HALT", "STAY", "ARREST", "CHECK", "STANDSTILL")),
SynonymList("HOUSE", listOf("DWELLING", "RESIDENCE", "DOMICILE", "LODGING", "HABITATION")),
SynonymList("PIT", listOf("HOLE", "HOLLOW", "WELL", "GULF", "CHASM", "ABYSS")),
SynonymList("PUSH", listOf("SHOVE", "THRUST", "PROD", "POKE", "BUTT", "PRESS")),
SynonymList("RED", listOf("ROUGE", "SCARLET", "CRIMSON", "FLAME", "RUBY")),
SynonymList("PAIN", listOf("SUFFERING", "HURT", "MISERY", "DISTRESS", "ACHE", "DISCOMFORT"))
)
class SynonymList(val word: String, val synonyms: List<String>)

View File

@@ -0,0 +1,94 @@
########################################################
#
# Synonym
#
# From Basic Computer Games (1978)
#
# A synonym of a word is another word (in the English language) which has the same,
# or very nearly the same, meaning. This program tests your knowledge of synonyms
# of a few common words.
#
# The computer chooses a word and asks you for a synonym. The computer then tells
# you whether youre right or wrong. If you cant think of a synonym, type “HELP”
# which causes a synonym to be printed.
# You may put in words of your choice in the data statements.
# The number following DATA in Statement 500 is the total number of data statements.
# In each data statement, the first number is the number of words in that statement.
#
# Can you think of a way to make this into a more general kind of CAI program for any subject?
# Walt Koetke of Lexington High School, Massachusetts created this program.
#
#
########################################################
puts <<~INSTRUCTIONS
SYNONYM
CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
A SYNONYM OF A WORD MEANS ANOTHER WORD IN THE ENGLISH
LANGUAGE WHICH HAS THE SAME OR VERY NEARLY THE SAME MEANING.
I CHOOSE A WORD -- YOU TYPE A SYNONYM.
IF YOU CAN'T THINK OF A SYNONYM, TYPE THE WORD 'HELP'
AND I WILL TELL YOU A SYNONYM.
INSTRUCTIONS
right_words = ["RIGHT", "CORRECT", "FINE", "GOOD!", "CHECK"]
synonym_words = [
["FIRST", "START", "BEGINNING", "ONSET", "INITIAL"],
["SIMILAR", "ALIKE", "SAME", "LIKE", "RESEMBLING"],
["MODEL", "PATTERN", "PROTOTYPE", "STANDARD", "CRITERION"],
["SMALL", "INSIGNIFICANT", "LITTLE", "TINY", "MINUTE"],
["STOP", "HALT", "STAY", "ARREST", "CHECK", "STANDSTILL"],
["HOUSE", "DWELLING", "RESIDENCE", "DOMICILE", "LODGING", "HABITATION"],
["PIT", "HOLE", "HOLLOW", "WELL", "GULF", "CHASM", "ABYSS"],
["PUSH", "SHOVE", "THRUST", "PROD", "POKE", "BUTT", "PRESS"],
["RED", "ROUGE", "SCARLET", "CRIMSON", "FLAME", "RUBY"],
["PAIN", "SUFFERING", "HURT", "MISERY", "DISTRESS", "ACHE", "DISCOMFORT"],
]
synonym_words.shuffle.each {|words_ar|
}
synonym_words.each {|words_ar|
answer = false
keyword = words_ar.shift
while not answer and words_ar.length != 0
puts " WHAT IS A SYNONYM OF #{keyword}? "
inp = gets.chomp.upcase
if inp == "HELP"
clue = words_ar.sample
puts "**** A SYNONYM OF #{keyword} IS #{clue}."
words_ar.delete(clue)
elsif words_ar.include? inp
puts right_words.sample
answer = true
else
puts "TRY AGAIN."
end
end
}
puts "SYNONYM DRILL COMPLETED"
######################################################################
#
# Porting notes
#
# There is a bug in the original program where if you keep asking for
# synoyms of a given word it ends up running out of synonyms
# in the array and the program crashes.
# The bug has been fixed in this version and now when
# it runs out of words it continues with the next
# array.
#
######################################################################

2
87_3-D_Plot/d/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*.exe
*.obj

182
87_3-D_Plot/d/README.md Normal file
View File

@@ -0,0 +1,182 @@
Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html)
Converted to [D](https://dlang.org/) by [Bastiaan Veelo](https://github.com/veelo).
## Running the code
Assuming the reference [dmd](https://dlang.org/download.html#dmd) compiler:
```shell
dmd -dip1000 -run threedeeplot.d
```
[Other compilers](https://dlang.org/download.html) also exist.
## On rounding floating point values to integer values
The D equivalent of Basic `INT` is [`floor`](https://dlang.org/phobos/std_math_rounding.html#.floor),
which rounds towards negative infinity. If you change occurrences of `floor` to
[`lrint`](https://dlang.org/phobos/std_math_rounding.html#.lrint), you'll see that the plots show a bit more detail,
as is done in the bonus below.
## Bonus: Self-writing programs
With a small modification to the source, the program can be extended to **plot a random function**, and **print its formula**.
```shell
rdmd -dip1000 threedeeplot_random.d
```
(`rdmd` caches the executable, which results in speedy execution when the source does not change.)
### Example output
```
3D Plot
(After Creative Computing Morristown, New Jersey)
f(z) = 30 * sin(z / 10.0)
*
* * * *
* * * * *
* * * * *
* * * * * *
* * * * * *
* * * * * **
* * * * * * *
* * * * * * **
* * * * * * * *
* * * * * * * *
* * * * * * * **
* * * * * * * * *
* * * * ** * * * *
* * * * ** * * *
* * * * * * * *
* * * * * * * *
* * * ** * * **
* * * ** * **
* * * * * **
* * * * * **
* * * * * **
* * * ** * **
* * * ** * * **
* * * * * * * *
* * * * * * * *
* * * * ** * * *
* * * * ** * * * *
* * * * * * * * *
* * * * * * * **
* * * * * * * *
* * * * * * * *
* * * * * * **
* * * * * * *
* * * * * **
* * * * * *
* * * * * *
* * * * *
* * * * *
* * * *
*
```
### Breakdown of differences
Have a look at the relevant differences between `threedeeplot.d` and `threedeeplot_random.d`.
This is the original function with the single expression that is evaluated for the plot:
```d
static float fna(float z)
{
return 30.0 * exp(-z * z / 100.0);
}
```
Here `static` means that the nested function does not need acces to its enclosing scope.
Now, by inserting the following:
```d
enum functions = ["30.0 * exp(-z * z / 100.0)",
"sqrt(900.01 - z * z) * .9 - 2",
"30 * (cos(z / 16.0) + .5)",
"30 - 30 * sin(z / 18.0)",
"30 * exp(-cos(z / 16.0)) - 30",
"30 * sin(z / 10.0)"];
size_t index = uniform(0, functions.length);
writeln(center("f(z) = " ~ functions[index], width), "\n");
```
and changing the implementation of `fna` to
```d
float fna(float z)
{
final switch (index)
{
static foreach (i, f; functions)
case i:
mixin("return " ~ f ~ ";");
}
}
```
we unlock some very special abilities of D. Let's break it down:
```d
enum functions = ["30.0 * exp(-z * z / 100.0)", /*...*/];
```
This defines an array of strings, each containing a mathematical expression. Due to the `enum` keyword, this is an
array that really only exists at compile-time.
```d
size_t index = uniform(0, functions.length);
```
This defines a random index into the array. `functions.length` is evaluated at compile-time, due to D's compile-time
function evaluation (CTFE).
```d
writeln(center("f(z) = " ~ functions[index], width), "\n");
```
Unmistakenly, this prints the formula centered on a line. What happens behind the scenes is that `functions` (which
only existed at compile-time before now) is pasted in, so that an instance of that array actually exists at run-time
at this spot, and is instantly indexed.
```d
float fna(float z)
{
final switch (index)
{
// ...
}
}
```
`static` has been dropped from the nested function because we want to evaluate `index` inside it. The function contains
an ordinary `switch`, with `final` providing some extra robustness. It disallows a `default` case and produces an error
when the switch doesn't handle all cases. The `switch` body is where the magic happens and consists of these three
lines:
```d
static foreach (i, f; functions)
case i:
mixin("return " ~ f ~ ";");
```
The `static foreach` iterates over `functions` at compile-time, producing one `case` for every element in `functions`.
`mixin` takes a string, which is constructed at compile-time, and pastes it right into the source.
In effect, the implementation of `float fna(float z)` unrolls itself into
```d
float fna(float z)
{
final switch (index)
{
case 0:
return 30.0 * exp(-z * z / 100.0);
case 1:
return sqrt(900.01 - z * z) * .9 - 2;
case 2:
return 30 * (cos(z / 16.0) + .5);
case 3:
return 30 - 30 * sin(z / 18.0);
case 4:
return 30 * exp(-cos(z / 16.0)) - 30;
case 5:
return 30 * sin(z / 10.0)";
}
}
```
So if you feel like adding another function, all you need to do is append it to the `functions` array, and the rest of
the program *rewrites itself...*

View File

@@ -0,0 +1,35 @@
@safe: // Make @safe the default for this file, enforcing memory-safety.
import std.stdio, std.string, std.math, std.range, std.conv, std.algorithm;
void main()
{
enum width = 80;
writeln(center("3D Plot", width));
writeln(center("(After Creative Computing Morristown, New Jersey)\n\n\n", width));
static float fna(float z)
{
return 30.0 * exp(-z * z / 100.0);
}
char[] row;
for (float x = -30.0; x <= 30.0; x += 1.5)
{
size_t max_z = 0L;
auto y1 = 5 * floor((sqrt(900 - x * x)) / 5.0);
for (float y = y1; y >= -y1; y -= 5)
{
auto z = to!size_t(max(0, floor(25 + fna(sqrt(x * x + y * y)) - .7 * y)));
if (z > max_z) // Visible
{
max_z = z;
if (z + 1 > row.length) // row needs to grow
row ~= ' '.repeat(z + 1 - row.length).array;
row[z] = '*';
}
}
writeln(row);
row = null;
}
}

View File

@@ -0,0 +1,50 @@
@safe: // Make @safe the default for this file, enforcing memory-safety.
import std.stdio, std.string, std.math, std.range, std.conv, std.random, std.algorithm;
void main()
{
enum width = 80;
writeln(center("3D Plot", width));
writeln(center("(After Creative Computing Morristown, New Jersey)\n\n", width));
enum functions = ["30.0 * exp(-z * z / 100.0)",
"sqrt(900.01 - z * z) * .9 - 2",
"30 * (cos(z / 16.0) + .5)",
"30 - 30 * sin(z / 18.0)",
"30 * exp(-cos(z / 16.0)) - 30",
"30 * sin(z / 10.0)"];
size_t index = uniform(0, functions.length);
writeln(center("f(z) = " ~ functions[index], width), "\n");
float fna(float z)
{
final switch (index)
{
static foreach (i, f; functions)
case i:
mixin("return " ~ f ~ ";");
}
}
char[] row;
for (float x = -30.0; x <= 30.0; x += 1.5)
{
size_t max_z = 0L;
auto y1 = 5 * lrint((sqrt(900 - x * x)) / 5.0);
for (float y = y1; y >= -y1; y -= 5)
{
auto z = to!size_t(max(0, lrint(25 + fna(sqrt(x * x + y * y)) - .7 * y)));
if (z > max_z) // Visible
{
max_z = z;
if (z + 1 > row.length) // row needs to grow
row ~= ' '.repeat(z + 1 - row.length).array;
row[z] = '*';
}
}
writeln(row);
row = null;
}
}

View File

@@ -1,60 +1,95 @@
// WAR // WAR
// //
// Converted from BASIC to Javascript by Oscar Toledo G. (nanochess) // Original conversion from BASIC to Javascript by Oscar Toledo G. (nanochess)
// //
function print(str) function print(str) {
{
document.getElementById("output").appendChild(document.createTextNode(str)); document.getElementById("output").appendChild(document.createTextNode(str));
} }
function input() function tab(space) {
{ let str = "";
var input_element; while (space-- > 0) {
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 += " "; str += " ";
}
return str; return str;
} }
var a = [, "S-2","H-2","C-2","D-2","S-3","H-3","C-3","D-3", function input() {
"S-4","H-4","C-4","D-4","S-5","H-5","C-5","D-5", return new Promise(function (resolve) {
"S-6","H-6","C-6","D-6","S-7","H-7","C-7","D-7", const input_element = document.createElement("INPUT");
"S-8","H-8","C-8","D-8","S-9","H-9","C-9","D-9",
"S-10","H-10","C-10","D-10","S-J","H-J","C-J","D-J",
"S-Q","H-Q","C-Q","D-Q","S-K","H-K","C-K","D-K",
"S-A","H-A","C-A","D-A"];
var l = []; print("? ");
input_element.setAttribute("type", "text");
input_element.setAttribute("length", "50");
document.getElementById("output").appendChild(input_element);
input_element.focus();
input_element.addEventListener("keydown", function (event) {
if (event.keyCode == 13) {
const input_str = input_element.value;
document.getElementById("output").removeChild(input_element);
print(input_str);
print("\n");
resolve(input_str);
}
});
});
}
// Main control section async function askYesOrNo(question) {
async function main() while (1) {
{ print(question);
const str = await input();
if (str == "YES") {
return true;
}
else if (str == "NO") {
return false;
}
else {
print("YES OR NO, PLEASE. ");
}
}
}
async function askAboutInstructions() {
const playerWantsInstructions = await askYesOrNo("DO YOU WANT DIRECTIONS");
if (playerWantsInstructions) {
print("THE COMPUTER GIVES YOU AND IT A 'CARD'. THE HIGHER CARD\n");
print("(NUMERICALLY) WINS. THE GAME ENDS WHEN YOU CHOOSE NOT TO\n");
print("CONTINUE OR WHEN YOU HAVE FINISHED THE PACK.\n");
}
print("\n");
print("\n");
}
function createGameDeck(cards, gameSize) {
const deck = [];
const deckSize = cards.length;
for (let j = 0; j < gameSize; j++) {
let card;
// Compute a new card index until we find one that isn't already in the new deck
do {
card = Math.floor(deckSize * Math.random());
} while (deck.includes(card));
deck.push(card);
}
return deck;
}
function computeCardValue(cardIndex) {
return Math.floor(cardIndex / 4);
}
function printGameOver(playerScore, computerScore) {
print("\n");
print("\n");
print(`WE HAVE RUN OUT OF CARDS. FINAL SCORE: YOU: ${playerScore} THE COMPUTER: ${computerScore}\n`);
print("\n");
}
function printTitle() {
print(tab(33) + "WAR\n"); print(tab(33) + "WAR\n");
print(tab(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n"); print(tab(15) + "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n");
print("\n"); print("\n");
@@ -62,72 +97,65 @@ async function main()
print("\n"); print("\n");
print("THIS IS THE CARD GAME OF WAR. EACH CARD IS GIVEN BY SUIT-#\n"); print("THIS IS THE CARD GAME OF WAR. EACH CARD IS GIVEN BY SUIT-#\n");
print("AS S-7 FOR SPADE 7. "); print("AS S-7 FOR SPADE 7. ");
while (1) { }
print("DO YOU WANT DIRECTIONS");
str = await input(); function printCards(playerCard, computerCard) {
if (str == "YES") {
print("THE COMPUTER GIVES YOU AND IT A 'CARD'. THE HIGHER CARD\n");
print("(NUMERICALLY) WINS. THE GAME ENDS WHEN YOU CHOOSE NOT TO\n");
print("CONTINUE OR WHEN YOU HAVE FINISHED THE PACK.\n");
break;
}
if (str == "NO")
break;
print("YES OR NO, PLEASE. ");
}
print("\n");
print("\n"); print("\n");
print(`YOU: ${playerCard}\tCOMPUTER: ${computerCard}\n`);
}
const cards = [
"S-2", "H-2", "C-2", "D-2",
"S-3", "H-3", "C-3", "D-3",
"S-4", "H-4", "C-4", "D-4",
"S-5", "H-5", "C-5", "D-5",
"S-6", "H-6", "C-6", "D-6",
"S-7", "H-7", "C-7", "D-7",
"S-8", "H-8", "C-8", "D-8",
"S-9", "H-9", "C-9", "D-9",
"S-10", "H-10", "C-10", "D-10",
"S-J", "H-J", "C-J", "D-J",
"S-Q", "H-Q", "C-Q", "D-Q",
"S-K", "H-K", "C-K", "D-K",
"S-A", "H-A", "C-A", "D-A"
];
// Main control section
async function main() {
printTitle();
await askAboutInstructions();
a1 = 0; let computerScore = 0;
b1 = 0; let playerScore = 0;
p = 0;
// Generate a random deck // Generate a random deck
for (j = 1; j <= 52; j++) { const gameSize = cards.length; // Number of cards to shuffle into the game deck. Can be <= cards.length.
do { const deck = createGameDeck(cards, gameSize);
l[j] = Math.floor(52 * Math.random()) + 1; let shouldContinuePlaying = true;
for (k = 1; k < j; k++) {
if (l[k] == l[j]) // Already in deck?
break;
}
} while (j != 1 && k < j) ;
}
l[j] = 0; // Mark the end of the deck
while (1) { while (deck.length > 0 && shouldContinuePlaying) {
m1 = l[++p]; // Take a card const playerCard = deck.shift(); // Take a card
m2 = l[++p]; // Take a card const computerCard = deck.shift(); // Take a card
print("\n"); printCards(cards[playerCard], cards[computerCard]);
print("YOU: " + a[m1] + "\tCOMPUTER: " + a[m2] + "\n");
n1 = Math.floor((m1 - 0.5) / 4); const playerCardValue = computeCardValue(playerCard);
n2 = Math.floor((m2 - 0.5) / 4); const computerCardValue = computeCardValue(computerCard);
if (n1 < n2) { if (playerCardValue < computerCardValue) {
a1++; computerScore++;
print("THE COMPUTER WINS!!! YOU HAVE " + b1 + " AND THE COMPUTER HAS " + a1 + "\n"); print("THE COMPUTER WINS!!! YOU HAVE " + playerScore + " AND THE COMPUTER HAS " + computerScore + "\n");
} else if (n1 > n2) { } else if (playerCardValue > computerCardValue) {
b1++; playerScore++;
print("YOU WIN. YOU HAVE " + b1 + " AND THE COMPUTER HAS " + a1 + "\n"); print("YOU WIN. YOU HAVE " + playerScore + " AND THE COMPUTER HAS " + computerScore + "\n");
} else { } else {
print("TIE. NO SCORE CHANGE.\n"); print("TIE. NO SCORE CHANGE.\n");
} }
if (l[p + 1] == 0) {
print("\n"); if (deck.length === 0) {
print("\n"); printGameOver(playerScore, computerScore);
print("WE HAVE RUN OUT OF CARDS. FINAL SCORE: YOU: " + b1 + " THE COMPUTER: " + a1 + "\n");
print("\n");
break;
} }
while (1) { else {
print("DO YOU WANT TO CONTINUE"); shouldContinuePlaying = await askYesOrNo("DO YOU WANT TO CONTINUE");
str = await input();
if (str == "YES")
break;
if (str == "NO")
break;
print("YES OR NO, PLEASE. ");
} }
if (str == "NO")
break;
} }
print("THANKS FOR PLAYING. IT WAS FUN.\n"); print("THANKS FOR PLAYING. IT WAS FUN.\n");
print("\n"); print("\n");

2
96_Word/d/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*.exe
*.obj

15
96_Word/d/README.md Normal file
View File

@@ -0,0 +1,15 @@
Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html)
Converted to [D](https://dlang.org/) by [Bastiaan Veelo](https://github.com/veelo).
The Basic original required words to be exactly five letters in length for the program to behave correctly.
This version does not replicate that limitation, and the test for that requirement is commented out.
## Running the code
Assuming the reference [dmd](https://dlang.org/download.html#dmd) compiler:
```shell
dmd -dip1000 -run word.d
```
[Other compilers](https://dlang.org/download.html) also exist.

85
96_Word/d/word.d Normal file
View File

@@ -0,0 +1,85 @@
@safe: // Make @safe the default for this file, enforcing memory-safety.
import std;
void main()
{
enum width = 80;
writeln(center("Word", width));
writeln(center("(After Creative Computing Morristown, New Jersey)\n\n\n", width));
writeln(wrap("I am thinking of a word -- you guess it. I will give you " ~
"clues to help you get it. Good luck!!\n\n", width));
string[] words = ["dinky", "smoke", "water", "grass", "train", "might", "first",
"candy", "champ", "would", "clump", "dopey"];
playLoop: while (true)
{
writeln("\n\nYou are starting a new game...");
string word = words[uniform(0, $-1)]; // $ is a short-hand for words.length.
int guesses = 0;
string knownLetters = '-'.repeat(word.length).array;
while (true)
{
writeln("Guess a ", word.length, " letter word");
string guess = readString.toLower;
if (guess == "?")
{
writeln("The secret word is ", word, "\n");
continue playLoop; // Start a new game.
}
/* Uncomment this for equivalence with Basic.
if (guess.length != 5)
{
writeln("You must guess a 5 letter word. Start again.");
continue; // Ask for new guess.
}
*/
guesses++;
if (guess == word)
break; // Done guessing
string commonLetters;
foreach (i, wordLetter; word)
foreach (j, guessLetter; guess)
if (guessLetter == wordLetter)
{
commonLetters ~= guessLetter;
if (i == j)
knownLetters.replaceInPlace(i, i + 1, [guessLetter]);
}
writeln("There were ", commonLetters.length, " matches and the common letters were... ", commonLetters);
writeln("From the exact letter matches, you know................ ", knownLetters);
if (knownLetters == word)
break; // Done guessing
if (commonLetters.length < 2)
writeln("If you give up, type '?' for your next guess.");
writeln;
}
writeln("You have guessed the word. It took ", guesses, " guesses!");
write("\n\nWant to play again? ");
if (readString.toLower != "yes")
break; // Terminate playLoop
}
}
/// Read a string from standard input, stripping newline and other enclosing whitespace.
string readString() nothrow
{
try
return trustedReadln.strip;
catch (Exception) // readln throws on I/O and Unicode errors, which we handle here.
return "";
}
/** An @trusted wrapper around readln.
*
* This is the only function that formally requires manual review for memory-safety.
* [Arguably readln should be safe already](https://forum.dlang.org/post/rab398$1up$1@digitalmars.com)
* which would remove the need to have any @trusted code in this program.
*/
string trustedReadln() @trusted
{
return readln;
}

85
HOW_TO_RUN_THE_GAMES.md Normal file
View File

@@ -0,0 +1,85 @@
# How to run the games
The games in this repository have been translated into a number of different languages. How to run them depends on the target language.
## csharp
### dotnet command-line
The best cross-platform method for running the csharp examples is with the `dotnet` command-line tool. This can be downloaded for **MacOS**, **Windows** and **Linux** from [dotnet.microsoft.com](https://dotnet.microsoft.com/).
From there, the program can be run by
1. Opening a terminal window
1. Navigating to the corresponding directory
1. Starting with `dotnet run`
### Visual Studio
Alternatively, for non-dotnet compatible translations, you will need [Visual Studio](https://visualstudio.microsoft.com/vs/community/) which can be used to both open the project and run the example.
1. Open the corresponding `.csproj` or `.sln` file
1. Click `Run` from within the Visual Studio IDE
## java
The Java translations can be run via the command line or from an IDE such as [Eclipse](https://www.eclipse.org/downloads/packages/release/kepler/sr1/eclipse-ide-java-developers) or [IntelliJ](https://www.jetbrains.com/idea/)
To run from the command line, you will need a Java SDK (eg. [Oracle JDK](https://www.oracle.com/java/technologies/downloads/) or [Open JDK](https://openjdk.java.net/)).
1. Navigate to the corresponding directory.
1. Compile the program with `javac`:
* eg. `javac AceyDuceyGame.java`
1. Run the compiled program with `java`:
* eg. `java AceyDuceyGame`
## javascript
The javascript examples can be run from within your web browser:
1. Simply open the corresponding `.html` file from your web browser.
## pascal
The pascal examples can be run using [Free Pascal](https://www.freepascal.org/). Additionally, `.lsi` project files can be opened with the [Lazarus Project IDE](https://www.lazarus-ide.org/).
The pascal examples include both *simple* (single-file) and *object-oriented* (in the `/object-pascal`directories) examples.
1. You can compile the program from the command line with the `fpc` command.
* eg. `fpc amazing.pas`
1. The output is an executable file that can be run directly.
## perl
The perl translations can be run using a perl interpreter (a copy can be downloaded from [perl.org](https://www.perl.org/)) if not already installed.
1. From the command-line, navigate to the corresponding directory.
1. Invoke with the `perl` command.
* eg. `perl aceyducey.pl`
## python
The python translations can be run from the command line by using the `py` interpreter. If not already installed, a copy can be downloaded from [python.org](https://www.python.org/downloads/) for **Windows**, **MacOS** and **Linux**.
1. From the command-line, navigate to the corresponding directory.
1. Invoke with the `py` or `python` interpreter (depending on your python version).
* eg. `py acey_ducey_oo.py`
* eg. `python aceyducey.py`
**Note**
Some translations include multiple versions for python, such as `acey ducey` which features versions for Python 2 (`aceyducey.py`) and Python 3 (`acey_ducey.py`) as well as an extra object-oriented version (`acey_ducey_oo.py`).
You can manage and use different versions of python with [pip](https://pypi.org/project/pip/).
## ruby
If you don't already have a ruby interpreter, you can download it from the [ruby project site](https://www.ruby-lang.org/en/).
1. From the command-line, navigate to the corresponding directory.
1. Invoke with the `ruby` tool.
* eg. `ruby aceyducey.rb`
## vbnet
Follow the same steps as for the [csharp](#csharp) translations. This can be run with `dotnet` or `Visual Studio`.