[BACK]Return to arithmetic.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / games / arithmetic

Annotation of src/games/arithmetic/arithmetic.c, Revision 1.21.16.1

1.21.16.1! matt        1: /*     $NetBSD$        */
1.5       cgd         2:
1.1       cgd         3: /*
1.5       cgd         4:  * Copyright (c) 1989, 1993
                      5:  *     The Regents of the University of California.  All rights reserved.
1.1       cgd         6:  *
                      7:  * This code is derived from software contributed to Berkeley by
                      8:  * Eamonn McManus of Trinity College Dublin.
                      9:  *
                     10:  * Redistribution and use in source and binary forms, with or without
                     11:  * modification, are permitted provided that the following conditions
                     12:  * are met:
                     13:  * 1. Redistributions of source code must retain the above copyright
                     14:  *    notice, this list of conditions and the following disclaimer.
                     15:  * 2. Redistributions in binary form must reproduce the above copyright
                     16:  *    notice, this list of conditions and the following disclaimer in the
                     17:  *    documentation and/or other materials provided with the distribution.
1.19      agc        18:  * 3. Neither the name of the University nor the names of its contributors
1.1       cgd        19:  *    may be used to endorse or promote products derived from this software
                     20:  *    without specific prior written permission.
                     21:  *
                     22:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     23:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     24:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     25:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     26:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     27:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     28:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     29:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     30:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     31:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     32:  * SUCH DAMAGE.
                     33:  */
                     34:
1.7       lukem      35: #include <sys/cdefs.h>
1.1       cgd        36: #ifndef lint
1.7       lukem      37: __COPYRIGHT("@(#) Copyright (c) 1989, 1993\n\
                     38:        The Regents of the University of California.  All rights reserved.\n");
1.1       cgd        39: #endif /* not lint */
                     40:
                     41: #ifndef lint
1.5       cgd        42: #if 0
                     43: static char sccsid[] = "@(#)arithmetic.c       8.1 (Berkeley) 5/31/93";
                     44: #else
1.21.16.1! matt       45: __RCSID("$NetBSD$");
1.5       cgd        46: #endif
1.1       cgd        47: #endif /* not lint */
                     48:
                     49: /*
                     50:  * By Eamonn McManus, Trinity College Dublin <emcmanus@cs.tcd.ie>.
                     51:  *
                     52:  * The operation of this program mimics that of the standard Unix game
                     53:  * `arithmetic'.  I've made it as close as I could manage without examining
                     54:  * the source code.  The principal differences are:
                     55:  *
                     56:  * The method of biasing towards numbers that had wrong answers in the past
                     57:  * is different; original `arithmetic' seems to retain the bias forever,
                     58:  * whereas this program lets the bias gradually decay as it is used.
                     59:  *
                     60:  * Original `arithmetic' delays for some period (3 seconds?) after printing
                     61:  * the score.  I saw no reason for this delay, so I scrapped it.
                     62:  *
                     63:  * There is no longer a limitation on the maximum range that can be supplied
                     64:  * to the program.  The original program required it to be less than 100.
                     65:  * Anomalous results may occur with this program if ranges big enough to
                     66:  * allow overflow are given.
                     67:  *
                     68:  * I have obviously not attempted to duplicate bugs in the original.  It
                     69:  * would go into an infinite loop if invoked as `arithmetic / 0'.  It also
                     70:  * did not recognise an EOF in its input, and would continue trying to read
                     71:  * after it.  It did not check that the input was a valid number, treating any
                     72:  * garbage as 0.  Finally, it did not flush stdout after printing its prompt,
                     73:  * so in the unlikely event that stdout was not a terminal, it would not work
                     74:  * properly.
                     75:  */
                     76:
                     77: #include <sys/types.h>
1.7       lukem      78: #include <err.h>
                     79: #include <ctype.h>
1.4       jtc        80: #include <signal.h>
1.1       cgd        81: #include <stdio.h>
1.7       lukem      82: #include <stdlib.h>
1.1       cgd        83: #include <string.h>
1.2       mycroft    84: #include <time.h>
1.10      perry      85: #include <unistd.h>
1.1       cgd        86:
1.20      jsm        87: int    getrandom(int, int, int);
1.21.16.1! matt       88: void   intr(int) __dead;
1.20      jsm        89: int    main(int, char *[]);
                     90: int    opnum(int);
                     91: void   penalise(int, int, int);
                     92: int    problem(void);
                     93: void   showstats(int);
1.21.16.1! matt       94: void   usage(void) __dead;
1.7       lukem      95:
1.12      hubertf    96: const char keylist[] = "+-x/";
                     97: const char defaultkeys[] = "+-";
                     98: const char *keys = defaultkeys;
1.1       cgd        99: int nkeys = sizeof(defaultkeys) - 1;
                    100: int rangemax = 10;
                    101: int nright, nwrong;
                    102: time_t qtime;
                    103: #define        NQUESTS 20
                    104:
                    105: /*
                    106:  * Select keys from +-x/ to be asked addition, subtraction, multiplication,
                    107:  * and division problems.  More than one key may be given.  The default is
                    108:  * +-.  Specify a range to confine the operands to 0 - range.  Default upper
                    109:  * bound is 10.  After every NQUESTS questions, statistics on the performance
                    110:  * so far are printed.
                    111:  */
1.6       jtc       112: int
1.1       cgd       113: main(argc, argv)
                    114:        int argc;
                    115:        char **argv;
                    116: {
                    117:        int ch, cnt;
                    118:
1.13      hubertf   119:        /* Revoke setgid privileges */
1.15      mycroft   120:        setgid(getgid());
1.13      hubertf   121:
1.8       lukem     122:        while ((ch = getopt(argc, argv, "r:o:")) != -1)
1.1       cgd       123:                switch(ch) {
                    124:                case 'o': {
1.12      hubertf   125:                        const char *p;
1.1       cgd       126:
                    127:                        for (p = keys = optarg; *p; ++p)
1.7       lukem     128:                                if (!strchr(keylist, *p))
                    129:                                        errx(1, "arithmetic: unknown key.");
1.1       cgd       130:                        nkeys = p - optarg;
                    131:                        break;
                    132:                }
                    133:                case 'r':
1.7       lukem     134:                        if ((rangemax = atoi(optarg)) <= 0)
                    135:                                errx(1, "arithmetic: invalid range.");
1.1       cgd       136:                        break;
                    137:                case '?':
                    138:                default:
                    139:                        usage();
                    140:                }
                    141:        if (argc -= optind)
                    142:                usage();
                    143:
                    144:        /* Seed the random-number generator. */
                    145:        srandom((int)time((time_t *)NULL));
                    146:
                    147:        (void)signal(SIGINT, intr);
                    148:
                    149:        /* Now ask the questions. */
                    150:        for (;;) {
                    151:                for (cnt = NQUESTS; cnt--;)
                    152:                        if (problem() == EOF)
                    153:                                exit(0);
1.18      hubertf   154:                showstats(0);
1.1       cgd       155:        }
                    156:        /* NOTREACHED */
                    157: }
                    158:
                    159: /* Handle interrupt character.  Print score and exit. */
                    160: void
1.7       lukem     161: intr(dummy)
1.21.16.1! matt      162:        int dummy __unused;
1.1       cgd       163: {
1.18      hubertf   164:        showstats(1);
1.1       cgd       165:        exit(0);
                    166: }
                    167:
                    168: /* Print score.  Original `arithmetic' had a delay after printing it. */
1.7       lukem     169: void
1.18      hubertf   170: showstats(bool_sigint)
                    171:        int bool_sigint;
1.1       cgd       172: {
                    173:        if (nright + nwrong > 0) {
                    174:                (void)printf("\n\nRights %d; Wrongs %d; Score %d%%",
                    175:                    nright, nwrong, (int)(100L * nright / (nright + nwrong)));
                    176:                if (nright > 0)
                    177:        (void)printf("\nTotal time %ld seconds; %.1f seconds per problem\n\n",
                    178:                            (long)qtime, (float)qtime / nright);
1.18      hubertf   179:        }
                    180:        if(!bool_sigint) {
                    181:                (void)printf("Press RETURN to continue...\n");
                    182:                while(!getchar()) ;
1.1       cgd       183:        }
                    184:        (void)printf("\n");
                    185: }
                    186:
                    187: /*
                    188:  * Pick a problem and ask it.  Keeps asking the same problem until supplied
                    189:  * with the correct answer, or until EOF or interrupt is typed.  Problems are
                    190:  * selected such that the right operand and either the left operand (for +, x)
                    191:  * or the correct result (for -, /) are in the range 0 to rangemax.  Each wrong
                    192:  * answer causes the numbers in the problem to be penalised, so that they are
                    193:  * more likely to appear in subsequent problems.
                    194:  */
1.7       lukem     195: int
1.1       cgd       196: problem()
                    197: {
1.7       lukem     198:        char *p;
1.1       cgd       199:        time_t start, finish;
                    200:        int left, op, right, result;
                    201:        char line[80];
                    202:
1.9       is        203:        right = left = result = 0;
1.1       cgd       204:        op = keys[random() % nkeys];
                    205:        if (op != '/')
                    206:                right = getrandom(rangemax + 1, op, 1);
                    207: retry:
                    208:        /* Get the operands. */
                    209:        switch (op) {
                    210:        case '+':
                    211:                left = getrandom(rangemax + 1, op, 0);
                    212:                result = left + right;
                    213:                break;
                    214:        case '-':
                    215:                result = getrandom(rangemax + 1, op, 0);
                    216:                left = right + result;
                    217:                break;
                    218:        case 'x':
                    219:                left = getrandom(rangemax + 1, op, 0);
                    220:                result = left * right;
                    221:                break;
                    222:        case '/':
                    223:                right = getrandom(rangemax, op, 1) + 1;
                    224:                result = getrandom(rangemax + 1, op, 0);
                    225:                left = right * result + random() % right;
                    226:                break;
                    227:        }
                    228:
                    229:        /*
                    230:         * A very big maxrange could cause negative values to pop
                    231:         * up, owing to overflow.
                    232:         */
                    233:        if (result < 0 || left < 0)
                    234:                goto retry;
                    235:
                    236:        (void)printf("%d %c %d =   ", left, op, right);
                    237:        (void)fflush(stdout);
                    238:        (void)time(&start);
                    239:
                    240:        /*
                    241:         * Keep looping until the correct answer is given, or until EOF or
                    242:         * interrupt is typed.
                    243:         */
                    244:        for (;;) {
                    245:                if (!fgets(line, sizeof(line), stdin)) {
                    246:                        (void)printf("\n");
                    247:                        return(EOF);
                    248:                }
1.21      dsl       249:                for (p = line; *p && isspace((unsigned char)*p); ++p);
                    250:                if (!isdigit((unsigned char)*p)) {
1.1       cgd       251:                        (void)printf("Please type a number.\n");
                    252:                        continue;
                    253:                }
                    254:                if (atoi(p) == result) {
                    255:                        (void)printf("Right!\n");
                    256:                        ++nright;
                    257:                        break;
                    258:                }
                    259:                /* Wrong answer; penalise and ask again. */
                    260:                (void)printf("What?\n");
                    261:                ++nwrong;
                    262:                penalise(right, op, 1);
                    263:                if (op == 'x' || op == '+')
                    264:                        penalise(left, op, 0);
                    265:                else
                    266:                        penalise(result, op, 0);
                    267:        }
                    268:
                    269:        /*
                    270:         * Accumulate the time taken.  Obviously rounding errors happen here;
                    271:         * however they should cancel out, because some of the time you are
                    272:         * charged for a partially elapsed second at the start, and some of
                    273:         * the time you are not charged for a partially elapsed second at the
                    274:         * end.
                    275:         */
                    276:        (void)time(&finish);
                    277:        qtime += finish - start;
                    278:        return(0);
                    279: }
                    280:
                    281: /*
                    282:  * Here is the code for accumulating penalties against the numbers for which
                    283:  * a wrong answer was given.  The right operand and either the left operand
                    284:  * (for +, x) or the result (for -, /) are stored in a list for the particular
                    285:  * operation, and each becomes more likely to appear again in that operation.
                    286:  * Initially, each number is charged a penalty of WRONGPENALTY, giving it that
                    287:  * many extra chances of appearing.  Each time it is selected because of this,
                    288:  * its penalty is decreased by one; it is removed when it reaches 0.
                    289:  *
                    290:  * The penalty[] array gives the sum of all penalties in the list for
                    291:  * each operation and each operand.  The penlist[] array has the lists of
                    292:  * penalties themselves.
                    293:  */
                    294:
                    295: int penalty[sizeof(keylist) - 1][2];
                    296: struct penalty {
                    297:        int value, penalty;     /* Penalised value and its penalty. */
                    298:        struct penalty *next;
                    299: } *penlist[sizeof(keylist) - 1][2];
                    300:
                    301: #define        WRONGPENALTY    5       /* Perhaps this should depend on maxrange. */
                    302:
                    303: /*
                    304:  * Add a penalty for the number `value' to the list for operation `op',
                    305:  * operand number `operand' (0 or 1).  If we run out of memory, we just
                    306:  * forget about the penalty (how likely is this, anyway?).
                    307:  */
1.7       lukem     308: void
1.1       cgd       309: penalise(value, op, operand)
                    310:        int value, op, operand;
                    311: {
                    312:        struct penalty *p;
                    313:
                    314:        op = opnum(op);
                    315:        if ((p = (struct penalty *)malloc((u_int)sizeof(*p))) == NULL)
                    316:                return;
                    317:        p->next = penlist[op][operand];
                    318:        penlist[op][operand] = p;
                    319:        penalty[op][operand] += p->penalty = WRONGPENALTY;
                    320:        p->value = value;
                    321: }
                    322:
                    323: /*
                    324:  * Select a random value from 0 to maxval - 1 for operand `operand' (0 or 1)
                    325:  * of operation `op'.  The random number we generate is either used directly
                    326:  * as a value, or represents a position in the penalty list.  If the latter,
                    327:  * we find the corresponding value and return that, decreasing its penalty.
                    328:  */
1.7       lukem     329: int
1.1       cgd       330: getrandom(maxval, op, operand)
                    331:        int maxval, op, operand;
                    332: {
                    333:        int value;
1.7       lukem     334:        struct penalty **pp, *p;
1.1       cgd       335:
                    336:        op = opnum(op);
                    337:        value = random() % (maxval + penalty[op][operand]);
                    338:
                    339:        /*
                    340:         * 0 to maxval - 1 is a number to be used directly; bigger values
                    341:         * are positions to be located in the penalty list.
                    342:         */
                    343:        if (value < maxval)
                    344:                return(value);
                    345:        value -= maxval;
                    346:
                    347:        /*
                    348:         * Find the penalty at position `value'; decrement its penalty and
                    349:         * delete it if it reaches 0; return the corresponding value.
                    350:         */
                    351:        for (pp = &penlist[op][operand]; (p = *pp) != NULL; pp = &p->next) {
                    352:                if (p->penalty > value) {
                    353:                        value = p->value;
                    354:                        penalty[op][operand]--;
                    355:                        if (--(p->penalty) <= 0) {
                    356:                                p = p->next;
                    357:                                (void)free((char *)*pp);
                    358:                                *pp = p;
                    359:                        }
                    360:                        return(value);
                    361:                }
                    362:                value -= p->penalty;
                    363:        }
                    364:        /*
                    365:         * We can only get here if the value from the penalty[] array doesn't
                    366:         * correspond to the actual sum of penalties in the list.  Provide an
                    367:         * obscure message.
                    368:         */
1.7       lukem     369:        errx(1, "arithmetic: bug: inconsistent penalties.");
1.1       cgd       370:        /* NOTREACHED */
                    371: }
                    372:
                    373: /* Return an index for the character op, which is one of [+-x/]. */
1.7       lukem     374: int
1.1       cgd       375: opnum(op)
                    376:        int op;
                    377: {
                    378:        char *p;
                    379:
1.7       lukem     380:        if (op == 0 || (p = strchr(keylist, op)) == NULL)
                    381:                errx(1, "arithmetic: bug: op %c not in keylist %s",
                    382:                    op, keylist);
1.1       cgd       383:        return(p - keylist);
                    384: }
                    385:
                    386: /* Print usage message and quit. */
1.7       lukem     387: void
1.1       cgd       388: usage()
                    389: {
1.16      christos  390:        (void)fprintf(stderr, "Usage: %s [-o +-x/] [-r range]\n",
1.17      cgd       391:                getprogname());
1.1       cgd       392:        exit(1);
                    393: }

CVSweb <webmaster@jp.NetBSD.org>