Initial revision

git-svn-id: file:///srv/svn/joey/bsdgames-trunk@5086 a4a2c43b-8ac3-0310-8836-e0e880c912e2
This commit is contained in:
joey
1999-09-08 23:56:51 +00:00
commit 03272d67da
623 changed files with 243457 additions and 0 deletions

8
arithmetic/Makefile.bsd Normal file
View File

@@ -0,0 +1,8 @@
# $NetBSD: Makefile,v 1.4 1995/03/21 11:59:28 cgd Exp $
# @(#)Makefile 8.1 (Berkeley) 5/31/93
PROG= arithmetic
MAN= arithmetic.6
HIDEGAME=hidegame
.include <bsd.prog.mk>

10
arithmetic/Makefrag Normal file
View File

@@ -0,0 +1,10 @@
# Makefrag - makefile fragment for arithmetic
arithmetic_DIRS := $(GAMESDIR) $(MAN6DIR)
arithmetic_all: arithmetic/arithmetic arithmetic/arithmetic.6
arithmetic_install: arithmetic/arithmetic
$(INSTALL_BINARY) arithmetic/arithmetic $(INSTALL_PREFIX)$(GAMESDIR)/arithmetic
$(HIDE_GAME) arithmetic
$(INSTALL_MANUAL) arithmetic/arithmetic.6

107
arithmetic/arithmetic.6 Normal file
View File

@@ -0,0 +1,107 @@
.\" $NetBSD: arithmetic.6,v 1.6 1998/04/28 07:19:28 fair Exp $
.\"
.\" Copyright (c) 1989, 1993
.\" The Regents of the University of California. All rights reserved.
.\"
.\" This code is derived from software contributed to Berkeley by
.\" Eamonn McManus of Trinity College Dublin.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\" 3. All advertising materials mentioning features or use of this software
.\" must display the following acknowledgement:
.\" This product includes software developed by the University of
.\" California, Berkeley and its contributors.
.\" 4. Neither the name of the University nor the names of its contributors
.\" may be used to endorse or promote products derived from this software
.\" without specific prior written permission.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" @(#)arithmetic.6 8.1 (Berkeley) 5/31/93
.\"
.Dd May 31, 1993
.Dt ARITHMETIC 6
.Os BSD 4
.Sh NAME
.Nm arithmetic
.Nd quiz on simple arithmetic
.Sh SYNOPSIS
.Nm
.Op Fl o Ar +\-x/
.Op Fl r Ar range
.Sh DESCRIPTION
.Nm
asks you to solve problems in simple arithmetic.
Each question must be answered correctly before going on to the next.
After every 20 problems, it prints the score so far and the time taken.
You can quit at any time by typing the interrupt or end-of-file character.
.Pp
The options are as follows:
.Bl -tag -width indent
.It Fl o
By default,
.Nm
asks questions on addition of numbers from 0 to 10, and corresponding
subtraction.
By supplying one or more of the characters
.Ar +\-x/ ,
you can ask for problems in addition, subtraction, multiplication, and
division, respectively.
If you give one of these characters more than once, that kind of problem
will be asked correspondingly more often.
.It Fl r
If a
.Ar range
is supplied,
.Nm
selects the numbers in its problems in the following way.
For addition and multiplication, the numbers to be added or multiplied
are between 0 and
.Ar range ,
inclusive.
For subtraction and division, both the required result and the number to
divide by or subtract will be between 0 and
.Ar range .
(Of course,
.Nm
will not ask you to divide by 0.) The default
.I range
is 10.
.El
.Pp
When you get a problem wrong,
.Nm
will remember the numbers involved, and will tend to select those numbers
more often than others, in problems of the same sort.
Eventually it will forgive and forget.
.Pp
.Nm
cannot be persuaded to tell you the right answer.
You must work it out for yourself.
.Sh DIAGNOSTICS
.Dq What?
if you get a question wrong.
.Dq Right!
if you get it right.
.Dq Please type a number.
if arithmetic doesn't understand what you typed.
.Sh SEE ALSO
.Xr bc 1 ,
.Xr dc 1

396
arithmetic/arithmetic.c Normal file
View File

@@ -0,0 +1,396 @@
/* $NetBSD: arithmetic.c,v 1.12 1998/09/14 09:13:46 hubertf Exp $ */
/*
* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Eamonn McManus of Trinity College Dublin.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/cdefs.h>
#ifndef lint
__COPYRIGHT("@(#) Copyright (c) 1989, 1993\n\
The Regents of the University of California. All rights reserved.\n");
#endif /* not lint */
#ifndef lint
#if 0
static char sccsid[] = "@(#)arithmetic.c 8.1 (Berkeley) 5/31/93";
#else
__RCSID("$NetBSD: arithmetic.c,v 1.12 1998/09/14 09:13:46 hubertf Exp $");
#endif
#endif /* not lint */
/*
* By Eamonn McManus, Trinity College Dublin <emcmanus@cs.tcd.ie>.
*
* The operation of this program mimics that of the standard Unix game
* `arithmetic'. I've made it as close as I could manage without examining
* the source code. The principal differences are:
*
* The method of biasing towards numbers that had wrong answers in the past
* is different; original `arithmetic' seems to retain the bias forever,
* whereas this program lets the bias gradually decay as it is used.
*
* Original `arithmetic' delays for some period (3 seconds?) after printing
* the score. I saw no reason for this delay, so I scrapped it.
*
* There is no longer a limitation on the maximum range that can be supplied
* to the program. The original program required it to be less than 100.
* Anomalous results may occur with this program if ranges big enough to
* allow overflow are given.
*
* I have obviously not attempted to duplicate bugs in the original. It
* would go into an infinite loop if invoked as `arithmetic / 0'. It also
* did not recognise an EOF in its input, and would continue trying to read
* after it. It did not check that the input was a valid number, treating any
* garbage as 0. Finally, it did not flush stdout after printing its prompt,
* so in the unlikely event that stdout was not a terminal, it would not work
* properly.
*/
#include <sys/types.h>
#include <err.h>
#include <ctype.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
int getrandom __P((int, int, int));
void intr __P((int)) __attribute__((__noreturn__));
int main __P((int, char *[]));
int opnum __P((int));
void penalise __P((int, int, int));
int problem __P((void));
void showstats __P((void));
void usage __P((void)) __attribute__((__noreturn__));
const char keylist[] = "+-x/";
const char defaultkeys[] = "+-";
const char *keys = defaultkeys;
int nkeys = sizeof(defaultkeys) - 1;
int rangemax = 10;
int nright, nwrong;
time_t qtime;
#define NQUESTS 20
/*
* Select keys from +-x/ to be asked addition, subtraction, multiplication,
* and division problems. More than one key may be given. The default is
* +-. Specify a range to confine the operands to 0 - range. Default upper
* bound is 10. After every NQUESTS questions, statistics on the performance
* so far are printed.
*/
int
main(argc, argv)
int argc;
char **argv;
{
extern char *optarg;
extern int optind;
int ch, cnt;
/* Revoke setgid privileges */
setregid(getgid(), getgid());
while ((ch = getopt(argc, argv, "r:o:")) != -1)
switch(ch) {
case 'o': {
const char *p;
for (p = keys = optarg; *p; ++p)
if (!strchr(keylist, *p))
errx(1, "arithmetic: unknown key.");
nkeys = p - optarg;
break;
}
case 'r':
if ((rangemax = atoi(optarg)) <= 0)
errx(1, "arithmetic: invalid range.");
break;
case '?':
default:
usage();
}
if (argc -= optind)
usage();
/* Seed the random-number generator. */
srandom((int)time((time_t *)NULL));
(void)signal(SIGINT, intr);
/* Now ask the questions. */
for (;;) {
for (cnt = NQUESTS; cnt--;)
if (problem() == EOF)
exit(0);
showstats();
}
/* NOTREACHED */
}
/* Handle interrupt character. Print score and exit. */
void
intr(dummy)
int dummy __attribute__((unused));
{
showstats();
exit(0);
}
/* Print score. Original `arithmetic' had a delay after printing it. */
void
showstats()
{
if (nright + nwrong > 0) {
(void)printf("\n\nRights %d; Wrongs %d; Score %d%%",
nright, nwrong, (int)(100L * nright / (nright + nwrong)));
if (nright > 0)
(void)printf("\nTotal time %ld seconds; %.1f seconds per problem\n\n",
(long)qtime, (float)qtime / nright);
}
(void)printf("\n");
}
/*
* Pick a problem and ask it. Keeps asking the same problem until supplied
* with the correct answer, or until EOF or interrupt is typed. Problems are
* selected such that the right operand and either the left operand (for +, x)
* or the correct result (for -, /) are in the range 0 to rangemax. Each wrong
* answer causes the numbers in the problem to be penalised, so that they are
* more likely to appear in subsequent problems.
*/
int
problem()
{
char *p;
time_t start, finish;
int left, op, right, result;
char line[80];
right = left = result = 0;
op = keys[random() % nkeys];
if (op != '/')
right = getrandom(rangemax + 1, op, 1);
retry:
/* Get the operands. */
switch (op) {
case '+':
left = getrandom(rangemax + 1, op, 0);
result = left + right;
break;
case '-':
result = getrandom(rangemax + 1, op, 0);
left = right + result;
break;
case 'x':
left = getrandom(rangemax + 1, op, 0);
result = left * right;
break;
case '/':
right = getrandom(rangemax, op, 1) + 1;
result = getrandom(rangemax + 1, op, 0);
left = right * result + random() % right;
break;
}
/*
* A very big maxrange could cause negative values to pop
* up, owing to overflow.
*/
if (result < 0 || left < 0)
goto retry;
(void)printf("%d %c %d = ", left, op, right);
(void)fflush(stdout);
(void)time(&start);
/*
* Keep looping until the correct answer is given, or until EOF or
* interrupt is typed.
*/
for (;;) {
if (!fgets(line, sizeof(line), stdin)) {
(void)printf("\n");
return(EOF);
}
for (p = line; *p && isspace(*p); ++p);
if (!isdigit(*p)) {
(void)printf("Please type a number.\n");
continue;
}
if (atoi(p) == result) {
(void)printf("Right!\n");
++nright;
break;
}
/* Wrong answer; penalise and ask again. */
(void)printf("What?\n");
++nwrong;
penalise(right, op, 1);
if (op == 'x' || op == '+')
penalise(left, op, 0);
else
penalise(result, op, 0);
}
/*
* Accumulate the time taken. Obviously rounding errors happen here;
* however they should cancel out, because some of the time you are
* charged for a partially elapsed second at the start, and some of
* the time you are not charged for a partially elapsed second at the
* end.
*/
(void)time(&finish);
qtime += finish - start;
return(0);
}
/*
* Here is the code for accumulating penalties against the numbers for which
* a wrong answer was given. The right operand and either the left operand
* (for +, x) or the result (for -, /) are stored in a list for the particular
* operation, and each becomes more likely to appear again in that operation.
* Initially, each number is charged a penalty of WRONGPENALTY, giving it that
* many extra chances of appearing. Each time it is selected because of this,
* its penalty is decreased by one; it is removed when it reaches 0.
*
* The penalty[] array gives the sum of all penalties in the list for
* each operation and each operand. The penlist[] array has the lists of
* penalties themselves.
*/
int penalty[sizeof(keylist) - 1][2];
struct penalty {
int value, penalty; /* Penalised value and its penalty. */
struct penalty *next;
} *penlist[sizeof(keylist) - 1][2];
#define WRONGPENALTY 5 /* Perhaps this should depend on maxrange. */
/*
* Add a penalty for the number `value' to the list for operation `op',
* operand number `operand' (0 or 1). If we run out of memory, we just
* forget about the penalty (how likely is this, anyway?).
*/
void
penalise(value, op, operand)
int value, op, operand;
{
struct penalty *p;
op = opnum(op);
if ((p = (struct penalty *)malloc((u_int)sizeof(*p))) == NULL)
return;
p->next = penlist[op][operand];
penlist[op][operand] = p;
penalty[op][operand] += p->penalty = WRONGPENALTY;
p->value = value;
}
/*
* Select a random value from 0 to maxval - 1 for operand `operand' (0 or 1)
* of operation `op'. The random number we generate is either used directly
* as a value, or represents a position in the penalty list. If the latter,
* we find the corresponding value and return that, decreasing its penalty.
*/
int
getrandom(maxval, op, operand)
int maxval, op, operand;
{
int value;
struct penalty **pp, *p;
op = opnum(op);
value = random() % (maxval + penalty[op][operand]);
/*
* 0 to maxval - 1 is a number to be used directly; bigger values
* are positions to be located in the penalty list.
*/
if (value < maxval)
return(value);
value -= maxval;
/*
* Find the penalty at position `value'; decrement its penalty and
* delete it if it reaches 0; return the corresponding value.
*/
for (pp = &penlist[op][operand]; (p = *pp) != NULL; pp = &p->next) {
if (p->penalty > value) {
value = p->value;
penalty[op][operand]--;
if (--(p->penalty) <= 0) {
p = p->next;
(void)free((char *)*pp);
*pp = p;
}
return(value);
}
value -= p->penalty;
}
/*
* We can only get here if the value from the penalty[] array doesn't
* correspond to the actual sum of penalties in the list. Provide an
* obscure message.
*/
errx(1, "arithmetic: bug: inconsistent penalties.");
/* NOTREACHED */
}
/* Return an index for the character op, which is one of [+-x/]. */
int
opnum(op)
int op;
{
char *p;
if (op == 0 || (p = strchr(keylist, op)) == NULL)
errx(1, "arithmetic: bug: op %c not in keylist %s",
op, keylist);
return(p - keylist);
}
/* Print usage message and quit. */
void
usage()
{
extern char *__progname; /* from crt0.o */
(void)fprintf(stderr, "usage: %s [-o +-x/] [-r range]\n",
__progname);
exit(1);
}