From Code to Community: Sponsoring The Perl and Raku Conference 2025 Learn more

#!/usr/bin/perl
=begin metadata
Name: arithmetic
Description: improve your arithmetic skills
Author: Abigail, perlpowertools@abigail.be
License: perl
=end metadata
=cut
use strict;
use integer;
use File::Basename qw(basename);
use Getopt::Std qw(getopts);
use constant EX_SUCCESS => 0;
use constant EX_FAILURE => 1;
my $Program = basename($0);
my ($VERSION) = '1.3';
my %opt;
getopts('o:r:', \%opt) or usage();
my $operators = '+';
if (defined $opt{'o'}) {
usage() if ($opt{'o'} =~ m{[^-x+/]});
$operators = $opt{'o'};
}
my $range = 10;
if (defined $opt{'r'}) {
usage() if ($opt{'r'} !~ m/\A[0-9]+\Z/);
$range = $opt{'r'};
}
# Special case the range of 0. Since the right operator can only be 0,
# division should be disallowed. die() if division was the only possible
# operator. There will be nothing left.
if ($range == 0) {
$operators =~ s{/+}{}g;
unless ($operators) {
warn "$Program: division by 0 is not allowed\n";
exit EX_FAILURE;
}
}
usage() if @ARGV;
# Play the game!
my $start = time;
my $questions = 0;
my $correct = 0;
$SIG {INT} = \&report;
use constant QUESTIONS => 20;
use constant REMEMBER => 20; # One in five? *shrug*
$range ++; # Because bounds are inclusive.
# Remember questions with wrong answers.
my %remember = map {$_ => []} split // => $operators;
while ($questions < QUESTIONS) {
my $operator = substr $operators => rand length $operators, 1;
my ($left, $right, $answer);
if (@{$remember {$operator}} && rand 100 < REMEMBER) {
# Remember something we did wrong.
($left, $right, $answer) =
@{$remember {$operator} -> [rand @{$remember {$operator}}]};
}
else {
# Right is always in the range; with the exception of a 0 divisor.
do {$right = int rand $range} while $operator eq '/' && $right == 0;
if ($operator eq '-' || $operator eq '/') {
# Pick the answer.
$answer = int rand $range;
# Find the other operand.
$left = $operator eq '-' ? $answer + $right : $answer * $right;
}
else {
# Pick the other operand.
$left = int rand $range;
# Find the answer.
$answer = $operator eq '+' ? $left + $right : $left * $right;
}
}
my $guess;
# Keep trying till we get something that's a number.
LOOP: {
print "$left $operator $right = ";
$guess = <>;
if (!defined($guess)) {
report();
}
chomp $guess;
$guess =~ s/\A\s+//;
$guess =~ s/\s+\Z//;
if (length($guess) == 0 || $guess =~ /\D/) {
print "Please type a number.\n";
redo;
}
}
if ($guess == $answer) {
# We got it right, forget it now.
$remember {$operator} = [grep {$left != $_ -> [0] ||
$right != $_ -> [1]}
@{$remember {$operator}}] if
@{$remember {$operator}};
print "Right!\n";
$correct ++;
}
else {
# Remember wrong answers.
push @{$remember {$operator}} => [$left, $right, $answer];
print "What?\n"
}
$questions ++;
}
report();
sub report {
my $seconds = time - $start;
my $hours = $seconds / 3600; $seconds %= 3600;
my $minutes = $seconds / 60; $seconds %= 60;
my $time = (join ", " =>
map {/^1 / ? $_ : $_ . "s"}
grep {!/^0/}
"$hours hour", "$minutes minute", "$seconds second") ||
"no time at all!";
print "\nYou had $correct answers correct, out of $questions. ",
"It took you $time.\n";
exit EX_SUCCESS;
}
sub usage {
warn "$Program (Perl games) $VERSION\n";
warn "usage: $Program [-o +-x/] [-r range]\n";
exit EX_FAILURE;
}
__END__
=pod
=head1 NAME
arithmetic - improve your arithmetic skills
=head1 SYNOPSIS
B<arithmetic> [B<-o> B<+-x/>] [B<-r> I<range>]
=head1 DESCRIPTION
B<arithmetic> prompts you with simple arithmetic exercises, and
verifies your answers. B<arithmetic> will reply with B<Right!>
if you answered correctly, and with B<What?> if you answered incorrectly.
If B<arithmetic> thinks your answer is not a number, it will
respond with B<Please type a number!> and repeat the exercise.
After 20 questions, B<arithmetic> will tell you how many exercises you
answered correctly, and how much time it took.
You can quit at any time by typing the interrupt or end-of-file character.
If you answer an exercise incorrectly, B<arithmetic> will remember the
numbers involved, and favour those over others.
=head2 OPTIONS
B<arithmetic> accepts the following options:
=over 4
=item B<-o> B<+-x/>
By default, B<arithmetic> only asks addition exercises. By giving the
B<-o> option, B<arithmetic> will randomly choose from the given I<operators>.
If an operator is given multiple times, that operator will be picked
more often. The more it is repeated, the more it will be picked.
=item B<-r> I<range>
For I<addition> and I<multiplication>, B<arithmetic> will pick both
I<operands> in the range from 0 to I<range>, inclusively. For
I<subtraction> and I<division>, both the answer, and the right I<operand>
will be in this range. The default range is B<10>.
=back
=head1 ENVIRONMENT
The working of B<arithmetic> is not influenced by any environment variables.
=head1 BUGS
None known.
=head1 REVISION HISTORY
$Log: arithmetic,v $
Revision 1.2 2004/08/05 14:17:43 cwest
cleanup, new version number on website
Revision 1.1 2004/07/23 20:09:59 cwest
initial import
Revision 1.1 1999/03/14 07:44:35 abigail
Initial revision
=head1 AUTHOR
The Perl implementation of B<arithmetic> was written by Abigail,
I<perlpowertools@abigail.be>.
=head1 COPYRIGHT and LICENSE
This program is copyright by Abigail 1999.
This program is free and open software. You may use, copy, modify, distribute,
and sell this program (and any modified variants) in any way you wish,
provided you do not restrict others from doing the same.
=cut