139101Sbostic /* 239101Sbostic * Copyright (c) 1989 The Regents of the University of California. 339101Sbostic * All rights reserved. 439101Sbostic * 539101Sbostic * This code is derived from software contributed to Berkeley by 639101Sbostic * Eamonn McManus of Trinity College Dublin. 739101Sbostic * 839101Sbostic * Redistribution and use in source and binary forms are permitted 939101Sbostic * provided that the above copyright notice and this paragraph are 1039101Sbostic * duplicated in all such forms and that any documentation, 1139101Sbostic * advertising materials, and other materials related to such 1239101Sbostic * distribution and use acknowledge that the software was developed 1339101Sbostic * by the University of California, Berkeley. The name of the 1439101Sbostic * University may not be used to endorse or promote products derived 1539101Sbostic * from this software without specific prior written permission. 1639101Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1739101Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1839101Sbostic * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1939101Sbostic */ 208852Smckusick 2139101Sbostic #ifndef lint 2239101Sbostic char copyright[] = 2339101Sbostic "@(#) Copyright (c) 1989 The Regents of the University of California.\n\ 2439101Sbostic All rights reserved.\n"; 2539101Sbostic #endif /* not lint */ 268852Smckusick 2739101Sbostic #ifndef lint 28*42017Sbostic static char sccsid[] = "@(#)arithmetic.c 5.3 (Berkeley) 05/15/90"; 2939101Sbostic #endif /* not lint */ 3039101Sbostic 3139101Sbostic /* 3239101Sbostic * By Eamonn McManus, Trinity College Dublin <emcmanus@cs.tcd.ie>. 3339101Sbostic * 3439101Sbostic * The operation of this program mimics that of the standard Unix game 3539101Sbostic * `arithmetic'. I've made it as close as I could manage without examining 3639101Sbostic * the source code. The principal differences are: 3739101Sbostic * 3839101Sbostic * The method of biasing towards numbers that had wrong answers in the past 3939101Sbostic * is different; original `arithmetic' seems to retain the bias forever, 4039101Sbostic * whereas this program lets the bias gradually decay as it is used. 4139101Sbostic * 4239101Sbostic * Original `arithmetic' delays for some period (3 seconds?) after printing 4339101Sbostic * the score. I saw no reason for this delay, so I scrapped it. 4439101Sbostic * 4539101Sbostic * There is no longer a limitation on the maximum range that can be supplied 4639101Sbostic * to the program. The original program required it to be less than 100. 4739101Sbostic * Anomalous results may occur with this program if ranges big enough to 4839101Sbostic * allow overflow are given. 4939101Sbostic * 5039101Sbostic * I have obviously not attempted to duplicate bugs in the original. It 5139101Sbostic * would go into an infinite loop if invoked as `arithmetic / 0'. It also 5239101Sbostic * did not recognise an EOF in its input, and would continue trying to read 5339101Sbostic * after it. It did not check that the input was a valid number, treating any 5439101Sbostic * garbage as 0. Finally, it did not flush stdout after printing its prompt, 5539101Sbostic * so in the unlikely event that stdout was not a terminal, it would not work 5639101Sbostic * properly. 5739101Sbostic */ 5839101Sbostic 5939101Sbostic #include <sys/types.h> 6039101Sbostic #include <sys/signal.h> 6139101Sbostic #include <ctype.h> 628852Smckusick #include <stdio.h> 63*42017Sbostic #include <string.h> 648852Smckusick 6539101Sbostic char keylist[] = "+-x/"; 6639101Sbostic char defaultkeys[] = "+-"; 6739101Sbostic char *keys = defaultkeys; 6839101Sbostic int nkeys = sizeof(defaultkeys) - 1; 6939101Sbostic int rangemax = 10; 7039101Sbostic int nright, nwrong; 7139101Sbostic time_t qtime; 7239101Sbostic #define NQUESTS 20 738852Smckusick 7439101Sbostic /* 7539101Sbostic * Select keys from +-x/ to be asked addition, subtraction, multiplication, 7639101Sbostic * and division problems. More than one key may be given. The default is 7739101Sbostic * +-. Specify a range to confine the operands to 0 - range. Default upper 7839101Sbostic * bound is 10. After every NQUESTS questions, statistics on the performance 7939101Sbostic * so far are printed. 8039101Sbostic */ 8139101Sbostic void 8239101Sbostic main(argc, argv) 8339101Sbostic int argc; 8439101Sbostic char **argv; 858852Smckusick { 8639101Sbostic extern char *optarg; 8739101Sbostic extern int optind; 8839101Sbostic int ch, cnt; 8939101Sbostic time_t time(); 9039101Sbostic sig_t intr(); 918852Smckusick 9239101Sbostic while ((ch = getopt(argc, argv, "r:o:")) != EOF) 9339101Sbostic switch(ch) { 9439101Sbostic case 'o': { 9539101Sbostic register char *p; 968852Smckusick 9739101Sbostic for (p = keys = optarg; *p; ++p) 9839101Sbostic if (!index(keylist, *p)) { 9939101Sbostic (void)fprintf(stderr, 10039101Sbostic "arithmetic: unknown key.\n"); 10139101Sbostic exit(1); 10239101Sbostic } 10339101Sbostic nkeys = p - optarg; 1048852Smckusick break; 10539101Sbostic } 10639101Sbostic case 'r': 10739101Sbostic if ((rangemax = atoi(optarg)) <= 0) { 10839101Sbostic (void)fprintf(stderr, 10939101Sbostic "arithmetic: invalid range.\n"); 11039101Sbostic exit(1); 11139101Sbostic } 11239101Sbostic break; 11339101Sbostic case '?': 1148852Smckusick default: 11539101Sbostic usage(); 1168852Smckusick } 11739101Sbostic if (argc -= optind) 11839101Sbostic usage(); 1198852Smckusick 12039101Sbostic /* Seed the random-number generator. */ 12139101Sbostic srandom((int)time((time_t *)NULL)); 1228852Smckusick 12339101Sbostic (void)signal(SIGINT, intr); 1248852Smckusick 12539101Sbostic /* Now ask the questions. */ 12639101Sbostic for (;;) { 12739101Sbostic for (cnt = NQUESTS; cnt--;) 12839101Sbostic if (problem() == EOF) 12939101Sbostic exit(0); 13039101Sbostic showstats(); 1318852Smckusick } 13239101Sbostic /* NOTREACHED */ 13339101Sbostic } 1348852Smckusick 13539101Sbostic /* Handle interrupt character. Print score and exit. */ 13639101Sbostic sig_t 13739101Sbostic intr() 13839101Sbostic { 13939101Sbostic showstats(); 14039101Sbostic exit(0); 14139101Sbostic } 1428852Smckusick 14339101Sbostic /* Print score. Original `arithmetic' had a delay after printing it. */ 14439101Sbostic showstats() 14539101Sbostic { 14639101Sbostic if (nright + nwrong > 0) { 14739101Sbostic (void)printf("\n\nRights %d; Wrongs %d; Score %d%%", 14839101Sbostic nright, nwrong, (int)(100L * nright / (nright + nwrong))); 14939101Sbostic if (nright > 0) 15039101Sbostic (void)printf("\nTotal time %ld seconds; %.1f seconds per problem\n\n", 15139101Sbostic (long)qtime, (float)qtime / nright); 1528852Smckusick } 15339101Sbostic (void)printf("\n"); 1548852Smckusick } 1558852Smckusick 15639101Sbostic /* 15739101Sbostic * Pick a problem and ask it. Keeps asking the same problem until supplied 15839101Sbostic * with the correct answer, or until EOF or interrupt is typed. Problems are 15939101Sbostic * selected such that the right operand and either the left operand (for +, x) 16039101Sbostic * or the correct result (for -, /) are in the range 0 to rangemax. Each wrong 16139101Sbostic * answer causes the numbers in the problem to be penalised, so that they are 16239101Sbostic * more likely to appear in subsequent problems. 16339101Sbostic */ 16439101Sbostic problem() 1658852Smckusick { 16639101Sbostic register char *p; 16739101Sbostic time_t start, finish; 16839101Sbostic int left, op, right, result; 16939101Sbostic char line[80]; 1708852Smckusick 17139101Sbostic op = keys[random() % nkeys]; 17239101Sbostic if (op != '/') 17339101Sbostic right = getrandom(rangemax + 1, op, 1); 17439101Sbostic retry: 17539101Sbostic /* Get the operands. */ 17639101Sbostic switch (op) { 17739101Sbostic case '+': 17839101Sbostic left = getrandom(rangemax + 1, op, 0); 17939101Sbostic result = left + right; 18039101Sbostic break; 18139101Sbostic case '-': 18239101Sbostic result = getrandom(rangemax + 1, op, 0); 18339101Sbostic left = right + result; 18439101Sbostic break; 18539101Sbostic case 'x': 18639101Sbostic left = getrandom(rangemax + 1, op, 0); 18739101Sbostic result = left * right; 18839101Sbostic break; 18939101Sbostic case '/': 19039101Sbostic right = getrandom(rangemax, op, 1) + 1; 19139101Sbostic result = getrandom(rangemax + 1, op, 0); 19239101Sbostic left = right * result + random() % right; 19339101Sbostic break; 19439101Sbostic } 1958852Smckusick 19639101Sbostic /* 19739101Sbostic * A very big maxrange could cause negative values to pop 19839101Sbostic * up, owing to overflow. 19939101Sbostic */ 20039101Sbostic if (result < 0 || left < 0) 20139101Sbostic goto retry; 20239101Sbostic 20339101Sbostic (void)printf("%d %c %d = ", left, op, right); 20439101Sbostic (void)fflush(stdout); 20539101Sbostic (void)time(&start); 20639101Sbostic 20739101Sbostic /* 20839101Sbostic * Keep looping until the correct answer is given, or until EOF or 20939101Sbostic * interrupt is typed. 21039101Sbostic */ 21139101Sbostic for (;;) { 21239101Sbostic if (!fgets(line, sizeof(line), stdin)) { 21339101Sbostic (void)printf("\n"); 21439101Sbostic return(EOF); 2158852Smckusick } 21639101Sbostic for (p = line; *p && isspace(*p); ++p); 21739101Sbostic if (!isdigit(*p)) { 21839101Sbostic (void)printf("Please type a number.\n"); 21939101Sbostic continue; 22039101Sbostic } 22139101Sbostic if (atoi(p) == result) { 22239101Sbostic (void)printf("Right!\n"); 22339101Sbostic ++nright; 22439101Sbostic break; 22539101Sbostic } 22639101Sbostic /* Wrong answer; penalise and ask again. */ 22739101Sbostic (void)printf("What?\n"); 22839101Sbostic ++nwrong; 22939101Sbostic penalise(right, op, 1); 23039101Sbostic if (op == 'x' || op == '+') 23139101Sbostic penalise(left, op, 0); 2328852Smckusick else 23339101Sbostic penalise(result, op, 0); 23439101Sbostic } 23539101Sbostic 23639101Sbostic /* 23739101Sbostic * Accumulate the time taken. Obviously rounding errors happen here; 23839101Sbostic * however they should cancel out, because some of the time you are 23939101Sbostic * charged for a partially elapsed second at the start, and some of 24039101Sbostic * the time you are not charged for a partially elapsed second at the 24139101Sbostic * end. 24239101Sbostic */ 24339101Sbostic (void)time(&finish); 24439101Sbostic qtime += finish - start; 24539101Sbostic return(0); 2468852Smckusick } 2478852Smckusick 24839101Sbostic /* 24939101Sbostic * Here is the code for accumulating penalties against the numbers for which 25039101Sbostic * a wrong answer was given. The right operand and either the left operand 25139101Sbostic * (for +, x) or the result (for -, /) are stored in a list for the particular 25239101Sbostic * operation, and each becomes more likely to appear again in that operation. 25339101Sbostic * Initially, each number is charged a penalty of WRONGPENALTY, giving it that 25439101Sbostic * many extra chances of appearing. Each time it is selected because of this, 25539101Sbostic * its penalty is decreased by one; it is removed when it reaches 0. 25639101Sbostic * 25739101Sbostic * The penalty[] array gives the sum of all penalties in the list for 25839101Sbostic * each operation and each operand. The penlist[] array has the lists of 25939101Sbostic * penalties themselves. 26039101Sbostic */ 2618852Smckusick 26239101Sbostic int penalty[sizeof(keylist) - 1][2]; 26339101Sbostic struct penalty { 26439101Sbostic int value, penalty; /* Penalised value and its penalty. */ 26539101Sbostic struct penalty *next; 26639101Sbostic } *penlist[sizeof(keylist) - 1][2]; 2678852Smckusick 26839101Sbostic #define WRONGPENALTY 5 /* Perhaps this should depend on maxrange. */ 2698852Smckusick 27039101Sbostic /* 27139101Sbostic * Add a penalty for the number `value' to the list for operation `op', 27239101Sbostic * operand number `operand' (0 or 1). If we run out of memory, we just 27339101Sbostic * forget about the penalty (how likely is this, anyway?). 27439101Sbostic */ 27539101Sbostic penalise(value, op, operand) 27639101Sbostic int value, op, operand; 2778852Smckusick { 27839101Sbostic struct penalty *p; 27939101Sbostic char *malloc(); 28039101Sbostic 28139101Sbostic op = opnum(op); 28239101Sbostic if ((p = (struct penalty *)malloc((u_int)sizeof(*p))) == NULL) 28339101Sbostic return; 28439101Sbostic p->next = penlist[op][operand]; 28539101Sbostic penlist[op][operand] = p; 28639101Sbostic penalty[op][operand] += p->penalty = WRONGPENALTY; 28739101Sbostic p->value = value; 2888852Smckusick } 2898852Smckusick 29039101Sbostic /* 29139101Sbostic * Select a random value from 0 to maxval - 1 for operand `operand' (0 or 1) 29239101Sbostic * of operation `op'. The random number we generate is either used directly 29339101Sbostic * as a value, or represents a position in the penalty list. If the latter, 29439101Sbostic * we find the corresponding value and return that, decreasing its penalty. 29539101Sbostic */ 29639101Sbostic getrandom(maxval, op, operand) 29739101Sbostic int maxval, op, operand; 2988852Smckusick { 29939101Sbostic int value; 30039101Sbostic register struct penalty **pp, *p; 3018852Smckusick 30239101Sbostic op = opnum(op); 30339101Sbostic value = random() % (maxval + penalty[op][operand]); 3048852Smckusick 30539101Sbostic /* 30639101Sbostic * 0 to maxval - 1 is a number to be used directly; bigger values 30739101Sbostic * are positions to be located in the penalty list. 30839101Sbostic */ 30939101Sbostic if (value < maxval) 31039101Sbostic return(value); 31139101Sbostic value -= maxval; 3128852Smckusick 31339101Sbostic /* 31439101Sbostic * Find the penalty at position `value'; decrement its penalty and 31539101Sbostic * delete it if it reaches 0; return the corresponding value. 31639101Sbostic */ 31739101Sbostic for (pp = &penlist[op][operand]; (p = *pp) != NULL; pp = &p->next) { 31839101Sbostic if (p->penalty > value) { 31939101Sbostic value = p->value; 32039101Sbostic penalty[op][operand]--; 32139101Sbostic if (--(p->penalty) <= 0) { 32239101Sbostic p = p->next; 32339101Sbostic (void)free((char *)*pp); 32439101Sbostic *pp = p; 32539101Sbostic } 32639101Sbostic return(value); 32739101Sbostic } 32839101Sbostic value -= p->penalty; 3298852Smckusick } 33039101Sbostic /* 33139101Sbostic * We can only get here if the value from the penalty[] array doesn't 33239101Sbostic * correspond to the actual sum of penalties in the list. Provide an 33339101Sbostic * obscure message. 33439101Sbostic */ 33539101Sbostic (void)fprintf(stderr, "arithmetic: bug: inconsistent penalties\n"); 33639101Sbostic exit(1); 33739101Sbostic /* NOTREACHED */ 33839101Sbostic } 3398852Smckusick 34039101Sbostic /* Return an index for the character op, which is one of [+-x/]. */ 34139101Sbostic opnum(op) 34239101Sbostic int op; 3438852Smckusick { 34439101Sbostic char *p; 3458852Smckusick 34639101Sbostic if (op == 0 || (p = index(keylist, op)) == NULL) { 34739101Sbostic (void)fprintf(stderr, 34839101Sbostic "arithmetic: bug: op %c not in keylist %s\n", op, keylist); 34939101Sbostic exit(1); 35039101Sbostic } 35139101Sbostic return(p - keylist); 3528852Smckusick } 3538852Smckusick 35439101Sbostic /* Print usage message and quit. */ 35539101Sbostic usage() 3568852Smckusick { 35739134Sbostic (void)fprintf(stderr, "usage: arithmetic [-o +-x/] [-r range]\n"); 35839101Sbostic exit(1); 3598852Smckusick } 360