|
|
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.