1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393
|
/* $NetBSD: arithmetic.c,v 1.10 1998/02/03 05:33:32 perry 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.10 1998/02/03 05:33:32 perry 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));
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));
char keylist[] = "+-x/";
char defaultkeys[] = "+-";
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;
while ((ch = getopt(argc, argv, "r:o:")) != -1)
switch(ch) {
case 'o': {
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);
}
|