mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2026-01-04 17:17:59 -08:00
Merge pull request #350 from manminusone/05-bagels
Perl version of Bagels
This commit is contained in:
195
05_Bagels/perl/bagels.pl
Executable file
195
05_Bagels/perl/bagels.pl
Executable file
@@ -0,0 +1,195 @@
|
||||
#!/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";
|
||||
Reference in New Issue
Block a user