mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2026-07-08 05:17:13 -07:00
Merge branch 'coding-horror:main' into main
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
|
||||
|
||||
Conversion to [C++17](https://en.wikipedia.org/wiki/C%2B%2B17)
|
||||
@@ -0,0 +1,21 @@
|
||||
#include <iostream> // std::cout, std::endl
|
||||
#include <string> // std::string(size_t n, char c)
|
||||
#include <cmath> // std::sin(double x)
|
||||
|
||||
int main()
|
||||
{
|
||||
std::cout << std::string(30, ' ') << "SINE WAVE" << std::endl;
|
||||
std::cout << std::string(15, ' ') << "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY" << std::endl;
|
||||
std::cout << std::string(5, '\n');
|
||||
|
||||
bool b = true;
|
||||
|
||||
for (double t = 0.0; t <= 40.0; t += 0.25)
|
||||
{
|
||||
int a = int(26 + 25 * std::sin(t));
|
||||
std::cout << std::string(a, ' ') << (b ? "CREATIVE" : "COMPUTING") << std::endl;
|
||||
b = !b;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,181 @@
|
||||
--
|
||||
-- Animal.lua
|
||||
--
|
||||
|
||||
-- maintain a flat table/list of all the known animals
|
||||
local animals = {
|
||||
"FISH",
|
||||
"BIRD"
|
||||
}
|
||||
|
||||
-- store the questions as a binary tree with each node having a
|
||||
-- "y" or "n" branch
|
||||
-- if the node has a member named "a" it's an answer, with an index
|
||||
-- into the animals list.
|
||||
-- Otherwise, it's a question that leads to more nodes.
|
||||
local questionTree = {
|
||||
q = "DOES IT SWIM",
|
||||
y = { a = 1 },
|
||||
n = { a = 2 }
|
||||
}
|
||||
|
||||
-- print the given prompt string and then wait for input
|
||||
-- loops until a non-empty input is given
|
||||
-- returns the input as an upper-case string
|
||||
function askPrompt(promptString)
|
||||
local answered = false
|
||||
local a
|
||||
while (not answered) do
|
||||
print(promptString)
|
||||
a = io.read()
|
||||
a = string.upper(a)
|
||||
if (string.len(a) > 0) then
|
||||
answered = true
|
||||
end
|
||||
end
|
||||
return a
|
||||
end
|
||||
|
||||
-- print the given prompt string and then wait for the
|
||||
-- user to enter a string beginning with "Y" or "N"
|
||||
function askYesOrNo(promptString)
|
||||
local a
|
||||
while ((a ~= "Y") and (a ~= "N")) do
|
||||
a = askPrompt(promptString)
|
||||
a = a:sub(1,1)
|
||||
end
|
||||
return a
|
||||
end
|
||||
|
||||
-- prints the introductory text from the original BASIC program
|
||||
function printIntro()
|
||||
print(string.format("%32s", " ") .. "ANIMAL")
|
||||
print(string.format("%15s", " ") .. "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY")
|
||||
print()
|
||||
print()
|
||||
print()
|
||||
print("PLAY 'GUESS THE ANIMAL'")
|
||||
print("THINK OF AN ANIMAL AND THE COMPUTER WILL TRY TO GUESS IT.")
|
||||
print()
|
||||
end
|
||||
|
||||
-- prints the animals known in the source list
|
||||
function listKnownAnimals()
|
||||
print()
|
||||
print("ANIMALS I ALREADY KNOW ARE:")
|
||||
|
||||
local x
|
||||
local item
|
||||
for x = 1,#animals do
|
||||
-- use string.format to space each animal in a 12-character-wide "cell"
|
||||
item = string.format("%-12s", animals[x])
|
||||
|
||||
-- io.write() works like print(), but doesn't automatically add a carriage return/newline
|
||||
io.write(item)
|
||||
|
||||
-- every fifth item, start a new line
|
||||
if ((x % 5) == 0) then
|
||||
io.write("\n")
|
||||
end
|
||||
end
|
||||
|
||||
print()
|
||||
print()
|
||||
end
|
||||
|
||||
-- Prompts the user for info about the animal they were thinking of, then
|
||||
-- uses that to add a new branch to the tree
|
||||
-- curNode: the node in the tree where the computer made a wrong guess
|
||||
-- branch: the answer the user gave to curNode's question
|
||||
function addAnimalToTree(curNode, branch)
|
||||
local newAnimal
|
||||
local curResponse = curNode[branch]
|
||||
local guessedIndex = curResponse.a
|
||||
local guessedAnimal = animals[guessedIndex]
|
||||
local newQuestion, newAnswer, newIndex
|
||||
local newNode
|
||||
|
||||
newAnimal = askPrompt("THE ANIMAL YOU WERE THINKING OF WAS A ?")
|
||||
newQuestion = askPrompt("PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A "..
|
||||
tostring(newAnimal).." FROM A "..tostring(guessedAnimal))
|
||||
newAnswer = askYesOrNo("FOR A "..tostring(newAnimal).." THE ANSWER WOULD BE?")
|
||||
|
||||
-- add the new animal to the master list at the end, and
|
||||
-- save off its index in the list
|
||||
table.insert(animals, newAnimal)
|
||||
newIndex = #animals
|
||||
|
||||
-- create a new node for the question we just learned
|
||||
newNode = {}
|
||||
newNode.q = newQuestion
|
||||
if (newAnswer == "Y") then
|
||||
newNode.y = { a = newIndex }
|
||||
newNode.n = { a = guessedIndex }
|
||||
else
|
||||
newNode.y = { a = guessedIndex }
|
||||
newNode.n = { a = newIndex }
|
||||
end
|
||||
|
||||
-- replace the previous answer with our new node
|
||||
curNode[branch] = newNode
|
||||
end
|
||||
|
||||
-- Starts at the root of the question tree and asks questions about
|
||||
-- the user's animal until the computer hits an "a" answer node and tries
|
||||
-- to make a guess
|
||||
function askAboutAnimal()
|
||||
local curNode = questionTree
|
||||
local finished = false
|
||||
local response, responseIndex
|
||||
local nextNode, animalName
|
||||
while (not finished) do
|
||||
response = askYesOrNo(curNode.q .. "?")
|
||||
|
||||
-- convert the response "Y" or "N" to the lowercase "y" or "n" that we use to name our branches
|
||||
branch = string.lower(response)
|
||||
nextNode = curNode[branch]
|
||||
|
||||
-- is the next node an answer node, or another question?
|
||||
if (nextNode.a ~= nil) then
|
||||
-- it's an answer, so make a guess
|
||||
animalName = animals[nextNode.a]
|
||||
response = askYesOrNo("IS IT A "..tostring(animalName).."?")
|
||||
if (response == "Y") then
|
||||
-- we got the correct answer, so prompt for a new animal
|
||||
print()
|
||||
print("WHY NOT TRY ANOTHER ANIMAL?")
|
||||
else
|
||||
-- incorrect answer, so add a new entry at this point in the tree
|
||||
addAnimalToTree(curNode, branch)
|
||||
end
|
||||
|
||||
-- whether we were right or wrong, we're finished with this round
|
||||
finished = true
|
||||
else
|
||||
-- it's another question, so advance down the tree
|
||||
curNode = nextNode
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- MAIN CONTROL SECTION
|
||||
|
||||
printIntro()
|
||||
|
||||
-- loop forever until the player requests an exit by entering a blank line
|
||||
local exitRequested = false
|
||||
local answer
|
||||
|
||||
while (not exitRequested) do
|
||||
print("ARE YOU THINKING OF AN ANIMAL?")
|
||||
answer = io.read()
|
||||
answer = string.upper(answer)
|
||||
|
||||
if (string.len(answer) == 0) then
|
||||
exitRequested = true
|
||||
elseif (answer:sub(1,4) == "LIST") then
|
||||
listKnownAnimals()
|
||||
elseif (answer:sub(1,1) == "Y") then
|
||||
askAboutAnimal()
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,150 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
# Banner program in Perl
|
||||
# Translated by Kevin Brannen (kbrannen)
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
sub print_lines
|
||||
{
|
||||
my $lines = shift;
|
||||
print "\n" x $lines;
|
||||
}
|
||||
|
||||
# each letter is made of 7 slices (or rows);
|
||||
# the initial & unused 0 is to allow for Perl arrays being 0-based
|
||||
# but allow the algorithm to be 1-based (like Basic arrays);
|
||||
# the numbers are essentially the dots/columns per slice in powers of 2.
|
||||
my %data = (
|
||||
" " => [ 0, 0, 0, 0, 0, 0, 0, 0 ],
|
||||
"." => [ 0, 1, 1, 129, 449, 129, 1, 1 ],
|
||||
"!" => [ 0, 1, 1, 1, 384, 1, 1, 1 ],
|
||||
"=" => [ 0, 41, 41, 41, 41, 41, 41, 41 ],
|
||||
"?" => [ 0, 5, 3, 2, 354, 18, 11, 5 ],
|
||||
"*" => [ 0, 69, 41, 17, 512, 17, 41, 69 ],
|
||||
"0" => [ 0, 57, 69, 131, 258, 131, 69, 57 ],
|
||||
"1" => [ 0, 0, 0, 261, 259, 512, 257, 257 ],
|
||||
"2" => [ 0, 261, 387, 322, 290, 274, 267, 261 ],
|
||||
"3" => [ 0, 66, 130, 258, 274, 266, 150, 100 ],
|
||||
"4" => [ 0, 33, 49, 41, 37, 35, 512, 33 ],
|
||||
"5" => [ 0, 160, 274, 274, 274, 274, 274, 226 ],
|
||||
"6" => [ 0, 194, 291, 293, 297, 305, 289, 193 ],
|
||||
"7" => [ 0, 258, 130, 66, 34, 18, 10, 8 ],
|
||||
"8" => [ 0, 69, 171, 274, 274, 274, 171, 69 ],
|
||||
"9" => [ 0, 263, 138, 74, 42, 26, 10, 7 ],
|
||||
"A" => [ 0, 505, 37, 35, 34, 35, 37, 505 ],
|
||||
"B" => [ 0, 512, 274, 274, 274, 274, 274, 239 ],
|
||||
"C" => [ 0, 125, 131, 258, 258, 258, 131, 69 ],
|
||||
"D" => [ 0, 512, 258, 258, 258, 258, 131, 125 ],
|
||||
"E" => [ 0, 512, 274, 274, 274, 274, 258, 258 ],
|
||||
"F" => [ 0, 512, 18, 18, 18, 18, 2, 2 ],
|
||||
"G" => [ 0, 125, 131, 258, 258, 290, 163, 101 ],
|
||||
"H" => [ 0, 512, 17, 17, 17, 17, 17, 512 ],
|
||||
"I" => [ 0, 258, 258, 258, 512, 258, 258, 258 ],
|
||||
"J" => [ 0, 65, 129, 257, 257, 257, 129, 128 ],
|
||||
"K" => [ 0, 512, 17, 17, 41, 69, 131, 258 ],
|
||||
"L" => [ 0, 512, 257, 257, 257, 257, 257, 257 ],
|
||||
"M" => [ 0, 512, 7, 13, 25, 13, 7, 512 ],
|
||||
"N" => [ 0, 512, 7, 9, 17, 33, 193, 512 ],
|
||||
"O" => [ 0, 125, 131, 258, 258, 258, 131, 125 ],
|
||||
"P" => [ 0, 512, 18, 18, 18, 18, 18, 15 ],
|
||||
"Q" => [ 0, 125, 131, 258, 258, 322, 131, 381 ],
|
||||
"R" => [ 0, 512, 18, 18, 50, 82, 146, 271 ],
|
||||
"S" => [ 0, 69, 139, 274, 274, 274, 163, 69 ],
|
||||
"T" => [ 0, 2, 2, 2, 512, 2, 2, 2 ],
|
||||
"U" => [ 0, 128, 129, 257, 257, 257, 129, 128 ],
|
||||
"V" => [ 0, 64, 65, 129, 257, 129, 65, 64 ],
|
||||
"W" => [ 0, 256, 257, 129, 65, 129, 257, 256 ],
|
||||
"X" => [ 0, 388, 69, 41, 17, 41, 69, 388 ],
|
||||
"Y" => [ 0, 8, 9, 17, 481, 17, 9, 8 ],
|
||||
"Z" => [ 0, 386, 322, 290, 274, 266, 262, 260 ],
|
||||
);
|
||||
|
||||
my ($horz, $vert, $center, $char, $msg) = (0, 0, '', '', '');
|
||||
|
||||
# get args to run with
|
||||
while ($horz < 1)
|
||||
{
|
||||
print "HORIZONTAL (1 or more): ";
|
||||
chomp($horz = <>);
|
||||
$horz = int($horz);
|
||||
}
|
||||
|
||||
while ($vert < 1)
|
||||
{
|
||||
print "VERTICAL (1 or more): ";
|
||||
chomp($vert = <>);
|
||||
$vert = int($vert);
|
||||
}
|
||||
|
||||
print "CENTERED (Y/N): ";
|
||||
chomp($center = <>);
|
||||
$center = ($center =~ m/^Y/i) ? 1 : 0;
|
||||
|
||||
# note you can enter multiple chars and the program will do the right thing
|
||||
# thanks to the length() calls below, which was in the original Basic
|
||||
print "CHARACTER TO PRINT (TYPE 'ALL' IF YOU WANT CHARACTER BEING PRINTED): ";
|
||||
chomp($char = uc(<>));
|
||||
|
||||
while (!$msg)
|
||||
{
|
||||
print "STATEMENT: ";
|
||||
chomp($msg = uc(<>));
|
||||
}
|
||||
|
||||
print "SET PAGE TO PRINT, HIT RETURN WHEN READY";
|
||||
$_ = <>;
|
||||
print_lines(2 * $horz);
|
||||
|
||||
# print the message
|
||||
for my $letter ( split(//, $msg) )
|
||||
{
|
||||
if (!exists($data{$letter}))
|
||||
{
|
||||
die "Cannot use letter '$letter'!";
|
||||
}
|
||||
my @s = @{$data{$letter}};
|
||||
#if ($letter eq " ") { print_lines(7 * $horz); next; }
|
||||
|
||||
my $print_letter = ($char eq "ALL") ? $letter : $char;
|
||||
for my $slice (1 .. 7)
|
||||
{
|
||||
my (@j, @f);
|
||||
for (my $k = 8; $k >= 0; $k--)
|
||||
{
|
||||
if (2**$k < $s[$slice])
|
||||
{
|
||||
$j[9 - $k] = 1;
|
||||
$s[$slice] = $s[$slice] - 2**$k;
|
||||
if ($s[$slice] == 1)
|
||||
{
|
||||
$f[$slice] = 9 - $k;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$j[9 - $k] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for my $t1 (1 .. $horz)
|
||||
{
|
||||
print " " x int((63 - 4.5 * $vert) * $center / (length($print_letter)) + 1);
|
||||
for my $b (1 .. (defined($f[$slice]) ? $f[$slice] : 0))
|
||||
{
|
||||
my $str = $j[$b] ? $print_letter : (" " x length($print_letter));
|
||||
print $str for (1 .. $vert);
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
}
|
||||
|
||||
# space between letters
|
||||
print_lines(2 * $horz);
|
||||
}
|
||||
|
||||
# while in the original code, this seems pretty excessive
|
||||
#print_lines(75);
|
||||
|
||||
exit(0);
|
||||
@@ -1,3 +1,21 @@
|
||||
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
|
||||
|
||||
Conversion to [Perl](https://www.perl.org/)
|
||||
|
||||
There are two version of the code here, a "faithful" translation (basketball-orig.pl) and
|
||||
a "modern" translation (basketball.pl). The main difference between the 2 are is that the
|
||||
faithful translation has 3 GOTOs in it while the modern version has no GOTO. I have added
|
||||
a "TIME" print when the score is shown so the Clock is visible. Halftime is at "50" and
|
||||
end of game is at 100 (per the Basic code).
|
||||
|
||||
The 3 GOTOs in the faitful version are because of the way the original code jumped into
|
||||
the "middle of logic" that has no obivious way to avoid ... that I can see, at least while
|
||||
still maintaining something of the look and structure of the original Basic.
|
||||
|
||||
The modern version avoided the GOTOs by restructuring the program in the 2 "play()" subs.
|
||||
Despite the change, this should play the same way as the faithful version.
|
||||
|
||||
All of the percentages remain the same. If writing this from scratch, we really should
|
||||
have only a single play() sub which uses the same code for both teams, which would also
|
||||
make the game more fair ... but that wasn't done so the percent edge to Darmouth has been
|
||||
maintained here.
|
||||
|
||||
Executable
+415
@@ -0,0 +1,415 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
# Basketball program in Perl
|
||||
# This is fairly faithful translation from the original Basic.
|
||||
# This becomes apparent because there are actually 3 GOTOs still present
|
||||
# because of the way the original code jumped into the "middle of logic"
|
||||
# that has no obivious way to avoid ... that I can see.
|
||||
# For better structure and no GOTOs, see the other version of this program.
|
||||
# Translated by Kevin Brannen (kbrannen)
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# globals
|
||||
my $Defense=0; # dartmouth defense value
|
||||
my $Opponent; # name of opponent
|
||||
my @Score = (0, 0); # scores, dart is [0], opponent is [1]
|
||||
my $Player = 0; # player, 0 = dart, 1 = opp
|
||||
my $Timer = 0; # time tick, 100 ticks per game, 50 is end of first half, if tie at end then back to T=93
|
||||
my $DoPlay = 1; # true if game is still being played
|
||||
my $ConTeam; # controlling team, "dart" or "opp"
|
||||
my $ShotType = 0; # current shot type
|
||||
|
||||
|
||||
print "\n";
|
||||
print " " x 31, "BASKETBALL";
|
||||
print " " x 15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY";
|
||||
print "\n\n\n";
|
||||
|
||||
print "THIS IS DARTMOUTH COLLEGE BASKETBALL. YOU WILL BE DARTMOUTH\n";
|
||||
print "CAPTAIN AND PLAYMAKER. CALL SHOTS AS FOLLOWS:\n";
|
||||
print " 1. LONG (30 FT.) JUMP SHOT;\n";
|
||||
print " 2. SHORT (15 FT.) JUMP SHOT;\n";
|
||||
print " 3. LAY UP;\n";
|
||||
print " 4. SET SHOT.\n";
|
||||
print "BOTH TEAMS WILL USE THE SAME DEFENSE. CALL DEFENSE AS FOLLOWS:\n";
|
||||
print " 6. PRESS;\n";
|
||||
print " 6.5 MAN-TO MAN;\n";
|
||||
print " 7. ZONE;\n";
|
||||
print " 7.5 NONE.\n";
|
||||
print "TO CHANGE DEFENSE, JUST TYPE 0 AS YOUR NEXT SHOT.\n\n";
|
||||
get_defense();
|
||||
print "\n";
|
||||
print "CHOOSE YOUR OPPONENT: ";
|
||||
chomp($Opponent = <>);
|
||||
|
||||
$ConTeam = center_jump();
|
||||
while ($DoPlay)
|
||||
{
|
||||
print "\n";
|
||||
if ($ConTeam eq "dart")
|
||||
{
|
||||
$Player = 0;
|
||||
get_your_shot();
|
||||
dartmouth_play();
|
||||
}
|
||||
else
|
||||
{
|
||||
opponent_play();
|
||||
}
|
||||
if ($Timer >= 100)
|
||||
{
|
||||
check_end_game();
|
||||
last if (!$DoPlay);
|
||||
$Timer = 93;
|
||||
$ConTeam = center_jump();
|
||||
}
|
||||
}
|
||||
exit(0);
|
||||
|
||||
###############################################################
|
||||
|
||||
sub dartmouth_play
|
||||
{
|
||||
if ($ShotType == 1 || $ShotType == 2)
|
||||
{
|
||||
$Timer++;
|
||||
if ($Timer == 50)
|
||||
{
|
||||
end_first_half();
|
||||
return;
|
||||
}
|
||||
if ($Timer == 92)
|
||||
{
|
||||
two_min_left();
|
||||
}
|
||||
|
||||
print "JUMP SHOT\n";
|
||||
if (rand(1) <= 0.341 * $Defense / 8)
|
||||
{
|
||||
print "SHOT IS GOOD.\n";
|
||||
dartmouth_score();
|
||||
$ConTeam = "opp";
|
||||
return;
|
||||
}
|
||||
|
||||
if (rand(1) <= 0.682*$Defense/8)
|
||||
{
|
||||
print "SHOT IS OFF TARGET.\n";
|
||||
if ($Defense/6*rand(1) > 0.45)
|
||||
{
|
||||
print "REBOUND TO ", $Opponent, "\n";
|
||||
$ConTeam = "opp";
|
||||
return;
|
||||
}
|
||||
|
||||
print "DARTMOUTH CONTROLS THE REBOUND.\n";
|
||||
if (rand(1) <= 0.4) { goto L1300; }
|
||||
if ($Defense == 6)
|
||||
{
|
||||
if (rand(1) <= 0.6)
|
||||
{
|
||||
print "PASS STOLEN BY $Opponent, EASY LAYUP.\n";
|
||||
opp_score();
|
||||
$ConTeam = "dart"; return;
|
||||
return;
|
||||
}
|
||||
}
|
||||
print "BALL PASSED BACK TO YOU.\n";
|
||||
$ConTeam = "dart";
|
||||
return;
|
||||
}
|
||||
|
||||
if (rand(1) <= 0.782*$Defense/8)
|
||||
{
|
||||
print "SHOT IS BLOCKED. BALL CONTROLLED BY "; # no NL
|
||||
if (rand(1) > 0.5)
|
||||
{
|
||||
print "$Opponent.\n";
|
||||
$ConTeam = "opp";
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
print "DARTMOUTH.\n";
|
||||
$ConTeam = "dart";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (rand(1) > 0.843*$Defense/8)
|
||||
{
|
||||
print "CHARGING FOUL. DARTMOUTH LOSES BALL.\n";
|
||||
$ConTeam = "opp";
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
print "SHOOTER IS FOULED. TWO SHOTS.\n";
|
||||
foul_shooting();
|
||||
$ConTeam = "opp";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
L1300:
|
||||
while (1)
|
||||
{
|
||||
$Timer++;
|
||||
if ($Timer == 50)
|
||||
{
|
||||
end_first_half();
|
||||
return;
|
||||
}
|
||||
if ($Timer == 92) { two_min_left(); }
|
||||
if ($ShotType == 0)
|
||||
{
|
||||
get_defense();
|
||||
return;
|
||||
}
|
||||
print '', ($ShotType > 3 ? "SET SHOT." : "LAY UP."), "\n";
|
||||
if (7 / $Defense * rand(1) <= 0.4)
|
||||
{
|
||||
print "SHOT IS GOOD. TWO POINTS.\n";
|
||||
dartmouth_score();
|
||||
$ConTeam = "opp";
|
||||
return;
|
||||
}
|
||||
|
||||
if (7 / $Defense * rand(1) <= 0.7)
|
||||
{
|
||||
print "SHOT IS OFF THE RIM.\n";
|
||||
if (rand(1) <= 0.667)
|
||||
{
|
||||
print "$Opponent CONTROLS THE REBOUND.\n";
|
||||
$ConTeam = "opp";
|
||||
return;
|
||||
}
|
||||
|
||||
print "DARTMOUTH CONTROLS THE REBOUND.\n";
|
||||
next if (rand(1) <= 0.4);
|
||||
|
||||
print "BALL PASSED BACK TO YOU.\n";
|
||||
$ConTeam = "dart";
|
||||
return;
|
||||
}
|
||||
|
||||
if (7 / $Defense * rand(1) <= 0.875)
|
||||
{
|
||||
print "SHOOTER FOULED. TWO SHOTS.\n";
|
||||
foul_shooting();
|
||||
$ConTeam = "opp";
|
||||
return;
|
||||
}
|
||||
|
||||
if (7 / $Defense * rand(1) <= 0.925)
|
||||
{
|
||||
print "SHOT BLOCKED. $Opponent\'S BALL.\n";
|
||||
$ConTeam = "opp";
|
||||
return;
|
||||
}
|
||||
|
||||
print "CHARGING FOUL. DARTMOUTH LOSES THE BALL.\n";
|
||||
$ConTeam = "opp";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
sub get_defense
|
||||
{
|
||||
$Defense = 0;
|
||||
while ($Defense < 6 || $Defense > 7.5)
|
||||
{
|
||||
print "YOUR NEW DEFENSIVE ALLIGNMENT IS (6, 6.5, 7. 7.5): ";
|
||||
chomp($Defense = <>);
|
||||
($Defense) =~ m/(\d(\.\d)?)/;
|
||||
}
|
||||
}
|
||||
|
||||
sub opponent_play
|
||||
{
|
||||
$Player = 1;
|
||||
$Timer++;
|
||||
if ($Timer == 50)
|
||||
{
|
||||
end_first_half();
|
||||
$ConTeam = center_jump();
|
||||
return;
|
||||
}
|
||||
|
||||
print "\n";
|
||||
while (1)
|
||||
{
|
||||
my $shot = 10.0 / 4 * rand(1) + 1;
|
||||
if ($shot <= 2.0)
|
||||
{
|
||||
print "JUMP SHOT.\n";
|
||||
if (8.0 / $Defense * rand(1) <= 0.35)
|
||||
{
|
||||
print "SHOT IS GOOD.\n";
|
||||
opp_score();
|
||||
$ConTeam = "dart";
|
||||
return;
|
||||
}
|
||||
|
||||
if (8.0 / $Defense * rand(1) <= 0.75)
|
||||
{
|
||||
print "SHOT IS OFF RIM.\n";
|
||||
|
||||
L3110:
|
||||
if ($Defense / 6.0 * rand(1) <= 0.5)
|
||||
{
|
||||
print "DARTMOUTH CONTROLS THE REBOUND.\n";
|
||||
$ConTeam = "dart";
|
||||
return;
|
||||
}
|
||||
print "$Opponent CONTROLS THE REBOUND.\n";
|
||||
if ($Defense == 6)
|
||||
{
|
||||
if (rand(1) <= 0.75)
|
||||
{
|
||||
print "BALL STOLEN. EASY LAY UP FOR DARTMOUTH.\n";
|
||||
dartmouth_score();
|
||||
$ConTeam = "opp";
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (rand(1) <= 0.5)
|
||||
{
|
||||
print "PASS BACK TO $Opponent GUARD.\n";
|
||||
$ConTeam = "opp";
|
||||
return;
|
||||
}
|
||||
goto L3500;
|
||||
}
|
||||
|
||||
if (8.0 / $Defense * rand(1) <= 0.9)
|
||||
{
|
||||
print "PLAYER FOULED. TWO SHOTS.\n";
|
||||
foul_shooting();
|
||||
$ConTeam = "dart";
|
||||
return;
|
||||
}
|
||||
print "OFFENSIVE FOUL. DARTMOUTH'S BALL.\n";
|
||||
$ConTeam = "dart";
|
||||
return;
|
||||
}
|
||||
|
||||
L3500:
|
||||
print ($shot > 3 ? "SET SHOT.\n" : "LAY UP.\n");
|
||||
if (7.0 / $Defense * rand(1) > 0.413)
|
||||
{
|
||||
print "SHOT IS MISSED.\n";
|
||||
{
|
||||
no warnings;
|
||||
goto L3110;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
print "SHOT IS GOOD.\n";
|
||||
opp_score();
|
||||
$ConTeam = "dart";
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub opp_score
|
||||
{
|
||||
$Score[0] += 2;
|
||||
print_score();
|
||||
}
|
||||
|
||||
sub dartmouth_score
|
||||
{
|
||||
$Score[1] += 2;
|
||||
print_score();
|
||||
}
|
||||
|
||||
sub print_score
|
||||
{
|
||||
print "SCORE: $Score[1] TO $Score[0]\n";
|
||||
print "TIME: $Timer\n";
|
||||
}
|
||||
|
||||
sub end_first_half
|
||||
{
|
||||
print "\n ***** END OF FIRST HALF *****\n\n";
|
||||
print "SCORE: DARTMOUTH: $Score[1] $Opponent: $Score[0]\n\n\n";
|
||||
center_jump();
|
||||
}
|
||||
|
||||
sub get_your_shot
|
||||
{
|
||||
$ShotType = -1;
|
||||
while ($ShotType < 0 || $ShotType > 4)
|
||||
{
|
||||
print "YOUR SHOT (0-4): ";
|
||||
chomp($ShotType = <>);
|
||||
$ShotType = int($ShotType);
|
||||
if ($ShotType < 0 || $ShotType > 4)
|
||||
{
|
||||
print "INCORRECT ANSWER. RETYPE IT. ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub center_jump
|
||||
{
|
||||
print "CENTER JUMP\n";
|
||||
if (rand(1) <= 0.6)
|
||||
{
|
||||
print "$Opponent CONTROLS THE TAP.\n";
|
||||
return "opp";
|
||||
}
|
||||
print "DARTMOUTH CONTROLS THE TAP.\n";
|
||||
return "dart";
|
||||
}
|
||||
|
||||
sub check_end_game
|
||||
{
|
||||
print "\n";
|
||||
if ($Score[1] != $Score[0])
|
||||
{
|
||||
print " ***** END OF GAME *****\n";
|
||||
print "FINAL SCORE: DARTMOUTH: $Score[1] $Opponent: $Score[0]\n\n";
|
||||
$DoPlay = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
print "\n ***** END OF SECOND HALF *****\n";
|
||||
print "SCORE AT END OF REGULATION TIME:\n";
|
||||
print " DARTMOUTH: $Score[1] $Opponent: $Score[0]\n\n";
|
||||
print "BEGIN TWO MINUTE OVERTIME PERIOD\n";
|
||||
}
|
||||
}
|
||||
|
||||
sub two_min_left
|
||||
{
|
||||
print "\n *** TWO MINUTES LEFT IN THE GAME ***\n\n";
|
||||
}
|
||||
|
||||
sub foul_shooting
|
||||
{
|
||||
if (rand(1) > 0.49)
|
||||
{
|
||||
if (rand(1) > 0.75)
|
||||
{
|
||||
print "BOTH SHOTS MISSED.\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
print "SHOOTER MAKES ONE SHOT AND MISSES ONE.\n";
|
||||
$Score[1 - $Player]++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
print "SHOOTER MAKES BOTH SHOTS.\n";
|
||||
$Score[1 - $Player] += 2;
|
||||
}
|
||||
|
||||
print_score();
|
||||
}
|
||||
Executable
+393
@@ -0,0 +1,393 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
# Basketball program in Perl
|
||||
# While this should play the same way as the fairly faithful translation version,
|
||||
# there are no GOTOs in this code. That was achieved by restructuring the program
|
||||
# in the 2 *_play() subs. All of the percentages remain the same. If writing this
|
||||
# from scratch, we really should have only a play() sub which uses the same code
|
||||
# for both teams, but the percent edge to Darmouth has been maintained here.
|
||||
# Translated by Kevin Brannen (kbrannen)
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# globals
|
||||
my $Defense=0; # dartmouth defense value
|
||||
my $Opponent; # name of opponent
|
||||
my @Score = (0, 0); # scores, dart is [0], opponent is [1]
|
||||
my $Player = 0; # player, 0 = dart, 1 = opp
|
||||
my $Timer = 0; # time tick, 100 ticks per game, 50 is end of first half, if tie at end then back to T=93
|
||||
my $DoPlay = 1; # true if game is still being played
|
||||
my $ConTeam; # controlling team, "dart" or "opp"
|
||||
my $ShotType = 0; # current shot type
|
||||
|
||||
|
||||
print "\n";
|
||||
print " " x 31, "BASKETBALL";
|
||||
print " " x 15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY";
|
||||
print "\n\n\n";
|
||||
|
||||
print "THIS IS DARTMOUTH COLLEGE BASKETBALL. YOU WILL BE DARTMOUTH\n";
|
||||
print "CAPTAIN AND PLAYMAKER. CALL SHOTS AS FOLLOWS:\n";
|
||||
print " 1. LONG (30 FT.) JUMP SHOT;\n";
|
||||
print " 2. SHORT (15 FT.) JUMP SHOT;\n";
|
||||
print " 3. LAY UP;\n";
|
||||
print " 4. SET SHOT.\n";
|
||||
print "BOTH TEAMS WILL USE THE SAME DEFENSE. CALL DEFENSE AS FOLLOWS:\n";
|
||||
print " 6. PRESS;\n";
|
||||
print " 6.5 MAN-TO MAN;\n";
|
||||
print " 7. ZONE;\n";
|
||||
print " 7.5 NONE.\n";
|
||||
print "TO CHANGE DEFENSE, JUST TYPE 0 AS YOUR NEXT SHOT.\n\n";
|
||||
get_defense();
|
||||
print "\n";
|
||||
print "CHOOSE YOUR OPPONENT: ";
|
||||
chomp($Opponent = <>);
|
||||
|
||||
$ConTeam = center_jump();
|
||||
while ($DoPlay)
|
||||
{
|
||||
print "\n";
|
||||
if ($ConTeam eq "dart")
|
||||
{
|
||||
get_your_shot();
|
||||
dartmouth_play();
|
||||
}
|
||||
else
|
||||
{
|
||||
$ShotType = 10.0 / 4 * rand(1) + 1;
|
||||
opponent_play();
|
||||
}
|
||||
if ($Timer >= 100)
|
||||
{
|
||||
check_end_game();
|
||||
last if (!$DoPlay);
|
||||
$Timer = 93;
|
||||
$ConTeam = center_jump();
|
||||
}
|
||||
}
|
||||
exit(0);
|
||||
|
||||
###############################################################
|
||||
|
||||
sub dartmouth_play
|
||||
{
|
||||
$Player = 0;
|
||||
print "\n";
|
||||
while (1)
|
||||
{
|
||||
$Timer++;
|
||||
if ($Timer == 50) { end_first_half(); return; }
|
||||
if ($Timer == 92) { two_min_left(); }
|
||||
|
||||
if ($ShotType == 0)
|
||||
{
|
||||
get_defense();
|
||||
return; # for new ShotType
|
||||
}
|
||||
elsif ($ShotType == 1 || $ShotType == 2)
|
||||
{
|
||||
print "JUMP SHOT\n";
|
||||
if (rand(1) <= 0.341 * $Defense / 8)
|
||||
{
|
||||
print "SHOT IS GOOD.\n";
|
||||
dartmouth_score();
|
||||
last;
|
||||
}
|
||||
|
||||
if (rand(1) <= 0.682*$Defense/8)
|
||||
{
|
||||
print "SHOT IS OFF TARGET.\n";
|
||||
if ($Defense/6*rand(1) > 0.45)
|
||||
{
|
||||
print "REBOUND TO $Opponent\n";
|
||||
last;
|
||||
}
|
||||
|
||||
print "DARTMOUTH CONTROLS THE REBOUND.\n";
|
||||
if (rand(1) <= 0.4)
|
||||
{
|
||||
$ShotType = (rand(1) <= 0.5) ? 3 : 4;
|
||||
next;
|
||||
}
|
||||
if ($Defense == 6)
|
||||
{
|
||||
if (rand(1) <= 0.6)
|
||||
{
|
||||
print "PASS STOLEN BY $Opponent, EASY LAYUP.\n";
|
||||
opp_score();
|
||||
next;
|
||||
}
|
||||
}
|
||||
print "BALL PASSED BACK TO YOU.\n";
|
||||
next;
|
||||
}
|
||||
|
||||
if (rand(1) <= 0.782*$Defense/8)
|
||||
{
|
||||
print "SHOT IS BLOCKED. BALL CONTROLLED BY "; # no NL
|
||||
if (rand(1) > 0.5)
|
||||
{
|
||||
print "$Opponent.\n";
|
||||
last;
|
||||
}
|
||||
else
|
||||
{
|
||||
print "DARTMOUTH.\n";
|
||||
next;
|
||||
}
|
||||
}
|
||||
|
||||
if (rand(1) > 0.843*$Defense/8)
|
||||
{
|
||||
print "CHARGING FOUL. DARTMOUTH LOSES BALL.\n";
|
||||
last;
|
||||
}
|
||||
else
|
||||
{
|
||||
print "SHOOTER IS FOULED. TWO SHOTS.\n";
|
||||
foul_shooting();
|
||||
last;
|
||||
}
|
||||
}
|
||||
else # elsif ($ShotType >= 3)
|
||||
{
|
||||
print '', ($ShotType > 3 ? "SET SHOT." : "LAY UP."), "\n";
|
||||
if (7 / $Defense * rand(1) <= 0.4)
|
||||
{
|
||||
print "SHOT IS GOOD. TWO POINTS.\n";
|
||||
dartmouth_score();
|
||||
last;
|
||||
}
|
||||
|
||||
if (7 / $Defense * rand(1) <= 0.7)
|
||||
{
|
||||
print "SHOT IS OFF THE RIM.\n";
|
||||
if (rand(1) <= 0.667)
|
||||
{
|
||||
print "$Opponent CONTROLS THE REBOUND.\n";
|
||||
last;
|
||||
}
|
||||
|
||||
print "DARTMOUTH CONTROLS THE REBOUND.\n";
|
||||
next if (rand(1) <= 0.4);
|
||||
|
||||
print "BALL PASSED BACK TO YOU.\n";
|
||||
next;
|
||||
}
|
||||
|
||||
if (7 / $Defense * rand(1) <= 0.875)
|
||||
{
|
||||
print "SHOOTER FOULED. TWO SHOTS.\n";
|
||||
foul_shooting();
|
||||
last;
|
||||
}
|
||||
|
||||
if (7 / $Defense * rand(1) <= 0.925)
|
||||
{
|
||||
print "SHOT BLOCKED. $Opponent\'S BALL.\n";
|
||||
last;
|
||||
}
|
||||
|
||||
print "CHARGING FOUL. DARTMOUTH LOSES THE BALL.\n";
|
||||
last;
|
||||
}
|
||||
}
|
||||
$ConTeam = "opp";
|
||||
}
|
||||
|
||||
sub get_defense
|
||||
{
|
||||
$Defense = 0;
|
||||
do {
|
||||
print "YOUR NEW DEFENSIVE ALLIGNMENT IS (6, 6.5, 7. 7.5): ";
|
||||
chomp($Defense = <>);
|
||||
($Defense) =~ m/(\d(\.\d)?)/;
|
||||
} while ($Defense < 6.0 || $Defense > 7.5)
|
||||
}
|
||||
|
||||
sub opponent_play
|
||||
{
|
||||
$Player = 1;
|
||||
print "\n";
|
||||
while (1)
|
||||
{
|
||||
$Timer++;
|
||||
if ($Timer == 50) { end_first_half(); return; }
|
||||
if ($Timer == 92) { two_min_left(); }
|
||||
|
||||
if ($ShotType <= 2.0)
|
||||
{
|
||||
print "JUMP SHOT.\n";
|
||||
if (8.0 / $Defense * rand(1) <= 0.35)
|
||||
{
|
||||
print "SHOT IS GOOD.\n";
|
||||
opp_score();
|
||||
last;
|
||||
}
|
||||
|
||||
if (8.0 / $Defense * rand(1) <= 0.75)
|
||||
{
|
||||
print "SHOT IS OFF RIM.\n";
|
||||
opp_missed();
|
||||
return; # for possible new ShotType or team change
|
||||
}
|
||||
|
||||
if (8.0 / $Defense * rand(1) <= 0.9)
|
||||
{
|
||||
print "PLAYER FOULED. TWO SHOTS.\n";
|
||||
foul_shooting();
|
||||
last;
|
||||
}
|
||||
print "OFFENSIVE FOUL. DARTMOUTH'S BALL.\n";
|
||||
last;
|
||||
}
|
||||
else # ShotType >= 3
|
||||
{
|
||||
print ($ShotType > 3 ? "SET SHOT.\n" : "LAY UP.\n");
|
||||
if (7.0 / $Defense * rand(1) > 0.413)
|
||||
{
|
||||
print "SHOT IS MISSED.\n";
|
||||
{
|
||||
opp_missed();
|
||||
return; # for possible new ShotType or team change
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
print "SHOT IS GOOD.\n";
|
||||
opp_score();
|
||||
last;
|
||||
}
|
||||
}
|
||||
}
|
||||
$ConTeam = "dart";
|
||||
}
|
||||
|
||||
sub opp_missed
|
||||
{
|
||||
if ($Defense / 6.0 * rand(1) <= 0.5)
|
||||
{
|
||||
print "DARTMOUTH CONTROLS THE REBOUND.\n";
|
||||
$ConTeam = "dart";
|
||||
}
|
||||
else
|
||||
{
|
||||
print "$Opponent CONTROLS THE REBOUND.\n";
|
||||
if ($Defense == 6)
|
||||
{
|
||||
if (rand(1) <= 0.75)
|
||||
{
|
||||
print "BALL STOLEN. EASY LAY UP FOR DARTMOUTH.\n";
|
||||
dartmouth_score();
|
||||
#$ConTeam = "opp";
|
||||
return; # for possible new ShotType
|
||||
}
|
||||
}
|
||||
if (rand(1) <= 0.5)
|
||||
{
|
||||
print "PASS BACK TO $Opponent GUARD.\n";
|
||||
#$ConTeam = "opp";
|
||||
return; # for possible new ShotType
|
||||
}
|
||||
$ShotType = (rand(1) <= 0.5) ? 3 : 4;
|
||||
}
|
||||
}
|
||||
|
||||
sub opp_score
|
||||
{
|
||||
$Score[0] += 2;
|
||||
print_score();
|
||||
}
|
||||
|
||||
sub dartmouth_score
|
||||
{
|
||||
$Score[1] += 2;
|
||||
print_score();
|
||||
}
|
||||
|
||||
sub print_score
|
||||
{
|
||||
print "SCORE: $Score[1] TO $Score[0]\n";
|
||||
print "TIME: $Timer\n";
|
||||
}
|
||||
|
||||
sub end_first_half
|
||||
{
|
||||
print "\n ***** END OF FIRST HALF *****\n\n";
|
||||
print "SCORE: DARTMOUTH: $Score[1] $Opponent: $Score[0]\n\n\n";
|
||||
$ConTeam = center_jump();
|
||||
}
|
||||
|
||||
sub get_your_shot
|
||||
{
|
||||
$ShotType = -1;
|
||||
while ($ShotType < 0 || $ShotType > 4)
|
||||
{
|
||||
print "YOUR SHOT (0-4): ";
|
||||
chomp($ShotType = <>);
|
||||
$ShotType = int($ShotType);
|
||||
if ($ShotType < 0 || $ShotType > 4)
|
||||
{
|
||||
print "INCORRECT ANSWER. RETYPE IT. ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub center_jump
|
||||
{
|
||||
print "CENTER JUMP\n";
|
||||
if (rand(1) <= 0.6)
|
||||
{
|
||||
print "$Opponent CONTROLS THE TAP.\n";
|
||||
return "opp";
|
||||
}
|
||||
print "DARTMOUTH CONTROLS THE TAP.\n";
|
||||
return "dart";
|
||||
}
|
||||
|
||||
sub check_end_game
|
||||
{
|
||||
print "\n";
|
||||
if ($Score[1] != $Score[0])
|
||||
{
|
||||
print " ***** END OF GAME *****\n";
|
||||
print "FINAL SCORE: DARTMOUTH: $Score[1] $Opponent: $Score[0]\n\n";
|
||||
$DoPlay = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
print "\n ***** END OF SECOND HALF *****\n";
|
||||
print "SCORE AT END OF REGULATION TIME:\n";
|
||||
print " DARTMOUTH: $Score[1] $Opponent: $Score[0]\n\n";
|
||||
print "BEGIN TWO MINUTE OVERTIME PERIOD\n";
|
||||
}
|
||||
}
|
||||
|
||||
sub two_min_left
|
||||
{
|
||||
print "\n *** TWO MINUTES LEFT IN THE GAME ***\n\n";
|
||||
}
|
||||
|
||||
sub foul_shooting
|
||||
{
|
||||
if (rand(1) > 0.49)
|
||||
{
|
||||
if (rand(1) > 0.75)
|
||||
{
|
||||
print "BOTH SHOTS MISSED.\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
print "SHOOTER MAKES ONE SHOT AND MISSES ONE.\n";
|
||||
$Score[1 - $Player]++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
print "SHOOTER MAKES BOTH SHOTS.\n";
|
||||
$Score[1 - $Player] += 2;
|
||||
}
|
||||
|
||||
print_score();
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
|
||||
|
||||
Conversion to [Perl](https://www.perl.org/)
|
||||
|
||||
Added feature so that if "TIME" value is "0" then it will quit,
|
||||
so you don't have to hit Control-C. Also added a little error checking of the input.
|
||||
|
||||
Executable
+97
@@ -0,0 +1,97 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
# Bounce program in Perl
|
||||
# Translated by Kevin Brannen (kbrannen)
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
print "\n";
|
||||
print " " x 31,"BOUNCE\n";
|
||||
print " " x 15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n";
|
||||
print "\n\n\n";
|
||||
|
||||
print "THIS SIMULATION LETS YOU SPECIFY THE INITIAL VELOCITY\n";
|
||||
print "OF A BALL THROWN STRAIGHT UP, AND THE COEFFICIENT OF\n";
|
||||
print "ELASTICITY OF THE BALL. PLEASE USE A DECIMAL FRACTION\n";
|
||||
print "COEFFICIENCY (LESS THAN 1).\n\n";
|
||||
print "YOU ALSO SPECIFY THE TIME INCREMENT TO BE USED IN\n";
|
||||
print "'STROBING' THE BALL'S FLIGHT (TRY .1 INITIALLY).\n\n";
|
||||
|
||||
# deal with basic's tab() for line positioning
|
||||
# line = line string we're starting with
|
||||
# pos = position to start writing
|
||||
# s = string to write
|
||||
# returns the resultant string, which might not have been changed
|
||||
sub line_tab
|
||||
{
|
||||
my ($line, $pos, $s) = @_;
|
||||
my $len = length($line);
|
||||
# if curser is past position, do nothing
|
||||
if ($len <= $pos) { $line .= " " x ($pos - $len) . $s; }
|
||||
return $line;
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
my @T; # time slice?
|
||||
my $time_inc; # time increment, probably in fractions of seconds
|
||||
my $velocity; # velocity in feet/sec
|
||||
my $coeff_elas; # coeeficent of elasticity
|
||||
my $line_pos; # position on line
|
||||
my $S1 # duration in full seconds?
|
||||
|
||||
# get input
|
||||
print "TIME INCREMENT (SEC, 0=QUIT): "; chomp($time_inc = <>);
|
||||
last if ($time_inc == 0);
|
||||
print "VELOCITY (FPS): "; chomp($velocity = <>);
|
||||
print "COEFFICIENT: "; chomp($coeff_elas = <>);
|
||||
if ($coeff_elas >= 1.0 || $coeff_elas <= 0)
|
||||
{
|
||||
print "COEFFICIENT MUST BE > 0 AND < 1.0\n\n\n";
|
||||
next;
|
||||
}
|
||||
|
||||
print "\nFEET\n";
|
||||
$S1 = int(70.0 / ($velocity / (16.0 * $time_inc)));
|
||||
for my $i (1 .. $S1)
|
||||
{
|
||||
$T[$i] = $velocity * $coeff_elas ** ($i - 1) / 16.0;
|
||||
}
|
||||
|
||||
# draw graph
|
||||
for (my $height=int(-16.0 * ($velocity / 32.0) ** 2.0 + $velocity ** 2.0 / 32.0 + .5) ; $height >= 0 ; $height -= .5)
|
||||
{
|
||||
if (int($height) == $height) { print sprintf("%2d", $height); }
|
||||
else { print " "; }
|
||||
$line_pos = 0;
|
||||
my $curr_line = "";
|
||||
for my $i (1 .. $S1)
|
||||
{
|
||||
my $time;
|
||||
for ($time=0 ; $time <= $T[$i] ; $time += $time_inc)
|
||||
{
|
||||
$line_pos += $time_inc;
|
||||
if (abs($height - (.5 * (-32) * $time ** 2.0 + $velocity * $coeff_elas ** ($i - 1) * $time)) <= .25)
|
||||
{
|
||||
$curr_line = line_tab($curr_line, ($line_pos / $time_inc), "0");
|
||||
}
|
||||
}
|
||||
$time = ($T[$i + 1] // 0) / 2; # we can reach 1 past the end, use 0 if that happens
|
||||
last if (-16.0 * $time ** 2.0 + $velocity * $coeff_elas ** ($i - 1) * $time < $height);
|
||||
}
|
||||
print "$curr_line\n";
|
||||
}
|
||||
|
||||
# draw scale
|
||||
print " .";
|
||||
print "." x (int($line_pos + 1) / $time_inc + 1), "\n";
|
||||
print " 0";
|
||||
my $curr_line = "";
|
||||
for my $i (1 .. int($line_pos + .9995))
|
||||
{
|
||||
$curr_line = line_tab($curr_line, int($i / $time_inc), $i);
|
||||
}
|
||||
print "$curr_line\n";
|
||||
print " " x (int($line_pos + 1) / (2 * $time_inc) - 2), "SECONDS\n\n";
|
||||
}
|
||||
@@ -1,3 +1,22 @@
|
||||
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
|
||||
|
||||
Conversion to [Perl](https://www.perl.org/)
|
||||
|
||||
###Bowling program in Perl
|
||||
|
||||
Run normally, this is a fairly faithful translation of the Basic game.
|
||||
The only real differences are a few trivial fix-ups on the prints to make it
|
||||
look better, and the player/frame/ball line was put before the "get the ball
|
||||
going" line to make it more obvious who's turn it is.
|
||||
|
||||
However, if you run it with "-a" on the command line, it will go into
|
||||
"advanced" mode, which means that "." is used to show pin down and "!" for
|
||||
pin up, current running scores are shown at the end of each frame, and the
|
||||
scoring also looks more normal at the end. This is all done because I think it
|
||||
looks better and I wanted to see a score. Having a flag says you can play
|
||||
whichever version of the game you like.
|
||||
|
||||
Note, the original code doesn't do the 10th frame correctly, in that it will
|
||||
never do more than 2 balls, so the best score you can get is a 290.
|
||||
This is true in both modes. That being said, it will always give you a mediocre
|
||||
game; I don't think I've ever seen a score over 140.
|
||||
|
||||
Executable
+254
@@ -0,0 +1,254 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
# Bowling program in Perl
|
||||
# Run normally, this is a fairly faithful translation of the Basic game.
|
||||
# The only real differences are a few trivial fix-ups on the prints to make it
|
||||
# look better, and the player/frame/ball line was put before the "get the ball
|
||||
# going" line to make it more obvious who's turn it is.
|
||||
#
|
||||
# However, if you run it with "-a" on the command line, it will go into
|
||||
# 'advanced' mode, which means that "." is used to show pin down and "!" for
|
||||
# pin up, current running scores are shown at the end of each frame, and the
|
||||
# scoring also looks more normal at the end. This is all done because I think it
|
||||
# looks better and I wanted to see a score. Having a flag says you can play
|
||||
# whichever version of the game you like.
|
||||
#
|
||||
# Note, the original code doesn't do the 10th frame correctly, in that it will
|
||||
# never do more than 2 balls, so the best score you can get is a 290.
|
||||
# This is true in both modes. That being said, it will always give you a mediocre game.
|
||||
# Translated by Kevin Brannen (kbrannen)
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
print "\n";
|
||||
print " " x 34, "BASKETBALL\n";
|
||||
print " " x 15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n";
|
||||
|
||||
# globals
|
||||
my @C; # pin position matraix?
|
||||
my @Scores; # scores: [player num][frame][ball] # ball3 is end-result, ball4 is frame score (advanced mode)
|
||||
my $Answer; # get answers
|
||||
my $Num_players; # number of players
|
||||
my $Advanced = 0; # flag, 1 to use advanced code, 0 for original code
|
||||
my $char_down = '0'; # char to show pin is down
|
||||
my $char_up = '+'; # char to show pin is standing
|
||||
|
||||
if ($ARGV[0] && $ARGV[0] eq "-a")
|
||||
{
|
||||
shift;
|
||||
$Advanced = 1;
|
||||
$char_down = '.';
|
||||
$char_up = '!';
|
||||
}
|
||||
|
||||
print "WELCOME TO THE ALLEY\n";
|
||||
print "BRING YOUR FRIENDS\n";
|
||||
print "OKAY LET'S FIRST GET ACQUAINTED\n\n";
|
||||
print "SEE THE INSTRUCTIONS (Y/N): ";
|
||||
chomp($Answer = uc(<>));
|
||||
if ($Answer eq "Y")
|
||||
{
|
||||
print "THE GAME OF BOWLING TAKES MIND AND SKILL. DURING THE GAME\n";
|
||||
print "THE COMPUTER WILL KEEP SCORE.YOU MAY COMPETE WITH\n";
|
||||
print "OTHER PLAYERS[UP TO FOUR]. YOU WILL BE PLAYING TEN FRAMES\n";
|
||||
print "ON THE PIN DIAGRAM 'O' MEANS THE PIN IS DOWN...'+' MEANS THE\n";
|
||||
print "PIN IS STANDING. AFTER THE GAME THE COMPUTER WILL SHOW YOUR SCORES.\n";
|
||||
}
|
||||
|
||||
do {
|
||||
print "FIRST OF ALL...HOW MANY ARE PLAYING (1-4): ";
|
||||
$Num_players = int(<>);
|
||||
} while ($Num_players < 1 || $Num_players > 4);
|
||||
|
||||
print "\nVERY GOOD...\n";
|
||||
|
||||
while (1)
|
||||
{
|
||||
# reset all scores
|
||||
for my $p (1 .. $Num_players) # players
|
||||
{
|
||||
for my $f (1 .. 10) # frames
|
||||
{
|
||||
for my $b (1 .. 3) # balls
|
||||
{
|
||||
$Scores[$p][$f][$b] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# play the game
|
||||
for my $frame (1 .. 10) # frame
|
||||
{
|
||||
for my $curr_player (1 .. $Num_players) # player
|
||||
{
|
||||
my $last_pins=0; # pins down for last ball
|
||||
my $ball=1; # ball number, 1 or 2
|
||||
my $end_result=0; # result at end of turn: 3=strike, 2=spare, 1=pins-left
|
||||
for my $i (1 .. 15) { $C[$i] = 0 }
|
||||
|
||||
while (1) # another ball
|
||||
{
|
||||
# ARK BALL GENERATOR USING MOD '15' SYSTEM
|
||||
my $K=0;
|
||||
my $curr_pins=0; # pins down for this ball
|
||||
for my $i (1 .. 20)
|
||||
{
|
||||
my $x = int(rand(1) * 100);
|
||||
my $j;
|
||||
for ($j=1 ; $j <= 10 ; $j++)
|
||||
{
|
||||
last if ($x < 15 * $j);
|
||||
}
|
||||
$C[15 * $j - $x] = 1;
|
||||
}
|
||||
|
||||
# ARK PIN DIAGRAM
|
||||
print "PLAYER: $curr_player FRAME: $frame BALL: $ball\n";
|
||||
print "PRESS ENTER TO GET THE BALL GOING.";
|
||||
$Answer = <>; # not used, just need an enter
|
||||
for my $i (0 .. 3)
|
||||
{
|
||||
print "\n";
|
||||
print " " x $i; # avoid the TAB(), just shift each row over for the triangle
|
||||
for my $j (1 .. 4 - $i)
|
||||
{
|
||||
$K++;
|
||||
print ($C[$K] == 1 ? " $char_down" : " $char_up");
|
||||
}
|
||||
}
|
||||
print "\n";
|
||||
|
||||
# ARK ROLL ANALYSIS
|
||||
for my $i (1 .. 10)
|
||||
{
|
||||
$curr_pins += $C[$i];
|
||||
}
|
||||
if ($curr_pins - $last_pins == 0)
|
||||
{
|
||||
print "GUTTER!!\n";
|
||||
}
|
||||
if ($ball == 1 && $curr_pins == 10)
|
||||
{
|
||||
print "STRIKE!!!!!\a\a\a\a\n"; # \a is for bell
|
||||
$end_result = 3;
|
||||
}
|
||||
elsif ($ball == 2 && $curr_pins == 10)
|
||||
{
|
||||
print "SPARE!!!!\n";
|
||||
$end_result = 2;
|
||||
}
|
||||
elsif ($ball == 2 && $curr_pins < 10)
|
||||
{
|
||||
if ($Advanced) { print 10 - $curr_pins, " PENS LEFT!!!\n"; }
|
||||
else { print "ERROR!!!\n"; }
|
||||
$end_result = 1;
|
||||
}
|
||||
if ($ball == 1 && $curr_pins < 10)
|
||||
{
|
||||
print "ROLL YOUR 2ND BALL\n";
|
||||
}
|
||||
print "\n";
|
||||
|
||||
# ARK STORAGE OF THE SCORES
|
||||
if ($Advanced) { $Scores[$curr_player][$frame][$ball] = $curr_pins - $last_pins; }
|
||||
else { $Scores[$curr_player][$frame][$ball] = $curr_pins; }
|
||||
if ($ball == 1)
|
||||
{
|
||||
$ball = 2;
|
||||
$last_pins = $curr_pins;
|
||||
|
||||
if ($end_result == 3) # strike, no more rolls, goto last
|
||||
{
|
||||
$Scores[$curr_player][$frame][$ball] = $curr_pins;
|
||||
}
|
||||
else
|
||||
{
|
||||
$Scores[$curr_player][$frame][$ball] = $curr_pins - $last_pins;
|
||||
next if ($end_result == 0); # next roll
|
||||
}
|
||||
}
|
||||
last;
|
||||
}
|
||||
$Scores[$curr_player][$frame][3] = $end_result;
|
||||
} # next player
|
||||
if ($Advanced)
|
||||
{
|
||||
print "Scores:\n";
|
||||
for my $p (1 .. $Num_players)
|
||||
{
|
||||
my $total = calc_score($p);
|
||||
print "\tPlayer $p: $total\n";
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
} # next frame
|
||||
|
||||
# end of game, show full scoreboard
|
||||
show_scoreboard();
|
||||
|
||||
print "DO YOU WANT ANOTHER GAME (Y/N): ";
|
||||
chomp($Answer = uc(<>));
|
||||
print "\n";
|
||||
last if ($Answer ne "Y");
|
||||
}
|
||||
exit(0);
|
||||
|
||||
sub show_scoreboard
|
||||
{
|
||||
print "FRAMES\n";
|
||||
for my $i (1 .. 10)
|
||||
{
|
||||
print " $i ";
|
||||
}
|
||||
print "\n";
|
||||
my @results = ( "-", ".", "/", "X" );
|
||||
for my $p (1 .. $Num_players)
|
||||
{
|
||||
print "Player $p\n" if ($Advanced);
|
||||
my $ball_max = ($Advanced ? 4 : 3);
|
||||
for my $b (1 .. $ball_max)
|
||||
{
|
||||
for my $f (1 .. 10)
|
||||
{
|
||||
if ($b != 3) { print sprintf("%2d ", $Scores[$p][$f][$b]); }
|
||||
else { print sprintf("%2s ", $results[$Scores[$p][$f][$b]]); }
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
print "\n";
|
||||
}
|
||||
}
|
||||
|
||||
sub calc_score
|
||||
{
|
||||
my $player = shift;
|
||||
my $total = 0;
|
||||
for my $frame (1 .. 10)
|
||||
{
|
||||
my $score = 0;
|
||||
if ($frame == 10 || $Scores[$player][$frame][3] == 1) # pins
|
||||
{
|
||||
$score = $Scores[$player][$frame][1] + $Scores[$player][$frame][2];
|
||||
}
|
||||
elsif ($Scores[$player][$frame][3] == 2) # spare
|
||||
{
|
||||
$score = 10 + $Scores[$player][$frame+1][1];
|
||||
}
|
||||
elsif ($Scores[$player][$frame][3] == 3) # strike
|
||||
{
|
||||
$score = 10 + $Scores[$player][$frame+1][1];
|
||||
if ($Scores[$player][$frame+1][1] == 10)
|
||||
{
|
||||
$score += ($frame < 9 ? $Scores[$player][$frame+2][1] : $Scores[$player][$frame+1][2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
$score += $Scores[$player][$frame+1][2];
|
||||
}
|
||||
}
|
||||
$Scores[$player][$frame][4] = $score;
|
||||
$total += $score;
|
||||
}
|
||||
return $total;
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
# Boxing program in Perl
|
||||
# Required extensive restructuring to remove all of the GOTO's.
|
||||
# Translated by Kevin Brannen (kbrannen)
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# globals
|
||||
my $Opp_won = 0; # num rounds opponent has won
|
||||
my $You_won = 0; # num rounds you have won
|
||||
my $Opp_name = ""; # opponent name
|
||||
my $Your_name = ""; # your name
|
||||
my $Your_best = 0; # your best punch
|
||||
my $Your_worst = 0; # your worst punch
|
||||
my $Opp_best; # opponent best punch
|
||||
my $Opp_worst; # opponent worst punch
|
||||
my $Opp_damage; # opponent damage ?
|
||||
my $Your_damage; # your damage ?
|
||||
|
||||
sub get_punch
|
||||
{
|
||||
my $prompt = shift;
|
||||
my $p;
|
||||
while (1)
|
||||
{
|
||||
print "$prompt: ";
|
||||
chomp($p = int(<>));
|
||||
last if ($p >= 1 && $p <= 4);
|
||||
print "DIFFERENT PUNCHES ARE: (1) FULL SWING; (2) HOOK; (3) UPPERCUT; (4) JAB.\n";
|
||||
}
|
||||
return $p;
|
||||
}
|
||||
|
||||
print "\n";
|
||||
print " " x 33, "BOXING\n";
|
||||
print " " x 15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n";
|
||||
|
||||
print "BOXING OLYMPIC STYLE (3 ROUNDS -- 2 OUT OF 3 WINS)\n\n";
|
||||
print "WHAT IS YOUR OPPONENT'S NAME: ";
|
||||
chomp($Opp_name = <>);
|
||||
print "INPUT YOUR MAN'S NAME: ";
|
||||
chomp($Your_name = <>);
|
||||
print "DIFFERENT PUNCHES ARE: (1) FULL SWING; (2) HOOK; (3) UPPERCUT; (4) JAB.\n";
|
||||
$Your_best = get_punch("WHAT IS YOUR MANS BEST");
|
||||
$Your_worst = get_punch("WHAT IS HIS VULNERABILITY");
|
||||
|
||||
do {
|
||||
$Opp_best = int(4*rand(1)+1);
|
||||
$Opp_worst = int(4*rand(1)+1);
|
||||
} while ($Opp_best == $Opp_worst);
|
||||
print "$Opp_name\'S ADVANTAGE IS $Opp_best AND VULNERABILITY IS SECRET.\n\n";
|
||||
|
||||
for my $R (1 .. 3) # rounds
|
||||
{
|
||||
last if ($Opp_won >= 2 || $You_won >= 2);
|
||||
$Opp_damage = 0;
|
||||
$Your_damage = 0;
|
||||
print "ROUND $R BEGINS...\n";
|
||||
for my $R1 (1 .. 7) # 7 events per round?
|
||||
{
|
||||
if (int(10*rand(1)+1) <= 5)
|
||||
{
|
||||
my $your_punch = get_punch("$Your_name\'S PUNCH");
|
||||
$Opp_damage += 2 if ($your_punch == $Your_best);
|
||||
|
||||
if ($your_punch == 1) { punch1(); }
|
||||
elsif ($your_punch == 2) { punch2(); }
|
||||
elsif ($your_punch == 3) { punch3(); }
|
||||
else { punch4(); }
|
||||
next;
|
||||
}
|
||||
|
||||
my $Opp_punch = int(4*rand(1)+1);
|
||||
$Your_damage += 2 if ($Opp_punch == $Opp_best);
|
||||
|
||||
if ($Opp_punch == 1) { opp1(); }
|
||||
elsif ($Opp_punch == 2) { opp2(); }
|
||||
elsif ($Opp_punch == 3) { opp3(); }
|
||||
else { opp4(); }
|
||||
}
|
||||
|
||||
if ($Opp_damage > $Your_damage)
|
||||
{
|
||||
print "\n$Your_name WINS ROUND $R\n\n";
|
||||
$You_won++;
|
||||
}
|
||||
else
|
||||
{
|
||||
print "\n$Opp_name WINS ROUND $R\n\n";
|
||||
$Opp_won++;
|
||||
}
|
||||
}
|
||||
|
||||
if ($Opp_won >= 2)
|
||||
{
|
||||
done("$Opp_name WINS (NICE GOING, $Opp_name).");
|
||||
}
|
||||
|
||||
#else # if ($You_won >= 2)
|
||||
done("$Your_name AMAZINGLY WINS!!");
|
||||
|
||||
###################################################
|
||||
|
||||
sub done
|
||||
{
|
||||
my $msg = shift;
|
||||
print $msg;
|
||||
print "\n\nAND NOW GOODBYE FROM THE OLYMPIC ARENA.\n\n";
|
||||
exit(0);
|
||||
}
|
||||
|
||||
sub punch1
|
||||
{
|
||||
# $your_punch == 1, full swing
|
||||
print "$Your_name SWINGS AND ";
|
||||
if ($Opp_worst == 4 || int(30*rand(1)+1) < 10)
|
||||
{
|
||||
print "HE CONNECTS!\n";
|
||||
if ($Opp_damage > 35)
|
||||
{
|
||||
done("$Opp_name IS KNOCKED COLD AND $Your_name IS THE WINNER AND CHAMP! ");
|
||||
}
|
||||
$Opp_damage += 15;
|
||||
}
|
||||
else
|
||||
{
|
||||
print "HE MISSES\n";
|
||||
print "\n\n" if ($Opp_damage != 1);
|
||||
}
|
||||
}
|
||||
|
||||
sub punch2
|
||||
{
|
||||
# $your_punch == 2, hook
|
||||
print "$Your_name GIVES THE HOOK... ";
|
||||
if ($Opp_worst == 2)
|
||||
{
|
||||
$Opp_damage += 7;
|
||||
return;
|
||||
}
|
||||
if (int(2*rand(1)+1) == 1)
|
||||
{
|
||||
print "BUT IT'S BLOCKED!!!!!!!!!!!!!\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
print "CONNECTS...\n";
|
||||
$Opp_damage += 7;
|
||||
}
|
||||
}
|
||||
|
||||
sub punch3
|
||||
{
|
||||
# $your_punch == 3, uppercut
|
||||
print "$Your_name TRIES AN UPPERCUT ";
|
||||
if ($Opp_worst == 3 || int(100*rand(1)+1) < 51)
|
||||
{
|
||||
print "AND HE CONNECTS!\n";
|
||||
$Opp_damage += 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
print "AND IT'S BLOCKED (LUCKY BLOCK!)\n";
|
||||
}
|
||||
}
|
||||
|
||||
sub punch4
|
||||
{
|
||||
# $your_punch == 4, jab
|
||||
print "$Your_name JABS AT $Opp_name\'S HEAD ";
|
||||
if ($Opp_worst == 4 || (int(8*rand(1)+1)) >= 4)
|
||||
{
|
||||
$Opp_damage += 3;
|
||||
print "\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
print "IT'S BLOCKED.\n";
|
||||
}
|
||||
}
|
||||
|
||||
sub opp1
|
||||
{
|
||||
# opp_punch == 1
|
||||
print "$Opp_name TAKES A FULL SWING AND ";
|
||||
if ($Your_worst == 1 || int(60*rand(1)+1) < 30)
|
||||
{
|
||||
print " POW!!!!! HE HITS HIM RIGHT IN THE FACE!\n";
|
||||
if ($Your_damage > 35)
|
||||
{
|
||||
done("$Your_name IS KNOCKED COLD AND $Opp_name IS THE WINNER AND CHAMP!");
|
||||
}
|
||||
$Your_damage += 15;
|
||||
}
|
||||
else
|
||||
{
|
||||
print " IT'S BLOCKED!\n";
|
||||
}
|
||||
}
|
||||
|
||||
sub opp2
|
||||
{
|
||||
# opp_punch == 2
|
||||
print "$Opp_name GETS $Your_name IN THE JAW (OUCH!)\n";
|
||||
$Your_damage += 7;
|
||||
print "....AND AGAIN!\n";
|
||||
$Your_damage += 5;
|
||||
if ($Your_damage > 35)
|
||||
{
|
||||
done("$Your_name IS KNOCKED COLD AND $Opp_name IS THE WINNER AND CHAMP!");
|
||||
}
|
||||
print "\n";
|
||||
# 2 continues into opp_punch == 3
|
||||
opp3();
|
||||
}
|
||||
|
||||
sub opp3()
|
||||
{
|
||||
# opp_punch == 3
|
||||
print "$Your_name IS ATTACKED BY AN UPPERCUT (OH,OH)...\n";
|
||||
if ($Your_worst != 3 && int(200*rand(1)+1) > 75)
|
||||
{
|
||||
print " BLOCKS AND HITS $Opp_name WITH A HOOK.\n";
|
||||
$Opp_damage += 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
print "AND $Opp_name CONNECTS...\n";
|
||||
$Your_damage += 8;
|
||||
}
|
||||
}
|
||||
|
||||
sub opp4
|
||||
{
|
||||
# opp_punch == 4
|
||||
print "$Opp_name JABS AND ";
|
||||
if ($Your_worst == 4)
|
||||
{
|
||||
$Your_damage += 5;
|
||||
}
|
||||
elsif (int(7*rand(1)+1) > 4)
|
||||
{
|
||||
print " BLOOD SPILLS !!!\n";
|
||||
$Your_damage += 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
print "IT'S BLOCKED!\n";
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,9 @@
|
||||
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
|
||||
|
||||
Conversion to [Perl](https://www.perl.org/)
|
||||
|
||||
Note: This version has lines and columns numbers to help you with choosing the cell
|
||||
to move from and to, so you don't have to continually count. It also puts a "." only for
|
||||
blank cells you can move to, which I think makes for a more pleasing look and makes
|
||||
it easier to play. If you want the original behavior, start the program with an arg
|
||||
of "-o" for the original behavior.
|
||||
|
||||
Executable
+351
@@ -0,0 +1,351 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
# Checkers program in Perl
|
||||
# Started with checkers.annotated.bas
|
||||
# Translated by Kevin Brannen (kbrannen)
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# globals
|
||||
#
|
||||
# The current move: (rating, current x, current y, new x, new y)
|
||||
# 'rating' represents how good the move is; higher is better.
|
||||
my @ratings = (-99); # (4); # Start with minimum score
|
||||
# The board. Pieces are represented by numeric values:
|
||||
#
|
||||
# - 0 = empty square
|
||||
# - -1,-2 = X (-1 for regular piece, -2 for king)
|
||||
# - 1,2 = O (1 for regular piece, 2 for king)
|
||||
#
|
||||
# This program's player ("me") plays X.
|
||||
my @board; # (7,7)
|
||||
# chars to print for the board, add 2 to the board value as an index to the char
|
||||
my @chars = ("X*", "X", ".", "O", "O*");
|
||||
my $neg1 = -1; # constant holding -1
|
||||
my $winner = "";
|
||||
my $upgrade = shift(@ARGV) // "";
|
||||
$upgrade = $upgrade eq "-o" ? 0 : 1;
|
||||
|
||||
#####
|
||||
|
||||
print "\n";
|
||||
print " " x 32, "CHECKERS\n";
|
||||
print " " x 15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n";
|
||||
|
||||
|
||||
print "THIS IS THE GAME OF CHECKERS. THE COMPUTER IS X,\n";
|
||||
print "AND YOU ARE O. THE COMPUTER WILL MOVE FIRST.\n";
|
||||
print "SQUARES ARE REFERRED TO BY A COORDINATE SYSTEM.\n";
|
||||
print "(0,0) IS THE LOWER LEFT CORNER\n";
|
||||
print "(0,7) IS THE UPPER LEFT CORNER\n";
|
||||
print "(7,0) IS THE LOWER RIGHT CORNER\n";
|
||||
print "(7,7) IS THE UPPER RIGHT CORNER\n";
|
||||
print "THE COMPUTER WILL TYPE '+TO' WHEN YOU HAVE ANOTHER\n";
|
||||
print "JUMP. TYPE TWO NEGATIVE NUMBERS IF YOU CANNOT JUMP.\n";
|
||||
print "ENTER YOUR MOVE POSITION LIKE '0 0' OR '0,0'.\n\n\n";
|
||||
|
||||
# Initialize the board. Data is 2 length-wise strips repeated.
|
||||
my @data = ();
|
||||
for (1 .. 32) { push(@data, (1,0,1,0,0,0,-1,0, 0,1,0,0,0,-1,0,-1)); }
|
||||
for my $x (0 .. 7)
|
||||
{
|
||||
for my $y (0 .. 7)
|
||||
{
|
||||
$board[$x][$y] = shift(@data);
|
||||
}
|
||||
}
|
||||
|
||||
# Start of game loop. First, my turn.
|
||||
while (1)
|
||||
{
|
||||
|
||||
# For each square on the board, search for one of my pieces
|
||||
# and if it can make the best move so far, store that move in 'r'
|
||||
for my $x (0 .. 7)
|
||||
{
|
||||
for my $y (0 .. 7)
|
||||
{
|
||||
# Skip if this is empty or an opponent's piece
|
||||
next if ($board[$x][$y] > -1);
|
||||
|
||||
# If this is one of my ordinary pieces, analyze possible
|
||||
# forward moves.
|
||||
if ($board[$x][$y] == -1)
|
||||
{
|
||||
for (my $a = -1 ; $a <= 1 ; $a +=2)
|
||||
{
|
||||
$b = $neg1;
|
||||
find_move($x, $y, $a, $b);
|
||||
}
|
||||
}
|
||||
|
||||
# If this is one of my kings, analyze possible forward
|
||||
# and backward moves.
|
||||
if ($board[$x][$y] == -2)
|
||||
{
|
||||
for (my $a = -1 ; $a <= 1 ; $a += 2)
|
||||
{
|
||||
for (my $b = -1 ; $a <= 1 ; $b += 2) { find_move($x, $y, $a, $b); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ($ratings[0] == -99) # Game is lost if no move could be found.
|
||||
{
|
||||
$winner = "you";
|
||||
last;
|
||||
}
|
||||
|
||||
# Print the computer's move. (Note: chr$(30) is an ASCII RS
|
||||
# (record separator) code; probably no longer relevant.)
|
||||
print "FROM $ratings[1],$ratings[2] TO $ratings[3],$ratings[4] ";
|
||||
$ratings[0] = -99;
|
||||
|
||||
# Make the computer's move. If the piece finds its way to the
|
||||
# end of the board, crown it.
|
||||
LOOP1240: {
|
||||
if ($ratings[4] == 0)
|
||||
{
|
||||
$board[$ratings[3]][$ratings[4]] = -2;
|
||||
last LOOP1240;
|
||||
}
|
||||
$board[$ratings[3]][$ratings[4]] = $board[$ratings[1]][$ratings[2]];
|
||||
$board[$ratings[1]][$ratings[2]] = 0;
|
||||
|
||||
# If the piece has jumped 2 squares, it means the computer has
|
||||
# taken an opponents' piece.
|
||||
if (abs($ratings[1] - $ratings[3]) == 2)
|
||||
{
|
||||
$board [($ratings[1]+$ratings[3])/2] [($ratings[2]+$ratings[4])/2] = 0; # Delete the opponent's piece
|
||||
|
||||
# See if we can jump again. Evaluate all possible moves.
|
||||
my $x = $ratings[3];
|
||||
my $y = $ratings[4];
|
||||
for (my $a = -2 ; $a <= 2 ; $a += 4)
|
||||
{
|
||||
if ($board[$x][$y] == -1)
|
||||
{
|
||||
$b = -2;
|
||||
eval_move($x, $y, $a, $b);
|
||||
}
|
||||
if ($board[$x][$y] == -2)
|
||||
{
|
||||
for (my $b = -2 ; $b <= 2 ; $b += 4) { eval_move($x, $y, $a, $b); }
|
||||
}
|
||||
}
|
||||
|
||||
# If we've found a move, go back and make that one as well
|
||||
if ($ratings[0] != -99)
|
||||
{
|
||||
print "TO $ratings[3], $ratings[4] ";
|
||||
$ratings[0] = -99;
|
||||
next LOOP1240;
|
||||
}
|
||||
}
|
||||
} # LOOP1240
|
||||
|
||||
# Now, print the board
|
||||
print "\n\n\n";
|
||||
for (my $y = 7 ; $y >= 0 ; $y--)
|
||||
{
|
||||
my $line = "";
|
||||
$line = "$y|" if ($upgrade);
|
||||
for my $x (0 .. 7)
|
||||
{
|
||||
my $c = $chars[$board[$x][$y] + 2];
|
||||
$c = ' ' if ($upgrade && (($y % 2 == 0 && $x % 2 == 1) || ($y % 2 == 1 && $x % 2 == 0)));
|
||||
$line = tab($line, 5*$x+7, $c);
|
||||
}
|
||||
print $line;
|
||||
print " \n\n";
|
||||
}
|
||||
print " _ _ _ _ _ _ _ _\n" if ($upgrade);
|
||||
print " 0 1 2 3 4 5 6 7\n" if ($upgrade);
|
||||
print "\n";
|
||||
|
||||
# Check if either player is out of pieces. If so, announce the
|
||||
# winner.
|
||||
my ($z, $t) = (0, 0);
|
||||
for my $x (0 .. 7)
|
||||
{
|
||||
for my $y (0 .. 7)
|
||||
{
|
||||
if ($board[$x][$y] == 1 || $board[$x][$y] == 2) { $z = 1; }
|
||||
if ($board[$x][$y] == -1 || $board[$x][$y] == -2) { $t = 1; }
|
||||
}
|
||||
}
|
||||
if ($z != 1) { $winner = "comp"; last; }
|
||||
if ($t != 1) { $winner = "you"; last; }
|
||||
|
||||
# Prompt the player for their move.
|
||||
($z, $t) = (0, 0);
|
||||
my ($x, $y, $e, $h, $a, $b);
|
||||
do {
|
||||
($e,$h) = get_pos("FROM:");
|
||||
$x = $e;
|
||||
$y = $h;
|
||||
} while ($board[$x][$y] <= 0);
|
||||
do {
|
||||
($a,$b) = get_pos("TO:");
|
||||
$x = $a;
|
||||
$y = $b;
|
||||
} while (!($board[$x][$y] == 0 && abs($a-$e) <= 2 && abs($a-$e) == abs($b-$h)));
|
||||
|
||||
LOOP1750: {
|
||||
# Make the move and stop unless it might be a jump.
|
||||
$board[$a][$b] = $board[$e][$h];
|
||||
$board[$e][$h] = 0;
|
||||
if (abs($e-$a) != 2) { last LOOP1750; }
|
||||
|
||||
# Remove the piece jumped over
|
||||
$board[($e+$a)/2][($h+$b)/2] = 0;
|
||||
|
||||
# Prompt for another move; -1 means player can't, so I've won.
|
||||
# Keep prompting until there's a valid move or the player gives
|
||||
# up.
|
||||
my ($a1, $b1);
|
||||
do {
|
||||
($a1,$b1) = get_pos("+TO:");
|
||||
if ($a1 < 0) { last LOOP1750; }
|
||||
} while ($board[$a1][$b1] != 0 || abs($a1-$a) != 2 || abs($b1-$b) != 2);
|
||||
|
||||
# Update the move variables to correspond to the next jump
|
||||
$e = $a;
|
||||
$h = $b;
|
||||
$a = $a1;
|
||||
$b = $b1;
|
||||
}
|
||||
|
||||
# If the player has reached the end of the board, crown this piece
|
||||
if ($b == 7) { $board[$a][$b] = 2; }
|
||||
|
||||
# And play the next turn.
|
||||
}
|
||||
|
||||
# Endgame:
|
||||
print "\n", ($winner eq "you" ? "YOU" : "I"), " WIN\n";
|
||||
exit(0);
|
||||
|
||||
###########################################
|
||||
|
||||
# make sure we get a 2 value position
|
||||
sub get_pos
|
||||
{
|
||||
my $prompt = shift;
|
||||
my ($p1, $p2);
|
||||
do {
|
||||
print "$prompt ";
|
||||
chomp(my $ans = <>);
|
||||
($p1,$p2) = split(/[, ]/, $ans);
|
||||
} while (!defined($p1) || !defined($p2) || $p1 < -1 || $p2 < -1 || $p1 > 7 || $p2 > 7);
|
||||
return ($p1,$p2);
|
||||
}
|
||||
|
||||
# deal with basic's tab() for line positioning
|
||||
# line = line string we're starting with
|
||||
# pos = position to start writing
|
||||
# s = string to write
|
||||
# returns the resultant string, which might not have been changed
|
||||
sub tab
|
||||
{
|
||||
my ($line, $pos, $str) = @_;
|
||||
my $len = length($line);
|
||||
# if curser is past position, do nothing
|
||||
if ($len <= $pos) { $line .= " " x ($pos - $len) . $str; }
|
||||
return $line;
|
||||
}
|
||||
|
||||
# Analyze a move from (x,y) to (x+a, y+b) and schedule it if it's
|
||||
# the best candidate so far.
|
||||
sub find_move
|
||||
{
|
||||
my ($x, $y, $a, $b) = @_;
|
||||
my $u = $x+$a;
|
||||
my $v = $y+$b;
|
||||
|
||||
# Done if it's off the board
|
||||
return if ($u < 0 || $u > 7 || $v < 0 || $ v> 7);
|
||||
|
||||
# Consider the destination if it's empty
|
||||
eval_jump($x, $y, $u, $v) if ($board[$u][$v] == 0);
|
||||
|
||||
# If it's got an opponent's piece, jump it instead
|
||||
if ($board[$u][$v] > 0)
|
||||
{
|
||||
|
||||
# Restore u and v, then return if it's off the board
|
||||
$u += $a;
|
||||
$v += $b;
|
||||
return if ($u < 0 || $v < 0 || $u > 7 || $v > 7);
|
||||
|
||||
# Otherwise, consider u,v
|
||||
eval_jump($x, $y, $u, $v) if ($board[$u][$v] == 0);
|
||||
}
|
||||
}
|
||||
|
||||
# Evaluate jumping (x,y) to (u,v).
|
||||
#
|
||||
# Computes a score for the proposed move and if it's higher
|
||||
# than the best-so-far move, uses that instead by storing it
|
||||
# and its score in @ratings.
|
||||
sub eval_jump
|
||||
{
|
||||
my ($x, $y, $u, $v) = @_;
|
||||
|
||||
# q is the score; it starts at 0
|
||||
my $q = 0;
|
||||
|
||||
# +2 if it promotes this piece
|
||||
$q += 2 if ($v == 0 && $board[$x][$y] == -1);
|
||||
|
||||
# +5 if it takes an opponent's piece
|
||||
$q += 5 if (abs($y-$v) == 2);
|
||||
|
||||
# -2 if the piece is moving away from the top boundary
|
||||
$q -= 2 if ($y == 7);
|
||||
|
||||
# +1 for putting the piece against a vertical boundary
|
||||
$q++ if ($u == 0 || $u == 7);
|
||||
|
||||
for (my $c = -1 ; $c <= 1 ; $c += 2)
|
||||
{
|
||||
next if ($u+$c < 0 || $u+$c > 7 || $v+$neg1 < 0);
|
||||
|
||||
# +1 for each adjacent friendly piece
|
||||
if ($board[$u+$c][$v+$neg1] < 0)
|
||||
{
|
||||
$q++;
|
||||
next;
|
||||
}
|
||||
|
||||
# Prevent out-of-bounds testing
|
||||
next if ($u-$c < 0 || $u-$c > 7 || $v-$neg1 > 7);
|
||||
|
||||
# -2 for each opponent piece that can now take this piece here
|
||||
$q -= 2 if ($board[$u+$c][$v+$neg1] > 0 && ($board[$u-$c][$v-$neg1] == 0 || ($u-$c == $x && $v-$neg1 == $y)));
|
||||
}
|
||||
|
||||
# Use this move if it's better than the previous best
|
||||
if ($q > $ratings[0])
|
||||
{
|
||||
$ratings[0] = $q;
|
||||
$ratings[1] = $x;
|
||||
$ratings[2] = $y;
|
||||
$ratings[3] = $u;
|
||||
$ratings[4] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
# If (u,v) is in the bounds, evaluate it as a move using
|
||||
# the sub at 910, so storing eval in @ratings.
|
||||
sub eval_move
|
||||
{
|
||||
my ($x, $y, $a, $b) = @_;
|
||||
my $u = $x+$a;
|
||||
my $v = $y+$b;
|
||||
return if ($u < 0 || $u > 7 || $v < 0 || $v > 7);
|
||||
eval_jump($x, $y, $u, $v) if ($board[$u][$v] == 0 && $board[$x+$a/2][$y+$b/2] > 0);
|
||||
}
|
||||
Executable
+202
@@ -0,0 +1,202 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
# Combat program in Perl
|
||||
# Translated by Kevin Brannen (kbrannen)
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# globals
|
||||
my $User_army;
|
||||
my $User_navy;
|
||||
my $User_AF;
|
||||
my $Comp_army = 30000;
|
||||
my $Comp_navy = 20000;
|
||||
my $Comp_AF = 22000;
|
||||
my $Attack_type;
|
||||
my $Attack_num;
|
||||
|
||||
print "\n";
|
||||
print " " x 33, "COMBAT\n";
|
||||
print " " x 15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n";
|
||||
|
||||
print "I AM AT WAR WITH YOU.\nWE HAVE 72000 SOLDIERS APIECE.\n\n";
|
||||
|
||||
do {
|
||||
print "DISTRIBUTE YOUR FORCES.\n";
|
||||
print "\tME\t YOU\n";
|
||||
print "ARMY\t$Comp_army\t";
|
||||
chomp($User_army = <>);
|
||||
print "NAVY\t$Comp_navy\t";
|
||||
chomp($User_navy = <>);
|
||||
print "A. F.\t$Comp_AF\t";
|
||||
chomp($User_AF = <>);
|
||||
} while ($User_army + $User_navy + $User_AF > 72000);
|
||||
|
||||
do {
|
||||
print "YOU ATTACK FIRST. TYPE (1) FOR ARMY; (2) FOR NAVY;\n";
|
||||
print "AND (3) FOR AIR FORCE.\n";
|
||||
chomp($Attack_num = <>);
|
||||
} while ($Attack_type < 1 && $Attack_type > 3);
|
||||
do {
|
||||
print "HOW MANY MEN\n";
|
||||
chomp($Attack_type = <>);
|
||||
} while ($Attack_type < 0
|
||||
|| ($Attack_num == 1 && $Attack_type > $User_army)
|
||||
|| ($Attack_num == 2 && $Attack_type > $User_navy)
|
||||
|| ($Attack_num == 3 && $Attack_type > $User_AF));
|
||||
|
||||
if ($Attack_num == 1)
|
||||
{
|
||||
if ($Attack_type<$User_army/3)
|
||||
{
|
||||
print "YOU LOST $Attack_type MEN FROM YOUR ARMY.\n";
|
||||
$User_army = int($User_army-$Attack_type);
|
||||
}
|
||||
if ($Attack_type<2*$User_army/3)
|
||||
{
|
||||
print "YOU LOST ", int($Attack_type/3), " MEN, BUT I LOST ", int(2*$Comp_army/3), "\n";
|
||||
$User_army = int($User_army-$Attack_type/3);
|
||||
$Comp_army = int(2*$Comp_army/3);
|
||||
}
|
||||
else
|
||||
{
|
||||
s270();
|
||||
}
|
||||
}
|
||||
elsif ($Attack_num == 2)
|
||||
{
|
||||
if ($Attack_type < $Comp_navy/3)
|
||||
{
|
||||
print "YOUR ATTACK WAS STOPPED!\n";
|
||||
$User_navy = int($User_navy-$Attack_type);
|
||||
}
|
||||
if ($Attack_type < 2*$Comp_navy/3)
|
||||
{
|
||||
print "YOU DESTROYED ", int(2*$Comp_navy/3), " OF MY ARMY.\n";
|
||||
$Comp_navy = int(2*$Comp_navy/3);
|
||||
}
|
||||
else
|
||||
{
|
||||
s270();
|
||||
}
|
||||
}
|
||||
else # $Attack_num == 3
|
||||
{
|
||||
if ($Attack_type < $User_AF/3)
|
||||
{
|
||||
print "YOUR ATTACK WAS WIPED OUT.\n";
|
||||
$User_AF = int($User_AF-$Attack_type);
|
||||
}
|
||||
if ($Attack_type < 2*$User_AF/3)
|
||||
{
|
||||
print "WE HAD A DOGFIGHT. YOU WON - AND FINISHED YOUR MISSION.\n";
|
||||
$Comp_army = int(2*$Comp_army/3);
|
||||
$Comp_navy = int($Comp_navy/3);
|
||||
$Comp_AF = int($Comp_AF/3);
|
||||
}
|
||||
else
|
||||
{
|
||||
print "YOU WIPED OUT ONE OF MY ARMY PATROLS, BUT I DESTROYED\n";
|
||||
print "TWO NAVY BASES AND BOMBED THREE ARMY BASES.\n";
|
||||
$User_army = int($User_army/4);
|
||||
$User_navy = int($User_navy/3);
|
||||
$User_AF = int(2*$User_AF/3);
|
||||
}
|
||||
}
|
||||
|
||||
print "\n\tYOU\tME\n";
|
||||
print "ARMY\t$User_army\t$Comp_army\n";
|
||||
print "NAVY\t$User_navy\t$Comp_navy\n";
|
||||
print "A. F.\t$User_AF\t$Comp_AF\n";
|
||||
do {
|
||||
print "WHAT IS YOUR NEXT MOVE?\n";
|
||||
print "ARMY=1 NAVY=2 AIR FORCE=3\n";
|
||||
chomp($Attack_type = <>);
|
||||
} while ($Attack_type < 1 && $Attack_type > 3);
|
||||
do {
|
||||
print "HOW MANY MEN\n";
|
||||
chomp($Attack_num = <>);
|
||||
} while ($Attack_num < 0
|
||||
|| ($Attack_type == 1 && $Attack_num > $User_army)
|
||||
|| ($Attack_type == 2 && $Attack_num > $User_navy)
|
||||
|| ($Attack_type == 3 && $Attack_num > $User_AF));
|
||||
|
||||
if ($Attack_num == 1)
|
||||
{
|
||||
if ($Attack_num < $Comp_army/2)
|
||||
{
|
||||
print "I WIPED OUT YOUR ATTACK!\n";
|
||||
$User_army -= $Attack_num;
|
||||
}
|
||||
else
|
||||
{
|
||||
print "YOU DESTROYED MY ARMY!\n";
|
||||
$Comp_army = 0;
|
||||
}
|
||||
}
|
||||
elsif ($Attack_num == 2)
|
||||
{
|
||||
if ($Attack_num < $Comp_navy/2)
|
||||
{
|
||||
print "I SUNK TWO OF YOUR BATTLESHIPS, AND MY AIR FORCE\n";
|
||||
print "WIPED OUT YOUR UNGAURDED CAPITOL.\n";
|
||||
$User_army /= 4;
|
||||
$User_navy /= 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
print "YOUR NAVY SHOT DOWN THREE OF MY XIII PLANES,\n";
|
||||
print "AND SUNK THREE BATTLESHIPS.\n";
|
||||
$Comp_AF = 2*$Comp_AF/3;
|
||||
$Comp_navy /= 2;
|
||||
}
|
||||
}
|
||||
else # $Attack_num == 3
|
||||
{
|
||||
if ($Attack_num > $Comp_AF/2)
|
||||
{
|
||||
print "MY NAVY AND AIR FORCE IN A COMBINED ATTACK LEFT\n";
|
||||
print "YOUR COUNTRY IN SHAMBLES.\n";
|
||||
$User_army /= 3;
|
||||
$User_navy /= 3;
|
||||
$User_AF /= 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
print "ONE OF YOUR PLANES CRASHED INTO MY HOUSE. I AM DEAD.\n";
|
||||
print "MY COUNTRY FELL APART.\n";
|
||||
$Comp_army = $Comp_navy = $Comp_AF = 0;
|
||||
}
|
||||
}
|
||||
|
||||
print "\nFROM THE RESULTS OF BOTH OF YOUR ATTACKS,\n";
|
||||
my $total_user = $User_army+$User_navy+$User_AF;
|
||||
my $total_comp = $Comp_army+$Comp_navy+$Comp_AF;
|
||||
if ($total_user > 3/2*($total_comp))
|
||||
{
|
||||
print "YOU WON, OH! SHUCKS!!!!\n";
|
||||
}
|
||||
elsif ($total_user < 2/3*($total_comp))
|
||||
{
|
||||
print "YOU LOST-I CONQUERED YOUR COUNTRY. IT SERVES YOU\n";
|
||||
print "RIGHT FOR PLAYING THIS STUPID GAME!!!\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
print "THE TREATY OF PARIS CONCLUDED THAT WE TAKE OUR\n";
|
||||
print "RESPECTIVE COUNTRIES AND LIVE IN PEACE.\n";
|
||||
}
|
||||
print "\n";
|
||||
exit(0);
|
||||
|
||||
#######################################################
|
||||
|
||||
sub s270
|
||||
{
|
||||
print "YOU SUNK ONE OF MY PATROL BOATS, BUT I WIPED OUT TWO\n";
|
||||
print "OF YOUR AIR FORCE BASES AND 3 ARMY BASES.\n";
|
||||
$User_army = int($User_army/3);
|
||||
$User_AF = int($User_AF/3);
|
||||
$Comp_navy = int(2*$Comp_navy/3);
|
||||
}
|
||||
Executable
+125
@@ -0,0 +1,125 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
# Digits program in Perl
|
||||
# Translated by Kevin Brannen (kbrannen)
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# globals
|
||||
my $Answer;
|
||||
|
||||
print "\n";
|
||||
print " " x 33, "DIGITS";
|
||||
print " " x 15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n";
|
||||
|
||||
print "THIS IS A GAME OF GUESSING.\n";
|
||||
print "FOR INSTRUCTIONS, TYPE '1', ELSE TYPE '0': ";
|
||||
chomp($Answer = <>);
|
||||
if ($Answer == 1)
|
||||
{
|
||||
print "\nPLEASE TAKE A PIECE OF PAPER AND WRITE DOWN\n";
|
||||
print "THE DIGITS '0', '1', OR '2' THIRTY TIMES AT RANDOM.\n";
|
||||
print "ARRANGE THEM IN THREE LINES OF TEN DIGITS EACH.\n";
|
||||
print "I WILL ASK FOR THEN TEN AT A TIME.\n";
|
||||
print "I WILL ALWAYS GUESS THEM FIRST AND THEN LOOK AT YOUR\n";
|
||||
print "NEXT NUMBER TO SEE IF I WAS RIGHT. BY PURE LUCK,\n";
|
||||
print "I OUGHT TO BE RIGHT TEN TIMES. BUT I HOPE TO DO BETTER\n";
|
||||
print "THAN THAT. *****\n\n\n";
|
||||
}
|
||||
|
||||
my ($A, $B, $C) = (0, 1, 3);
|
||||
my (@M, @K, @L); # DIM M(26,2),K(2,2),L(8,2)
|
||||
while (1)
|
||||
{
|
||||
for my $i (0 .. 26) { for my $j (0 .. 2) { $M[$i][$j] = 1; } }
|
||||
for my $i (0 .. 2) { for my $j (0 .. 2) { $K[$i][$j] = 9; } }
|
||||
for my $i (0 .. 8) { for my $j (0 .. 2) { $L[$i][$j] = 3; } }
|
||||
$L[0][0] = $L[4][1] = $L[8][2] = 2;
|
||||
my $Z = 26;
|
||||
my $Z1 = 8;
|
||||
my $Z2 = 2;
|
||||
my $X = 0;
|
||||
my @N;
|
||||
|
||||
for my $T (1 .. 3)
|
||||
{
|
||||
my $have_input = 0;
|
||||
while (!$have_input)
|
||||
{
|
||||
$have_input = 1;
|
||||
print "\nTEN NUMBERS, PLEASE: ";
|
||||
chomp($Answer = <>);
|
||||
$Answer = "0 " . $Answer; # need to be 1-based, so prepend a throw-away value for [0]
|
||||
@N = split(/\s+/, $Answer);
|
||||
for my $i (1 .. 10)
|
||||
{
|
||||
if (!defined($N[$i]) || ($N[$i] != 0 && $N[$i] != 1 && $N[$i] != 2))
|
||||
{
|
||||
print "ONLY USE THE DIGITS '0', '1', OR '2'.\n";
|
||||
print "LET'S TRY AGAIN.";
|
||||
$have_input = 0;
|
||||
last;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print "\nMY GUESS\tYOUR NO.\tRESULT\tNO. RIGHT\n\n";
|
||||
for my $U (1 .. 10)
|
||||
{
|
||||
my $num = $N[$U];
|
||||
my $S = 0;
|
||||
my $G;
|
||||
for my $J (0 .. 2)
|
||||
{
|
||||
my $S1 = $A * $K[$Z2][$J] + $B * $L[$Z1][$J] + $C * $M[$Z][$J];
|
||||
next if ($S > $S1);
|
||||
if ($S >= $S1)
|
||||
{
|
||||
next if (rand(1) < .5);
|
||||
}
|
||||
$S = $S1;
|
||||
$G = $J;
|
||||
} # NEXT J
|
||||
print " $G\t\t$N[$U]\t\t";
|
||||
if ($G == $N[$U])
|
||||
{
|
||||
$X++;
|
||||
print "RIGHT\t$X\n";
|
||||
$M[$Z][$num]++;
|
||||
$L[$Z1][$num]++;
|
||||
$K[$Z2][$num]++;
|
||||
$Z -= int($Z / 9) * 9;
|
||||
$Z = 3 * $Z + $N[$U];
|
||||
}
|
||||
else
|
||||
{
|
||||
print "WRONG\t$X\n";
|
||||
}
|
||||
$Z1 = $Z - int($Z / 9) * 9;
|
||||
$Z2 = $N[$U];
|
||||
} # NEXT U
|
||||
} # NEXT T
|
||||
|
||||
print "\n";
|
||||
if ($X == 10)
|
||||
{
|
||||
print "I GUESSED EXACTLY 1/3 OF YOUR NUMBERS.\n";
|
||||
print "IT'S A TIE GAME.\n";
|
||||
}
|
||||
elsif ($X > 10)
|
||||
{
|
||||
print "I GUESSED MORE THAN 1/3, OR $X, OF YOUR NUMBERS.\n";
|
||||
print "I WIN.\a\a\a\a\a\a\a\a\a\a"
|
||||
}
|
||||
else
|
||||
{
|
||||
print "I GUESSED LESS THAN 1/3, OR $X, OF YOUR NUMBERS.\n";
|
||||
print "YOU BEAT ME. CONGRATULATIONS *****\n";
|
||||
}
|
||||
|
||||
print "\nDO YOU WANT TO TRY AGAIN (1 FOR YES, 0 FOR NO): ";
|
||||
chomp($Answer = <>);
|
||||
last if ($Answer != 1);
|
||||
}
|
||||
print "\nTHANKS FOR THE GAME.\n";
|
||||
Executable
+124
@@ -0,0 +1,124 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
# Flip Flop program in Perl
|
||||
# Translated by Kevin Brannen (kbrannen)
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
use Math::Trig;
|
||||
|
||||
print "\n";
|
||||
print " " x 32, "FLIPFLOP";
|
||||
print " " x 15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n";
|
||||
# *** CREATED BY MICHAEL CASS
|
||||
|
||||
print "THE OBJECT OF THIS PUZZLE IS TO CHANGE THIS:\n\n";
|
||||
print "X X X X X X X X X X\n\n";
|
||||
print "TO THIS:\n\n";
|
||||
print "O O O O O O O O O O\n\n";
|
||||
print "BY TYPING THE NUMBER CORRESPONDING TO THE POSITION OF THE\n";
|
||||
print "LETTER ON SOME NUMBERS, ONE POSITION WILL CHANGE, ON\n";
|
||||
print "OTHERS, TWO WILL CHANGE. TO RESET LINE TO ALL X'S, TYPE 0\n";
|
||||
print "(ZERO) AND TO START OVER IN THE MIDDLE OF A GAME, TYPE \n";
|
||||
print "11 (ELEVEN).\n\n";
|
||||
|
||||
sub initialize
|
||||
{
|
||||
my @a;
|
||||
print "1 2 3 4 5 6 7 8 9 10\n";
|
||||
print "X X X X X X X X X X\n";
|
||||
for my $i (0 .. 10) { $a[$i] = "X"; } # make sure [0] has a value just in case
|
||||
return @a;
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
my $Q = rand(1);
|
||||
my $C = 0;
|
||||
print "HERE IS THE STARTING LINE OF X'S.\n\n";
|
||||
my @A = initialize();
|
||||
|
||||
while (1)
|
||||
{
|
||||
my $M = 0;
|
||||
my $N;
|
||||
while (1)
|
||||
{
|
||||
print "\nINPUT THE NUMBER: ";
|
||||
chomp($N = <>);
|
||||
if ($N != int($N) || $N < 0 || $N > 11)
|
||||
{
|
||||
print "ILLEGAL ENTRY--TRY AGAIN.\n";
|
||||
next;
|
||||
}
|
||||
last;
|
||||
}
|
||||
if ($N == 11) # start a new game
|
||||
{
|
||||
print "\n\n";
|
||||
last;
|
||||
}
|
||||
if ($N == 0) # reset line
|
||||
{
|
||||
@A = initialize();
|
||||
next;
|
||||
}
|
||||
|
||||
if ($M != $N)
|
||||
{
|
||||
$M = $N;
|
||||
$A[$N] = ($A[$N] eq "O") ? "X" : "O";
|
||||
while ($M == $N)
|
||||
{
|
||||
my $R = tan($Q + $N / $Q - $N) - sin($Q / $N) + 336 * sin(8 * $N);
|
||||
$N = $R - int($R);
|
||||
$N = int(10 * $N);
|
||||
if ($A[$N] eq "O")
|
||||
{
|
||||
$A[$N] = "X";
|
||||
next;
|
||||
}
|
||||
$A[$N] = "O";
|
||||
last; # GOTO 610
|
||||
|
||||
$A[$N] = "X";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($A[$N] ne "O") { $A[$N] = "O"; }
|
||||
while ($M == $N)
|
||||
{
|
||||
my $R = .592 * (1 / tan($Q / $N + $Q)) / sin( $N * 2 + $Q) - cos($N);
|
||||
$N = $R - int($R);
|
||||
$N = int(10 * $N);
|
||||
if ($A[$N] eq "O")
|
||||
{
|
||||
$A[$N] = "X";
|
||||
next;
|
||||
}
|
||||
$A[$N] = "O";
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
print "1 2 3 4 5 6 7 8 9 10\n";
|
||||
for my $i (1 .. 10) { print "$A[$i] "; }
|
||||
print "\n";
|
||||
$C++;
|
||||
my $i;
|
||||
for ($i=1 ; $i <= 10 ; $i++) {
|
||||
last if ($A[$i] ne "O");
|
||||
}
|
||||
if ($i == 11)
|
||||
{
|
||||
if ($C <= 12) { print "VERY GOOD. YOU GUESSED IT IN ONLY $C GUESSES.\n"; }
|
||||
else { print "TRY HARDER NEXT TIME. IT TOOK YOU $C GUESSES.\n"; }
|
||||
last;
|
||||
}
|
||||
}
|
||||
print "DO YOU WANT TO TRY ANOTHER PUZZLE (Y/N): ";
|
||||
$_ = <>;
|
||||
print "\n";
|
||||
last if (m/^n/i);
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
|
||||
|
||||
Conversion to [Perl](https://www.perl.org/)
|
||||
|
||||
You can answer yes/no questions in lower case if desired.
|
||||
|
||||
Executable
+265
@@ -0,0 +1,265 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
# Fur Trader program in Perl
|
||||
# Translated by Kevin Brannen (kbrannen)
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# globals
|
||||
my @Pelts = (qw(0 MINK BEAVER ERMINE FOX ));
|
||||
my $Num_pelts = 4;
|
||||
my @Quantity; # how many of each fur
|
||||
my $Money;
|
||||
my $Max_pelts = 190;
|
||||
my $Ermine_price; # like we have @Pelts and @Quantity we could have @Prices
|
||||
my $Beaver_price; # then have 4 constants as index into the arrays to avoid
|
||||
my $Fox_price; # the magic numbers 1-4, or better have a array of objects (really a hash)
|
||||
my $Mink_price; # with the 3 keys (name, number, price), but well keep it
|
||||
# with 4 vars like the basic program did
|
||||
|
||||
print "\n";
|
||||
print " " x 31, "FUR TRADER\n";
|
||||
print " " x 15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n";
|
||||
|
||||
init();
|
||||
while (1)
|
||||
{
|
||||
my $ans = get_yesno();
|
||||
last if ($ans ne "YES");
|
||||
|
||||
$Ermine_price = new_price(.15, 0.95);
|
||||
$Beaver_price = new_price(.25, 1.00);
|
||||
|
||||
print "\nYOU HAVE \$$Money SAVINGS.\n";
|
||||
print "AND $Max_pelts FURS TO BEGIN THE EXPEDITION.\n";
|
||||
print "\nYOUR $Max_pelts FURS ARE DISTRIBUTED AMONG THE FOLLOWING\n";
|
||||
print "KINDS OF PELTS: MINK, BEAVER, ERMINE AND FOX.\n";
|
||||
reset_furs();
|
||||
|
||||
my $total = 0;
|
||||
for my $j ( 1 .. $Num_pelts)
|
||||
{
|
||||
print "\nHOW MANY $Pelts[$j] PELTS DO YOU HAVE? ";
|
||||
chomp(my $ans = <>);
|
||||
$Quantity[$j] = int($ans);
|
||||
$total += $Quantity[$j];
|
||||
}
|
||||
if ($total > $Max_pelts)
|
||||
{
|
||||
print "\nYOU MAY NOT HAVE THAT MANY FURS.\n";
|
||||
print "DO NOT TRY TO CHEAT. I CAN ADD.\n";
|
||||
print "YOU MUST START AGAIN.\n";
|
||||
init();
|
||||
next;
|
||||
}
|
||||
|
||||
print "\nYOU MAY TRADE YOUR FURS AT FORT 1, FORT 2,\n";
|
||||
print "OR FORT 3. FORT 1 IS FORT HOCHELAGA (MONTREAL)\n";
|
||||
print "AND IS UNDER THE PROTECTION OF THE FRENCH ARMY.\n";
|
||||
print "FORT 2 IS FORT STADACONA (QUEBEC) AND IS UNDER THE\n";
|
||||
print "PROTECTION OF THE FRENCH ARMY. HOWEVER, YOU MUST\n";
|
||||
print "MAKE A PORTAGE AND CROSS THE LACHINE RAPIDS.\n";
|
||||
print "FORT 3 IS FORT NEW YORK AND IS UNDER DUTCH CONTROL.\n";
|
||||
print "YOU MUST CROSS THROUGH IROQUOIS LAND.\n";
|
||||
my $done = 0;
|
||||
while (!$done)
|
||||
{
|
||||
$ans = 0;
|
||||
while ($ans < 1 || $ans > 3)
|
||||
{
|
||||
no warnings; # in case user enters alpha chars, then int() will return 0 with no warnings
|
||||
print "ANSWER 1, 2, OR 3: ";
|
||||
$ans = int(<>);
|
||||
}
|
||||
# returns 0 if they want to go somewhere else;
|
||||
# or returns the old basic line number to show what to do
|
||||
if ($ans == 1) { $done = fort1(); }
|
||||
elsif ($ans == 2) { $done = fort2(); }
|
||||
elsif ($ans == 3) { $done = fort3(); }
|
||||
}
|
||||
|
||||
if ($done == 1410)
|
||||
{
|
||||
print "YOUR BEAVER SOLD FOR \$", $Beaver_price * $Quantity[2], "\t";
|
||||
}
|
||||
|
||||
if ($done <= 1414)
|
||||
{
|
||||
print "YOUR FOX SOLD FOR \$", $Fox_price * $Quantity[4], "\n";
|
||||
print "YOUR ERMINE SOLD FOR \$", $Ermine_price * $Quantity[3], "\t";
|
||||
print "YOUR MINK SOLD FOR \$", $Mink_price * $Quantity[1], "\n";
|
||||
}
|
||||
|
||||
# 1418 is always done
|
||||
$Money += $Mink_price * $Quantity[1] + $Beaver_price * $Quantity[2] + $Ermine_price * $Quantity[3] + $Fox_price * $Quantity[4];
|
||||
print "\nYOU NOW HAVE \$$Money INCLUDING YOUR PREVIOUS SAVINGS\n";
|
||||
print "\nDO YOU WANT TO TRADE FURS NEXT YEAR? ";
|
||||
}
|
||||
exit(0);
|
||||
|
||||
###############################################################
|
||||
|
||||
sub init
|
||||
{
|
||||
print "YOU ARE THE LEADER OF A FRENCH FUR TRADING EXPEDITION IN\n";
|
||||
print "1776 LEAVING THE LAKE ONTARIO AREA TO SELL FURS AND GET\n";
|
||||
print "SUPPLIES FOR THE NEXT YEAR. YOU HAVE A CHOICE OF THREE\n";
|
||||
print "FORTS AT WHICH YOU MAY TRADE. THE COST OF SUPPLIES\n";
|
||||
print "AND THE AMOUNT YOU RECEIVE FOR YOUR FURS WILL DEPEND\n";
|
||||
print "ON THE FORT THAT YOU CHOOSE.\n";
|
||||
|
||||
$Money = 600;
|
||||
print "DO YOU WISH TO TRADE FURS?\n";
|
||||
}
|
||||
|
||||
sub new_price
|
||||
{
|
||||
my ($base, $factor) = @_;
|
||||
return int(($base * rand(1) + $factor) * 100 + .5) / 100;
|
||||
}
|
||||
|
||||
sub supplies_fs
|
||||
{
|
||||
print "SUPPLIES AT FORT STADACONA COST \$125.00.\n";
|
||||
print "YOUR TRAVEL EXPENSES TO STADACONA WERE \$15.00.\n";
|
||||
}
|
||||
|
||||
sub supplies_ny
|
||||
{
|
||||
print "SUPPLIES AT NEW YORK COST \$80.00.\n";
|
||||
print "YOUR TRAVEL EXPENSES TO NEW YORK WERE \$25.00.\n";
|
||||
}
|
||||
|
||||
sub reset_furs
|
||||
{
|
||||
for my $j (1 .. $Num_pelts) { $Quantity[$j] = 0; }
|
||||
}
|
||||
|
||||
sub get_yesno
|
||||
{
|
||||
my $ans;
|
||||
print "ANSWER YES OR NO: ";
|
||||
chomp($ans = uc(<>));
|
||||
return $ans;
|
||||
}
|
||||
|
||||
sub trade_elsewhere
|
||||
{
|
||||
print "DO YOU WANT TO TRADE AT ANOTHER FORT? ";
|
||||
my $ans = get_yesno();
|
||||
return $ans;
|
||||
}
|
||||
|
||||
sub fort1
|
||||
{
|
||||
print "\nYOU HAVE CHOSEN THE EASIEST ROUTE. HOWEVER, THE FORT\n";
|
||||
print "IS FAR FROM ANY SEAPORT. THE VALUE\n";
|
||||
print "YOU RECEIVE FOR YOUR FURS WILL BE LOW AND THE COST\n";
|
||||
print "OF SUPPLIES HIGHER THAN AT FORTS STADACONA OR NEW YORK.\n";
|
||||
my $ans = trade_elsewhere();
|
||||
if ($ans eq "YES") { return 0; }
|
||||
|
||||
$Money -= 160;
|
||||
$Mink_price = new_price(.2, .7 );
|
||||
$Ermine_price = new_price(.2, .65);
|
||||
$Beaver_price = new_price(.2, .75);
|
||||
$Fox_price = new_price(.2, .8 );
|
||||
print "\nSUPPLIES AT FORT HOCHELAGA COST \$150.00.\n";
|
||||
print "YOUR TRAVEL EXPENSES TO HOCHELAGA WERE \$10.00.\n";
|
||||
return 1410;
|
||||
}
|
||||
|
||||
sub fort2
|
||||
{
|
||||
print "\nYOU HAVE CHOSEN A HARD ROUTE. IT IS, IN COMPARSION,\n";
|
||||
print "HARDER THAN THE ROUTE TO HOCHELAGA BUT EASIER THAN\n";
|
||||
print "THE ROUTE TO NEW YORK. YOU WILL RECEIVE AN AVERAGE VALUE\n";
|
||||
print "FOR YOUR FURS AND THE COST OF YOUR SUPPLIES WILL BE AVERAGE.\n";
|
||||
my $ans = trade_elsewhere();
|
||||
if ($ans eq "YES") { return 0; }
|
||||
|
||||
$Money -= 140;
|
||||
print "\n";
|
||||
$Mink_price = new_price(.3, .85);
|
||||
$Ermine_price = new_price(.15, .8);
|
||||
$Beaver_price = new_price(.2, .9);
|
||||
my $P = int(10 * rand(1)) + 1;
|
||||
if ($P <= 2)
|
||||
{
|
||||
$Quantity[2] = 0;
|
||||
print "YOUR BEAVER WERE TOO HEAVY TO CARRY ACROSS\n";
|
||||
print "THE PORTAGE. YOU HAD TO LEAVE THE PELTS, BUT FOUND\n";
|
||||
print "THEM STOLEN WHEN YOU RETURNED.\n";
|
||||
supplies_fs();
|
||||
return 1414;
|
||||
}
|
||||
elsif ($P <= 6)
|
||||
{
|
||||
print "YOU ARRIVED SAFELY AT FORT STADACONA.\n";
|
||||
supplies_fs();
|
||||
}
|
||||
elsif ($P <= 8)
|
||||
{
|
||||
reset_furs();
|
||||
print "YOUR CANOE UPSET IN THE LACHINE RAPIDS. YOU\n";
|
||||
print "LOST ALL YOUR FURS.\n";
|
||||
supplies_fs();
|
||||
return 1418;
|
||||
}
|
||||
elsif ($P <= 10)
|
||||
{
|
||||
$Quantity[4] = 0;
|
||||
print "YOUR FOX PELTS WERE NOT CURED PROPERLY.\n";
|
||||
print "NO ONE WILL BUY THEM.\n";
|
||||
supplies_fs();
|
||||
}
|
||||
return 1410;
|
||||
}
|
||||
|
||||
sub fort3
|
||||
{
|
||||
print "\nYOU HAVE CHOSEN THE MOST DIFFICULT ROUTE. AT\n";
|
||||
print "FORT NEW YORK YOU WILL RECEIVE THE HIGHEST VALUE\n";
|
||||
print "FOR YOUR FURS. THE COST OF YOUR SUPPLIES\n";
|
||||
print "WILL BE LOWER THAN AT ALL THE OTHER FORTS.\n";
|
||||
my $ans = trade_elsewhere();
|
||||
if ($ans eq "YES") { return 0; }
|
||||
|
||||
$Money -= 105;
|
||||
print "\n";
|
||||
$Mink_price = new_price(.15, 1.05);
|
||||
$Fox_price = new_price(.25, 1.1);
|
||||
$Fox_price = new_price(.25, 1.1);
|
||||
my $P = int(10 * rand(1)) + 1;
|
||||
if ($P <= 2)
|
||||
{
|
||||
print "YOU WERE ATTACKED BY A PARTY OF IROQUOIS.\n";
|
||||
print "ALL PEOPLE IN YOUR TRADING GROUP WERE\n";
|
||||
print "KILLED. THIS ENDS THE GAME.\n";
|
||||
exit(0);
|
||||
}
|
||||
elsif ($P <= 6)
|
||||
{
|
||||
print "YOU WERE LUCKY. YOU ARRIVED SAFELY\n";
|
||||
print "AT FORT NEW YORK.\n";
|
||||
supplies_ny();
|
||||
}
|
||||
elsif ($P <= 8)
|
||||
{
|
||||
reset_furs();
|
||||
print "YOU NARROWLY ESCAPED AN IROQUOIS RAIDING PARTY.\n";
|
||||
print "HOWEVER, YOU HAD TO LEAVE ALL YOUR FURS BEHIND.\n";
|
||||
supplies_ny();
|
||||
return 1418;
|
||||
}
|
||||
elsif ($P <= 10)
|
||||
{
|
||||
$Beaver_price /= 2;
|
||||
$Mink_price /= 2;
|
||||
print "YOUR MINK AND BEAVER WERE DAMAGED ON YOUR TRIP.\n";
|
||||
print "YOU RECEIVE ONLY HALF THE CURRENT PRICE FOR THESE FURS.\n";
|
||||
supplies_ny();
|
||||
}
|
||||
return 1410;
|
||||
}
|
||||
Executable
+81
@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
# Gunner program in Perl
|
||||
# Required extensive restructuring to remove all of the GOTO's.
|
||||
# Translated by Kevin Brannen (kbrannen)
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# globals
|
||||
my $Max_range = int(40000*rand(1)+20000);
|
||||
my $Total_shots = 0;
|
||||
my $Games = 0;
|
||||
|
||||
print "\n";
|
||||
print " " x 30, "GUNNER\n";
|
||||
print " " x 15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n";
|
||||
|
||||
print "YOU ARE THE OFFICER-IN-CHARGE, GIVING ORDERS TO A GUN\n";
|
||||
print "CREW, TELLING THEM THE DEGREES OF ELEVATION YOU ESTIMATE\n";
|
||||
print "WILL PLACE A PROJECTILE ON TARGET. A HIT WITHIN 100 YARDS\n";
|
||||
print "OF THE TARGET WILL DESTROY IT.\n\n";
|
||||
print "MAXIMUM RANGE OF YOUR GUN IS $Max_range YARDS.\n\n";
|
||||
|
||||
GAME: while (1)
|
||||
{
|
||||
my $target_dist = int($Max_range * (.1 + .8 * rand(1)));
|
||||
my $shots = 0;
|
||||
print "DISTANCE TO THE TARGET IS $target_dist YARDS.\n\n";
|
||||
while (1)
|
||||
{
|
||||
my $elevation = get_elevation(); # in degrees
|
||||
$shots++;
|
||||
my $dist = int($target_dist - ($Max_range * sin(2 * $elevation / 57.3)));
|
||||
if (abs($dist) < 100)
|
||||
{
|
||||
print "*** TARGET DESTROYED *** $shots ROUNDS OF AMMUNITION EXPENDED.\n";
|
||||
$Total_shots += $shots;
|
||||
if ($Games++ == 4)
|
||||
{
|
||||
print "\n\nTOTAL ROUNDS EXPENDED WERE: $Total_shots\n";
|
||||
if ($Total_shots > 18) { print "BETTER GO BACK TO FORT SILL FOR REFRESHER TRAINING!\n"; }
|
||||
else { print "NICE SHOOTING !!\n"; }
|
||||
last;
|
||||
}
|
||||
print "\nTHE FORWARD OBSERVER HAS SIGHTED MORE ENEMY ACTIVITY...\n";
|
||||
next GAME;
|
||||
}
|
||||
if ($dist > 100) { print "SHORT OF TARGET BY ", abs($dist)," YARDS.\n"; }
|
||||
else { print "OVER TARGET BY ", abs($dist), " YARDS.\n"; }
|
||||
|
||||
if ($shots >= 5)
|
||||
{
|
||||
print "\nBOOM !!!! YOU HAVE JUST BEEN DESTROYED BY THE ENEMY.\n\n\n\n";
|
||||
print "BETTER GO BACK TO FORT SILL FOR REFRESHER TRAINING!\n";
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
print "\nTRY AGAIN (Y OR N): ";
|
||||
chomp(my $ans=uc(<>));
|
||||
if ($ans ne "Y") { last; }
|
||||
else { $Games = 0; $Total_shots = 0; }
|
||||
}
|
||||
|
||||
print "\nOK. RETURN TO BASE CAMP.\n";
|
||||
|
||||
####################################
|
||||
|
||||
sub get_elevation
|
||||
{
|
||||
my $elevation;
|
||||
while (1)
|
||||
{
|
||||
print "\nELEVATION: ";
|
||||
chomp($elevation = <>);
|
||||
if ($elevation > 89) { print "MAXIMUM ELEVATION IS 89 DEGREES.\n"; }
|
||||
elsif ($elevation < 1) { print "MINIMUM ELEVATION IS ONE DEGREE.\n"; }
|
||||
else { return $elevation; }
|
||||
}
|
||||
}
|
||||
+14
-2
@@ -1,10 +1,10 @@
|
||||
## King
|
||||
|
||||
This is one of the most comprehensive, difficult, and interesting games. (If you’ve never played one of these games, start with HAMMURABI.)
|
||||
This is one of the most comprehensive, difficult, and interesting games. (If you've never played one of these games, start with HAMMURABI.)
|
||||
|
||||
In this game, you are Premier of Setats Detinu, a small communist island 30 by 70 miles long. Your job is to decide upon the budget of your country and distribute money to your country from the communal treasury.
|
||||
|
||||
The money system is Rollods; each person needs 100 Rallods per year to survive. Your country’s income comes from farm produce and tourists visiting your magnificent forests, hunting, fishing, etc. Part of your land is farm land but it also has an excellent mineral content and may be sold to foreign industry for strip mining. Industry import and support their own workers. Crops cost between 10 and 15 Rallods per square mile to plant, cultivate, and harvest. Your goal is to complete an eight-year term of office without major mishap. A word of warning: it isn’t easy!
|
||||
The money system is Rollods; each person needs 100 Rallods per year to survive. Your country's income comes from farm produce and tourists visiting your magnificent forests, hunting, fishing, etc. Part of your land is farm land but it also has an excellent mineral content and may be sold to foreign industry for strip mining. Industry import and support their own workers. Crops cost between 10 and 15 Rallods per square mile to plant, cultivate, and harvest. Your goal is to complete an eight-year term of office without major mishap. A word of warning: it isn't easy!
|
||||
|
||||
The author of this program is James A. Storer who wrote it while a student at Lexington High School.
|
||||
|
||||
@@ -66,3 +66,15 @@ On basic line 1997 it is:
|
||||
but it should be:
|
||||
|
||||
1997 PRINT " AND 1,000 SQ. MILES OF FOREST LAND."
|
||||
|
||||
### Bug 4
|
||||
|
||||
On basic line 1310 we see this:
|
||||
|
||||
1310 IF C=0 THEN 1324
|
||||
1320 PRINT "OF ";INT(J);"SQ. MILES PLANTED,";
|
||||
1324 ...
|
||||
|
||||
but it should probably be:
|
||||
|
||||
1310 IF J=0 THEN 1324
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
namespace King;
|
||||
|
||||
internal class Country
|
||||
{
|
||||
private const int InitialLand = 1000;
|
||||
|
||||
private readonly IReadWrite _io;
|
||||
private readonly IRandom _random;
|
||||
private float _rallods;
|
||||
private float _countrymen;
|
||||
private float _foreigners;
|
||||
private float _arableLand;
|
||||
private float _industryLand;
|
||||
|
||||
public Country(IReadWrite io, IRandom random)
|
||||
: this(
|
||||
io,
|
||||
random,
|
||||
(int)(60000 + random.NextFloat(1000) - random.NextFloat(1000)),
|
||||
(int)(500 + random.NextFloat(10) - random.NextFloat(10)),
|
||||
0,
|
||||
InitialLand)
|
||||
{
|
||||
}
|
||||
|
||||
public Country(IReadWrite io, IRandom random, float rallods, float countrymen, float foreigners, float land)
|
||||
{
|
||||
_io = io;
|
||||
_random = random;
|
||||
_rallods = rallods;
|
||||
_countrymen = countrymen;
|
||||
_foreigners = foreigners;
|
||||
_arableLand = land;
|
||||
}
|
||||
|
||||
public string GetStatus(int landValue, int plantingCost)
|
||||
=> Resource.Status(_rallods, _countrymen, _foreigners, _arableLand, landValue, plantingCost);
|
||||
|
||||
public float Countrymen => _countrymen;
|
||||
public float Workers => _foreigners;
|
||||
public bool HasWorkers => _foreigners > 0;
|
||||
private float FarmLand => _arableLand;
|
||||
public bool HasRallods => _rallods > 0;
|
||||
public float Rallods => _rallods;
|
||||
public float IndustryLand => InitialLand - _arableLand;
|
||||
public int PreviousTourismIncome { get; private set; }
|
||||
|
||||
public bool SellLand(int landValue, out float landSold)
|
||||
{
|
||||
if (_io.TryReadValue(
|
||||
SellLandPrompt,
|
||||
out landSold,
|
||||
new ValidityTest(v => v <= FarmLand, () => SellLandError(FarmLand))))
|
||||
{
|
||||
_arableLand = (int)(_arableLand - landSold);
|
||||
_rallods = (int)(_rallods + landSold * landValue);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool DistributeRallods(out float rallodsGiven)
|
||||
{
|
||||
if (_io.TryReadValue(
|
||||
GiveRallodsPrompt,
|
||||
out rallodsGiven,
|
||||
new ValidityTest(v => v <= _rallods, () => GiveRallodsError(_rallods))))
|
||||
{
|
||||
_rallods = (int)(_rallods - rallodsGiven);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool PlantLand(int plantingCost, out float landPlanted)
|
||||
{
|
||||
if (_io.TryReadValue(
|
||||
PlantLandPrompt,
|
||||
out landPlanted,
|
||||
new ValidityTest(v => v <= _countrymen * 2, PlantLandError1),
|
||||
new ValidityTest(v => v <= FarmLand, PlantLandError2(FarmLand)),
|
||||
new ValidityTest(v => v * plantingCost <= _rallods, PlantLandError3(_rallods))))
|
||||
{
|
||||
_rallods -= (int)(landPlanted * plantingCost);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool ControlPollution(out float rallodsSpent)
|
||||
{
|
||||
if (_io.TryReadValue(
|
||||
PollutionPrompt,
|
||||
out rallodsSpent,
|
||||
new ValidityTest(v => v <= _rallods, () => PollutionError(_rallods))))
|
||||
{
|
||||
_rallods = (int)(_rallods - rallodsSpent);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TrySpend(float amount, float landValue)
|
||||
{
|
||||
if (_rallods >= amount)
|
||||
{
|
||||
_rallods -= amount;
|
||||
return true;
|
||||
}
|
||||
|
||||
_arableLand = (int)(_arableLand - (int)(amount - _rallods) / landValue);
|
||||
_rallods = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
public void RemoveTheDead(int deaths) => _countrymen = (int)(_countrymen - deaths);
|
||||
|
||||
public void Migration(int migration) => _countrymen = (int)(_countrymen + migration);
|
||||
|
||||
public void AddWorkers(int newWorkers) => _foreigners = (int)(_foreigners + newWorkers);
|
||||
|
||||
public void SellCrops(int income) => _rallods = (int)(_rallods + income);
|
||||
|
||||
public void EntertainTourists(int income)
|
||||
{
|
||||
PreviousTourismIncome = income;
|
||||
_rallods = (int)(_rallods + income);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
namespace King;
|
||||
|
||||
internal class Game
|
||||
{
|
||||
const int TermOfOffice = 8;
|
||||
|
||||
private readonly IReadWrite _io;
|
||||
private readonly IRandom _random;
|
||||
|
||||
public Game(IReadWrite io, IRandom random)
|
||||
{
|
||||
_io = io;
|
||||
_random = random;
|
||||
}
|
||||
|
||||
public void Play()
|
||||
{
|
||||
_io.Write(Title);
|
||||
|
||||
var reign = SetUpReign();
|
||||
if (reign != null)
|
||||
{
|
||||
while (reign.PlayYear());
|
||||
}
|
||||
|
||||
_io.WriteLine();
|
||||
_io.WriteLine();
|
||||
}
|
||||
|
||||
private Reign? SetUpReign()
|
||||
{
|
||||
var response = _io.ReadString(InstructionsPrompt).ToUpper();
|
||||
|
||||
if (response.Equals("Again", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
return _io.TryReadGameData(_random, out var reign) ? reign : null;
|
||||
}
|
||||
|
||||
if (!response.StartsWith("N", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
_io.Write(InstructionsText(TermOfOffice));
|
||||
}
|
||||
|
||||
_io.WriteLine();
|
||||
return new Reign(_io, _random);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using static King.Resources.Resource;
|
||||
|
||||
namespace King;
|
||||
|
||||
internal static class IOExtensions
|
||||
{
|
||||
internal static bool TryReadGameData(this IReadWrite io, IRandom random, [NotNullWhen(true)] out Reign? reign)
|
||||
{
|
||||
if (io.TryReadValue(SavedYearsPrompt, v => v < Reign.MaxTerm, SavedYearsError(Reign.MaxTerm), out var years) &&
|
||||
io.TryReadValue(SavedTreasuryPrompt, out var rallods) &&
|
||||
io.TryReadValue(SavedCountrymenPrompt, out var countrymen) &&
|
||||
io.TryReadValue(SavedWorkersPrompt, out var workers) &&
|
||||
io.TryReadValue(SavedLandPrompt, v => v is > 1000 and <= 2000, SavedLandError, out var land))
|
||||
{
|
||||
reign = new Reign(io, random, new Country(io, random, rallods, countrymen, workers, land), years + 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
reign = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
internal static bool TryReadValue(this IReadWrite io, string prompt, out float value, params ValidityTest[] tests)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var response = value = io.ReadNumber(prompt);
|
||||
if (response == 0) { return false; }
|
||||
if (tests.All(test => test.IsValid(response, io))) { return true; }
|
||||
}
|
||||
}
|
||||
|
||||
internal static bool TryReadValue(this IReadWrite io, string prompt, out float value)
|
||||
=> io.TryReadValue(prompt, _ => true, "", out value);
|
||||
|
||||
internal static bool TryReadValue(
|
||||
this IReadWrite io,
|
||||
string prompt,
|
||||
Predicate<float> isValid,
|
||||
string error,
|
||||
out float value)
|
||||
=> io.TryReadValue(prompt, isValid, () => error, out value);
|
||||
|
||||
internal static bool TryReadValue(
|
||||
this IReadWrite io,
|
||||
string prompt,
|
||||
Predicate<float> isValid,
|
||||
Func<string> getError,
|
||||
out float value)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
value = io.ReadNumber(prompt);
|
||||
if (value < 0) { return false; }
|
||||
if (isValid(value)) { return true; }
|
||||
|
||||
io.Write(getError());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,4 +6,12 @@
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Resources/*.txt" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\00_Common\dotnet\Games.Common\Games.Common.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
global using Games.Common.IO;
|
||||
global using Games.Common.Randomness;
|
||||
global using King.Resources;
|
||||
global using static King.Resources.Resource;
|
||||
using King;
|
||||
|
||||
new Game(new ConsoleIO(), new RandomNumberGenerator()).Play();
|
||||
@@ -0,0 +1,45 @@
|
||||
namespace King;
|
||||
|
||||
internal class Reign
|
||||
{
|
||||
public const int MaxTerm = 8;
|
||||
|
||||
private readonly IReadWrite _io;
|
||||
private readonly IRandom _random;
|
||||
private readonly Country _country;
|
||||
private float _yearNumber;
|
||||
|
||||
public Reign(IReadWrite io, IRandom random)
|
||||
: this(io, random, new Country(io, random), 1)
|
||||
{
|
||||
}
|
||||
|
||||
public Reign(IReadWrite io, IRandom random, Country country, float year)
|
||||
{
|
||||
_io = io;
|
||||
_random = random;
|
||||
_country = country;
|
||||
_yearNumber = year;
|
||||
}
|
||||
|
||||
public bool PlayYear()
|
||||
{
|
||||
var year = new Year(_country, _random, _io);
|
||||
|
||||
_io.Write(year.Status);
|
||||
|
||||
var result = year.GetPlayerActions() ?? year.EvaluateResults() ?? IsAtEndOfTerm();
|
||||
if (result.IsGameOver)
|
||||
{
|
||||
_io.WriteLine(result.Message);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private Result IsAtEndOfTerm()
|
||||
=> _yearNumber == MaxTerm
|
||||
? Result.GameOver(EndCongratulations(MaxTerm))
|
||||
: Result.Continue;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{0} countrymen died of carbon-monoxide and dust inhalation
|
||||
@@ -0,0 +1 @@
|
||||
{0} countrymen died of starvation
|
||||
@@ -0,0 +1 @@
|
||||
{0} countrymen left the island.
|
||||
@@ -0,0 +1,3 @@
|
||||
also had your left eye gouged out!
|
||||
;have also gained a very bad reputation.
|
||||
;have also been declared national fink.
|
||||
@@ -0,0 +1,10 @@
|
||||
|
||||
|
||||
Congratulations!!!!!!!!!!!!!!!!!!
|
||||
You have successfully completed your {0} year term
|
||||
of office. You were, of course, extremely lucky, but
|
||||
nevertheless, it's quite an achievement. Goodbye and good
|
||||
luck - you'll probably need it if you're the type that
|
||||
plays this game.
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
You have been thrown out of office and are now
|
||||
residing in prison.;
|
||||
You have been assassinated.
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
|
||||
The number of foreign workers has exceeded the number
|
||||
of countrymen. As a minority, they have revolted and
|
||||
taken over the country.
|
||||
{0}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
{0} countrymen died in one year!!!!!
|
||||
due to this extreme mismanagement, you have not only
|
||||
been impeached and thrown out of office, but you
|
||||
{1}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
Money was left over in the treasury which you did
|
||||
not spend. As a result, some of your countrymen died
|
||||
of starvation. The public is enraged and you have
|
||||
been forced to resign.
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
|
||||
Over one third of the population has died since you
|
||||
were elected to office. The people (remaining)
|
||||
hate your guts.
|
||||
{0}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
You were forced to spend {0} rallods on funeral expenses
|
||||
@@ -0,0 +1 @@
|
||||
Think again. You've only got {0} rallods in the treasury.
|
||||
@@ -0,0 +1 @@
|
||||
How many rallods will you distribute among your countrymen
|
||||
@@ -0,0 +1,4 @@
|
||||
Goodbye.
|
||||
(If you wish to continue this game at a later date, answer
|
||||
'again' when asked if you want instructions at the start
|
||||
of the game).
|
||||
@@ -0,0 +1,2 @@
|
||||
you harvested {0} sq. miles of crops.
|
||||
{1}making {2} rallods.
|
||||
@@ -0,0 +1 @@
|
||||
(Due to increased air and water pollution from foreign industry.)
|
||||
@@ -0,0 +1 @@
|
||||
{0} countrymen came to the island.
|
||||
@@ -0,0 +1 @@
|
||||
Do you want instructions
|
||||
@@ -0,0 +1,17 @@
|
||||
|
||||
|
||||
|
||||
Congratulations! You've just been elected Premier of Setats
|
||||
Detinu, a small communist island 30 by 70 miles long. Your
|
||||
job is to decide upon the country's budget and distribute
|
||||
money to your countrymen from the communal treasury.
|
||||
The money system is rallods, and each person needs 100
|
||||
rallods per year to survive. Your country's income comes
|
||||
from farm produce and tourists visiting your magnificent
|
||||
forests, hunting, fishing, etc. Half your land if farm land
|
||||
which also has an excellent mineral content and may be sold
|
||||
to foreign industry (strip mining) who import and support
|
||||
their own workers. Crops cost between 10 and 15 rallods per
|
||||
square mile to plant.
|
||||
Your goal is to complete your {0} year term of office.
|
||||
Good luck!
|
||||
@@ -0,0 +1 @@
|
||||
Of {0} sq. miles planted,
|
||||
@@ -0,0 +1 @@
|
||||
Sorry, but each countryman can only plant 2 sq. miles.
|
||||
@@ -0,0 +1 @@
|
||||
Sorry, but you've only {0} sq. miles of farm land.
|
||||
@@ -0,0 +1 @@
|
||||
Think again, You've only {0} rallods left in the treasury.
|
||||
@@ -0,0 +1 @@
|
||||
How many square miles do you wish to plant
|
||||
@@ -0,0 +1 @@
|
||||
Think again. You only have {0} rallods remaining.
|
||||
@@ -0,0 +1 @@
|
||||
How many rallods do you wish to spend on pollution control
|
||||
@@ -0,0 +1,112 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace King.Resources;
|
||||
|
||||
internal static class Resource
|
||||
{
|
||||
private static bool _sellLandErrorShown;
|
||||
|
||||
public static Stream Title => GetStream();
|
||||
|
||||
public static string InstructionsPrompt => GetString();
|
||||
public static string InstructionsText(int years) => string.Format(GetString(), years);
|
||||
|
||||
public static string Status(
|
||||
float rallods,
|
||||
float countrymen,
|
||||
float workers,
|
||||
float land,
|
||||
float landValue,
|
||||
float plantingCost)
|
||||
=> string.Format(
|
||||
workers == 0 ? StatusWithWorkers : StatusSansWorkers,
|
||||
rallods,
|
||||
(int)countrymen,
|
||||
(int)workers,
|
||||
(int)land,
|
||||
landValue,
|
||||
plantingCost);
|
||||
|
||||
private static string StatusWithWorkers => GetString();
|
||||
private static string StatusSansWorkers => GetString();
|
||||
|
||||
public static string SellLandPrompt => GetString();
|
||||
public static string SellLandError(float farmLand)
|
||||
{
|
||||
var error = string.Format(GetString(), farmLand, _sellLandErrorShown ? "" : SellLandErrorReason);
|
||||
_sellLandErrorShown = true;
|
||||
return error;
|
||||
}
|
||||
private static string SellLandErrorReason => GetString();
|
||||
|
||||
public static string GiveRallodsPrompt => GetString();
|
||||
public static string GiveRallodsError(float rallods) => string.Format(GetString(), rallods);
|
||||
|
||||
public static string PlantLandPrompt => GetString();
|
||||
public static string PlantLandError1 => GetString();
|
||||
public static string PlantLandError2(float farmLand) => string.Format(GetString(), farmLand);
|
||||
public static string PlantLandError3(float rallods) => string.Format(GetString(), rallods);
|
||||
|
||||
public static string PollutionPrompt => GetString();
|
||||
public static string PollutionError(float rallods) => string.Format(GetString(), rallods);
|
||||
|
||||
public static string DeathsStarvation(float deaths) => string.Format(GetString(), (int)deaths);
|
||||
public static string DeathsPollution(int deaths) => string.Format(GetString(), deaths);
|
||||
public static string FuneralExpenses(int expenses) => string.Format(GetString(), expenses);
|
||||
public static string InsufficientReserves => GetString();
|
||||
|
||||
public static string WorkerMigration(int newWorkers) => string.Format(GetString(), newWorkers);
|
||||
public static string Migration(int migration)
|
||||
=> string.Format(migration < 0 ? Emigration : Immigration, Math.Abs(migration));
|
||||
public static string Emigration => GetString();
|
||||
public static string Immigration => GetString();
|
||||
|
||||
public static string LandPlanted(float landPlanted)
|
||||
=> landPlanted > 0 ? string.Format(GetString(), (int)landPlanted) : "";
|
||||
public static string Harvest(int yield, int income, bool hasIndustry)
|
||||
=> string.Format(GetString(), yield, HarvestReason(hasIndustry), income);
|
||||
private static string HarvestReason(bool hasIndustry) => hasIndustry ? GetString() : "";
|
||||
|
||||
public static string TourismEarnings(int income) => string.Format(GetString(), income);
|
||||
public static string TourismDecrease(IRandom random) => string.Format(GetString(), TourismReason(random));
|
||||
private static string TourismReason(IRandom random) => GetStrings()[random.Next(5)];
|
||||
|
||||
private static string EndAlso(IRandom random)
|
||||
=> random.Next(10) switch
|
||||
{
|
||||
<= 3 => GetStrings()[0],
|
||||
<= 6 => GetStrings()[1],
|
||||
_ => GetStrings()[2]
|
||||
};
|
||||
|
||||
public static string EndCongratulations(int termLength) => string.Format(GetString(), termLength);
|
||||
private static string EndConsequences(IRandom random) => GetStrings()[random.Next(2)];
|
||||
public static string EndForeignWorkers(IRandom random) => string.Format(GetString(), EndConsequences(random));
|
||||
public static string EndManyDead(int deaths, IRandom random) => string.Format(GetString(), deaths, EndAlso(random));
|
||||
public static string EndMoneyLeftOver() => GetString();
|
||||
public static string EndOneThirdDead(IRandom random) => string.Format(GetString(), EndConsequences(random));
|
||||
|
||||
public static string SavedYearsPrompt => GetString();
|
||||
public static string SavedYearsError(int years) => string.Format(GetString(), years);
|
||||
public static string SavedTreasuryPrompt => GetString();
|
||||
public static string SavedCountrymenPrompt => GetString();
|
||||
public static string SavedWorkersPrompt => GetString();
|
||||
public static string SavedLandPrompt => GetString();
|
||||
public static string SavedLandError => GetString();
|
||||
|
||||
public static string Goodbye => GetString();
|
||||
|
||||
private static string[] GetStrings([CallerMemberName] string? name = null) => GetString(name).Split(';');
|
||||
|
||||
private static string GetString([CallerMemberName] string? name = null)
|
||||
{
|
||||
using var stream = GetStream(name);
|
||||
using var reader = new StreamReader(stream);
|
||||
return reader.ReadToEnd();
|
||||
}
|
||||
|
||||
private static Stream GetStream([CallerMemberName] string? name = null) =>
|
||||
Assembly.GetExecutingAssembly().GetManifestResourceStream($"{typeof(Resource).Namespace}.{name}.txt")
|
||||
?? throw new Exception($"Could not find embedded resource stream '{name}'.");
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
How many countrymen
|
||||
@@ -0,0 +1,2 @@
|
||||
Come on, you started with 1000 sq. miles of farm land
|
||||
and 10,000 sq. miles of forest land
|
||||
@@ -0,0 +1 @@
|
||||
How many square miles of land
|
||||
@@ -0,0 +1 @@
|
||||
How much did you have in the treasury
|
||||
@@ -0,0 +1 @@
|
||||
How many workers
|
||||
@@ -0,0 +1 @@
|
||||
Come on, your term in office is only {0} years.
|
||||
@@ -0,0 +1 @@
|
||||
How many years had you been in office when interrupted
|
||||
@@ -0,0 +1,2 @@
|
||||
*** Think again. You only have {0} square miles of farm land.
|
||||
{1}
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
(Foreign industry will only buy farm land because
|
||||
forest land is uneconomical to strip mine due to trees,
|
||||
thicker top soil, etc.)
|
||||
@@ -0,0 +1 @@
|
||||
How many square miles do you wish to sell to industry
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
You now have {0} rallods in the treasury.
|
||||
{1} countrymen, and {3} sq. miles of land.
|
||||
This year industry will buy land for {4} rallods per square mile.
|
||||
Land currently costs {5} rallods per square mile to plant.
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
You now have {0} rallods in the treasury.
|
||||
{1} countrymen, {2} foreign workers and {3} sq. miles of land.
|
||||
This year industry will buy land for {4} rallods per square mile.
|
||||
Land currently costs {5} rallods per square mile to plant.
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
King
|
||||
Creative Computing Morristown, New Jersey
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
Decrease because {0}
|
||||
@@ -0,0 +1 @@
|
||||
You made {0} rallods from tourist trade.
|
||||
@@ -0,0 +1,5 @@
|
||||
fish population has dwindled due to water pollution.
|
||||
;air pollution is killing game bird population.
|
||||
;mineral baths are being ruined by water pollution.
|
||||
;unpleasant smog is discouraging sun bathers.
|
||||
;hotels are looking shabby due to smog grit.
|
||||
@@ -0,0 +1 @@
|
||||
{0} workers came to the country and
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace King;
|
||||
|
||||
internal record struct Result (bool IsGameOver, string Message)
|
||||
{
|
||||
internal static Result GameOver(string message) => new(true, message);
|
||||
internal static Result Continue => new(false, "");
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
namespace King;
|
||||
|
||||
internal class ValidityTest
|
||||
{
|
||||
private readonly Predicate<float> _isValid;
|
||||
private readonly Func<string> _getError;
|
||||
|
||||
public ValidityTest(Predicate<float> isValid, string error)
|
||||
: this(isValid, () => error)
|
||||
{
|
||||
}
|
||||
|
||||
public ValidityTest(Predicate<float> isValid, Func<string> getError)
|
||||
{
|
||||
_isValid = isValid;
|
||||
_getError = getError;
|
||||
}
|
||||
|
||||
public bool IsValid(float value, IReadWrite io)
|
||||
{
|
||||
if (_isValid(value)) { return true; }
|
||||
|
||||
io.Write(_getError());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
using System.Text;
|
||||
|
||||
namespace King;
|
||||
|
||||
internal class Year
|
||||
{
|
||||
private readonly Country _country;
|
||||
private readonly IRandom _random;
|
||||
private readonly IReadWrite _io;
|
||||
private readonly int _plantingCost;
|
||||
private readonly int _landValue;
|
||||
|
||||
private float _landSold;
|
||||
private float _rallodsDistributed;
|
||||
private float _landPlanted;
|
||||
private float _pollutionControlCost;
|
||||
|
||||
private float _citizenSupport;
|
||||
private int _deaths;
|
||||
private float _starvationDeaths;
|
||||
private int _pollutionDeaths;
|
||||
private int _migration;
|
||||
|
||||
public Year(Country country, IRandom random, IReadWrite io)
|
||||
{
|
||||
_country = country;
|
||||
_random = random;
|
||||
_io = io;
|
||||
|
||||
_plantingCost = random.Next(10, 15);
|
||||
_landValue = random.Next(95, 105);
|
||||
}
|
||||
|
||||
public string Status => _country.GetStatus(_landValue, _plantingCost);
|
||||
|
||||
public Result? GetPlayerActions()
|
||||
{
|
||||
var playerSoldLand = _country.SellLand(_landValue, out _landSold);
|
||||
var playerDistributedRallods = _country.DistributeRallods(out _rallodsDistributed);
|
||||
var playerPlantedLand = _country.HasRallods && _country.PlantLand(_plantingCost, out _landPlanted);
|
||||
var playerControlledPollution = _country.HasRallods && _country.ControlPollution(out _pollutionControlCost);
|
||||
|
||||
return playerSoldLand || playerDistributedRallods || playerPlantedLand || playerControlledPollution
|
||||
? null
|
||||
: Result.GameOver(Goodbye);
|
||||
}
|
||||
|
||||
public Result? EvaluateResults()
|
||||
{
|
||||
var rallodsUnspent = _country.Rallods;
|
||||
|
||||
_io.WriteLine();
|
||||
_io.WriteLine();
|
||||
|
||||
return EvaluateDeaths()
|
||||
?? EvaluateMigration()
|
||||
?? EvaluateAgriculture()
|
||||
?? EvaluateTourism()
|
||||
?? DetermineResult(rallodsUnspent);
|
||||
}
|
||||
|
||||
public Result? EvaluateDeaths()
|
||||
{
|
||||
var supportedCountrymen = _rallodsDistributed / 100;
|
||||
_citizenSupport = supportedCountrymen - _country.Countrymen;
|
||||
_starvationDeaths = -_citizenSupport;
|
||||
if (_starvationDeaths > 0)
|
||||
{
|
||||
if (supportedCountrymen < 50) { return Result.GameOver(EndOneThirdDead(_random)); }
|
||||
_io.WriteLine(DeathsStarvation(_starvationDeaths));
|
||||
}
|
||||
|
||||
var pollutionControl = _pollutionControlCost >= 25 ? _pollutionControlCost / 25 : 1;
|
||||
_pollutionDeaths = (int)(_random.Next((int)_country.IndustryLand) / pollutionControl);
|
||||
if (_pollutionDeaths > 0)
|
||||
{
|
||||
_io.WriteLine(DeathsPollution(_pollutionDeaths));
|
||||
}
|
||||
|
||||
_deaths = (int)(_starvationDeaths + _pollutionDeaths);
|
||||
if (_deaths > 0)
|
||||
{
|
||||
var funeralCosts = _deaths * 9;
|
||||
_io.WriteLine(FuneralExpenses(funeralCosts));
|
||||
|
||||
if (!_country.TrySpend(funeralCosts, _landValue))
|
||||
{
|
||||
_io.WriteLine(InsufficientReserves);
|
||||
}
|
||||
|
||||
_country.RemoveTheDead(_deaths);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Result? EvaluateMigration()
|
||||
{
|
||||
if (_landSold > 0)
|
||||
{
|
||||
var newWorkers = (int)(_landSold + _random.NextFloat(10) - _random.NextFloat(20));
|
||||
if (!_country.HasWorkers) { newWorkers += 20; }
|
||||
_io.Write(WorkerMigration(newWorkers));
|
||||
_country.AddWorkers(newWorkers);
|
||||
}
|
||||
|
||||
_migration =
|
||||
(int)(_citizenSupport / 10 + _pollutionControlCost / 25 - _country.IndustryLand / 50 - _pollutionDeaths / 2);
|
||||
_io.WriteLine(Migration(_migration));
|
||||
_country.Migration(_migration);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Result? EvaluateAgriculture()
|
||||
{
|
||||
var ruinedCrops = (int)Math.Min(_country.IndustryLand * (_random.NextFloat() + 1.5f) / 2, _landPlanted);
|
||||
var yield = (int)(_landPlanted - ruinedCrops);
|
||||
var income = (int)(yield * _landValue / 2f);
|
||||
|
||||
_io.Write(LandPlanted(_landPlanted));
|
||||
_io.Write(Harvest(yield, income, _country.IndustryLand > 0));
|
||||
|
||||
_country.SellCrops(income);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Result? EvaluateTourism()
|
||||
{
|
||||
var reputationValue = (int)((_country.Countrymen - _migration) * 22 + _random.NextFloat(500));
|
||||
var industryAdjustment = (int)(_country.IndustryLand * 15);
|
||||
var tourismIncome = Math.Abs(reputationValue - industryAdjustment);
|
||||
|
||||
_io.WriteLine(TourismEarnings(tourismIncome));
|
||||
if (industryAdjustment > 0 && tourismIncome < _country.PreviousTourismIncome)
|
||||
{
|
||||
_io.Write(TourismDecrease(_random));
|
||||
}
|
||||
|
||||
_country.EntertainTourists(tourismIncome);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Result? DetermineResult(float rallodsUnspent)
|
||||
{
|
||||
if (_deaths > 200) { return Result.GameOver(EndManyDead(_deaths, _random)); }
|
||||
if (_country.Countrymen < 343) { return Result.GameOver(EndOneThirdDead(_random)); }
|
||||
if (rallodsUnspent / 100 > 5 && _starvationDeaths >= 2) { return Result.GameOver(EndMoneyLeftOver()); }
|
||||
if (_country.Workers > _country.Countrymen) { return Result.GameOver(EndForeignWorkers(_random)); }
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -48,3 +48,17 @@ http://www.vintage-basic.net/games.html
|
||||
#### Porting Notes
|
||||
|
||||
(please note any difficulties or challenges in porting here)
|
||||
|
||||
Note: The original program has a bug. The instructions say that if both players
|
||||
enter the same cell that the cell is set to 0 or empty. However, the original
|
||||
Basic program tells the player "ILLEGAL COORDINATES" and makes another cell be entered,
|
||||
giving a slightly unfair advantage to the 2nd player.
|
||||
|
||||
The Perl verson of the program fixes the bug and follows the instructions.
|
||||
|
||||
Note: The original code had "GOTO 800" but label 800 didn't exist; it should have gone to label 999.
|
||||
The Basic program has been fixed.
|
||||
|
||||
Note: The Basic program is written to assume it's being played on a Teletype, i.e. output is printed
|
||||
on paper. To play on a terminal the input must not be echoed, which can be a challenge to do portably
|
||||
and without tying the solution to a specific OS. Some versions may tell you how to do this, others might not.
|
||||
|
||||
@@ -60,8 +60,8 @@
|
||||
571 IF M3=0 THEN B=1: GOTO 575
|
||||
572 IF M2=0 THEN B=2: GOTO 575
|
||||
573 GOTO 580
|
||||
574 PRINT: PRINT "A DRAW":GOTO 800
|
||||
575 PRINT: PRINT "PLAYER";B;"IS THE WINNER":GOTO 800
|
||||
574 PRINT: PRINT "A DRAW":GOTO 999
|
||||
575 PRINT: PRINT "PLAYER";B;"IS THE WINNER":GOTO 999
|
||||
580 FOR B=1 TO 2: PRINT: PRINT: PRINT "PLAYER";B;: GOSUB 700
|
||||
581 IF B=99 THEN 560
|
||||
582 NEXT B
|
||||
|
||||
@@ -1,3 +1,21 @@
|
||||
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
|
||||
|
||||
Conversion to [Perl](https://www.perl.org/)
|
||||
|
||||
Note: The original program has a bug (see the README in the above dir). This Perl version fixes it.
|
||||
|
||||
Note: For input, the X value is to the right while the Y value is down.
|
||||
Therefore, the top right cell is "5,1", not "1,5".
|
||||
|
||||
The original program was made to be played on a Teletype, i.e. a printer on paper.
|
||||
That allowed the program to "black out" the input line to hide a user's input from his/her
|
||||
opponent, assuming the opponent was at least looking away. To do the equivalent on a
|
||||
terminal would require a Perl module that isn't installed by default (i.e. it is not
|
||||
part of CORE and would also require a C compiler to install), nor do I want to issue a
|
||||
shell command to "stty" to hide the input because that would restrict the game to Linux/Unix.
|
||||
This means it would have to be played on the honor system.
|
||||
|
||||
However, if you want to try it, install the module "Term::ReadKey" ("sudo cpan -i Term::ReadKey"
|
||||
if on Linux/Unix and you have root access). If the code finds that module, it will automatically
|
||||
use it and hide the input ... and restore echoing input again when the games ends. If the module
|
||||
is not found, input will be visible.
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
# Life_For_Two program in Perl
|
||||
# Required extensive restructuring to remove all of the GOTO's.
|
||||
# Translated by Kevin Brannen (kbrannen)
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# try to load module to hide input, set RKey to true if found
|
||||
my $Rkey = eval { require Term::ReadKey } // 0;
|
||||
END { Term::ReadKey::ReadMode('normal') if ($Rkey); }
|
||||
|
||||
# globals
|
||||
my @Board; # 2D board
|
||||
my @X; # ?
|
||||
my @Y; # ?
|
||||
my $Player; # 1 or 2
|
||||
my $M2 = 0; # ?
|
||||
my $M3 = 0; # ?
|
||||
|
||||
# add 0 on front to make data 1 based
|
||||
my @K = (0,3,102,103,120,130,121,112,111,12,21,30,1020,1030,1011,1021,1003,1002,1012);
|
||||
my @A = (0,-1,0,1,0,0,-1,0,1,-1,-1,1,-1,-1,1,1,1);
|
||||
|
||||
print "\n";
|
||||
print " " x 33, "LIFE2\n";
|
||||
print " " x 15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n\n\n";
|
||||
print " " x 10, "U.B. LIFE GAME\n";
|
||||
for my $j (1 .. 5) { for my $k (1 .. 5) { $Board[$j][$k] = 0; } }
|
||||
|
||||
for (1 .. 2)
|
||||
{
|
||||
$Player = $_; # if we make $Player the loop var, the global isn't set
|
||||
my $p1 = ($Player == 2) ? 30 : 3;
|
||||
print "\nPLAYER $Player - 3 LIVE PIECES.\n";
|
||||
for (1 .. 3)
|
||||
{
|
||||
get_input();
|
||||
$Board[$X[$Player]][$Y[$Player]] = $p1 if ($Player != 99);
|
||||
}
|
||||
}
|
||||
print_board(); # print board after initial input
|
||||
|
||||
while (1)
|
||||
{
|
||||
print "\n";
|
||||
calc_board(); # calc new positions
|
||||
print_board(); # print current board after calc
|
||||
|
||||
if ($M2 == 0 && $M3 == 0)
|
||||
{
|
||||
print "\nA DRAW\n";
|
||||
last;
|
||||
}
|
||||
if ($M3 == 0)
|
||||
{
|
||||
win(1);
|
||||
last;
|
||||
}
|
||||
if ($M2 == 0)
|
||||
{
|
||||
win(2);
|
||||
last;
|
||||
}
|
||||
|
||||
for (1 .. 2)
|
||||
{
|
||||
$Player = $_; # if we make $Player the loop var, the global isn't set
|
||||
print "\n\nPLAYER $Player ";
|
||||
get_input();
|
||||
last if ($Player == 99);
|
||||
}
|
||||
next if ($Player == 99);
|
||||
|
||||
$Board[$X[1]][$Y[1]] = 100;
|
||||
$Board[$X[2]][$Y[2]] = 1000;
|
||||
}
|
||||
exit(0);
|
||||
|
||||
###########################################################
|
||||
|
||||
sub win
|
||||
{
|
||||
my $p = shift;
|
||||
print "\nPLAYER $p IS THE WINNER\n";
|
||||
}
|
||||
|
||||
sub calc_board
|
||||
{
|
||||
for my $j (1 .. 5)
|
||||
{
|
||||
for my $k (1 .. 5)
|
||||
{
|
||||
if ($Board[$j][$k] > 99)
|
||||
{
|
||||
$Player = $Board[$j][$k] > 999 ? 10 : 1;
|
||||
for (my $c = 1 ; $c <= 15 ; $c += 2)
|
||||
{
|
||||
$Board[$j+$A[$c]][$k+$A[$c+1]] = ($Board[$j+$A[$c]][$k+$A[$c+1]] // 0) + $Player;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub print_board
|
||||
{
|
||||
$M2 = 0;
|
||||
$M3 = 0;
|
||||
for my $j (0 .. 6)
|
||||
{
|
||||
print "\n";
|
||||
for my $k (0 .. 6)
|
||||
{
|
||||
if ($j != 0 && $j != 6)
|
||||
{
|
||||
if ($k != 0 && $k != 6)
|
||||
{
|
||||
print_row($j, $k);
|
||||
next;
|
||||
}
|
||||
if ($j == 6)
|
||||
{
|
||||
print "0\n";
|
||||
return;
|
||||
}
|
||||
print " $j ";
|
||||
}
|
||||
else
|
||||
{
|
||||
if ($k == 6)
|
||||
{
|
||||
print " 0 ";
|
||||
last;
|
||||
}
|
||||
print " $k ";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub print_row
|
||||
{
|
||||
my ($j, $k) = @_;
|
||||
|
||||
if ($Board[$j][$k] >= 3)
|
||||
{
|
||||
my $c;
|
||||
for $c (1 .. 18)
|
||||
{
|
||||
if ($Board[$j][$k] == $K[$c])
|
||||
{
|
||||
if ($c <= 9)
|
||||
{
|
||||
$Board[$j][$k] = 100;
|
||||
$M2++;
|
||||
print " * ";
|
||||
}
|
||||
else
|
||||
{
|
||||
$Board[$j][$k] = 1000;
|
||||
$M3++;
|
||||
print " # ";
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
$Board[$j][$k] = 0;
|
||||
print " ";
|
||||
}
|
||||
|
||||
sub get_input
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
print "X,Y\n";
|
||||
my $ans;
|
||||
|
||||
if ($Rkey)
|
||||
{
|
||||
# code to hide input
|
||||
Term::ReadKey::ReadMode('noecho');
|
||||
$ans = Term::ReadKey::ReadLine(0);
|
||||
Term::ReadKey::ReadMode('restore');
|
||||
print "\n"; # do this since the one entered was hidden
|
||||
}
|
||||
else
|
||||
{
|
||||
# normal, input visible
|
||||
chomp($ans = <>);
|
||||
}
|
||||
|
||||
($Y[$Player], $X[$Player]) = split(/[,\s]+/, $ans, 2);
|
||||
if ($X[$Player] > 5 || $X[$Player] < 1 || $Y[$Player] > 5 || $Y[$Player] < 1)
|
||||
{
|
||||
print "ILLEGAL COORDS. RETYPE\n";
|
||||
next;
|
||||
}
|
||||
# this tells you the cell was already taken not zero it out, bug!
|
||||
#if ($Board[$X[$Player]][$Y[$Player]] != 0)
|
||||
#{
|
||||
# print "ILLEGAL COORDS. RETYPE\n";
|
||||
# next;
|
||||
#}
|
||||
last;
|
||||
}
|
||||
|
||||
return if ($Player == 1 || $X[1] != $X[2] || $Y[1] != $Y[2]);
|
||||
|
||||
print "SAME COORD. SET TO 0\n";
|
||||
$Board[$X[$Player]+1][$Y[$Player]+1] = 0;
|
||||
$Player = 99;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
namespace Queen;
|
||||
|
||||
internal class Computer
|
||||
{
|
||||
private static readonly HashSet<Position> _randomiseFrom = new() { 41, 44, 73, 75, 126, 127 };
|
||||
private static readonly HashSet<Position> _desirable = new() { 73, 75, 126, 127, 158 };
|
||||
private readonly IRandom _random;
|
||||
|
||||
public Computer(IRandom random)
|
||||
{
|
||||
_random = random;
|
||||
}
|
||||
|
||||
public Position GetMove(Position from)
|
||||
=> from + (_randomiseFrom.Contains(from) ? _random.NextMove() : FindMove(from));
|
||||
|
||||
private Move FindMove(Position from)
|
||||
{
|
||||
for (int i = 7; i > 0; i--)
|
||||
{
|
||||
if (IsOptimal(Move.Left, out var move)) { return move; }
|
||||
if (IsOptimal(Move.Down, out move)) { return move; }
|
||||
if (IsOptimal(Move.DownLeft, out move)) { return move; }
|
||||
|
||||
bool IsOptimal(Move direction, out Move move)
|
||||
{
|
||||
move = direction * i;
|
||||
return _desirable.Contains(from + move);
|
||||
}
|
||||
}
|
||||
|
||||
return _random.NextMove();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
namespace Queen;
|
||||
|
||||
internal class Game
|
||||
{
|
||||
private readonly IReadWrite _io;
|
||||
private readonly IRandom _random;
|
||||
private readonly Computer _computer;
|
||||
|
||||
public Game(IReadWrite io, IRandom random)
|
||||
{
|
||||
_io = io;
|
||||
_random = random;
|
||||
_computer = new Computer(random);
|
||||
}
|
||||
|
||||
internal void PlaySeries()
|
||||
{
|
||||
_io.Write(Streams.Title);
|
||||
if (_io.ReadYesNo(Prompts.Instructions)) { _io.Write(Streams.Instructions); }
|
||||
|
||||
while (true)
|
||||
{
|
||||
var result = PlayGame();
|
||||
_io.Write(result switch
|
||||
{
|
||||
Result.HumanForfeits => Streams.Forfeit,
|
||||
Result.HumanWins => Streams.Congratulations,
|
||||
Result.ComputerWins => Streams.IWin,
|
||||
_ => throw new InvalidOperationException($"Unexpected result {result}")
|
||||
});
|
||||
|
||||
if (!_io.ReadYesNo(Prompts.Anyone)) { break; }
|
||||
}
|
||||
|
||||
_io.Write(Streams.Thanks);
|
||||
}
|
||||
|
||||
private Result PlayGame()
|
||||
{
|
||||
_io.Write(Streams.Board);
|
||||
var humanPosition = _io.ReadPosition(Prompts.Start, p => p.IsStart, Streams.IllegalStart, repeatPrompt: true);
|
||||
if (humanPosition.IsZero) { return Result.HumanForfeits; }
|
||||
|
||||
while (true)
|
||||
{
|
||||
var computerPosition = _computer.GetMove(humanPosition);
|
||||
_io.Write(Strings.ComputerMove(computerPosition));
|
||||
if (computerPosition.IsEnd) { return Result.ComputerWins; }
|
||||
|
||||
humanPosition = _io.ReadPosition(Prompts.Move, p => (p - computerPosition).IsValid, Streams.IllegalMove);
|
||||
if (humanPosition.IsZero) { return Result.HumanForfeits; }
|
||||
if (humanPosition.IsEnd) { return Result.HumanWins; }
|
||||
}
|
||||
}
|
||||
|
||||
private enum Result { ComputerWins, HumanWins, HumanForfeits };
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
namespace Queen;
|
||||
|
||||
internal static class IOExtensions
|
||||
{
|
||||
internal static bool ReadYesNo(this IReadWrite io, string prompt)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var answer = io.ReadString(prompt).ToLower();
|
||||
if (answer == "yes") { return true; }
|
||||
if (answer == "no") { return false; }
|
||||
|
||||
io.Write(Streams.YesOrNo);
|
||||
}
|
||||
}
|
||||
|
||||
internal static Position ReadPosition(
|
||||
this IReadWrite io,
|
||||
string prompt,
|
||||
Predicate<Position> isValid,
|
||||
Stream error,
|
||||
bool repeatPrompt = false)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
var response = io.ReadNumber(prompt);
|
||||
var number = (int)response;
|
||||
var position = new Position(number);
|
||||
if (number == response && (position.IsZero || isValid(position)))
|
||||
{
|
||||
return position;
|
||||
}
|
||||
|
||||
io.Write(error);
|
||||
if (!repeatPrompt) { prompt = ""; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace Queen;
|
||||
|
||||
internal record struct Move(int Diagonal, int Row)
|
||||
{
|
||||
public static readonly Move Left = new(1, 0);
|
||||
public static readonly Move DownLeft = new(2, 1);
|
||||
public static readonly Move Down = new(1, 1);
|
||||
|
||||
public bool IsValid => Diagonal > 0 && (IsLeft || IsDown || IsDownLeft);
|
||||
private bool IsLeft => Row == 0;
|
||||
private bool IsDown => Row == Diagonal;
|
||||
private bool IsDownLeft => Row * 2 == Diagonal;
|
||||
|
||||
public static Move operator *(Move move, int scale) => new(move.Diagonal * scale, move.Row * scale);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
namespace Queen;
|
||||
|
||||
internal record struct Position(int Diagonal, int Row)
|
||||
{
|
||||
public static readonly Position Zero = new(0);
|
||||
|
||||
public Position(int number)
|
||||
: this(Diagonal: number / 10, Row: number % 10)
|
||||
{
|
||||
}
|
||||
|
||||
public bool IsZero => Row == 0 && Diagonal == 0;
|
||||
public bool IsStart => Row == 1 || Row == Diagonal;
|
||||
public bool IsEnd => Row == 8 && Diagonal == 15;
|
||||
|
||||
public override string ToString() => $"{Diagonal}{Row}";
|
||||
|
||||
public static implicit operator Position(int value) => new(value);
|
||||
|
||||
public static Position operator +(Position position, Move move)
|
||||
=> new(Diagonal: position.Diagonal + move.Diagonal, Row: position.Row + move.Row);
|
||||
public static Move operator -(Position to, Position from)
|
||||
=> new(Diagonal: to.Diagonal - from.Diagonal, Row: to.Row - from.Row);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
global using Games.Common.IO;
|
||||
global using Games.Common.Randomness;
|
||||
global using static Queen.Resources.Resource;
|
||||
|
||||
using Queen;
|
||||
|
||||
new Game(new ConsoleIO(), new RandomNumberGenerator()).PlaySeries();
|
||||
@@ -6,4 +6,12 @@
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Resources/*.txt" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\00_Common\dotnet\Games.Common\Games.Common.csproj" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Queen;
|
||||
|
||||
internal static class RandomExtensions
|
||||
{
|
||||
internal static Move NextMove(this IRandom random)
|
||||
=> random.NextFloat() switch
|
||||
{
|
||||
> 0.6F => Move.Down,
|
||||
> 0.3F => Move.DownLeft,
|
||||
_ => Move.Left
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
Anyone else care to try
|
||||
@@ -0,0 +1,27 @@
|
||||
|
||||
|
||||
81 71 61 51 41 31 21 11
|
||||
|
||||
|
||||
92 82 72 62 52 42 32 22
|
||||
|
||||
|
||||
103 93 83 73 63 53 43 33
|
||||
|
||||
|
||||
114 104 94 84 74 64 54 44
|
||||
|
||||
|
||||
125 115 105 95 85 75 65 55
|
||||
|
||||
|
||||
136 126 116 106 96 86 76 66
|
||||
|
||||
|
||||
147 137 127 117 107 97 87 77
|
||||
|
||||
|
||||
158 148 138 128 118 108 98 88
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
Computer moves to square {0}
|
||||
@@ -0,0 +1,7 @@
|
||||
|
||||
C O N G R A T U L A T I O N S . . .
|
||||
|
||||
You have won--very well played.
|
||||
It looks like I have met my match.
|
||||
Thanks for playing--I can't win all the time.
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
|
||||
It looks like I have won by forfeit.
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
Nice try, but it looks like I have won.
|
||||
Thanks for playing.
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
Y O U C H E A T . . . Try again
|
||||
@@ -0,0 +1,3 @@
|
||||
Please read the instructions again.
|
||||
You have begun illegally.
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
We are going to play a game based on one of the chess
|
||||
moves. Our queen will be able to move only to the left,
|
||||
down, or diagonally down and to the left.
|
||||
|
||||
The object of the game is to place the queen in the lower
|
||||
left hand square by alternating moves between you and the
|
||||
computer. The first one to place the queen there wins.
|
||||
|
||||
You go first and place the queen in any one of the squares
|
||||
on the top row or right hand column.
|
||||
That will be your first move.
|
||||
We alternate moves.
|
||||
You may forfeit by typing '0' as your move.
|
||||
Be sure to press the return key after each response.
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
Do you want instructions
|
||||
@@ -0,0 +1 @@
|
||||
What is your move
|
||||
@@ -0,0 +1,47 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Queen.Resources;
|
||||
|
||||
internal static class Resource
|
||||
{
|
||||
internal static class Streams
|
||||
{
|
||||
public static Stream Title => GetStream();
|
||||
public static Stream Instructions => GetStream();
|
||||
public static Stream YesOrNo => GetStream();
|
||||
public static Stream Board => GetStream();
|
||||
public static Stream IllegalStart => GetStream();
|
||||
public static Stream IllegalMove => GetStream();
|
||||
public static Stream Forfeit => GetStream();
|
||||
public static Stream IWin => GetStream();
|
||||
public static Stream Congratulations => GetStream();
|
||||
public static Stream Thanks => GetStream();
|
||||
}
|
||||
|
||||
internal static class Prompts
|
||||
{
|
||||
public static string Instructions => GetPrompt();
|
||||
public static string Start => GetPrompt();
|
||||
public static string Move => GetPrompt();
|
||||
public static string Anyone => GetPrompt();
|
||||
}
|
||||
|
||||
internal static class Strings
|
||||
{
|
||||
public static string ComputerMove(Position position) => string.Format(GetString(), position);
|
||||
}
|
||||
|
||||
private static string GetPrompt([CallerMemberName] string? name = null) => GetString($"{name}Prompt");
|
||||
|
||||
private static string GetString([CallerMemberName] string? name = null)
|
||||
{
|
||||
using var stream = GetStream(name);
|
||||
using var reader = new StreamReader(stream);
|
||||
return reader.ReadToEnd();
|
||||
}
|
||||
|
||||
private static Stream GetStream([CallerMemberName] string? name = null) =>
|
||||
Assembly.GetExecutingAssembly().GetManifestResourceStream($"{typeof(Resource).Namespace}.{name}.txt")
|
||||
?? throw new Exception($"Could not find embedded resource stream '{name}'.");
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
Where would you like to start
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user