Annotation of 43BSDReno/games/arithmetic/arithmetic.c, revision 1.1.1.1

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.