mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2026-01-02 00:00:14 -08:00
196 lines
7.5 KiB
Perl
Executable File
196 lines
7.5 KiB
Perl
Executable File
#!/usr/bin/perl
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
# global variable declaration (just the user's score)
|
|
my($Y) = 0;
|
|
|
|
|
|
# yes_input is a subroutine that returns a true value
|
|
# if the first character of the user's input from STDIN
|
|
# is a 'Y' (checking case-insensitively via regex)
|
|
sub yes_input {
|
|
chomp(my $A = <STDIN>);
|
|
return $A =~ m/^Y/i;
|
|
}
|
|
|
|
# Main code starts here.
|
|
|
|
print ' 'x32; print "Bagels\n";
|
|
print ' 'x14; print "Creative Computing Morristown, New Jersey\n\n";
|
|
|
|
# Provide instructions if requested
|
|
print "Would you like the rules (yes or no)? ";
|
|
if (yes_input()) {
|
|
|
|
# Print out the instructions using a here doc
|
|
# (useful for large blocks of text)
|
|
print <<HERE;
|
|
|
|
I am thinking of a three-digit number. Try to guess
|
|
my number and I will give you clues as follows:
|
|
PICO - one digit correct but in the wrong position
|
|
FERMI - one digit correct and in the right place
|
|
BAGELS - no digits correct
|
|
HERE
|
|
}
|
|
|
|
# There are three code loops here. The outermost one, labeled 'PLAY',
|
|
# performs one game start to finish every time it runs.
|
|
# The next loop is a for loop that implements the required 20 guesses.
|
|
# And the innermost loop, labeled 'CHECK', does the game logic of
|
|
# checking the user's guess to make sure it's valid, figuring out
|
|
# the response, and rewarding the user if they won.
|
|
|
|
PLAY: while (1) {
|
|
|
|
# The computer's number is three randomly selected unique digits.
|
|
# To generate the number, 3 digits are randomly splice()d out
|
|
# of the @DIGITS array, which is initialized with the digits 0 to 9.
|
|
my @DIGITS = (0..9);
|
|
my @A = ();
|
|
push @A, splice(@DIGITS, int(rand(scalar @DIGITS)), 1) for 1..3;
|
|
|
|
print "\n";
|
|
print "O.K. I have a number in mind.\n";
|
|
|
|
for my $i (1..20) {
|
|
|
|
# Note that the CHECK loop will automatically loop to ask
|
|
# for the user's input, and it's only with the 'next' and
|
|
# 'last' statements that this loop is exited. So if the
|
|
# user's input is somehow invalid, we just print out an
|
|
# appropriate message. Execution will by default return
|
|
# to the start of the loop, and only leaves the loop if
|
|
# the user's input is successfully parsed and accepted
|
|
# (look for the 'next' and 'last' statements below).
|
|
|
|
CHECK: while (1) {
|
|
printf("Guess # %2d ? ", $i);
|
|
chomp(my $A = <STDIN>);
|
|
|
|
# Use a regex to check if the user entered three digits,
|
|
# and complain if they did not.
|
|
if ($A !~ m{^(\d)(\d)(\d)$}) {
|
|
print "What?\n";
|
|
# Program execution will now pass through the rest
|
|
# of the logic below and loop back to the start
|
|
# of the CHECK loop.
|
|
} else {
|
|
|
|
# As a side effect of the regex match above, the
|
|
# $1, $2, and $3 variables are each of the digits
|
|
# of the user's guess. Perl treats numbers and
|
|
# strings interchangably, so we will not have to
|
|
# use the ASC() conversion functions required
|
|
# by the BASIC program.
|
|
my @B = ($1,$2,$3);
|
|
|
|
# Check for duplicate digits in the user's guess
|
|
if ($B[0] == $B[1] || $B[0] == $B[2] || $B[1] == $B[2]) {
|
|
print "Oh, I forgot to tell you that the number I have in mind\n";
|
|
print "has no two digits the same.\n";
|
|
# Again, no further action is required here
|
|
# because we want to loop back to the start
|
|
# of the CHECK loop.
|
|
} else {
|
|
|
|
# This code block is the actual game logic, so
|
|
# it's executed only if the user's input has
|
|
# passed all the above checks.
|
|
my($C,$D);
|
|
$C = 0; $D = 0;
|
|
|
|
# As a replacement for the original BASIC logic,
|
|
# this for loop works over an anonymous array of
|
|
# pairs of digits to compare the computer's and
|
|
# the user's digits to see how many similar ones
|
|
# there are. Keep in mind that Perl arrays are
|
|
# zero-indexed, so we're comparing items numbered
|
|
# 0, 1, and 2, instead of 1, 2, and 3 in BASIC.
|
|
|
|
for my $PAIR ( [0,1], [1,0], [1,2], [2,1], [0,2], [2,0] ) {
|
|
if ($A[$PAIR->[0]] == $B[$PAIR->[1]]) {
|
|
++$C;
|
|
}
|
|
}
|
|
|
|
# Check for digits that are correctly guessed
|
|
for my $i (0..2) {
|
|
if ($A[$i] == $B[$i]) {
|
|
++$D;
|
|
}
|
|
}
|
|
|
|
# If the user guessed all 3 digits they get
|
|
# a point, and the 'PLAY' loop is restarted
|
|
# (see the 'continue' loop below)
|
|
if ($D == 3) {
|
|
print "You got it!!!\n\n";
|
|
++$Y;
|
|
next PLAY;
|
|
}
|
|
|
|
# Print out the clues. The 'x' operator
|
|
# prints out the string the indicated number
|
|
# of times. The "BAGELS" line uses Perl's
|
|
# ternary operator to print the word if
|
|
# the expression ($C + $D) is equal to 0.
|
|
|
|
printf("%s%s%s\n",
|
|
"PICO " x$C,
|
|
"FERMI "x$D,
|
|
($C+$D==0 ? "BAGELS" : '')
|
|
);
|
|
|
|
# Program execution leaves the CHECK loop and
|
|
# goes to the next iteration of the $i loop.
|
|
last CHECK;
|
|
|
|
} # end of game logic else block
|
|
} # end of regex match else block
|
|
|
|
# If program execution reaches this particular point,
|
|
# then the user's input has not been accepted (the
|
|
# only ways out of this loop are the "next PLAY" statement
|
|
# when the user wins, and the "last CHECK" statement
|
|
# when the user's input is successfully parsed).
|
|
# So the program execution goes back to the top of the
|
|
# CHECK loop, printing the request for user input
|
|
# again.
|
|
|
|
} # end of CHECK loop
|
|
|
|
# This location is reached by the "last CHECK" statement,
|
|
# and it's another execution of the $i loop.
|
|
|
|
} # end of $i loop
|
|
|
|
# If program execution reaches here, the user has guessed 20
|
|
# times and not won.
|
|
|
|
print "Oh well.\n";
|
|
printf("That's twenty guesses. My number was %s\n", join('',@A));
|
|
|
|
} # end of the PLAY loop
|
|
|
|
# This 'continue' block is executed before the conditional part of the
|
|
# PLAY loop is evaluated, so we can ask if the user wants another game
|
|
# (i.e., if we should restart the PLAY loop).
|
|
|
|
continue {
|
|
|
|
# This 'continue' loop is reached either when the PLAY loop has completed
|
|
# or via the 'next PLAY' statement when the user wins a game. In either
|
|
# case we ask if the player wants to go again, and use the 'last'
|
|
# statement to exit the loop if the response is not yes.
|
|
print "Play again (yes or no) ? ";
|
|
last unless yes_input();
|
|
}
|
|
|
|
# And as in the original BASIC program, print out
|
|
# the user's score only if it is > 0.
|
|
printf("A %d point bagels buff!\n", $Y) if $Y > 0;
|
|
print "Hope you had fun. Bye.\n";
|