|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.