Annotation of 43BSDReno/games/arithmetic/arithmetic.c, revision 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.